security: redact CLI logs, bound regex matching (ReDoS) (PER-8609/8615)#2279
security: redact CLI logs, bound regex matching (ReDoS) (PER-8609/8615)#2279Shivanshu-07 wants to merge 5 commits into
Conversation
Claude Code PR ReviewPR: #2279 • Head: bda111a • Reviewers: stack:code-reviewer SummaryThree runtime-hardening measures in
Review Table
Findings
Verdict: PASS |
| if (!_compiledSecretPatterns) { | ||
| const filepath = path.resolve(url.fileURLToPath(import.meta.url), '../secretPatterns.yml'); | ||
| const secretPatterns = YAML.parse(readFileSync(filepath, 'utf-8')); | ||
| _compiledSecretPatterns = secretPatterns.patterns.map(p => new RegExp(p.pattern.regex, 'g')); |
…omium base URL (PER-8609/8615/8616) Three contained runtime hardening fixes in @percy/core: PER-8609 (CWE-532) — clilogs were sent to the Percy API without secret redaction (cilogs already were). Wrap clilogs in the existing redactSecrets() so tokens / credential-bearing URLs are stripped before egress. PER-8615 (CWE-1333) — snapshotMatches() runs user-controllable regex/glob patterns against snapshot.name; a crafted long name reaching the matcher (e.g. via the local API, per chain PER-8627) could trigger catastrophic backtracking. Cap the matched-input length (MAX_MATCH_INPUT_LENGTH = 2048) before any RegExp/micromatch call; exact-string matching is unaffected. PER-8616 (CWE-918) — PERCY_CHROMIUM_BASE_URL was used as a download base with no validation, enabling SSRF / an integrity downgrade. Add resolveChromiumBaseUrl(): require a well-formed HTTPS URL, otherwise warn and fall back to the trusted default host. (Private HTTPS mirrors remain supported, so no host allowlist; this also gives transport integrity for PER-8605 — full checksum pinning of the binary is a separate follow-up.) Verified against real source: resolveChromiumBaseUrl (https-only + fallback), redactSecrets on the clilogs array shape (GitHub token + bearer redacted), and the ReDoS guard (catastrophic pattern on a 50k-char input returns in 0ms). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…eouts redactSecrets re-read and re-parsed secretPatterns.yml (~1.7k regexes) and recompiled every RegExp on every recursive call. Once sendBuildLogs began running redactSecrets over the entire CLI log array on egress, that per-entry cost scaled with the buffered log count (hundreds of entries), pushing snapshot/upload/core specs past the 25s jasmine timeout. Compile the pattern list once and reuse it; redaction output is unchanged. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Adds unit tests for resolveChromiumBaseUrl: default-host fallback, env default, trailing-slash normalization, and the warn-and-fallback paths for unparseable and non-HTTPS values, restoring 100% coverage. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
c7a1844 to
3e0aae3
Compare
…-fix) PER-8616 is closed as won't-fix (machine-access: exploiting it requires attacker control of the process environment). Remove resolveChromiumBaseUrl and restore install.js to the original download behavior; drop its unit tests. This PR now covers PER-8609 (redact CLI logs) and PER-8615 (bound regex matching / ReDoS) only. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Summary
Second focused percy-cli security PR — two contained runtime findings in
@percy/core.Changes
percy.js(PER-8609):sendBuildLogs()sentclilogsraw whilecilogswere already passed throughredactSecrets(). Wrapclilogsin the sameredactSecrets()so tokens / credential-bearing URLs are stripped before egress. A Percy-token pattern is added tosecretPatterns.yml, andredactSecrets()now compiles the pattern set once (memoized) so running redaction over the full CLI log array on egress does not re-parse/re-compile ~1.7k regexes per string.snapshot.js(PER-8615):snapshotMatches()runs user-controllable regex/glob patterns againstsnapshot.name. A crafted long name reaching the matcher (e.g. via the local API — chain PER-8627) plus a backtracking-prone pattern could hang the process. AddedMAX_MATCH_INPUT_LENGTH = 2048: glob/RegExp matching is skipped for over-long names (exact-string matching is unaffected, and real snapshot names are short).Verification (against real source)
redactSecretson theclilogsarray shape: GitHub token + bearer + Percy token redacted to[REDACTED];utils.test.jscovers all Percy-token prefixes. ✅(a+)+$against a 50k-char input returns in 0 ms (would otherwise hang); normal patterns still match. ✅sendBuildLogstests use secret-free messages (redaction is a no-op → content matches). ✅Closes PER-8609, PER-8615. Mitigates the ReDoS leg of PER-8627.
🤖 Generated with Claude Code