fix(scoring): cap labelPatternToRegExp wildcard groups to prevent ReDoS#2482
Conversation
labelPatternToRegExp compiled a registry-supplied label_multipliers key into a RegExp with unbounded chained-wildcard translation and no length/complexity cap -- the exact class of bug #2445 fixed in change-guardrail.ts's globToRegExp, which caps chained wildcard groups at 2 after benchmarking confirmed catastrophic backtracking (over 2s at ~4,000 chars for 3 chained groups). This sibling was never patched. The pattern originates from a repo's registryConfig.labelMultipliers, sourced from the externally-fetched gittensor registry (registry/ sync.ts + registry/normalize.ts) with no validation -- not a value a repo's own maintainer directly controls via .gittensory.yml, despite an inaccurate in-code comment claiming the key set was "bounded, not attacker-supplied". Reachable via the public score-preview API, the MCP tool, and the per-PR label-audit signal, so one bad registry entry could hang scoring for every PR on that repo. Reuses change-guardrail.ts's exported hasUnsafeWildcardCount directly (same *-group counting, same empirically-safe threshold) rather than reimplementing it -- a `*` in this fnmatch-style pattern language has the identical "any run of chars" semantics as that glob compiler's `*`, so the same catastrophic-backtracking risk and safe boundary apply. An over-complex pattern now compiles to a safe never-match instead of a pathological RegExp. Fixes #2456
|
Warning 🟨🟨🟨🟨🟨🟨🟨🟨🟨🟨🟨🟨 ⏸️ Gittensory review result - manual review recommendedReview updated: 2026-07-02 05:44:00 UTC
⏸️ Suggested Action - Manual Review
Review summary Nits — 7 non-blocking
Review context
Contributor next steps
Signal definitions
🟩 Safe / merged · 🟦 Advisory · 🟨 Held for review · 🟥 Blocked / closed 💰 Earn for open-source contributions like this. Gittensor lets GitHub contributors earn for the work they already do — register to start earning →. Checked by Gittensory, a quiet PR intelligence layer for OSS maintainers.
|
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #2482 +/- ##
==========================================
+ Coverage 95.93% 95.95% +0.01%
==========================================
Files 225 226 +1
Lines 25338 25391 +53
Branches 9218 9235 +17
==========================================
+ Hits 24308 24363 +55
Misses 417 417
+ Partials 613 611 -2
🚀 New features to boost your workflow:
|
Summary
labelPatternToRegExp(src/scoring/preview.ts) compiled a registry-suppliedlabel_multiplierskey into aRegExpvia unbounded chained-wildcard translation with no length/complexity cap — the exact class of bug fix(signals): cap globToRegExp wildcard count to prevent ReDoS #2445 fixed insrc/signals/change-guardrail.ts'sglobToRegExp(which caps chained wildcard groups at 2, after benchmarking confirmed catastrophic backtracking: over 2 seconds at ~4,000 chars for 3 chained groups). This sibling function was never patched.registryConfig.labelMultipliers, sourced from the externally-fetched gittensor registry (registry/sync.ts+registry/normalize.ts) with no validation — not a value a repo's own maintainer directly controls via.gittensory.yml, despite an inaccurate in-code comment claiming the key set was "bounded, not attacker-supplied" (updated as part of this fix). Reachable via the public score-preview API, the MCP tool, and the per-PR label-audit signal (labelMatchesPattern), so one bad registry entry could hang scoring for every PR on that repo.change-guardrail.ts's already-exportedhasUnsafeWildcardCountdirectly (same*-group counting, same empirically-safe threshold) rather than reimplementing it — a*in this fnmatch-style pattern language has the identical "any run of chars" semantics as that glob compiler's*, so the same catastrophic-backtracking risk and safe boundary apply. An over-complex pattern now compiles to a safe never-matchRegExpinstead of a pathological one, mirroringglobToRegExp's ownNEVER_MATCHESfallback design.Scope
type(scope): short summaryConventional Commit format, for examplefix(api): restore profile access checks.CONTRIBUTING.mdand does not reintroduce GitHub Pages, VitePress,site/, orCNAME.Validation
git diff --checknpm run actionlintnpm run typechecknpm run test:coverage— new code is 100% line+branch covered in the edited range (verified againstcoverage/lcov.info). Global 96.56%/95.53%.npm run test:workersnpm run build:mcpnpm run test:mcp-packnpm run ui:openapi:checknpm run ui:lintnpm run ui:typechecknpm run ui:buildnpm audit --audit-level=moderate— 0 vulnerabilitiesFull suite: 6064/6064 passing. Also ran
npm run db:migrations:check(unaffected, green).Safety
change-guardrail.test.ts's exact pattern: a 3-wildcard pathological pattern resolves instantly (<1s) against a ~4,000-char adversarial label, plus a not-over-the-cap test proving normal 2-wildcard patterns still compile and match correctly.Notes
RegExps fed by attacker-influenced input following directly from the fix(signals): cap globToRegExp wildcard count to prevent ReDoS #2445 fix pattern.