Skip to content

fix: remove backtracking-prone regexes flagged by SonarQube - #3

Merged
ethanasm merged 1 commit into
mainfrom
claude/sonarqube-vpt-mcp-review-o1qhx3
Aug 7, 2026
Merged

fix: remove backtracking-prone regexes flagged by SonarQube#3
ethanasm merged 1 commit into
mainfrom
claude/sonarqube-vpt-mcp-review-o1qhx3

Conversation

@ethanasm

@ethanasm ethanasm commented Aug 7, 2026

Copy link
Copy Markdown
Owner

Two separate things are wrong with SonarQube here

1. The scan can't authenticate — this PR does not fix that, and it needs you.

The last run (30970722778) never reached analysis:

INFO  JRE provisioning: os[linux], arch[x86_64]
ERROR Failed to query JRE metadata: GET https://api.sonarcloud.io/analysis/jres?os=linux&arch=x86_64
      failed with HTTP 403 Forbidden. Please check the property sonar.token or the
      environment variable SONAR_TOKEN.

That endpoint answers 200 unauthenticated and 403 for a bad bearer token, so the repo's SONAR_TOKEN secret is present but no longer valid. The last green scan was 2026-02-11; a SonarCloud token minted around then has since expired. Fix: mint a new token in SonarCloud (My Account → Security) and replace SONAR_TOKEN under Settings → Secrets and variables → Actions. Nothing in the repo can do this.

2. The quality gate was failing on its own merits — that part is this PR.

new_coverage                    ERROR  69.0  (threshold 80)
new_security_hotspots_reviewed  ERROR   0.0  (threshold 100)

All five hotspots are typescript:S5852, "regex vulnerable to super-linear runtime due to backtracking":

File Line Pattern
src/llm/openai.ts 75 replace(/\/+$/, '')
src/llm/openai.ts 77 replace(/\/+$/, '')
src/tools/file-context/handlers.ts 210 import\s+.*?from\s+['"](.+?)['"]|…
src/tools/related-files/handlers.ts 124 same pattern, duplicated verbatim
src/tools/related-files/handlers.ts 265 split(/\s+as\s+/)

Changes

The import scanner was the same regex copy-pasted into both handlers. The lazy .*? sitting next to \s+ gives the engine ambiguous ways to split one line — and this scans every line of up to 500 files from a repository being reviewed, i.e. attacker-influenced input. Extracted to src/tools/import-scan.ts as extractImportSpecifiers, with each quantifier over a character set disjoint from whatever follows it ([\s(]* then a quote, [^'"\n]+ then a quote), so there is exactly one way to match and the scan is linear in line length.

Two behaviour changes fall out, both improvements: it now also finds side-effect imports (import './polyfill.js') and re-exports (export { x } from './p'), which the old pattern silently missed.

\s+as\s+\bas\b. The two \s+ runs around a literal let the engine split whitespace ambiguously; the word boundaries have no such freedom and the extracted name is identical.

replace(/\/+$/, '') → a scan. An anchored + makes the engine retry the run from every position. The trailing-slash trim and the endpoint join now live in an exported resolveChatCompletionsEndpoint, which also makes the join testable — it wasn't before.

Coverage

New/changed lines are 100% covered (checked line-by-line against coverage/lcov.info), so this lands well clear of the 80% new-code threshold.

Two pre-existing gaps got closed along the way:

  • src/llm/openai.ts 24.4% → 37.6% — there was no tests/llm/openai.test.ts at all.
  • src/tools/file-context/handlers.ts 77.0% → 97.6% — the "includes importers section" test only asserted that the ## Imported By header rendered. vi.clearAllMocks() keeps queued mockResolvedValueOnce values, and leftovers from earlier tests meant readdir never returned any files, so the entire match loop it was supposed to exercise ran zero times. The new test resets the mocks and asserts the matches themselves.

Each former hot spot also gets a timing guard (20k spaces / 50k slashes must resolve in under a second) so a future rewrite can't quietly reintroduce the backtracking.

Verification

bun run typecheck   ✓
bun run build       ✓
bun run check       ✓ (7 pre-existing warnings, unchanged)
bun run test:coverage  ✓ 24 files, 303 passed | 7 todo

The SonarQube check on this PR will still fail on the 403 until the token is rotated.


Generated by Claude Code

All five S5852 "super-linear runtime due to backtracking" hotspots that
were holding SonarCloud's Security Hotspots Reviewed condition at 0%:

- The import scanner (`import\s+.*?from\s+['"](.+?)['"]|...`) was
  duplicated verbatim in the related-files and file-context handlers. The
  lazy `.*?` next to `\s+` gives the engine ambiguous ways to split a
  line, and these scanners run over every line of up to 500 files from an
  untrusted repository. Extracted to `src/tools/import-scan.ts` with each
  quantifier over a character set disjoint from what follows it, so the
  match is linear. It now also recognises side-effect imports
  (`import 'p'`) and re-exports, which the old pattern missed.
- `\s+as\s+` in the named-export-list parser: two `\s+` runs around a
  literal split whitespace ambiguously. Replaced with `\bas\b`.
- `replace(/\/+$/, '')` in the OpenAI base-URL join (twice): the anchored
  `+` retries from every position. Replaced with a scan, behind a new
  exported `resolveChatCompletionsEndpoint` so the join is testable.

Tests cover every changed line, including a timing guard on each former
hot spot. Along the way, the file-context importer test was only
asserting that the "Imported By" header rendered — leftover
`mockResolvedValueOnce` values from earlier tests meant the scan never
saw any files, so the whole match loop was untested. It now asserts the
matches themselves (handlers.ts 77% -> 98%).

ethanasm commented Aug 7, 2026

Copy link
Copy Markdown
Owner Author

CI status on 6b0bd33:

  • CI (lint → typecheck → build → test:coverage) — ✅ passed
  • SonarQube / scan — ❌ failed, on the blocker described in the PR body, not on this diff

The scan died in the same place as the previous run on main, before analysing anything:

04:25:18.405 INFO  Communicating with SonarQube Cloud
04:25:18.406 INFO  JRE provisioning: os[linux], arch[x86_64]
04:25:18.880 ERROR Failed to query JRE metadata: GET https://api.sonarcloud.io/analysis/jres?os=linux&arch=x86_64
                   failed with HTTP 403 Forbidden. Please check the property sonar.token
                   or the environment variable SONAR_TOKEN.

I'm not pushing anything for this one — it isn't a code failure. That endpoint returns 200 unauthenticated and 403 for a bad bearer token, so SONAR_TOKEN is set but rejected. The last green scan was 2026-02-11 and a token from around then has since expired.

This needs a token rotation, which only you can do: mint a new token in SonarCloud (My Account → Security), then replace SONAR_TOKEN under Settings → Secrets and variables → Actions. Re-run the check after that and the scan will actually analyse the code — at which point the five S5852 hotspots this PR removes are what decide the gate.

The coverage step did run before the scanner tried to authenticate, and it confirms the numbers claimed above landed — file-context/handlers.ts at 97.55%, related-files/handlers.ts at 99.49%, import-scan.ts at 100%.


Generated by Claude Code

@ethanasm
ethanasm merged commit 4e41aa7 into main Aug 7, 2026
2 of 3 checks passed
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