wave17: 562/240 — vim/qemu/tcl/nodejs/bun/httpd-0002/systemd/emacs
This commit is contained in:
parent
cce7ec653a
commit
dde5ec97fb
13 changed files with 887 additions and 4 deletions
64
defects/nodejs/patch/nodejs-0001-resolve-paths-dedup-set.md
Normal file
64
defects/nodejs/patch/nodejs-0001-resolve-paths-dedup-set.md
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
# nodejs-0001 — CWE-407 in Module._resolveFilename paths dedup
|
||||
|
||||
**Severity:** MEDIUM
|
||||
**File:** `lib/internal/modules/cjs/loader.js`
|
||||
**Symbol:** `Module._resolveFilename` — `ArrayPrototypeIncludes(paths, lookupPaths[j])`
|
||||
|
||||
## Defect
|
||||
|
||||
When `require.resolve(id, { paths: [...] })` is called with an explicit `paths`
|
||||
array of length P, the implementation deduplicates lookup paths using a linear
|
||||
scan of the accumulator array:
|
||||
|
||||
```js
|
||||
for (let i = 0; i < options.paths.length; i++) { // O(P)
|
||||
const lookupPaths = Module._resolveLookupPaths(request, fakeParent); // O(L)
|
||||
for (let j = 0; j < lookupPaths.length; j++) { // O(L)
|
||||
if (!ArrayPrototypeIncludes(paths, lookupPaths[j])) { // O(P*L) scan of accumulator
|
||||
ArrayPrototypePush(paths, lookupPaths[j]);
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
`_resolveLookupPaths` returns up to ~20 node_modules ancestor directories per
|
||||
input path. The accumulator `paths` grows to P×L entries. Each
|
||||
`ArrayPrototypeIncludes` call scans the entire accumulator: O(P×L) per check,
|
||||
called P×L times total → **O(P²×L²)** overall.
|
||||
|
||||
At P=50 input paths and L=10 lookup paths per entry, the accumulator reaches
|
||||
500 entries and the dedup loop performs ~250,000 comparisons instead of ~500.
|
||||
|
||||
## Fix
|
||||
|
||||
Replace the accumulator array + linear scan with a `Set` for O(1) membership:
|
||||
|
||||
```js
|
||||
const pathsSet = new Set();
|
||||
const paths = [];
|
||||
|
||||
for (let i = 0; i < options.paths.length; i++) {
|
||||
const path = options.paths[i];
|
||||
fakeParent.paths = Module._nodeModulePaths(path);
|
||||
const lookupPaths = Module._resolveLookupPaths(request, fakeParent);
|
||||
|
||||
for (let j = 0; j < lookupPaths.length; j++) {
|
||||
if (!pathsSet.has(lookupPaths[j])) { // O(1)
|
||||
pathsSet.add(lookupPaths[j]);
|
||||
ArrayPrototypePush(paths, lookupPaths[j]);
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Complexity
|
||||
|
||||
| Scenario | Before | After |
|
||||
|---|---|---|
|
||||
| P=50 paths, L=10 lookup paths each | O(P²×L²) = ~250k ops | O(P×L) = ~500 ops |
|
||||
| Ratio at P=50, L=10 | 500× | 1× |
|
||||
|
||||
## Source location
|
||||
|
||||
`lib/internal/modules/cjs/loader.js` — `Module._resolveFilename`, the
|
||||
`ArrayIsArray(options.paths)` / non-relative branch, lines ~1408–1414.
|
||||
Loading…
Add table
Add a link
Reference in a new issue