����JFIF��x�x����'
Server IP : 78.140.185.180 / Your IP : 3.17.139.45 Web Server : LiteSpeed System : Linux cpanel13.v.fozzy.com 4.18.0-513.11.1.lve.el8.x86_64 #1 SMP Thu Jan 18 16:21:02 UTC 2024 x86_64 User : builderbox ( 1072) PHP Version : 7.3.33 Disable Function : NONE MySQL : OFF | cURL : ON | WGET : ON | Perl : ON | Python : ON | Sudo : OFF | Pkexec : OFF Directory : /home/builderbox/www/vendor/sentry/sentry/src/Integration/ |
Upload File : |
<?php declare(strict_types=1); namespace Sentry\Integration; use GuzzleHttp\Psr7\Utils; use Psr\Http\Message\ServerRequestInterface; use Psr\Http\Message\UploadedFileInterface; use Sentry\Event; use Sentry\Exception\JsonException; use Sentry\Options; use Sentry\SentrySdk; use Sentry\State\Scope; use Sentry\UserDataBag; use Sentry\Util\JSON; use Symfony\Component\OptionsResolver\Options as SymfonyOptions; use Symfony\Component\OptionsResolver\OptionsResolver; /** * This integration collects information from the request and attaches them to * the event. * * @author Stefano Arlandini <sarlandini@alice.it> */ final class RequestIntegration implements IntegrationInterface { /** * This constant represents the size limit in bytes beyond which the body * of the request is not captured when the `max_request_body_size` option * is set to `small`. */ private const REQUEST_BODY_SMALL_MAX_CONTENT_LENGTH = 10 ** 3; /** * This constant represents the size limit in bytes beyond which the body * of the request is not captured when the `max_request_body_size` option * is set to `medium`. */ private const REQUEST_BODY_MEDIUM_MAX_CONTENT_LENGTH = 10 ** 4; /** * This constant is a map of maximum allowed sizes for each value of the * `max_request_body_size` option. */ private const MAX_REQUEST_BODY_SIZE_OPTION_TO_MAX_LENGTH_MAP = [ 'none' => 0, 'small' => self::REQUEST_BODY_SMALL_MAX_CONTENT_LENGTH, 'medium' => self::REQUEST_BODY_MEDIUM_MAX_CONTENT_LENGTH, 'always' => -1, ]; /** * This constant defines the default list of headers that may contain * sensitive data and that will be sanitized if sending PII is disabled. */ private const DEFAULT_SENSITIVE_HEADERS = [ 'Authorization', 'Cookie', 'Set-Cookie', 'X-Forwarded-For', 'X-Real-IP', ]; /** * @var RequestFetcherInterface PSR-7 request fetcher */ private $requestFetcher; /** * @var array<string, mixed> The options */ private $options; /** * Constructor. * * @param RequestFetcherInterface|null $requestFetcher PSR-7 request fetcher * @param array<string, mixed> $options The options * * @psalm-param array{ * pii_sanitize_headers?: string[] * } $options */ public function __construct(?RequestFetcherInterface $requestFetcher = null, array $options = []) { $resolver = new OptionsResolver(); $this->configureOptions($resolver); $this->requestFetcher = $requestFetcher ?? new RequestFetcher(); $this->options = $resolver->resolve($options); } /** * {@inheritdoc} */ public function setupOnce(): void { Scope::addGlobalEventProcessor(function (Event $event): Event { $currentHub = SentrySdk::getCurrentHub(); $integration = $currentHub->getIntegration(self::class); $client = $currentHub->getClient(); // The client bound to the current hub, if any, could not have this // integration enabled. If this is the case, bail out if (null === $integration || null === $client) { return $event; } $this->processEvent($event, $client->getOptions()); return $event; }); } private function processEvent(Event $event, Options $options): void { $request = $this->requestFetcher->fetchRequest(); if (null === $request) { return; } $requestData = [ 'url' => (string) $request->getUri(), 'method' => $request->getMethod(), ]; if ($request->getUri()->getQuery()) { $requestData['query_string'] = $request->getUri()->getQuery(); } if ($options->shouldSendDefaultPii()) { $serverParams = $request->getServerParams(); if (isset($serverParams['REMOTE_ADDR'])) { $user = $event->getUser(); $requestData['env']['REMOTE_ADDR'] = $serverParams['REMOTE_ADDR']; if (null === $user) { $user = UserDataBag::createFromUserIpAddress($serverParams['REMOTE_ADDR']); } elseif (null === $user->getIpAddress()) { $user->setIpAddress($serverParams['REMOTE_ADDR']); } $event->setUser($user); } $requestData['cookies'] = $request->getCookieParams(); $requestData['headers'] = $request->getHeaders(); } else { $requestData['headers'] = $this->sanitizeHeaders($request->getHeaders()); } $requestBody = $this->captureRequestBody($options, $request); if (!empty($requestBody)) { $requestData['data'] = $requestBody; } $event->setRequest($requestData); } /** * Removes headers containing potential PII. * * @param array<string, string[]> $headers Array containing request headers * * @return array<string, string[]> */ private function sanitizeHeaders(array $headers): array { foreach ($headers as $name => $values) { if (!\in_array(strtolower($name), $this->options['pii_sanitize_headers'], true)) { continue; } foreach ($values as $headerLine => $headerValue) { $headers[$name][$headerLine] = '[Filtered]'; } } return $headers; } /** * Gets the decoded body of the request, if available. If the Content-Type * header contains "application/json" then the content is decoded and if * the parsing fails then the raw data is returned. If there are submitted * fields or files, all of their information are parsed and returned. * * @param Options $options The options of the client * @param ServerRequestInterface $request The server request * * @return mixed */ private function captureRequestBody(Options $options, ServerRequestInterface $request) { $maxRequestBodySize = $options->getMaxRequestBodySize(); $requestBodySize = (int) $request->getHeaderLine('Content-Length'); if (!$this->isRequestBodySizeWithinReadBounds($requestBodySize, $maxRequestBodySize)) { return null; } $requestData = $request->getParsedBody(); $requestData = array_merge( $this->parseUploadedFiles($request->getUploadedFiles()), \is_array($requestData) ? $requestData : [] ); if (!empty($requestData)) { return $requestData; } $requestBody = Utils::copyToString($request->getBody(), self::MAX_REQUEST_BODY_SIZE_OPTION_TO_MAX_LENGTH_MAP[$maxRequestBodySize]); if ('application/json' === $request->getHeaderLine('Content-Type')) { try { return JSON::decode($requestBody); } catch (JsonException $exception) { // Fallback to returning the raw data from the request body } } return $requestBody; } /** * Create an array with the same structure as $uploadedFiles, but replacing * each UploadedFileInterface with an array of info. * * @param array<string, mixed> $uploadedFiles The uploaded files info from a PSR-7 server request * * @return array<string, mixed> */ private function parseUploadedFiles(array $uploadedFiles): array { $result = []; foreach ($uploadedFiles as $key => $item) { if ($item instanceof UploadedFileInterface) { $result[$key] = [ 'client_filename' => $item->getClientFilename(), 'client_media_type' => $item->getClientMediaType(), 'size' => $item->getSize(), ]; } elseif (\is_array($item)) { $result[$key] = $this->parseUploadedFiles($item); } else { throw new \UnexpectedValueException(sprintf('Expected either an object implementing the "%s" interface or an array. Got: "%s".', UploadedFileInterface::class, \is_object($item) ? \get_class($item) : \gettype($item))); } } return $result; } private function isRequestBodySizeWithinReadBounds(int $requestBodySize, string $maxRequestBodySize): bool { if ($requestBodySize <= 0) { return false; } if ('none' === $maxRequestBodySize) { return false; } if ('small' === $maxRequestBodySize && $requestBodySize > self::REQUEST_BODY_SMALL_MAX_CONTENT_LENGTH) { return false; } if ('medium' === $maxRequestBodySize && $requestBodySize > self::REQUEST_BODY_MEDIUM_MAX_CONTENT_LENGTH) { return false; } return true; } /** * Configures the options of the client. * * @param OptionsResolver $resolver The resolver for the options */ private function configureOptions(OptionsResolver $resolver): void { $resolver->setDefault('pii_sanitize_headers', self::DEFAULT_SENSITIVE_HEADERS); $resolver->setAllowedTypes('pii_sanitize_headers', 'string[]'); $resolver->setNormalizer('pii_sanitize_headers', static function (SymfonyOptions $options, array $value): array { return array_map('strtolower', $value); }); } }