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.
Files changed (2) hide show
  1. package/dist/cli.js +690 -112
  2. 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 || getNodeText(nameNode) !== "CommandText")
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: "CommandText",
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, true);
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, false);
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 buildMethodCFG(body2, startId, isJavaScript) {
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, isJavaScript);
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, isJavaScript) {
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, isJavaScript))
8179
+ if (!isStatement(stmt, dialect))
8084
8180
  continue;
8085
- const result = processStatement(stmt, currentId, blocks, edges, isJavaScript);
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, isJavaScript) {
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, isJavaScript);
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
- return processForStatement(stmt, startId, blocks, edges, isJavaScript);
8210
+ case "foreach_statement":
8211
+ return processForStatement(stmt, startId, blocks, edges, dialect);
8115
8212
  case "while_statement":
8116
- return processWhileStatement(stmt, startId, blocks, edges, isJavaScript);
8213
+ return processWhileStatement(stmt, startId, blocks, edges, dialect);
8117
8214
  case "do_statement":
8118
- return processDoWhileStatement(stmt, startId, blocks, edges, isJavaScript);
8215
+ return processDoWhileStatement(stmt, startId, blocks, edges, dialect);
8119
8216
  case "try_statement":
8120
- return processTryStatement(stmt, startId, blocks, edges, isJavaScript);
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, isJavaScript);
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, isJavaScript);
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, isJavaScript) {
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, isJavaScript);
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, isJavaScript);
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, isJavaScript) {
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, isJavaScript);
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, isJavaScript) {
8218
- return processForStatement(stmt, startId, blocks, edges, isJavaScript);
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, isJavaScript) {
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, isJavaScript);
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, isJavaScript) {
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, isJavaScript);
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, isJavaScript);
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
- const finallyClause = stmt.childForFieldName("finally") ?? stmt.childForFieldName("finalizer");
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, isJavaScript);
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, isJavaScript) {
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 = isJavaScript ? ["switch_case", "switch_default"] : ["switch_block_statement_group", "switch_rule"];
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, isJavaScript);
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, false);
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, false);
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, isJavaScript) {
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 isJavaScript ? jsStatementTypes.has(node.type) : javaStatementTypes.has(node.type);
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, false);
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 isTaintable = param.type ? isInterproceduralTaintableType(param.type, language) : true;
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
- function isSafeCSharpProcessStartCall(call, pattern, language) {
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
- if (call.method_name !== "Start")
13749
+ const method = call.method_name;
13750
+ if (method !== "Start" && method !== "ProcessStartInfo")
13549
13751
  return false;
13550
- if (call.arguments.length < 2)
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
- const fileArg = call.arguments.find((a) => a.position === 0);
13553
- if (!fileArg)
13891
+ if (pattern.type !== "ssrf")
13554
13892
  return false;
13555
- let raw;
13556
- if (fileArg.literal !== null && fileArg.literal !== undefined) {
13557
- raw = String(fileArg.literal).trim();
13558
- } else {
13559
- raw = (fileArg.expression ?? "").trim();
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 (!/^@?"[^"]*"$/.test(raw))
13902
+ if (candidates.size === 0)
13562
13903
  return false;
13563
- const program = (raw.replace(/^@?"|"$/g, "").split(/[\\/]/).pop() ?? "").toLowerCase().replace(/\.exe$/, "");
13564
- const SHELL_PROGRAMS = new Set([
13565
- "sh",
13566
- "bash",
13567
- "zsh",
13568
- "dash",
13569
- "ash",
13570
- "ksh",
13571
- "cmd",
13572
- "powershell",
13573
- "pwsh"
13574
- ]);
13575
- return !SHELL_PROGRAMS.has(program);
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 detections = this.detect(call, language, constProp, literalBindings);
42892
- for (const det of detections) {
42893
- const line = call.location.line;
42894
- findings.push({ line, language, ...det });
42895
- const message = this.buildMessage(det);
42896
- ctx.addFinding({
42897
- id: `${this.name}-${file}-${line}-${det.issue}`,
42898
- pass: this.name,
42899
- category: this.category,
42900
- rule_id: this.name,
42901
- cwe: ISSUE_CWE[det.issue],
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 detections = this.detect(call, language);
45170
- for (const det of detections) {
45171
- const line = call.location.line;
45172
- findings.push({ line, language, ...det });
45173
- ctx.addFinding({
45174
- id: `${this.name}-${file}-${line}-${det.pattern}`,
45175
- pass: this.name,
45176
- category: this.category,
45177
- rule_id: this.name,
45178
- cwe: "CWE-347",
45179
- severity: "critical",
45180
- level: "error",
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.2";
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.2",
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.2"
69
+ "circle-ir": "^4.9.7"
70
70
  },
71
71
  "devDependencies": {
72
72
  "@types/node": "^25.5.0",