cognium-dev 3.133.0 → 3.134.0

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 +270 -1
  2. package/package.json +2 -2
package/dist/cli.js CHANGED
@@ -22613,6 +22613,9 @@ class LanguageSourcesPass {
22613
22613
  for (const finding of findGoPluginOpenCodeInjectionFindings(code, graph.ir.meta.file)) {
22614
22614
  ctx.addFinding(finding);
22615
22615
  }
22616
+ for (const finding of findGoMongoNosqlInjectionFindings(code, graph.ir.meta.file)) {
22617
+ ctx.addFinding(finding);
22618
+ }
22616
22619
  }
22617
22620
  if (language === "python") {
22618
22621
  additionalSanitizers.push(...findPythonNetlocAllowlistGuardSanitizers(code));
@@ -22637,6 +22640,9 @@ class LanguageSourcesPass {
22637
22640
  for (const finding of findPythonInteractiveInterpreterCodeInjectionFindings(code, graph.ir.meta.file)) {
22638
22641
  ctx.addFinding(finding);
22639
22642
  }
22643
+ for (const finding of findPythonMongoengineWhereNosqlInjectionFindings(code, graph.ir.meta.file)) {
22644
+ ctx.addFinding(finding);
22645
+ }
22640
22646
  }
22641
22647
  if (language === "rust") {
22642
22648
  additionalSanitizers.push(...findRustSetAllowlistGuardSanitizers(code));
@@ -22700,6 +22706,9 @@ class LanguageSourcesPass {
22700
22706
  for (const finding of findJavaResponseWriterXssFindings(code, graph.ir.meta.file)) {
22701
22707
  ctx.addFinding(finding);
22702
22708
  }
22709
+ for (const finding of findJavaMongoNosqlInjectionFindings(code, graph.ir.meta.file)) {
22710
+ ctx.addFinding(finding);
22711
+ }
22703
22712
  }
22704
22713
  if (language === "python" || language === "javascript" || language === "typescript" || language === "go") {
22705
22714
  const exfilFindings = findExternalSecretExfiltrationFindings(code, graph.ir.meta.file, language);
@@ -26680,6 +26689,266 @@ function findRustEvalCrateCodeInjectionFindings(code, file) {
26680
26689
  }
26681
26690
  return findings;
26682
26691
  }
26692
+ function findGoMongoNosqlInjectionFindings(code, file) {
26693
+ const findings = [];
26694
+ if (typeof code !== "string" || code.length === 0)
26695
+ return findings;
26696
+ if (!/(\bbson\s*\.\s*[MDAE]\b|mongo-driver|\*\s*mongo\.Collection)/.test(code)) {
26697
+ return findings;
26698
+ }
26699
+ const lines = code.split(`
26700
+ `);
26701
+ const reqExtractRe = /\b\w+\s*\.\s*(?:FormValue|PostFormValue|URL\s*\.\s*Query\s*\(\s*\)\s*\.\s*Get|Header\s*\.\s*Get|Cookie)\s*\(/;
26702
+ const httpReqParamRe = /\*\s*http\.Request\b/;
26703
+ const opsAlt = "(?:FindOne|Find|InsertOne|InsertMany|UpdateOne|UpdateMany|DeleteOne|DeleteMany|FindOneAndUpdate|FindOneAndDelete|FindOneAndReplace|Aggregate)";
26704
+ const callHeadRe = new RegExp(`\\.\\s*(${opsAlt})\\s*\\(`);
26705
+ function extractBalanced(line, openIdx) {
26706
+ let depth = 0;
26707
+ for (let k = openIdx;k < line.length; k++) {
26708
+ const ch = line[k];
26709
+ if (ch === "(")
26710
+ depth++;
26711
+ else if (ch === ")") {
26712
+ depth--;
26713
+ if (depth === 0)
26714
+ return line.substring(openIdx + 1, k);
26715
+ }
26716
+ }
26717
+ return null;
26718
+ }
26719
+ const funcs = [];
26720
+ let cur = null;
26721
+ for (let i2 = 0;i2 < lines.length; i2++) {
26722
+ const t = lines[i2].trim();
26723
+ if (/^func\b/.test(t)) {
26724
+ if (cur) {
26725
+ cur.end = i2 - 1;
26726
+ funcs.push(cur);
26727
+ }
26728
+ cur = { start: i2, end: lines.length - 1 };
26729
+ }
26730
+ }
26731
+ if (cur)
26732
+ funcs.push(cur);
26733
+ for (const fn of funcs) {
26734
+ const header = lines[fn.start];
26735
+ if (!httpReqParamRe.test(header))
26736
+ continue;
26737
+ const taintedVars = new Set;
26738
+ for (let pass = 0;pass < 3; pass++) {
26739
+ const before = taintedVars.size;
26740
+ for (let i2 = fn.start;i2 <= fn.end; i2++) {
26741
+ const trimmed = lines[i2].trim();
26742
+ const assignMatch = trimmed.match(/^(\w+)\s*(?::=|=)\s*(.+?)(?:\s*\/\/.*)?$/);
26743
+ if (!assignMatch)
26744
+ continue;
26745
+ const lhs = assignMatch[1];
26746
+ const rhs = assignMatch[2];
26747
+ if (taintedVars.has(lhs))
26748
+ continue;
26749
+ if (reqExtractRe.test(rhs)) {
26750
+ taintedVars.add(lhs);
26751
+ continue;
26752
+ }
26753
+ for (const v of taintedVars) {
26754
+ if (new RegExp(`\\b${v}\\b`).test(rhs)) {
26755
+ taintedVars.add(lhs);
26756
+ break;
26757
+ }
26758
+ }
26759
+ }
26760
+ if (taintedVars.size === before)
26761
+ break;
26762
+ }
26763
+ if (taintedVars.size === 0)
26764
+ continue;
26765
+ for (let i2 = fn.start;i2 <= fn.end; i2++) {
26766
+ const line = lines[i2];
26767
+ const m = callHeadRe.exec(line);
26768
+ if (!m)
26769
+ continue;
26770
+ const op = m[1];
26771
+ const openIdx = m.index + m[0].length - 1;
26772
+ const args2 = extractBalanced(line, openIdx);
26773
+ if (args2 === null || args2.length === 0)
26774
+ continue;
26775
+ let tainted = reqExtractRe.test(args2);
26776
+ if (!tainted) {
26777
+ for (const v of taintedVars) {
26778
+ if (new RegExp(`\\b${v}\\b`).test(args2)) {
26779
+ tainted = true;
26780
+ break;
26781
+ }
26782
+ }
26783
+ }
26784
+ if (!tainted)
26785
+ continue;
26786
+ findings.push({
26787
+ id: `nosql_injection-${file}-${i2 + 1}-go-mongo-${op.toLowerCase()}`,
26788
+ pass: "language-sources",
26789
+ category: "security",
26790
+ rule_id: "nosql_injection",
26791
+ cwe: "CWE-943",
26792
+ severity: "critical",
26793
+ level: "error",
26794
+ message: `NoSQL injection: Go MongoDB driver \`${op}(...)\` called with a ` + "filter derived from *http.Request input. Untrusted values inside " + "a `bson.M` / `bson.D` filter can be operator objects (e.g. " + '`{"$ne": null}`) and bypass authentication/intent. Validate or ' + "coerce the value to a primitive before building the filter.",
26795
+ file,
26796
+ line: i2 + 1,
26797
+ snippet: line.trim()
26798
+ });
26799
+ }
26800
+ }
26801
+ return findings;
26802
+ }
26803
+ function findJavaMongoNosqlInjectionFindings(code, file) {
26804
+ const findings = [];
26805
+ if (typeof code !== "string" || code.length === 0)
26806
+ return findings;
26807
+ if (!/(MongoCollection\b|com\.mongodb\b|\bFilters\b|\bnew\s+Document\s*\()/.test(code)) {
26808
+ return findings;
26809
+ }
26810
+ const lines = code.split(`
26811
+ `);
26812
+ const reqExtractRe = /\b\w+\s*\.\s*(?:getParameter|getParameterValues|getHeader|getHeaders|getCookies|getReader|getQueryString|getRequestURI|getInputStream|getPart|getParts)\s*\(/;
26813
+ const opsAlt = "(?:find|findOne|findOneAndUpdate|findOneAndDelete|findOneAndReplace|insertOne|insertMany|updateOne|updateMany|deleteOne|deleteMany|replaceOne|aggregate|countDocuments|distinct)";
26814
+ const callRe = new RegExp(`\\.\\s*(${opsAlt})\\s*\\(([\\s\\S]*?)\\)`);
26815
+ const taintedVars = new Set;
26816
+ for (let pass = 0;pass < 3; pass++) {
26817
+ const before = taintedVars.size;
26818
+ for (let i2 = 0;i2 < lines.length; i2++) {
26819
+ const t = lines[i2].trim();
26820
+ const a = t.match(/^(?:final\s+)?(?:[\w<>?,\[\]]+\s+)?(\w+)\s*=\s*(.+?);?$/);
26821
+ if (!a)
26822
+ continue;
26823
+ const lhs = a[1];
26824
+ const rhs = a[2];
26825
+ if (taintedVars.has(lhs))
26826
+ continue;
26827
+ if (reqExtractRe.test(rhs)) {
26828
+ taintedVars.add(lhs);
26829
+ continue;
26830
+ }
26831
+ for (const v of taintedVars) {
26832
+ if (new RegExp(`\\b${v}\\b`).test(rhs)) {
26833
+ taintedVars.add(lhs);
26834
+ break;
26835
+ }
26836
+ }
26837
+ }
26838
+ if (taintedVars.size === before)
26839
+ break;
26840
+ }
26841
+ if (taintedVars.size === 0)
26842
+ return findings;
26843
+ for (let i2 = 0;i2 < lines.length; i2++) {
26844
+ const line = lines[i2];
26845
+ const m = line.match(callRe);
26846
+ if (!m)
26847
+ continue;
26848
+ const op = m[1];
26849
+ const args2 = m[2];
26850
+ if (args2.trim().length === 0)
26851
+ continue;
26852
+ let tainted = reqExtractRe.test(args2);
26853
+ if (!tainted) {
26854
+ for (const v of taintedVars) {
26855
+ if (new RegExp(`\\b${v}\\b`).test(args2)) {
26856
+ tainted = true;
26857
+ break;
26858
+ }
26859
+ }
26860
+ }
26861
+ if (!tainted)
26862
+ continue;
26863
+ findings.push({
26864
+ id: `nosql_injection-${file}-${i2 + 1}-java-mongo-${op.toLowerCase()}`,
26865
+ pass: "language-sources",
26866
+ category: "security",
26867
+ rule_id: "nosql_injection",
26868
+ cwe: "CWE-943",
26869
+ severity: "critical",
26870
+ level: "error",
26871
+ message: `NoSQL injection: Java Mongo driver \`${op}(...)\` called with a ` + "filter derived from servlet request input. Untrusted values can " + "reach BSON operator positions and bypass intent. Validate the " + "input type before constructing the filter (e.g. require String).",
26872
+ file,
26873
+ line: i2 + 1,
26874
+ snippet: line.trim()
26875
+ });
26876
+ }
26877
+ return findings;
26878
+ }
26879
+ function findPythonMongoengineWhereNosqlInjectionFindings(code, file) {
26880
+ const findings = [];
26881
+ if (typeof code !== "string" || code.length === 0)
26882
+ return findings;
26883
+ if (!/['"]\$where['"]/.test(code))
26884
+ return findings;
26885
+ const lines = code.split(`
26886
+ `);
26887
+ const reqExtractRe = /\b(?:request\.(?:args|form|values|json|files|cookies|headers|data)\b|flask\.request\b)/;
26888
+ const taintedVars = new Set;
26889
+ for (let pass = 0;pass < 3; pass++) {
26890
+ const before = taintedVars.size;
26891
+ for (let i2 = 0;i2 < lines.length; i2++) {
26892
+ const t = lines[i2].trim();
26893
+ if (t.startsWith("#"))
26894
+ continue;
26895
+ const a = t.match(/^(\w+)\s*=\s*(.+?)$/);
26896
+ if (!a)
26897
+ continue;
26898
+ const lhs = a[1];
26899
+ const rhs = a[2];
26900
+ if (taintedVars.has(lhs))
26901
+ continue;
26902
+ if (reqExtractRe.test(rhs)) {
26903
+ taintedVars.add(lhs);
26904
+ continue;
26905
+ }
26906
+ for (const v of taintedVars) {
26907
+ if (new RegExp(`\\b${v}\\b`).test(rhs)) {
26908
+ taintedVars.add(lhs);
26909
+ break;
26910
+ }
26911
+ }
26912
+ }
26913
+ if (taintedVars.size === before)
26914
+ break;
26915
+ }
26916
+ const whereRe = /['"]\$where['"]\s*:\s*([^,}\n]+)/;
26917
+ for (let i2 = 0;i2 < lines.length; i2++) {
26918
+ const line = lines[i2];
26919
+ const m = line.match(whereRe);
26920
+ if (!m)
26921
+ continue;
26922
+ const valueExpr = m[1].trim();
26923
+ if (/^"[^"]*"$/.test(valueExpr) || /^'[^']*'$/.test(valueExpr))
26924
+ continue;
26925
+ let tainted = reqExtractRe.test(valueExpr);
26926
+ if (!tainted) {
26927
+ for (const v of taintedVars) {
26928
+ if (new RegExp(`\\b${v}\\b`).test(valueExpr)) {
26929
+ tainted = true;
26930
+ break;
26931
+ }
26932
+ }
26933
+ }
26934
+ if (!tainted)
26935
+ continue;
26936
+ findings.push({
26937
+ id: `nosql_injection-${file}-${i2 + 1}-py-mongoengine-where`,
26938
+ pass: "language-sources",
26939
+ category: "security",
26940
+ rule_id: "nosql_injection",
26941
+ cwe: "CWE-943",
26942
+ severity: "critical",
26943
+ level: "error",
26944
+ message: 'NoSQL injection: mongoengine `__raw__={"$where": ...}` payload ' + "derived from HTTP request input. The `$where` operator evaluates " + "JavaScript on the server; tainted string concatenation lets an " + "attacker inject arbitrary JS. Replace `$where` with field-based " + "operators or validate the input.",
26945
+ file,
26946
+ line: i2 + 1,
26947
+ snippet: line.trim()
26948
+ });
26949
+ }
26950
+ return findings;
26951
+ }
26683
26952
 
26684
26953
  // ../circle-ir/dist/analysis/passes/sink-filter-pass.js
26685
26954
  var JS_XSS_SANITIZERS = [
@@ -39807,7 +40076,7 @@ var colors = {
39807
40076
  };
39808
40077
 
39809
40078
  // src/version.ts
39810
- var version = "3.133.0";
40079
+ var version = "3.134.0";
39811
40080
 
39812
40081
  // src/formatters.ts
39813
40082
  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": "3.133.0",
3
+ "version": "3.134.0",
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": "^3.133.0"
69
+ "circle-ir": "^3.134.0"
70
70
  },
71
71
  "devDependencies": {
72
72
  "@types/node": "^25.5.0",