circle-ir 4.7.2 → 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 +23 -0
- package/dist/analysis/config-loader.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/taint-matcher.d.ts.map +1 -1
- package/dist/analysis/taint-matcher.js +59 -25
- 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 +252 -67
- package/dist/core/circle-ir-core.cjs +194 -66
- package/dist/core/circle-ir-core.js +194 -66
- 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
|
@@ -6000,13 +6000,21 @@ function extractCSharpCalls(tree, cache) {
|
|
|
6000
6000
|
const left = asn.childForFieldName("left");
|
|
6001
6001
|
if (left?.type !== "member_access_expression") continue;
|
|
6002
6002
|
const nameNode = left.childForFieldName("name");
|
|
6003
|
-
if (!nameNode
|
|
6003
|
+
if (!nameNode) continue;
|
|
6004
|
+
const propName = getNodeText(nameNode);
|
|
6005
|
+
const exprNode = left.childForFieldName("expression");
|
|
6006
|
+
if (propName === "Filter") {
|
|
6007
|
+
const recv = exprNode ? getNodeText(exprNode) : null;
|
|
6008
|
+
const recvType = recv ? typeMap.get(recv) : void 0;
|
|
6009
|
+
if (recvType !== "DirectorySearcher") continue;
|
|
6010
|
+
} else if (propName !== "CommandText") {
|
|
6011
|
+
continue;
|
|
6012
|
+
}
|
|
6004
6013
|
const right = asn.childForFieldName("right");
|
|
6005
6014
|
if (!right) continue;
|
|
6006
6015
|
const rhsText = getNodeText(right);
|
|
6007
|
-
const exprNode = left.childForFieldName("expression");
|
|
6008
6016
|
calls.push({
|
|
6009
|
-
method_name:
|
|
6017
|
+
method_name: propName,
|
|
6010
6018
|
receiver: exprNode ? getNodeText(exprNode) : null,
|
|
6011
6019
|
receiver_type: null,
|
|
6012
6020
|
receiver_type_fqn: null,
|
|
@@ -8572,6 +8580,9 @@ function buildCFG(tree, language, cache) {
|
|
|
8572
8580
|
if (effectiveLanguage === "go") {
|
|
8573
8581
|
return buildGoCFG(tree, blockIdCounter, cache);
|
|
8574
8582
|
}
|
|
8583
|
+
if (effectiveLanguage === "csharp") {
|
|
8584
|
+
return buildCSharpCFG(tree, blockIdCounter, cache);
|
|
8585
|
+
}
|
|
8575
8586
|
if (isJavaScript) {
|
|
8576
8587
|
const functions = [
|
|
8577
8588
|
...getNodesFromCache(tree.rootNode, "function_declaration", cache),
|
|
@@ -8584,7 +8595,7 @@ function buildCFG(tree, language, cache) {
|
|
|
8584
8595
|
const body2 = func2.childForFieldName("body");
|
|
8585
8596
|
if (!body2) continue;
|
|
8586
8597
|
if (body2.type === "statement_block") {
|
|
8587
|
-
const { blocks, edges, nextId } = buildMethodCFG(body2, blockIdCounter,
|
|
8598
|
+
const { blocks, edges, nextId } = buildMethodCFG(body2, blockIdCounter, "js");
|
|
8588
8599
|
allBlocks.push(...blocks);
|
|
8589
8600
|
allEdges.push(...edges);
|
|
8590
8601
|
blockIdCounter = nextId;
|
|
@@ -8606,7 +8617,7 @@ function buildCFG(tree, language, cache) {
|
|
|
8606
8617
|
for (const method of methods) {
|
|
8607
8618
|
const body2 = method.childForFieldName("body");
|
|
8608
8619
|
if (!body2) continue;
|
|
8609
|
-
const { blocks, edges, nextId } = buildMethodCFG(body2, blockIdCounter,
|
|
8620
|
+
const { blocks, edges, nextId } = buildMethodCFG(body2, blockIdCounter, "java");
|
|
8610
8621
|
allBlocks.push(...blocks);
|
|
8611
8622
|
allEdges.push(...edges);
|
|
8612
8623
|
blockIdCounter = nextId;
|
|
@@ -8614,7 +8625,28 @@ function buildCFG(tree, language, cache) {
|
|
|
8614
8625
|
}
|
|
8615
8626
|
return { blocks: allBlocks, edges: allEdges };
|
|
8616
8627
|
}
|
|
8617
|
-
function
|
|
8628
|
+
function buildCSharpCFG(tree, blockIdCounter, cache) {
|
|
8629
|
+
const allBlocks = [];
|
|
8630
|
+
const allEdges = [];
|
|
8631
|
+
const containers = [
|
|
8632
|
+
...getNodesFromCache(tree.rootNode, "method_declaration", cache),
|
|
8633
|
+
...getNodesFromCache(tree.rootNode, "constructor_declaration", cache),
|
|
8634
|
+
...getNodesFromCache(tree.rootNode, "destructor_declaration", cache),
|
|
8635
|
+
...getNodesFromCache(tree.rootNode, "operator_declaration", cache),
|
|
8636
|
+
...getNodesFromCache(tree.rootNode, "local_function_statement", cache),
|
|
8637
|
+
...getNodesFromCache(tree.rootNode, "accessor_declaration", cache)
|
|
8638
|
+
];
|
|
8639
|
+
for (const container of containers) {
|
|
8640
|
+
const body2 = container.childForFieldName("body");
|
|
8641
|
+
if (!body2 || body2.type !== "block") continue;
|
|
8642
|
+
const { blocks, edges, nextId } = buildMethodCFG(body2, blockIdCounter, "csharp");
|
|
8643
|
+
allBlocks.push(...blocks);
|
|
8644
|
+
allEdges.push(...edges);
|
|
8645
|
+
blockIdCounter = nextId;
|
|
8646
|
+
}
|
|
8647
|
+
return { blocks: allBlocks, edges: allEdges };
|
|
8648
|
+
}
|
|
8649
|
+
function buildMethodCFG(body2, startId, dialect) {
|
|
8618
8650
|
const blocks = [];
|
|
8619
8651
|
const edges = [];
|
|
8620
8652
|
let currentId = startId;
|
|
@@ -8625,7 +8657,7 @@ function buildMethodCFG(body2, startId, isJavaScript) {
|
|
|
8625
8657
|
end_line: body2.startPosition.row + 1
|
|
8626
8658
|
};
|
|
8627
8659
|
blocks.push(entryBlock);
|
|
8628
|
-
const result = processStatements(body2, currentId, blocks, edges,
|
|
8660
|
+
const result = processStatements(body2, currentId, blocks, edges, dialect);
|
|
8629
8661
|
currentId = result.nextId;
|
|
8630
8662
|
if (result.entryId !== -1) {
|
|
8631
8663
|
edges.push({
|
|
@@ -8657,15 +8689,15 @@ function buildMethodCFG(body2, startId, isJavaScript) {
|
|
|
8657
8689
|
}
|
|
8658
8690
|
return { blocks, edges, nextId: currentId };
|
|
8659
8691
|
}
|
|
8660
|
-
function processStatements(container, startId, blocks, edges,
|
|
8692
|
+
function processStatements(container, startId, blocks, edges, dialect) {
|
|
8661
8693
|
let currentId = startId;
|
|
8662
8694
|
let firstBlockId = -1;
|
|
8663
8695
|
let lastExitIds = [];
|
|
8664
8696
|
for (let i2 = 0; i2 < container.childCount; i2++) {
|
|
8665
8697
|
const stmt = container.child(i2);
|
|
8666
8698
|
if (!stmt) continue;
|
|
8667
|
-
if (!isStatement(stmt,
|
|
8668
|
-
const result = processStatement(stmt, currentId, blocks, edges,
|
|
8699
|
+
if (!isStatement(stmt, dialect)) continue;
|
|
8700
|
+
const result = processStatement(stmt, currentId, blocks, edges, dialect);
|
|
8669
8701
|
currentId = result.nextId;
|
|
8670
8702
|
if (firstBlockId === -1) {
|
|
8671
8703
|
firstBlockId = result.entryId;
|
|
@@ -8686,31 +8718,49 @@ function processStatements(container, startId, blocks, edges, isJavaScript) {
|
|
|
8686
8718
|
nextId: currentId
|
|
8687
8719
|
};
|
|
8688
8720
|
}
|
|
8689
|
-
function processStatement(stmt, startId, blocks, edges,
|
|
8721
|
+
function processStatement(stmt, startId, blocks, edges, dialect) {
|
|
8690
8722
|
switch (stmt.type) {
|
|
8691
8723
|
case "if_statement":
|
|
8692
|
-
return processIfStatement(stmt, startId, blocks, edges,
|
|
8724
|
+
return processIfStatement(stmt, startId, blocks, edges, dialect);
|
|
8693
8725
|
case "for_statement":
|
|
8694
8726
|
case "enhanced_for_statement":
|
|
8695
8727
|
case "for_in_statement":
|
|
8696
8728
|
case "for_of_statement":
|
|
8697
|
-
|
|
8729
|
+
case "foreach_statement":
|
|
8730
|
+
return processForStatement(stmt, startId, blocks, edges, dialect);
|
|
8698
8731
|
case "while_statement":
|
|
8699
|
-
return processWhileStatement(stmt, startId, blocks, edges,
|
|
8732
|
+
return processWhileStatement(stmt, startId, blocks, edges, dialect);
|
|
8700
8733
|
case "do_statement":
|
|
8701
|
-
return processDoWhileStatement(stmt, startId, blocks, edges,
|
|
8734
|
+
return processDoWhileStatement(stmt, startId, blocks, edges, dialect);
|
|
8702
8735
|
case "try_statement":
|
|
8703
|
-
return processTryStatement(stmt, startId, blocks, edges,
|
|
8736
|
+
return processTryStatement(stmt, startId, blocks, edges, dialect);
|
|
8704
8737
|
case "switch_expression":
|
|
8705
8738
|
case "switch_statement":
|
|
8706
|
-
return processSwitchStatement(stmt, startId, blocks, edges,
|
|
8739
|
+
return processSwitchStatement(stmt, startId, blocks, edges, dialect);
|
|
8740
|
+
// C# scoped blocks add no branching — flow through the inner block so its
|
|
8741
|
+
// nested statements are still captured.
|
|
8742
|
+
case "using_statement":
|
|
8743
|
+
case "lock_statement":
|
|
8744
|
+
case "checked_statement":
|
|
8745
|
+
case "unsafe_statement": {
|
|
8746
|
+
const inner = stmt.childForFieldName("body") ?? lastBlockChild(stmt);
|
|
8747
|
+
if (inner) return processStatement(inner, startId, blocks, edges, dialect);
|
|
8748
|
+
return processSimpleStatement(stmt, startId, blocks);
|
|
8749
|
+
}
|
|
8707
8750
|
case "block":
|
|
8708
8751
|
case "statement_block":
|
|
8709
|
-
return processStatements(stmt, startId, blocks, edges,
|
|
8752
|
+
return processStatements(stmt, startId, blocks, edges, dialect);
|
|
8710
8753
|
default:
|
|
8711
8754
|
return processSimpleStatement(stmt, startId, blocks);
|
|
8712
8755
|
}
|
|
8713
8756
|
}
|
|
8757
|
+
function lastBlockChild(node) {
|
|
8758
|
+
for (let i2 = node.childCount - 1; i2 >= 0; i2--) {
|
|
8759
|
+
const c = node.child(i2);
|
|
8760
|
+
if (c && c.type === "block") return c;
|
|
8761
|
+
}
|
|
8762
|
+
return null;
|
|
8763
|
+
}
|
|
8714
8764
|
function processSimpleStatement(stmt, startId, blocks) {
|
|
8715
8765
|
const block = {
|
|
8716
8766
|
id: startId,
|
|
@@ -8725,7 +8775,7 @@ function processSimpleStatement(stmt, startId, blocks) {
|
|
|
8725
8775
|
nextId: startId + 1
|
|
8726
8776
|
};
|
|
8727
8777
|
}
|
|
8728
|
-
function processIfStatement(stmt, startId, blocks, edges,
|
|
8778
|
+
function processIfStatement(stmt, startId, blocks, edges, dialect) {
|
|
8729
8779
|
let currentId = startId;
|
|
8730
8780
|
const condBlock = {
|
|
8731
8781
|
id: currentId++,
|
|
@@ -8737,7 +8787,7 @@ function processIfStatement(stmt, startId, blocks, edges, isJavaScript) {
|
|
|
8737
8787
|
const exitIds = [];
|
|
8738
8788
|
const consequence = stmt.childForFieldName("consequence");
|
|
8739
8789
|
if (consequence) {
|
|
8740
|
-
const thenResult = processStatement(consequence, currentId, blocks, edges,
|
|
8790
|
+
const thenResult = processStatement(consequence, currentId, blocks, edges, dialect);
|
|
8741
8791
|
currentId = thenResult.nextId;
|
|
8742
8792
|
edges.push({
|
|
8743
8793
|
from: condBlock.id,
|
|
@@ -8748,7 +8798,7 @@ function processIfStatement(stmt, startId, blocks, edges, isJavaScript) {
|
|
|
8748
8798
|
}
|
|
8749
8799
|
const alternative = stmt.childForFieldName("alternative");
|
|
8750
8800
|
if (alternative) {
|
|
8751
|
-
const elseResult = processStatement(alternative, currentId, blocks, edges,
|
|
8801
|
+
const elseResult = processStatement(alternative, currentId, blocks, edges, dialect);
|
|
8752
8802
|
currentId = elseResult.nextId;
|
|
8753
8803
|
edges.push({
|
|
8754
8804
|
from: condBlock.id,
|
|
@@ -8765,7 +8815,7 @@ function processIfStatement(stmt, startId, blocks, edges, isJavaScript) {
|
|
|
8765
8815
|
nextId: currentId
|
|
8766
8816
|
};
|
|
8767
8817
|
}
|
|
8768
|
-
function processForStatement(stmt, startId, blocks, edges,
|
|
8818
|
+
function processForStatement(stmt, startId, blocks, edges, dialect) {
|
|
8769
8819
|
let currentId = startId;
|
|
8770
8820
|
const loopBlock = {
|
|
8771
8821
|
id: currentId++,
|
|
@@ -8776,7 +8826,7 @@ function processForStatement(stmt, startId, blocks, edges, isJavaScript) {
|
|
|
8776
8826
|
blocks.push(loopBlock);
|
|
8777
8827
|
const body2 = stmt.childForFieldName("body");
|
|
8778
8828
|
if (body2) {
|
|
8779
|
-
const bodyResult = processStatement(body2, currentId, blocks, edges,
|
|
8829
|
+
const bodyResult = processStatement(body2, currentId, blocks, edges, dialect);
|
|
8780
8830
|
currentId = bodyResult.nextId;
|
|
8781
8831
|
edges.push({
|
|
8782
8832
|
from: loopBlock.id,
|
|
@@ -8798,16 +8848,16 @@ function processForStatement(stmt, startId, blocks, edges, isJavaScript) {
|
|
|
8798
8848
|
nextId: currentId
|
|
8799
8849
|
};
|
|
8800
8850
|
}
|
|
8801
|
-
function processWhileStatement(stmt, startId, blocks, edges,
|
|
8802
|
-
return processForStatement(stmt, startId, blocks, edges,
|
|
8851
|
+
function processWhileStatement(stmt, startId, blocks, edges, dialect) {
|
|
8852
|
+
return processForStatement(stmt, startId, blocks, edges, dialect);
|
|
8803
8853
|
}
|
|
8804
|
-
function processDoWhileStatement(stmt, startId, blocks, edges,
|
|
8854
|
+
function processDoWhileStatement(stmt, startId, blocks, edges, dialect) {
|
|
8805
8855
|
let currentId = startId;
|
|
8806
8856
|
const body2 = stmt.childForFieldName("body");
|
|
8807
8857
|
let bodyEntryId = currentId;
|
|
8808
8858
|
let bodyExitIds = [];
|
|
8809
8859
|
if (body2) {
|
|
8810
|
-
const bodyResult = processStatement(body2, currentId, blocks, edges,
|
|
8860
|
+
const bodyResult = processStatement(body2, currentId, blocks, edges, dialect);
|
|
8811
8861
|
currentId = bodyResult.nextId;
|
|
8812
8862
|
bodyEntryId = bodyResult.entryId;
|
|
8813
8863
|
bodyExitIds = bodyResult.exitIds;
|
|
@@ -8837,13 +8887,13 @@ function processDoWhileStatement(stmt, startId, blocks, edges, isJavaScript) {
|
|
|
8837
8887
|
nextId: currentId
|
|
8838
8888
|
};
|
|
8839
8889
|
}
|
|
8840
|
-
function processTryStatement(stmt, startId, blocks, edges,
|
|
8890
|
+
function processTryStatement(stmt, startId, blocks, edges, dialect) {
|
|
8841
8891
|
let currentId = startId;
|
|
8842
8892
|
const exitIds = [];
|
|
8843
8893
|
const body2 = stmt.childForFieldName("body");
|
|
8844
8894
|
let tryEntryId = -1;
|
|
8845
8895
|
if (body2) {
|
|
8846
|
-
const bodyResult = processStatements(body2, currentId, blocks, edges,
|
|
8896
|
+
const bodyResult = processStatements(body2, currentId, blocks, edges, dialect);
|
|
8847
8897
|
currentId = bodyResult.nextId;
|
|
8848
8898
|
tryEntryId = bodyResult.entryId;
|
|
8849
8899
|
exitIds.push(...bodyResult.exitIds);
|
|
@@ -8853,7 +8903,7 @@ function processTryStatement(stmt, startId, blocks, edges, isJavaScript) {
|
|
|
8853
8903
|
if (child?.type === "catch_clause") {
|
|
8854
8904
|
const catchBody = child.childForFieldName("body");
|
|
8855
8905
|
if (catchBody) {
|
|
8856
|
-
const catchResult = processStatements(catchBody, currentId, blocks, edges,
|
|
8906
|
+
const catchResult = processStatements(catchBody, currentId, blocks, edges, dialect);
|
|
8857
8907
|
currentId = catchResult.nextId;
|
|
8858
8908
|
if (tryEntryId !== -1) {
|
|
8859
8909
|
edges.push({
|
|
@@ -8866,9 +8916,18 @@ function processTryStatement(stmt, startId, blocks, edges, isJavaScript) {
|
|
|
8866
8916
|
}
|
|
8867
8917
|
}
|
|
8868
8918
|
}
|
|
8869
|
-
|
|
8919
|
+
let finallyClause = stmt.childForFieldName("finally") ?? stmt.childForFieldName("finalizer");
|
|
8920
|
+
if (!finallyClause && dialect === "csharp") {
|
|
8921
|
+
for (let i2 = 0; i2 < stmt.childCount; i2++) {
|
|
8922
|
+
const child = stmt.child(i2);
|
|
8923
|
+
if (child?.type === "finally_clause") {
|
|
8924
|
+
finallyClause = lastBlockChild(child);
|
|
8925
|
+
break;
|
|
8926
|
+
}
|
|
8927
|
+
}
|
|
8928
|
+
}
|
|
8870
8929
|
if (finallyClause) {
|
|
8871
|
-
const finallyResult = processStatements(finallyClause, currentId, blocks, edges,
|
|
8930
|
+
const finallyResult = processStatements(finallyClause, currentId, blocks, edges, dialect);
|
|
8872
8931
|
currentId = finallyResult.nextId;
|
|
8873
8932
|
for (const exitId of exitIds) {
|
|
8874
8933
|
edges.push({
|
|
@@ -8889,7 +8948,7 @@ function processTryStatement(stmt, startId, blocks, edges, isJavaScript) {
|
|
|
8889
8948
|
nextId: currentId
|
|
8890
8949
|
};
|
|
8891
8950
|
}
|
|
8892
|
-
function processSwitchStatement(stmt, startId, blocks, edges,
|
|
8951
|
+
function processSwitchStatement(stmt, startId, blocks, edges, dialect) {
|
|
8893
8952
|
let currentId = startId;
|
|
8894
8953
|
const switchBlock = {
|
|
8895
8954
|
id: currentId++,
|
|
@@ -8901,11 +8960,11 @@ function processSwitchStatement(stmt, startId, blocks, edges, isJavaScript) {
|
|
|
8901
8960
|
const exitIds = [];
|
|
8902
8961
|
const body2 = stmt.childForFieldName("body");
|
|
8903
8962
|
if (body2) {
|
|
8904
|
-
const caseTypes =
|
|
8963
|
+
const caseTypes = dialect === "js" ? ["switch_case", "switch_default"] : dialect === "csharp" ? ["switch_section"] : ["switch_block_statement_group", "switch_rule"];
|
|
8905
8964
|
for (let i2 = 0; i2 < body2.childCount; i2++) {
|
|
8906
8965
|
const child = body2.child(i2);
|
|
8907
8966
|
if (child && caseTypes.includes(child.type)) {
|
|
8908
|
-
const caseResult = processStatements(child, currentId, blocks, edges,
|
|
8967
|
+
const caseResult = processStatements(child, currentId, blocks, edges, dialect);
|
|
8909
8968
|
currentId = caseResult.nextId;
|
|
8910
8969
|
if (caseResult.entryId !== -1) {
|
|
8911
8970
|
edges.push({
|
|
@@ -8935,7 +8994,7 @@ function buildBashCFG(tree, startId, cache) {
|
|
|
8935
8994
|
for (const func2 of functions) {
|
|
8936
8995
|
const body2 = func2.childForFieldName("body");
|
|
8937
8996
|
if (!body2) continue;
|
|
8938
|
-
const { blocks, edges, nextId } = buildMethodCFG(body2, blockIdCounter,
|
|
8997
|
+
const { blocks, edges, nextId } = buildMethodCFG(body2, blockIdCounter, "java");
|
|
8939
8998
|
allBlocks.push(...blocks);
|
|
8940
8999
|
allEdges.push(...edges);
|
|
8941
9000
|
blockIdCounter = nextId;
|
|
@@ -8958,7 +9017,7 @@ function buildBashCFG(tree, startId, cache) {
|
|
|
8958
9017
|
let lastExitIds = [];
|
|
8959
9018
|
let firstBlockId = -1;
|
|
8960
9019
|
for (const stmt of topLevelStatements) {
|
|
8961
|
-
const result = processStatement(stmt, blockIdCounter, allBlocks, allEdges,
|
|
9020
|
+
const result = processStatement(stmt, blockIdCounter, allBlocks, allEdges, "java");
|
|
8962
9021
|
blockIdCounter = result.nextId;
|
|
8963
9022
|
if (firstBlockId === -1) {
|
|
8964
9023
|
firstBlockId = result.entryId;
|
|
@@ -9002,7 +9061,8 @@ function isBashStatement(node) {
|
|
|
9002
9061
|
]);
|
|
9003
9062
|
return bashStatementTypes.has(node.type);
|
|
9004
9063
|
}
|
|
9005
|
-
function isStatement(node,
|
|
9064
|
+
function isStatement(node, dialect) {
|
|
9065
|
+
if (dialect === "csharp") return csharpStatementTypes.has(node.type);
|
|
9006
9066
|
const javaStatementTypes = /* @__PURE__ */ new Set([
|
|
9007
9067
|
"local_variable_declaration",
|
|
9008
9068
|
"expression_statement",
|
|
@@ -9046,8 +9106,30 @@ function isStatement(node, isJavaScript) {
|
|
|
9046
9106
|
"export_statement",
|
|
9047
9107
|
"import_statement"
|
|
9048
9108
|
]);
|
|
9049
|
-
return
|
|
9050
|
-
}
|
|
9109
|
+
return dialect === "js" ? jsStatementTypes.has(node.type) : javaStatementTypes.has(node.type);
|
|
9110
|
+
}
|
|
9111
|
+
var csharpStatementTypes = /* @__PURE__ */ new Set([
|
|
9112
|
+
"local_declaration_statement",
|
|
9113
|
+
"expression_statement",
|
|
9114
|
+
"if_statement",
|
|
9115
|
+
"for_statement",
|
|
9116
|
+
"foreach_statement",
|
|
9117
|
+
"while_statement",
|
|
9118
|
+
"do_statement",
|
|
9119
|
+
"try_statement",
|
|
9120
|
+
"switch_statement",
|
|
9121
|
+
"return_statement",
|
|
9122
|
+
"throw_statement",
|
|
9123
|
+
"break_statement",
|
|
9124
|
+
"continue_statement",
|
|
9125
|
+
"using_statement",
|
|
9126
|
+
"lock_statement",
|
|
9127
|
+
"checked_statement",
|
|
9128
|
+
"unsafe_statement",
|
|
9129
|
+
"yield_statement",
|
|
9130
|
+
"goto_statement",
|
|
9131
|
+
"block"
|
|
9132
|
+
]);
|
|
9051
9133
|
function buildGoCFG(tree, blockIdCounter, cache) {
|
|
9052
9134
|
const allBlocks = [];
|
|
9053
9135
|
const allEdges = [];
|
|
@@ -9058,7 +9140,7 @@ function buildGoCFG(tree, blockIdCounter, cache) {
|
|
|
9058
9140
|
for (const func2 of functions) {
|
|
9059
9141
|
const body2 = func2.childForFieldName("body");
|
|
9060
9142
|
if (!body2 || body2.type !== "block") continue;
|
|
9061
|
-
const { blocks, edges, nextId } = buildMethodCFG(body2, blockIdCounter,
|
|
9143
|
+
const { blocks, edges, nextId } = buildMethodCFG(body2, blockIdCounter, "java");
|
|
9062
9144
|
allBlocks.push(...blocks);
|
|
9063
9145
|
allEdges.push(...edges);
|
|
9064
9146
|
blockIdCounter = nextId;
|
|
@@ -13789,6 +13871,11 @@ var DEFAULT_SINKS = [
|
|
|
13789
13871
|
// ADO.NET `cmd.CommandText = "…" + x` — emitted as a synthetic call by
|
|
13790
13872
|
// extractCSharpCalls (property-assignment sink; the ctor-arg sink misses it).
|
|
13791
13873
|
{ method: "CommandText", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0], languages: ["csharp"] },
|
|
13874
|
+
// `DirectorySearcher.Filter = "(uid=" + x + ")"` — LDAP injection (cognium-ai#272).
|
|
13875
|
+
// Emitted as a synthetic call by extractCSharpCalls ONLY when the receiver
|
|
13876
|
+
// resolves to a DirectorySearcher, so this classless entry never over-matches
|
|
13877
|
+
// other `.Filter =` assignments (DataView/BindingSource/collection filters).
|
|
13878
|
+
{ method: "Filter", type: "ldap_injection", cwe: "CWE-90", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
13792
13879
|
// ADO.NET `cmd.Execute*()` — object-carried sink (cognium-dev#271). The taint
|
|
13793
13880
|
// rides the SqlCommand receiver (CommandText set from tainted data); the
|
|
13794
13881
|
// receiver is surfaced as arg[0] by extractCSharpCalls, and the command object
|
|
@@ -13805,6 +13892,9 @@ var DEFAULT_SINKS = [
|
|
|
13805
13892
|
// injectable — the second is the argv path where taint rides arg[1] (#276).
|
|
13806
13893
|
{ method: "Start", class: "Process", type: "command_injection", cwe: "CWE-78", severity: "critical", arg_positions: [0, 1], languages: ["csharp"] },
|
|
13807
13894
|
{ method: "ProcessStartInfo", type: "command_injection", cwe: "CWE-78", severity: "critical", arg_positions: [0, 1], languages: ["csharp"] },
|
|
13895
|
+
// P/Invoke of libc `system(cmd)` — lowercase `system` in C# is virtually
|
|
13896
|
+
// always the imported shell entry point (cognium-ai#275 interop/IlPinvoke).
|
|
13897
|
+
{ method: "system", type: "command_injection", cwe: "CWE-78", severity: "critical", arg_positions: [0], languages: ["csharp"] },
|
|
13808
13898
|
// C# path traversal — System.IO file APIs (CWE-22). Distinctive method names.
|
|
13809
13899
|
{ method: "ReadAllText", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
13810
13900
|
{ method: "ReadAllBytes", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
@@ -13833,6 +13923,9 @@ var DEFAULT_SINKS = [
|
|
|
13833
13923
|
{ method: "Delete", class: "Directory", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
13834
13924
|
{ method: "GetFiles", class: "Directory", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
13835
13925
|
{ method: "EnumerateFiles", class: "Directory", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
13926
|
+
// ASP.NET `ControllerBase.PhysicalFile(path, contentType)` serves a file from
|
|
13927
|
+
// an absolute disk path — attacker-controllable path is CWE-22 (cognium-ai#326/#275).
|
|
13928
|
+
{ method: "PhysicalFile", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
13836
13929
|
// C# SSRF — HttpClient / WebClient / WebRequest (CWE-918).
|
|
13837
13930
|
{ method: "GetAsync", type: "ssrf", cwe: "CWE-918", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
13838
13931
|
{ method: "PostAsync", type: "ssrf", cwe: "CWE-918", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
@@ -13871,6 +13964,14 @@ var DEFAULT_SINKS = [
|
|
|
13871
13964
|
{ method: "Raw", class: "Html", type: "xss", cwe: "CWE-79", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
13872
13965
|
{ method: "Write", class: "Response", type: "xss", cwe: "CWE-79", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
13873
13966
|
{ method: "HtmlString", type: "xss", cwe: "CWE-79", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
13967
|
+
// Blazor `new MarkupString(x)` renders its argument as raw HTML (the framework's
|
|
13968
|
+
// documented "trusted markup" escape hatch) — attacker-controlled input is XSS. (ca#275)
|
|
13969
|
+
{ method: "MarkupString", class: "constructor", type: "xss", cwe: "CWE-79", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
13970
|
+
// `String.Format(fmt, …)` with an attacker-controlled FORMAT string — CWE-134.
|
|
13971
|
+
// Taint-gated on arg 0 (the format), so ordinary `String.Format("...", user)` where
|
|
13972
|
+
// only an argument is tainted never fires. .NET composite formatting can't corrupt
|
|
13973
|
+
// memory, so medium (FormatException DoS / unintended arg access), unlike C printf.
|
|
13974
|
+
{ method: "Format", class: "String", type: "format_string", cwe: "CWE-134", severity: "medium", arg_positions: [0], languages: ["csharp"] },
|
|
13874
13975
|
// C# LDAP injection — System.DirectoryServices (CWE-90). The user-built
|
|
13875
13976
|
// filter is the constructor argument.
|
|
13876
13977
|
{ method: "DirectorySearcher", type: "ldap_injection", cwe: "CWE-90", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
@@ -13909,6 +14010,10 @@ var DEFAULT_SINKS = [
|
|
|
13909
14010
|
{ method: "SelectSingleNode", type: "xpath_injection", cwe: "CWE-643", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
13910
14011
|
{ method: "SelectNodes", type: "xpath_injection", cwe: "CWE-643", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
13911
14012
|
{ method: "Compile", class: "XPathExpression", type: "xpath_injection", cwe: "CWE-643", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
14013
|
+
// XPathNavigator.Select/Evaluate — class-scoped (both names collide with LINQ
|
|
14014
|
+
// `.Select`/`.Evaluate`, so they must resolve to an XPathNavigator receiver).
|
|
14015
|
+
{ method: "Select", class: "XPathNavigator", type: "xpath_injection", cwe: "CWE-643", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
14016
|
+
{ method: "Evaluate", class: "XPathNavigator", type: "xpath_injection", cwe: "CWE-643", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
13912
14017
|
// C# XXE — untrusted XML into a parser without DTD hardening (CWE-611).
|
|
13913
14018
|
{ method: "LoadXml", type: "xxe", cwe: "CWE-611", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
13914
14019
|
{ method: "Load", class: "XmlDocument", type: "xxe", cwe: "CWE-611", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
@@ -15330,33 +15435,56 @@ function isSafeJSChildProcessCall(call, pattern, language) {
|
|
|
15330
15435
|
if (SHELL_PROGRAMS.has(program)) return false;
|
|
15331
15436
|
return true;
|
|
15332
15437
|
}
|
|
15333
|
-
|
|
15438
|
+
var CSHARP_SHELL_PROGRAMS = /* @__PURE__ */ new Set([
|
|
15439
|
+
"sh",
|
|
15440
|
+
"bash",
|
|
15441
|
+
"zsh",
|
|
15442
|
+
"dash",
|
|
15443
|
+
"ash",
|
|
15444
|
+
"ksh",
|
|
15445
|
+
"cmd",
|
|
15446
|
+
"powershell",
|
|
15447
|
+
"pwsh"
|
|
15448
|
+
]);
|
|
15449
|
+
function isConstNonShellExe(raw) {
|
|
15450
|
+
if (!raw) return false;
|
|
15451
|
+
const t = raw.trim();
|
|
15452
|
+
if (!/^@?"[^"]*"$/.test(t)) return false;
|
|
15453
|
+
const program = (t.replace(/^@?"|"$/g, "").split(/[\\/]/).pop() ?? "").toLowerCase().replace(/\.exe$/, "");
|
|
15454
|
+
return !CSHARP_SHELL_PROGRAMS.has(program);
|
|
15455
|
+
}
|
|
15456
|
+
var PSI_CTOR_EXE_RE = /\bnew\s+ProcessStartInfo\s*(?:<[^>]*>)?\s*\(\s*(@?"[^"]*")/;
|
|
15457
|
+
function processStartInfoExe(expr, sourceLines) {
|
|
15458
|
+
const inline = PSI_CTOR_EXE_RE.exec(expr);
|
|
15459
|
+
if (inline) return inline[1];
|
|
15460
|
+
if (sourceLines && /^[A-Za-z_]\w*$/.test(expr.trim())) {
|
|
15461
|
+
const varName = expr.trim();
|
|
15462
|
+
const assignRe = new RegExp(
|
|
15463
|
+
`\\b${varName}\\s*=\\s*new\\s+ProcessStartInfo\\s*(?:<[^>]*>)?\\s*\\(\\s*(@?"[^"]*")`
|
|
15464
|
+
);
|
|
15465
|
+
for (const line of sourceLines) {
|
|
15466
|
+
const m = assignRe.exec(line);
|
|
15467
|
+
if (m) return m[1];
|
|
15468
|
+
}
|
|
15469
|
+
}
|
|
15470
|
+
return null;
|
|
15471
|
+
}
|
|
15472
|
+
function isSafeCSharpProcessStartCall(call, pattern, language, sourceLines) {
|
|
15334
15473
|
if (language !== "csharp") return false;
|
|
15335
15474
|
if (pattern.type !== "command_injection") return false;
|
|
15336
|
-
|
|
15337
|
-
if (
|
|
15338
|
-
|
|
15339
|
-
|
|
15340
|
-
|
|
15341
|
-
|
|
15342
|
-
raw = String(fileArg.literal).trim();
|
|
15343
|
-
} else {
|
|
15344
|
-
raw = (fileArg.expression ?? "").trim();
|
|
15475
|
+
const method = call.method_name;
|
|
15476
|
+
if (method !== "Start" && method !== "ProcessStartInfo") return false;
|
|
15477
|
+
if (call.arguments.length >= 2) {
|
|
15478
|
+
const fileArg = call.arguments.find((a) => a.position === 0);
|
|
15479
|
+
const raw = fileArg?.literal != null ? String(fileArg.literal) : fileArg?.expression;
|
|
15480
|
+
return isConstNonShellExe(raw);
|
|
15345
15481
|
}
|
|
15346
|
-
if (
|
|
15347
|
-
|
|
15348
|
-
|
|
15349
|
-
|
|
15350
|
-
|
|
15351
|
-
|
|
15352
|
-
"dash",
|
|
15353
|
-
"ash",
|
|
15354
|
-
"ksh",
|
|
15355
|
-
"cmd",
|
|
15356
|
-
"powershell",
|
|
15357
|
-
"pwsh"
|
|
15358
|
-
]);
|
|
15359
|
-
return !SHELL_PROGRAMS.has(program);
|
|
15482
|
+
if (method === "Start" && call.arguments.length === 1) {
|
|
15483
|
+
const arg0 = call.arguments.find((a) => a.position === 0);
|
|
15484
|
+
const expr = (arg0?.expression ?? "").trim();
|
|
15485
|
+
return isConstNonShellExe(processStartInfoExe(expr, sourceLines));
|
|
15486
|
+
}
|
|
15487
|
+
return false;
|
|
15360
15488
|
}
|
|
15361
15489
|
function isSafeRustCommandCall(call, pattern, language) {
|
|
15362
15490
|
if (language !== "rust") return false;
|
|
@@ -15613,7 +15741,7 @@ function findSinks(calls, patterns, typeHierarchy, language, sourceLines, types)
|
|
|
15613
15741
|
if (isSafeJSChildProcessCall(call, pattern, language)) {
|
|
15614
15742
|
continue;
|
|
15615
15743
|
}
|
|
15616
|
-
if (isSafeCSharpProcessStartCall(call, pattern, language)) {
|
|
15744
|
+
if (isSafeCSharpProcessStartCall(call, pattern, language, sourceLines)) {
|
|
15617
15745
|
continue;
|
|
15618
15746
|
}
|
|
15619
15747
|
if (pattern.safe_if_class_literal_at !== void 0 && argIsClassLiteral(call, pattern.safe_if_class_literal_at, types)) {
|
|
@@ -43434,8 +43562,12 @@ var InsecureDeserializationConfigPass = class {
|
|
|
43434
43562
|
name = "insecure-deserialization-config";
|
|
43435
43563
|
category = "security";
|
|
43436
43564
|
run(ctx) {
|
|
43565
|
+
if (ctx.language === "java") return this.runJava(ctx);
|
|
43566
|
+
if (ctx.language === "csharp") return this.runCSharp(ctx);
|
|
43567
|
+
return { findings: [] };
|
|
43568
|
+
}
|
|
43569
|
+
runJava(ctx) {
|
|
43437
43570
|
const { graph, language } = ctx;
|
|
43438
|
-
if (language !== "java") return { findings: [] };
|
|
43439
43571
|
const file = graph.ir.meta.file;
|
|
43440
43572
|
const findings = [];
|
|
43441
43573
|
for (const call of graph.ir.calls) {
|
|
@@ -43460,12 +43592,46 @@ var InsecureDeserializationConfigPass = class {
|
|
|
43460
43592
|
}
|
|
43461
43593
|
return { findings };
|
|
43462
43594
|
}
|
|
43595
|
+
// C# — Json.NET `TypeNameHandling` set to a value other than `None` (ca#318).
|
|
43596
|
+
// `TypeNameHandling.All/Auto/Objects/Arrays` makes Json.NET honour a `$type`
|
|
43597
|
+
// field in the payload and instantiate the named .NET type, so
|
|
43598
|
+
// `JsonConvert.DeserializeObject(untrusted, settings)` becomes a gadget-chain
|
|
43599
|
+
// RCE (the .NET analogue of XStream's grant-all). The vulnerability is the
|
|
43600
|
+
// constant setting, independent of flow — the secure value `None` never matches.
|
|
43601
|
+
runCSharp(ctx) {
|
|
43602
|
+
const file = ctx.graph.ir.meta.file;
|
|
43603
|
+
const findings = [];
|
|
43604
|
+
const lines = ctx.code.split("\n");
|
|
43605
|
+
for (let i2 = 0; i2 < lines.length; i2++) {
|
|
43606
|
+
const m = INSECURE_TYPE_NAME_HANDLING_RE.exec(lines[i2]);
|
|
43607
|
+
if (!m) continue;
|
|
43608
|
+
const line = i2 + 1;
|
|
43609
|
+
const api = `TypeNameHandling = TypeNameHandling.${m[1]}`;
|
|
43610
|
+
findings.push({ line, api });
|
|
43611
|
+
ctx.addFinding({
|
|
43612
|
+
id: `${this.name}-${file}-${line}`,
|
|
43613
|
+
pass: this.name,
|
|
43614
|
+
category: this.category,
|
|
43615
|
+
rule_id: this.name,
|
|
43616
|
+
cwe: "CWE-502",
|
|
43617
|
+
severity: "high",
|
|
43618
|
+
level: "error",
|
|
43619
|
+
message: `Json.NET configured with TypeNameHandling.${m[1]}: a $type field in untrusted JSON can instantiate arbitrary .NET types (deserialization RCE)`,
|
|
43620
|
+
file,
|
|
43621
|
+
line,
|
|
43622
|
+
fix: "Use TypeNameHandling.None (the default), or bind a SerializationBinder that allow-lists the exact types you deserialize.",
|
|
43623
|
+
evidence: { api, language: "csharp" }
|
|
43624
|
+
});
|
|
43625
|
+
}
|
|
43626
|
+
return { findings };
|
|
43627
|
+
}
|
|
43463
43628
|
isPermissiveXStreamConfig(call) {
|
|
43464
43629
|
if (call.method_name !== "addPermission") return false;
|
|
43465
43630
|
const arg0 = call.arguments[0]?.expression;
|
|
43466
43631
|
return typeof arg0 === "string" && ANY_TYPE_PERMISSION_RE.test(arg0);
|
|
43467
43632
|
}
|
|
43468
43633
|
};
|
|
43634
|
+
var INSECURE_TYPE_NAME_HANDLING_RE = /\bTypeNameHandling\s*=\s*(?:Newtonsoft\.Json\.)?TypeNameHandling\.(All|Auto|Objects|Arrays)\b/;
|
|
43469
43635
|
|
|
43470
43636
|
// src/analysis/passes/plaintext-password-storage-pass.ts
|
|
43471
43637
|
function isWriteStorageCall(call, language) {
|
|
@@ -45629,6 +45795,25 @@ function getNodeTypesForLanguage(language) {
|
|
|
45629
45795
|
"selector_expression",
|
|
45630
45796
|
"identifier"
|
|
45631
45797
|
]);
|
|
45798
|
+
case "csharp":
|
|
45799
|
+
return /* @__PURE__ */ new Set([
|
|
45800
|
+
"method_invocation",
|
|
45801
|
+
"object_creation_expression",
|
|
45802
|
+
"class_declaration",
|
|
45803
|
+
"method_declaration",
|
|
45804
|
+
"constructor_declaration",
|
|
45805
|
+
"field_declaration",
|
|
45806
|
+
"import_declaration",
|
|
45807
|
+
"interface_declaration",
|
|
45808
|
+
"enum_declaration",
|
|
45809
|
+
"package_declaration",
|
|
45810
|
+
"local_variable_declaration",
|
|
45811
|
+
// buildCSharpCFG method-like containers
|
|
45812
|
+
"destructor_declaration",
|
|
45813
|
+
"operator_declaration",
|
|
45814
|
+
"local_function_statement",
|
|
45815
|
+
"accessor_declaration"
|
|
45816
|
+
]);
|
|
45632
45817
|
default:
|
|
45633
45818
|
return /* @__PURE__ */ new Set([
|
|
45634
45819
|
"method_invocation",
|