getRequest(); if (!empty($request)) { $this->scrubArray($request); $event->setRequest($request); } // Scrub extra context $extra = $event->getExtra(); if (!empty($extra)) { $this->scrubArray($extra); $event->setExtra($extra); } // Scrub tags that might contain PII $tags = $event->getTags(); if (!empty($tags)) { $this->scrubStringArray($tags); $event->setTags($tags); } // Never drop the event - we want all errors tracked return $event; } /** * Recursively scrub PII from an array. * * @param array $data */ private function scrubArray(array &$data): void { foreach ($data as $key => &$value) { if (is_string($key) && $this->isPiiKey($key)) { $value = '[FILTERED]'; } elseif (is_array($value)) { $this->scrubArray($value); } elseif (is_string($value) && $this->looksLikePii($value)) { $value = '[FILTERED]'; } } } /** * Scrub PII from a string-only array (tags). * * @param array $data */ private function scrubStringArray(array &$data): void { foreach ($data as $key => &$value) { if ($this->isPiiKey($key) || $this->looksLikePii($value)) { $value = '[FILTERED]'; } } } private function isPiiKey(string $key): bool { return PiiPatterns::isSensitiveKey($key); } private function looksLikePii(string $value): bool { // Filter email-like patterns if (filter_var($value, FILTER_VALIDATE_EMAIL) !== false) { return true; } // Filter JWT tokens if (preg_match('/^eyJ[a-zA-Z0-9_-]+\.eyJ[a-zA-Z0-9_-]+\.[a-zA-Z0-9_-]+$/', $value)) { return true; } // Filter UUIDs in specific contexts (but not all - some are legitimate IDs) // We keep UUIDs as they're often needed for debugging return false; } }