cognium-dev 4.7.2 → 4.9.7
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/cli.js +690 -112
- package/package.json +2 -2
package/dist/cli.js
CHANGED
|
@@ -3646,6 +3646,7 @@ function extractCSharpTypes(tree, cache) {
|
|
|
3646
3646
|
const nameNode = node.childForFieldName("name");
|
|
3647
3647
|
const body2 = node.childForFieldName("body");
|
|
3648
3648
|
const methods = [];
|
|
3649
|
+
const fields = extractCSharpFields(body2);
|
|
3649
3650
|
if (body2) {
|
|
3650
3651
|
for (let i2 = 0;i2 < body2.childCount; i2++) {
|
|
3651
3652
|
const m = body2.child(i2);
|
|
@@ -3665,7 +3666,7 @@ function extractCSharpTypes(tree, cache) {
|
|
|
3665
3666
|
parameters.push({
|
|
3666
3667
|
name: getNodeText(pName),
|
|
3667
3668
|
type: pType ? getNodeText(pType) : null,
|
|
3668
|
-
annotations:
|
|
3669
|
+
annotations: extractCSharpParamAnnotations(pnode),
|
|
3669
3670
|
line: pnode.startPosition.row + 1
|
|
3670
3671
|
});
|
|
3671
3672
|
}
|
|
@@ -3691,7 +3692,7 @@ function extractCSharpTypes(tree, cache) {
|
|
|
3691
3692
|
implements: [],
|
|
3692
3693
|
annotations: [],
|
|
3693
3694
|
methods,
|
|
3694
|
-
fields
|
|
3695
|
+
fields,
|
|
3695
3696
|
start_line: node.startPosition.row + 1,
|
|
3696
3697
|
end_line: node.endPosition.row + 1
|
|
3697
3698
|
});
|
|
@@ -3699,6 +3700,67 @@ function extractCSharpTypes(tree, cache) {
|
|
|
3699
3700
|
}
|
|
3700
3701
|
return types;
|
|
3701
3702
|
}
|
|
3703
|
+
function extractCSharpFields(body2) {
|
|
3704
|
+
const fields = [];
|
|
3705
|
+
if (!body2)
|
|
3706
|
+
return fields;
|
|
3707
|
+
for (let i2 = 0;i2 < body2.childCount; i2++) {
|
|
3708
|
+
const c = body2.child(i2);
|
|
3709
|
+
if (!c)
|
|
3710
|
+
continue;
|
|
3711
|
+
if (c.type === "field_declaration") {
|
|
3712
|
+
let varDecl = null;
|
|
3713
|
+
for (let k = 0;k < c.childCount; k++) {
|
|
3714
|
+
const cc = c.child(k);
|
|
3715
|
+
if (cc?.type === "variable_declaration") {
|
|
3716
|
+
varDecl = cc;
|
|
3717
|
+
break;
|
|
3718
|
+
}
|
|
3719
|
+
}
|
|
3720
|
+
if (!varDecl)
|
|
3721
|
+
continue;
|
|
3722
|
+
const typeNode = varDecl.childForFieldName("type");
|
|
3723
|
+
const type = typeNode ? getNodeText(typeNode) : null;
|
|
3724
|
+
const modifiers = extractCSharpModifiers(c);
|
|
3725
|
+
for (let k = 0;k < varDecl.childCount; k++) {
|
|
3726
|
+
const decl = varDecl.child(k);
|
|
3727
|
+
if (decl?.type !== "variable_declarator")
|
|
3728
|
+
continue;
|
|
3729
|
+
const nameNode = decl.childForFieldName("name");
|
|
3730
|
+
fields.push({ name: nameNode ? getNodeText(nameNode) : "unknown", type, modifiers, annotations: [] });
|
|
3731
|
+
}
|
|
3732
|
+
} else if (c.type === "property_declaration") {
|
|
3733
|
+
const nameNode = c.childForFieldName("name");
|
|
3734
|
+
if (!nameNode)
|
|
3735
|
+
continue;
|
|
3736
|
+
const typeNode = c.childForFieldName("type");
|
|
3737
|
+
fields.push({
|
|
3738
|
+
name: getNodeText(nameNode),
|
|
3739
|
+
type: typeNode ? getNodeText(typeNode) : null,
|
|
3740
|
+
modifiers: extractCSharpModifiers(c),
|
|
3741
|
+
annotations: []
|
|
3742
|
+
});
|
|
3743
|
+
}
|
|
3744
|
+
}
|
|
3745
|
+
return fields;
|
|
3746
|
+
}
|
|
3747
|
+
function extractCSharpParamAnnotations(param) {
|
|
3748
|
+
const out2 = [];
|
|
3749
|
+
for (let i2 = 0;i2 < param.childCount; i2++) {
|
|
3750
|
+
const list = param.child(i2);
|
|
3751
|
+
if (list?.type !== "attribute_list")
|
|
3752
|
+
continue;
|
|
3753
|
+
for (let j = 0;j < list.childCount; j++) {
|
|
3754
|
+
const attr = list.child(j);
|
|
3755
|
+
if (attr?.type !== "attribute")
|
|
3756
|
+
continue;
|
|
3757
|
+
const name2 = attr.childForFieldName("name");
|
|
3758
|
+
if (name2)
|
|
3759
|
+
out2.push(getNodeText(name2));
|
|
3760
|
+
}
|
|
3761
|
+
}
|
|
3762
|
+
return out2;
|
|
3763
|
+
}
|
|
3702
3764
|
function extractCSharpModifiers(node) {
|
|
3703
3765
|
const mods = [];
|
|
3704
3766
|
for (let i2 = 0;i2 < node.childCount; i2++) {
|
|
@@ -5328,15 +5390,24 @@ function extractCSharpCalls(tree, cache) {
|
|
|
5328
5390
|
if (left?.type !== "member_access_expression")
|
|
5329
5391
|
continue;
|
|
5330
5392
|
const nameNode = left.childForFieldName("name");
|
|
5331
|
-
if (!nameNode
|
|
5393
|
+
if (!nameNode)
|
|
5394
|
+
continue;
|
|
5395
|
+
const propName = getNodeText(nameNode);
|
|
5396
|
+
const exprNode = left.childForFieldName("expression");
|
|
5397
|
+
if (propName === "Filter") {
|
|
5398
|
+
const recv = exprNode ? getNodeText(exprNode) : null;
|
|
5399
|
+
const recvType = recv ? typeMap.get(recv) : undefined;
|
|
5400
|
+
if (recvType !== "DirectorySearcher")
|
|
5401
|
+
continue;
|
|
5402
|
+
} else if (propName !== "CommandText") {
|
|
5332
5403
|
continue;
|
|
5404
|
+
}
|
|
5333
5405
|
const right = asn.childForFieldName("right");
|
|
5334
5406
|
if (!right)
|
|
5335
5407
|
continue;
|
|
5336
5408
|
const rhsText = getNodeText(right);
|
|
5337
|
-
const exprNode = left.childForFieldName("expression");
|
|
5338
5409
|
calls.push({
|
|
5339
|
-
method_name:
|
|
5410
|
+
method_name: propName,
|
|
5340
5411
|
receiver: exprNode ? getNodeText(exprNode) : null,
|
|
5341
5412
|
receiver_type: null,
|
|
5342
5413
|
receiver_type_fqn: null,
|
|
@@ -7985,6 +8056,9 @@ function buildCFG(tree, language, cache) {
|
|
|
7985
8056
|
if (effectiveLanguage === "go") {
|
|
7986
8057
|
return buildGoCFG(tree, blockIdCounter, cache);
|
|
7987
8058
|
}
|
|
8059
|
+
if (effectiveLanguage === "csharp") {
|
|
8060
|
+
return buildCSharpCFG(tree, blockIdCounter, cache);
|
|
8061
|
+
}
|
|
7988
8062
|
if (isJavaScript) {
|
|
7989
8063
|
const functions = [
|
|
7990
8064
|
...getNodesFromCache(tree.rootNode, "function_declaration", cache),
|
|
@@ -7998,7 +8072,7 @@ function buildCFG(tree, language, cache) {
|
|
|
7998
8072
|
if (!body2)
|
|
7999
8073
|
continue;
|
|
8000
8074
|
if (body2.type === "statement_block") {
|
|
8001
|
-
const { blocks, edges, nextId } = buildMethodCFG(body2, blockIdCounter,
|
|
8075
|
+
const { blocks, edges, nextId } = buildMethodCFG(body2, blockIdCounter, "js");
|
|
8002
8076
|
allBlocks.push(...blocks);
|
|
8003
8077
|
allEdges.push(...edges);
|
|
8004
8078
|
blockIdCounter = nextId;
|
|
@@ -8021,7 +8095,7 @@ function buildCFG(tree, language, cache) {
|
|
|
8021
8095
|
const body2 = method.childForFieldName("body");
|
|
8022
8096
|
if (!body2)
|
|
8023
8097
|
continue;
|
|
8024
|
-
const { blocks, edges, nextId } = buildMethodCFG(body2, blockIdCounter,
|
|
8098
|
+
const { blocks, edges, nextId } = buildMethodCFG(body2, blockIdCounter, "java");
|
|
8025
8099
|
allBlocks.push(...blocks);
|
|
8026
8100
|
allEdges.push(...edges);
|
|
8027
8101
|
blockIdCounter = nextId;
|
|
@@ -8029,7 +8103,29 @@ function buildCFG(tree, language, cache) {
|
|
|
8029
8103
|
}
|
|
8030
8104
|
return { blocks: allBlocks, edges: allEdges };
|
|
8031
8105
|
}
|
|
8032
|
-
function
|
|
8106
|
+
function buildCSharpCFG(tree, blockIdCounter, cache) {
|
|
8107
|
+
const allBlocks = [];
|
|
8108
|
+
const allEdges = [];
|
|
8109
|
+
const containers = [
|
|
8110
|
+
...getNodesFromCache(tree.rootNode, "method_declaration", cache),
|
|
8111
|
+
...getNodesFromCache(tree.rootNode, "constructor_declaration", cache),
|
|
8112
|
+
...getNodesFromCache(tree.rootNode, "destructor_declaration", cache),
|
|
8113
|
+
...getNodesFromCache(tree.rootNode, "operator_declaration", cache),
|
|
8114
|
+
...getNodesFromCache(tree.rootNode, "local_function_statement", cache),
|
|
8115
|
+
...getNodesFromCache(tree.rootNode, "accessor_declaration", cache)
|
|
8116
|
+
];
|
|
8117
|
+
for (const container of containers) {
|
|
8118
|
+
const body2 = container.childForFieldName("body");
|
|
8119
|
+
if (!body2 || body2.type !== "block")
|
|
8120
|
+
continue;
|
|
8121
|
+
const { blocks, edges, nextId } = buildMethodCFG(body2, blockIdCounter, "csharp");
|
|
8122
|
+
allBlocks.push(...blocks);
|
|
8123
|
+
allEdges.push(...edges);
|
|
8124
|
+
blockIdCounter = nextId;
|
|
8125
|
+
}
|
|
8126
|
+
return { blocks: allBlocks, edges: allEdges };
|
|
8127
|
+
}
|
|
8128
|
+
function buildMethodCFG(body2, startId, dialect) {
|
|
8033
8129
|
const blocks = [];
|
|
8034
8130
|
const edges = [];
|
|
8035
8131
|
let currentId = startId;
|
|
@@ -8040,7 +8136,7 @@ function buildMethodCFG(body2, startId, isJavaScript) {
|
|
|
8040
8136
|
end_line: body2.startPosition.row + 1
|
|
8041
8137
|
};
|
|
8042
8138
|
blocks.push(entryBlock);
|
|
8043
|
-
const result = processStatements(body2, currentId, blocks, edges,
|
|
8139
|
+
const result = processStatements(body2, currentId, blocks, edges, dialect);
|
|
8044
8140
|
currentId = result.nextId;
|
|
8045
8141
|
if (result.entryId !== -1) {
|
|
8046
8142
|
edges.push({
|
|
@@ -8072,7 +8168,7 @@ function buildMethodCFG(body2, startId, isJavaScript) {
|
|
|
8072
8168
|
}
|
|
8073
8169
|
return { blocks, edges, nextId: currentId };
|
|
8074
8170
|
}
|
|
8075
|
-
function processStatements(container, startId, blocks, edges,
|
|
8171
|
+
function processStatements(container, startId, blocks, edges, dialect) {
|
|
8076
8172
|
let currentId = startId;
|
|
8077
8173
|
let firstBlockId = -1;
|
|
8078
8174
|
let lastExitIds = [];
|
|
@@ -8080,9 +8176,9 @@ function processStatements(container, startId, blocks, edges, isJavaScript) {
|
|
|
8080
8176
|
const stmt = container.child(i2);
|
|
8081
8177
|
if (!stmt)
|
|
8082
8178
|
continue;
|
|
8083
|
-
if (!isStatement(stmt,
|
|
8179
|
+
if (!isStatement(stmt, dialect))
|
|
8084
8180
|
continue;
|
|
8085
|
-
const result = processStatement(stmt, currentId, blocks, edges,
|
|
8181
|
+
const result = processStatement(stmt, currentId, blocks, edges, dialect);
|
|
8086
8182
|
currentId = result.nextId;
|
|
8087
8183
|
if (firstBlockId === -1) {
|
|
8088
8184
|
firstBlockId = result.entryId;
|
|
@@ -8103,31 +8199,49 @@ function processStatements(container, startId, blocks, edges, isJavaScript) {
|
|
|
8103
8199
|
nextId: currentId
|
|
8104
8200
|
};
|
|
8105
8201
|
}
|
|
8106
|
-
function processStatement(stmt, startId, blocks, edges,
|
|
8202
|
+
function processStatement(stmt, startId, blocks, edges, dialect) {
|
|
8107
8203
|
switch (stmt.type) {
|
|
8108
8204
|
case "if_statement":
|
|
8109
|
-
return processIfStatement(stmt, startId, blocks, edges,
|
|
8205
|
+
return processIfStatement(stmt, startId, blocks, edges, dialect);
|
|
8110
8206
|
case "for_statement":
|
|
8111
8207
|
case "enhanced_for_statement":
|
|
8112
8208
|
case "for_in_statement":
|
|
8113
8209
|
case "for_of_statement":
|
|
8114
|
-
|
|
8210
|
+
case "foreach_statement":
|
|
8211
|
+
return processForStatement(stmt, startId, blocks, edges, dialect);
|
|
8115
8212
|
case "while_statement":
|
|
8116
|
-
return processWhileStatement(stmt, startId, blocks, edges,
|
|
8213
|
+
return processWhileStatement(stmt, startId, blocks, edges, dialect);
|
|
8117
8214
|
case "do_statement":
|
|
8118
|
-
return processDoWhileStatement(stmt, startId, blocks, edges,
|
|
8215
|
+
return processDoWhileStatement(stmt, startId, blocks, edges, dialect);
|
|
8119
8216
|
case "try_statement":
|
|
8120
|
-
return processTryStatement(stmt, startId, blocks, edges,
|
|
8217
|
+
return processTryStatement(stmt, startId, blocks, edges, dialect);
|
|
8121
8218
|
case "switch_expression":
|
|
8122
8219
|
case "switch_statement":
|
|
8123
|
-
return processSwitchStatement(stmt, startId, blocks, edges,
|
|
8220
|
+
return processSwitchStatement(stmt, startId, blocks, edges, dialect);
|
|
8221
|
+
case "using_statement":
|
|
8222
|
+
case "lock_statement":
|
|
8223
|
+
case "checked_statement":
|
|
8224
|
+
case "unsafe_statement": {
|
|
8225
|
+
const inner = stmt.childForFieldName("body") ?? lastBlockChild(stmt);
|
|
8226
|
+
if (inner)
|
|
8227
|
+
return processStatement(inner, startId, blocks, edges, dialect);
|
|
8228
|
+
return processSimpleStatement(stmt, startId, blocks);
|
|
8229
|
+
}
|
|
8124
8230
|
case "block":
|
|
8125
8231
|
case "statement_block":
|
|
8126
|
-
return processStatements(stmt, startId, blocks, edges,
|
|
8232
|
+
return processStatements(stmt, startId, blocks, edges, dialect);
|
|
8127
8233
|
default:
|
|
8128
8234
|
return processSimpleStatement(stmt, startId, blocks);
|
|
8129
8235
|
}
|
|
8130
8236
|
}
|
|
8237
|
+
function lastBlockChild(node) {
|
|
8238
|
+
for (let i2 = node.childCount - 1;i2 >= 0; i2--) {
|
|
8239
|
+
const c = node.child(i2);
|
|
8240
|
+
if (c && c.type === "block")
|
|
8241
|
+
return c;
|
|
8242
|
+
}
|
|
8243
|
+
return null;
|
|
8244
|
+
}
|
|
8131
8245
|
function processSimpleStatement(stmt, startId, blocks) {
|
|
8132
8246
|
const block = {
|
|
8133
8247
|
id: startId,
|
|
@@ -8142,7 +8256,7 @@ function processSimpleStatement(stmt, startId, blocks) {
|
|
|
8142
8256
|
nextId: startId + 1
|
|
8143
8257
|
};
|
|
8144
8258
|
}
|
|
8145
|
-
function processIfStatement(stmt, startId, blocks, edges,
|
|
8259
|
+
function processIfStatement(stmt, startId, blocks, edges, dialect) {
|
|
8146
8260
|
let currentId = startId;
|
|
8147
8261
|
const condBlock = {
|
|
8148
8262
|
id: currentId++,
|
|
@@ -8154,7 +8268,7 @@ function processIfStatement(stmt, startId, blocks, edges, isJavaScript) {
|
|
|
8154
8268
|
const exitIds = [];
|
|
8155
8269
|
const consequence = stmt.childForFieldName("consequence");
|
|
8156
8270
|
if (consequence) {
|
|
8157
|
-
const thenResult = processStatement(consequence, currentId, blocks, edges,
|
|
8271
|
+
const thenResult = processStatement(consequence, currentId, blocks, edges, dialect);
|
|
8158
8272
|
currentId = thenResult.nextId;
|
|
8159
8273
|
edges.push({
|
|
8160
8274
|
from: condBlock.id,
|
|
@@ -8165,7 +8279,7 @@ function processIfStatement(stmt, startId, blocks, edges, isJavaScript) {
|
|
|
8165
8279
|
}
|
|
8166
8280
|
const alternative = stmt.childForFieldName("alternative");
|
|
8167
8281
|
if (alternative) {
|
|
8168
|
-
const elseResult = processStatement(alternative, currentId, blocks, edges,
|
|
8282
|
+
const elseResult = processStatement(alternative, currentId, blocks, edges, dialect);
|
|
8169
8283
|
currentId = elseResult.nextId;
|
|
8170
8284
|
edges.push({
|
|
8171
8285
|
from: condBlock.id,
|
|
@@ -8182,7 +8296,7 @@ function processIfStatement(stmt, startId, blocks, edges, isJavaScript) {
|
|
|
8182
8296
|
nextId: currentId
|
|
8183
8297
|
};
|
|
8184
8298
|
}
|
|
8185
|
-
function processForStatement(stmt, startId, blocks, edges,
|
|
8299
|
+
function processForStatement(stmt, startId, blocks, edges, dialect) {
|
|
8186
8300
|
let currentId = startId;
|
|
8187
8301
|
const loopBlock = {
|
|
8188
8302
|
id: currentId++,
|
|
@@ -8193,7 +8307,7 @@ function processForStatement(stmt, startId, blocks, edges, isJavaScript) {
|
|
|
8193
8307
|
blocks.push(loopBlock);
|
|
8194
8308
|
const body2 = stmt.childForFieldName("body");
|
|
8195
8309
|
if (body2) {
|
|
8196
|
-
const bodyResult = processStatement(body2, currentId, blocks, edges,
|
|
8310
|
+
const bodyResult = processStatement(body2, currentId, blocks, edges, dialect);
|
|
8197
8311
|
currentId = bodyResult.nextId;
|
|
8198
8312
|
edges.push({
|
|
8199
8313
|
from: loopBlock.id,
|
|
@@ -8214,16 +8328,16 @@ function processForStatement(stmt, startId, blocks, edges, isJavaScript) {
|
|
|
8214
8328
|
nextId: currentId
|
|
8215
8329
|
};
|
|
8216
8330
|
}
|
|
8217
|
-
function processWhileStatement(stmt, startId, blocks, edges,
|
|
8218
|
-
return processForStatement(stmt, startId, blocks, edges,
|
|
8331
|
+
function processWhileStatement(stmt, startId, blocks, edges, dialect) {
|
|
8332
|
+
return processForStatement(stmt, startId, blocks, edges, dialect);
|
|
8219
8333
|
}
|
|
8220
|
-
function processDoWhileStatement(stmt, startId, blocks, edges,
|
|
8334
|
+
function processDoWhileStatement(stmt, startId, blocks, edges, dialect) {
|
|
8221
8335
|
let currentId = startId;
|
|
8222
8336
|
const body2 = stmt.childForFieldName("body");
|
|
8223
8337
|
let bodyEntryId = currentId;
|
|
8224
8338
|
let bodyExitIds = [];
|
|
8225
8339
|
if (body2) {
|
|
8226
|
-
const bodyResult = processStatement(body2, currentId, blocks, edges,
|
|
8340
|
+
const bodyResult = processStatement(body2, currentId, blocks, edges, dialect);
|
|
8227
8341
|
currentId = bodyResult.nextId;
|
|
8228
8342
|
bodyEntryId = bodyResult.entryId;
|
|
8229
8343
|
bodyExitIds = bodyResult.exitIds;
|
|
@@ -8253,13 +8367,13 @@ function processDoWhileStatement(stmt, startId, blocks, edges, isJavaScript) {
|
|
|
8253
8367
|
nextId: currentId
|
|
8254
8368
|
};
|
|
8255
8369
|
}
|
|
8256
|
-
function processTryStatement(stmt, startId, blocks, edges,
|
|
8370
|
+
function processTryStatement(stmt, startId, blocks, edges, dialect) {
|
|
8257
8371
|
let currentId = startId;
|
|
8258
8372
|
const exitIds = [];
|
|
8259
8373
|
const body2 = stmt.childForFieldName("body");
|
|
8260
8374
|
let tryEntryId = -1;
|
|
8261
8375
|
if (body2) {
|
|
8262
|
-
const bodyResult = processStatements(body2, currentId, blocks, edges,
|
|
8376
|
+
const bodyResult = processStatements(body2, currentId, blocks, edges, dialect);
|
|
8263
8377
|
currentId = bodyResult.nextId;
|
|
8264
8378
|
tryEntryId = bodyResult.entryId;
|
|
8265
8379
|
exitIds.push(...bodyResult.exitIds);
|
|
@@ -8269,7 +8383,7 @@ function processTryStatement(stmt, startId, blocks, edges, isJavaScript) {
|
|
|
8269
8383
|
if (child?.type === "catch_clause") {
|
|
8270
8384
|
const catchBody = child.childForFieldName("body");
|
|
8271
8385
|
if (catchBody) {
|
|
8272
|
-
const catchResult = processStatements(catchBody, currentId, blocks, edges,
|
|
8386
|
+
const catchResult = processStatements(catchBody, currentId, blocks, edges, dialect);
|
|
8273
8387
|
currentId = catchResult.nextId;
|
|
8274
8388
|
if (tryEntryId !== -1) {
|
|
8275
8389
|
edges.push({
|
|
@@ -8282,9 +8396,18 @@ function processTryStatement(stmt, startId, blocks, edges, isJavaScript) {
|
|
|
8282
8396
|
}
|
|
8283
8397
|
}
|
|
8284
8398
|
}
|
|
8285
|
-
|
|
8399
|
+
let finallyClause = stmt.childForFieldName("finally") ?? stmt.childForFieldName("finalizer");
|
|
8400
|
+
if (!finallyClause && dialect === "csharp") {
|
|
8401
|
+
for (let i2 = 0;i2 < stmt.childCount; i2++) {
|
|
8402
|
+
const child = stmt.child(i2);
|
|
8403
|
+
if (child?.type === "finally_clause") {
|
|
8404
|
+
finallyClause = lastBlockChild(child);
|
|
8405
|
+
break;
|
|
8406
|
+
}
|
|
8407
|
+
}
|
|
8408
|
+
}
|
|
8286
8409
|
if (finallyClause) {
|
|
8287
|
-
const finallyResult = processStatements(finallyClause, currentId, blocks, edges,
|
|
8410
|
+
const finallyResult = processStatements(finallyClause, currentId, blocks, edges, dialect);
|
|
8288
8411
|
currentId = finallyResult.nextId;
|
|
8289
8412
|
for (const exitId of exitIds) {
|
|
8290
8413
|
edges.push({
|
|
@@ -8305,7 +8428,7 @@ function processTryStatement(stmt, startId, blocks, edges, isJavaScript) {
|
|
|
8305
8428
|
nextId: currentId
|
|
8306
8429
|
};
|
|
8307
8430
|
}
|
|
8308
|
-
function processSwitchStatement(stmt, startId, blocks, edges,
|
|
8431
|
+
function processSwitchStatement(stmt, startId, blocks, edges, dialect) {
|
|
8309
8432
|
let currentId = startId;
|
|
8310
8433
|
const switchBlock = {
|
|
8311
8434
|
id: currentId++,
|
|
@@ -8317,11 +8440,11 @@ function processSwitchStatement(stmt, startId, blocks, edges, isJavaScript) {
|
|
|
8317
8440
|
const exitIds = [];
|
|
8318
8441
|
const body2 = stmt.childForFieldName("body");
|
|
8319
8442
|
if (body2) {
|
|
8320
|
-
const caseTypes =
|
|
8443
|
+
const caseTypes = dialect === "js" ? ["switch_case", "switch_default"] : dialect === "csharp" ? ["switch_section"] : ["switch_block_statement_group", "switch_rule"];
|
|
8321
8444
|
for (let i2 = 0;i2 < body2.childCount; i2++) {
|
|
8322
8445
|
const child = body2.child(i2);
|
|
8323
8446
|
if (child && caseTypes.includes(child.type)) {
|
|
8324
|
-
const caseResult = processStatements(child, currentId, blocks, edges,
|
|
8447
|
+
const caseResult = processStatements(child, currentId, blocks, edges, dialect);
|
|
8325
8448
|
currentId = caseResult.nextId;
|
|
8326
8449
|
if (caseResult.entryId !== -1) {
|
|
8327
8450
|
edges.push({
|
|
@@ -8352,7 +8475,7 @@ function buildBashCFG(tree, startId, cache) {
|
|
|
8352
8475
|
const body2 = func2.childForFieldName("body");
|
|
8353
8476
|
if (!body2)
|
|
8354
8477
|
continue;
|
|
8355
|
-
const { blocks, edges, nextId } = buildMethodCFG(body2, blockIdCounter,
|
|
8478
|
+
const { blocks, edges, nextId } = buildMethodCFG(body2, blockIdCounter, "java");
|
|
8356
8479
|
allBlocks.push(...blocks);
|
|
8357
8480
|
allEdges.push(...edges);
|
|
8358
8481
|
blockIdCounter = nextId;
|
|
@@ -8375,7 +8498,7 @@ function buildBashCFG(tree, startId, cache) {
|
|
|
8375
8498
|
let lastExitIds = [];
|
|
8376
8499
|
let firstBlockId = -1;
|
|
8377
8500
|
for (const stmt of topLevelStatements) {
|
|
8378
|
-
const result = processStatement(stmt, blockIdCounter, allBlocks, allEdges,
|
|
8501
|
+
const result = processStatement(stmt, blockIdCounter, allBlocks, allEdges, "java");
|
|
8379
8502
|
blockIdCounter = result.nextId;
|
|
8380
8503
|
if (firstBlockId === -1) {
|
|
8381
8504
|
firstBlockId = result.entryId;
|
|
@@ -8419,7 +8542,9 @@ function isBashStatement(node) {
|
|
|
8419
8542
|
]);
|
|
8420
8543
|
return bashStatementTypes.has(node.type);
|
|
8421
8544
|
}
|
|
8422
|
-
function isStatement(node,
|
|
8545
|
+
function isStatement(node, dialect) {
|
|
8546
|
+
if (dialect === "csharp")
|
|
8547
|
+
return csharpStatementTypes.has(node.type);
|
|
8423
8548
|
const javaStatementTypes = new Set([
|
|
8424
8549
|
"local_variable_declaration",
|
|
8425
8550
|
"expression_statement",
|
|
@@ -8463,8 +8588,30 @@ function isStatement(node, isJavaScript) {
|
|
|
8463
8588
|
"export_statement",
|
|
8464
8589
|
"import_statement"
|
|
8465
8590
|
]);
|
|
8466
|
-
return
|
|
8467
|
-
}
|
|
8591
|
+
return dialect === "js" ? jsStatementTypes.has(node.type) : javaStatementTypes.has(node.type);
|
|
8592
|
+
}
|
|
8593
|
+
var csharpStatementTypes = new Set([
|
|
8594
|
+
"local_declaration_statement",
|
|
8595
|
+
"expression_statement",
|
|
8596
|
+
"if_statement",
|
|
8597
|
+
"for_statement",
|
|
8598
|
+
"foreach_statement",
|
|
8599
|
+
"while_statement",
|
|
8600
|
+
"do_statement",
|
|
8601
|
+
"try_statement",
|
|
8602
|
+
"switch_statement",
|
|
8603
|
+
"return_statement",
|
|
8604
|
+
"throw_statement",
|
|
8605
|
+
"break_statement",
|
|
8606
|
+
"continue_statement",
|
|
8607
|
+
"using_statement",
|
|
8608
|
+
"lock_statement",
|
|
8609
|
+
"checked_statement",
|
|
8610
|
+
"unsafe_statement",
|
|
8611
|
+
"yield_statement",
|
|
8612
|
+
"goto_statement",
|
|
8613
|
+
"block"
|
|
8614
|
+
]);
|
|
8468
8615
|
function buildGoCFG(tree, blockIdCounter, cache) {
|
|
8469
8616
|
const allBlocks = [];
|
|
8470
8617
|
const allEdges = [];
|
|
@@ -8476,7 +8623,7 @@ function buildGoCFG(tree, blockIdCounter, cache) {
|
|
|
8476
8623
|
const body2 = func2.childForFieldName("body");
|
|
8477
8624
|
if (!body2 || body2.type !== "block")
|
|
8478
8625
|
continue;
|
|
8479
|
-
const { blocks, edges, nextId } = buildMethodCFG(body2, blockIdCounter,
|
|
8626
|
+
const { blocks, edges, nextId } = buildMethodCFG(body2, blockIdCounter, "java");
|
|
8480
8627
|
allBlocks.push(...blocks);
|
|
8481
8628
|
allEdges.push(...edges);
|
|
8482
8629
|
blockIdCounter = nextId;
|
|
@@ -12328,6 +12475,7 @@ var DEFAULT_SINKS = [
|
|
|
12328
12475
|
{ method: "FromSqlRaw", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0], languages: ["csharp"] },
|
|
12329
12476
|
{ method: "FromSqlRawAsync", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0], languages: ["csharp"] },
|
|
12330
12477
|
{ method: "CommandText", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0], languages: ["csharp"] },
|
|
12478
|
+
{ method: "Filter", type: "ldap_injection", cwe: "CWE-90", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
12331
12479
|
{ method: "ExecuteScalar", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0], languages: ["csharp"] },
|
|
12332
12480
|
{ method: "ExecuteReader", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0], languages: ["csharp"] },
|
|
12333
12481
|
{ method: "ExecuteNonQuery", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0], languages: ["csharp"] },
|
|
@@ -12336,6 +12484,7 @@ var DEFAULT_SINKS = [
|
|
|
12336
12484
|
{ method: "ExecuteNonQueryAsync", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0], languages: ["csharp"] },
|
|
12337
12485
|
{ method: "Start", class: "Process", type: "command_injection", cwe: "CWE-78", severity: "critical", arg_positions: [0, 1], languages: ["csharp"] },
|
|
12338
12486
|
{ method: "ProcessStartInfo", type: "command_injection", cwe: "CWE-78", severity: "critical", arg_positions: [0, 1], languages: ["csharp"] },
|
|
12487
|
+
{ method: "system", type: "command_injection", cwe: "CWE-78", severity: "critical", arg_positions: [0], languages: ["csharp"] },
|
|
12339
12488
|
{ method: "ReadAllText", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
12340
12489
|
{ method: "ReadAllBytes", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
12341
12490
|
{ method: "ReadAllLines", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
@@ -12359,6 +12508,7 @@ var DEFAULT_SINKS = [
|
|
|
12359
12508
|
{ method: "Delete", class: "Directory", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
12360
12509
|
{ method: "GetFiles", class: "Directory", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
12361
12510
|
{ method: "EnumerateFiles", class: "Directory", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
12511
|
+
{ method: "PhysicalFile", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
12362
12512
|
{ method: "GetAsync", type: "ssrf", cwe: "CWE-918", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
12363
12513
|
{ method: "PostAsync", type: "ssrf", cwe: "CWE-918", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
12364
12514
|
{ method: "PutAsync", type: "ssrf", cwe: "CWE-918", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
@@ -12376,6 +12526,9 @@ var DEFAULT_SINKS = [
|
|
|
12376
12526
|
{ method: "Load", class: "Assembly", type: "code_injection", cwe: "CWE-94", severity: "critical", arg_positions: [0], languages: ["csharp"] },
|
|
12377
12527
|
{ method: "LoadFrom", class: "Assembly", type: "code_injection", cwe: "CWE-94", severity: "critical", arg_positions: [0], languages: ["csharp"] },
|
|
12378
12528
|
{ method: "LoadFile", class: "Assembly", type: "code_injection", cwe: "CWE-94", severity: "critical", arg_positions: [0], languages: ["csharp"] },
|
|
12529
|
+
{ method: "RunCompile", type: "code_injection", cwe: "CWE-94", severity: "critical", arg_positions: [0], languages: ["csharp"] },
|
|
12530
|
+
{ method: "CompileRenderStringAsync", type: "code_injection", cwe: "CWE-94", severity: "critical", arg_positions: [0, 1], languages: ["csharp"] },
|
|
12531
|
+
{ method: "Compile", class: "Handlebars", type: "code_injection", cwe: "CWE-94", severity: "critical", arg_positions: [0], languages: ["csharp"] },
|
|
12379
12532
|
{ method: "Deserialize", class: "BinaryFormatter", type: "deserialization", cwe: "CWE-502", severity: "critical", arg_positions: [0], languages: ["csharp"] },
|
|
12380
12533
|
{ method: "Deserialize", class: "NetDataContractSerializer", type: "deserialization", cwe: "CWE-502", severity: "critical", arg_positions: [0], languages: ["csharp"] },
|
|
12381
12534
|
{ method: "Deserialize", class: "SoapFormatter", type: "deserialization", cwe: "CWE-502", severity: "critical", arg_positions: [0], languages: ["csharp"] },
|
|
@@ -12384,6 +12537,8 @@ var DEFAULT_SINKS = [
|
|
|
12384
12537
|
{ method: "Raw", class: "Html", type: "xss", cwe: "CWE-79", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
12385
12538
|
{ method: "Write", class: "Response", type: "xss", cwe: "CWE-79", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
12386
12539
|
{ method: "HtmlString", type: "xss", cwe: "CWE-79", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
12540
|
+
{ method: "MarkupString", class: "constructor", type: "xss", cwe: "CWE-79", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
12541
|
+
{ method: "Format", class: "String", type: "format_string", cwe: "CWE-134", severity: "medium", arg_positions: [0], languages: ["csharp"] },
|
|
12387
12542
|
{ method: "DirectorySearcher", type: "ldap_injection", cwe: "CWE-90", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
12388
12543
|
{ method: "DirectoryEntry", type: "ldap_injection", cwe: "CWE-90", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
12389
12544
|
{ method: "Redirect", type: "open_redirect", cwe: "CWE-601", severity: "medium", arg_positions: [0], languages: ["csharp"] },
|
|
@@ -12401,6 +12556,8 @@ var DEFAULT_SINKS = [
|
|
|
12401
12556
|
{ method: "SelectSingleNode", type: "xpath_injection", cwe: "CWE-643", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
12402
12557
|
{ method: "SelectNodes", type: "xpath_injection", cwe: "CWE-643", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
12403
12558
|
{ method: "Compile", class: "XPathExpression", type: "xpath_injection", cwe: "CWE-643", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
12559
|
+
{ method: "Select", class: "XPathNavigator", type: "xpath_injection", cwe: "CWE-643", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
12560
|
+
{ method: "Evaluate", class: "XPathNavigator", type: "xpath_injection", cwe: "CWE-643", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
12404
12561
|
{ method: "LoadXml", type: "xxe", cwe: "CWE-611", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
12405
12562
|
{ method: "Load", class: "XmlDocument", type: "xxe", cwe: "CWE-611", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
12406
12563
|
{ method: "Load", class: "XDocument", type: "xxe", cwe: "CWE-611", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
@@ -13179,7 +13336,8 @@ function findSources(calls, types, patterns, sourceLines, language) {
|
|
|
13179
13336
|
if (skipMethods.includes(method.name))
|
|
13180
13337
|
continue;
|
|
13181
13338
|
for (const param of method.parameters) {
|
|
13182
|
-
const
|
|
13339
|
+
const hasCSharpBindingAttr = language === "csharp" && param.annotations.some((a) => CSHARP_BINDING_ATTRS.has(a));
|
|
13340
|
+
const isTaintable = hasCSharpBindingAttr ? true : param.type ? isInterproceduralTaintableType(param.type, language) : true;
|
|
13183
13341
|
if (isTaintable) {
|
|
13184
13342
|
const paramLine = param.line ?? method.start_line;
|
|
13185
13343
|
sources.push({
|
|
@@ -13292,6 +13450,13 @@ function findSources(calls, types, patterns, sourceLines, language) {
|
|
|
13292
13450
|
}
|
|
13293
13451
|
return result;
|
|
13294
13452
|
}
|
|
13453
|
+
var CSHARP_BINDING_ATTRS = new Set([
|
|
13454
|
+
"FromBody",
|
|
13455
|
+
"FromQuery",
|
|
13456
|
+
"FromRoute",
|
|
13457
|
+
"FromForm",
|
|
13458
|
+
"FromHeader"
|
|
13459
|
+
]);
|
|
13295
13460
|
function isInterproceduralTaintableType(typeName, language) {
|
|
13296
13461
|
const baseType = typeName.split("<")[0].trim();
|
|
13297
13462
|
const excludedTypes = [
|
|
@@ -13540,39 +13705,226 @@ function isSafeJSChildProcessCall(call, pattern, language) {
|
|
|
13540
13705
|
return false;
|
|
13541
13706
|
return true;
|
|
13542
13707
|
}
|
|
13543
|
-
|
|
13708
|
+
var CSHARP_SHELL_PROGRAMS = new Set([
|
|
13709
|
+
"sh",
|
|
13710
|
+
"bash",
|
|
13711
|
+
"zsh",
|
|
13712
|
+
"dash",
|
|
13713
|
+
"ash",
|
|
13714
|
+
"ksh",
|
|
13715
|
+
"cmd",
|
|
13716
|
+
"powershell",
|
|
13717
|
+
"pwsh"
|
|
13718
|
+
]);
|
|
13719
|
+
function isConstNonShellExe(raw) {
|
|
13720
|
+
if (!raw)
|
|
13721
|
+
return false;
|
|
13722
|
+
const t = raw.trim();
|
|
13723
|
+
if (!/^@?"[^"]*"$/.test(t))
|
|
13724
|
+
return false;
|
|
13725
|
+
const program = (t.replace(/^@?"|"$/g, "").split(/[\\/]/).pop() ?? "").toLowerCase().replace(/\.exe$/, "");
|
|
13726
|
+
return !CSHARP_SHELL_PROGRAMS.has(program);
|
|
13727
|
+
}
|
|
13728
|
+
var PSI_CTOR_EXE_RE = /\bnew\s+ProcessStartInfo\s*(?:<[^>]*>)?\s*\(\s*(@?"[^"]*")/;
|
|
13729
|
+
function processStartInfoExe(expr, sourceLines) {
|
|
13730
|
+
const inline = PSI_CTOR_EXE_RE.exec(expr);
|
|
13731
|
+
if (inline)
|
|
13732
|
+
return inline[1];
|
|
13733
|
+
if (sourceLines && /^[A-Za-z_]\w*$/.test(expr.trim())) {
|
|
13734
|
+
const varName = expr.trim();
|
|
13735
|
+
const assignRe = new RegExp(`\\b${varName}\\s*=\\s*new\\s+ProcessStartInfo\\s*(?:<[^>]*>)?\\s*\\(\\s*(@?"[^"]*")`);
|
|
13736
|
+
for (const line of sourceLines) {
|
|
13737
|
+
const m = assignRe.exec(line);
|
|
13738
|
+
if (m)
|
|
13739
|
+
return m[1];
|
|
13740
|
+
}
|
|
13741
|
+
}
|
|
13742
|
+
return null;
|
|
13743
|
+
}
|
|
13744
|
+
function isSafeCSharpProcessStartCall(call, pattern, language, sourceLines) {
|
|
13544
13745
|
if (language !== "csharp")
|
|
13545
13746
|
return false;
|
|
13546
13747
|
if (pattern.type !== "command_injection")
|
|
13547
13748
|
return false;
|
|
13548
|
-
|
|
13749
|
+
const method = call.method_name;
|
|
13750
|
+
if (method !== "Start" && method !== "ProcessStartInfo")
|
|
13549
13751
|
return false;
|
|
13550
|
-
if (call.arguments.length
|
|
13752
|
+
if (call.arguments.length >= 2) {
|
|
13753
|
+
const fileArg = call.arguments.find((a) => a.position === 0);
|
|
13754
|
+
const raw = fileArg?.literal != null ? String(fileArg.literal) : fileArg?.expression;
|
|
13755
|
+
return isConstNonShellExe(raw);
|
|
13756
|
+
}
|
|
13757
|
+
if (method === "Start" && call.arguments.length === 1) {
|
|
13758
|
+
const arg0 = call.arguments.find((a) => a.position === 0);
|
|
13759
|
+
const expr = (arg0?.expression ?? "").trim();
|
|
13760
|
+
return isConstNonShellExe(processStartInfoExe(expr, sourceLines));
|
|
13761
|
+
}
|
|
13762
|
+
return false;
|
|
13763
|
+
}
|
|
13764
|
+
function stripCsLiterals(line) {
|
|
13765
|
+
return line.replace(/\/\/.*$/, "").replace(/@?"(?:[^"\\]|\\.)*"/g, '""').replace(/'(?:[^'\\]|\\.)'/g, "''");
|
|
13766
|
+
}
|
|
13767
|
+
function csBraceDepthBefore(lines, idx) {
|
|
13768
|
+
let depth = 0;
|
|
13769
|
+
for (let i2 = 0;i2 < idx && i2 < lines.length; i2++) {
|
|
13770
|
+
for (const ch of stripCsLiterals(lines[i2])) {
|
|
13771
|
+
if (ch === "{")
|
|
13772
|
+
depth++;
|
|
13773
|
+
else if (ch === "}")
|
|
13774
|
+
depth--;
|
|
13775
|
+
}
|
|
13776
|
+
}
|
|
13777
|
+
return depth;
|
|
13778
|
+
}
|
|
13779
|
+
function netBraces(fragment) {
|
|
13780
|
+
let n = 0;
|
|
13781
|
+
for (const ch of fragment) {
|
|
13782
|
+
if (ch === "{")
|
|
13783
|
+
n++;
|
|
13784
|
+
else if (ch === "}")
|
|
13785
|
+
n--;
|
|
13786
|
+
}
|
|
13787
|
+
return n;
|
|
13788
|
+
}
|
|
13789
|
+
function splitCsIf(line) {
|
|
13790
|
+
const m = /\bif\s*\(/.exec(line);
|
|
13791
|
+
if (!m)
|
|
13792
|
+
return null;
|
|
13793
|
+
const ifCol = m.index;
|
|
13794
|
+
let i2 = m.index + m[0].length;
|
|
13795
|
+
let depth = 1;
|
|
13796
|
+
let inStr = false;
|
|
13797
|
+
let strCh = "";
|
|
13798
|
+
for (;i2 < line.length; i2++) {
|
|
13799
|
+
const c = line[i2];
|
|
13800
|
+
if (inStr) {
|
|
13801
|
+
if (c === "\\") {
|
|
13802
|
+
i2++;
|
|
13803
|
+
continue;
|
|
13804
|
+
}
|
|
13805
|
+
if (c === strCh)
|
|
13806
|
+
inStr = false;
|
|
13807
|
+
continue;
|
|
13808
|
+
}
|
|
13809
|
+
if (c === '"' || c === "'") {
|
|
13810
|
+
inStr = true;
|
|
13811
|
+
strCh = c;
|
|
13812
|
+
continue;
|
|
13813
|
+
}
|
|
13814
|
+
if (c === "(")
|
|
13815
|
+
depth++;
|
|
13816
|
+
else if (c === ")") {
|
|
13817
|
+
depth--;
|
|
13818
|
+
if (depth === 0)
|
|
13819
|
+
break;
|
|
13820
|
+
}
|
|
13821
|
+
}
|
|
13822
|
+
if (depth !== 0)
|
|
13823
|
+
return null;
|
|
13824
|
+
return { cond: line.slice(m.index + m[0].length, i2), rest: line.slice(i2 + 1), ifCol };
|
|
13825
|
+
}
|
|
13826
|
+
function csEqualityGuard(cond) {
|
|
13827
|
+
const c = cond.trim();
|
|
13828
|
+
let m = /^(.*?)\s*(==|!=)\s*@?"[^"]*"\s*$/.exec(c);
|
|
13829
|
+
if (m)
|
|
13830
|
+
return { op: m[2], exprSide: m[1].trim() };
|
|
13831
|
+
m = /^@?"[^"]*"\s*(==|!=)\s*(.*)$/.exec(c);
|
|
13832
|
+
if (m)
|
|
13833
|
+
return { op: m[1], exprSide: m[2].trim() };
|
|
13834
|
+
return null;
|
|
13835
|
+
}
|
|
13836
|
+
var CS_IDENT_RE = /[A-Za-z_]\w*/g;
|
|
13837
|
+
function csIdentifiers(expr) {
|
|
13838
|
+
return new Set(expr.match(CS_IDENT_RE) ?? []);
|
|
13839
|
+
}
|
|
13840
|
+
function csThenBlock(lines, ifIdx, rest) {
|
|
13841
|
+
const isExit = (s) => /^\s*(?:return|throw|continue|break)\b/.test(s);
|
|
13842
|
+
let firstIdx;
|
|
13843
|
+
let firstText;
|
|
13844
|
+
if (rest.trim()) {
|
|
13845
|
+
firstIdx = ifIdx;
|
|
13846
|
+
firstText = rest;
|
|
13847
|
+
} else {
|
|
13848
|
+
let j = ifIdx + 1;
|
|
13849
|
+
while (j < lines.length && stripCsLiterals(lines[j]).trim() === "")
|
|
13850
|
+
j++;
|
|
13851
|
+
firstIdx = j;
|
|
13852
|
+
firstText = lines[j] ?? "";
|
|
13853
|
+
}
|
|
13854
|
+
if (firstText.trim().startsWith("{")) {
|
|
13855
|
+
let depth = 0;
|
|
13856
|
+
let started = false;
|
|
13857
|
+
let endLine = lines.length - 1;
|
|
13858
|
+
let body2 = "";
|
|
13859
|
+
for (let i2 = firstIdx;i2 < lines.length; i2++) {
|
|
13860
|
+
const stripped = stripCsLiterals(i2 === firstIdx ? firstText : lines[i2]);
|
|
13861
|
+
let broke = false;
|
|
13862
|
+
for (const ch of stripped) {
|
|
13863
|
+
if (ch === "{") {
|
|
13864
|
+
depth++;
|
|
13865
|
+
started = true;
|
|
13866
|
+
if (depth === 1)
|
|
13867
|
+
continue;
|
|
13868
|
+
} else if (ch === "}") {
|
|
13869
|
+
depth--;
|
|
13870
|
+
if (depth === 0) {
|
|
13871
|
+
endLine = i2;
|
|
13872
|
+
broke = true;
|
|
13873
|
+
break;
|
|
13874
|
+
}
|
|
13875
|
+
}
|
|
13876
|
+
if (started && depth >= 1)
|
|
13877
|
+
body2 += ch;
|
|
13878
|
+
}
|
|
13879
|
+
if (broke)
|
|
13880
|
+
break;
|
|
13881
|
+
if (started)
|
|
13882
|
+
body2 += " ";
|
|
13883
|
+
}
|
|
13884
|
+
return { start: ifIdx, end: endLine, earlyExit: isExit(body2.trim()) };
|
|
13885
|
+
}
|
|
13886
|
+
return { start: ifIdx, end: firstIdx, earlyExit: isExit(firstText.trim()) };
|
|
13887
|
+
}
|
|
13888
|
+
function isCSharpSsrfHostAllowlistGuarded(call, pattern, language, sourceLines) {
|
|
13889
|
+
if (language !== "csharp")
|
|
13551
13890
|
return false;
|
|
13552
|
-
|
|
13553
|
-
if (!fileArg)
|
|
13891
|
+
if (pattern.type !== "ssrf")
|
|
13554
13892
|
return false;
|
|
13555
|
-
|
|
13556
|
-
|
|
13557
|
-
|
|
13558
|
-
|
|
13559
|
-
|
|
13893
|
+
if (!sourceLines || sourceLines.length === 0)
|
|
13894
|
+
return false;
|
|
13895
|
+
const candidates = new Set;
|
|
13896
|
+
for (const a of call.arguments) {
|
|
13897
|
+
if (a.variable)
|
|
13898
|
+
candidates.add(a.variable);
|
|
13899
|
+
for (const id of csIdentifiers(a.expression ?? ""))
|
|
13900
|
+
candidates.add(id);
|
|
13560
13901
|
}
|
|
13561
|
-
if (
|
|
13902
|
+
if (candidates.size === 0)
|
|
13562
13903
|
return false;
|
|
13563
|
-
const
|
|
13564
|
-
const
|
|
13565
|
-
|
|
13566
|
-
|
|
13567
|
-
|
|
13568
|
-
|
|
13569
|
-
|
|
13570
|
-
|
|
13571
|
-
|
|
13572
|
-
|
|
13573
|
-
|
|
13574
|
-
|
|
13575
|
-
|
|
13904
|
+
const sinkIdx = call.location.line - 1;
|
|
13905
|
+
const sinkDepth = csBraceDepthBefore(sourceLines, sinkIdx);
|
|
13906
|
+
for (let i2 = 0;i2 < sinkIdx; i2++) {
|
|
13907
|
+
const parts2 = splitCsIf(sourceLines[i2]);
|
|
13908
|
+
if (!parts2)
|
|
13909
|
+
continue;
|
|
13910
|
+
const guard = csEqualityGuard(parts2.cond);
|
|
13911
|
+
if (!guard)
|
|
13912
|
+
continue;
|
|
13913
|
+
const guardIds = csIdentifiers(guard.exprSide);
|
|
13914
|
+
if (![...guardIds].some((id) => candidates.has(id)))
|
|
13915
|
+
continue;
|
|
13916
|
+
const block = csThenBlock(sourceLines, i2, parts2.rest);
|
|
13917
|
+
if (guard.op === "==") {
|
|
13918
|
+
if (sinkIdx >= block.start && sinkIdx <= block.end)
|
|
13919
|
+
return true;
|
|
13920
|
+
} else {
|
|
13921
|
+
const guardDepth = csBraceDepthBefore(sourceLines, i2) + netBraces(stripCsLiterals(sourceLines[i2]).slice(0, parts2.ifCol));
|
|
13922
|
+
if (block.earlyExit && sinkIdx > block.end && guardDepth === sinkDepth) {
|
|
13923
|
+
return true;
|
|
13924
|
+
}
|
|
13925
|
+
}
|
|
13926
|
+
}
|
|
13927
|
+
return false;
|
|
13576
13928
|
}
|
|
13577
13929
|
function isSafeRustCommandCall(call, pattern, language) {
|
|
13578
13930
|
if (language !== "rust")
|
|
@@ -13864,7 +14216,10 @@ function findSinks(calls, patterns, typeHierarchy, language, sourceLines, types)
|
|
|
13864
14216
|
if (isSafeJSChildProcessCall(call, pattern, language)) {
|
|
13865
14217
|
continue;
|
|
13866
14218
|
}
|
|
13867
|
-
if (isSafeCSharpProcessStartCall(call, pattern, language)) {
|
|
14219
|
+
if (isSafeCSharpProcessStartCall(call, pattern, language, sourceLines)) {
|
|
14220
|
+
continue;
|
|
14221
|
+
}
|
|
14222
|
+
if (isCSharpSsrfHostAllowlistGuarded(call, pattern, language, sourceLines)) {
|
|
13868
14223
|
continue;
|
|
13869
14224
|
}
|
|
13870
14225
|
if (pattern.safe_if_class_literal_at !== undefined && argIsClassLiteral(call, pattern.safe_if_class_literal_at, types)) {
|
|
@@ -25097,6 +25452,8 @@ class LanguageSourcesPass {
|
|
|
25097
25452
|
additionalSources.push(...findSetterChainSources(types, code, language));
|
|
25098
25453
|
additionalSources.push(...findJavaScriptAssignmentSources(code, language));
|
|
25099
25454
|
additionalSources.push(...findCSharpRequestSources(code, language));
|
|
25455
|
+
additionalSources.push(...findCSharpBindingAttributeSources(code, language));
|
|
25456
|
+
additionalSources.push(...findCSharpMinimalApiSources(code, language));
|
|
25100
25457
|
additionalSources.push(...findGoArgvSources(code, language));
|
|
25101
25458
|
const jsDOMSinks = findJavaScriptDOMSinks(code, language);
|
|
25102
25459
|
for (const s of jsDOMSinks) {
|
|
@@ -25761,6 +26118,104 @@ function findCSharpRequestSources(sourceCode, language) {
|
|
|
25761
26118
|
}
|
|
25762
26119
|
return sources;
|
|
25763
26120
|
}
|
|
26121
|
+
var CSHARP_MINIMAL_API_BINDING = new Map([
|
|
26122
|
+
["FromBody", "http_body"],
|
|
26123
|
+
["FromForm", "http_body"],
|
|
26124
|
+
["FromQuery", "http_param"],
|
|
26125
|
+
["FromRoute", "http_path"],
|
|
26126
|
+
["FromHeader", "http_param"]
|
|
26127
|
+
]);
|
|
26128
|
+
var CSHARP_NON_INPUT_TYPES = new Set([
|
|
26129
|
+
"HttpContext",
|
|
26130
|
+
"HttpRequest",
|
|
26131
|
+
"HttpResponse",
|
|
26132
|
+
"CancellationToken",
|
|
26133
|
+
"ClaimsPrincipal",
|
|
26134
|
+
"ILogger",
|
|
26135
|
+
"IFormFileCollection"
|
|
26136
|
+
]);
|
|
26137
|
+
function findCSharpBindingAttributeSources(sourceCode, language) {
|
|
26138
|
+
if (language !== "csharp")
|
|
26139
|
+
return [];
|
|
26140
|
+
const sources = [];
|
|
26141
|
+
const lines = sourceCode.split(`
|
|
26142
|
+
`);
|
|
26143
|
+
const re = /\[\s*(FromQuery|FromBody|FromForm|FromRoute|FromHeader)(?:\s*\([^)]*\))?\s*\]\s*(?:\[[^\]]*\]\s*)*[\w.<>[\]?]+\s+([A-Za-z_]\w*)/g;
|
|
26144
|
+
for (let i2 = 0;i2 < lines.length; i2++) {
|
|
26145
|
+
const lineRe = new RegExp(re.source, "g");
|
|
26146
|
+
let m;
|
|
26147
|
+
while ((m = lineRe.exec(lines[i2])) !== null) {
|
|
26148
|
+
const attr = m[1];
|
|
26149
|
+
const name2 = m[2];
|
|
26150
|
+
const type = attr === "FromBody" || attr === "FromForm" ? "http_body" : attr === "FromRoute" ? "http_path" : "http_param";
|
|
26151
|
+
if (sources.some((s) => s.line === i2 + 1 && s.variable === name2))
|
|
26152
|
+
continue;
|
|
26153
|
+
sources.push({
|
|
26154
|
+
type,
|
|
26155
|
+
location: `[${attr}] ${name2}`,
|
|
26156
|
+
severity: "high",
|
|
26157
|
+
line: i2 + 1,
|
|
26158
|
+
confidence: 1,
|
|
26159
|
+
variable: name2
|
|
26160
|
+
});
|
|
26161
|
+
}
|
|
26162
|
+
}
|
|
26163
|
+
return sources;
|
|
26164
|
+
}
|
|
26165
|
+
function findCSharpMinimalApiSources(sourceCode, language) {
|
|
26166
|
+
if (language !== "csharp")
|
|
26167
|
+
return [];
|
|
26168
|
+
const sources = [];
|
|
26169
|
+
const lines = sourceCode.split(`
|
|
26170
|
+
`);
|
|
26171
|
+
const mapRe = /\bMap(?:Get|Post|Put|Delete|Patch)\s*\(\s*(?:@?"[^"]*"|[\w.]+)\s*,\s*(?:\[[^\]]*\]\s*)?(?:async\s*)?\(([^)]*)\)\s*=>/;
|
|
26172
|
+
for (let i2 = 0;i2 < lines.length; i2++) {
|
|
26173
|
+
const m = mapRe.exec(lines[i2]);
|
|
26174
|
+
if (!m || !m[1].trim())
|
|
26175
|
+
continue;
|
|
26176
|
+
for (const rawParam of m[1].split(",")) {
|
|
26177
|
+
const seed = classifyCSharpLambdaParam(rawParam);
|
|
26178
|
+
if (!seed)
|
|
26179
|
+
continue;
|
|
26180
|
+
if (sources.some((s) => s.line === i2 + 1 && s.variable === seed.name))
|
|
26181
|
+
continue;
|
|
26182
|
+
sources.push({
|
|
26183
|
+
type: seed.type,
|
|
26184
|
+
location: `${seed.name} (Minimal API ${seed.via})`,
|
|
26185
|
+
severity: "high",
|
|
26186
|
+
line: i2 + 1,
|
|
26187
|
+
confidence: 1,
|
|
26188
|
+
variable: seed.name
|
|
26189
|
+
});
|
|
26190
|
+
}
|
|
26191
|
+
}
|
|
26192
|
+
return sources;
|
|
26193
|
+
}
|
|
26194
|
+
function classifyCSharpLambdaParam(raw) {
|
|
26195
|
+
const attrs = [...raw.matchAll(/\[([^\]]*)\]/g)].map((a) => a[1].split("(")[0].trim());
|
|
26196
|
+
if (attrs.includes("FromServices"))
|
|
26197
|
+
return null;
|
|
26198
|
+
if (attrs.some((a) => CSHARP_MINIMAL_API_BINDING.has(a)))
|
|
26199
|
+
return null;
|
|
26200
|
+
const noAttr = raw.replace(/\[[^\]]*\]/g, "").trim();
|
|
26201
|
+
const parts2 = noAttr.split(/\s+/).filter(Boolean);
|
|
26202
|
+
if (parts2.length < 2)
|
|
26203
|
+
return null;
|
|
26204
|
+
const name2 = parts2[parts2.length - 1];
|
|
26205
|
+
if (!/^[A-Za-z_]\w*$/.test(name2))
|
|
26206
|
+
return null;
|
|
26207
|
+
const baseType = (parts2[parts2.length - 2] ?? "").replace(/[?\[\]]/g, "").split("<")[0].split(".").pop() ?? "";
|
|
26208
|
+
if (CSHARP_NON_INPUT_TYPES.has(baseType))
|
|
26209
|
+
return null;
|
|
26210
|
+
for (const a of attrs) {
|
|
26211
|
+
const t = CSHARP_MINIMAL_API_BINDING.get(a);
|
|
26212
|
+
if (t)
|
|
26213
|
+
return { type: t, name: name2, via: `[${a}]` };
|
|
26214
|
+
}
|
|
26215
|
+
if (baseType === "string")
|
|
26216
|
+
return { type: "http_param", name: name2, via: "string param" };
|
|
26217
|
+
return null;
|
|
26218
|
+
}
|
|
25764
26219
|
function findJavaScriptAssignmentSources(sourceCode, language) {
|
|
25765
26220
|
if (!["javascript", "typescript"].includes(language))
|
|
25766
26221
|
return [];
|
|
@@ -42193,6 +42648,9 @@ var JAVA_SET_HTTPONLY_TRUE_RE = /\.setHttpOnly\s*\(\s*true\s*\)/;
|
|
|
42193
42648
|
var GO_SECURE_TRUE_RE = /\bSecure\s*:\s*true\b/;
|
|
42194
42649
|
var GO_HTTPONLY_TRUE_RE = /\bHttpOnly\s*:\s*true\b/;
|
|
42195
42650
|
var RUST_SET_COOKIE_MACRO_RE = /(format!|write!|writeln!)\s*\(([^()]*Set-Cookie[^()]*)\)/gis;
|
|
42651
|
+
var CS_COOKIE_OPTIONS_RE = /\bnew\s+CookieOptions\s*\{([^{}]*)\}/gs;
|
|
42652
|
+
var CS_SECURE_FALSE_RE = /\bSecure\s*=\s*false\b/;
|
|
42653
|
+
var CS_HTTPONLY_FALSE_RE = /\bHttpOnly\s*=\s*false\b/;
|
|
42196
42654
|
|
|
42197
42655
|
class InsecureCookiePass {
|
|
42198
42656
|
name = "insecure-cookie";
|
|
@@ -42241,9 +42699,30 @@ class InsecureCookiePass {
|
|
|
42241
42699
|
insecureCookies.push(det);
|
|
42242
42700
|
this.emit(ctx, file, det, "rust");
|
|
42243
42701
|
}
|
|
42702
|
+
} else if (language === "csharp") {
|
|
42703
|
+
for (const det of this.detectCSharpCookieOptions(code)) {
|
|
42704
|
+
insecureCookies.push(det);
|
|
42705
|
+
this.emit(ctx, file, det, "csharp");
|
|
42706
|
+
}
|
|
42244
42707
|
}
|
|
42245
42708
|
return { insecureCookies };
|
|
42246
42709
|
}
|
|
42710
|
+
detectCSharpCookieOptions(code) {
|
|
42711
|
+
const out2 = [];
|
|
42712
|
+
const re = new RegExp(CS_COOKIE_OPTIONS_RE.source, CS_COOKIE_OPTIONS_RE.flags);
|
|
42713
|
+
let m;
|
|
42714
|
+
while ((m = re.exec(code)) !== null) {
|
|
42715
|
+
const body2 = m[1] ?? "";
|
|
42716
|
+
const missingSecure = CS_SECURE_FALSE_RE.test(body2);
|
|
42717
|
+
const missingHttpOnly = CS_HTTPONLY_FALSE_RE.test(body2);
|
|
42718
|
+
if (!missingSecure && !missingHttpOnly)
|
|
42719
|
+
continue;
|
|
42720
|
+
const line = code.slice(0, m.index).split(`
|
|
42721
|
+
`).length;
|
|
42722
|
+
out2.push({ line, receiver: "CookieOptions", missingSecure, missingHttpOnly, optionsPresent: true });
|
|
42723
|
+
}
|
|
42724
|
+
return out2;
|
|
42725
|
+
}
|
|
42247
42726
|
detectJs(call) {
|
|
42248
42727
|
if (call.method_name !== "cookie")
|
|
42249
42728
|
return null;
|
|
@@ -42357,12 +42836,12 @@ class InsecureCookiePass {
|
|
|
42357
42836
|
emit(ctx, file, det, flavor) {
|
|
42358
42837
|
const missing = [];
|
|
42359
42838
|
if (det.missingSecure) {
|
|
42360
|
-
missing.push(flavor === "js" ? "`secure: true`" : flavor === "python" ? "`secure=True`" : flavor === "java" ? "`setSecure(true)`" : flavor === "go" ? "`Secure: true`" : "`Secure` attribute");
|
|
42839
|
+
missing.push(flavor === "js" ? "`secure: true`" : flavor === "python" ? "`secure=True`" : flavor === "java" ? "`setSecure(true)`" : flavor === "go" ? "`Secure: true`" : flavor === "csharp" ? "`Secure = true`" : "`Secure` attribute");
|
|
42361
42840
|
}
|
|
42362
42841
|
if (det.missingHttpOnly) {
|
|
42363
|
-
missing.push(flavor === "js" ? "`httpOnly: true`" : flavor === "python" ? "`httponly=True`" : flavor === "java" ? "`setHttpOnly(true)`" : flavor === "go" ? "`HttpOnly: true`" : "`HttpOnly` attribute");
|
|
42842
|
+
missing.push(flavor === "js" ? "`httpOnly: true`" : flavor === "python" ? "`httponly=True`" : flavor === "java" ? "`setHttpOnly(true)`" : flavor === "go" ? "`HttpOnly: true`" : flavor === "csharp" ? "`HttpOnly = true`" : "`HttpOnly` attribute");
|
|
42364
42843
|
}
|
|
42365
|
-
const fix = flavor === "js" ? 'Pass `{ secure: true, httpOnly: true, sameSite: "lax" }` as the third argument to `res.cookie()`.' : flavor === "python" ? 'Pass `secure=True, httponly=True, samesite="Lax"` to `response.set_cookie(...)`.' : flavor === "java" ? "After constructing the cookie, call `cookie.setSecure(true)` and `cookie.setHttpOnly(true)` before adding it to the response." : flavor === "go" ? "Set `Secure: true` and `HttpOnly: true` on the `http.Cookie` struct literal passed to `http.SetCookie`." : "Append `; Secure; HttpOnly` to the `Set-Cookie` header string.";
|
|
42844
|
+
const fix = flavor === "js" ? 'Pass `{ secure: true, httpOnly: true, sameSite: "lax" }` as the third argument to `res.cookie()`.' : flavor === "python" ? 'Pass `secure=True, httponly=True, samesite="Lax"` to `response.set_cookie(...)`.' : flavor === "java" ? "After constructing the cookie, call `cookie.setSecure(true)` and `cookie.setHttpOnly(true)` before adding it to the response." : flavor === "go" ? "Set `Secure: true` and `HttpOnly: true` on the `http.Cookie` struct literal passed to `http.SetCookie`." : flavor === "csharp" ? "Set `Secure = true` and `HttpOnly = true` on the `CookieOptions` (or enforce them globally via `CookiePolicyOptions`)." : "Append `; Secure; HttpOnly` to the `Set-Cookie` header string.";
|
|
42366
42845
|
ctx.addFinding({
|
|
42367
42846
|
id: `${this.name}-${file}-${det.line}`,
|
|
42368
42847
|
pass: this.name,
|
|
@@ -42871,6 +43350,7 @@ var ISSUE_CWE = {
|
|
|
42871
43350
|
"hardcoded-key": "CWE-321",
|
|
42872
43351
|
"weak-rsa-key": "CWE-326"
|
|
42873
43352
|
};
|
|
43353
|
+
var CS_CIPHER_MODE_ECB_RE = /\bCipherMode\s*\.\s*ECB\b/;
|
|
42874
43354
|
|
|
42875
43355
|
class WeakCryptoPass {
|
|
42876
43356
|
name = "weak-crypto";
|
|
@@ -42887,26 +43367,35 @@ class WeakCryptoPass {
|
|
|
42887
43367
|
const findings = [];
|
|
42888
43368
|
const constProp = ctx.hasResult("constant-propagation") ? ctx.getResult("constant-propagation") : null;
|
|
42889
43369
|
const literalBindings = scanLiteralBindings(code, language);
|
|
43370
|
+
const emit = (line, det) => {
|
|
43371
|
+
findings.push({ line, language, ...det });
|
|
43372
|
+
ctx.addFinding({
|
|
43373
|
+
id: `${this.name}-${file}-${line}-${det.issue}`,
|
|
43374
|
+
pass: this.name,
|
|
43375
|
+
category: this.category,
|
|
43376
|
+
rule_id: this.name,
|
|
43377
|
+
cwe: ISSUE_CWE[det.issue],
|
|
43378
|
+
severity: "high",
|
|
43379
|
+
level: "error",
|
|
43380
|
+
message: this.buildMessage(det),
|
|
43381
|
+
file,
|
|
43382
|
+
line,
|
|
43383
|
+
fix: this.buildFix(det.issue),
|
|
43384
|
+
evidence: { ...det, language }
|
|
43385
|
+
});
|
|
43386
|
+
};
|
|
42890
43387
|
for (const call of graph.ir.calls) {
|
|
42891
|
-
const
|
|
42892
|
-
|
|
42893
|
-
|
|
42894
|
-
|
|
42895
|
-
|
|
42896
|
-
|
|
42897
|
-
|
|
42898
|
-
|
|
42899
|
-
|
|
42900
|
-
|
|
42901
|
-
|
|
42902
|
-
severity: "high",
|
|
42903
|
-
level: "error",
|
|
42904
|
-
message,
|
|
42905
|
-
file,
|
|
42906
|
-
line,
|
|
42907
|
-
fix: this.buildFix(det.issue),
|
|
42908
|
-
evidence: { ...det, language }
|
|
42909
|
-
});
|
|
43388
|
+
for (const det of this.detect(call, language, constProp, literalBindings)) {
|
|
43389
|
+
emit(call.location.line, det);
|
|
43390
|
+
}
|
|
43391
|
+
}
|
|
43392
|
+
if (language === "csharp") {
|
|
43393
|
+
const lines = code.split(`
|
|
43394
|
+
`);
|
|
43395
|
+
for (let i2 = 0;i2 < lines.length; i2++) {
|
|
43396
|
+
if (CS_CIPHER_MODE_ECB_RE.test(lines[i2])) {
|
|
43397
|
+
emit(i2 + 1, { issue: "ecb-mode", detail: "CipherMode.ECB", api: "SymmetricAlgorithm.Mode" });
|
|
43398
|
+
}
|
|
42910
43399
|
}
|
|
42911
43400
|
}
|
|
42912
43401
|
return { findings };
|
|
@@ -44166,9 +44655,14 @@ class InsecureDeserializationConfigPass {
|
|
|
44166
44655
|
name = "insecure-deserialization-config";
|
|
44167
44656
|
category = "security";
|
|
44168
44657
|
run(ctx) {
|
|
44658
|
+
if (ctx.language === "java")
|
|
44659
|
+
return this.runJava(ctx);
|
|
44660
|
+
if (ctx.language === "csharp")
|
|
44661
|
+
return this.runCSharp(ctx);
|
|
44662
|
+
return { findings: [] };
|
|
44663
|
+
}
|
|
44664
|
+
runJava(ctx) {
|
|
44169
44665
|
const { graph, language } = ctx;
|
|
44170
|
-
if (language !== "java")
|
|
44171
|
-
return { findings: [] };
|
|
44172
44666
|
const file = graph.ir.meta.file;
|
|
44173
44667
|
const findings = [];
|
|
44174
44668
|
for (const call of graph.ir.calls) {
|
|
@@ -44194,6 +44688,35 @@ class InsecureDeserializationConfigPass {
|
|
|
44194
44688
|
}
|
|
44195
44689
|
return { findings };
|
|
44196
44690
|
}
|
|
44691
|
+
runCSharp(ctx) {
|
|
44692
|
+
const file = ctx.graph.ir.meta.file;
|
|
44693
|
+
const findings = [];
|
|
44694
|
+
const lines = ctx.code.split(`
|
|
44695
|
+
`);
|
|
44696
|
+
for (let i2 = 0;i2 < lines.length; i2++) {
|
|
44697
|
+
const m = INSECURE_TYPE_NAME_HANDLING_RE.exec(lines[i2]);
|
|
44698
|
+
if (!m)
|
|
44699
|
+
continue;
|
|
44700
|
+
const line = i2 + 1;
|
|
44701
|
+
const api = `TypeNameHandling = TypeNameHandling.${m[1]}`;
|
|
44702
|
+
findings.push({ line, api });
|
|
44703
|
+
ctx.addFinding({
|
|
44704
|
+
id: `${this.name}-${file}-${line}`,
|
|
44705
|
+
pass: this.name,
|
|
44706
|
+
category: this.category,
|
|
44707
|
+
rule_id: this.name,
|
|
44708
|
+
cwe: "CWE-502",
|
|
44709
|
+
severity: "high",
|
|
44710
|
+
level: "error",
|
|
44711
|
+
message: `Json.NET configured with TypeNameHandling.${m[1]}: a $type field in ` + "untrusted JSON can instantiate arbitrary .NET types (deserialization RCE)",
|
|
44712
|
+
file,
|
|
44713
|
+
line,
|
|
44714
|
+
fix: "Use TypeNameHandling.None (the default), or bind a SerializationBinder that allow-lists the exact types you deserialize.",
|
|
44715
|
+
evidence: { api, language: "csharp" }
|
|
44716
|
+
});
|
|
44717
|
+
}
|
|
44718
|
+
return { findings };
|
|
44719
|
+
}
|
|
44197
44720
|
isPermissiveXStreamConfig(call) {
|
|
44198
44721
|
if (call.method_name !== "addPermission")
|
|
44199
44722
|
return false;
|
|
@@ -44201,6 +44724,7 @@ class InsecureDeserializationConfigPass {
|
|
|
44201
44724
|
return typeof arg0 === "string" && ANY_TYPE_PERMISSION_RE.test(arg0);
|
|
44202
44725
|
}
|
|
44203
44726
|
}
|
|
44727
|
+
var INSECURE_TYPE_NAME_HANDLING_RE = /\bTypeNameHandling\s*=\s*(?:Newtonsoft\.Json\.)?TypeNameHandling\.(All|Auto|Objects|Arrays)\b/;
|
|
44204
44728
|
|
|
44205
44729
|
// ../circle-ir/dist/analysis/passes/plaintext-password-storage-pass.js
|
|
44206
44730
|
function isWriteStorageCall(call, language) {
|
|
@@ -44461,6 +44985,8 @@ var VERIFY_FALSE_RE = /\bverify\s*=\s*False\b/;
|
|
|
44461
44985
|
var REJECT_UNAUTHORIZED_FALSE_RE = /\brejectUnauthorized\s*:\s*false\b/;
|
|
44462
44986
|
var INSECURE_SKIP_VERIFY_TRUE_RE = /\bInsecureSkipVerify\s*:\s*true\b/;
|
|
44463
44987
|
var HOSTNAME_LAMBDA_TRUE_RE = /\(\s*\w+\s*,\s*\w+\s*\)\s*->\s*true\b/;
|
|
44988
|
+
var CS_CERT_CALLBACK_TRUE_RE = /\b(ServerCertificateValidationCallback|ServerCertificateCustomValidationCallback|RemoteCertificateValidationCallback)\s*(?:\+?=|\()\s*(?:\([^)]*\)|\w+)\s*=>\s*(?:true\b|\{\s*return\s+true\b)/;
|
|
44989
|
+
var CS_DANGEROUS_ACCEPT_RE = /\bDangerousAcceptAnyServerCertificateValidator\b/;
|
|
44464
44990
|
var ALLOW_ALL_HOSTNAME_VERIFIERS = new Set([
|
|
44465
44991
|
"NoopHostnameVerifier.INSTANCE",
|
|
44466
44992
|
"new AllowAllHostnameVerifier()",
|
|
@@ -44609,6 +45135,21 @@ class TlsVerifyDisabledPass {
|
|
|
44609
45135
|
}
|
|
44610
45136
|
}
|
|
44611
45137
|
}
|
|
45138
|
+
if (language === "csharp") {
|
|
45139
|
+
for (let i2 = 0;i2 < lines.length; i2++) {
|
|
45140
|
+
const l = lines[i2];
|
|
45141
|
+
const m = CS_CERT_CALLBACK_TRUE_RE.exec(l);
|
|
45142
|
+
if (m) {
|
|
45143
|
+
out2.push({ line: i2 + 1, pattern: `${m[1]} => true`, api: m[1] });
|
|
45144
|
+
} else if (CS_DANGEROUS_ACCEPT_RE.test(l)) {
|
|
45145
|
+
out2.push({
|
|
45146
|
+
line: i2 + 1,
|
|
45147
|
+
pattern: "DangerousAcceptAnyServerCertificateValidator",
|
|
45148
|
+
api: "HttpClientHandler"
|
|
45149
|
+
});
|
|
45150
|
+
}
|
|
45151
|
+
}
|
|
45152
|
+
}
|
|
44612
45153
|
return out2;
|
|
44613
45154
|
}
|
|
44614
45155
|
fixFor(language, pattern) {
|
|
@@ -44630,6 +45171,9 @@ class TlsVerifyDisabledPass {
|
|
|
44630
45171
|
if (pattern.includes("ssl._create_unverified_context")) {
|
|
44631
45172
|
return "Do not use `_create_unverified_context()`. Use `ssl.create_default_context()`.";
|
|
44632
45173
|
}
|
|
45174
|
+
if (language === "csharp") {
|
|
45175
|
+
return "Do not accept every certificate. Remove the always-true validation " + "callback (and `DangerousAcceptAnyServerCertificateValidator`); rely on the " + "platform default. To trust a private CA, validate the chain against it in " + "the callback instead of returning true.";
|
|
45176
|
+
}
|
|
44633
45177
|
return "Restore TLS certificate and hostname verification.";
|
|
44634
45178
|
}
|
|
44635
45179
|
}
|
|
@@ -45157,6 +45701,8 @@ var PY_VERIFY_SIGNATURE_FALSE_RE = /["']verify_signature["']\s*:\s*False\b/;
|
|
|
45157
45701
|
var PY_VERIFY_KW_FALSE_RE = /\bverify\s*=\s*False\b/;
|
|
45158
45702
|
var PY_ALG_NONE_RE = /\balgorithms\s*=\s*[\[\(]\s*["']none["']/i;
|
|
45159
45703
|
var JS_ALG_NONE_RE = /\balgorithms\s*:\s*\[\s*["']none["']/i;
|
|
45704
|
+
var CS_REQUIRE_SIGNED_FALSE_RE = /\bRequireSignedTokens\s*=\s*false\b/;
|
|
45705
|
+
var CS_SIGNATURE_VALIDATOR_BYPASS_RE = /\bSignatureValidator\s*=\s*[^;]*=>\s*new\s+JwtSecurityToken\b/;
|
|
45160
45706
|
|
|
45161
45707
|
class JwtVerifyDisabledPass {
|
|
45162
45708
|
name = "jwt-verify-disabled";
|
|
@@ -45165,25 +45711,36 @@ class JwtVerifyDisabledPass {
|
|
|
45165
45711
|
const { graph, language } = ctx;
|
|
45166
45712
|
const file = graph.ir.meta.file;
|
|
45167
45713
|
const findings = [];
|
|
45714
|
+
const emit = (line, det) => {
|
|
45715
|
+
findings.push({ line, language, ...det });
|
|
45716
|
+
ctx.addFinding({
|
|
45717
|
+
id: `${this.name}-${file}-${line}-${det.pattern}`,
|
|
45718
|
+
pass: this.name,
|
|
45719
|
+
category: this.category,
|
|
45720
|
+
rule_id: this.name,
|
|
45721
|
+
cwe: "CWE-347",
|
|
45722
|
+
severity: "critical",
|
|
45723
|
+
level: "error",
|
|
45724
|
+
message: `JWT signature verification disabled via \`${det.pattern}\` in ` + `\`${det.api}\`. Any attacker can forge a token with arbitrary ` + "claims (user id, roles, expiry) since the signature is not " + "checked.",
|
|
45725
|
+
file,
|
|
45726
|
+
line,
|
|
45727
|
+
fix: this.fixFor(language),
|
|
45728
|
+
evidence: { ...det, language }
|
|
45729
|
+
});
|
|
45730
|
+
};
|
|
45168
45731
|
for (const call of graph.ir.calls) {
|
|
45169
|
-
const
|
|
45170
|
-
|
|
45171
|
-
|
|
45172
|
-
|
|
45173
|
-
|
|
45174
|
-
|
|
45175
|
-
|
|
45176
|
-
|
|
45177
|
-
|
|
45178
|
-
|
|
45179
|
-
|
|
45180
|
-
|
|
45181
|
-
message: `JWT signature verification disabled via \`${det.pattern}\` in ` + `\`${det.api}\`. Any attacker can forge a token with arbitrary ` + "claims (user id, roles, expiry) since the signature is not " + "checked.",
|
|
45182
|
-
file,
|
|
45183
|
-
line,
|
|
45184
|
-
fix: this.fixFor(language),
|
|
45185
|
-
evidence: { ...det, language }
|
|
45186
|
-
});
|
|
45732
|
+
for (const det of this.detect(call, language))
|
|
45733
|
+
emit(call.location.line, det);
|
|
45734
|
+
}
|
|
45735
|
+
if (language === "csharp") {
|
|
45736
|
+
const lines = ctx.code.split(`
|
|
45737
|
+
`);
|
|
45738
|
+
for (let i2 = 0;i2 < lines.length; i2++) {
|
|
45739
|
+
if (CS_REQUIRE_SIGNED_FALSE_RE.test(lines[i2])) {
|
|
45740
|
+
emit(i2 + 1, { pattern: "RequireSignedTokens = false", api: "TokenValidationParameters" });
|
|
45741
|
+
} else if (CS_SIGNATURE_VALIDATOR_BYPASS_RE.test(lines[i2])) {
|
|
45742
|
+
emit(i2 + 1, { pattern: "SignatureValidator returns an unvalidated token", api: "TokenValidationParameters" });
|
|
45743
|
+
}
|
|
45187
45744
|
}
|
|
45188
45745
|
}
|
|
45189
45746
|
return { findings };
|
|
@@ -45257,6 +45814,9 @@ class JwtVerifyDisabledPass {
|
|
|
45257
45814
|
if (language === "java") {
|
|
45258
45815
|
return "For auth0/java-jwt: use `JWT.require(Algorithm.HMAC256(secret))` or " + "an RSA algorithm. For jjwt: call `parseClaimsJws(token)` (signature " + "enforced) rather than `parse(token)` (signature ignored).";
|
|
45259
45816
|
}
|
|
45817
|
+
if (language === "csharp") {
|
|
45818
|
+
return "Leave `RequireSignedTokens = true` and do not install a custom " + "`SignatureValidator` that returns the token unverified. Configure " + "`IssuerSigningKey`/`IssuerSigningKeys` and let the handler validate " + "the signature.";
|
|
45819
|
+
}
|
|
45260
45820
|
return "Enforce JWT signature verification with a concrete algorithm " + "(HS256/RS256/ES256). Never accept `alg: none`.";
|
|
45261
45821
|
}
|
|
45262
45822
|
}
|
|
@@ -46601,6 +47161,24 @@ function getNodeTypesForLanguage(language) {
|
|
|
46601
47161
|
"selector_expression",
|
|
46602
47162
|
"identifier"
|
|
46603
47163
|
]);
|
|
47164
|
+
case "csharp":
|
|
47165
|
+
return new Set([
|
|
47166
|
+
"method_invocation",
|
|
47167
|
+
"object_creation_expression",
|
|
47168
|
+
"class_declaration",
|
|
47169
|
+
"method_declaration",
|
|
47170
|
+
"constructor_declaration",
|
|
47171
|
+
"field_declaration",
|
|
47172
|
+
"import_declaration",
|
|
47173
|
+
"interface_declaration",
|
|
47174
|
+
"enum_declaration",
|
|
47175
|
+
"package_declaration",
|
|
47176
|
+
"local_variable_declaration",
|
|
47177
|
+
"destructor_declaration",
|
|
47178
|
+
"operator_declaration",
|
|
47179
|
+
"local_function_statement",
|
|
47180
|
+
"accessor_declaration"
|
|
47181
|
+
]);
|
|
46604
47182
|
default:
|
|
46605
47183
|
return new Set([
|
|
46606
47184
|
"method_invocation",
|
|
@@ -48153,7 +48731,7 @@ var colors = {
|
|
|
48153
48731
|
};
|
|
48154
48732
|
|
|
48155
48733
|
// src/version.ts
|
|
48156
|
-
var version = "4.7
|
|
48734
|
+
var version = "4.9.7";
|
|
48157
48735
|
|
|
48158
48736
|
// src/formatters.ts
|
|
48159
48737
|
var LIBRARY_API_SURFACE_TAG2 = "library-api-surface:caller-responsibility";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cognium-dev",
|
|
3
|
-
"version": "4.7
|
|
3
|
+
"version": "4.9.7",
|
|
4
4
|
"description": "Static Application Security Testing CLI for detecting security vulnerabilities via taint tracking",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -66,7 +66,7 @@
|
|
|
66
66
|
},
|
|
67
67
|
"dependencies": {
|
|
68
68
|
"@cognium/project-profile-detect": "^1.1.0",
|
|
69
|
-
"circle-ir": "^4.7
|
|
69
|
+
"circle-ir": "^4.9.7"
|
|
70
70
|
},
|
|
71
71
|
"devDependencies": {
|
|
72
72
|
"@types/node": "^25.5.0",
|