Skip to content

Instantly share code, notes, and snippets.

@mingalevme
Last active April 13, 2023 05:21
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mingalevme/1beec319c17286df76afad068ee00c76 to your computer and use it in GitHub Desktop.
Save mingalevme/1beec319c17286df76afad068ee00c76 to your computer and use it in GitHub Desktop.
Laravel Sentry Data Sanitizer
<?php
namespace App\Providers;
use App\Helpers\SentrySanitizeDataOnBeforeSendListener;
use Illuminate\Contracts\Config\Repository as ConfigRepository;
use Illuminate\Contracts\Foundation\CachesConfiguration;
use Sentry\Event as SentryEvent;
use Sentry\EventHint as SentryEventHint;
use Sentry\Laravel\ServiceProvider as SentryServiceProvider;
final class AppServiceProvider extends AbstractServiceProvider
{
public function register(): void
{
// Sentry
if (!($this->app instanceof CachesConfiguration && $this->app->configurationIsCached())) {
$this->app->when(SentrySanitizeDataOnBeforeSendListener::class)
->needs('$secretNameList')
->give(
fn (): array => array_filter(
explode(',', $this->getStrEnv('SENTRY_SANITIZE_DATA') ?: ''),
) ?: ['token', 'password'],
);
/** @var ConfigRepository $config */
$config = $this->app->make('config');
$config->set(
SentryServiceProvider::$abstract,
array_merge([
'before_send' => function (SentryEvent $event, ?SentryEventHint $hint): SentryEvent {
$listener = $this->getContainerEntry(SentrySanitizeDataOnBeforeSendListener::class);
return $listener->onBeforeSend($event, $hint);
},
], (array)$config->get(SentryServiceProvider::$abstract, [])),
);
}
// ...
}
/**
* @template T of object
* @param class-string<T> $id
* @return T
*/
protected function getContainerEntry(string $id): object
{
/** @var T $entry */
$entry = $this->app->get($id);
return $entry;
}
}
<?php
namespace App\Helpers;
use Sentry\Event;
use Sentry\EventHint;
final class SentrySanitizeDataOnBeforeSendListener
{
/**
* @param list<non-empty-string> $secretNameList
*/
public function __construct(
private readonly array $secretNameList,
) {
}
public function onBeforeSend(Event $event, ?EventHint $hint): Event
{
/** @var array{query_string?: ?string, data?: array<string, mixed>} $request */
$request = $event->getRequest();
if (!empty($request['query_string'])) {
foreach ($this->secretNameList as $secretName) {
$request['query_string'] =
preg_replace(
"/$secretName=([^&?]+)/",
"$secretName=%5BFiltered%5D",
$request['query_string'],
);
}
}
if (!empty($request['data'])) {
foreach ($this->secretNameList as $secretName) {
if (!empty($request['data'][$secretName])) {
$request['data'][$secretName] = '[Filtered]';
}
}
}
$event->setRequest($request);
return $event;
}
public static function call(Event $event, ?EventHint $hint): Event
{
/** @var SentrySanitizeDataOnBeforeSendListener $self */
$self = app(self::class);
return $self->onBeforeSend($event, $hint);
}
}
<?php
namespace Tests\Suites\Feature;
use App\Helpers\SentrySanitizeDataOnBeforeSendListener;
use Sentry\Event;
use Sentry\EventId;
use Tests\TestCase;
/**
* @see SentrySanitizeDataOnBeforeSendListener
*/
final class SentrySanitizeDataOnBeforeSendListenerTest extends TestCase
{
public function test(): void
{
$event = Event::createEvent(EventId::generate());
$payload = [
'foo' => 'bar',
'token' => 'token',
'password' => 'password',
];
$event->setRequest([
'query_string' => http_build_query($payload),
'data' => $payload,
]);
/**
* @var Event $event
* @see SentrySanitizeDataOnBeforeSendListener::call()
*/
$event = [SentrySanitizeDataOnBeforeSendListener::class, 'call']($event, null);
self::assertSame(http_build_query(array_merge($payload, [
'token' => '[Filtered]',
'password' => '[Filtered]',
])), $event->getRequest()['query_string'] ?? null);
self::assertSame('bar', $event->getRequest()['data']['foo'] ?? null);
self::assertSame('[Filtered]', $event->getRequest()['data']['token'] ?? null);
self::assertSame('[Filtered]', $event->getRequest()['data']['password'] ?? null);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment