Skip to content

Commit c2d8cec

Browse files
committed
Fix ReDoS regex and strengthen worker dispatch guard
- src/global.js: replace scriptUrl regex /\w+:\/\/(.+?\/)*.+\.js/ with /\w+:\/\/(?:[^/\s]+\/)*[^/\s]*\.js/ to remove exponential backtracking (CodeQL ReDoS) - src/worker.js: validate the resolved handler with typeof func !== 'function' at the call site so the dynamic dispatch can never invoke an inherited or non-function value (CodeQL unvalidated dynamic method call) - build.mjs: drop unused fs/promises import in watch(); guard fs.watch recursive mode (unsupported on Linux) with a non-recursive fallback; retain FSWatcher references for process lifetime - Rebuild dist bundles
1 parent 60147b1 commit c2d8cec

5 files changed

Lines changed: 30 additions & 9 deletions

File tree

build.mjs

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -328,7 +328,6 @@ async function build() {
328328
}
329329

330330
async function watch() {
331-
const chokidar = await import("node:fs/promises");
332331
const { watch: fsWatch } = await import("node:fs");
333332
const all = new Set([...fullBuild, LICENSE_FILE]);
334333
let timer = null;
@@ -340,16 +339,35 @@ async function watch() {
340339
};
341340
await build();
342341
console.log("watching for changes...");
342+
343+
// Keep references to every FSWatcher so they are not garbage-collected
344+
// and stay active for the lifetime of the process.
345+
const watchers = [];
346+
343347
for (const f of all) {
344348
try {
345-
fsWatch(f, rebuild);
349+
watchers.push(fsWatch(f, rebuild));
346350
} catch {
347351
// file may not exist yet — that's fine
348352
}
349353
}
354+
350355
// Also watch the directories that contain source files so newly-added
351-
// files trigger rebuilds.
352-
fsWatch("src", { recursive: true }, rebuild);
356+
// files trigger rebuilds. fs.watch({ recursive: true }) is not supported
357+
// on all platforms (notably Linux), where it throws — fall back to a
358+
// non-recursive watch on the src directory in that case. The per-file
359+
// watchers above still cover every file in the build list either way.
360+
try {
361+
watchers.push(fsWatch("src", { recursive: true }, rebuild));
362+
} catch {
363+
try {
364+
watchers.push(fsWatch("src", rebuild));
365+
} catch {
366+
// src may not be watchable — per-file watchers still apply
367+
}
368+
}
369+
370+
return watchers;
353371
}
354372

355373
const args = process.argv.slice(2);

dist/msrcrypto.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ var scriptUrl = (function() {
8080
throw new Error();
8181
} catch (e) {
8282
if (e.stack) {
83-
var match = /\w+:\/\/(.+?\/)*.+\.js/.exec(e.stack);
83+
var match = /\w+:\/\/(?:[^/\s]+\/)*[^/\s]*\.js/.exec(e.stack);
8484
return (match && match.length > 0) ? match[0] : null;
8585
}
8686
}
@@ -866,7 +866,7 @@ var msrcryptoWorker = (function() {
866866

867867
var func = operations.get(operation, algorithmName);
868868

869-
if (!func) {
869+
if (typeof func !== "function") {
870870
throw new Error("unregistered algorithm.");
871871
}
872872

dist/msrcrypto.min.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/global.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ var scriptUrl = (function() {
2525
throw new Error();
2626
} catch (e) {
2727
if (e.stack) {
28-
var match = /\w+:\/\/(.+?\/)*.+\.js/.exec(e.stack);
28+
var match = /\w+:\/\/(?:[^/\s]+\/)*[^/\s]*\.js/.exec(e.stack);
2929
return (match && match.length > 0) ? match[0] : null;
3030
}
3131
}

src/worker.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,10 @@ var msrcryptoWorker = (function() {
4848

4949
var func = operations.get(operation, algorithmName);
5050

51-
if (!func) {
51+
// Explicit own-property + function-type validation at the call site
52+
// so the dynamically resolved handler can never dispatch to an
53+
// inherited Object.prototype member or a non-function value.
54+
if (typeof func !== "function") {
5255
throw new Error("unregistered algorithm.");
5356
}
5457

0 commit comments

Comments
 (0)