java-topology/whitepaper/outreach/jasmine.md
russell@unturf.com d67ec93a5d test-frameworks wave 3: vitest + testng + jasmine + libcheck (4 patches)
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.
2026-04-23 08:54:44 -04:00

2.1 KiB
Raw Blame History

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.