@@ -3,8 +3,18 @@ import { FILTERED_VALUE } from './filtering-snippets';
33import { shouldFilterDataKey } from './filterKeyValueData' ;
44import { filterQueryParams } from './filterQueryParams' ;
55
6- /** Matches `key=value&key2=value2` bodies, the only non-JSON shape whose keys the denylist can check. */
7- const FORM_BODY_RE = / ^ [ ^ = & ] + = [ ^ & ] * (?: & [ ^ = & ] + = [ ^ & ] * ) * $ / ;
6+ /** One `&`-separated form segment: empty, a bare key, or `key=value`. */
7+ const FORM_SEGMENT_RE = / ^ (?: [ ^ = ] + (?: = .* ) ? ) ? $ / ;
8+
9+ /**
10+ * A form body is `&`-separated `key=value` pairs, the only non-JSON shape whose keys the denylist
11+ * can check. Valueless keys, empty segments, and a trailing `&` are tolerated — a too-strict gate
12+ * would let a body like `password=secret&` skip the filter and ship raw. At least one `=` is
13+ * required so prose is never rewritten as a pseudo-form.
14+ */
15+ function isFormBody ( body : string ) : boolean {
16+ return body . includes ( '=' ) && body . split ( '&' ) . every ( segment => FORM_SEGMENT_RE . test ( segment ) ) ;
17+ }
818
919/**
1020 * Scrubs the values of known-sensitive keys in an HTTP body the SDK collected itself, before it
@@ -40,7 +50,7 @@ export function filterCollectedHttpBodyString(body: string): string {
4050 // Not JSON. The form-encoded attempt below runs instead.
4151 }
4252
43- if ( FORM_BODY_RE . test ( body ) ) {
53+ if ( isFormBody ( body ) ) {
4454 // The query-param filter keeps the body's original encoding byte-for-byte.
4555 return filterQueryParams ( body , true ) ?? body ;
4656 }
0 commit comments