java-topology/whitepaper/vectors/compiler/tsc.rst
russell@unturf.com 0a580b313d undefect. CWE-407 — 63 sites patched across 27 ecosystems
Authors: russell@unturf.com · brackishbert@gmail.com · foxhop.net · TimeHexOn.com

Patches, unit tests, benchmarks, whitepaper, and outreach briefs.
Public domain — no copyright claimed. Use freely.
2026-03-26 17:11:57 -04:00

191 lines
5.6 KiB
ReStructuredText

TypeScript Compiler (tsc) — CWE-407 Analysis
============================================
.. contents:: :local:
Overview
--------
``tsc`` is the TypeScript compiler, which performs type checking, symbol resolution, and
export-star graph traversal. Three CWE-407 defect sites were found in ``checker.ts``, the
largest single source file in the compiler (54,000+ lines). **All three are now patched
in a single 120-line diff** (``defects/typescript/patch/ts-checker-set-visited.patch``).
The defects affect cycle detection in resolution chains, export-star symbol merging, and
symbol-table inheritance traversal.
Defect Sites
------------
ts-0001
~~~~~~~
**File:** ``src/compiler/checker.ts:11503``
**Pattern:**
.. code-block:: typescript
// Resolution cycle detection — linear scan of resolutionTargets array
function findResolutionCycleStartIndex(target: object, propertyName: TypeSystemPropertyName): number {
for (let i = resolutionTargets.length - 1; i >= 0; i--) {
if (resolutionTargets[i] === target &&
resolutionPropertyNames[i] === propertyName) {
return i; // O(depth) linear scan every call
}
}
return -1;
}
**Why this is O(n):** ``resolutionTargets`` is a plain array; finding a target requires scanning
from the end. Called once per symbol resolution: O(depth²) total for a chain of depth ``depth``.
**Complexity:** ``O(depth²)`` where depth = resolution stack depth
**Patch:**
.. code-block:: typescript
// Use a Map<object, Map<TypeSystemPropertyName, number>> for O(1) lookup
const resolutionTargetIndex = new Map<object, Map<TypeSystemPropertyName, number>>();
function findResolutionCycleStartIndex(target: object, propertyName: TypeSystemPropertyName): number {
return resolutionTargetIndex.get(target)?.get(propertyName) ?? -1;
}
**Data structure change:** ``object[]`` array + linear scan → ``Map<object, Map<...>>``
**Status:** **PATCHED**``defects/typescript/patch/ts-checker-set-visited.patch``
Benchmark Results
-----------------
.. TODO: benchmark pending patch
Complexity Proof
----------------
Let depth = maximum resolution stack depth. Each call to ``findResolutionCycleStartIndex``
scans an array of up to ``depth`` entries: O(depth). With O(depth) calls total during a
resolution chain: O(depth²). Map lookup is O(1) amortized: O(depth) total. QED.
References
----------
* Defect ticket: ``tools/tickets/defects/ts-0001.md``
ts-0002
~~~~~~~
**File:** ``src/compiler/checker.ts:5256``
**Pattern:**
.. code-block:: typescript
// Export-star symbol merging — pushIfUnique on visitedSymbols array
function getExportsOfModuleWorker(moduleSymbol: Symbol): Symbol[] {
const visitedSymbols: Symbol[] = [];
// ...
function visit(symbol: Symbol) {
if (!pushIfUnique(visitedSymbols, symbol)) return; // O(V) per call
// ... traverse exports
}
}
function pushIfUnique<T>(array: T[], toAdd: T): boolean {
if (array.includes(toAdd)) return false; // O(V) linear scan
array.push(toAdd);
return true;
}
**Why this is O(n):** ``array.includes()`` is O(|array|); called once per symbol in the
export-star graph; total is O(V²) where V = number of exported symbols.
**Complexity:** ``O(V²)`` where V = number of symbols in the export-star graph
**Patch:**
.. code-block:: typescript
function getExportsOfModuleWorker(moduleSymbol: Symbol): Symbol[] {
const visitedSymbols = new Set<Symbol>();
function visit(symbol: Symbol) {
if (visitedSymbols.has(symbol)) return; // O(1)
visitedSymbols.add(symbol);
// ... traverse exports
}
}
**Data structure change:** ``Symbol[]`` array + ``pushIfUnique````Set<Symbol>`` + ``has/add``
**Status:** **PATCHED**``defects/typescript/patch/ts-checker-set-visited.patch``
Benchmark Results
-----------------
.. TODO: benchmark pending patch
Complexity Proof
----------------
Let V = number of unique symbols in the export graph. ``pushIfUnique`` scans an array of up to V
elements: O(V) per call. With V calls total: O(V²). ``Set.has()`` is O(1): O(V) total. QED.
References
----------
* Defect ticket: ``tools/tickets/defects/ts-0002.md``
ts-0003
~~~~~~~
**File:** ``src/compiler/checker.ts:5763``
**Pattern:**
.. code-block:: typescript
// Symbol-table inheritance traversal — pushIfUnique on visitedSymbolTables
const visitedSymbolTables: SymbolTable[] = [];
function visit(symbolTable: SymbolTable) {
if (!pushIfUnique(visitedSymbolTables, symbolTable)) return; // O(depth) per call
// ... inherit symbols
}
**Why this is O(n):** Same ``pushIfUnique`` pattern as ts-0002; ``visitedSymbolTables`` is a
plain array scanned linearly on every call.
**Complexity:** ``O(depth²)`` where depth = symbol-table inheritance depth
**Patch:**
.. code-block:: typescript
const visitedSymbolTables = new Set<SymbolTable>();
function visit(symbolTable: SymbolTable) {
if (visitedSymbolTables.has(symbolTable)) return; // O(1)
visitedSymbolTables.add(symbolTable);
// ... inherit symbols
}
**Data structure change:** ``SymbolTable[]`` array + ``pushIfUnique````Set<SymbolTable>``
**Status:** **PATCHED**``defects/typescript/patch/ts-checker-set-visited.patch``
Benchmark Results
-----------------
.. TODO: benchmark pending patch
Complexity Proof
----------------
Identical argument to ts-0002. Let depth = inheritance chain depth. ``pushIfUnique`` costs
O(depth) per call; O(depth²) total. ``Set.has()`` costs O(1) per call; O(depth) total. QED.
References
----------
* Defect ticket: ``tools/tickets/defects/ts-0003.md``