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.
This commit is contained in:
commit
0a580b313d
70422 changed files with 17213626 additions and 0 deletions
120
defects/typescript/patch/ts-checker-set-visited.patch
Normal file
120
defects/typescript/patch/ts-checker-set-visited.patch
Normal file
|
|
@ -0,0 +1,120 @@
|
|||
diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts
|
||||
index 0567712..5509a04 100644
|
||||
--- a/src/compiler/checker.ts
|
||||
+++ b/src/compiler/checker.ts
|
||||
@@ -2348,6 +2348,8 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
|
||||
var resolutionTargets: TypeSystemEntity[] = [];
|
||||
var resolutionResults: boolean[] = [];
|
||||
var resolutionPropertyNames: TypeSystemPropertyName[] = [];
|
||||
+ // CWE-407 fix: O(1) lookup for findResolutionCycleStartIndex (was O(depth) scan)
|
||||
+ var resolutionTargetsSet = new Map<TypeSystemEntity, Map<TypeSystemPropertyName, number>>();
|
||||
var resolutionStart = 0;
|
||||
var inVarianceComputation = false;
|
||||
|
||||
@@ -5227,7 +5229,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
|
||||
}
|
||||
|
||||
function getExportsOfModuleWorker(moduleSymbol: Symbol) {
|
||||
- const visitedSymbols: Symbol[] = [];
|
||||
+ const visitedSymbols = new Set<Symbol>(); // CWE-407 fix: was Symbol[] (O(n) pushIfUnique)
|
||||
let typeOnlyExportStarMap: Map<__String, ExportDeclaration & { readonly isTypeOnly: true; readonly moduleSpecifier: Expression; }> | undefined;
|
||||
const nonTypeOnlyNames = new Set<__String>();
|
||||
|
||||
@@ -5253,9 +5255,10 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
|
||||
// again with 'export *' will override the type-onlyness of its exports.
|
||||
symbol.exports.forEach((_, name) => nonTypeOnlyNames.add(name));
|
||||
}
|
||||
- if (!(symbol && symbol.exports && pushIfUnique(visitedSymbols, symbol))) {
|
||||
- return;
|
||||
- }
|
||||
+ // CWE-407 fix: O(1) Set.has replaces O(n) pushIfUnique on array
|
||||
+ if (!(symbol && symbol.exports)) return;
|
||||
+ if (visitedSymbols.has(symbol)) return;
|
||||
+ visitedSymbols.add(symbol);
|
||||
const symbols = new Map(symbol.exports);
|
||||
|
||||
// All export * declarations are collected in an __export symbol by the binder
|
||||
@@ -5734,7 +5737,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
|
||||
return rightMeaning === SymbolFlags.Value ? SymbolFlags.Value : SymbolFlags.Namespace;
|
||||
}
|
||||
|
||||
- function getAccessibleSymbolChain(symbol: Symbol | undefined, enclosingDeclaration: Node | undefined, meaning: SymbolFlags, useOnlyExternalAliasing: boolean, visitedSymbolTablesMap = new Map<SymbolId, SymbolTable[]>()): Symbol[] | undefined {
|
||||
+ function getAccessibleSymbolChain(symbol: Symbol | undefined, enclosingDeclaration: Node | undefined, meaning: SymbolFlags, useOnlyExternalAliasing: boolean, visitedSymbolTablesMap = new Map<SymbolId, Set<SymbolTable>>()): Symbol[] | undefined {
|
||||
if (!(symbol && !isPropertyOrMethodDeclarationSymbol(symbol))) {
|
||||
return undefined;
|
||||
}
|
||||
@@ -5750,7 +5753,8 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
|
||||
const id = getSymbolId(symbol);
|
||||
let visitedSymbolTables = visitedSymbolTablesMap.get(id);
|
||||
if (!visitedSymbolTables) {
|
||||
- visitedSymbolTablesMap.set(id, visitedSymbolTables = []);
|
||||
+ // CWE-407 fix: Set<SymbolTable> for O(1) has/add/delete (was SymbolTable[] + pushIfUnique)
|
||||
+ visitedSymbolTablesMap.set(id, visitedSymbolTables = new Set());
|
||||
}
|
||||
const result = forEachSymbolTableInScope(enclosingDeclaration, getAccessibleSymbolChainFromSymbolTable);
|
||||
cache.set(key, result);
|
||||
@@ -5760,12 +5764,13 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
|
||||
* @param {ignoreQualification} boolean Set when a symbol is being looked for through the exports of another symbol (meaning we have a route to qualify it already)
|
||||
*/
|
||||
function getAccessibleSymbolChainFromSymbolTable(symbols: SymbolTable, ignoreQualification?: boolean, isLocalNameLookup?: boolean): Symbol[] | undefined {
|
||||
- if (!pushIfUnique(visitedSymbolTables!, symbols)) {
|
||||
+ // CWE-407 fix: O(1) Set.has/add/delete replaces O(n) pushIfUnique + pop on array
|
||||
+ if (visitedSymbolTables!.has(symbols)) {
|
||||
return undefined;
|
||||
}
|
||||
-
|
||||
+ visitedSymbolTables!.add(symbols);
|
||||
const result = trySymbolTable(symbols, ignoreQualification, isLocalNameLookup);
|
||||
- visitedSymbolTables!.pop();
|
||||
+ visitedSymbolTables!.delete(symbols);
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -11497,19 +11502,27 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
|
||||
resolutionTargets.push(target);
|
||||
resolutionResults.push(/*items*/ true);
|
||||
resolutionPropertyNames.push(propertyName);
|
||||
+ // CWE-407 fix: record index for O(1) cycle detection
|
||||
+ let propMap = resolutionTargetsSet.get(target);
|
||||
+ if (!propMap) resolutionTargetsSet.set(target, propMap = new Map());
|
||||
+ propMap.set(propertyName, resolutionTargets.length - 1);
|
||||
return true;
|
||||
}
|
||||
|
||||
function findResolutionCycleStartIndex(target: TypeSystemEntity, propertyName: TypeSystemPropertyName): number {
|
||||
- for (let i = resolutionTargets.length - 1; i >= resolutionStart; i--) {
|
||||
+ // CWE-407 fix: O(1) map lookup replaces O(depth) linear scan for membership test
|
||||
+ const propMap = resolutionTargetsSet.get(target);
|
||||
+ const idx = propMap?.get(propertyName);
|
||||
+ if (idx === undefined || idx < resolutionStart) {
|
||||
+ return -1;
|
||||
+ }
|
||||
+ // Verify no resolved entry sits above idx that would invalidate the cycle
|
||||
+ for (let i = resolutionTargets.length - 1; i > idx; i--) {
|
||||
if (resolutionTargetHasProperty(resolutionTargets[i], resolutionPropertyNames[i])) {
|
||||
return -1;
|
||||
}
|
||||
- if (resolutionTargets[i] === target && resolutionPropertyNames[i] === propertyName) {
|
||||
- return i;
|
||||
- }
|
||||
}
|
||||
- return -1;
|
||||
+ return idx;
|
||||
}
|
||||
|
||||
function resolutionTargetHasProperty(target: TypeSystemEntity, propertyName: TypeSystemPropertyName): boolean {
|
||||
@@ -11541,8 +11554,12 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
|
||||
* be true if no circularities were detected, or false if a circularity was found.
|
||||
*/
|
||||
function popTypeResolution(): boolean {
|
||||
- resolutionTargets.pop();
|
||||
- resolutionPropertyNames.pop();
|
||||
+ const target = resolutionTargets.pop()!;
|
||||
+ const propertyName = resolutionPropertyNames.pop()!;
|
||||
+ // CWE-407 fix: keep map in sync
|
||||
+ const propMap = resolutionTargetsSet.get(target)!;
|
||||
+ propMap.delete(propertyName);
|
||||
+ if (propMap.size === 0) resolutionTargetsSet.delete(target);
|
||||
return resolutionResults.pop()!;
|
||||
}
|
||||
|
||||
Loading…
Add table
Add a link
Reference in a new issue