Repository navigation
fix(session-flow): bound the JWT, URL, email and private-key redaction regexes - #6151
Conversation
The running-retro observer's ledger redaction and save_point.py's secret-shape scan re-scanned an unbounded run from every start position, so a long line with no '.', '://' or '@' took seconds to minutes. Cap the JWT header at 512 (as the ghs_ rule does), the URL scheme at 64, and the email local part and domain at the RFC 5321 limits. Closes #5951 Co-authored-by: ksextonmelodic <ksextonmelodic@gmail.com>
The running-retro observer's private key rule scanned lazily to the end of the text from every -----BEGIN header, so a repeated header took about 38 seconds. Stop the body at the next -----BEGIN and at 16384 characters (an 8192-bit RSA key is about 6.5 KB as PEM or OpenSSH) and cap the label at 64. save_point.py's header-only rule is already linear; its timing test now covers the same shape. Refs #5951 Co-authored-by: ksextonmelodic <ksextonmelodic@gmail.com>
|
| GitGuardian id | GitGuardian status | Secret | Commit | Filename | |
|---|---|---|---|---|---|
| 37850296 | Triggered | JSON Web Token | bd90c55 | plugins/session-flow/scripts/tests/test_save_point.py | View secret |
| 37850296 | Triggered | JSON Web Token | bd90c55 | plugins/session-flow/skills/running-retro/scripts/test_observer.py | View secret |
🛠 Guidelines to remediate hardcoded secrets
- Understand the implications of revoking this secret by investigating where it is used in your code.
- Replace and store your secrets safely. Learn here the best practices.
- Revoke and rotate these secrets.
- If possible, rewrite git history. Rewriting git history is not a trivial act. You might completely break other contributing developers' workflow and you risk accidentally deleting legitimate data.
To avoid such incidents in the future consider
- following these best practices for managing and storing secrets including API keys and other credentials
- install secret detection on pre-commit to catch secret before it leaves your machine and ease remediation.
🦉 GitGuardian detects secrets in your source code to help developers and security teams secure the modern development process. You are seeing this because you or someone else with access to this repository has authorized GitGuardian to scan your pull request.
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
|
Claude finished @kyle-sexton's task in 8m 10s —— View job
|
|
Claude finished @kyle-sexton's task in 6m 19s —— View job Security review (
|
|
claude-security-review has reviewed this pull request through 0e471e5; a later push is reviewed from there. |
|
claude-review has reviewed this pull request through 0e471e5; a later push is reviewed from there. |
…lag the fixtures Co-authored-by: ksextonmelodic <ksextonmelodic@gmail.com>
…irst commit Co-authored-by: ksextonmelodic <ksextonmelodic@gmail.com>
…12-character bound The bounded JWT rule no longer matched a header over 512 characters, so such a token, as with an embedded x5c chain, went through unredacted. A linear fallback after the JWT rule now consumes the whole eyJ-prefixed run in one match: the observer redacts it whole and save_point.py flags it. Co-authored-by: ksextonmelodic <ksextonmelodic@gmail.com>
… past the 512-character bound The bounded ghs_<APPID>_<JWT> rule missed a header over 512 characters, and neither JWT rule can rescue it: `_` is a word character, so no word boundary precedes its eyJ. A linear fallback after the ghs_ rule now consumes the whole run in one match: the observer redacts it whole and save_point.py flags it. Co-authored-by: ksextonmelodic <ksextonmelodic@gmail.com>
…achable first commit Co-authored-by: ksextonmelodic <ksextonmelodic@gmail.com>
Summary
The running-retro observer's ledger redaction and
save_point.py's secret-shape scan took seconds to minutes on adversarial lines: 20 s for the JWT rule, up to 86 s for the URL rule, up to 80 s for the email rule, and 37 s for the observer's private-key rule. They now finish in milliseconds, and realistic secrets are still redacted.Fix
Each pattern started at a word boundary or header and then ran an unbounded run. On a long line with no terminator, every start position scanned to the end of the line and backtracked, so the scan was quadratic (OWASP ReDoS). Each run is now bounded, as #5935 did for the
ghs_rule:ghs_rule does) and the URL scheme at 64, in bothobserver.pyandsave_point.py. The longest scheme in the IANA registry is 36.ghs_<APPID>_<JWT>token whose header exceeds 512 characters (for example one carrying anx5cchain, RFC 7515 §4.1.6) matches a linear fallback. The JWT fallback,\beyJ[A-Za-z0-9_-]{513}[A-Za-z0-9_.-]*, sits right after the JWT rule, and theghs_fallback,\bghs_[0-9]+_eyJ[A-Za-z0-9_-]{513}[A-Za-z0-9_.-]*, right after theghs_rule.observer.pyredacts the whole token, payload and signature included, andsave_point.pystill flags it with the same label.*, so neither fallback backtracks, and a matching run is consumed in one match.eyJ.ghs_fallback is needed because_is a word character, so no word boundary precedes aghs_token'seyJand neither JWT rule can match it.observer.py.observer.py, cap the private-key label at 64 and stop the body at the next-----BEGINand at 16384 characters.openssl genpkeyandssh-keygen), so 16384 is over twice that.-----BEGINmakes the scan linear.-, because legacy encrypted PEM headers such asDEK-Info: DES-EDE3-CBC,…contain it (RFC 7468, RFC 1421).save_point.py's header-only private-key rule is already linear; its timing test now covers the repeated-header input.-----ENDfollowed by a complete block now redacts only the complete block..gitleaksignorecarries commit-bound entries for the two fake JWT lines in this PR's first commit (the repository's documented mechanism for intentional findings).Verification
'eyJ' + 'A'*600000,('eyJ' + 'A'*600) * 1000and('ghs_1_eyJ' + 'A'*600) * 1000.ghs_tokens with 513- and 4000-character headers, compound-scheme and 64-character-scheme URLs, emails at the RFC limits, and a 4096-bit-sized PEM (plain,RSAandOPENSSHlabels) are still redacted or flagged; real generated 4096- and 8192-bit keys are fully redacted. The long-header cases fail with only the source change stashed.GIT_CONFIG_GLOBAL=/dev/null, because the test VM's globalurl.insteadOfrewrite breaks the unrelatedtest_new_origin_falls_back_to_directory_nameon main too).hop_chain.test.sh,tidy_work.test.shandobserver.test.shpass.gitleaks gitover this PR's commits: no leaks.scripts/run-ruff.sh check,typos, markdownlint and all fourcheck-changelog-parity.shmodes pass.bd90c553c). They spell FAKE and were split in a later commit, so the squash commit on main carries no such literal; the incidents can be marked as test credentials in the GitGuardian dashboard. Rewriting the branch history is not done here.Related
Closes #5951
Follows #5935.