[GH-ISSUE #1591] [Bug] Scenario redaction can keep a declared secret #537

Closed
opened 2026-10-15 18:25:58 +00:00 by bsakaguchi · 0 comments
Owner

Originally created by @Adolanium on GitHub (Oct 10, 2026).
Original GitHub issue: https://github.com/morluto/rea/issues/1591

Before submitting

  • I searched the open and closed issues for a related report.
  • I removed credentials, tokens, private paths, and sensitive target data.

Area

Process capture or runtime observation

Observed behavior

A declared browser-scenario secret can remain in captured evidence after redaction.

With secret id password and value pass, BrowserScenarioSecrets.redact("seen pass in the page") returned seen [REDACTED:password] in the page, which still contains pass.

redactBytes searches only the original bytes. A body that contains only session-secret-value, with secrets password=session-secret-value and pin=pass, became body [REDACTED:password] tail. That result contains pass.

Cookie name, domain, and path, and localStorage / sessionStorage keys, are stored without redaction. Header names already go through redact. The secret value itself is fingerprinted.

Reproduction

  1. Declare a scenario secret whose value is a substring of [REDACTED: plus its secret_id, for example id password and value pass.
  2. Capture text or a response body that contains that secret.
  3. Observe the retained text. The marker still contains the secret.
  4. Separately, capture storage where a cookie name or storage key equals a declared secret. The name is returned unchanged. The value is fingerprinted.

Expected behavior

docs/browser-scenario-contract.md says declared secret values are removed from observed content, including a secret declared only so matching observations are removed. Digests describe the retained bytes after redaction. redactExplicitText already refuses a replacement that still contains a declared literal.

The retained text, URL, body, cookie name, domain, path, and storage key must not contain a declared secret spelling. A marker that would contain one should be replaced by a marker that does not, or the field should be cleared.

Impact and scope

Scenario evidence returned to the caller can include a declared secret. Long random tokens that are not substrings of the marker are still removed. Storage values, which are the usual place for secrets, were already fingerprinted. This does not affect HAR header-value redaction or URL userinfo removal.

REA version

6.3.0, revision 02ef2b1c41e80364b6482e4586ae40e575c6d5a5

Runtime and platform

Node 22 or 24. The replacement behavior is platform-independent.

Target and artifact context

Browser scenario capture. Synthetic secret values only.

Interface

CLI

Earliest failure stage

Capture or observation

Relevant output or evidence

seen [REDACTED:password] in the page

body [REDACTED:password] tail when the original body contained only session-secret-value and another declared secret was pass.

Typed outcome or recovery expected

Text and body evidence with no remaining declared spelling. Storage identity fields redacted the same way. value_state: "redacted-secret" when the storage value itself contains the secret.

Analysis or suspected cause

Source-derived. redact and redactBytes in src/browser/BrowserScenarioSecrets.ts always insert [REDACTED:<id>]. String.replaceAll does not scan the inserted text, and redactBytes does not scan it either. captureStorage in src/browser/PlaywrightScenarioArtifacts.ts copies cookie and storage names directly.

Additional context

Fix is in https://github.com/morluto/rea/pull/1590.

Originally created by @Adolanium on GitHub (Oct 10, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/1591 ### Before submitting - [x] I searched the open and closed issues for a related report. - [x] I removed credentials, tokens, private paths, and sensitive target data. ### Area Process capture or runtime observation ### Observed behavior A declared browser-scenario secret can remain in captured evidence after redaction. With secret id `password` and value `pass`, `BrowserScenarioSecrets.redact("seen pass in the page")` returned `seen [REDACTED:password] in the page`, which still contains `pass`. `redactBytes` searches only the original bytes. A body that contains only `session-secret-value`, with secrets `password=session-secret-value` and `pin=pass`, became `body [REDACTED:password] tail`. That result contains `pass`. Cookie `name`, `domain`, and `path`, and `localStorage` / `sessionStorage` keys, are stored without redaction. Header names already go through `redact`. The secret value itself is fingerprinted. ### Reproduction 1. Declare a scenario secret whose value is a substring of `[REDACTED:` plus its `secret_id`, for example id `password` and value `pass`. 2. Capture text or a response body that contains that secret. 3. Observe the retained text. The marker still contains the secret. 4. Separately, capture storage where a cookie name or storage key equals a declared secret. The name is returned unchanged. The value is fingerprinted. ### Expected behavior `docs/browser-scenario-contract.md` says declared secret values are removed from observed content, including a secret declared only so matching observations are removed. Digests describe the retained bytes after redaction. `redactExplicitText` already refuses a replacement that still contains a declared literal. The retained text, URL, body, cookie name, domain, path, and storage key must not contain a declared secret spelling. A marker that would contain one should be replaced by a marker that does not, or the field should be cleared. ### Impact and scope Scenario evidence returned to the caller can include a declared secret. Long random tokens that are not substrings of the marker are still removed. Storage values, which are the usual place for secrets, were already fingerprinted. This does not affect HAR header-value redaction or URL userinfo removal. ### REA version 6.3.0, revision `02ef2b1c41e80364b6482e4586ae40e575c6d5a5` ### Runtime and platform Node 22 or 24. The replacement behavior is platform-independent. ### Target and artifact context Browser scenario capture. Synthetic secret values only. ### Interface CLI ### Earliest failure stage Capture or observation ### Relevant output or evidence `seen [REDACTED:password] in the page` `body [REDACTED:password] tail` when the original body contained only `session-secret-value` and another declared secret was `pass`. ### Typed outcome or recovery expected Text and body evidence with no remaining declared spelling. Storage identity fields redacted the same way. `value_state: "redacted-secret"` when the storage value itself contains the secret. ### Analysis or suspected cause Source-derived. `redact` and `redactBytes` in `src/browser/BrowserScenarioSecrets.ts` always insert `[REDACTED:<id>]`. `String.replaceAll` does not scan the inserted text, and `redactBytes` does not scan it either. `captureStorage` in `src/browser/PlaywrightScenarioArtifacts.ts` copies cookie and storage names directly. ### Additional context Fix is in https://github.com/morluto/rea/pull/1590.
bsakaguchi 2026-10-15 18:25:58 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
morluto/rea#537
No description provided.