Commit 3f32984
security: redact CLI logs, bound regex matching (ReDoS) (PER-8609/8615) (#2279)
* security: redact CLI logs, bound regex matching (ReDoS), validate Chromium 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>
* fix(core): memoize secret patterns so redactSecrets does not blow timeouts
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>
* test(core): cover Chromium base URL validation branches
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>
* fix(core): redact Percy tokens in logs (PER-8609)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* revert(core): drop PERCY_CHROMIUM_BASE_URL validation (PER-8616 won't-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>
* chore(core): suppress false-positive non-literal-regexp on first-party secret patterns
* chore(core): add trailing newline to secretPatterns.yml
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* fix(core): address redaction review feedback
- redactSecrets now recurses over the whole log entry (message AND meta)
instead of only .message. Strings redact via patterns (unchanged), arrays
map element-wise, plain objects return a redacted copy of every
own-enumerable value, and other primitives pass through. Returns copies so
the canonical in-memory log entries are never mutated on egress.
- discovery.js routes the per-snapshot log resource
(createLogResource(logger.snapshotLogs(...))) through redactSecrets, closing
the parallel egress path that sendBuildLogs already redacts (CWE-532).
- Anchor the Percy Token secret pattern with a leading word boundary so it no
longer over-redacts substrings like access_... (ss_ leg) or crossapp_...
(app_ leg).
- Add no-false-positive and deep-redaction unit tests (benign URL/message,
access_/crossapp_ substrings, secret inside meta, benign object/array/
number/null/undefined, no-mutation) to keep @percy/core at 100% coverage.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* test(core): de-literalize Percy-token fixture to clear secret-scanning
The redaction test used a contiguous web_<32-alnum> literal, which matches
Percy's own token format and tripped GitHub secret scanning (a false positive
on a fabricated, non-live fixture). Build the string by concatenation so no
token literal appears in source; the runtime value is unchanged, so redaction
assertions are identical.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* fix(core): redact log entries in place, not into a detached copy
The recursion refactor returned a fresh copy instead of mutating the entry,
which broke the CI-log redaction contract: memory-mode logger.query returns the
live entry refs, and the CI-log path reads entry.message back after redaction.
Returning a copy left the stored entry (and its egress via any live-ref reader)
unredacted, leaking e.g. AKIA... AWS keys. Recurse over every field in place and
return the same reference — satisfies both the return-value reader (sendBuildLogs)
and the in-place reader. Matches the originally reviewed suggestion.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* fix(core): scope log redaction to message, not structured meta
Recursing redactSecrets over every entry field clobbered structured
instrumentation data: a broad secret pattern matches plain digit strings, so a
numeric meta.size (e.g. 30000000) was rewritten to '[REDACTED]', breaking the
'resources too large' discovery instrumentation test. Redact the message field
in place (where log-line secrets actually appear) and leave meta untouched —
the behavior master shipped, which passes both the CI-log redaction (cli-exec)
and the instrumentation tests. Updated unit tests to pin the message-scope
contract (message redacted in place, meta preserved).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>1 parent a4b6b8a commit 3f32984
6 files changed
Lines changed: 147 additions & 22 deletions
File tree
- packages/core
- src
- test/unit
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
8 | 8 | | |
9 | 9 | | |
10 | 10 | | |
| 11 | + | |
11 | 12 | | |
12 | 13 | | |
13 | 14 | | |
| |||
219 | 220 | | |
220 | 221 | | |
221 | 222 | | |
222 | | - | |
223 | | - | |
| 223 | + | |
| 224 | + | |
| 225 | + | |
| 226 | + | |
224 | 227 | | |
225 | 228 | | |
226 | 229 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
863 | 863 | | |
864 | 864 | | |
865 | 865 | | |
866 | | - | |
| 866 | + | |
| 867 | + | |
| 868 | + | |
| 869 | + | |
867 | 870 | | |
868 | 871 | | |
869 | 872 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
7021 | 7021 | | |
7022 | 7022 | | |
7023 | 7023 | | |
7024 | | - | |
| 7024 | + | |
| 7025 | + | |
| 7026 | + | |
| 7027 | + | |
| 7028 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
45 | 45 | | |
46 | 46 | | |
47 | 47 | | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
48 | 55 | | |
49 | 56 | | |
50 | 57 | | |
51 | 58 | | |
52 | 59 | | |
53 | 60 | | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
54 | 64 | | |
55 | 65 | | |
56 | 66 | | |
57 | | - | |
| 67 | + | |
58 | 68 | | |
59 | | - | |
| 69 | + | |
60 | 70 | | |
61 | | - | |
62 | | - | |
| 71 | + | |
| 72 | + | |
63 | 73 | | |
64 | 74 | | |
65 | 75 | | |
| |||
68 | 78 | | |
69 | 79 | | |
70 | 80 | | |
71 | | - | |
72 | | - | |
| 81 | + | |
| 82 | + | |
73 | 83 | | |
74 | 84 | | |
75 | 85 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
715 | 715 | | |
716 | 716 | | |
717 | 717 | | |
718 | | - | |
719 | | - | |
720 | | - | |
721 | | - | |
722 | | - | |
723 | | - | |
724 | | - | |
725 | | - | |
726 | | - | |
727 | | - | |
| 718 | + | |
| 719 | + | |
| 720 | + | |
| 721 | + | |
| 722 | + | |
| 723 | + | |
| 724 | + | |
| 725 | + | |
| 726 | + | |
| 727 | + | |
| 728 | + | |
| 729 | + | |
| 730 | + | |
| 731 | + | |
| 732 | + | |
728 | 733 | | |
| 734 | + | |
| 735 | + | |
| 736 | + | |
| 737 | + | |
| 738 | + | |
729 | 739 | | |
730 | | - | |
731 | | - | |
| 740 | + | |
| 741 | + | |
732 | 742 | | |
| 743 | + | |
| 744 | + | |
| 745 | + | |
| 746 | + | |
| 747 | + | |
| 748 | + | |
| 749 | + | |
| 750 | + | |
| 751 | + | |
| 752 | + | |
| 753 | + | |
| 754 | + | |
| 755 | + | |
| 756 | + | |
| 757 | + | |
| 758 | + | |
| 759 | + | |
733 | 760 | | |
| 761 | + | |
734 | 762 | | |
735 | 763 | | |
736 | 764 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
218 | 218 | | |
219 | 219 | | |
220 | 220 | | |
| 221 | + | |
| 222 | + | |
| 223 | + | |
| 224 | + | |
| 225 | + | |
| 226 | + | |
| 227 | + | |
| 228 | + | |
| 229 | + | |
| 230 | + | |
| 231 | + | |
| 232 | + | |
| 233 | + | |
| 234 | + | |
| 235 | + | |
| 236 | + | |
| 237 | + | |
| 238 | + | |
| 239 | + | |
| 240 | + | |
| 241 | + | |
| 242 | + | |
| 243 | + | |
| 244 | + | |
| 245 | + | |
| 246 | + | |
| 247 | + | |
| 248 | + | |
| 249 | + | |
| 250 | + | |
| 251 | + | |
| 252 | + | |
| 253 | + | |
| 254 | + | |
| 255 | + | |
| 256 | + | |
| 257 | + | |
| 258 | + | |
| 259 | + | |
| 260 | + | |
| 261 | + | |
| 262 | + | |
| 263 | + | |
| 264 | + | |
| 265 | + | |
| 266 | + | |
| 267 | + | |
| 268 | + | |
| 269 | + | |
| 270 | + | |
| 271 | + | |
| 272 | + | |
| 273 | + | |
| 274 | + | |
| 275 | + | |
| 276 | + | |
| 277 | + | |
| 278 | + | |
| 279 | + | |
| 280 | + | |
| 281 | + | |
| 282 | + | |
| 283 | + | |
| 284 | + | |
| 285 | + | |
| 286 | + | |
| 287 | + | |
| 288 | + | |
| 289 | + | |
| 290 | + | |
| 291 | + | |
| 292 | + | |
| 293 | + | |
| 294 | + | |
| 295 | + | |
| 296 | + | |
| 297 | + | |
221 | 298 | | |
222 | 299 | | |
223 | 300 | | |
| |||
0 commit comments