fix(security): stop credential/PII leaks at capture, persistence and replay

SA
2mo ago
+263-22

Problem

Recorded credentials and PII currently leak on three independent paths:

  1. semanticInfo.value carries the RAW value of every field — including passwords. The step's own value is masked to ********, but the semanticInfo object embedded in the same event still contains the cleartext, which is stored in the session log, dumped to the page console (console.log("Sending CUSTOM_INPUT_EVENT:", inputData)), and shipped to the local recorder server.
  2. Masking only covers input[type=password]. OTP fields (type=text/tel + autocomplete=one-time-code), credit-card/CVV fields (type=text/number), SSN/IBAN and password-named fields are recorded in cleartext. We verified this with a real recording where a phone number typed into a tel field landed verbatim in the saved .workflow.yaml.
  3. High-confidence sensitive matches are persisted as plaintext defaults. variable_identifier deliberately sets suggested_default=None for SSN/credit-card/password matches (confidence ≥ 0.95), but _generate_input_schema overrides that intent — entry['default'] = candidate.value — so the recorded secret is written into the saved workflow file anyway, tagged format: ssn / credit-card.

On replay, input values (including ********-masked and unmasked ones) are logged at INFO and embedded in error reports verbatim on both executor paths.

Fix

Extension (capture):

  • new isSensitiveField(): checks type, autocomplete (one-time-code, cc-number, cc-csc, cc-exp, new-password, current-password) and name/id/aria-label/placeholder conventions
  • extractSemanticInfo masks the value of sensitive fields — the raw value never enters the event stream
  • handleInput masks by isSensitiveField instead of type === 'password' only
  • per-event console dumps reduced to event-type-only lines (no payload objects)

Python (persistence + replay):

  • _generate_input_schema respects suggested_default=None: sensitive defaults are omitted instead of falling back to the raw recorded value
  • new workflow_use/workflow/redaction.py: redact_step_value() masks values whose field hints look sensitive; wired into the semantic executor's input log, ErrorContext.input_value, and the deterministic controller's input log

No behavior change for non-sensitive fields.

Related: #165 (audit findings), #166, #168.

🤖 Generated with Claude Code


Summary by cubic

Prevents credential and PII leaks by masking sensitive values at capture, persistence, and replay. rrweb input events now use maskAllInputs, and logs/error reports no longer expose raw secrets; non-sensitive inputs behave the same.

  • Bug Fixes
    • Capture (extension): rrweb maskAllInputs; broaden sensitive field detection (type, autocomplete, and name/label hints incl. tel/phone, EN/TR); mask semanticInfo.value and input values; console logs omit payloads.
    • Persistence: _generate_input_schema omits defaults for sensitive types and for sensitive name/context hints; no '********' defaults; these inputs are forced required.
    • Replay: redact_step_value redacts inputs in INFO logs and error reports using hints incl. elementText, elementTag, inputType; applied in the semantic executor, deterministic controller, and error context.