cognium-dev 4.9.22 → 4.9.23

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 +288 -4
  2. package/package.json +2 -2
package/dist/cli.js CHANGED
@@ -14400,6 +14400,35 @@ function classifyGoDestinationType(decl) {
14400
14400
  return "unsafe";
14401
14401
  return "safe";
14402
14402
  }
14403
+ function isGoFprintToNonResponseWriter(call, pattern, language, sourceLines) {
14404
+ if (language !== "go")
14405
+ return false;
14406
+ if (pattern.type !== "xss" || pattern.class !== "fmt")
14407
+ return false;
14408
+ if (!/^Fprint(?:f|ln)?$/.test(call.method_name))
14409
+ return false;
14410
+ if (!sourceLines)
14411
+ return true;
14412
+ const writer = call.arguments.find((a) => a.position === 0);
14413
+ const name2 = (writer?.expression ?? "").trim();
14414
+ if (!/^[A-Za-z_]\w*$/.test(name2))
14415
+ return true;
14416
+ const rwRe = new RegExp(`\\b${name2}\\s+(?:net/)?http\\.ResponseWriter\\b`);
14417
+ for (let i2 = call.location.line - 1;i2 >= 0; i2--) {
14418
+ const line = sourceLines[i2] ?? "";
14419
+ const funcIdx = line.search(/\bfunc\b/);
14420
+ if (funcIdx < 0)
14421
+ continue;
14422
+ const header = sourceLines.slice(i2, i2 + 4).join(" ");
14423
+ const brace = header.indexOf("{");
14424
+ const params = header.slice(funcIdx, brace >= 0 ? brace : header.length);
14425
+ if (rwRe.test(params))
14426
+ return false;
14427
+ if (/^\s*func\b/.test(line))
14428
+ return true;
14429
+ }
14430
+ return true;
14431
+ }
14403
14432
  function isSafeGoJsonUnmarshalCall(call, pattern, language, sourceLines) {
14404
14433
  if (language !== "go")
14405
14434
  return false;
@@ -14545,6 +14574,9 @@ function findSinks(calls, patterns, typeHierarchy, language, sourceLines, types)
14545
14574
  if (isSafeGoJsonUnmarshalCall(call, pattern, language, sourceLines)) {
14546
14575
  continue;
14547
14576
  }
14577
+ if (isGoFprintToNonResponseWriter(call, pattern, language, sourceLines)) {
14578
+ continue;
14579
+ }
14548
14580
  if (isSafeJinjaRenderCall(call, pattern, language, sourceLines)) {
14549
14581
  continue;
14550
14582
  }
@@ -24126,7 +24158,23 @@ class GoPlugin extends BaseLanguagePlugin {
24126
24158
  type: "xss",
24127
24159
  cwe: "CWE-79",
24128
24160
  severity: "high",
24129
- argPositions: [1]
24161
+ argPositions: [1, 2, 3, 4, 5, 6]
24162
+ },
24163
+ {
24164
+ method: "Fprint",
24165
+ class: "fmt",
24166
+ type: "xss",
24167
+ cwe: "CWE-79",
24168
+ severity: "high",
24169
+ argPositions: [1, 2, 3, 4, 5, 6]
24170
+ },
24171
+ {
24172
+ method: "Fprintln",
24173
+ class: "fmt",
24174
+ type: "xss",
24175
+ cwe: "CWE-79",
24176
+ severity: "high",
24177
+ argPositions: [1, 2, 3, 4, 5, 6]
24130
24178
  },
24131
24179
  {
24132
24180
  method: "Write",
@@ -25810,6 +25858,8 @@ class LanguageSourcesPass {
25810
25858
  additionalSources.push(...findGoArgvSources(code, language));
25811
25859
  additionalSources.push(...findGoArchiveEntrySources(code, language));
25812
25860
  additionalSources.push(...findGoMultipartUploadSources(code, language));
25861
+ additionalSources.push(...findGoGrpcRequestSources(code, language));
25862
+ additionalSources.push(...findGoRequestBodySources(code, language));
25813
25863
  const jsDOMSinks = findJavaScriptDOMSinks(code, language);
25814
25864
  for (const s of jsDOMSinks) {
25815
25865
  const alreadyExists = additionalSinks.some((x) => x.line === s.line && x.cwe === s.cwe);
@@ -26555,6 +26605,127 @@ function findGoMultipartUploadSources(sourceCode, language) {
26555
26605
  }
26556
26606
  return sources;
26557
26607
  }
26608
+ function findGoGrpcRequestSources(sourceCode, language) {
26609
+ if (language !== "go")
26610
+ return [];
26611
+ if (!sourceCode.includes("context.Context"))
26612
+ return [];
26613
+ const sources = [];
26614
+ const lines = sourceCode.split(`
26615
+ `);
26616
+ const sigRe = /^func\s*\([^)]*\)\s*[A-Z]\w*\s*\(\s*\w+\s+context\.Context\s*,\s*([A-Za-z_]\w*)\s+\*(?:\w+\.)?\w*(?:Request|Req)\s*\)\s*\(\s*\*[\w.]+\s*,\s*error\s*\)\s*\{/;
26617
+ const controlRe = /^\s*(?:\}\s*else\s+)?(?:if|for|switch|case|return)\b/;
26618
+ for (let i2 = 0;i2 < lines.length; i2++) {
26619
+ const sig = sigRe.exec(lines[i2]);
26620
+ if (!sig)
26621
+ continue;
26622
+ const param = sig[1];
26623
+ if (param === "_")
26624
+ continue;
26625
+ const readRe = new RegExp(`(?<![\\w.])${param}\\s*\\.\\s*(?:Get[A-Z]\\w*\\s*\\(\\s*\\)|[A-Z]\\w*\\b(?!\\s*\\())`);
26626
+ const directReadRe = new RegExp(`^(?:(?:\\[\\]\\w+|string)\\s*\\(\\s*)*${param}(?:\\s*\\.\\s*(?:Get[A-Z]\\w*\\s*\\(\\s*\\)|[A-Z]\\w*\\b(?!\\s*\\()))+(?:\\s*\\))*$`);
26627
+ const numericParseRe = new RegExp(`\\bstrconv\\s*\\.\\s*(?:Atoi|ParseInt|ParseUint|ParseFloat|ParseBool)\\s*\\(\\s*${param}\\s*\\.`);
26628
+ let end = i2 + 1;
26629
+ while (end < lines.length && !/^\}/.test(lines[end]))
26630
+ end++;
26631
+ for (let j = i2 + 1;j < end; j++) {
26632
+ const line = lines[j];
26633
+ const read = readRe.exec(line);
26634
+ if (!read)
26635
+ continue;
26636
+ if (numericParseRe.test(line))
26637
+ continue;
26638
+ let variable;
26639
+ const unmarshal = /\bUnmarshal\w*\s*\([^&]*&\s*([A-Za-z_]\w*)/.exec(line);
26640
+ const assign = /^\s*(?:var\s+)?([A-Za-z_][\w\s,]*?)\s*:?=(?!=)\s*(.+)$/.exec(line);
26641
+ if (unmarshal && line.indexOf(read[0]) < line.indexOf("&")) {
26642
+ variable = unmarshal[1];
26643
+ } else if (assign && directReadRe.test(assign[2].trim()) && !controlRe.test(line)) {
26644
+ variable = assign[1].split(",").map((v) => v.trim()).find((v) => v !== "_" && v !== "err" && v !== "ok" && /^[A-Za-z_]\w*$/.test(v));
26645
+ if (!variable)
26646
+ continue;
26647
+ } else if (controlRe.test(line)) {
26648
+ continue;
26649
+ } else {
26650
+ variable = param;
26651
+ }
26652
+ const lineNumber = j + 1;
26653
+ if (sources.some((sc) => sc.line === lineNumber && sc.variable === variable))
26654
+ continue;
26655
+ sources.push({
26656
+ type: "http_body",
26657
+ location: `${read[0].replace(/\s+/g, "")} — field of the gRPC request message \`${param}\``,
26658
+ severity: "high",
26659
+ line: lineNumber,
26660
+ confidence: 0.85,
26661
+ variable
26662
+ });
26663
+ }
26664
+ i2 = end;
26665
+ }
26666
+ return sources;
26667
+ }
26668
+ function findGoRequestBodySources(sourceCode, language) {
26669
+ if (language !== "go")
26670
+ return [];
26671
+ if (!sourceCode.includes(".Body"))
26672
+ return [];
26673
+ const lines = sourceCode.split(`
26674
+ `);
26675
+ const reqNames = new Set;
26676
+ const paramRe = /([A-Za-z_]\w*)\s+\*http\.Request\b/g;
26677
+ for (const line of lines) {
26678
+ let m;
26679
+ paramRe.lastIndex = 0;
26680
+ while ((m = paramRe.exec(line)) !== null)
26681
+ reqNames.add(m[1]);
26682
+ }
26683
+ if (reqNames.size === 0)
26684
+ return [];
26685
+ const bodyAlt = [...reqNames].map((n) => n.replace(/\$/g, "\\$")).join("|");
26686
+ const bodyRef = `(?<![\\w.])(?:${bodyAlt})\\s*\\.\\s*Body\\b`;
26687
+ const sources = [];
26688
+ const push = (variable, line, how) => {
26689
+ if (variable === "_" || variable === "")
26690
+ return;
26691
+ if (sources.some((s) => s.line === line && s.variable === variable))
26692
+ return;
26693
+ sources.push({
26694
+ type: "http_body",
26695
+ location: `${how} — HTTP request body`,
26696
+ severity: "high",
26697
+ line,
26698
+ confidence: 0.85,
26699
+ ...variable ? { variable } : {}
26700
+ });
26701
+ };
26702
+ const bodyRe = new RegExp(bodyRef);
26703
+ const decodeRe = new RegExp(`(?:json|xml)\\s*\\.\\s*NewDecoder\\s*\\(\\s*(?:io\\.NopCloser\\s*\\(\\s*)?(?:${bodyAlt})\\s*\\.\\s*Body\\b[^)]*\\)\\s*\\)?\\s*\\.\\s*Decode\\s*\\(\\s*&\\s*([A-Za-z_]\\w*)`);
26704
+ const readAllRe = new RegExp(`\\b([A-Za-z_]\\w*)\\s*,\\s*\\w+\\s*:?=\\s*(?:io|ioutil)\\s*\\.\\s*ReadAll\\s*\\(\\s*(?:${bodyAlt})\\s*\\.\\s*Body\\b`);
26705
+ const copyRe = new RegExp(`\\bio\\s*\\.\\s*Copy\\s*\\(\\s*([A-Za-z_]\\w*|&\\s*[A-Za-z_]\\w*)\\s*,\\s*(?:${bodyAlt})\\s*\\.\\s*Body\\b`);
26706
+ for (let i2 = 0;i2 < lines.length; i2++) {
26707
+ const line = lines[i2];
26708
+ if (!bodyRe.test(line))
26709
+ continue;
26710
+ const n = i2 + 1;
26711
+ const dec = decodeRe.exec(line);
26712
+ if (dec) {
26713
+ push(dec[1], n, `json/xml decode of ${dec[0].split(".")[0]}.Body`);
26714
+ continue;
26715
+ }
26716
+ const ra = readAllRe.exec(line);
26717
+ if (ra) {
26718
+ push(ra[1], n, "io.ReadAll(req.Body)");
26719
+ continue;
26720
+ }
26721
+ const cp = copyRe.exec(line);
26722
+ if (cp) {
26723
+ push(cp[1].replace(/^&\s*/, ""), n, "io.Copy(dst, req.Body)");
26724
+ continue;
26725
+ }
26726
+ }
26727
+ return sources;
26728
+ }
26558
26729
  function findCSharpRequestSources(sourceCode, language) {
26559
26730
  if (language !== "csharp")
26560
26731
  return [];
@@ -37790,6 +37961,116 @@ function pickScopedSource(sources, sinkLine, methodName, types, taintedVar) {
37790
37961
  }
37791
37962
 
37792
37963
  // ../circle-ir/dist/analysis/interprocedural.js
37964
+ var GO_BUILTIN_CALLS = new Set([
37965
+ "len",
37966
+ "cap",
37967
+ "make",
37968
+ "new",
37969
+ "append",
37970
+ "copy",
37971
+ "delete",
37972
+ "close",
37973
+ "panic",
37974
+ "recover",
37975
+ "print",
37976
+ "println",
37977
+ "min",
37978
+ "max",
37979
+ "clear",
37980
+ "string",
37981
+ "int",
37982
+ "int8",
37983
+ "int16",
37984
+ "int32",
37985
+ "int64",
37986
+ "uint",
37987
+ "uint8",
37988
+ "uint16",
37989
+ "uint32",
37990
+ "uint64",
37991
+ "float32",
37992
+ "float64",
37993
+ "bool",
37994
+ "byte",
37995
+ "rune",
37996
+ "error"
37997
+ ]);
37998
+ var GO_ERROR_AND_LOG_CALLS = new Set([
37999
+ "Errorf",
38000
+ "Sprintf",
38001
+ "Sprint",
38002
+ "Sprintln",
38003
+ "Print",
38004
+ "Printf",
38005
+ "Println",
38006
+ "Fatal",
38007
+ "Fatalf",
38008
+ "Fatalln",
38009
+ "Panic",
38010
+ "Panicf",
38011
+ "Panicln",
38012
+ "Debug",
38013
+ "Debugf",
38014
+ "Debugln",
38015
+ "Debugw",
38016
+ "Info",
38017
+ "Infof",
38018
+ "Infoln",
38019
+ "Infow",
38020
+ "InfoS",
38021
+ "Warn",
38022
+ "Warnf",
38023
+ "Warnln",
38024
+ "Warnw",
38025
+ "Warning",
38026
+ "Warningf",
38027
+ "Warningln",
38028
+ "Error",
38029
+ "Errorln",
38030
+ "Errorw",
38031
+ "ErrorS",
38032
+ "Trace",
38033
+ "Tracef",
38034
+ "V",
38035
+ "WithError",
38036
+ "WithField",
38037
+ "WithFields",
38038
+ "WithValues",
38039
+ "Msg",
38040
+ "Msgf"
38041
+ ]);
38042
+ var GO_ERROR_PACKAGES = new Set(["errors", "status", "xerrors", "multierr", "multierror"]);
38043
+ var GO_PURE_PACKAGES = new Set([
38044
+ "strings",
38045
+ "strconv",
38046
+ "bytes",
38047
+ "unicode",
38048
+ "utf8",
38049
+ "path",
38050
+ "filepath",
38051
+ "math",
38052
+ "sort",
38053
+ "slices",
38054
+ "maps",
38055
+ "time",
38056
+ "regexp",
38057
+ "url",
38058
+ "hex",
38059
+ "base64"
38060
+ ]);
38061
+ function isGoNonEscapeCall(call) {
38062
+ const name2 = call.method_name;
38063
+ const recv = (call.receiver ?? "").split(".").pop() ?? "";
38064
+ if (!call.receiver && GO_BUILTIN_CALLS.has(name2))
38065
+ return true;
38066
+ if (GO_ERROR_PACKAGES.has(recv))
38067
+ return true;
38068
+ if (GO_PURE_PACKAGES.has(recv))
38069
+ return true;
38070
+ if (GO_ERROR_AND_LOG_CALLS.has(name2))
38071
+ return true;
38072
+ return false;
38073
+ }
37793
38074
  function analyzeInterprocedural2(graphOrTypes, callsOrSources, dfgOrSinks, sourcesOrSanitizers, sinksOrOptions, sanitizersArg, optionsArg = {}) {
37794
38075
  let graph;
37795
38076
  let sources;
@@ -38021,6 +38302,9 @@ function analyzeInterprocedural2(graphOrTypes, callsOrSources, dfgOrSinks, sourc
38021
38302
  if (!targetMethod) {
38022
38303
  if (taintedArgPositions.length > 0 && !collectionMethods.has(call.method_name) && !sanitizerMethods.has(call.method_name) && !safeUtilityMethods.has(call.method_name) && !ormQueryBuilderMethods.has(call.method_name)) {
38023
38304
  const isBash = graph.ir.meta.language === "bash";
38305
+ if (graph.ir.meta.language === "go" && isGoNonEscapeCall(call)) {
38306
+ continue;
38307
+ }
38024
38308
  const bashSafeBuiltins = new Set([
38025
38309
  "echo",
38026
38310
  "printf",
@@ -49846,7 +50130,7 @@ var colors = {
49846
50130
  };
49847
50131
 
49848
50132
  // src/version.ts
49849
- var version = "4.9.22";
50133
+ var version = "4.9.23";
49850
50134
 
49851
50135
  // src/formatters.ts
49852
50136
  var LIBRARY_API_SURFACE_TAG2 = "library-api-surface:caller-responsibility";
@@ -50391,8 +50675,8 @@ function generateSarifResults(results, crossFileData) {
50391
50675
 
50392
50676
  // src/build-info.ts
50393
50677
  var buildInfo = {
50394
- gitSha: "54a142d",
50395
- builtAt: "2026-09-22T01:39:02.544Z"
50678
+ gitSha: "e7e65f8",
50679
+ builtAt: "2026-09-22T22:15:34.355Z"
50396
50680
  };
50397
50681
 
50398
50682
  // src/utils/args.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cognium-dev",
3
- "version": "4.9.22",
3
+ "version": "4.9.23",
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",
@@ -67,7 +67,7 @@
67
67
  },
68
68
  "dependencies": {
69
69
  "@cognium/project-profile-detect": "1.1.1",
70
- "circle-ir": "4.9.22"
70
+ "circle-ir": "4.9.23"
71
71
  },
72
72
  "devDependencies": {
73
73
  "@types/node": "^26.6.1",