cognium-dev 4.9.26 → 4.9.28
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 +463 -29
- package/package.json +3 -3
package/dist/cli.js
CHANGED
|
@@ -7,6 +7,7 @@ import { readFileSync, existsSync, writeFileSync } from "fs";
|
|
|
7
7
|
import { stat as stat2, readdir as readdir2 } from "fs/promises";
|
|
8
8
|
import { join as join2, dirname as dirname3, extname, resolve as resolve2, relative as relative4, basename } from "path";
|
|
9
9
|
import { createRequire as createRequire2 } from "module";
|
|
10
|
+
import { getHeapStatistics } from "v8";
|
|
10
11
|
|
|
11
12
|
// ../../node_modules/web-tree-sitter/web-tree-sitter.js
|
|
12
13
|
var __defProp = Object.defineProperty;
|
|
@@ -5505,6 +5506,33 @@ function extractCSharpCalls(tree, cache) {
|
|
|
5505
5506
|
});
|
|
5506
5507
|
continue;
|
|
5507
5508
|
}
|
|
5509
|
+
if (left?.type === "identifier" && asn.parent?.type === "initializer_expression") {
|
|
5510
|
+
const creation = asn.parent.parent;
|
|
5511
|
+
if (creation?.type !== "object_creation_expression")
|
|
5512
|
+
continue;
|
|
5513
|
+
const ctorType = creation.childForFieldName("type");
|
|
5514
|
+
const right2 = asn.childForFieldName("right");
|
|
5515
|
+
if (!ctorType || !right2)
|
|
5516
|
+
continue;
|
|
5517
|
+
const typeName = csharpBareName(ctorType);
|
|
5518
|
+
const rhsText2 = getNodeText(right2);
|
|
5519
|
+
calls.push({
|
|
5520
|
+
method_name: getNodeText(left),
|
|
5521
|
+
receiver: typeName,
|
|
5522
|
+
receiver_type: typeName,
|
|
5523
|
+
receiver_type_fqn: null,
|
|
5524
|
+
arguments: [{
|
|
5525
|
+
position: 0,
|
|
5526
|
+
expression: rhsText2,
|
|
5527
|
+
variable: right2.type === "identifier" ? rhsText2 : null,
|
|
5528
|
+
literal: CSHARP_LITERAL_NODE_TYPES.has(right2.type) ? rhsText2 : null,
|
|
5529
|
+
value: null
|
|
5530
|
+
}],
|
|
5531
|
+
location: { line: asn.startPosition.row + 1, column: asn.startPosition.column },
|
|
5532
|
+
in_method: findEnclosingMethod(asn)
|
|
5533
|
+
});
|
|
5534
|
+
continue;
|
|
5535
|
+
}
|
|
5508
5536
|
if (left?.type !== "member_access_expression")
|
|
5509
5537
|
continue;
|
|
5510
5538
|
const nameNode = left.childForFieldName("name");
|
|
@@ -5517,6 +5545,15 @@ function extractCSharpCalls(tree, cache) {
|
|
|
5517
5545
|
const recvType = recv ? typeMap.get(recv) : undefined;
|
|
5518
5546
|
if (recvType !== "DirectorySearcher")
|
|
5519
5547
|
continue;
|
|
5548
|
+
} else if (propName === "StatusDescription") {
|
|
5549
|
+
const recv = exprNode ? getNodeText(exprNode) : "";
|
|
5550
|
+
if (!/(^|\.)(Response|HttpResponse|Context\.Response)$/.test(recv))
|
|
5551
|
+
continue;
|
|
5552
|
+
} else if (propName === "BaseAddress") {
|
|
5553
|
+
const recv = exprNode ? getNodeText(exprNode) : null;
|
|
5554
|
+
const recvType = recv ? typeMap.get(recv) : undefined;
|
|
5555
|
+
if (recvType !== "HttpClient")
|
|
5556
|
+
continue;
|
|
5520
5557
|
} else if (propName !== "CommandText") {
|
|
5521
5558
|
continue;
|
|
5522
5559
|
}
|
|
@@ -5540,8 +5577,36 @@ function extractCSharpCalls(tree, cache) {
|
|
|
5540
5577
|
in_method: findEnclosingMethod(asn)
|
|
5541
5578
|
});
|
|
5542
5579
|
}
|
|
5580
|
+
const casts = getNodesFromCache(tree.rootNode, "cast_expression", cache);
|
|
5581
|
+
for (const cast of casts) {
|
|
5582
|
+
const typeNode = cast.childForFieldName("type");
|
|
5583
|
+
const valueNode = cast.childForFieldName("value");
|
|
5584
|
+
if (!typeNode || !valueNode)
|
|
5585
|
+
continue;
|
|
5586
|
+
const typeName = csharpBareName(typeNode);
|
|
5587
|
+
if (!CSHARP_RAW_MARKUP_CAST_TYPES.has(typeName))
|
|
5588
|
+
continue;
|
|
5589
|
+
const valueText = getNodeText(valueNode);
|
|
5590
|
+
calls.push({
|
|
5591
|
+
method_name: typeName,
|
|
5592
|
+
receiver: null,
|
|
5593
|
+
receiver_type: null,
|
|
5594
|
+
receiver_type_fqn: null,
|
|
5595
|
+
arguments: [{
|
|
5596
|
+
position: 0,
|
|
5597
|
+
expression: valueText,
|
|
5598
|
+
variable: valueNode.type === "identifier" ? valueText : null,
|
|
5599
|
+
literal: CSHARP_LITERAL_NODE_TYPES.has(valueNode.type) ? valueText : null,
|
|
5600
|
+
value: null
|
|
5601
|
+
}],
|
|
5602
|
+
location: { line: cast.startPosition.row + 1, column: cast.startPosition.column },
|
|
5603
|
+
in_method: findEnclosingMethod(cast),
|
|
5604
|
+
is_constructor: true
|
|
5605
|
+
});
|
|
5606
|
+
}
|
|
5543
5607
|
return calls;
|
|
5544
5608
|
}
|
|
5609
|
+
var CSHARP_RAW_MARKUP_CAST_TYPES = new Set(["MarkupString"]);
|
|
5545
5610
|
function extractCSharpArguments(argsNode) {
|
|
5546
5611
|
const args2 = [];
|
|
5547
5612
|
let position = 0;
|
|
@@ -5562,12 +5627,22 @@ function extractCSharpArguments(argsNode) {
|
|
|
5562
5627
|
position: position++,
|
|
5563
5628
|
expression: text,
|
|
5564
5629
|
variable: expr?.type === "identifier" ? text : null,
|
|
5565
|
-
literal: expr
|
|
5630
|
+
literal: expr && CSHARP_LITERAL_NODE_TYPES.has(expr.type) ? text : null,
|
|
5566
5631
|
value: null
|
|
5567
5632
|
});
|
|
5568
5633
|
}
|
|
5569
5634
|
return args2;
|
|
5570
5635
|
}
|
|
5636
|
+
var CSHARP_LITERAL_NODE_TYPES = new Set([
|
|
5637
|
+
"string_literal",
|
|
5638
|
+
"verbatim_string_literal",
|
|
5639
|
+
"raw_string_literal",
|
|
5640
|
+
"character_literal",
|
|
5641
|
+
"integer_literal",
|
|
5642
|
+
"real_literal",
|
|
5643
|
+
"boolean_literal",
|
|
5644
|
+
"null_literal"
|
|
5645
|
+
]);
|
|
5571
5646
|
function extractJavaScriptCalls(tree, cache) {
|
|
5572
5647
|
const calls = [];
|
|
5573
5648
|
const context = buildJSResolutionContext(tree, cache);
|
|
@@ -12481,8 +12556,6 @@ var DEFAULT_SINKS = [
|
|
|
12481
12556
|
{ method: "deserialize", class: "bincode", type: "deserialization", cwe: "CWE-502", severity: "critical", arg_positions: [0] },
|
|
12482
12557
|
{ method: "from_str", class: "toml", type: "deserialization", cwe: "CWE-502", severity: "high", arg_positions: [0] },
|
|
12483
12558
|
{ method: "from_str", class: "ron", type: "deserialization", cwe: "CWE-502", severity: "high", arg_positions: [0] },
|
|
12484
|
-
{ method: "from_str", type: "deserialization", cwe: "CWE-502", severity: "medium", arg_positions: [0] },
|
|
12485
|
-
{ method: "from_slice", type: "deserialization", cwe: "CWE-502", severity: "medium", arg_positions: [0] },
|
|
12486
12559
|
{ method: "body", class: "HttpResponseBuilder", type: "xss", cwe: "CWE-79", severity: "high", arg_positions: [0] },
|
|
12487
12560
|
{ method: "body", class: "HttpResponse", type: "xss", cwe: "CWE-79", severity: "high", arg_positions: [0] },
|
|
12488
12561
|
{ method: "Html", type: "xss", cwe: "CWE-79", severity: "high", arg_positions: [0] },
|
|
@@ -12703,12 +12776,15 @@ var DEFAULT_SINKS = [
|
|
|
12703
12776
|
{ method: "Deserialize", class: "ObjectStateFormatter", type: "deserialization", cwe: "CWE-502", severity: "critical", arg_positions: [0], languages: ["csharp"] },
|
|
12704
12777
|
{ method: "Raw", class: "Html", type: "xss", cwe: "CWE-79", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
12705
12778
|
{ method: "Write", class: "Response", type: "xss", cwe: "CWE-79", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
12779
|
+
{ method: "WriteAsync", class: "Response", type: "xss", cwe: "CWE-79", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
12706
12780
|
{ method: "HtmlString", type: "xss", cwe: "CWE-79", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
12707
12781
|
{ method: "MarkupString", class: "constructor", type: "xss", cwe: "CWE-79", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
12782
|
+
{ method: "StatusDescription", class: "Response", type: "xss", cwe: "CWE-81", severity: "medium", arg_positions: [0], languages: ["csharp"] },
|
|
12708
12783
|
{ method: "Content", type: "xss", cwe: "CWE-79", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
12709
12784
|
{ method: "Format", class: "String", type: "format_string", cwe: "CWE-134", severity: "medium", arg_positions: [0], languages: ["csharp"] },
|
|
12710
12785
|
{ method: "DirectorySearcher", type: "ldap_injection", cwe: "CWE-90", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
12711
12786
|
{ method: "DirectoryEntry", type: "ldap_injection", cwe: "CWE-90", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
12787
|
+
{ method: "BaseAddress", class: "HttpClient", type: "ssrf", cwe: "CWE-918", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
12712
12788
|
{ method: "Redirect", type: "open_redirect", cwe: "CWE-601", severity: "medium", arg_positions: [0], languages: ["csharp"] },
|
|
12713
12789
|
{ method: "RedirectPermanent", type: "open_redirect", cwe: "CWE-601", severity: "medium", arg_positions: [0], languages: ["csharp"] },
|
|
12714
12790
|
{ method: "AddHeader", type: "crlf", cwe: "CWE-113", severity: "medium", arg_positions: [1], languages: ["csharp"] },
|
|
@@ -14918,8 +14994,9 @@ function matchesSinkPattern(call, pattern, typeHierarchy, language) {
|
|
|
14918
14994
|
return false;
|
|
14919
14995
|
} else if (!call.receiver && !call.receiver_type) {
|
|
14920
14996
|
const target = call.resolution?.target;
|
|
14997
|
+
const normalisedTarget = language === "rust" ? target?.replace(/::/g, ".") : target;
|
|
14921
14998
|
const expectedTail = `${pattern.class}.${pattern.method}`;
|
|
14922
|
-
if (
|
|
14999
|
+
if (normalisedTarget && (normalisedTarget === expectedTail || normalisedTarget.endsWith("." + expectedTail))) {} else {
|
|
14923
15000
|
return false;
|
|
14924
15001
|
}
|
|
14925
15002
|
}
|
|
@@ -26077,7 +26154,7 @@ class LanguageSourcesPass {
|
|
|
26077
26154
|
}
|
|
26078
26155
|
}
|
|
26079
26156
|
if (language === "csharp") {
|
|
26080
|
-
additionalSanitizers.push(...findCSharpFullPathStartsWithGuardSanitizers(code));
|
|
26157
|
+
additionalSanitizers.push(...findCSharpFullPathStartsWithGuardSanitizers(code), ...findCSharpEarlyReturnGuardSanitizers(code, language));
|
|
26081
26158
|
}
|
|
26082
26159
|
if (language === "java") {
|
|
26083
26160
|
additionalSanitizers.push(...findJavaSafeJsonParseSanitizers(code));
|
|
@@ -26783,18 +26860,23 @@ function findCSharpRequestSources(sourceCode, language) {
|
|
|
26783
26860
|
const lines = sourceCode.split(`
|
|
26784
26861
|
`);
|
|
26785
26862
|
const assignRe = /^\s*(?:await\s+)?(?:using\s*\(?\s*)?(?:var\s+|[A-Za-z_][\w.<>\[\]]*\s+)?([A-Za-z_]\w*)\s*=\s*(.+?);?\s*$/;
|
|
26786
|
-
const requestReadRe = /\bRequest\s*\.\s*(?:Query|Form|Headers|Cookies|QueryString|RouteValues|Body|Files|Params)\b/;
|
|
26787
26863
|
const consoleReadRe = /\bConsole\s*\.\s*ReadLine\s*\(/;
|
|
26788
26864
|
const envReadRe = /\bEnvironment\s*\.\s*GetEnvironmentVariables?\s*(?:\(|\[)/;
|
|
26789
26865
|
const formFileParams = new Set;
|
|
26790
26866
|
const formFileDeclRe = /\bIFormFile\s+([A-Za-z_]\w*)/g;
|
|
26867
|
+
const httpRequestReceivers = new Set(["Request"]);
|
|
26868
|
+
const httpRequestDeclRe = /\b(?:HttpRequest|HttpRequestBase)\??\s+([A-Za-z_]\w*)/g;
|
|
26791
26869
|
for (const line of lines) {
|
|
26792
|
-
const
|
|
26870
|
+
const formRe = new RegExp(formFileDeclRe.source, "g");
|
|
26793
26871
|
let d;
|
|
26794
|
-
while ((d =
|
|
26872
|
+
while ((d = formRe.exec(line)) !== null)
|
|
26795
26873
|
formFileParams.add(d[1]);
|
|
26874
|
+
const reqRe = new RegExp(httpRequestDeclRe.source, "g");
|
|
26875
|
+
while ((d = reqRe.exec(line)) !== null)
|
|
26876
|
+
httpRequestReceivers.add(d[1]);
|
|
26796
26877
|
}
|
|
26797
26878
|
const formFileReadRe = formFileParams.size > 0 ? new RegExp(`\\b(?:${[...formFileParams].join("|")})\\s*\\.\\s*(?:FileName|ContentType)\\b`) : null;
|
|
26879
|
+
const requestReadRe = new RegExp(`\\b(?:${[...httpRequestReceivers].join("|")})\\s*\\.\\s*(?:Query|Form|Headers|Cookies|QueryString|RouteValues|Body|Files|Params)\\b`);
|
|
26798
26880
|
const classify = (text) => requestReadRe.test(text) ? "http_param" : formFileReadRe?.test(text) ? "http_param" : consoleReadRe.test(text) ? "io_input" : envReadRe.test(text) ? "env_input" : null;
|
|
26799
26881
|
const inlineReads = [
|
|
26800
26882
|
[requestReadRe, "http_param"],
|
|
@@ -26819,10 +26901,37 @@ function findCSharpRequestSources(sourceCode, language) {
|
|
|
26819
26901
|
};
|
|
26820
26902
|
const exprStmtCallRe = /^\s*(?:await\s+)?[A-Za-z_][\w.]*(?:<[^>]*>)?\s*\(.*\)\s*;\s*$/;
|
|
26821
26903
|
const nonCallKeywordRe = /^\s*(?:await\s+)?(?:if|while|for|foreach|switch|using|lock|return|throw|catch|when|nameof|typeof|sizeof|default)\b/;
|
|
26904
|
+
const embeddedAssignRe = /(?:^|[{;])\s*(?:await\s+)?(?:using\s*\(?\s*)?(?:var\s+|[A-Za-z_][\w.<>\[\]]*\s+)?([A-Za-z_]\w*)\s*=(?!=)\s*([^;{}]+)/;
|
|
26822
26905
|
for (let i2 = 0;i2 < lines.length; i2++) {
|
|
26823
26906
|
const m = assignRe.exec(lines[i2]);
|
|
26824
26907
|
if (!m) {
|
|
26825
26908
|
const stmt = lines[i2];
|
|
26909
|
+
if (stmt.includes("{") && !/^\s*(?:\/\/|\/\*|\*)/.test(stmt)) {
|
|
26910
|
+
const emb = new RegExp(embeddedAssignRe.source, "g");
|
|
26911
|
+
let em;
|
|
26912
|
+
let seeded = false;
|
|
26913
|
+
while ((em = emb.exec(stmt)) !== null) {
|
|
26914
|
+
const varName2 = em[1];
|
|
26915
|
+
const rhs2 = em[2];
|
|
26916
|
+
const type3 = classify(rhs2);
|
|
26917
|
+
if (!type3)
|
|
26918
|
+
continue;
|
|
26919
|
+
const lineNumber3 = i2 + 1;
|
|
26920
|
+
if (sources.some((s) => s.line === lineNumber3 && s.variable === varName2))
|
|
26921
|
+
continue;
|
|
26922
|
+
sources.push({
|
|
26923
|
+
type: type3,
|
|
26924
|
+
location: `${varName2} = ${rhs2.trim().substring(0, 50)}${rhs2.length > 50 ? "..." : ""}`,
|
|
26925
|
+
severity: "high",
|
|
26926
|
+
line: lineNumber3,
|
|
26927
|
+
confidence: 1,
|
|
26928
|
+
variable: varName2
|
|
26929
|
+
});
|
|
26930
|
+
seeded = true;
|
|
26931
|
+
}
|
|
26932
|
+
if (seeded)
|
|
26933
|
+
continue;
|
|
26934
|
+
}
|
|
26826
26935
|
if (!exprStmtCallRe.test(stmt) || nonCallKeywordRe.test(stmt))
|
|
26827
26936
|
continue;
|
|
26828
26937
|
const hit = inlineRead(stmt);
|
|
@@ -29540,6 +29649,76 @@ function guardRejects(lines, i2, terminatorRe, window2) {
|
|
|
29540
29649
|
}
|
|
29541
29650
|
return false;
|
|
29542
29651
|
}
|
|
29652
|
+
function findCSharpEarlyReturnGuardSanitizers(code, language) {
|
|
29653
|
+
if (language !== "csharp")
|
|
29654
|
+
return [];
|
|
29655
|
+
const sanitizers = [];
|
|
29656
|
+
const lines = code.split(`
|
|
29657
|
+
`);
|
|
29658
|
+
const terminatorRe = /\b(throw|return)\b/;
|
|
29659
|
+
const allowlistRes = [
|
|
29660
|
+
/\bif\s*\(\s*Array\s*\.\s*IndexOf\s*\([^,]+,\s*([A-Za-z_][\w.]*)\s*\)\s*<\s*0\s*\)/,
|
|
29661
|
+
/\bif\s*\(\s*!\s*[A-Za-z_][\w.]*\s*\.\s*Contains\s*\(\s*([A-Za-z_][\w.]*)\s*\)\s*\)/
|
|
29662
|
+
];
|
|
29663
|
+
const charClassRe = /\bif\s*\(\s*!\s*([A-Za-z_][\w.]*)\s*\.\s*All\s*\(\s*char\s*\.\s*Is[A-Za-z]+\s*\)\s*\)/;
|
|
29664
|
+
for (let i2 = 0;i2 < lines.length; i2++) {
|
|
29665
|
+
let guardedExpr = null;
|
|
29666
|
+
for (const re of allowlistRes) {
|
|
29667
|
+
const m = re.exec(lines[i2]);
|
|
29668
|
+
if (m) {
|
|
29669
|
+
guardedExpr = m[1];
|
|
29670
|
+
break;
|
|
29671
|
+
}
|
|
29672
|
+
}
|
|
29673
|
+
if (!guardedExpr) {
|
|
29674
|
+
const m = charClassRe.exec(lines[i2]);
|
|
29675
|
+
if (m)
|
|
29676
|
+
guardedExpr = m[1];
|
|
29677
|
+
}
|
|
29678
|
+
if (!guardedExpr)
|
|
29679
|
+
continue;
|
|
29680
|
+
if (!guardRejects(lines, i2, terminatorRe, 6))
|
|
29681
|
+
continue;
|
|
29682
|
+
const root = guardedExpr.split(".")[0];
|
|
29683
|
+
const guarded = new Set([guardedExpr, root]);
|
|
29684
|
+
const assignRe = /^\s*(?:(?:var|string)\s+)?([A-Za-z_]\w*)\s*=(?!=)\s*(.*)$/;
|
|
29685
|
+
for (let b = i2 - 1;b >= 0; b--) {
|
|
29686
|
+
const a = assignRe.exec(lines[b]);
|
|
29687
|
+
if (!a || !guarded.has(a[1]))
|
|
29688
|
+
continue;
|
|
29689
|
+
for (const id of a[2].match(/[A-Za-z_]\w*/g) ?? [])
|
|
29690
|
+
guarded.add(id);
|
|
29691
|
+
}
|
|
29692
|
+
const mentions = (t) => {
|
|
29693
|
+
for (const n of guarded)
|
|
29694
|
+
if (new RegExp(`\\b${n.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}\\b`).test(t))
|
|
29695
|
+
return true;
|
|
29696
|
+
return false;
|
|
29697
|
+
};
|
|
29698
|
+
for (let l = i2 + 1;l < lines.length; l++) {
|
|
29699
|
+
const a = assignRe.exec(lines[l]);
|
|
29700
|
+
if (a && mentions(a[2]))
|
|
29701
|
+
guarded.add(a[1]);
|
|
29702
|
+
if (mentions(lines[l])) {
|
|
29703
|
+
sanitizers.push({
|
|
29704
|
+
type: "csharp_early_return_guard",
|
|
29705
|
+
method: "guard",
|
|
29706
|
+
line: l + 1,
|
|
29707
|
+
sanitizes: [
|
|
29708
|
+
"ssrf",
|
|
29709
|
+
"xpath_injection",
|
|
29710
|
+
"ldap_injection",
|
|
29711
|
+
"path_traversal",
|
|
29712
|
+
"command_injection",
|
|
29713
|
+
"sql_injection",
|
|
29714
|
+
"external_taint_escape"
|
|
29715
|
+
]
|
|
29716
|
+
});
|
|
29717
|
+
}
|
|
29718
|
+
}
|
|
29719
|
+
}
|
|
29720
|
+
return sanitizers;
|
|
29721
|
+
}
|
|
29543
29722
|
function findCSharpFullPathStartsWithGuardSanitizers(code) {
|
|
29544
29723
|
const sanitizers = [];
|
|
29545
29724
|
const lines = code.split(`
|
|
@@ -36613,6 +36792,65 @@ class LibraryProfileXssGatePass {
|
|
|
36613
36792
|
}
|
|
36614
36793
|
|
|
36615
36794
|
// ../circle-ir/dist/analysis/passes/taint-propagation-pass.js
|
|
36795
|
+
var CONTAINER_READ_SOURCES = new Set(["plugin_param", "config_param"]);
|
|
36796
|
+
function makeConstPropVeto(constProp, graph, sources, code) {
|
|
36797
|
+
const lines = typeof code === "string" ? code.split(`
|
|
36798
|
+
`) : [];
|
|
36799
|
+
const selfReassign = new Map;
|
|
36800
|
+
const escape = (v) => v.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
36801
|
+
for (const [line, defs] of graph.defsByLine) {
|
|
36802
|
+
const text = lines[line - 1];
|
|
36803
|
+
if (!text)
|
|
36804
|
+
continue;
|
|
36805
|
+
for (const d of defs) {
|
|
36806
|
+
if (d.kind !== "local" && d.kind !== "param")
|
|
36807
|
+
continue;
|
|
36808
|
+
const v = escape(d.variable);
|
|
36809
|
+
const m = new RegExp(`(?:^|[^\\w.$])${v}\\s*(?:([-+*/%&|^]|<<|>>)=|=(?!=))(.*)$`).exec(text);
|
|
36810
|
+
if (!m)
|
|
36811
|
+
continue;
|
|
36812
|
+
if (m[1] || new RegExp(`(?:^|[^\\w.$])${v}\\b`).test(m[2])) {
|
|
36813
|
+
const arr = selfReassign.get(d.variable) ?? [];
|
|
36814
|
+
arr.push(line);
|
|
36815
|
+
selfReassign.set(d.variable, arr);
|
|
36816
|
+
}
|
|
36817
|
+
}
|
|
36818
|
+
}
|
|
36819
|
+
const reassignedAt = (v, line) => (selfReassign.get(v) ?? []).includes(line);
|
|
36820
|
+
const defAt = (v, line) => {
|
|
36821
|
+
const own = (graph.defsByLine.get(line) ?? []).find((d) => d.variable === v);
|
|
36822
|
+
if (own)
|
|
36823
|
+
return own;
|
|
36824
|
+
const use = (graph.usesByLine.get(line) ?? []).find((u) => u.variable === v && u.def_id != null);
|
|
36825
|
+
return use ? graph.defById.get(use.def_id) : undefined;
|
|
36826
|
+
};
|
|
36827
|
+
return (step, sourceLine, sinkLine) => {
|
|
36828
|
+
const r = isFalsePositive(constProp, step.line, step.variable);
|
|
36829
|
+
if (!r.isFalsePositive)
|
|
36830
|
+
return false;
|
|
36831
|
+
if (r.reason !== "variable_not_tainted")
|
|
36832
|
+
return true;
|
|
36833
|
+
const lo = Math.min(sourceLine, sinkLine);
|
|
36834
|
+
let d = defAt(step.variable, step.line);
|
|
36835
|
+
let selfHops = 0;
|
|
36836
|
+
for (let guard = 0;d && d.line > lo && reassignedAt(d.variable, d.line) && guard < 32; guard++) {
|
|
36837
|
+
const here = d.id;
|
|
36838
|
+
const prior = (graph.usesByLine.get(d.line) ?? []).find((u) => u.variable === d.variable && u.def_id != null && u.def_id !== here);
|
|
36839
|
+
if (!prior)
|
|
36840
|
+
break;
|
|
36841
|
+
selfHops++;
|
|
36842
|
+
d = graph.defById.get(prior.def_id);
|
|
36843
|
+
}
|
|
36844
|
+
if (!d)
|
|
36845
|
+
return true;
|
|
36846
|
+
if (d.line === sourceLine && sources.some((s) => s.line === sourceLine && !CONTAINER_READ_SOURCES.has(s.type)))
|
|
36847
|
+
return false;
|
|
36848
|
+
if (selfHops > 0 && d.line >= lo)
|
|
36849
|
+
return false;
|
|
36850
|
+
return true;
|
|
36851
|
+
};
|
|
36852
|
+
}
|
|
36853
|
+
|
|
36616
36854
|
class TaintPropagationPass {
|
|
36617
36855
|
name = "taint-propagation";
|
|
36618
36856
|
category = "security";
|
|
@@ -36630,12 +36868,12 @@ class TaintPropagationPass {
|
|
|
36630
36868
|
return { flows: [] };
|
|
36631
36869
|
}
|
|
36632
36870
|
const propagationResult = propagateTaint(graph, sources, sinks, sanitizers);
|
|
36871
|
+
const isVetoed = makeConstPropVeto(constProp, graph, sources, ctx.code);
|
|
36633
36872
|
const verifiedFlows = propagationResult.flows.filter((flow) => {
|
|
36634
36873
|
if (constProp.unreachableLines.has(flow.sink.line))
|
|
36635
36874
|
return false;
|
|
36636
36875
|
for (const step of flow.path) {
|
|
36637
|
-
|
|
36638
|
-
if (fpCheck.isFalsePositive)
|
|
36876
|
+
if (isVetoed(step, flow.source.line, flow.sink.line))
|
|
36639
36877
|
return false;
|
|
36640
36878
|
}
|
|
36641
36879
|
if (isCorrelatedPredicateFP(constProp, flow))
|
|
@@ -36741,7 +36979,7 @@ class TaintPropagationPass {
|
|
|
36741
36979
|
continue;
|
|
36742
36980
|
let isFP = false;
|
|
36743
36981
|
for (const step of f.path) {
|
|
36744
|
-
if (
|
|
36982
|
+
if (isVetoed(step, f.source_line, f.sink_line)) {
|
|
36745
36983
|
isFP = true;
|
|
36746
36984
|
break;
|
|
36747
36985
|
}
|
|
@@ -36767,7 +37005,7 @@ class TaintPropagationPass {
|
|
|
36767
37005
|
continue;
|
|
36768
37006
|
let isFP = false;
|
|
36769
37007
|
for (const step of f.path) {
|
|
36770
|
-
if (
|
|
37008
|
+
if (isVetoed(step, f.source_line, f.sink_line)) {
|
|
36771
37009
|
isFP = true;
|
|
36772
37010
|
break;
|
|
36773
37011
|
}
|
|
@@ -38171,6 +38409,109 @@ var GO_PURE_PACKAGES = new Set([
|
|
|
38171
38409
|
"hex",
|
|
38172
38410
|
"base64"
|
|
38173
38411
|
]);
|
|
38412
|
+
var CSHARP_PURE_STATIC_RECEIVERS = new Set([
|
|
38413
|
+
"string",
|
|
38414
|
+
"String",
|
|
38415
|
+
"Convert",
|
|
38416
|
+
"Encoding",
|
|
38417
|
+
"BitConverter",
|
|
38418
|
+
"Math",
|
|
38419
|
+
"Int16",
|
|
38420
|
+
"Int32",
|
|
38421
|
+
"Int64",
|
|
38422
|
+
"UInt16",
|
|
38423
|
+
"UInt32",
|
|
38424
|
+
"UInt64",
|
|
38425
|
+
"Double",
|
|
38426
|
+
"Single",
|
|
38427
|
+
"Decimal",
|
|
38428
|
+
"Boolean",
|
|
38429
|
+
"Byte",
|
|
38430
|
+
"SByte",
|
|
38431
|
+
"Char",
|
|
38432
|
+
"Guid",
|
|
38433
|
+
"DateTime",
|
|
38434
|
+
"DateTimeOffset",
|
|
38435
|
+
"TimeSpan",
|
|
38436
|
+
"Enum",
|
|
38437
|
+
"Array",
|
|
38438
|
+
"Buffer",
|
|
38439
|
+
"Nullable",
|
|
38440
|
+
"CultureInfo",
|
|
38441
|
+
"StringComparer",
|
|
38442
|
+
"Comparer",
|
|
38443
|
+
"EqualityComparer",
|
|
38444
|
+
"Console",
|
|
38445
|
+
"Debug",
|
|
38446
|
+
"Trace",
|
|
38447
|
+
"Logger",
|
|
38448
|
+
"logger",
|
|
38449
|
+
"_logger",
|
|
38450
|
+
"Log"
|
|
38451
|
+
]);
|
|
38452
|
+
var CSHARP_NON_ESCAPE_METHODS = new Set([
|
|
38453
|
+
"ToString",
|
|
38454
|
+
"ToArray",
|
|
38455
|
+
"ToList",
|
|
38456
|
+
"ToCharArray",
|
|
38457
|
+
"GetHashCode",
|
|
38458
|
+
"Equals",
|
|
38459
|
+
"CompareTo",
|
|
38460
|
+
"Trim",
|
|
38461
|
+
"TrimStart",
|
|
38462
|
+
"TrimEnd",
|
|
38463
|
+
"Substring",
|
|
38464
|
+
"Split",
|
|
38465
|
+
"Join",
|
|
38466
|
+
"Concat",
|
|
38467
|
+
"PadLeft",
|
|
38468
|
+
"PadRight",
|
|
38469
|
+
"ToLower",
|
|
38470
|
+
"ToUpper",
|
|
38471
|
+
"ToLowerInvariant",
|
|
38472
|
+
"ToUpperInvariant",
|
|
38473
|
+
"StartsWith",
|
|
38474
|
+
"EndsWith",
|
|
38475
|
+
"Contains",
|
|
38476
|
+
"IndexOf",
|
|
38477
|
+
"LastIndexOf",
|
|
38478
|
+
"Insert",
|
|
38479
|
+
"Remove",
|
|
38480
|
+
"Normalize",
|
|
38481
|
+
"Append",
|
|
38482
|
+
"AppendLine",
|
|
38483
|
+
"AppendFormat",
|
|
38484
|
+
"TryParse",
|
|
38485
|
+
"Parse",
|
|
38486
|
+
"LogInformation",
|
|
38487
|
+
"LogWarning",
|
|
38488
|
+
"LogError",
|
|
38489
|
+
"LogDebug",
|
|
38490
|
+
"LogTrace",
|
|
38491
|
+
"LogCritical",
|
|
38492
|
+
"Information",
|
|
38493
|
+
"Warning",
|
|
38494
|
+
"Verbose",
|
|
38495
|
+
"Fatal",
|
|
38496
|
+
"BeginScope",
|
|
38497
|
+
"Add",
|
|
38498
|
+
"AddLast",
|
|
38499
|
+
"AddFirst",
|
|
38500
|
+
"AddRange",
|
|
38501
|
+
"TryAdd",
|
|
38502
|
+
"Insert",
|
|
38503
|
+
"Push",
|
|
38504
|
+
"Enqueue"
|
|
38505
|
+
]);
|
|
38506
|
+
function isCSharpNonEscapeCall(call) {
|
|
38507
|
+
const name2 = call.method_name;
|
|
38508
|
+
const recv = (call.receiver ?? "").split(".").pop() ?? "";
|
|
38509
|
+
if (CSHARP_PURE_STATIC_RECEIVERS.has(recv))
|
|
38510
|
+
return true;
|
|
38511
|
+
if (CSHARP_NON_ESCAPE_METHODS.has(name2))
|
|
38512
|
+
return true;
|
|
38513
|
+
return false;
|
|
38514
|
+
}
|
|
38174
38515
|
function isGoNonEscapeCall(call) {
|
|
38175
38516
|
const name2 = call.method_name;
|
|
38176
38517
|
const recv = (call.receiver ?? "").split(".").pop() ?? "";
|
|
@@ -38418,6 +38759,9 @@ function analyzeInterprocedural2(graphOrTypes, callsOrSources, dfgOrSinks, sourc
|
|
|
38418
38759
|
if (graph.ir.meta.language === "go" && isGoNonEscapeCall(call)) {
|
|
38419
38760
|
continue;
|
|
38420
38761
|
}
|
|
38762
|
+
if (graph.ir.meta.language === "csharp" && isCSharpNonEscapeCall(call)) {
|
|
38763
|
+
continue;
|
|
38764
|
+
}
|
|
38421
38765
|
const bashSafeBuiltins = new Set([
|
|
38422
38766
|
"echo",
|
|
38423
38767
|
"printf",
|
|
@@ -40052,6 +40396,8 @@ class VariableShadowingPass {
|
|
|
40052
40396
|
const outerEntry = declLocals[0];
|
|
40053
40397
|
for (let i2 = 1;i2 < numDeclLocals; i2++) {
|
|
40054
40398
|
const inner = declLocals[i2];
|
|
40399
|
+
if (inner.def.line <= outerEntry.def.line)
|
|
40400
|
+
continue;
|
|
40055
40401
|
if (!isInNestedScope(codeLines, outerEntry.def.line, inner.def.line))
|
|
40056
40402
|
continue;
|
|
40057
40403
|
const key = `${variable}-${inner.def.line}`;
|
|
@@ -49135,7 +49481,30 @@ async function analyzeProject(files, options = {}) {
|
|
|
49135
49481
|
const perFileBudgetMs = options.perFileBudgetMs ?? 0;
|
|
49136
49482
|
const perFileStart = Date.now();
|
|
49137
49483
|
let perFileBudgetExceeded = false;
|
|
49484
|
+
const maxProjectSourceChars = options.maxProjectSourceChars ?? 0;
|
|
49485
|
+
const maxProjectFiles = options.maxProjectFiles ?? 0;
|
|
49486
|
+
let projectSourceChars = 0;
|
|
49487
|
+
let projectSizeBudgetExceeded = false;
|
|
49138
49488
|
for (const { code, filePath, language } of files) {
|
|
49489
|
+
if (maxProjectFiles > 0 && fileAnalyses.length >= maxProjectFiles) {
|
|
49490
|
+
projectSizeBudgetExceeded = true;
|
|
49491
|
+
logger.warn("analyzeProject: file-count ceiling reached, skipping remaining files", {
|
|
49492
|
+
analysed: fileAnalyses.length,
|
|
49493
|
+
total: files.length,
|
|
49494
|
+
maxProjectFiles
|
|
49495
|
+
});
|
|
49496
|
+
break;
|
|
49497
|
+
}
|
|
49498
|
+
if (maxProjectSourceChars > 0 && projectSourceChars + code.length > maxProjectSourceChars) {
|
|
49499
|
+
projectSizeBudgetExceeded = true;
|
|
49500
|
+
logger.warn("analyzeProject: source-size ceiling reached, skipping remaining files", {
|
|
49501
|
+
analysed: fileAnalyses.length,
|
|
49502
|
+
total: files.length,
|
|
49503
|
+
sourceChars: projectSourceChars,
|
|
49504
|
+
maxProjectSourceChars
|
|
49505
|
+
});
|
|
49506
|
+
break;
|
|
49507
|
+
}
|
|
49139
49508
|
if (perFileBudgetMs > 0 && Date.now() - perFileStart > perFileBudgetMs) {
|
|
49140
49509
|
perFileBudgetExceeded = true;
|
|
49141
49510
|
logger.warn("analyzeProject: per-file budget exceeded, skipping remaining files", {
|
|
@@ -49150,6 +49519,7 @@ async function analyzeProject(files, options = {}) {
|
|
|
49150
49519
|
projectGraph.addFile(filePath, new CodeGraph(ir));
|
|
49151
49520
|
sourceLinesByFile.set(filePath, code.split(`
|
|
49152
49521
|
`));
|
|
49522
|
+
projectSourceChars += code.length;
|
|
49153
49523
|
}
|
|
49154
49524
|
const crossFileBudgetMs = options.crossFileBudgetMs ?? 300000;
|
|
49155
49525
|
const crossFileResult = new CrossFilePass().run(projectGraph, sourceLinesByFile, { budgetMs: crossFileBudgetMs });
|
|
@@ -49203,6 +49573,9 @@ async function analyzeProject(files, options = {}) {
|
|
|
49203
49573
|
if (perFileBudgetExceeded) {
|
|
49204
49574
|
projectAnalysis.per_file_budget_exceeded = true;
|
|
49205
49575
|
}
|
|
49576
|
+
if (projectSizeBudgetExceeded) {
|
|
49577
|
+
projectAnalysis.project_size_budget_exceeded = true;
|
|
49578
|
+
}
|
|
49206
49579
|
return projectAnalysis;
|
|
49207
49580
|
}
|
|
49208
49581
|
function deriveProjectName(paths) {
|
|
@@ -50296,7 +50669,7 @@ var colors = {
|
|
|
50296
50669
|
};
|
|
50297
50670
|
|
|
50298
50671
|
// src/version.ts
|
|
50299
|
-
var version = "4.9.
|
|
50672
|
+
var version = "4.9.28";
|
|
50300
50673
|
|
|
50301
50674
|
// src/formatters.ts
|
|
50302
50675
|
var LIBRARY_API_SURFACE_TAG2 = "library-api-surface:caller-responsibility";
|
|
@@ -50694,6 +51067,12 @@ function formatResults(results, verbose, crossFileData, profileSummary) {
|
|
|
50694
51067
|
lines.push("");
|
|
50695
51068
|
lines.push(formatCrossFilePaths(crossFileData.taintPaths));
|
|
50696
51069
|
}
|
|
51070
|
+
if (crossFileData?.projectSizeBudgetExceeded) {
|
|
51071
|
+
lines.push("");
|
|
51072
|
+
lines.push(colors.yellow(`⚠ Project size ceiling reached — analysed ${crossFileData.filesAnalysed ?? "?"} of ${crossFileData.filesTotal ?? "?"} files.`));
|
|
51073
|
+
lines.push(colors.yellow(" Results cover the analysed files only; this is NOT a clean scan of the project."));
|
|
51074
|
+
lines.push(colors.yellow(" Raise or disable the cap with --max-project-source-chars <n> (0 = unlimited)."));
|
|
51075
|
+
}
|
|
50697
51076
|
if (crossFileData?.budgetExceeded) {
|
|
50698
51077
|
lines.push("");
|
|
50699
51078
|
lines.push(colors.yellow("⚠ Cross-file budget exceeded — some cross-file taint paths may be missing."));
|
|
@@ -50714,6 +51093,7 @@ function formatJSON(results, crossFileData, profileSummary) {
|
|
|
50714
51093
|
cross_file_taint_paths: crossFileData?.taintPaths ?? [],
|
|
50715
51094
|
cross_file_calls: crossFileData?.crossFileCalls ?? [],
|
|
50716
51095
|
cross_file_budget_exceeded: crossFileData?.budgetExceeded ?? false,
|
|
51096
|
+
project_size_budget_exceeded: crossFileData?.projectSizeBudgetExceeded ?? false,
|
|
50717
51097
|
...profileSummary ? { project_profile: profileSummary } : {},
|
|
50718
51098
|
summary: {
|
|
50719
51099
|
filesScanned: results.length,
|
|
@@ -50841,8 +51221,8 @@ function generateSarifResults(results, crossFileData) {
|
|
|
50841
51221
|
|
|
50842
51222
|
// src/build-info.ts
|
|
50843
51223
|
var buildInfo = {
|
|
50844
|
-
gitSha: "
|
|
50845
|
-
builtAt: "2026-
|
|
51224
|
+
gitSha: "7bfe5de",
|
|
51225
|
+
builtAt: "2026-10-01T00:24:15.337Z"
|
|
50846
51226
|
};
|
|
50847
51227
|
|
|
50848
51228
|
// src/utils/args.ts
|
|
@@ -50932,6 +51312,17 @@ SCAN OPTIONS:
|
|
|
50932
51312
|
On exceed: partial taint paths kept, remaining
|
|
50933
51313
|
cross-file phases skipped, cross_file_budget_exceeded
|
|
50934
51314
|
surfaced in output (text warning / JSON / SARIF field).
|
|
51315
|
+
--max-project-source-chars <n>
|
|
51316
|
+
Cap the per-file phase by total source characters
|
|
51317
|
+
[default: scaled to this process's V8 heap limit
|
|
51318
|
+
(~7000 chars per MB, so ~29M at Node's default
|
|
51319
|
+
4 GB heap) — 0 = unlimited. Raise it together
|
|
51320
|
+
with --max-old-space-size, not on its own.]
|
|
51321
|
+
A project large enough to exhaust the V8 heap would
|
|
51322
|
+
otherwise abort the process with no results at all.
|
|
51323
|
+
On exceed: remaining files skipped, results kept for
|
|
51324
|
+
the analysed subset, project_size_budget_exceeded
|
|
51325
|
+
surfaced in output (text warning / JSON field).
|
|
50935
51326
|
|
|
50936
51327
|
METRICS OPTIONS:
|
|
50937
51328
|
-l, --language <lang> Analyze only files for language (bash|csharp|go|html|java|javascript|typescript|python|rust)
|
|
@@ -51350,12 +51741,35 @@ async function scanFile(filePath, language, analyzeOpts) {
|
|
|
51350
51741
|
};
|
|
51351
51742
|
}
|
|
51352
51743
|
}
|
|
51353
|
-
|
|
51354
|
-
|
|
51355
|
-
|
|
51356
|
-
|
|
51357
|
-
|
|
51358
|
-
|
|
51744
|
+
var CHARS_PER_MB_OF_HEAP = 7000;
|
|
51745
|
+
function defaultMaxProjectSourceChars() {
|
|
51746
|
+
try {
|
|
51747
|
+
const heapMb = getHeapStatistics().heap_size_limit / (1024 * 1024);
|
|
51748
|
+
if (!Number.isFinite(heapMb) || heapMb <= 0)
|
|
51749
|
+
return 32000000;
|
|
51750
|
+
return Math.floor(heapMb * CHARS_PER_MB_OF_HEAP);
|
|
51751
|
+
} catch {
|
|
51752
|
+
return 32000000;
|
|
51753
|
+
}
|
|
51754
|
+
}
|
|
51755
|
+
async function scanProject(files, language, analyzeOpts, crossFileBudgetMs, maxProjectSourceChars) {
|
|
51756
|
+
const sourceCap = maxProjectSourceChars ?? defaultMaxProjectSourceChars();
|
|
51757
|
+
const filesWithCode = [];
|
|
51758
|
+
let sourceChars = 0;
|
|
51759
|
+
let readTruncated = false;
|
|
51760
|
+
for (const f of files) {
|
|
51761
|
+
const code = readFileSync(f, "utf-8");
|
|
51762
|
+
if (sourceCap > 0 && sourceChars + code.length > sourceCap && filesWithCode.length > 0) {
|
|
51763
|
+
readTruncated = true;
|
|
51764
|
+
break;
|
|
51765
|
+
}
|
|
51766
|
+
sourceChars += code.length;
|
|
51767
|
+
filesWithCode.push({
|
|
51768
|
+
code,
|
|
51769
|
+
filePath: f,
|
|
51770
|
+
language: language || detectLanguage7(f)
|
|
51771
|
+
});
|
|
51772
|
+
}
|
|
51359
51773
|
const projectResult = await analyzeProject(filesWithCode, {
|
|
51360
51774
|
passOptions: analyzeOpts?.passOptions,
|
|
51361
51775
|
disabledPasses: analyzeOpts?.disabledPasses,
|
|
@@ -51391,7 +51805,10 @@ async function scanProject(files, language, analyzeOpts, crossFileBudgetMs) {
|
|
|
51391
51805
|
crossFileData: {
|
|
51392
51806
|
taintPaths: projectResult.taint_paths,
|
|
51393
51807
|
crossFileCalls: projectResult.cross_file_calls,
|
|
51394
|
-
budgetExceeded: projectResult.cross_file_budget_exceeded === true
|
|
51808
|
+
budgetExceeded: projectResult.cross_file_budget_exceeded === true,
|
|
51809
|
+
projectSizeBudgetExceeded: readTruncated || projectResult.project_size_budget_exceeded === true,
|
|
51810
|
+
filesAnalysed: projectResult.files.length,
|
|
51811
|
+
filesTotal: files.length
|
|
51395
51812
|
}
|
|
51396
51813
|
};
|
|
51397
51814
|
}
|
|
@@ -51622,7 +52039,7 @@ async function runScan(targetPath, options) {
|
|
|
51622
52039
|
if ((await stat2(absPath)).isDirectory()) {
|
|
51623
52040
|
if (spin)
|
|
51624
52041
|
spin.text = `Running project analysis on ${files.length} file(s)...`;
|
|
51625
|
-
const projectScan = await scanProject(files, options.language, analyzeOpts, options.crossFileBudgetMs);
|
|
52042
|
+
const projectScan = await scanProject(files, options.language, analyzeOpts, options.crossFileBudgetMs, options.maxProjectSourceChars);
|
|
51626
52043
|
results = projectScan.results;
|
|
51627
52044
|
crossFileData = projectScan.crossFileData;
|
|
51628
52045
|
} else {
|
|
@@ -51654,8 +52071,10 @@ async function runScan(targetPath, options) {
|
|
|
51654
52071
|
spin.text = `Scanning... (${processed}/${files.length})`;
|
|
51655
52072
|
}
|
|
51656
52073
|
}
|
|
51657
|
-
if (spin)
|
|
51658
|
-
|
|
52074
|
+
if (spin) {
|
|
52075
|
+
const analysed = crossFileData?.projectSizeBudgetExceeded ? crossFileData.filesAnalysed ?? files.length : files.length;
|
|
52076
|
+
spin.succeed(analysed === files.length ? `Scanned ${files.length} file(s)` : `Scanned ${analysed} of ${files.length} file(s) — stopped at the project size ceiling`);
|
|
52077
|
+
}
|
|
51659
52078
|
if (suppressions.length > 0) {
|
|
51660
52079
|
const beforeCount = results.reduce((sum, r) => sum + r.vulnerabilities.length, 0) + (crossFileData?.taintPaths.length ?? 0);
|
|
51661
52080
|
results = applySuppressionsToResults(results, suppressions, process.cwd());
|
|
@@ -51731,7 +52150,8 @@ async function runScan(targetPath, options) {
|
|
|
51731
52150
|
const qualityCount = allVulns.filter((v) => v.category !== "security").length;
|
|
51732
52151
|
const errors = results.filter((r) => r.error).length;
|
|
51733
52152
|
const crossFilePaths = crossFileData?.taintPaths.length ?? 0;
|
|
51734
|
-
const
|
|
52153
|
+
const truncated = crossFileData?.projectSizeBudgetExceeded === true;
|
|
52154
|
+
const shouldOutput = totalVulns > 0 || crossFilePaths > 0 || errors > 0 || options.verbose || options.output || options.format !== "text" || truncated;
|
|
51735
52155
|
const profileSummary = buildProfileSummary(absPath, detectedModules, resolvedProfiles);
|
|
51736
52156
|
if (resolvedProfiles) {
|
|
51737
52157
|
for (const r of results) {
|
|
@@ -51763,15 +52183,17 @@ async function runScan(targetPath, options) {
|
|
|
51763
52183
|
}
|
|
51764
52184
|
if (!options.quiet && options.format === "text") {
|
|
51765
52185
|
console.log();
|
|
52186
|
+
const scanned = crossFileData?.projectSizeBudgetExceeded ? crossFileData.filesAnalysed ?? files.length : files.length;
|
|
52187
|
+
const scannedLabel = crossFileData?.projectSizeBudgetExceeded ? `${scanned} of ${crossFileData.filesTotal ?? files.length} file(s) analysed` : `${scanned} file(s)`;
|
|
51766
52188
|
if (securityCount > 0) {
|
|
51767
|
-
console.log(colors.red(`Found ${securityCount} security finding(s) in ${
|
|
52189
|
+
console.log(colors.red(`Found ${securityCount} security finding(s) in ${scannedLabel}`));
|
|
51768
52190
|
}
|
|
51769
52191
|
if (qualityCount > 0) {
|
|
51770
52192
|
const label = securityCount > 0 ? "Also found" : "Found";
|
|
51771
|
-
console.log(colors.yellow(`${label} ${qualityCount} code quality finding(s) in ${
|
|
52193
|
+
console.log(colors.yellow(`${label} ${qualityCount} code quality finding(s) in ${scannedLabel}`));
|
|
51772
52194
|
}
|
|
51773
52195
|
if (securityCount === 0 && qualityCount === 0 && options.verbose) {
|
|
51774
|
-
console.log(colors.green(`No findings in ${
|
|
52196
|
+
console.log(colors.green(`No findings in ${scannedLabel}`));
|
|
51775
52197
|
}
|
|
51776
52198
|
if (errors > 0) {
|
|
51777
52199
|
console.log(colors.yellow(`${errors} file(s) had errors during analysis`));
|
|
@@ -52065,6 +52487,17 @@ function parseCrossFileBudgetMs(raw) {
|
|
|
52065
52487
|
}
|
|
52066
52488
|
return n;
|
|
52067
52489
|
}
|
|
52490
|
+
function parseMaxProjectSourceChars(raw) {
|
|
52491
|
+
if (raw === undefined || raw === true || raw === "")
|
|
52492
|
+
return;
|
|
52493
|
+
const s = String(raw);
|
|
52494
|
+
const n = Number.parseInt(s, 10);
|
|
52495
|
+
if (!Number.isFinite(n) || n < 0 || String(n) !== s) {
|
|
52496
|
+
console.error(colors.yellow(`Warning: invalid --max-project-source-chars "${s}" (expected non-negative integer, 0 = unlimited); ignoring`));
|
|
52497
|
+
return;
|
|
52498
|
+
}
|
|
52499
|
+
return n;
|
|
52500
|
+
}
|
|
52068
52501
|
var SBOM_MANIFESTS = {
|
|
52069
52502
|
"package.json": parseNpmDependencies,
|
|
52070
52503
|
"package-lock.json": parseNpmLockDependencies,
|
|
@@ -52262,6 +52695,7 @@ Usage: cognium-dev scan <path> [options]`);
|
|
|
52262
52695
|
profile: options.profile || options.p,
|
|
52263
52696
|
disablePass: options["disable-pass"],
|
|
52264
52697
|
crossFileBudgetMs: parseCrossFileBudgetMs(options["cross-file-budget-ms"]),
|
|
52698
|
+
maxProjectSourceChars: parseMaxProjectSourceChars(options["max-project-source-chars"]),
|
|
52265
52699
|
projectProfile: parseProjectProfileArg(options["project-profile"]),
|
|
52266
52700
|
noProjectProfile: options["no-project-profile"] === true,
|
|
52267
52701
|
projectProfileExplain: options["project-profile-explain"] === true
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cognium-dev",
|
|
3
|
-
"version": "4.9.
|
|
3
|
+
"version": "4.9.28",
|
|
4
4
|
"description": "Static Application Security Testing CLI for detecting security vulnerabilities via taint tracking",
|
|
5
5
|
"bin": {
|
|
6
6
|
"cognium-dev": "dist/cli.js"
|
|
@@ -65,10 +65,10 @@
|
|
|
65
65
|
},
|
|
66
66
|
"dependencies": {
|
|
67
67
|
"@cognium/project-profile-detect": "1.1.2",
|
|
68
|
-
"circle-ir": "4.9.
|
|
68
|
+
"circle-ir": "4.9.28"
|
|
69
69
|
},
|
|
70
70
|
"devDependencies": {
|
|
71
|
-
"@types/node": "^26.6.
|
|
71
|
+
"@types/node": "^26.6.2",
|
|
72
72
|
"ajv": "^8.17.1",
|
|
73
73
|
"ajv-draft-04": "^1.0.0",
|
|
74
74
|
"bun-types": "^1.4.2",
|