circle-ir 3.206.0 → 3.209.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.
@@ -12187,8 +12187,16 @@ var DEFAULT_SINKS = [
12187
12187
  // node ClientRequest.write, etc.) as xss. Real HTML writers are covered
12188
12188
  // by class-scoped entries: PrintWriter.write (line 843), ServletOutputStream.write
12189
12189
  // (line 849), JspWriter.write (xss.yaml), Response.write (nodejs.json).
12190
- { method: "append", class: "StringBuilder", type: "xss", cwe: "CWE-79", severity: "medium", arg_positions: [0] },
12191
- { method: "append", class: "StringBuffer", type: "xss", cwe: "CWE-79", severity: "medium", arg_positions: [0] },
12190
+ // NOTE (cognium-ai#268): `StringBuilder.append` / `StringBuffer.append` were
12191
+ // registered here as xss sinks, but string construction is NOT an HTML-output
12192
+ // context — the actual sink is wherever the built string is later written to
12193
+ // a response / template (PrintWriter, ServletOutputStream, JspWriter, etc.,
12194
+ // which taint propagation reaches via `sb.toString()`). This was the #1
12195
+ // crit/high FP bucket on the top-100 Java sweep (fires on every append,
12196
+ // incl. constant args). Removed globally: measured 0 OWASP xss true-positive
12197
+ // loss (every xss TP has a real HTML-output sink), and the #244
12198
+ // LibraryProfileXssGatePass already dropped them for `library/*`. Taint still
12199
+ // flows source → StringBuilder → real write sink.
12192
12200
  // Wiki/CMS XSS sinks (JSPWiki, Confluence, etc.)
12193
12201
  { method: "handleHyperlinks", type: "xss", cwe: "CWE-79", severity: "high", arg_positions: [0] },
12194
12202
  { method: "handleDiv", type: "xss", cwe: "CWE-79", severity: "high", arg_positions: [0] },
@@ -32457,6 +32465,8 @@ var SQL_EXEC_METHODS = /* @__PURE__ */ new Set([
32457
32465
  "addBatch"
32458
32466
  ]);
32459
32467
  var JAVA_SQL_ASSIGN_RE_TEMPLATE = "\\b(?:String|CharSequence|final\\s+String|var)\\s+SQLVAR\\b\\s*=\\s*(.+?);";
32468
+ var REPLACE_ALL_TO_PLACEHOLDER_RE = /\.replaceAll\s*\([\s\S]*?,\s*"[\s,?]*\?[\s,?]*"\s*\)/;
32469
+ var HTML_CONTENT_TYPE_RE = /text\/html|TEXT_HTML/;
32460
32470
  var JAVA_INLINE_MATCHES_RE = /\.\s*matches\s*\(\s*"((?:[^"\\]|\\.)*)"\s*\)/g;
32461
32471
  function resolveJavaReceiverType(receiver, sinkLine, sourceLines) {
32462
32472
  if (!receiver || !/^[A-Za-z_]\w*$/.test(receiver)) return null;
@@ -33131,6 +33141,18 @@ var SinkFilterPass = class {
33131
33141
  return true;
33132
33142
  });
33133
33143
  }
33144
+ if (language === "java") {
33145
+ const sourceLines = ctx.code.split("\n");
33146
+ filtered = filtered.filter((sink) => {
33147
+ if (sink.type !== "sql_injection") return true;
33148
+ if (!SQL_EXEC_METHODS.has(sink.method ?? "")) return true;
33149
+ const sinkLineText = sourceLines[sink.line - 1] ?? "";
33150
+ return !REPLACE_ALL_TO_PLACEHOLDER_RE.test(sinkLineText);
33151
+ });
33152
+ }
33153
+ if (language === "java" && !HTML_CONTENT_TYPE_RE.test(ctx.code)) {
33154
+ filtered = filtered.filter((sink) => !(sink.type === "xss" && sink.method === "body"));
33155
+ }
33134
33156
  if (["javascript", "typescript"].includes(language)) {
33135
33157
  const sourceLines = ctx.code.split("\n");
33136
33158
  filtered = filtered.filter((sink) => {
@@ -39911,6 +39933,34 @@ function shannonEntropy(s) {
39911
39933
  return h;
39912
39934
  }
39913
39935
  var CREDENTIAL_NAME_RE = /(?:key|secret|token|password|passwd|credential|api[_-]?key)/i;
39936
+ var CONNECTION_STRING_RE = /\b([a-z][a-z0-9+.-]{1,20}):\/\/([^\s:/@"'`]*):([^\s:/@"'`]{2,})@/i;
39937
+ var PASSWORD_INTERPOLATION_RE = /\$\{|\$\(|\{\{|%\([^)]*\)[sd]|%[sdvfeg]\b|[{}<>]/i;
39938
+ var PLACEHOLDER_PASSWORD_WORDS = /* @__PURE__ */ new Set([
39939
+ "password",
39940
+ "passwd",
39941
+ "pass",
39942
+ "pwd",
39943
+ "secret",
39944
+ "user",
39945
+ "username",
39946
+ "changeme",
39947
+ "example",
39948
+ "yourpassword",
39949
+ "your_password",
39950
+ "test",
39951
+ "xxx",
39952
+ "xxxx"
39953
+ ]);
39954
+ function matchConnectionStringCredential(lineText) {
39955
+ const m = CONNECTION_STRING_RE.exec(lineText);
39956
+ if (!m) return null;
39957
+ const password = m[3];
39958
+ if (/^\d+$/.test(password)) return null;
39959
+ if (PLACEHOLDER_PASSWORD_WORDS.has(password.toLowerCase())) return null;
39960
+ if (PLACEHOLDER_RE.test(password)) return null;
39961
+ if (PASSWORD_INTERPOLATION_RE.test(password)) return null;
39962
+ return { scheme: m[1], match: m[0].substring(0, 60) };
39963
+ }
39914
39964
  function findAnnotationLineRanges(code) {
39915
39965
  const lines = code.split("\n");
39916
39966
  const inAnnotation = /* @__PURE__ */ new Set();
@@ -40106,6 +40156,32 @@ var ScanSecretsPass = class {
40106
40156
  });
40107
40157
  providerFindings += 1;
40108
40158
  }
40159
+ for (let i2 = 0; i2 < lines.length; i2++) {
40160
+ const lineText = lines[i2];
40161
+ const lineNum = i2 + 1;
40162
+ const hit = matchConnectionStringCredential(lineText);
40163
+ if (!hit) continue;
40164
+ const key = `${lineNum}:hardcoded-credential`;
40165
+ if (seen.has(key)) continue;
40166
+ seen.add(key);
40167
+ const dg = applyDemoDowngrade(demoPath, "high", "error");
40168
+ ctx.addFinding({
40169
+ id: `hardcoded-credential-${file}-${lineNum}`,
40170
+ pass: this.name,
40171
+ category: this.category,
40172
+ rule_id: "hardcoded-credential",
40173
+ cwe: "CWE-798",
40174
+ severity: dg.severity,
40175
+ level: dg.level,
40176
+ message: `Hardcoded credential: password embedded in a ${hit.scheme} connection string`,
40177
+ file,
40178
+ line: lineNum,
40179
+ snippet: lineText.trim().substring(0, 120),
40180
+ fix: "Move the credential out of the connection string into an environment variable or secrets manager, and rotate the exposed password.",
40181
+ evidence: { kind: "connection-string", scheme: hit.scheme, match: hit.match }
40182
+ });
40183
+ providerFindings += 1;
40184
+ }
40109
40185
  if (hasEntropyCandidate) for (let i2 = 0; i2 < lines.length; i2++) {
40110
40186
  const lineText = lines[i2];
40111
40187
  const lineNum = i2 + 1;
@@ -11582,8 +11582,16 @@ var DEFAULT_SINKS = [
11582
11582
  // node ClientRequest.write, etc.) as xss. Real HTML writers are covered
11583
11583
  // by class-scoped entries: PrintWriter.write (line 843), ServletOutputStream.write
11584
11584
  // (line 849), JspWriter.write (xss.yaml), Response.write (nodejs.json).
11585
- { method: "append", class: "StringBuilder", type: "xss", cwe: "CWE-79", severity: "medium", arg_positions: [0] },
11586
- { method: "append", class: "StringBuffer", type: "xss", cwe: "CWE-79", severity: "medium", arg_positions: [0] },
11585
+ // NOTE (cognium-ai#268): `StringBuilder.append` / `StringBuffer.append` were
11586
+ // registered here as xss sinks, but string construction is NOT an HTML-output
11587
+ // context — the actual sink is wherever the built string is later written to
11588
+ // a response / template (PrintWriter, ServletOutputStream, JspWriter, etc.,
11589
+ // which taint propagation reaches via `sb.toString()`). This was the #1
11590
+ // crit/high FP bucket on the top-100 Java sweep (fires on every append,
11591
+ // incl. constant args). Removed globally: measured 0 OWASP xss true-positive
11592
+ // loss (every xss TP has a real HTML-output sink), and the #244
11593
+ // LibraryProfileXssGatePass already dropped them for `library/*`. Taint still
11594
+ // flows source → StringBuilder → real write sink.
11587
11595
  // Wiki/CMS XSS sinks (JSPWiki, Confluence, etc.)
11588
11596
  { method: "handleHyperlinks", type: "xss", cwe: "CWE-79", severity: "high", arg_positions: [0] },
11589
11597
  { method: "handleDiv", type: "xss", cwe: "CWE-79", severity: "high", arg_positions: [0] },
@@ -11516,8 +11516,16 @@ var DEFAULT_SINKS = [
11516
11516
  // node ClientRequest.write, etc.) as xss. Real HTML writers are covered
11517
11517
  // by class-scoped entries: PrintWriter.write (line 843), ServletOutputStream.write
11518
11518
  // (line 849), JspWriter.write (xss.yaml), Response.write (nodejs.json).
11519
- { method: "append", class: "StringBuilder", type: "xss", cwe: "CWE-79", severity: "medium", arg_positions: [0] },
11520
- { method: "append", class: "StringBuffer", type: "xss", cwe: "CWE-79", severity: "medium", arg_positions: [0] },
11519
+ // NOTE (cognium-ai#268): `StringBuilder.append` / `StringBuffer.append` were
11520
+ // registered here as xss sinks, but string construction is NOT an HTML-output
11521
+ // context — the actual sink is wherever the built string is later written to
11522
+ // a response / template (PrintWriter, ServletOutputStream, JspWriter, etc.,
11523
+ // which taint propagation reaches via `sb.toString()`). This was the #1
11524
+ // crit/high FP bucket on the top-100 Java sweep (fires on every append,
11525
+ // incl. constant args). Removed globally: measured 0 OWASP xss true-positive
11526
+ // loss (every xss TP has a real HTML-output sink), and the #244
11527
+ // LibraryProfileXssGatePass already dropped them for `library/*`. Taint still
11528
+ // flows source → StringBuilder → real write sink.
11521
11529
  // Wiki/CMS XSS sinks (JSPWiki, Confluence, etc.)
11522
11530
  { method: "handleHyperlinks", type: "xss", cwe: "CWE-79", severity: "high", arg_positions: [0] },
11523
11531
  { method: "handleDiv", type: "xss", cwe: "CWE-79", severity: "high", arg_positions: [0] },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "circle-ir",
3
- "version": "3.206.0",
3
+ "version": "3.209.0",
4
4
  "description": "High-performance Static Application Security Testing (SAST) library for detecting security vulnerabilities through taint analysis",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.js",