vitest-0001: coverage-v8 coverage.result.find inside merged.result.forEach -> Map<url, result> lookup. Bench: 824x at N=M=10000 coverage entries. testng-0001: DynamicGraph.toDot freeNodes.contains inside two for-each loops -> Map<T, String> color lookup via getOrDefault. Bench: 64x at N=2000. jasmine-0001: SpyRegistry.spyOnAllFunctions propertiesToSkip.indexOf inside Array.filter + .concat growth across D prototype levels -> Set.has + O(1) growth. Bench: 61x at D=10, P=300. check-0001: libcheck suite_tcase linear strcmp scan over tclst List -> parallel hashtable for O(1) lookup amortized. Bench: 117x at N=1000. Shipped as design sketch; full integration requires companion hashtable. Also ships whitepaper/outreach/test-harness-survey.md documenting 14 clean-scan frameworks across Clojure, OCaml, Haskell, Erlang, Go, F#, Julia, Shell, Lua, JS. Scope covered 61 targets across 30+ languages. UNDF IDs: 1292 (check), 1293 (jasmine), 1294 (testng), 1295 (vitest). All 12 tests pass.
2.1 KiB
Jasmine — CWE-407 Disclosure Brief
Project: Jasmine (jasmine/jasmine) Disclosure date: 2026-04-23 Severity: MEDIUM Speedup: 61x at D=10, P=300 (SpyRegistry prototype walk), confirmed by benchmark Status: patch-ready, 1 patch plus test suite, benchmarks complete
Summary
jasmine-core's spyOnAllFunctions walks an object's prototype chain, filtering properties at each level against already-seen entries via Array.indexOf and growing the skip list via Array.concat. For chain depth D and P properties per level, cost scales as O(D × P²).
Frameworks that build deep class hierarchies (Angular services, Mongoose models, Ember objects) hit this pattern hard when the test suite calls spyOnAllFunctions on class instances.
The Defects
jasmine-0001 (MOAD-0001 — MEDIUM): src/core/SpyRegistry.js:203-221
let propertiesToSkip = [];
while (pointer && (...)) {
properties = getProps(pointer, includeNonEnumerable);
properties = properties.filter(function(prop) {
return propertiesToSkip.indexOf(prop) === -1; // O(P) per prop
});
propertiesToSkip = propertiesToSkip.concat(properties); // grows
...
pointer = Object.getPrototypeOf(pointer);
}
Fix: Replace propertiesToSkip Array with a Set. Filter lookup and growth drop to O(1). Total cost becomes O(D × P).
| Benchmark (D levels, P per level) | defective | fixed | speedup |
|---|---|---|---|
| D=5, P=200 | 5.62ms | 0.29ms | 19.6x |
| D=8, P=200 | 13.81ms | 0.53ms | 26.1x |
| D=10, P=300 | 54.05ms | 0.88ms | 61.2x |
Per-test overhead in microseconds to milliseconds on average objects; scales dramatically on framework-heavy object graphs.
Scanner Evidence
unmoad detects the pattern at HIGH severity. Trigger + clean fixture pair in tests/integration/fixtures/moad_0001/.
Patches
jasmine-0001-spyregistry-spyonallfunctions-indexof.patch(UNDF-2026-000001293)
Full test + bench suite at defects/jasmine/ in the java-topology research repo.