From c6e9f6ed19729e3bf5c0c2be8cbbb2830c8de410 Mon Sep 17 00:00:00 2001 From: Shawn Maddock Date: Wed, 11 Jun 2025 13:16:26 -0500 Subject: [PATCH] [ReactPHP] Add user-option for redacting query parameters (#379) * add user-option for redacting query parameters * Update src/Instrumentation/ReactPHP/src/ReactPHPInstrumentation.php Co-authored-by: Tobias Bachert * Update src/Instrumentation/ReactPHP/src/ReactPHPInstrumentation.php Co-authored-by: Tobias Bachert --------- Co-authored-by: Tobias Bachert --- README.md | 8 +++++++- phpunit.xml.dist | 1 + src/ReactPHPInstrumentation.php | 17 ++++++++++++++++- .../Integration/ReactPHPInstrumentationTest.php | 10 +++++++++- 4 files changed, 33 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 951e4fe..ac2b116 100644 --- a/README.md +++ b/README.md @@ -35,12 +35,18 @@ The extension can be disabled via [runtime configuration](https://opentelemetry. OTEL_PHP_DISABLED_INSTRUMENTATIONS=reactphp ``` -Custom HTTP methods can replace the known methods via environment variables, e.g.: +Custom HTTP methods can replace the known methods via an environment variable, e.g.: ```shell OTEL_INSTRUMENTATION_HTTP_KNOWN_METHODS="GET,HEAD,POST,PUT,DELETE,CONNECT,OPTIONS,TRACE,PATCH,MyCustomMethod" ``` +Additional HTTP query string parameters can be redacted via an environment variable, e.g., + +```shell +OTEL_PHP_INSTRUMENTATION_URL_SANITIZE_FIELD_NAMES="password,passwd,pwd,secret" +``` + Request and/or response headers can be added as span attributes via environment variables, e.g.: ```shell diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 50fd90f..c0b1f0e 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -32,6 +32,7 @@ + diff --git a/src/ReactPHPInstrumentation.php b/src/ReactPHPInstrumentation.php index 36f5987..9230108 100644 --- a/src/ReactPHPInstrumentation.php +++ b/src/ReactPHPInstrumentation.php @@ -56,6 +56,15 @@ class ReactPHPInstrumentation * @see https://opentelemetry.io/docs/specs/semconv/http/http-spans/#http-client-span */ private const ENV_HTTP_RESPONSE_HEADERS = 'OTEL_PHP_INSTRUMENTATION_HTTP_RESPONSE_HEADERS'; + /** + * The environment variable which adds to the URL query parameter keys to redact the values for. + * This supports a comma-separated list of case-sensitive query parameter keys. + * + * Note that this is not currently defined in OTel SemConv, and therefore subject to change. + * + * @see https://github.com/open-telemetry/semantic-conventions/issues/877 + */ + private const ENV_URL_SANITIZE_FIELD_NAMES = 'OTEL_PHP_INSTRUMENTATION_URL_SANITIZE_FIELD_NAMES'; /** * The `{method}` component of the span name when the original method is not known to the instrumentation. * @@ -313,6 +322,12 @@ private static function sanitizeUrl(UriInterface $uri): string $uri = $uri->withUserInfo(self::URL_REDACTION); } + $sanitizeFields = self::URL_QUERY_REDACT_KEYS; + $customFields = $_ENV[self::ENV_URL_SANITIZE_FIELD_NAMES] ?? ''; + if ($customFields !== '') { + $sanitizeFields = array_merge($sanitizeFields, explode(',', $customFields)); + } + $queryString = $uri->getQuery(); // http_build_query(parse_str()) is not idempotent, so using Guzzle’s Query class for now if ($queryString !== '') { @@ -321,7 +336,7 @@ private static function sanitizeUrl(UriInterface $uri): string $queryParameters, array_intersect_key( array_fill_keys( - self::URL_QUERY_REDACT_KEYS, + $sanitizeFields, self::URL_REDACTION ), $queryParameters diff --git a/tests/Integration/ReactPHPInstrumentationTest.php b/tests/Integration/ReactPHPInstrumentationTest.php index 586bb86..ac4da19 100644 --- a/tests/Integration/ReactPHPInstrumentationTest.php +++ b/tests/Integration/ReactPHPInstrumentationTest.php @@ -111,7 +111,7 @@ public function test_fulfilled_promise(): void $this->assertSame(['text/plain; charset=utf-8'], $span->getAttributes()->get(sprintf('%s.%s', TraceAttributes::HTTP_RESPONSE_HEADER, 'content-type'))); } - public function test_fulfilled_promise_with_redactions(): void + public function test_fulfilled_promise_with_required_redactions(): void { $this->browser->request('GET', 'http://username@example.com/success')->then(); @@ -124,6 +124,14 @@ public function test_fulfilled_promise_with_redactions(): void $this->assertSame('http://REDACTED:REDACTED@example.com/success?Signature=REDACTED', $span->getAttributes()->get(TraceAttributes::URL_FULL)); } + public function test_fulfilled_promise_with_custom_redactions(): void + { + $this->browser->request('GET', 'http://example.com/success?password=private')->then(); + + $span = $this->storage->offsetGet(0); + $this->assertSame('http://example.com/success?password=REDACTED', $span->getAttributes()->get(TraceAttributes::URL_FULL)); + } + public function test_fulfilled_promise_with_overridden_methods(): void { $this->browser->request('CUSTOM', 'http://example.com:8888/success')->then();