circle-ir 3.176.0 → 3.178.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/analysis/config-loader.d.ts.map +1 -1
- package/dist/analysis/config-loader.js +75 -0
- package/dist/analysis/config-loader.js.map +1 -1
- package/dist/analysis/constant-propagation/propagator.d.ts +1 -2
- package/dist/analysis/constant-propagation/propagator.d.ts.map +1 -1
- package/dist/analysis/constant-propagation/propagator.js +28 -30
- package/dist/analysis/constant-propagation/propagator.js.map +1 -1
- package/dist/analysis/dependency-versions.d.ts +102 -0
- package/dist/analysis/dependency-versions.d.ts.map +1 -0
- package/dist/analysis/dependency-versions.js +153 -0
- package/dist/analysis/dependency-versions.js.map +1 -0
- package/dist/analysis/dfg-walk.d.ts.map +1 -1
- package/dist/analysis/dfg-walk.js +28 -1
- package/dist/analysis/dfg-walk.js.map +1 -1
- package/dist/analysis/note-coalescer.d.ts +46 -0
- package/dist/analysis/note-coalescer.d.ts.map +1 -0
- package/dist/analysis/note-coalescer.js +106 -0
- package/dist/analysis/note-coalescer.js.map +1 -0
- package/dist/analysis/passes/deserialization-safety-gate-pass.d.ts +58 -0
- package/dist/analysis/passes/deserialization-safety-gate-pass.d.ts.map +1 -0
- package/dist/analysis/passes/deserialization-safety-gate-pass.js +122 -0
- package/dist/analysis/passes/deserialization-safety-gate-pass.js.map +1 -0
- package/dist/analysis/passes/sink-filter-pass.d.ts.map +1 -1
- package/dist/analysis/passes/sink-filter-pass.js +33 -2
- package/dist/analysis/passes/sink-filter-pass.js.map +1 -1
- package/dist/analysis/taint-matcher.d.ts.map +1 -1
- package/dist/analysis/taint-matcher.js +55 -33
- package/dist/analysis/taint-matcher.js.map +1 -1
- package/dist/analyzer.d.ts +34 -0
- package/dist/analyzer.d.ts.map +1 -1
- package/dist/analyzer.js +21 -1
- package/dist/analyzer.js.map +1 -1
- package/dist/browser/circle-ir.js +351 -47
- package/dist/core/circle-ir-core.cjs +197 -45
- package/dist/core/circle-ir-core.js +197 -45
- package/dist/core/extractors/calls.js +131 -1
- package/dist/core/extractors/calls.js.map +1 -1
- package/dist/core/extractors/cfg.d.ts.map +1 -1
- package/dist/core/extractors/cfg.js +17 -9
- package/dist/core/extractors/cfg.js.map +1 -1
- package/dist/types/index.d.ts +24 -0
- package/dist/types/index.d.ts.map +1 -1
- package/package.json +1 -1
|
@@ -7415,8 +7415,16 @@ function extractGoCallInfo(node) {
|
|
|
7415
7415
|
if (funcNode.type === "selector_expression") {
|
|
7416
7416
|
const operand = funcNode.childForFieldName("operand");
|
|
7417
7417
|
const field = funcNode.childForFieldName("field");
|
|
7418
|
-
receiver = operand ? getNodeText(operand) : null;
|
|
7419
7418
|
methodName = field ? getNodeText(field) : getNodeText(funcNode);
|
|
7419
|
+
if (operand) {
|
|
7420
|
+
const opText = getNodeText(operand);
|
|
7421
|
+
if (operand.type === "identifier") {
|
|
7422
|
+
const resolved = resolveGoLocalReceiverType(opText, node);
|
|
7423
|
+
receiver = resolved !== null ? resolved : opText;
|
|
7424
|
+
} else {
|
|
7425
|
+
receiver = opText;
|
|
7426
|
+
}
|
|
7427
|
+
}
|
|
7420
7428
|
} else if (funcNode.type === "identifier") {
|
|
7421
7429
|
methodName = getNodeText(funcNode);
|
|
7422
7430
|
} else {
|
|
@@ -7494,6 +7502,80 @@ function findGoEnclosingFunction(node) {
|
|
|
7494
7502
|
}
|
|
7495
7503
|
return null;
|
|
7496
7504
|
}
|
|
7505
|
+
function resolveGoLocalReceiverType(operandName, callNode) {
|
|
7506
|
+
let cur = callNode.parent;
|
|
7507
|
+
while (cur) {
|
|
7508
|
+
if (cur.type === "method_declaration") {
|
|
7509
|
+
const receiver = cur.childForFieldName("receiver");
|
|
7510
|
+
if (receiver) {
|
|
7511
|
+
const t = extractGoParamTypeForName(receiver, operandName);
|
|
7512
|
+
if (t !== null) return t;
|
|
7513
|
+
}
|
|
7514
|
+
const params = cur.childForFieldName("parameters");
|
|
7515
|
+
if (params) {
|
|
7516
|
+
const t = extractGoParamTypeForName(params, operandName);
|
|
7517
|
+
if (t !== null) return t;
|
|
7518
|
+
}
|
|
7519
|
+
return null;
|
|
7520
|
+
}
|
|
7521
|
+
if (cur.type === "function_declaration" || cur.type === "func_literal") {
|
|
7522
|
+
const params = cur.childForFieldName("parameters");
|
|
7523
|
+
if (params) {
|
|
7524
|
+
const t = extractGoParamTypeForName(params, operandName);
|
|
7525
|
+
if (t !== null) return t;
|
|
7526
|
+
}
|
|
7527
|
+
return null;
|
|
7528
|
+
}
|
|
7529
|
+
cur = cur.parent;
|
|
7530
|
+
}
|
|
7531
|
+
return null;
|
|
7532
|
+
}
|
|
7533
|
+
function extractGoParamTypeForName(list, operandName) {
|
|
7534
|
+
for (let i2 = 0; i2 < list.namedChildCount; i2++) {
|
|
7535
|
+
const child = list.namedChild(i2);
|
|
7536
|
+
if (!child || child.type !== "parameter_declaration") continue;
|
|
7537
|
+
const typeNode = child.childForFieldName("type");
|
|
7538
|
+
if (!typeNode) continue;
|
|
7539
|
+
let matched = false;
|
|
7540
|
+
for (let j = 0; j < child.namedChildCount; j++) {
|
|
7541
|
+
const c = child.namedChild(j);
|
|
7542
|
+
if (!c) continue;
|
|
7543
|
+
if (c.type === "identifier" && getNodeText(c) === operandName) {
|
|
7544
|
+
matched = true;
|
|
7545
|
+
break;
|
|
7546
|
+
}
|
|
7547
|
+
if (c.type === "identifier_list") {
|
|
7548
|
+
for (let k = 0; k < c.namedChildCount; k++) {
|
|
7549
|
+
const id = c.namedChild(k);
|
|
7550
|
+
if (id && id.type === "identifier" && getNodeText(id) === operandName) {
|
|
7551
|
+
matched = true;
|
|
7552
|
+
break;
|
|
7553
|
+
}
|
|
7554
|
+
}
|
|
7555
|
+
if (matched) break;
|
|
7556
|
+
}
|
|
7557
|
+
}
|
|
7558
|
+
if (!matched) continue;
|
|
7559
|
+
return extractGoTypeLastSegment(typeNode);
|
|
7560
|
+
}
|
|
7561
|
+
return null;
|
|
7562
|
+
}
|
|
7563
|
+
function extractGoTypeLastSegment(typeNode) {
|
|
7564
|
+
if (typeNode.type === "pointer_type") {
|
|
7565
|
+
const inner = typeNode.namedChild(0);
|
|
7566
|
+
return inner ? extractGoTypeLastSegment(inner) : null;
|
|
7567
|
+
}
|
|
7568
|
+
if (typeNode.type === "qualified_type") {
|
|
7569
|
+
const name2 = typeNode.childForFieldName("name");
|
|
7570
|
+
if (name2) return getNodeText(name2);
|
|
7571
|
+
const last = typeNode.namedChild(typeNode.namedChildCount - 1);
|
|
7572
|
+
return last ? getNodeText(last) : null;
|
|
7573
|
+
}
|
|
7574
|
+
if (typeNode.type === "type_identifier" || typeNode.type === "identifier") {
|
|
7575
|
+
return getNodeText(typeNode);
|
|
7576
|
+
}
|
|
7577
|
+
return null;
|
|
7578
|
+
}
|
|
7497
7579
|
|
|
7498
7580
|
// src/core/extractors/imports.ts
|
|
7499
7581
|
function detectLanguage2(tree) {
|
|
@@ -8242,10 +8324,10 @@ function buildCFG(tree, language, cache) {
|
|
|
8242
8324
|
const allEdges = [];
|
|
8243
8325
|
let blockIdCounter = 0;
|
|
8244
8326
|
if (effectiveLanguage === "bash") {
|
|
8245
|
-
return buildBashCFG(tree, blockIdCounter);
|
|
8327
|
+
return buildBashCFG(tree, blockIdCounter, cache);
|
|
8246
8328
|
}
|
|
8247
8329
|
if (effectiveLanguage === "go") {
|
|
8248
|
-
return buildGoCFG(tree, blockIdCounter);
|
|
8330
|
+
return buildGoCFG(tree, blockIdCounter, cache);
|
|
8249
8331
|
}
|
|
8250
8332
|
if (isJavaScript) {
|
|
8251
8333
|
const functions = [
|
|
@@ -8602,11 +8684,11 @@ function processSwitchStatement(stmt, startId, blocks, edges, isJavaScript) {
|
|
|
8602
8684
|
nextId: currentId
|
|
8603
8685
|
};
|
|
8604
8686
|
}
|
|
8605
|
-
function buildBashCFG(tree, startId) {
|
|
8687
|
+
function buildBashCFG(tree, startId, cache) {
|
|
8606
8688
|
const allBlocks = [];
|
|
8607
8689
|
const allEdges = [];
|
|
8608
8690
|
let blockIdCounter = startId;
|
|
8609
|
-
const functions =
|
|
8691
|
+
const functions = getNodesFromCache(tree.rootNode, "function_definition", cache);
|
|
8610
8692
|
for (const func2 of functions) {
|
|
8611
8693
|
const body2 = func2.childForFieldName("body");
|
|
8612
8694
|
if (!body2) continue;
|
|
@@ -8723,12 +8805,12 @@ function isStatement(node, isJavaScript) {
|
|
|
8723
8805
|
]);
|
|
8724
8806
|
return isJavaScript ? jsStatementTypes.has(node.type) : javaStatementTypes.has(node.type);
|
|
8725
8807
|
}
|
|
8726
|
-
function buildGoCFG(tree, blockIdCounter) {
|
|
8808
|
+
function buildGoCFG(tree, blockIdCounter, cache) {
|
|
8727
8809
|
const allBlocks = [];
|
|
8728
8810
|
const allEdges = [];
|
|
8729
8811
|
const functions = [
|
|
8730
|
-
...
|
|
8731
|
-
...
|
|
8812
|
+
...getNodesFromCache(tree.rootNode, "function_declaration", cache),
|
|
8813
|
+
...getNodesFromCache(tree.rootNode, "method_declaration", cache)
|
|
8732
8814
|
];
|
|
8733
8815
|
for (const func2 of functions) {
|
|
8734
8816
|
const body2 = func2.childForFieldName("body");
|
|
@@ -10642,6 +10724,59 @@ var OPEN_REDIRECT_FRAMEWORK_SINKS = [
|
|
|
10642
10724
|
{ method: "Redirect", class: "Context", type: "open_redirect", cwe: "CWE-601", severity: "medium", arg_positions: [1], languages: ["go"] },
|
|
10643
10725
|
{ method: "Redirect", class: "Ctx", type: "open_redirect", cwe: "CWE-601", severity: "medium", arg_positions: [0], languages: ["go"] }
|
|
10644
10726
|
];
|
|
10727
|
+
var DESERIALIZATION_FRAMEWORK_SINKS = [
|
|
10728
|
+
// --- Python: stdlib + popular third-party --------------------------------
|
|
10729
|
+
// pickle: known-dangerous, any unpickle on untrusted bytes is RCE.
|
|
10730
|
+
{ method: "loads", class: "pickle", type: "deserialization", cwe: "CWE-502", severity: "critical", arg_positions: [0], languages: ["python"] },
|
|
10731
|
+
{ method: "load", class: "pickle", type: "deserialization", cwe: "CWE-502", severity: "critical", arg_positions: [0], languages: ["python"] },
|
|
10732
|
+
// cPickle alias (Python 2 name, still around in older codebases).
|
|
10733
|
+
{ method: "loads", class: "cPickle", type: "deserialization", cwe: "CWE-502", severity: "critical", arg_positions: [0], languages: ["python"] },
|
|
10734
|
+
{ method: "load", class: "cPickle", type: "deserialization", cwe: "CWE-502", severity: "critical", arg_positions: [0], languages: ["python"] },
|
|
10735
|
+
// marshal: stdlib code-object deserializer. Loading a tainted bytestring
|
|
10736
|
+
// as a code object followed by `exec` is arbitrary-code execution.
|
|
10737
|
+
{ method: "loads", class: "marshal", type: "deserialization", cwe: "CWE-502", severity: "critical", arg_positions: [0], languages: ["python"] },
|
|
10738
|
+
{ method: "load", class: "marshal", type: "deserialization", cwe: "CWE-502", severity: "critical", arg_positions: [0], languages: ["python"] },
|
|
10739
|
+
// dill: pickle superset — same RCE profile.
|
|
10740
|
+
{ method: "loads", class: "dill", type: "deserialization", cwe: "CWE-502", severity: "critical", arg_positions: [0], languages: ["python"] },
|
|
10741
|
+
{ method: "load", class: "dill", type: "deserialization", cwe: "CWE-502", severity: "critical", arg_positions: [0], languages: ["python"] },
|
|
10742
|
+
// jsonpickle: JSON wrapper around pickle — trusts `py/object` marker.
|
|
10743
|
+
{ method: "decode", class: "jsonpickle", type: "deserialization", cwe: "CWE-502", severity: "critical", arg_positions: [0], languages: ["python"] },
|
|
10744
|
+
// --- Go: encoding/gob + yaml.Unmarshal -----------------------------------
|
|
10745
|
+
// gob.NewDecoder(r).Decode(&v): tainted io.Reader → arbitrary Go values.
|
|
10746
|
+
{ method: "Decode", class: "Decoder", type: "deserialization", cwe: "CWE-502", severity: "high", arg_positions: [0], languages: ["go"] },
|
|
10747
|
+
// gopkg.in/yaml.v2 + v3 top-level function; interface{} target is unsafe.
|
|
10748
|
+
{ method: "Unmarshal", class: "yaml", type: "deserialization", cwe: "CWE-502", severity: "high", arg_positions: [0], languages: ["go"] },
|
|
10749
|
+
// --- JS/TS: node-serialize ------------------------------------------------
|
|
10750
|
+
// Known-dangerous — accepts embedded IIFE that runs during deserialize.
|
|
10751
|
+
{ method: "unserialize", class: "nodeSerialize", type: "deserialization", cwe: "CWE-502", severity: "critical", arg_positions: [0], languages: ["javascript", "typescript"] }
|
|
10752
|
+
];
|
|
10753
|
+
var NOSQL_FRAMEWORK_SINKS = [
|
|
10754
|
+
// --- Python: pymongo Collection ------------------------------------------
|
|
10755
|
+
// Every filter-taking Collection method; filter arg[0] is the query dict.
|
|
10756
|
+
{ method: "find", class: "Collection", type: "nosql_injection", cwe: "CWE-943", severity: "high", arg_positions: [0], languages: ["python"] },
|
|
10757
|
+
{ method: "find_one", class: "Collection", type: "nosql_injection", cwe: "CWE-943", severity: "high", arg_positions: [0], languages: ["python"] },
|
|
10758
|
+
{ method: "aggregate", class: "Collection", type: "nosql_injection", cwe: "CWE-943", severity: "high", arg_positions: [0], languages: ["python"] },
|
|
10759
|
+
{ method: "update_one", class: "Collection", type: "nosql_injection", cwe: "CWE-943", severity: "high", arg_positions: [0, 1], languages: ["python"] },
|
|
10760
|
+
{ method: "update_many", class: "Collection", type: "nosql_injection", cwe: "CWE-943", severity: "high", arg_positions: [0, 1], languages: ["python"] },
|
|
10761
|
+
{ method: "delete_one", class: "Collection", type: "nosql_injection", cwe: "CWE-943", severity: "high", arg_positions: [0], languages: ["python"] },
|
|
10762
|
+
{ method: "delete_many", class: "Collection", type: "nosql_injection", cwe: "CWE-943", severity: "high", arg_positions: [0], languages: ["python"] },
|
|
10763
|
+
{ method: "count_documents", class: "Collection", type: "nosql_injection", cwe: "CWE-943", severity: "high", arg_positions: [0], languages: ["python"] },
|
|
10764
|
+
// --- Java: Spring Data MongoTemplate + native MongoCollection ------------
|
|
10765
|
+
{ method: "find", class: "MongoTemplate", type: "nosql_injection", cwe: "CWE-943", severity: "high", arg_positions: [0], languages: ["java"] },
|
|
10766
|
+
{ method: "findOne", class: "MongoTemplate", type: "nosql_injection", cwe: "CWE-943", severity: "high", arg_positions: [0], languages: ["java"] },
|
|
10767
|
+
{ method: "findAll", class: "MongoTemplate", type: "nosql_injection", cwe: "CWE-943", severity: "high", arg_positions: [0], languages: ["java"] },
|
|
10768
|
+
{ method: "find", class: "MongoCollection", type: "nosql_injection", cwe: "CWE-943", severity: "high", arg_positions: [0], languages: ["java"] },
|
|
10769
|
+
{ method: "aggregate", class: "MongoCollection", type: "nosql_injection", cwe: "CWE-943", severity: "high", arg_positions: [0], languages: ["java"] },
|
|
10770
|
+
// --- Go: go.mongodb.org/mongo-driver Collection --------------------------
|
|
10771
|
+
// These fire once Go local-receiver type resolution lands (see
|
|
10772
|
+
// taint-matcher.ts + #240 ship 2 Go receiver work). Same gate as the
|
|
10773
|
+
// gin/fiber Ctx sinks in ship 1.
|
|
10774
|
+
{ method: "Find", class: "Collection", type: "nosql_injection", cwe: "CWE-943", severity: "high", arg_positions: [1], languages: ["go"] },
|
|
10775
|
+
{ method: "FindOne", class: "Collection", type: "nosql_injection", cwe: "CWE-943", severity: "high", arg_positions: [1], languages: ["go"] },
|
|
10776
|
+
{ method: "UpdateOne", class: "Collection", type: "nosql_injection", cwe: "CWE-943", severity: "high", arg_positions: [1, 2], languages: ["go"] },
|
|
10777
|
+
{ method: "UpdateMany", class: "Collection", type: "nosql_injection", cwe: "CWE-943", severity: "high", arg_positions: [1, 2], languages: ["go"] },
|
|
10778
|
+
{ method: "DeleteOne", class: "Collection", type: "nosql_injection", cwe: "CWE-943", severity: "high", arg_positions: [1], languages: ["go"] }
|
|
10779
|
+
];
|
|
10645
10780
|
var TRUST_BOUNDARY_FRAMEWORK_SINKS = [
|
|
10646
10781
|
// --- Python: Django cache write -----------------------------------------
|
|
10647
10782
|
{ method: "set", class: "cache", type: "trust_boundary", cwe: "CWE-501", severity: "medium", arg_positions: [1], languages: ["python"] },
|
|
@@ -12325,7 +12460,12 @@ var DEFAULT_SINKS = [
|
|
|
12325
12460
|
// keep the DEFAULT_SINKS literal within TypeScript's union-type
|
|
12326
12461
|
// inference complexity limit (TS2590). See ~lines 661-752.
|
|
12327
12462
|
...OPEN_REDIRECT_FRAMEWORK_SINKS,
|
|
12328
|
-
...TRUST_BOUNDARY_FRAMEWORK_SINKS
|
|
12463
|
+
...TRUST_BOUNDARY_FRAMEWORK_SINKS,
|
|
12464
|
+
// cognium-dev #240 ship 2 — extended framework sinks for
|
|
12465
|
+
// deserialization (CWE-502) and nosql_injection (CWE-943). Same
|
|
12466
|
+
// pattern: constants defined near the ship-1 blocks and spread here.
|
|
12467
|
+
...DESERIALIZATION_FRAMEWORK_SINKS,
|
|
12468
|
+
...NOSQL_FRAMEWORK_SINKS
|
|
12329
12469
|
];
|
|
12330
12470
|
var DEFAULT_SANITIZERS = [
|
|
12331
12471
|
// SQL Injection - proper parameter binding sanitizes input
|
|
@@ -12845,11 +12985,11 @@ function attachSourceLineCode(sources, sinks, code) {
|
|
|
12845
12985
|
}
|
|
12846
12986
|
function findSources(calls, types, patterns, sourceLines, language) {
|
|
12847
12987
|
const sources = [];
|
|
12988
|
+
const patternsForLanguage = language === void 0 ? patterns : patterns.filter(
|
|
12989
|
+
(p) => !p.languages || p.languages.length === 0 || p.languages.includes(language)
|
|
12990
|
+
);
|
|
12848
12991
|
for (const call of calls) {
|
|
12849
|
-
for (const pattern of
|
|
12850
|
-
if (pattern.languages && pattern.languages.length > 0 && language !== void 0 && !pattern.languages.includes(language)) {
|
|
12851
|
-
continue;
|
|
12852
|
-
}
|
|
12992
|
+
for (const pattern of patternsForLanguage) {
|
|
12853
12993
|
if (matchesSourcePattern(call, pattern)) {
|
|
12854
12994
|
sources.push({
|
|
12855
12995
|
type: pattern.type,
|
|
@@ -12865,11 +13005,8 @@ function findSources(calls, types, patterns, sourceLines, language) {
|
|
|
12865
13005
|
for (const type of types) {
|
|
12866
13006
|
for (const method of type.methods) {
|
|
12867
13007
|
for (const param of method.parameters) {
|
|
12868
|
-
for (const pattern of
|
|
13008
|
+
for (const pattern of patternsForLanguage) {
|
|
12869
13009
|
if (pattern.annotation && pattern.param_tainted) {
|
|
12870
|
-
if (pattern.languages && pattern.languages.length > 0 && language !== void 0 && !pattern.languages.includes(language)) {
|
|
12871
|
-
continue;
|
|
12872
|
-
}
|
|
12873
13010
|
if (matchesAnnotation(param.annotations, pattern.annotation)) {
|
|
12874
13011
|
const paramLine = param.line ?? method.start_line;
|
|
12875
13012
|
sources.push({
|
|
@@ -12888,11 +13025,8 @@ function findSources(calls, types, patterns, sourceLines, language) {
|
|
|
12888
13025
|
}
|
|
12889
13026
|
for (const type of types) {
|
|
12890
13027
|
for (const method of type.methods) {
|
|
12891
|
-
for (const pattern of
|
|
13028
|
+
for (const pattern of patternsForLanguage) {
|
|
12892
13029
|
if (!pattern.method_annotation) continue;
|
|
12893
|
-
if (pattern.languages && pattern.languages.length > 0 && language !== void 0 && !pattern.languages.includes(language)) {
|
|
12894
|
-
continue;
|
|
12895
|
-
}
|
|
12896
13030
|
if (!matchesAnnotation(method.annotations, pattern.method_annotation)) continue;
|
|
12897
13031
|
for (const param of method.parameters) {
|
|
12898
13032
|
const paramLine = param.line ?? method.start_line;
|
|
@@ -13514,9 +13648,12 @@ function isSafeJinjaRenderCall(call, pattern, language, sourceLines) {
|
|
|
13514
13648
|
return false;
|
|
13515
13649
|
}
|
|
13516
13650
|
function findSinks(calls, patterns, typeHierarchy, language, sourceLines, types) {
|
|
13651
|
+
const patternsForLanguage = language === void 0 ? patterns : patterns.filter(
|
|
13652
|
+
(p) => !p.languages || p.languages.length === 0 || p.languages.includes(language)
|
|
13653
|
+
);
|
|
13517
13654
|
const sinkMap = /* @__PURE__ */ new Map();
|
|
13518
13655
|
for (const call of calls) {
|
|
13519
|
-
for (const pattern of
|
|
13656
|
+
for (const pattern of patternsForLanguage) {
|
|
13520
13657
|
if (matchesSinkPattern(call, pattern, typeHierarchy, language)) {
|
|
13521
13658
|
if (isParameterizedQueryCall(call, pattern)) {
|
|
13522
13659
|
continue;
|
|
@@ -13934,7 +14071,20 @@ function matchesAnnotation(annotations, targetAnnotation) {
|
|
|
13934
14071
|
}
|
|
13935
14072
|
return false;
|
|
13936
14073
|
}
|
|
14074
|
+
var RECEIVER_MIGHT_BE_CLASS_CACHE = /* @__PURE__ */ new Map();
|
|
14075
|
+
var RECEIVER_MIGHT_BE_CLASS_CACHE_CAP = 1e4;
|
|
13937
14076
|
function receiverMightBeClass(receiver, className) {
|
|
14077
|
+
const key = receiver + "\0" + className;
|
|
14078
|
+
const cached = RECEIVER_MIGHT_BE_CLASS_CACHE.get(key);
|
|
14079
|
+
if (cached !== void 0) return cached;
|
|
14080
|
+
const result = receiverMightBeClassImpl(receiver, className);
|
|
14081
|
+
if (RECEIVER_MIGHT_BE_CLASS_CACHE.size >= RECEIVER_MIGHT_BE_CLASS_CACHE_CAP) {
|
|
14082
|
+
RECEIVER_MIGHT_BE_CLASS_CACHE.clear();
|
|
14083
|
+
}
|
|
14084
|
+
RECEIVER_MIGHT_BE_CLASS_CACHE.set(key, result);
|
|
14085
|
+
return result;
|
|
14086
|
+
}
|
|
14087
|
+
function receiverMightBeClassImpl(receiver, className) {
|
|
13938
14088
|
if (className.startsWith("*") && className.length > 1) {
|
|
13939
14089
|
const suffix = className.slice(1).toLowerCase();
|
|
13940
14090
|
let simpleReceiver = receiver;
|
|
@@ -14638,8 +14788,15 @@ var CodeGraph = class {
|
|
|
14638
14788
|
};
|
|
14639
14789
|
|
|
14640
14790
|
// src/analysis/dfg-walk.ts
|
|
14791
|
+
var walkBackwardDefsMemo = /* @__PURE__ */ new WeakMap();
|
|
14641
14792
|
function walkBackwardDefs(startDefId, chainsByToDef, defById, options = {}) {
|
|
14642
14793
|
const maxHops = options.maxHops ?? 32;
|
|
14794
|
+
let perFile = walkBackwardDefsMemo.get(chainsByToDef);
|
|
14795
|
+
if (perFile !== void 0) {
|
|
14796
|
+
const key = `${startDefId}|${maxHops}`;
|
|
14797
|
+
const hit = perFile.get(key);
|
|
14798
|
+
if (hit !== void 0) return hit;
|
|
14799
|
+
}
|
|
14643
14800
|
const visited = /* @__PURE__ */ new Set();
|
|
14644
14801
|
const lines = /* @__PURE__ */ new Set();
|
|
14645
14802
|
let hopCapReached = false;
|
|
@@ -14671,7 +14828,13 @@ function walkBackwardDefs(startDefId, chainsByToDef, defById, options = {}) {
|
|
|
14671
14828
|
queue.push(fromId);
|
|
14672
14829
|
}
|
|
14673
14830
|
}
|
|
14674
|
-
|
|
14831
|
+
const result = { visited, lines, hopCapReached };
|
|
14832
|
+
if (perFile === void 0) {
|
|
14833
|
+
perFile = /* @__PURE__ */ new Map();
|
|
14834
|
+
walkBackwardDefsMemo.set(chainsByToDef, perFile);
|
|
14835
|
+
}
|
|
14836
|
+
perFile.set(`${startDefId}|${maxHops}`, result);
|
|
14837
|
+
return result;
|
|
14675
14838
|
}
|
|
14676
14839
|
|
|
14677
14840
|
// src/analysis/sanitizer-index.ts
|
|
@@ -15654,7 +15817,7 @@ var ConstantPropagator = class _ConstantPropagator {
|
|
|
15654
15817
|
this.constructorParamPositions.clear();
|
|
15655
15818
|
this.safePatternFieldsCache = null;
|
|
15656
15819
|
this.isTaintedExpressionCache = null;
|
|
15657
|
-
this.
|
|
15820
|
+
const prePassMethods = this.collectClassFieldsAndMethods(tree.rootNode);
|
|
15658
15821
|
for (const methodName of sanitizerMethods) {
|
|
15659
15822
|
this.methodReturnsSanitized.add(methodName);
|
|
15660
15823
|
}
|
|
@@ -15662,7 +15825,7 @@ var ConstantPropagator = class _ConstantPropagator {
|
|
|
15662
15825
|
this.source,
|
|
15663
15826
|
(name2) => this.lookupSymbol(name2)
|
|
15664
15827
|
);
|
|
15665
|
-
this.analyzeMethodReturns(
|
|
15828
|
+
this.analyzeMethodReturns(prePassMethods);
|
|
15666
15829
|
this.seedPythonModuleConstants(tree.rootNode);
|
|
15667
15830
|
this.visit(tree.rootNode);
|
|
15668
15831
|
this.refineTaintFromConstants();
|
|
@@ -15732,8 +15895,7 @@ var ConstantPropagator = class _ConstantPropagator {
|
|
|
15732
15895
|
/**
|
|
15733
15896
|
* Pre-pass: Analyze all methods to detect those that always return constants or sanitized values.
|
|
15734
15897
|
*/
|
|
15735
|
-
analyzeMethodReturns(
|
|
15736
|
-
const methods = this.findAllMethods(root);
|
|
15898
|
+
analyzeMethodReturns(methods) {
|
|
15737
15899
|
for (const method of methods) {
|
|
15738
15900
|
const methodName = this.getMethodName(method);
|
|
15739
15901
|
if (!methodName) continue;
|
|
@@ -15997,11 +16159,15 @@ var ConstantPropagator = class _ConstantPropagator {
|
|
|
15997
16159
|
* Collect all class field names (instance/static variables declared at class level).
|
|
15998
16160
|
* These are variables declared directly in the class body, not inside methods.
|
|
15999
16161
|
*/
|
|
16000
|
-
|
|
16162
|
+
collectClassFieldsAndMethods(root) {
|
|
16163
|
+
const methods = [];
|
|
16001
16164
|
const stack = [root];
|
|
16002
16165
|
while (stack.length > 0) {
|
|
16003
16166
|
const n = stack.pop();
|
|
16004
16167
|
if (!n) continue;
|
|
16168
|
+
if (n.type === "method_declaration" || n.type === "function_declaration") {
|
|
16169
|
+
methods.push(n);
|
|
16170
|
+
}
|
|
16005
16171
|
if (n.type === "class_body") {
|
|
16006
16172
|
for (const child of n.children) {
|
|
16007
16173
|
if (child.type === "field_declaration") {
|
|
@@ -16015,14 +16181,15 @@ var ConstantPropagator = class _ConstantPropagator {
|
|
|
16015
16181
|
}
|
|
16016
16182
|
}
|
|
16017
16183
|
}
|
|
16018
|
-
stack.push(child);
|
|
16184
|
+
if (child) stack.push(child);
|
|
16019
16185
|
}
|
|
16020
16186
|
continue;
|
|
16021
16187
|
}
|
|
16022
16188
|
for (const child of n.children) {
|
|
16023
|
-
stack.push(child);
|
|
16189
|
+
if (child) stack.push(child);
|
|
16024
16190
|
}
|
|
16025
16191
|
}
|
|
16192
|
+
return methods;
|
|
16026
16193
|
}
|
|
16027
16194
|
/**
|
|
16028
16195
|
* Sprint 9 #55 — seed the symbol table with Python module-level constant
|
|
@@ -16180,21 +16347,6 @@ var ConstantPropagator = class _ConstantPropagator {
|
|
|
16180
16347
|
this.symbols.set(name2, value);
|
|
16181
16348
|
}
|
|
16182
16349
|
}
|
|
16183
|
-
findAllMethods(node) {
|
|
16184
|
-
const methods = [];
|
|
16185
|
-
const stack = [node];
|
|
16186
|
-
while (stack.length > 0) {
|
|
16187
|
-
const n = stack.pop();
|
|
16188
|
-
if (!n) continue;
|
|
16189
|
-
if (n.type === "method_declaration" || n.type === "function_declaration") {
|
|
16190
|
-
methods.push(n);
|
|
16191
|
-
}
|
|
16192
|
-
for (const child of n.children) {
|
|
16193
|
-
if (child) stack.push(child);
|
|
16194
|
-
}
|
|
16195
|
-
}
|
|
16196
|
-
return methods;
|
|
16197
|
-
}
|
|
16198
16350
|
getMethodName(method) {
|
|
16199
16351
|
const nameNode = method.childForFieldName("name");
|
|
16200
16352
|
if (nameNode) {
|