Skip to content

[FEAT] HTTP Headers Capture - #1163

Open
marco-saia-datadog wants to merge 26 commits into
developfrom
marcosaia/RUM-13466/network-request-headers
Open

marco-saia-datadog wants to merge 26 commits into
developfrom
marcosaia/RUM-13466/network-request-headers

Conversation

@marco-saia-datadog

@marco-saia-datadog marco-saia-datadog commented Feb 26, 2026 •

Copy link
Copy Markdown
Member

What does this PR do?

Adds opt-in HTTP request and response header capture to RUM resource events. With resource tracking enabled (trackResources: true), set the headerCaptureRules option in your RUM configuration to choose which headers get captured. headerCaptureRules is the only switch: there is no separate on/off flag.

headerCaptureRules accepts:

  • Omitted (default): no headers are captured and the capture path adds no overhead.
  • 'defaults': captures a predefined set of 10 response headers (cache-control, etag, age, expires, content-type, content-encoding, content-length, vary, server-timing, x-cache) and 2 request headers (cache-control, content-type) on all URLs.
  • HeaderCaptureRule[]: an array of composable rules, each with a type: 'defaults', 'matchHeaders', 'matchRequestHeaders' or 'matchResponseHeaders'. A rule can be scoped to specific URLs with forURLs:
    • A hostname (api.example.com) matches itself and its subdomains, never a hostname that only appears in the path, query, fragment or userinfo of another URL.
    • A hostname + path prefix (api.example.com/v2) matches whole path segments: /v2 and /v2/users, but not /v22.
    • Hostnames match case-insensitively; path prefixes match case-sensitively.
    • Omitting forURLs, or including '*', makes the rule catch-all.

Rule precedence: the header sets of all matching rules are merged. However, if at least one scoped rule (with forURLs) matches a URL, catch-all rules are ignored for that URL. This lets you narrow capture on specific hosts. For example, [{ type: 'defaults' }, { type: 'matchResponseHeaders', headers: ['x-request-id'], forURLs: ['api.example.com'] }] captures only x-request-id on api.example.com and the default headers everywhere else.

Captured headers appear as _dd.request_headers and _dd.response_headers context attributes on stopResource events. These attributes are omitted entirely when there is nothing to report.

Supported clients and configuration

  • XHR (trackResources: true): covers React Native's built-in fetch, axios and any other XMLHttpRequest-based client.
  • Global Fetch (trackFetchResources: true): covers the Expo-installed global fetch. When the global fetch is backed by XHR, headers are reported once, on the Fetch resource. Direct imports retained from expo/fetch are not intercepted.
  • File-based configuration: headerCaptureRules is supported in the JSON configuration and described in datadog-configuration.schema.json. Rules are validated at runtime, and malformed rules are skipped with a warning instead of preventing resource tracking from starting.

Security model

Two layers of non-overridable filtering are always applied, regardless of configuration:

  1. Sensitive header blocklist: headers matching a compiled pattern (token, cookie, secret, authorization, password, credential, bearer, API/access key variants, forwarding and IP headers) are blocked at capture time and never stored in memory.
  2. SDK header exclusion: the distributed tracing headers injected by the SDK (Datadog, W3C/OTel, B3) and the x-datadog-tracked-by header are always excluded.

Implementation pipeline

The feature is a pipeline applied per request, shared by the XHR and Fetch proxies (requestProxy/common/headerCapture.ts):

  1. compileHeaderConfig.ts: validates headerCaptureRules and compiles it into pre-built RegExp + Set<string> objects once at SDK initialization, so there is no per-request config traversal.
  2. captureHeaders.ts: accumulates user-set request headers (after security filtering), then filters request and response headers by the rules matching the request URL.
  3. parseResponseHeaders.ts: parses the raw XHR getAllResponseHeaders() string. Fetch reads headers from the Response object.
  4. enforceSizeLimits.ts: caps each header value (128 bytes), the number of headers (100) and the total payload (2048 bytes, UTF-8) before attributes are forwarded to the native layer.

Configuration is snapshotted per request when the request starts, so in-flight requests are never affected by config changes. Captured headers are finalized when the request completes, before any asynchronous GraphQL body parsing, and the request header store is then released.

Motivation

Caching and content negotiation issues are among the most common and hardest-to-diagnose categories of network problems in mobile apps. Headers like Cache-Control, ETag, Age, Server-Timing and Vary carry the information needed to understand whether resources are being served from cache, how long they live, and whether CDN or server configuration is correct. None of this was previously visible in RUM resource events without custom instrumentation.

This feature makes that class of debugging available out of the box, while keeping security as a first-class constraint: sensitive headers are hard-blocked at the SDK level and cannot be enabled through misconfiguration.

Additional Notes

  • Header capture is disabled by default and opt-in only: leave headerCaptureRules unset and existing integrations are unaffected.
  • headerCaptureRules requires trackResources: true. If it is set while trackResources is false, the SDK logs a warning and captures nothing.
  • Requests that fail without a response (aborted, network error, XHR status 0) never report headers.
  • Fetch Headers lowercase header names, so on Fetch resources custom request headers use the casing from the rule's headers array rather than the casing passed to fetch.
  • Full design and behavior reference: packages/core/src/rum/instrumentation/resourceTracking/headerCapture/README.md

Review checklist (to be filled by reviewers)

  • Feature or bugfix MUST have appropriate tests
  • Make sure you discussed the feature or bugfix with the maintaining team in an Issue
  • Make sure each commit and the PR mention the Issue number (cf the CONTRIBUTING doc)
  • If this PR is auto-generated, please make sure also to manually update the code related to the change

@marco-saia-datadog
marco-saia-datadog force-pushed the marcosaia/RUM-13466/network-request-headers branch from 0cf3bbb to 1c3b17e Compare February 26, 2026 14:03
@marco-saia-datadog
marco-saia-datadog force-pushed the marcosaia/RUM-13466/network-request-headers branch from 1c3b17e to f9f867a Compare March 19, 2026 10:50
@marco-saia-datadog
marco-saia-datadog force-pushed the marcosaia/RUM-13466/network-request-headers branch from f9f867a to eb092fd Compare June 8, 2026 10:55
@datadog-prod-us1-3

datadog-prod-us1-3 Bot commented Jun 8, 2026 •

Copy link
Copy Markdown

Pipelines  Tests

🎉 All green!

🧪 All tests passed
❄️ No new flaky tests detected

This comment will be updated automatically if new data arrives.
🔗 Commit SHA: b2f1344 | Docs | View more details | Give us feedback!

@marco-saia-datadog
marco-saia-datadog force-pushed the marcosaia/RUM-13466/network-request-headers branch from be7468a to 9590fd3 Compare June 8, 2026 13:51
@marco-saia-datadog
marco-saia-datadog marked this pull request as ready for review June 8, 2026 14:18
@marco-saia-datadog
marco-saia-datadog requested a review from a team as a code owner June 8, 2026 14:18
Copilot AI review requested due to automatic review settings June 8, 2026 14:18
@marco-saia-datadog
marco-saia-datadog requested a review from a team as a code owner June 8, 2026 14:18
@marco-saia-datadog marco-saia-datadog changed the title [FEAT] [POC] HTTP Headers Capture [FEAT] HTTP Headers Capture Jun 8, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds opt-in HTTP request/response header capture to RUM resource events (via XHR interception), including configuration compilation, security filtering, and size limiting before forwarding captured headers as _dd.request_headers / _dd.response_headers attributes on stopResource.

Changes:

  • Introduces headerCaptureRules RUM config (defaults shortcut + composable rules), compiles it once at init, and plumbs it through resource tracking.
  • Captures request headers from setRequestHeader, parses response headers from getAllResponseHeaders(), applies sensitive/tracing header exclusion, then enforces payload size limits.
  • Adds extensive unit/integration tests plus a feature README; exposes rule types from the package entrypoint.

Reviewed changes

Copilot reviewed 35 out of 36 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
packages/core/src/sdk/DatadogProvider/tests/initialization.test.tsx Updates provider init snapshot (adds headerCaptureRules, changes RN version handling).
packages/core/src/rum/instrumentation/resourceTracking/requestProxy/XHRProxy/XHRProxy.ts Implements per-request header capture + size limiting in XHR proxy pipeline.
packages/core/src/rum/instrumentation/resourceTracking/requestProxy/XHRProxy/DatadogRumResource/ResourceReporter.ts Adds _dd.request_headers / _dd.response_headers to stop context when present.
packages/core/src/rum/instrumentation/resourceTracking/requestProxy/XHRProxy/DatadogRumResource/tests/ResourceReporter.test.ts Tests header attributes presence/absence in stop context.
packages/core/src/rum/instrumentation/resourceTracking/requestProxy/XHRProxy/tests/XHRProxy.test.ts Adds XHRProxy-level tests for header capture, security filtering, and integration into stopResource.
packages/core/src/rum/instrumentation/resourceTracking/requestProxy/interfaces/RumResource.ts Extends resource interface with optional captured headers.
packages/core/src/rum/instrumentation/resourceTracking/requestProxy/interfaces/RequestProxy.ts Extends tracking context with compiled header-capture config.
packages/core/src/rum/instrumentation/resourceTracking/headerCapture/types.ts Defines compiled header-capture config/rule runtime types.
packages/core/src/rum/instrumentation/resourceTracking/headerCapture/tracingHeaderExclusion.ts Implements explicit tracing-header exclusion set.
packages/core/src/rum/instrumentation/resourceTracking/headerCapture/sensitiveHeaderBlocklist.ts Implements sensitive-header regex blocklist.
packages/core/src/rum/instrumentation/resourceTracking/headerCapture/README.md Documents configuration, behavior, security model, and constraints.
packages/core/src/rum/instrumentation/resourceTracking/headerCapture/parseResponseHeaders.ts Adds safe parsing for getAllResponseHeaders() output.
packages/core/src/rum/instrumentation/resourceTracking/headerCapture/isHeaderAllowed.ts Composes sensitive + tracing exclusion into a single check.
packages/core/src/rum/instrumentation/resourceTracking/headerCapture/enforceSizeLimits.ts Adds truncation + count + total-budget limiting for captured headers.
packages/core/src/rum/instrumentation/resourceTracking/headerCapture/compileHeaderConfig.ts Compiles user config into URL regex + sets/maps for O(1) matching.
packages/core/src/rum/instrumentation/resourceTracking/headerCapture/captureHeaders.ts Core capture logic: accumulate request headers, capture/merge/filter response headers, apply casing rules.
packages/core/src/rum/instrumentation/resourceTracking/headerCapture/tests/tracingHeaderExclusion.test.ts Tests tracing header exclusion list behavior.
packages/core/src/rum/instrumentation/resourceTracking/headerCapture/tests/sensitiveHeaderBlocklist.test.ts Tests sensitive header regex behavior.
packages/core/src/rum/instrumentation/resourceTracking/headerCapture/tests/parseResponseHeaders.test.ts Tests response header parsing edge cases.
packages/core/src/rum/instrumentation/resourceTracking/headerCapture/tests/isHeaderAllowed.test.ts Tests composed allow/deny decision.
packages/core/src/rum/instrumentation/resourceTracking/headerCapture/tests/enforceSizeLimits.test.ts Unit tests for truncation/count/budget behavior.
packages/core/src/rum/instrumentation/resourceTracking/headerCapture/tests/enforceSizeLimits.integration.test.ts End-to-end test ensuring limits apply before reaching native stopResource.
packages/core/src/rum/instrumentation/resourceTracking/headerCapture/tests/compileHeaderConfig.test.ts Tests compilation behavior for rule variants and forURLs.
packages/core/src/rum/instrumentation/resourceTracking/headerCapture/tests/captureHeaders.test.ts Tests request accumulation, response capture, casing, union/precedence behavior.
packages/core/src/rum/instrumentation/resourceTracking/distributedTracing/firstPartyHosts.ts Exports escapeRegExp for reuse in header URL matching.
packages/core/src/rum/instrumentation/resourceTracking/DdRumResourceTracking.tsx Plumbs headerCaptureRules into tracking start via compilation.
packages/core/src/rum/instrumentation/resourceTracking/tests/utils/XMLHttpRequestMock.ts Adjusts mock getAllResponseHeaders default return value.
packages/core/src/index.tsx Exports header-capture rule types publicly.
packages/core/src/DdSdkReactNative.tsx Wires headerCaptureRules through initialization; warns if set without trackResources.
packages/core/src/config/features/RumConfiguration.type.ts Adds headerCaptureRules option + rule type definitions.
packages/core/src/config/features/RumConfiguration.ts Adds headerCaptureRules to defaults and configuration instance.
packages/core/src/config/async/AutoInstrumentationConfiguration.ts Adds headerCaptureRules to auto-instrumentation config shapes/defaulting.
packages/core/src/config/tests/FileBasedConfiguration.test.ts Updates snapshots for new rum config field.
packages/core/src/tests/DdSdkReactNativeConfiguration.test.ts Updates snapshots for new rum config field.
packages/core/src/tests/DdSdkReactNative.test.tsx Updates expectations to include headerCaptureRules.
.gitignore Ignores .planning directory/file.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread packages/core/src/rum/instrumentation/resourceTracking/headerCapture/README.md Outdated
Comment thread packages/core/src/config/features/RumConfiguration.type.ts
Comment thread packages/core/src/config/features/RumConfiguration.type.ts
Comment thread packages/core/src/rum/instrumentation/resourceTracking/headerCapture/README.md Outdated
Copilot AI review requested due to automatic review settings June 8, 2026 14:27

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 35 out of 36 changed files in this pull request and generated 7 comments.

Comment thread packages/core/src/rum/instrumentation/resourceTracking/headerCapture/README.md Outdated
Comment thread packages/core/src/config/features/RumConfiguration.type.ts
Comment thread packages/core/src/config/features/RumConfiguration.type.ts
@marco-saia-datadog
marco-saia-datadog force-pushed the marcosaia/RUM-13466/network-request-headers branch from 3390b99 to 89808ed Compare June 8, 2026 14:35
Copilot AI review requested due to automatic review settings June 8, 2026 14:37

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 35 out of 36 changed files in this pull request and generated 7 comments.

Comment thread packages/core/src/config/features/RumConfiguration.type.ts Outdated
Comment thread packages/core/src/sdk/DatadogProvider/__tests__/initialization.test.tsx Outdated
Comment thread packages/core/src/rum/instrumentation/resourceTracking/headerCapture/README.md Outdated
Copilot AI review requested due to automatic review settings June 9, 2026 14:57

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 37 out of 38 changed files in this pull request and generated 4 comments.

Comment thread packages/core/src/utils/stringUtils.ts
Copilot AI review requested due to automatic review settings June 9, 2026 15:03
@DataDog DataDog deleted a comment from datadog-datadog-prod-us1-2 Bot Jun 9, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 37 out of 38 changed files in this pull request and generated 3 comments.

Comment thread packages/core/src/utils/stringUtils.ts
Copilot AI review requested due to automatic review settings June 9, 2026 15:13
@marco-saia-datadog
marco-saia-datadog force-pushed the marcosaia/RUM-13466/network-request-headers branch from d8d9645 to 0fac228 Compare June 9, 2026 15:13
- Exclude backslashes from forURLs subdomain matching, since URL parsers
  treat them as path separators (host-scope bypass)
- Treat any forURLs array containing '*' as catch-all
- Snapshot Fetch captured headers before awaiting GraphQL body parsing
- Stringify request header values before capture so non-string values
  no longer drop the resource event

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

XHR header capture can suppress resource reporting and inflate timings, and the advertised configuration contract needs alignment.

4 open issues

Review effort: Balanced

firstPartyHosts,
trackFetchResources
trackFetchResources,
headerCaptureRules
}
}

return hasScopedMatch ? scopedMatches : catchAllMatches;
Copilot AI balanced review requested due to automatic review settings October 26, 2026 10:13

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Application-key credentials can bypass filtering, and the advertised opt-in contract remains inconsistent with the implementation.

5 open issues
1 previously missed issue

In code that hasn't changed since last review

Medium severity trackResourceHeaders flag is missing from configuration paths

packages/​core/​src/​DdSdkReactNative.tsx:522

The advertised trackResourceHeaders opt-in is absent from the configuration types and initialization path: flag-only opt-in cannot enable default capture, while configured rules enable capture regardless of the flag. Thread the false-by-default flag through public, file-based, and async configurations, gate capture on it, and use 'defaults' when enabled with rules omitted. If rules alone are the intended opt-in, reconcile the PR description with that API instead.

Review effort: Balanced

- Stop the XHR resource timer before finalizing captured headers, so
  header parsing and size enforcement do not inflate the duration
- Stringify XHR header values once and use the same string for sending
  and capture

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Copilot AI balanced review requested due to automatic review settings October 26, 2026 10:21

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

URL matching can bypass configured capture restrictions, and malformed rule types can interrupt resource-tracking initialization.

3 open issues
3 previously missed issues

In code that hasn't changed since last review

Medium severity Allow userinfo in XHR URL hostname matching

packages/​core/​src/​rum/​instrumentation/​resourceTracking/​headerCapture/​compileHeaderConfig.ts:61

This regex rejects an XHR URL containing userinfo, such as https://user:pass@api.example.com/users, even though its hostname is api.example.com. With catch-all defaults and a restrictive scoped rule for that host, this incorrectly captures defaults instead of applying the restriction. Allow optional userinfo before the hostname while still rejecting hostnames appearing only before @, and add a positive userinfo test alongside the existing spoofing tests.

Medium severity Handle non-string rule types without crashing compilation

packages/​core/​src/​rum/​instrumentation/​resourceTracking/​headerCapture/​compileHeaderConfig.ts:154

A malformed JSON rule such as {"type":{"toString":null}} makes String(type) throw because the object cannot be converted to a primitive. The exception escapes rule compilation and prevents resource tracking from starting instead of skipping the invalid rule. Check that type is a string before interpolating it, and add this case to the malformed-rule tests.

Medium severity Canonicalize URL dot segments before scope matching

packages/​core/​src/​rum/​instrumentation/​resourceTracking/​headerCapture/​normalizeUrlForMatching.ts:30

URL matching leaves dot segments untouched: https://api.example.com/public/../private (also /public/%2e%2e/private) matches a scope for api.example.com/public, although the destination path is /private. This can capture headers outside the configured path scope; conversely, /v1/../v2/users misses a restrictive /v2 scope and can fall back to broader catch-all rules. Canonicalize the pathname using the client's URL semantics before matching, preserving path case, and add regression tests for literal and encoded dot segments.

2 resolved since last review

Review effort: Balanced

- Block application-key credentials (e.g. DD-APPLICATION-KEY)
- Allow userinfo before the host in forURLs matching, while still
  rejecting hostnames that only appear in the userinfo
- Resolve dot segments (including %2e encodings) and backslashes in the
  path before scope matching
- Skip rules with a non-string type instead of throwing while building
  the warning message

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Copilot AI balanced review requested due to automatic review settings October 26, 2026 10:44

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

URL normalization can allow header capture outside configured path scopes, and Unicode size-limit boundary coverage remains incomplete.

3 open issues
1 previously missed issue

In code that hasn't changed since last review

Medium severity Add UTF-8 byte-boundary tests for truncation

packages/​core/​src/​rum/​instrumentation/​resourceTracking/​headerCapture/​enforceSizeLimits.ts:41

The new truncation logic handles multibyte characters and surrogate pairs, but all enforceSizeLimits tests use ASCII; its byte-budget helper also counts UTF-16 code units with .length. The utf8ByteLength tests do not exercise truncation. Please add 2-, 3-, and 4-byte boundary cases around 128 bytes, including an emoji crossing the boundary and lone surrogates, and verify the combined budget using UTF-8 byte counts.

1 resolved since last review

Review effort: Balanced

- Remove ASCII tabs, CRs and LFs and trim leading/trailing control
  characters and spaces before normalizing URLs, as URL parsers do
- Add UTF-8 boundary tests for value truncation (2-, 3- and 4-byte
  characters, lone surrogates) and for the total byte budget

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Copilot AI balanced review requested due to automatic review settings October 26, 2026 11:06

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

URL scope matching can miss restrictive rules and apply broader catch-all header capture instead.

2 open issues
2 previously missed issues

In code that hasn't changed since last review

Medium severity Match root URL scopes when request paths are omitted

packages/​core/​src/​rum/​instrumentation/​resourceTracking/​headerCapture/​compileHeaderConfig.ts:58

A root-path scope such as forURLs: ['api.example.com/'] requires a literal slash here, but the request context preserves inputs such as https://api.example.com or https://api.example.com?view=home. Both requests use / as their HTTP path, yet this rule fails to match; capture is either omitted or falls back to broader catch-all rules. Treat the root prefix as matching an omitted path as well, and cover both forms in the URL-matching tests.

Medium severity Canonicalize encoded hostnames before URL scope matching

packages/​core/​src/​rum/​instrumentation/​resourceTracking/​headerCapture/​normalizeUrlForMatching.ts:41

https://%61pi.example.com/v2/users resolves to api.example.com, but lowercasing leaves %61pi intact, so a scoped api.example.com/v2 rule does not match. With a broader catch-all rule, this captures headers that the scoped rule should suppress. Canonicalize the hostname using HTTP URL semantics before matching, and add a regression covering encoded hostnames with scoped-over-catch-all precedence.

1 resolved since last review

Review effort: Balanced

- Treat an omitted URL path as '/', so root scopes such as
  'api.example.com/' match 'https://api.example.com?view=home'
- Decode percent-encoded hostname characters before scope matching, so
  scoped rules are not bypassed in favor of catch-all rules

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Copilot AI balanced review requested due to automatic review settings October 26, 2026 11:28

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

URL canonicalization can select broader capture rules than intended, and response adapters do not meet the stated filter-before-storage guarantee.

2 open issues
2 previously missed issues

In code that hasn't changed since last review

Medium severity Canonicalize equivalent URLs before scoped rule matching

packages/​core/​src/​rum/​instrumentation/​resourceTracking/​headerCapture/​normalizeUrlForMatching.ts:69

Equivalent HTTP URL spellings can miss restrictive scoped rules and select broader catch-all capture: https://api.example.com/v2/users (U+FF0E, including its percent-encoded form) is not normalized to api.example.com, and extra scheme slashes such as https:///api.example.com/v2/users are retained at lines 38–48. For clients that resolve these spellings to https://api.example.com/v2/users, headers outside the intended scoped set can therefore be reported. Use URL canonicalization compatible with the supported clients, normalize configured hostnames consistently, and add precedence tests for these equivalent spellings.

Medium severity Filter blocked headers before SDK-owned storage

packages/​core/​src/​rum/​instrumentation/​resourceTracking/​headerCapture/​parseResponseHeaders.ts:53

Blocked response headers such as Set-Cookie are copied into an SDK-owned record before filtering, contrary to the stated filter-before-storage guarantee; they are excluded from the eventual event. Apply isHeaderAllowed(name) before extracting/storing values here and before assignment in requestProxy/common/headerCapture.ts:110–112. Update both adapters' tests to verify that blocked headers never enter their output records.

Review effort: Balanced

…torage

- Normalize hostnames like HTTP clients do before scope matching:
  decode percent-encoded UTF-8, apply NFKC, map IDNA label separators
  (U+3002, U+FF0E, U+FF61) to '.', and apply the same normalization to
  configured forURLs hostnames
- Collapse extra slashes after http(s): before matching
- Drop blocked headers while parsing XHR response headers and copying
  Fetch headers, so they never enter SDK-owned records

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Copilot AI balanced review requested due to automatic review settings October 26, 2026 11:58

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

Incomplete hostname normalization can cause restrictive scoped rules to be bypassed in favor of broader header capture.

2 open issues
1 previously missed issue

In code that hasn't changed since last review

Medium severity Normalize scoped hostnames with client-compatible IDNA

packages/​core/​src/​rum/​instrumentation/​resourceTracking/​headerCapture/​normalizeUrlForMatching.ts:105

NFKC plus lowercasing does not normalize internationalized hostnames consistently: a scope of bücher.example misses https://xn--bcher-kva.example/users, and IDNA-ignored characters such as the soft hyphen in api.exam\u00adple.com remain in the matching hostname. With a restrictive scoped rule and a broader catch-all rule, these misses capture headers that the scoped rule excludes. Use client-compatible IDNA normalization for both configured and request hostnames, disable capture when normalization cannot be performed safely, and add scoped-precedence tests for ignored characters and Unicode/punycode equivalents.

Review effort: Balanced

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Copilot AI balanced review requested due to automatic review settings October 26, 2026 12:12

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

URL normalization gaps can bypass restrictive scoped rules and enable broader header capture.

2 open issues
2 previously missed issues

In code that hasn't changed since last review

Medium severity Canonicalize Unicode and punycode hostnames consistently

packages/​core/​src/​rum/​instrumentation/​resourceTracking/​headerCapture/​normalizeUrlForMatching.ts:80

Equivalent hostnames such as bücher.example and xn--bcher-kva.example remain different here, so a restrictive scoped rule can be missed and broader catch-all headers captured instead. NFKC also leaves U+00AD (soft hyphen) intact, although HTTP hostname processing removes it. Canonicalize configured and request hostnames with the same HTTP-compatible IDNA implementation, and add scoped-precedence regression tests for Unicode/punycode spellings and ignored characters.

Medium severity Normalize empty ports to preserve scoped rule matching

packages/​core/​src/​rum/​instrumentation/​resourceTracking/​headerCapture/​normalizeUrlForMatching.ts:130

https://api.example.com:/v2/users is a valid spelling of https://api.example.com/v2/users, but retaining the empty port makes the compiled scope miss it and apply broader catch-all rules instead. Drop the lone : during normalization and add a scoped-precedence regression test for this spelling.

Review effort: Balanced

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants