Skip to content

Commit 1ae886e

Browse files
fix(core): remove polynomial-ReDoS regex from audioRelPathForSrc
CodeQL js/polynomial-redos: the lazy `.+?` followed by an optional trailing `[?#].*$` backtracks polynomially on crafted `/preview/...` inputs. Parse the preview-relative path with indexOf/slice instead, and strip the query/hash with a single linear char-class search. Behavior is unchanged for all preview/absolute/blob/data/bare inputs. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Co-authored-by: Miguel Ángel <miguel07alm@protonmail.com>
1 parent ec30986 commit 1ae886e

1 file changed

Lines changed: 14 additions & 3 deletions

File tree

packages/core/src/beats/beatFile.ts

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,20 @@ export function audioRelPathForSrc(src: string | null | undefined): string | nul
1313
if (!src) return null;
1414
// blob:/data: URLs have no stable identity across sessions — not persistable.
1515
if (/^(blob:|data:)/i.test(src)) return null;
16-
// Studio preview URLs: /api/projects/<id>/preview[/comp]/<relpath>
17-
const previewMatch = src.match(/\/preview\/(?:comp\/)?(.+?)(?:[?#].*)?$/);
18-
let rel: string | null = previewMatch ? decodeURIComponent(previewMatch[1]!) : null;
16+
// Studio preview URLs: /api/projects/<id>/preview[/comp]/<relpath>.
17+
// Parsed with indexOf/slice (not a regex) to avoid polynomial backtracking
18+
// on adversarial inputs (CodeQL js/polynomial-redos).
19+
let rel: string | null = null;
20+
const PREVIEW = "/preview/";
21+
const previewIdx = src.indexOf(PREVIEW);
22+
if (previewIdx !== -1) {
23+
let after = src.slice(previewIdx + PREVIEW.length);
24+
// Strip query/hash (single char class — linear, ReDoS-safe).
25+
const queryOrHash = after.search(/[?#]/);
26+
if (queryOrHash !== -1) after = after.slice(0, queryOrHash);
27+
if (after.startsWith("comp/")) after = after.slice("comp/".length);
28+
rel = after ? decodeURIComponent(after) : null;
29+
}
1930
if (!rel) {
2031
// Fall back to the FULL pathname (not just basename) so two files with the
2132
// same name in different folders don't collide on one beat file.

0 commit comments

Comments
 (0)