circle-ir 4.7.1 → 4.8.2
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 +79 -2
- package/dist/analysis/config-loader.js.map +1 -1
- package/dist/analysis/findings.js +1 -1
- package/dist/analysis/findings.js.map +1 -1
- package/dist/analysis/passes/insecure-deserialization-config-pass.d.ts +2 -0
- package/dist/analysis/passes/insecure-deserialization-config-pass.d.ts.map +1 -1
- package/dist/analysis/passes/insecure-deserialization-config-pass.js +45 -2
- package/dist/analysis/passes/insecure-deserialization-config-pass.js.map +1 -1
- package/dist/analysis/passes/language-sources-pass.d.ts.map +1 -1
- package/dist/analysis/passes/language-sources-pass.js +11 -1
- package/dist/analysis/passes/language-sources-pass.js.map +1 -1
- package/dist/analysis/passes/scan-secrets-pass.d.ts.map +1 -1
- package/dist/analysis/passes/scan-secrets-pass.js +10 -0
- package/dist/analysis/passes/scan-secrets-pass.js.map +1 -1
- package/dist/analysis/taint-matcher.d.ts.map +1 -1
- package/dist/analysis/taint-matcher.js +80 -0
- package/dist/analysis/taint-matcher.js.map +1 -1
- package/dist/analyzer.d.ts.map +1 -1
- package/dist/analyzer.js +14 -0
- package/dist/analyzer.js.map +1 -1
- package/dist/browser/circle-ir.js +320 -47
- package/dist/core/circle-ir-core.cjs +256 -43
- package/dist/core/circle-ir-core.js +256 -43
- package/dist/core/extractors/calls.js +17 -3
- 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 +140 -40
- package/dist/core/extractors/cfg.js.map +1 -1
- package/package.json +1 -1
|
@@ -5980,13 +5980,21 @@ function extractCSharpCalls(tree, cache) {
|
|
|
5980
5980
|
const left = asn.childForFieldName("left");
|
|
5981
5981
|
if (left?.type !== "member_access_expression") continue;
|
|
5982
5982
|
const nameNode = left.childForFieldName("name");
|
|
5983
|
-
if (!nameNode
|
|
5983
|
+
if (!nameNode) continue;
|
|
5984
|
+
const propName = getNodeText(nameNode);
|
|
5985
|
+
const exprNode = left.childForFieldName("expression");
|
|
5986
|
+
if (propName === "Filter") {
|
|
5987
|
+
const recv = exprNode ? getNodeText(exprNode) : null;
|
|
5988
|
+
const recvType = recv ? typeMap.get(recv) : void 0;
|
|
5989
|
+
if (recvType !== "DirectorySearcher") continue;
|
|
5990
|
+
} else if (propName !== "CommandText") {
|
|
5991
|
+
continue;
|
|
5992
|
+
}
|
|
5984
5993
|
const right = asn.childForFieldName("right");
|
|
5985
5994
|
if (!right) continue;
|
|
5986
5995
|
const rhsText = getNodeText(right);
|
|
5987
|
-
const exprNode = left.childForFieldName("expression");
|
|
5988
5996
|
calls.push({
|
|
5989
|
-
method_name:
|
|
5997
|
+
method_name: propName,
|
|
5990
5998
|
receiver: exprNode ? getNodeText(exprNode) : null,
|
|
5991
5999
|
receiver_type: null,
|
|
5992
6000
|
receiver_type_fqn: null,
|
|
@@ -8552,6 +8560,9 @@ function buildCFG(tree, language, cache) {
|
|
|
8552
8560
|
if (effectiveLanguage === "go") {
|
|
8553
8561
|
return buildGoCFG(tree, blockIdCounter, cache);
|
|
8554
8562
|
}
|
|
8563
|
+
if (effectiveLanguage === "csharp") {
|
|
8564
|
+
return buildCSharpCFG(tree, blockIdCounter, cache);
|
|
8565
|
+
}
|
|
8555
8566
|
if (isJavaScript) {
|
|
8556
8567
|
const functions = [
|
|
8557
8568
|
...getNodesFromCache(tree.rootNode, "function_declaration", cache),
|
|
@@ -8564,7 +8575,7 @@ function buildCFG(tree, language, cache) {
|
|
|
8564
8575
|
const body2 = func2.childForFieldName("body");
|
|
8565
8576
|
if (!body2) continue;
|
|
8566
8577
|
if (body2.type === "statement_block") {
|
|
8567
|
-
const { blocks, edges, nextId } = buildMethodCFG(body2, blockIdCounter,
|
|
8578
|
+
const { blocks, edges, nextId } = buildMethodCFG(body2, blockIdCounter, "js");
|
|
8568
8579
|
allBlocks.push(...blocks);
|
|
8569
8580
|
allEdges.push(...edges);
|
|
8570
8581
|
blockIdCounter = nextId;
|
|
@@ -8586,7 +8597,7 @@ function buildCFG(tree, language, cache) {
|
|
|
8586
8597
|
for (const method of methods) {
|
|
8587
8598
|
const body2 = method.childForFieldName("body");
|
|
8588
8599
|
if (!body2) continue;
|
|
8589
|
-
const { blocks, edges, nextId } = buildMethodCFG(body2, blockIdCounter,
|
|
8600
|
+
const { blocks, edges, nextId } = buildMethodCFG(body2, blockIdCounter, "java");
|
|
8590
8601
|
allBlocks.push(...blocks);
|
|
8591
8602
|
allEdges.push(...edges);
|
|
8592
8603
|
blockIdCounter = nextId;
|
|
@@ -8594,7 +8605,28 @@ function buildCFG(tree, language, cache) {
|
|
|
8594
8605
|
}
|
|
8595
8606
|
return { blocks: allBlocks, edges: allEdges };
|
|
8596
8607
|
}
|
|
8597
|
-
function
|
|
8608
|
+
function buildCSharpCFG(tree, blockIdCounter, cache) {
|
|
8609
|
+
const allBlocks = [];
|
|
8610
|
+
const allEdges = [];
|
|
8611
|
+
const containers = [
|
|
8612
|
+
...getNodesFromCache(tree.rootNode, "method_declaration", cache),
|
|
8613
|
+
...getNodesFromCache(tree.rootNode, "constructor_declaration", cache),
|
|
8614
|
+
...getNodesFromCache(tree.rootNode, "destructor_declaration", cache),
|
|
8615
|
+
...getNodesFromCache(tree.rootNode, "operator_declaration", cache),
|
|
8616
|
+
...getNodesFromCache(tree.rootNode, "local_function_statement", cache),
|
|
8617
|
+
...getNodesFromCache(tree.rootNode, "accessor_declaration", cache)
|
|
8618
|
+
];
|
|
8619
|
+
for (const container of containers) {
|
|
8620
|
+
const body2 = container.childForFieldName("body");
|
|
8621
|
+
if (!body2 || body2.type !== "block") continue;
|
|
8622
|
+
const { blocks, edges, nextId } = buildMethodCFG(body2, blockIdCounter, "csharp");
|
|
8623
|
+
allBlocks.push(...blocks);
|
|
8624
|
+
allEdges.push(...edges);
|
|
8625
|
+
blockIdCounter = nextId;
|
|
8626
|
+
}
|
|
8627
|
+
return { blocks: allBlocks, edges: allEdges };
|
|
8628
|
+
}
|
|
8629
|
+
function buildMethodCFG(body2, startId, dialect) {
|
|
8598
8630
|
const blocks = [];
|
|
8599
8631
|
const edges = [];
|
|
8600
8632
|
let currentId = startId;
|
|
@@ -8605,7 +8637,7 @@ function buildMethodCFG(body2, startId, isJavaScript) {
|
|
|
8605
8637
|
end_line: body2.startPosition.row + 1
|
|
8606
8638
|
};
|
|
8607
8639
|
blocks.push(entryBlock);
|
|
8608
|
-
const result = processStatements(body2, currentId, blocks, edges,
|
|
8640
|
+
const result = processStatements(body2, currentId, blocks, edges, dialect);
|
|
8609
8641
|
currentId = result.nextId;
|
|
8610
8642
|
if (result.entryId !== -1) {
|
|
8611
8643
|
edges.push({
|
|
@@ -8637,15 +8669,15 @@ function buildMethodCFG(body2, startId, isJavaScript) {
|
|
|
8637
8669
|
}
|
|
8638
8670
|
return { blocks, edges, nextId: currentId };
|
|
8639
8671
|
}
|
|
8640
|
-
function processStatements(container, startId, blocks, edges,
|
|
8672
|
+
function processStatements(container, startId, blocks, edges, dialect) {
|
|
8641
8673
|
let currentId = startId;
|
|
8642
8674
|
let firstBlockId = -1;
|
|
8643
8675
|
let lastExitIds = [];
|
|
8644
8676
|
for (let i2 = 0; i2 < container.childCount; i2++) {
|
|
8645
8677
|
const stmt = container.child(i2);
|
|
8646
8678
|
if (!stmt) continue;
|
|
8647
|
-
if (!isStatement(stmt,
|
|
8648
|
-
const result = processStatement(stmt, currentId, blocks, edges,
|
|
8679
|
+
if (!isStatement(stmt, dialect)) continue;
|
|
8680
|
+
const result = processStatement(stmt, currentId, blocks, edges, dialect);
|
|
8649
8681
|
currentId = result.nextId;
|
|
8650
8682
|
if (firstBlockId === -1) {
|
|
8651
8683
|
firstBlockId = result.entryId;
|
|
@@ -8666,31 +8698,49 @@ function processStatements(container, startId, blocks, edges, isJavaScript) {
|
|
|
8666
8698
|
nextId: currentId
|
|
8667
8699
|
};
|
|
8668
8700
|
}
|
|
8669
|
-
function processStatement(stmt, startId, blocks, edges,
|
|
8701
|
+
function processStatement(stmt, startId, blocks, edges, dialect) {
|
|
8670
8702
|
switch (stmt.type) {
|
|
8671
8703
|
case "if_statement":
|
|
8672
|
-
return processIfStatement(stmt, startId, blocks, edges,
|
|
8704
|
+
return processIfStatement(stmt, startId, blocks, edges, dialect);
|
|
8673
8705
|
case "for_statement":
|
|
8674
8706
|
case "enhanced_for_statement":
|
|
8675
8707
|
case "for_in_statement":
|
|
8676
8708
|
case "for_of_statement":
|
|
8677
|
-
|
|
8709
|
+
case "foreach_statement":
|
|
8710
|
+
return processForStatement(stmt, startId, blocks, edges, dialect);
|
|
8678
8711
|
case "while_statement":
|
|
8679
|
-
return processWhileStatement(stmt, startId, blocks, edges,
|
|
8712
|
+
return processWhileStatement(stmt, startId, blocks, edges, dialect);
|
|
8680
8713
|
case "do_statement":
|
|
8681
|
-
return processDoWhileStatement(stmt, startId, blocks, edges,
|
|
8714
|
+
return processDoWhileStatement(stmt, startId, blocks, edges, dialect);
|
|
8682
8715
|
case "try_statement":
|
|
8683
|
-
return processTryStatement(stmt, startId, blocks, edges,
|
|
8716
|
+
return processTryStatement(stmt, startId, blocks, edges, dialect);
|
|
8684
8717
|
case "switch_expression":
|
|
8685
8718
|
case "switch_statement":
|
|
8686
|
-
return processSwitchStatement(stmt, startId, blocks, edges,
|
|
8719
|
+
return processSwitchStatement(stmt, startId, blocks, edges, dialect);
|
|
8720
|
+
// C# scoped blocks add no branching — flow through the inner block so its
|
|
8721
|
+
// nested statements are still captured.
|
|
8722
|
+
case "using_statement":
|
|
8723
|
+
case "lock_statement":
|
|
8724
|
+
case "checked_statement":
|
|
8725
|
+
case "unsafe_statement": {
|
|
8726
|
+
const inner = stmt.childForFieldName("body") ?? lastBlockChild(stmt);
|
|
8727
|
+
if (inner) return processStatement(inner, startId, blocks, edges, dialect);
|
|
8728
|
+
return processSimpleStatement(stmt, startId, blocks);
|
|
8729
|
+
}
|
|
8687
8730
|
case "block":
|
|
8688
8731
|
case "statement_block":
|
|
8689
|
-
return processStatements(stmt, startId, blocks, edges,
|
|
8732
|
+
return processStatements(stmt, startId, blocks, edges, dialect);
|
|
8690
8733
|
default:
|
|
8691
8734
|
return processSimpleStatement(stmt, startId, blocks);
|
|
8692
8735
|
}
|
|
8693
8736
|
}
|
|
8737
|
+
function lastBlockChild(node) {
|
|
8738
|
+
for (let i2 = node.childCount - 1; i2 >= 0; i2--) {
|
|
8739
|
+
const c = node.child(i2);
|
|
8740
|
+
if (c && c.type === "block") return c;
|
|
8741
|
+
}
|
|
8742
|
+
return null;
|
|
8743
|
+
}
|
|
8694
8744
|
function processSimpleStatement(stmt, startId, blocks) {
|
|
8695
8745
|
const block = {
|
|
8696
8746
|
id: startId,
|
|
@@ -8705,7 +8755,7 @@ function processSimpleStatement(stmt, startId, blocks) {
|
|
|
8705
8755
|
nextId: startId + 1
|
|
8706
8756
|
};
|
|
8707
8757
|
}
|
|
8708
|
-
function processIfStatement(stmt, startId, blocks, edges,
|
|
8758
|
+
function processIfStatement(stmt, startId, blocks, edges, dialect) {
|
|
8709
8759
|
let currentId = startId;
|
|
8710
8760
|
const condBlock = {
|
|
8711
8761
|
id: currentId++,
|
|
@@ -8717,7 +8767,7 @@ function processIfStatement(stmt, startId, blocks, edges, isJavaScript) {
|
|
|
8717
8767
|
const exitIds = [];
|
|
8718
8768
|
const consequence = stmt.childForFieldName("consequence");
|
|
8719
8769
|
if (consequence) {
|
|
8720
|
-
const thenResult = processStatement(consequence, currentId, blocks, edges,
|
|
8770
|
+
const thenResult = processStatement(consequence, currentId, blocks, edges, dialect);
|
|
8721
8771
|
currentId = thenResult.nextId;
|
|
8722
8772
|
edges.push({
|
|
8723
8773
|
from: condBlock.id,
|
|
@@ -8728,7 +8778,7 @@ function processIfStatement(stmt, startId, blocks, edges, isJavaScript) {
|
|
|
8728
8778
|
}
|
|
8729
8779
|
const alternative = stmt.childForFieldName("alternative");
|
|
8730
8780
|
if (alternative) {
|
|
8731
|
-
const elseResult = processStatement(alternative, currentId, blocks, edges,
|
|
8781
|
+
const elseResult = processStatement(alternative, currentId, blocks, edges, dialect);
|
|
8732
8782
|
currentId = elseResult.nextId;
|
|
8733
8783
|
edges.push({
|
|
8734
8784
|
from: condBlock.id,
|
|
@@ -8745,7 +8795,7 @@ function processIfStatement(stmt, startId, blocks, edges, isJavaScript) {
|
|
|
8745
8795
|
nextId: currentId
|
|
8746
8796
|
};
|
|
8747
8797
|
}
|
|
8748
|
-
function processForStatement(stmt, startId, blocks, edges,
|
|
8798
|
+
function processForStatement(stmt, startId, blocks, edges, dialect) {
|
|
8749
8799
|
let currentId = startId;
|
|
8750
8800
|
const loopBlock = {
|
|
8751
8801
|
id: currentId++,
|
|
@@ -8756,7 +8806,7 @@ function processForStatement(stmt, startId, blocks, edges, isJavaScript) {
|
|
|
8756
8806
|
blocks.push(loopBlock);
|
|
8757
8807
|
const body2 = stmt.childForFieldName("body");
|
|
8758
8808
|
if (body2) {
|
|
8759
|
-
const bodyResult = processStatement(body2, currentId, blocks, edges,
|
|
8809
|
+
const bodyResult = processStatement(body2, currentId, blocks, edges, dialect);
|
|
8760
8810
|
currentId = bodyResult.nextId;
|
|
8761
8811
|
edges.push({
|
|
8762
8812
|
from: loopBlock.id,
|
|
@@ -8778,16 +8828,16 @@ function processForStatement(stmt, startId, blocks, edges, isJavaScript) {
|
|
|
8778
8828
|
nextId: currentId
|
|
8779
8829
|
};
|
|
8780
8830
|
}
|
|
8781
|
-
function processWhileStatement(stmt, startId, blocks, edges,
|
|
8782
|
-
return processForStatement(stmt, startId, blocks, edges,
|
|
8831
|
+
function processWhileStatement(stmt, startId, blocks, edges, dialect) {
|
|
8832
|
+
return processForStatement(stmt, startId, blocks, edges, dialect);
|
|
8783
8833
|
}
|
|
8784
|
-
function processDoWhileStatement(stmt, startId, blocks, edges,
|
|
8834
|
+
function processDoWhileStatement(stmt, startId, blocks, edges, dialect) {
|
|
8785
8835
|
let currentId = startId;
|
|
8786
8836
|
const body2 = stmt.childForFieldName("body");
|
|
8787
8837
|
let bodyEntryId = currentId;
|
|
8788
8838
|
let bodyExitIds = [];
|
|
8789
8839
|
if (body2) {
|
|
8790
|
-
const bodyResult = processStatement(body2, currentId, blocks, edges,
|
|
8840
|
+
const bodyResult = processStatement(body2, currentId, blocks, edges, dialect);
|
|
8791
8841
|
currentId = bodyResult.nextId;
|
|
8792
8842
|
bodyEntryId = bodyResult.entryId;
|
|
8793
8843
|
bodyExitIds = bodyResult.exitIds;
|
|
@@ -8817,13 +8867,13 @@ function processDoWhileStatement(stmt, startId, blocks, edges, isJavaScript) {
|
|
|
8817
8867
|
nextId: currentId
|
|
8818
8868
|
};
|
|
8819
8869
|
}
|
|
8820
|
-
function processTryStatement(stmt, startId, blocks, edges,
|
|
8870
|
+
function processTryStatement(stmt, startId, blocks, edges, dialect) {
|
|
8821
8871
|
let currentId = startId;
|
|
8822
8872
|
const exitIds = [];
|
|
8823
8873
|
const body2 = stmt.childForFieldName("body");
|
|
8824
8874
|
let tryEntryId = -1;
|
|
8825
8875
|
if (body2) {
|
|
8826
|
-
const bodyResult = processStatements(body2, currentId, blocks, edges,
|
|
8876
|
+
const bodyResult = processStatements(body2, currentId, blocks, edges, dialect);
|
|
8827
8877
|
currentId = bodyResult.nextId;
|
|
8828
8878
|
tryEntryId = bodyResult.entryId;
|
|
8829
8879
|
exitIds.push(...bodyResult.exitIds);
|
|
@@ -8833,7 +8883,7 @@ function processTryStatement(stmt, startId, blocks, edges, isJavaScript) {
|
|
|
8833
8883
|
if (child?.type === "catch_clause") {
|
|
8834
8884
|
const catchBody = child.childForFieldName("body");
|
|
8835
8885
|
if (catchBody) {
|
|
8836
|
-
const catchResult = processStatements(catchBody, currentId, blocks, edges,
|
|
8886
|
+
const catchResult = processStatements(catchBody, currentId, blocks, edges, dialect);
|
|
8837
8887
|
currentId = catchResult.nextId;
|
|
8838
8888
|
if (tryEntryId !== -1) {
|
|
8839
8889
|
edges.push({
|
|
@@ -8846,9 +8896,18 @@ function processTryStatement(stmt, startId, blocks, edges, isJavaScript) {
|
|
|
8846
8896
|
}
|
|
8847
8897
|
}
|
|
8848
8898
|
}
|
|
8849
|
-
|
|
8899
|
+
let finallyClause = stmt.childForFieldName("finally") ?? stmt.childForFieldName("finalizer");
|
|
8900
|
+
if (!finallyClause && dialect === "csharp") {
|
|
8901
|
+
for (let i2 = 0; i2 < stmt.childCount; i2++) {
|
|
8902
|
+
const child = stmt.child(i2);
|
|
8903
|
+
if (child?.type === "finally_clause") {
|
|
8904
|
+
finallyClause = lastBlockChild(child);
|
|
8905
|
+
break;
|
|
8906
|
+
}
|
|
8907
|
+
}
|
|
8908
|
+
}
|
|
8850
8909
|
if (finallyClause) {
|
|
8851
|
-
const finallyResult = processStatements(finallyClause, currentId, blocks, edges,
|
|
8910
|
+
const finallyResult = processStatements(finallyClause, currentId, blocks, edges, dialect);
|
|
8852
8911
|
currentId = finallyResult.nextId;
|
|
8853
8912
|
for (const exitId of exitIds) {
|
|
8854
8913
|
edges.push({
|
|
@@ -8869,7 +8928,7 @@ function processTryStatement(stmt, startId, blocks, edges, isJavaScript) {
|
|
|
8869
8928
|
nextId: currentId
|
|
8870
8929
|
};
|
|
8871
8930
|
}
|
|
8872
|
-
function processSwitchStatement(stmt, startId, blocks, edges,
|
|
8931
|
+
function processSwitchStatement(stmt, startId, blocks, edges, dialect) {
|
|
8873
8932
|
let currentId = startId;
|
|
8874
8933
|
const switchBlock = {
|
|
8875
8934
|
id: currentId++,
|
|
@@ -8881,11 +8940,11 @@ function processSwitchStatement(stmt, startId, blocks, edges, isJavaScript) {
|
|
|
8881
8940
|
const exitIds = [];
|
|
8882
8941
|
const body2 = stmt.childForFieldName("body");
|
|
8883
8942
|
if (body2) {
|
|
8884
|
-
const caseTypes =
|
|
8943
|
+
const caseTypes = dialect === "js" ? ["switch_case", "switch_default"] : dialect === "csharp" ? ["switch_section"] : ["switch_block_statement_group", "switch_rule"];
|
|
8885
8944
|
for (let i2 = 0; i2 < body2.childCount; i2++) {
|
|
8886
8945
|
const child = body2.child(i2);
|
|
8887
8946
|
if (child && caseTypes.includes(child.type)) {
|
|
8888
|
-
const caseResult = processStatements(child, currentId, blocks, edges,
|
|
8947
|
+
const caseResult = processStatements(child, currentId, blocks, edges, dialect);
|
|
8889
8948
|
currentId = caseResult.nextId;
|
|
8890
8949
|
if (caseResult.entryId !== -1) {
|
|
8891
8950
|
edges.push({
|
|
@@ -8915,7 +8974,7 @@ function buildBashCFG(tree, startId, cache) {
|
|
|
8915
8974
|
for (const func2 of functions) {
|
|
8916
8975
|
const body2 = func2.childForFieldName("body");
|
|
8917
8976
|
if (!body2) continue;
|
|
8918
|
-
const { blocks, edges, nextId } = buildMethodCFG(body2, blockIdCounter,
|
|
8977
|
+
const { blocks, edges, nextId } = buildMethodCFG(body2, blockIdCounter, "java");
|
|
8919
8978
|
allBlocks.push(...blocks);
|
|
8920
8979
|
allEdges.push(...edges);
|
|
8921
8980
|
blockIdCounter = nextId;
|
|
@@ -8938,7 +8997,7 @@ function buildBashCFG(tree, startId, cache) {
|
|
|
8938
8997
|
let lastExitIds = [];
|
|
8939
8998
|
let firstBlockId = -1;
|
|
8940
8999
|
for (const stmt of topLevelStatements) {
|
|
8941
|
-
const result = processStatement(stmt, blockIdCounter, allBlocks, allEdges,
|
|
9000
|
+
const result = processStatement(stmt, blockIdCounter, allBlocks, allEdges, "java");
|
|
8942
9001
|
blockIdCounter = result.nextId;
|
|
8943
9002
|
if (firstBlockId === -1) {
|
|
8944
9003
|
firstBlockId = result.entryId;
|
|
@@ -8982,7 +9041,8 @@ function isBashStatement(node) {
|
|
|
8982
9041
|
]);
|
|
8983
9042
|
return bashStatementTypes.has(node.type);
|
|
8984
9043
|
}
|
|
8985
|
-
function isStatement(node,
|
|
9044
|
+
function isStatement(node, dialect) {
|
|
9045
|
+
if (dialect === "csharp") return csharpStatementTypes.has(node.type);
|
|
8986
9046
|
const javaStatementTypes = /* @__PURE__ */ new Set([
|
|
8987
9047
|
"local_variable_declaration",
|
|
8988
9048
|
"expression_statement",
|
|
@@ -9026,8 +9086,30 @@ function isStatement(node, isJavaScript) {
|
|
|
9026
9086
|
"export_statement",
|
|
9027
9087
|
"import_statement"
|
|
9028
9088
|
]);
|
|
9029
|
-
return
|
|
9030
|
-
}
|
|
9089
|
+
return dialect === "js" ? jsStatementTypes.has(node.type) : javaStatementTypes.has(node.type);
|
|
9090
|
+
}
|
|
9091
|
+
var csharpStatementTypes = /* @__PURE__ */ new Set([
|
|
9092
|
+
"local_declaration_statement",
|
|
9093
|
+
"expression_statement",
|
|
9094
|
+
"if_statement",
|
|
9095
|
+
"for_statement",
|
|
9096
|
+
"foreach_statement",
|
|
9097
|
+
"while_statement",
|
|
9098
|
+
"do_statement",
|
|
9099
|
+
"try_statement",
|
|
9100
|
+
"switch_statement",
|
|
9101
|
+
"return_statement",
|
|
9102
|
+
"throw_statement",
|
|
9103
|
+
"break_statement",
|
|
9104
|
+
"continue_statement",
|
|
9105
|
+
"using_statement",
|
|
9106
|
+
"lock_statement",
|
|
9107
|
+
"checked_statement",
|
|
9108
|
+
"unsafe_statement",
|
|
9109
|
+
"yield_statement",
|
|
9110
|
+
"goto_statement",
|
|
9111
|
+
"block"
|
|
9112
|
+
]);
|
|
9031
9113
|
function buildGoCFG(tree, blockIdCounter, cache) {
|
|
9032
9114
|
const allBlocks = [];
|
|
9033
9115
|
const allEdges = [];
|
|
@@ -9038,7 +9120,7 @@ function buildGoCFG(tree, blockIdCounter, cache) {
|
|
|
9038
9120
|
for (const func2 of functions) {
|
|
9039
9121
|
const body2 = func2.childForFieldName("body");
|
|
9040
9122
|
if (!body2 || body2.type !== "block") continue;
|
|
9041
|
-
const { blocks, edges, nextId } = buildMethodCFG(body2, blockIdCounter,
|
|
9123
|
+
const { blocks, edges, nextId } = buildMethodCFG(body2, blockIdCounter, "java");
|
|
9042
9124
|
allBlocks.push(...blocks);
|
|
9043
9125
|
allEdges.push(...edges);
|
|
9044
9126
|
blockIdCounter = nextId;
|
|
@@ -11587,7 +11669,11 @@ var DEFAULT_SINKS = [
|
|
|
11587
11669
|
// File: covers both File(String pathname) and File(parent, child). The 2-arg
|
|
11588
11670
|
// overload's child argument carries CVE-2018-8041 (Camel mail Content-Disposition
|
|
11589
11671
|
// filename written to disk).
|
|
11590
|
-
|
|
11672
|
+
// Java `new File(path)` idiom. Excluded for C#: there is no `new File()` in
|
|
11673
|
+
// .NET (it's `System.IO.File` statics + `FileStream`, covered separately), and
|
|
11674
|
+
// this over-matched ASP.NET `ControllerBase.File(stream|bytes, contentType)` —
|
|
11675
|
+
// a result helper that returns a file, not a path sink (cognium-ai#326 B).
|
|
11676
|
+
{ method: "File", class: "constructor", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0, 1], exclude_languages: ["csharp"] },
|
|
11591
11677
|
{ method: "FileInputStream", class: "constructor", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0] },
|
|
11592
11678
|
{ method: "FileOutputStream", class: "constructor", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0] },
|
|
11593
11679
|
{ method: "FileReader", class: "constructor", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0] },
|
|
@@ -13114,6 +13200,11 @@ var DEFAULT_SINKS = [
|
|
|
13114
13200
|
// ADO.NET `cmd.CommandText = "…" + x` — emitted as a synthetic call by
|
|
13115
13201
|
// extractCSharpCalls (property-assignment sink; the ctor-arg sink misses it).
|
|
13116
13202
|
{ method: "CommandText", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0], languages: ["csharp"] },
|
|
13203
|
+
// `DirectorySearcher.Filter = "(uid=" + x + ")"` — LDAP injection (cognium-ai#272).
|
|
13204
|
+
// Emitted as a synthetic call by extractCSharpCalls ONLY when the receiver
|
|
13205
|
+
// resolves to a DirectorySearcher, so this classless entry never over-matches
|
|
13206
|
+
// other `.Filter =` assignments (DataView/BindingSource/collection filters).
|
|
13207
|
+
{ method: "Filter", type: "ldap_injection", cwe: "CWE-90", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
13117
13208
|
// ADO.NET `cmd.Execute*()` — object-carried sink (cognium-dev#271). The taint
|
|
13118
13209
|
// rides the SqlCommand receiver (CommandText set from tainted data); the
|
|
13119
13210
|
// receiver is surfaced as arg[0] by extractCSharpCalls, and the command object
|
|
@@ -13130,6 +13221,9 @@ var DEFAULT_SINKS = [
|
|
|
13130
13221
|
// injectable — the second is the argv path where taint rides arg[1] (#276).
|
|
13131
13222
|
{ method: "Start", class: "Process", type: "command_injection", cwe: "CWE-78", severity: "critical", arg_positions: [0, 1], languages: ["csharp"] },
|
|
13132
13223
|
{ method: "ProcessStartInfo", type: "command_injection", cwe: "CWE-78", severity: "critical", arg_positions: [0, 1], languages: ["csharp"] },
|
|
13224
|
+
// P/Invoke of libc `system(cmd)` — lowercase `system` in C# is virtually
|
|
13225
|
+
// always the imported shell entry point (cognium-ai#275 interop/IlPinvoke).
|
|
13226
|
+
{ method: "system", type: "command_injection", cwe: "CWE-78", severity: "critical", arg_positions: [0], languages: ["csharp"] },
|
|
13133
13227
|
// C# path traversal — System.IO file APIs (CWE-22). Distinctive method names.
|
|
13134
13228
|
{ method: "ReadAllText", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
13135
13229
|
{ method: "ReadAllBytes", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
@@ -13142,6 +13236,25 @@ var DEFAULT_SINKS = [
|
|
|
13142
13236
|
{ method: "FileStream", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
13143
13237
|
{ method: "StreamReader", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
13144
13238
|
{ method: "StreamWriter", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
13239
|
+
// Remaining System.IO.File path APIs — class-scoped so they don't collide
|
|
13240
|
+
// with unrelated methods. Copy/Move take a source AND destination path (both
|
|
13241
|
+
// attacker-controllable → read-anywhere / write-anywhere), hence [0, 1].
|
|
13242
|
+
{ method: "Copy", class: "File", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0, 1], languages: ["csharp"] },
|
|
13243
|
+
{ method: "Move", class: "File", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0, 1], languages: ["csharp"] },
|
|
13244
|
+
{ method: "Delete", class: "File", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
13245
|
+
{ method: "Open", class: "File", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
13246
|
+
{ method: "OpenText", class: "File", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
13247
|
+
{ method: "Create", class: "File", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
13248
|
+
{ method: "WriteAllLines", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
13249
|
+
{ method: "AppendAllLines", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
13250
|
+
// System.IO.Directory path APIs.
|
|
13251
|
+
{ method: "CreateDirectory", class: "Directory", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
13252
|
+
{ method: "Delete", class: "Directory", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
13253
|
+
{ method: "GetFiles", class: "Directory", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
13254
|
+
{ method: "EnumerateFiles", class: "Directory", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
13255
|
+
// ASP.NET `ControllerBase.PhysicalFile(path, contentType)` serves a file from
|
|
13256
|
+
// an absolute disk path — attacker-controllable path is CWE-22 (cognium-ai#326/#275).
|
|
13257
|
+
{ method: "PhysicalFile", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
13145
13258
|
// C# SSRF — HttpClient / WebClient / WebRequest (CWE-918).
|
|
13146
13259
|
{ method: "GetAsync", type: "ssrf", cwe: "CWE-918", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
13147
13260
|
{ method: "PostAsync", type: "ssrf", cwe: "CWE-918", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
@@ -13158,13 +13271,36 @@ var DEFAULT_SINKS = [
|
|
|
13158
13271
|
// C# code injection — dynamic script/assembly loading (CWE-94).
|
|
13159
13272
|
{ method: "EvaluateAsync", type: "code_injection", cwe: "CWE-94", severity: "critical", arg_positions: [0], languages: ["csharp"] },
|
|
13160
13273
|
{ method: "RunAsync", class: "CSharpScript", type: "code_injection", cwe: "CWE-94", severity: "critical", arg_positions: [0], languages: ["csharp"] },
|
|
13161
|
-
//
|
|
13274
|
+
// Loading an assembly from an attacker-controlled name/path is arbitrary code
|
|
13275
|
+
// execution. Class-scoped to `Assembly` (low FP — a tainted arg here is rare
|
|
13276
|
+
// in benign code). `Type.GetType`/`Activator.CreateInstance` are intentionally
|
|
13277
|
+
// NOT added — reflection with config-driven type names is common in DI, an FP
|
|
13278
|
+
// trap without the reflection-suppress machinery.
|
|
13279
|
+
{ method: "Load", class: "Assembly", type: "code_injection", cwe: "CWE-94", severity: "critical", arg_positions: [0], languages: ["csharp"] },
|
|
13280
|
+
{ method: "LoadFrom", class: "Assembly", type: "code_injection", cwe: "CWE-94", severity: "critical", arg_positions: [0], languages: ["csharp"] },
|
|
13281
|
+
{ method: "LoadFile", class: "Assembly", type: "code_injection", cwe: "CWE-94", severity: "critical", arg_positions: [0], languages: ["csharp"] },
|
|
13282
|
+
// C# insecure deserialization — the polymorphic BCL formatters that can
|
|
13283
|
+
// instantiate arbitrary types named in the payload (CWE-502, cognium-ai#318).
|
|
13284
|
+
// Each of these classes exists only to deserialize and is unsafe on untrusted
|
|
13285
|
+
// input; class-scoped, so no collision with safe JSON APIs (System.Text.Json /
|
|
13286
|
+
// Newtonsoft-default, which are intentionally NOT sinks).
|
|
13162
13287
|
{ method: "Deserialize", class: "BinaryFormatter", type: "deserialization", cwe: "CWE-502", severity: "critical", arg_positions: [0], languages: ["csharp"] },
|
|
13163
13288
|
{ method: "Deserialize", class: "NetDataContractSerializer", type: "deserialization", cwe: "CWE-502", severity: "critical", arg_positions: [0], languages: ["csharp"] },
|
|
13289
|
+
{ method: "Deserialize", class: "SoapFormatter", type: "deserialization", cwe: "CWE-502", severity: "critical", arg_positions: [0], languages: ["csharp"] },
|
|
13290
|
+
{ method: "Deserialize", class: "LosFormatter", type: "deserialization", cwe: "CWE-502", severity: "critical", arg_positions: [0], languages: ["csharp"] },
|
|
13291
|
+
{ method: "Deserialize", class: "ObjectStateFormatter", type: "deserialization", cwe: "CWE-502", severity: "critical", arg_positions: [0], languages: ["csharp"] },
|
|
13164
13292
|
// C# XSS — raw HTML output (CWE-79).
|
|
13165
13293
|
{ method: "Raw", class: "Html", type: "xss", cwe: "CWE-79", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
13166
13294
|
{ method: "Write", class: "Response", type: "xss", cwe: "CWE-79", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
13167
13295
|
{ method: "HtmlString", type: "xss", cwe: "CWE-79", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
13296
|
+
// Blazor `new MarkupString(x)` renders its argument as raw HTML (the framework's
|
|
13297
|
+
// documented "trusted markup" escape hatch) — attacker-controlled input is XSS. (ca#275)
|
|
13298
|
+
{ method: "MarkupString", class: "constructor", type: "xss", cwe: "CWE-79", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
13299
|
+
// `String.Format(fmt, …)` with an attacker-controlled FORMAT string — CWE-134.
|
|
13300
|
+
// Taint-gated on arg 0 (the format), so ordinary `String.Format("...", user)` where
|
|
13301
|
+
// only an argument is tainted never fires. .NET composite formatting can't corrupt
|
|
13302
|
+
// memory, so medium (FormatException DoS / unintended arg access), unlike C printf.
|
|
13303
|
+
{ method: "Format", class: "String", type: "format_string", cwe: "CWE-134", severity: "medium", arg_positions: [0], languages: ["csharp"] },
|
|
13168
13304
|
// C# LDAP injection — System.DirectoryServices (CWE-90). The user-built
|
|
13169
13305
|
// filter is the constructor argument.
|
|
13170
13306
|
{ method: "DirectorySearcher", type: "ldap_injection", cwe: "CWE-90", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
@@ -13185,14 +13321,37 @@ var DEFAULT_SINKS = [
|
|
|
13185
13321
|
// cognium-dev#273/#275). `BsonDocument.Parse(userJson)` deserializes an
|
|
13186
13322
|
// attacker-controlled query document. Class-scoped (static receiver resolves).
|
|
13187
13323
|
{ method: "Parse", class: "BsonDocument", type: "nosql_injection", cwe: "CWE-943", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
13324
|
+
// C# log injection — Microsoft.Extensions.Logging `ILogger` (CWE-117,
|
|
13325
|
+
// cognium-ai#318). Only the message TEMPLATE (arg[0]) is the injectable
|
|
13326
|
+
// position; the structured form `LogInformation("… {Id}", id)` keeps taint in
|
|
13327
|
+
// later args (the logger renders them as data), so arg_positions:[0] does not
|
|
13328
|
+
// flag it. Mirrors the Java `Logger` sinks; severity low.
|
|
13329
|
+
{ method: "LogInformation", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0], languages: ["csharp"] },
|
|
13330
|
+
{ method: "LogWarning", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0], languages: ["csharp"] },
|
|
13331
|
+
{ method: "LogError", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0], languages: ["csharp"] },
|
|
13332
|
+
{ method: "LogDebug", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0], languages: ["csharp"] },
|
|
13333
|
+
{ method: "LogCritical", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0], languages: ["csharp"] },
|
|
13334
|
+
{ method: "LogTrace", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0], languages: ["csharp"] },
|
|
13335
|
+
// Generic `ILogger.Log(logLevel, message, …)` — class-scoped (`Log` alone is
|
|
13336
|
+
// too generic); the message is arg[1].
|
|
13337
|
+
{ method: "Log", class: "ILogger", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [1], languages: ["csharp"] },
|
|
13188
13338
|
// C# XPath injection — System.Xml.XPath (CWE-643). Distinctive selector methods.
|
|
13189
13339
|
{ method: "SelectSingleNode", type: "xpath_injection", cwe: "CWE-643", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
13190
13340
|
{ method: "SelectNodes", type: "xpath_injection", cwe: "CWE-643", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
13191
13341
|
{ method: "Compile", class: "XPathExpression", type: "xpath_injection", cwe: "CWE-643", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
13342
|
+
// XPathNavigator.Select/Evaluate — class-scoped (both names collide with LINQ
|
|
13343
|
+
// `.Select`/`.Evaluate`, so they must resolve to an XPathNavigator receiver).
|
|
13344
|
+
{ method: "Select", class: "XPathNavigator", type: "xpath_injection", cwe: "CWE-643", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
13345
|
+
{ method: "Evaluate", class: "XPathNavigator", type: "xpath_injection", cwe: "CWE-643", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
13192
13346
|
// C# XXE — untrusted XML into a parser without DTD hardening (CWE-611).
|
|
13193
13347
|
{ method: "LoadXml", type: "xxe", cwe: "CWE-611", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
13194
13348
|
{ method: "Load", class: "XmlDocument", type: "xxe", cwe: "CWE-611", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
13195
13349
|
{ method: "Load", class: "XDocument", type: "xxe", cwe: "CWE-611", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
13350
|
+
// `new XmlTextReader(input)` — its DtdProcessing defaults to Parse (resolves
|
|
13351
|
+
// external entities), unlike `XmlReader.Create` (Prohibit). CWE-611,
|
|
13352
|
+
// cognium-ai#318. The 4.7.0 XmlResolver=null / DtdProcessing=Prohibit
|
|
13353
|
+
// hardening still suppresses the safe shape.
|
|
13354
|
+
{ method: "XmlTextReader", type: "xxe", cwe: "CWE-611", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
13196
13355
|
// Python SQLi — asyncpg Connection.*
|
|
13197
13356
|
{ method: "execute", class: "Connection", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0] },
|
|
13198
13357
|
{ method: "fetch", class: "Connection", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0] },
|
|
@@ -14510,6 +14669,57 @@ function isSafeJSChildProcessCall(call, pattern, language) {
|
|
|
14510
14669
|
if (SHELL_PROGRAMS.has(program)) return false;
|
|
14511
14670
|
return true;
|
|
14512
14671
|
}
|
|
14672
|
+
var CSHARP_SHELL_PROGRAMS = /* @__PURE__ */ new Set([
|
|
14673
|
+
"sh",
|
|
14674
|
+
"bash",
|
|
14675
|
+
"zsh",
|
|
14676
|
+
"dash",
|
|
14677
|
+
"ash",
|
|
14678
|
+
"ksh",
|
|
14679
|
+
"cmd",
|
|
14680
|
+
"powershell",
|
|
14681
|
+
"pwsh"
|
|
14682
|
+
]);
|
|
14683
|
+
function isConstNonShellExe(raw) {
|
|
14684
|
+
if (!raw) return false;
|
|
14685
|
+
const t = raw.trim();
|
|
14686
|
+
if (!/^@?"[^"]*"$/.test(t)) return false;
|
|
14687
|
+
const program = (t.replace(/^@?"|"$/g, "").split(/[\\/]/).pop() ?? "").toLowerCase().replace(/\.exe$/, "");
|
|
14688
|
+
return !CSHARP_SHELL_PROGRAMS.has(program);
|
|
14689
|
+
}
|
|
14690
|
+
var PSI_CTOR_EXE_RE = /\bnew\s+ProcessStartInfo\s*(?:<[^>]*>)?\s*\(\s*(@?"[^"]*")/;
|
|
14691
|
+
function processStartInfoExe(expr, sourceLines) {
|
|
14692
|
+
const inline = PSI_CTOR_EXE_RE.exec(expr);
|
|
14693
|
+
if (inline) return inline[1];
|
|
14694
|
+
if (sourceLines && /^[A-Za-z_]\w*$/.test(expr.trim())) {
|
|
14695
|
+
const varName = expr.trim();
|
|
14696
|
+
const assignRe = new RegExp(
|
|
14697
|
+
`\\b${varName}\\s*=\\s*new\\s+ProcessStartInfo\\s*(?:<[^>]*>)?\\s*\\(\\s*(@?"[^"]*")`
|
|
14698
|
+
);
|
|
14699
|
+
for (const line of sourceLines) {
|
|
14700
|
+
const m = assignRe.exec(line);
|
|
14701
|
+
if (m) return m[1];
|
|
14702
|
+
}
|
|
14703
|
+
}
|
|
14704
|
+
return null;
|
|
14705
|
+
}
|
|
14706
|
+
function isSafeCSharpProcessStartCall(call, pattern, language, sourceLines) {
|
|
14707
|
+
if (language !== "csharp") return false;
|
|
14708
|
+
if (pattern.type !== "command_injection") return false;
|
|
14709
|
+
const method = call.method_name;
|
|
14710
|
+
if (method !== "Start" && method !== "ProcessStartInfo") return false;
|
|
14711
|
+
if (call.arguments.length >= 2) {
|
|
14712
|
+
const fileArg = call.arguments.find((a) => a.position === 0);
|
|
14713
|
+
const raw = fileArg?.literal != null ? String(fileArg.literal) : fileArg?.expression;
|
|
14714
|
+
return isConstNonShellExe(raw);
|
|
14715
|
+
}
|
|
14716
|
+
if (method === "Start" && call.arguments.length === 1) {
|
|
14717
|
+
const arg0 = call.arguments.find((a) => a.position === 0);
|
|
14718
|
+
const expr = (arg0?.expression ?? "").trim();
|
|
14719
|
+
return isConstNonShellExe(processStartInfoExe(expr, sourceLines));
|
|
14720
|
+
}
|
|
14721
|
+
return false;
|
|
14722
|
+
}
|
|
14513
14723
|
function isSafeRustCommandCall(call, pattern, language) {
|
|
14514
14724
|
if (language !== "rust") return false;
|
|
14515
14725
|
if (pattern.type !== "command_injection") return false;
|
|
@@ -14765,6 +14975,9 @@ function findSinks(calls, patterns, typeHierarchy, language, sourceLines, types)
|
|
|
14765
14975
|
if (isSafeJSChildProcessCall(call, pattern, language)) {
|
|
14766
14976
|
continue;
|
|
14767
14977
|
}
|
|
14978
|
+
if (isSafeCSharpProcessStartCall(call, pattern, language, sourceLines)) {
|
|
14979
|
+
continue;
|
|
14980
|
+
}
|
|
14768
14981
|
if (pattern.safe_if_class_literal_at !== void 0 && argIsClassLiteral(call, pattern.safe_if_class_literal_at, types)) {
|
|
14769
14982
|
continue;
|
|
14770
14983
|
}
|
|
@@ -224,15 +224,29 @@ function extractCSharpCalls(tree, cache) {
|
|
|
224
224
|
if (left?.type !== 'member_access_expression')
|
|
225
225
|
continue;
|
|
226
226
|
const nameNode = left.childForFieldName('name');
|
|
227
|
-
if (!nameNode
|
|
227
|
+
if (!nameNode)
|
|
228
228
|
continue;
|
|
229
|
+
const propName = getNodeText(nameNode);
|
|
230
|
+
const exprNode = left.childForFieldName('expression');
|
|
231
|
+
// `CommandText` is distinctive to ADO.NET commands, so it fires classless.
|
|
232
|
+
// `Filter` is a common property name, so it is class-gated to a resolved
|
|
233
|
+
// `DirectorySearcher` receiver (cognium-ai#272 LDAP injection) — this keeps
|
|
234
|
+
// the classless registry sink from over-matching unrelated `.Filter =`.
|
|
235
|
+
if (propName === 'Filter') {
|
|
236
|
+
const recv = exprNode ? getNodeText(exprNode) : null;
|
|
237
|
+
const recvType = recv ? typeMap.get(recv) : undefined;
|
|
238
|
+
if (recvType !== 'DirectorySearcher')
|
|
239
|
+
continue;
|
|
240
|
+
}
|
|
241
|
+
else if (propName !== 'CommandText') {
|
|
242
|
+
continue;
|
|
243
|
+
}
|
|
229
244
|
const right = asn.childForFieldName('right');
|
|
230
245
|
if (!right)
|
|
231
246
|
continue;
|
|
232
247
|
const rhsText = getNodeText(right);
|
|
233
|
-
const exprNode = left.childForFieldName('expression');
|
|
234
248
|
calls.push({
|
|
235
|
-
method_name:
|
|
249
|
+
method_name: propName,
|
|
236
250
|
receiver: exprNode ? getNodeText(exprNode) : null,
|
|
237
251
|
receiver_type: null,
|
|
238
252
|
receiver_type_fqn: null,
|