Skip to content

fix(signals): linear-time manifest glob matcher (ReDoS) + **/ matches root + length cap#1366

Merged
JSONbored merged 1 commit into
mainfrom
fix/manifest-glob-redos
Jun 25, 2026
Merged

fix(signals): linear-time manifest glob matcher (ReDoS) + **/ matches root + length cap#1366
JSONbored merged 1 commit into
mainfrom
fix/manifest-glob-redos

Conversation

@JSONbored

Copy link
Copy Markdown
Owner

Summary

Adversarial-audit finding (high, 3/3 verifiers). compileManifestPathMatcher compiled a *-glob to a .*-per-star regex (^.*a.*a…$) that backtracks catastrophically on a near-miss path. A .gittensory.yml glob with many non-adjacent * (in review.exclude_paths / when_paths / path_instructions[].path, or wantedPaths/blockedPaths) could hang the gate for an entire repo on Cloudflare's hard CPU limit — and the parsed manifest is cached, so every subsequent PR in that repo re-hits the hang.

Changes

  • Linear matcher — prefix + suffix + ordered-substring (indexOf) scan: O(path × parts), no backtracking. */** still match any run of chars across / (semantics unchanged for every existing case).
  • **/ matches root — a **-then-separator run absorbs the mandatory slash, so a leading ** glob also matches a zero-depth/root file (e.g. **/*.test.ts now excludes a root app.test.ts).
  • Length cap — each glob and path_instructions[].path capped at MAX_ITEM_LENGTH (300) at parse (defense-in-depth).

From the 2026-06-25 multi-agent review-subsystem audit.

Validation

  • npm run test:ci exit 0 (4407 tests); npm audit clean; all changed lines+branches covered
  • New tests: **/ root, multi-wildcard correctness, a ReDoS regression (20-star glob vs 300-char near-miss <100ms, was >30s), length cap; all prior matchesManifestPath assertions still pass

… root + glob length cap

The manifest path matcher compiled `*` globs to a `.*`-per-star regex (`^.*a.*a...$`), which
backtracks CATASTROPHICALLY on a near-miss path. A `.gittensory.yml` glob with many non-adjacent
`*` (review.exclude_paths / when_paths / path_instructions[].path, or wantedPaths/blockedPaths)
could hang the gate for an entire repo on Cloudflare's hard CPU limit — and the parsed manifest is
cached, so every subsequent PR in that repo re-hits the hang.

- Replace the regex with a LINEAR prefix + suffix + ordered-substring (indexOf) matcher — O(path ×
  parts), no backtracking. `*`/`**` still match any run of chars across `/` (unchanged semantics).
- A `**`-then-separator run now absorbs the mandatory slash, so a leading `**` glob also matches a
  ROOT-level file (e.g. exclude `**/*.test.ts` now excludes a root `app.test.ts`, not only nested).
- Cap each glob / path_instructions path at MAX_ITEM_LENGTH (300) at parse time (defense-in-depth).

Verified: a 20-star hostile glob against a 300-char near-miss returns in <100ms (previously >30s).
@dosubot dosubot Bot added the size:M label Jun 25, 2026
@superagent-security

Copy link
Copy Markdown
Contributor

Superagent didn't find any vulnerabilities or security issues in this PR.

@codecov

codecov Bot commented Jun 25, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 95.36%. Comparing base (fda93f9) to head (e27307f).
⚠️ Report is 1 commits behind head on main.
✅ All tests successful. No failed tests found.

Additional details and impacted files
@@           Coverage Diff           @@
##             main    #1366   +/-   ##
=======================================
  Coverage   95.35%   95.36%           
=======================================
  Files         191      191           
  Lines       20679    20696   +17     
  Branches     7475     7479    +4     
=======================================
+ Hits        19719    19736   +17     
  Misses        378      378           
  Partials      582      582           
Files with missing lines Coverage Δ
src/signals/focus-manifest.ts 97.92% <100.00%> (+0.06%) ⬆️
🚀 New features to boost your workflow:
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@JSONbored
JSONbored merged commit 940b7c2 into main Jun 25, 2026
18 checks passed
@JSONbored
JSONbored deleted the fix/manifest-glob-redos branch June 25, 2026 13:26
@github-actions github-actions Bot mentioned this pull request Jun 25, 2026
12 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant