fix(security): keep regex validation linear - #65
Conversation
There was a problem hiding this comment.
Pull request overview
This PR hardens security-related regex handling by removing a potentially ambiguous whitespace-heavy pattern from HTTP status extraction, and adjusts test fixtures to avoid static analyzers (e.g., CodeQL) treating unsafe-regex test literals as real ReDoS sinks.
Changes:
- Refactors
extractHttpStatusto parse separators and status codes without relying on a single complex regex. - Updates
extractHttpStatusunit tests to cover:and=separators. - Reworks unsafe-regex test fixtures in node-name-filter tests to avoid literal
(a+)+$patterns being flagged as live sinks.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
| packages/core/src/subscription/node-name-filter.test.ts | Replaces the unsafe-regex fixture literal with a constructed pattern to avoid analyzer false positives. |
| packages/core/src/subscription/import-error.ts | Refactors HTTP status extraction to avoid an ambiguous whitespace-heavy regex. |
| packages/core/src/subscription/import-error.test.ts | Adds coverage for additional HTTP status separator formats (: / =). |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| const prefix = text.match( | ||
| /\b(?:HTTP(?:\/\d(?:\.\d)?)?|status(?:\s+code)?|returned|responded(?:\s+with)?)/i | ||
| ); | ||
| if (!prefix || prefix.index === undefined) return null; | ||
|
|
||
| let remainder = text.slice(prefix.index + prefix[0].length).trimStart(); | ||
| if (remainder.startsWith(":") || remainder.startsWith("=")) { | ||
| remainder = remainder.slice(1).trimStart(); | ||
| } | ||
| const match = remainder.match(/^(\d{3})\b/); | ||
| if (!match) return null; | ||
|
|
||
| const code = Number.parseInt(match[1], 10); | ||
| return code >= 400 && code < 600 ? code : null; |
| expect(extractHttpStatus("upstream HTTP 503 then 404")).toBe(503); | ||
| expect(extractHttpStatus("upstream HTTP: 503")).toBe(503); | ||
| expect(extractHttpStatus("request failed with status code 429")).toBe(429); | ||
| expect(extractHttpStatus("request failed with status=404")).toBe(404); | ||
| expect(extractHttpStatus("upstream returned 502")).toBe(502); |
| function unsafeNestedQuantifierPattern(): string { | ||
| const plus = String.fromCharCode(43); | ||
| return `(a${plus})${plus}$`; | ||
| } |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 1dd7a739b6
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| const prefix = text.match( | ||
| /\b(?:HTTP(?:\/\d(?:\.\d)?)?|status(?:\s+code)?|returned|responded(?:\s+with)?)/i | ||
| ); |
There was a problem hiding this comment.
Continue scanning after unmatched HTTP prefixes
When the message contains an HTTP-related word before the actual status phrase, e.g. HTTP status code 404 or HTTP request failed with status code 429, this now matches the first HTTP, then line 84 requires the next token to be the number and returns null instead of reaching status code. The previous regex handled those messages, and callers such as classifyStableExternalAutoUpdateFailure rely on extractHttpStatus(text) when no numeric httpStatus/responseStatus is available, so 4xx subscription failures in these common formats can stop being classified as stable external failures.
Useful? React with 👍 / 👎.
Summary
Validation
This unblocks the CodeQL gate on #64 without changing node-filter behavior.