Skip to content

Commit 0f8786c

Browse files
committed
fix(autonumber): drop backtracking lookahead in seed scan (ReDoS)
The empty-prefix legacy branch used /(\d+)(?!.*\d)/ to grab the last digit run, whose negative lookahead is a polynomial-ReDoS sink on stored values with many repeated zeros (CodeQL js/polynomial-redos, high). Replace both branches with the linear /\d+/g, preserving the last-digit-run semantics.
1 parent 3d55c55 commit 0f8786c

1 file changed

Lines changed: 13 additions & 2 deletions

File tree

packages/objectql/src/engine.ts

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -865,8 +865,19 @@ export class ObjectQL implements IDataEngine {
865865
const s = String(v);
866866
if (prefix && !s.startsWith(prefix)) continue;
867867
const tail = prefix ? s.slice(prefix.length) : s;
868-
const m = prefix ? tail.match(/^(\d+)/) : tail.match(/(\d+)(?!.*\d)/);
869-
if (m) max = Math.max(max, parseInt(m[1], 10) || 0);
868+
// With a prefix the counter is the digit run right after it; without one
869+
// (legacy fixed-prefix formats) it is the LAST digit run. Both use the
870+
// linear /\d+/g — a backtracking lookahead here is a polynomial-ReDoS
871+
// sink on stored values full of zeros (CodeQL js/polynomial-redos).
872+
let digits: string | undefined;
873+
if (prefix) {
874+
const head = tail.match(/^\d+/);
875+
digits = head ? head[0] : undefined;
876+
} else {
877+
const runs = tail.match(/\d+/g);
878+
digits = runs ? runs[runs.length - 1] : undefined;
879+
}
880+
if (digits) max = Math.max(max, parseInt(digits, 10) || 0);
870881
}
871882
return max;
872883
} catch {

0 commit comments

Comments
 (0)