circle-ir 3.8.0 → 3.8.3

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.
@@ -9211,9 +9211,7 @@ var DEFAULT_SINKS = [
9211
9211
  { method: "resolveURI", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0] },
9212
9212
  { method: "resolve", class: "SourceResolver", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0] },
9213
9213
  { method: "getSource", class: "SourceResolver", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0] },
9214
- // URL-based resource loading
9215
- { method: "URL", class: "constructor", type: "path_traversal", cwe: "CWE-22", severity: "medium", arg_positions: [0] },
9216
- { method: "openStream", class: "URL", type: "path_traversal", cwe: "CWE-22", severity: "medium", arg_positions: [] },
9214
+ // NOTE: new URL(userInput) is SSRF (CWE-918), not path traversal — see ssrf section below
9217
9215
  // Servlet context resource loading
9218
9216
  { method: "getResource", class: "ServletContext", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0] },
9219
9217
  { method: "getResourceAsStream", class: "ServletContext", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0] },
@@ -9250,8 +9248,7 @@ var DEFAULT_SINKS = [
9250
9248
  { method: "extract", type: "path_traversal", cwe: "CWE-22", severity: "critical", arg_positions: [0, 1] },
9251
9249
  { method: "extractAll", type: "path_traversal", cwe: "CWE-22", severity: "critical", arg_positions: [0, 1] },
9252
9250
  { method: "unjar", type: "path_traversal", cwe: "CWE-22", severity: "critical", arg_positions: [0, 1] },
9253
- // Additional file constructors
9254
- { method: "BufferedReader", class: "constructor", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0] },
9251
+ // Additional file constructors — BufferedReader(Reader) is NOT a path traversal sink; it wraps a Reader, not a file path
9255
9252
  { method: "PrintWriter", class: "constructor", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0] },
9256
9253
  { method: "Scanner", class: "constructor", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0] },
9257
9254
  // Topic/queue names (for message queue systems - can be exploited for path traversal)
@@ -9278,7 +9275,6 @@ var DEFAULT_SINKS = [
9278
9275
  { method: "getPath", class: "BaseFileSystem", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0] },
9279
9276
  { method: "getPathMatcher", class: "BaseFileSystem", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0] },
9280
9277
  { method: "getFileStores", class: "RootedFileSystem", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0] },
9281
- { method: "deleteRecursive", class: "CommonTestSupportUtils", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0] },
9282
9278
  // SftpFileSystemProvider
9283
9279
  { method: "move", class: "SftpFileSystemProvider", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0, 1] },
9284
9280
  { method: "copy", class: "SftpFileSystemProvider", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0, 1] },
@@ -9328,18 +9324,6 @@ var DEFAULT_SINKS = [
9328
9324
  { method: "createPlainAccessConfig", class: "MQClientAPIImpl", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0] },
9329
9325
  // XWiki velocity introspector
9330
9326
  { method: "SecureIntrospector", class: "constructor", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0] },
9331
- // Generic test methods that process paths
9332
- { method: "testLifeCycle", type: "path_traversal", cwe: "CWE-22", severity: "medium", arg_positions: [] },
9333
- { method: "testPathAccess", type: "path_traversal", cwe: "CWE-22", severity: "medium", arg_positions: [] },
9334
- { method: "single", type: "path_traversal", cwe: "CWE-22", severity: "medium", arg_positions: [] },
9335
- { method: "invalidPath", type: "path_traversal", cwe: "CWE-22", severity: "medium", arg_positions: [] },
9336
- { method: "invalidPathWithPreviousDirectoryAllEncoded", type: "path_traversal", cwe: "CWE-22", severity: "medium", arg_positions: [] },
9337
- // Embedded server test methods
9338
- { method: "create", class: "EmbeddedJettyFactoryTest", type: "path_traversal", cwe: "CWE-22", severity: "medium", arg_positions: [] },
9339
- { method: "create_withThreadPool", class: "EmbeddedJettyFactoryTest", type: "path_traversal", cwe: "CWE-22", severity: "medium", arg_positions: [] },
9340
- { method: "create_withNullThreadPool", class: "EmbeddedJettyFactoryTest", type: "path_traversal", cwe: "CWE-22", severity: "medium", arg_positions: [] },
9341
- // Camel file tests
9342
- { method: "testProducerComplexByExpression", type: "path_traversal", cwe: "CWE-22", severity: "medium", arg_positions: [] },
9343
9327
  // XSS (CWE-79)
9344
9328
  { method: "write", class: "PrintWriter", type: "xss", cwe: "CWE-79", severity: "high", arg_positions: [0] },
9345
9329
  { method: "println", class: "PrintWriter", type: "xss", cwe: "CWE-79", severity: "high", arg_positions: [0] },
@@ -9774,9 +9758,12 @@ var DEFAULT_SINKS = [
9774
9758
  { method: "execSync", class: "child_process", type: "command_injection", cwe: "CWE-78", severity: "critical", arg_positions: [0] },
9775
9759
  { method: "spawn", class: "child_process", type: "command_injection", cwe: "CWE-78", severity: "critical", arg_positions: [0] },
9776
9760
  { method: "spawnSync", class: "child_process", type: "command_injection", cwe: "CWE-78", severity: "critical", arg_positions: [0] },
9777
- // Also match without receiver (destructured imports)
9761
+ // Also match without receiver (destructured imports: const { exec } = require('child_process'))
9778
9762
  { method: "exec", type: "command_injection", cwe: "CWE-78", severity: "high", arg_positions: [0] },
9779
9763
  { method: "execSync", type: "command_injection", cwe: "CWE-78", severity: "high", arg_positions: [0] },
9764
+ { method: "spawn", type: "command_injection", cwe: "CWE-78", severity: "high", arg_positions: [0] },
9765
+ { method: "spawnSync", type: "command_injection", cwe: "CWE-78", severity: "high", arg_positions: [0] },
9766
+ { method: "execFile", type: "command_injection", cwe: "CWE-78", severity: "high", arg_positions: [0] },
9780
9767
  // Node.js File System (path traversal)
9781
9768
  { method: "readFile", class: "fs", type: "path_traversal", cwe: "CWE-22", severity: "critical", arg_positions: [0] },
9782
9769
  { method: "readFileSync", class: "fs", type: "path_traversal", cwe: "CWE-22", severity: "critical", arg_positions: [0] },
@@ -9819,7 +9806,9 @@ var DEFAULT_SINKS = [
9819
9806
  { method: "request", class: "axios", type: "ssrf", cwe: "CWE-918", severity: "high", arg_positions: [0] },
9820
9807
  { method: "fetch", type: "ssrf", cwe: "CWE-918", severity: "high", arg_positions: [0] },
9821
9808
  { method: "request", class: "http", type: "ssrf", cwe: "CWE-918", severity: "high", arg_positions: [0] },
9809
+ { method: "get", class: "http", type: "ssrf", cwe: "CWE-918", severity: "high", arg_positions: [0] },
9822
9810
  { method: "request", class: "https", type: "ssrf", cwe: "CWE-918", severity: "high", arg_positions: [0] },
9811
+ { method: "get", class: "https", type: "ssrf", cwe: "CWE-918", severity: "high", arg_positions: [0] },
9823
9812
  // needle library (used in NodeGoat)
9824
9813
  { method: "get", class: "needle", type: "ssrf", cwe: "CWE-918", severity: "high", arg_positions: [0] },
9825
9814
  { method: "post", class: "needle", type: "ssrf", cwe: "CWE-918", severity: "high", arg_positions: [0] },
@@ -11311,7 +11300,20 @@ function analyzeInterprocedural(types, calls, dfg, sources, sinks, sanitizers, o
11311
11300
  "hashCode",
11312
11301
  "equals",
11313
11302
  "clone",
11314
- "clear"
11303
+ "clear",
11304
+ // StringBuilder / StringBuffer / Writer accumulator methods — taint propagates through these
11305
+ // but the CWE-668 sink check should not fire on pure string accumulation
11306
+ "append",
11307
+ "insert",
11308
+ "prepend",
11309
+ "concat",
11310
+ "delete",
11311
+ "deleteCharAt",
11312
+ "replace",
11313
+ "reverse",
11314
+ "write",
11315
+ "writeln",
11316
+ "println"
11315
11317
  ]);
11316
11318
  const safeUtilityMethods = /* @__PURE__ */ new Set([
11317
11319
  // Path validation and normalization
@@ -11342,7 +11344,25 @@ function analyzeInterprocedural(types, calls, dfg, sources, sinks, sanitizers, o
11342
11344
  "validate",
11343
11345
  "validateInput",
11344
11346
  "check",
11345
- "verify"
11347
+ "verify",
11348
+ // I/O stream wrappers — pure decorators that wrap a stream, not security sinks
11349
+ // e.g. new InputStreamReader(proc.getInputStream()) is safe; the underlying stream is the source
11350
+ "InputStreamReader",
11351
+ "OutputStreamWriter",
11352
+ "BufferedInputStream",
11353
+ "BufferedOutputStream",
11354
+ "ByteArrayInputStream",
11355
+ "ByteArrayOutputStream",
11356
+ "DataInputStream",
11357
+ "DataOutputStream",
11358
+ "PushbackInputStream",
11359
+ "SequenceInputStream",
11360
+ "BufferedReader",
11361
+ "BufferedWriter",
11362
+ "PrintStream",
11363
+ "PrintWriter",
11364
+ "ObjectOutputStream"
11365
+ // ObjectInputStream IS a sink (deserialization), keep it out
11346
11366
  ]);
11347
11367
  const sanitizerMethods = /* @__PURE__ */ new Set();
11348
11368
  for (const san of sanitizers) {
@@ -16726,6 +16746,26 @@ function buildPythonTaintedVars(sourceCode) {
16726
16746
  }
16727
16747
  return tainted;
16728
16748
  }
16749
+ function buildJavaScriptTaintedVars(sourceCode, language) {
16750
+ if (!["javascript", "typescript"].includes(language)) return /* @__PURE__ */ new Map();
16751
+ const tainted = /* @__PURE__ */ new Map();
16752
+ const lines = sourceCode.split("\n");
16753
+ for (let i2 = 0; i2 < lines.length; i2++) {
16754
+ const line = lines[i2];
16755
+ const trimmed = line.trimStart();
16756
+ if (trimmed.startsWith("//") || trimmed.startsWith("*")) continue;
16757
+ const assignMatch = line.match(/(?:(?:var|let|const)\s+)?(\w+)\s*=\s*(.+)/);
16758
+ if (!assignMatch) continue;
16759
+ const [, lhs, rhs] = assignMatch;
16760
+ if (["if", "while", "for", "return", "true", "false", "null", "undefined", "case"].includes(lhs)) continue;
16761
+ const isDirectSource = JS_TAINTED_PATTERNS.some((p) => p.pattern.test(rhs));
16762
+ const isTaintedPropagation = tainted.size > 0 && [...tainted.keys()].some((v) => new RegExp(`\\b${v}\\b`).test(rhs));
16763
+ if (isDirectSource || isTaintedPropagation) {
16764
+ tainted.set(lhs, i2 + 1);
16765
+ }
16766
+ }
16767
+ return tainted;
16768
+ }
16729
16769
  function findPythonQuoteSanitizedVars(sourceCode) {
16730
16770
  const sanitized = /* @__PURE__ */ new Set();
16731
16771
  const lines = sourceCode.split("\n");
@@ -16771,6 +16811,25 @@ function findPythonTrustBoundaryViolations(sourceCode, language, taintedVars) {
16771
16811
  }
16772
16812
  return violations;
16773
16813
  }
16814
+ function findPythonReturnXSSSinks(sourceCode, language, taintedVars) {
16815
+ if (language !== "python" || taintedVars.size === 0) return [];
16816
+ const sinks = [];
16817
+ const lines = sourceCode.split("\n");
16818
+ const taintedKeys = [...taintedVars.keys()];
16819
+ for (let i2 = 0; i2 < lines.length; i2++) {
16820
+ const line = lines[i2];
16821
+ if (line.trimStart().startsWith("#")) continue;
16822
+ const returnMatch = line.match(/^\s*(?:return|yield)\s+(.+)$/);
16823
+ if (!returnMatch) continue;
16824
+ const expr = returnMatch[1];
16825
+ const hasTaintedVar = taintedKeys.some((v) => new RegExp(`\\b${v}\\b`).test(expr));
16826
+ if (!hasTaintedVar) continue;
16827
+ const looksLikeHTML = expr.includes("<") || /['"]\s*\+/.test(expr) || /\+\s*['"]/.test(expr) || /f['"][^'"]*\{/.test(expr);
16828
+ if (!looksLikeHTML) continue;
16829
+ sinks.push({ sinkLine: i2 + 1 });
16830
+ }
16831
+ return sinks;
16832
+ }
16774
16833
  function findJavaScriptDOMSinks(sourceCode, language) {
16775
16834
  const sinks = [];
16776
16835
  if (!["javascript", "typescript"].includes(language)) {
@@ -17099,6 +17158,34 @@ async function analyze(code, filePath, language, options = {}) {
17099
17158
  });
17100
17159
  }
17101
17160
  }
17161
+ const pyReturnXSS = findPythonReturnXSSSinks(code, language, pyTaintedVars);
17162
+ for (const r of pyReturnXSS) {
17163
+ const alreadyExists = taint.sinks.some(
17164
+ (s) => s.line === r.sinkLine && s.type === "xss"
17165
+ );
17166
+ if (!alreadyExists) {
17167
+ taint.sinks.push({
17168
+ type: "xss",
17169
+ cwe: "CWE-79",
17170
+ line: r.sinkLine,
17171
+ location: `return HTML with user input at line ${r.sinkLine}`,
17172
+ confidence: 0.9
17173
+ });
17174
+ }
17175
+ }
17176
+ }
17177
+ if (["javascript", "typescript"].includes(language)) {
17178
+ const jsTaintedVars = buildJavaScriptTaintedVars(code, language);
17179
+ if (jsTaintedVars.size > 0) {
17180
+ const jsSourceLines = code.split("\n");
17181
+ taint.sinks = taint.sinks.filter((sink) => {
17182
+ if (sink.type !== "xss") return true;
17183
+ const sinkLineText = jsSourceLines[sink.line - 1] ?? "";
17184
+ if ([...jsTaintedVars.keys()].some((v) => new RegExp(`\\b${v}\\b`).test(sinkLineText))) return true;
17185
+ if (JS_TAINTED_PATTERNS.some((p) => p.pattern.test(sinkLineText))) return true;
17186
+ return false;
17187
+ });
17188
+ }
17102
17189
  }
17103
17190
  if (taint.sources.length > 0 && taint.sinks.length > 0) {
17104
17191
  const propagationResult = propagateTaint(
@@ -17219,6 +17306,7 @@ async function analyze(code, filePath, language, options = {}) {
17219
17306
  }
17220
17307
  );
17221
17308
  for (const sink of interProc.propagatedSinks) {
17309
+ if (sink.type === "external_taint_escape") continue;
17222
17310
  if (!taint.sinks.some((s) => s.line === sink.line)) {
17223
17311
  taint.sinks.push(sink);
17224
17312
  }
@@ -9319,9 +9319,7 @@ var DEFAULT_SINKS = [
9319
9319
  { method: "resolveURI", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0] },
9320
9320
  { method: "resolve", class: "SourceResolver", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0] },
9321
9321
  { method: "getSource", class: "SourceResolver", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0] },
9322
- // URL-based resource loading
9323
- { method: "URL", class: "constructor", type: "path_traversal", cwe: "CWE-22", severity: "medium", arg_positions: [0] },
9324
- { method: "openStream", class: "URL", type: "path_traversal", cwe: "CWE-22", severity: "medium", arg_positions: [] },
9322
+ // NOTE: new URL(userInput) is SSRF (CWE-918), not path traversal — see ssrf section below
9325
9323
  // Servlet context resource loading
9326
9324
  { method: "getResource", class: "ServletContext", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0] },
9327
9325
  { method: "getResourceAsStream", class: "ServletContext", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0] },
@@ -9358,8 +9356,7 @@ var DEFAULT_SINKS = [
9358
9356
  { method: "extract", type: "path_traversal", cwe: "CWE-22", severity: "critical", arg_positions: [0, 1] },
9359
9357
  { method: "extractAll", type: "path_traversal", cwe: "CWE-22", severity: "critical", arg_positions: [0, 1] },
9360
9358
  { method: "unjar", type: "path_traversal", cwe: "CWE-22", severity: "critical", arg_positions: [0, 1] },
9361
- // Additional file constructors
9362
- { method: "BufferedReader", class: "constructor", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0] },
9359
+ // Additional file constructors — BufferedReader(Reader) is NOT a path traversal sink; it wraps a Reader, not a file path
9363
9360
  { method: "PrintWriter", class: "constructor", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0] },
9364
9361
  { method: "Scanner", class: "constructor", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0] },
9365
9362
  // Topic/queue names (for message queue systems - can be exploited for path traversal)
@@ -9386,7 +9383,6 @@ var DEFAULT_SINKS = [
9386
9383
  { method: "getPath", class: "BaseFileSystem", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0] },
9387
9384
  { method: "getPathMatcher", class: "BaseFileSystem", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0] },
9388
9385
  { method: "getFileStores", class: "RootedFileSystem", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0] },
9389
- { method: "deleteRecursive", class: "CommonTestSupportUtils", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0] },
9390
9386
  // SftpFileSystemProvider
9391
9387
  { method: "move", class: "SftpFileSystemProvider", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0, 1] },
9392
9388
  { method: "copy", class: "SftpFileSystemProvider", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0, 1] },
@@ -9436,18 +9432,6 @@ var DEFAULT_SINKS = [
9436
9432
  { method: "createPlainAccessConfig", class: "MQClientAPIImpl", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0] },
9437
9433
  // XWiki velocity introspector
9438
9434
  { method: "SecureIntrospector", class: "constructor", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0] },
9439
- // Generic test methods that process paths
9440
- { method: "testLifeCycle", type: "path_traversal", cwe: "CWE-22", severity: "medium", arg_positions: [] },
9441
- { method: "testPathAccess", type: "path_traversal", cwe: "CWE-22", severity: "medium", arg_positions: [] },
9442
- { method: "single", type: "path_traversal", cwe: "CWE-22", severity: "medium", arg_positions: [] },
9443
- { method: "invalidPath", type: "path_traversal", cwe: "CWE-22", severity: "medium", arg_positions: [] },
9444
- { method: "invalidPathWithPreviousDirectoryAllEncoded", type: "path_traversal", cwe: "CWE-22", severity: "medium", arg_positions: [] },
9445
- // Embedded server test methods
9446
- { method: "create", class: "EmbeddedJettyFactoryTest", type: "path_traversal", cwe: "CWE-22", severity: "medium", arg_positions: [] },
9447
- { method: "create_withThreadPool", class: "EmbeddedJettyFactoryTest", type: "path_traversal", cwe: "CWE-22", severity: "medium", arg_positions: [] },
9448
- { method: "create_withNullThreadPool", class: "EmbeddedJettyFactoryTest", type: "path_traversal", cwe: "CWE-22", severity: "medium", arg_positions: [] },
9449
- // Camel file tests
9450
- { method: "testProducerComplexByExpression", type: "path_traversal", cwe: "CWE-22", severity: "medium", arg_positions: [] },
9451
9435
  // XSS (CWE-79)
9452
9436
  { method: "write", class: "PrintWriter", type: "xss", cwe: "CWE-79", severity: "high", arg_positions: [0] },
9453
9437
  { method: "println", class: "PrintWriter", type: "xss", cwe: "CWE-79", severity: "high", arg_positions: [0] },
@@ -9882,9 +9866,12 @@ var DEFAULT_SINKS = [
9882
9866
  { method: "execSync", class: "child_process", type: "command_injection", cwe: "CWE-78", severity: "critical", arg_positions: [0] },
9883
9867
  { method: "spawn", class: "child_process", type: "command_injection", cwe: "CWE-78", severity: "critical", arg_positions: [0] },
9884
9868
  { method: "spawnSync", class: "child_process", type: "command_injection", cwe: "CWE-78", severity: "critical", arg_positions: [0] },
9885
- // Also match without receiver (destructured imports)
9869
+ // Also match without receiver (destructured imports: const { exec } = require('child_process'))
9886
9870
  { method: "exec", type: "command_injection", cwe: "CWE-78", severity: "high", arg_positions: [0] },
9887
9871
  { method: "execSync", type: "command_injection", cwe: "CWE-78", severity: "high", arg_positions: [0] },
9872
+ { method: "spawn", type: "command_injection", cwe: "CWE-78", severity: "high", arg_positions: [0] },
9873
+ { method: "spawnSync", type: "command_injection", cwe: "CWE-78", severity: "high", arg_positions: [0] },
9874
+ { method: "execFile", type: "command_injection", cwe: "CWE-78", severity: "high", arg_positions: [0] },
9888
9875
  // Node.js File System (path traversal)
9889
9876
  { method: "readFile", class: "fs", type: "path_traversal", cwe: "CWE-22", severity: "critical", arg_positions: [0] },
9890
9877
  { method: "readFileSync", class: "fs", type: "path_traversal", cwe: "CWE-22", severity: "critical", arg_positions: [0] },
@@ -9927,7 +9914,9 @@ var DEFAULT_SINKS = [
9927
9914
  { method: "request", class: "axios", type: "ssrf", cwe: "CWE-918", severity: "high", arg_positions: [0] },
9928
9915
  { method: "fetch", type: "ssrf", cwe: "CWE-918", severity: "high", arg_positions: [0] },
9929
9916
  { method: "request", class: "http", type: "ssrf", cwe: "CWE-918", severity: "high", arg_positions: [0] },
9917
+ { method: "get", class: "http", type: "ssrf", cwe: "CWE-918", severity: "high", arg_positions: [0] },
9930
9918
  { method: "request", class: "https", type: "ssrf", cwe: "CWE-918", severity: "high", arg_positions: [0] },
9919
+ { method: "get", class: "https", type: "ssrf", cwe: "CWE-918", severity: "high", arg_positions: [0] },
9931
9920
  // needle library (used in NodeGoat)
9932
9921
  { method: "get", class: "needle", type: "ssrf", cwe: "CWE-918", severity: "high", arg_positions: [0] },
9933
9922
  { method: "post", class: "needle", type: "ssrf", cwe: "CWE-918", severity: "high", arg_positions: [0] },
@@ -9254,9 +9254,7 @@ var DEFAULT_SINKS = [
9254
9254
  { method: "resolveURI", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0] },
9255
9255
  { method: "resolve", class: "SourceResolver", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0] },
9256
9256
  { method: "getSource", class: "SourceResolver", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0] },
9257
- // URL-based resource loading
9258
- { method: "URL", class: "constructor", type: "path_traversal", cwe: "CWE-22", severity: "medium", arg_positions: [0] },
9259
- { method: "openStream", class: "URL", type: "path_traversal", cwe: "CWE-22", severity: "medium", arg_positions: [] },
9257
+ // NOTE: new URL(userInput) is SSRF (CWE-918), not path traversal — see ssrf section below
9260
9258
  // Servlet context resource loading
9261
9259
  { method: "getResource", class: "ServletContext", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0] },
9262
9260
  { method: "getResourceAsStream", class: "ServletContext", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0] },
@@ -9293,8 +9291,7 @@ var DEFAULT_SINKS = [
9293
9291
  { method: "extract", type: "path_traversal", cwe: "CWE-22", severity: "critical", arg_positions: [0, 1] },
9294
9292
  { method: "extractAll", type: "path_traversal", cwe: "CWE-22", severity: "critical", arg_positions: [0, 1] },
9295
9293
  { method: "unjar", type: "path_traversal", cwe: "CWE-22", severity: "critical", arg_positions: [0, 1] },
9296
- // Additional file constructors
9297
- { method: "BufferedReader", class: "constructor", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0] },
9294
+ // Additional file constructors — BufferedReader(Reader) is NOT a path traversal sink; it wraps a Reader, not a file path
9298
9295
  { method: "PrintWriter", class: "constructor", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0] },
9299
9296
  { method: "Scanner", class: "constructor", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0] },
9300
9297
  // Topic/queue names (for message queue systems - can be exploited for path traversal)
@@ -9321,7 +9318,6 @@ var DEFAULT_SINKS = [
9321
9318
  { method: "getPath", class: "BaseFileSystem", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0] },
9322
9319
  { method: "getPathMatcher", class: "BaseFileSystem", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0] },
9323
9320
  { method: "getFileStores", class: "RootedFileSystem", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0] },
9324
- { method: "deleteRecursive", class: "CommonTestSupportUtils", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0] },
9325
9321
  // SftpFileSystemProvider
9326
9322
  { method: "move", class: "SftpFileSystemProvider", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0, 1] },
9327
9323
  { method: "copy", class: "SftpFileSystemProvider", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0, 1] },
@@ -9371,18 +9367,6 @@ var DEFAULT_SINKS = [
9371
9367
  { method: "createPlainAccessConfig", class: "MQClientAPIImpl", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0] },
9372
9368
  // XWiki velocity introspector
9373
9369
  { method: "SecureIntrospector", class: "constructor", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0] },
9374
- // Generic test methods that process paths
9375
- { method: "testLifeCycle", type: "path_traversal", cwe: "CWE-22", severity: "medium", arg_positions: [] },
9376
- { method: "testPathAccess", type: "path_traversal", cwe: "CWE-22", severity: "medium", arg_positions: [] },
9377
- { method: "single", type: "path_traversal", cwe: "CWE-22", severity: "medium", arg_positions: [] },
9378
- { method: "invalidPath", type: "path_traversal", cwe: "CWE-22", severity: "medium", arg_positions: [] },
9379
- { method: "invalidPathWithPreviousDirectoryAllEncoded", type: "path_traversal", cwe: "CWE-22", severity: "medium", arg_positions: [] },
9380
- // Embedded server test methods
9381
- { method: "create", class: "EmbeddedJettyFactoryTest", type: "path_traversal", cwe: "CWE-22", severity: "medium", arg_positions: [] },
9382
- { method: "create_withThreadPool", class: "EmbeddedJettyFactoryTest", type: "path_traversal", cwe: "CWE-22", severity: "medium", arg_positions: [] },
9383
- { method: "create_withNullThreadPool", class: "EmbeddedJettyFactoryTest", type: "path_traversal", cwe: "CWE-22", severity: "medium", arg_positions: [] },
9384
- // Camel file tests
9385
- { method: "testProducerComplexByExpression", type: "path_traversal", cwe: "CWE-22", severity: "medium", arg_positions: [] },
9386
9370
  // XSS (CWE-79)
9387
9371
  { method: "write", class: "PrintWriter", type: "xss", cwe: "CWE-79", severity: "high", arg_positions: [0] },
9388
9372
  { method: "println", class: "PrintWriter", type: "xss", cwe: "CWE-79", severity: "high", arg_positions: [0] },
@@ -9817,9 +9801,12 @@ var DEFAULT_SINKS = [
9817
9801
  { method: "execSync", class: "child_process", type: "command_injection", cwe: "CWE-78", severity: "critical", arg_positions: [0] },
9818
9802
  { method: "spawn", class: "child_process", type: "command_injection", cwe: "CWE-78", severity: "critical", arg_positions: [0] },
9819
9803
  { method: "spawnSync", class: "child_process", type: "command_injection", cwe: "CWE-78", severity: "critical", arg_positions: [0] },
9820
- // Also match without receiver (destructured imports)
9804
+ // Also match without receiver (destructured imports: const { exec } = require('child_process'))
9821
9805
  { method: "exec", type: "command_injection", cwe: "CWE-78", severity: "high", arg_positions: [0] },
9822
9806
  { method: "execSync", type: "command_injection", cwe: "CWE-78", severity: "high", arg_positions: [0] },
9807
+ { method: "spawn", type: "command_injection", cwe: "CWE-78", severity: "high", arg_positions: [0] },
9808
+ { method: "spawnSync", type: "command_injection", cwe: "CWE-78", severity: "high", arg_positions: [0] },
9809
+ { method: "execFile", type: "command_injection", cwe: "CWE-78", severity: "high", arg_positions: [0] },
9823
9810
  // Node.js File System (path traversal)
9824
9811
  { method: "readFile", class: "fs", type: "path_traversal", cwe: "CWE-22", severity: "critical", arg_positions: [0] },
9825
9812
  { method: "readFileSync", class: "fs", type: "path_traversal", cwe: "CWE-22", severity: "critical", arg_positions: [0] },
@@ -9862,7 +9849,9 @@ var DEFAULT_SINKS = [
9862
9849
  { method: "request", class: "axios", type: "ssrf", cwe: "CWE-918", severity: "high", arg_positions: [0] },
9863
9850
  { method: "fetch", type: "ssrf", cwe: "CWE-918", severity: "high", arg_positions: [0] },
9864
9851
  { method: "request", class: "http", type: "ssrf", cwe: "CWE-918", severity: "high", arg_positions: [0] },
9852
+ { method: "get", class: "http", type: "ssrf", cwe: "CWE-918", severity: "high", arg_positions: [0] },
9865
9853
  { method: "request", class: "https", type: "ssrf", cwe: "CWE-918", severity: "high", arg_positions: [0] },
9854
+ { method: "get", class: "https", type: "ssrf", cwe: "CWE-918", severity: "high", arg_positions: [0] },
9866
9855
  // needle library (used in NodeGoat)
9867
9856
  { method: "get", class: "needle", type: "ssrf", cwe: "CWE-918", severity: "high", arg_positions: [0] },
9868
9857
  { method: "post", class: "needle", type: "ssrf", cwe: "CWE-918", severity: "high", arg_positions: [0] },
@@ -9,7 +9,7 @@
9
9
  * logger.info('Processing file', { file: 'test.java' });
10
10
  * logger.error('Failed to parse', { error: err.message });
11
11
  *
12
- * Injecting a custom logger (e.g. from circle-pack):
12
+ * Injecting a custom logger (e.g. pino):
13
13
  * import pino from 'pino';
14
14
  * import { setLogger } from 'circle-ir';
15
15
  * setLogger(pino({ level: 'debug' }));
@@ -9,7 +9,7 @@
9
9
  * logger.info('Processing file', { file: 'test.java' });
10
10
  * logger.error('Failed to parse', { error: err.message });
11
11
  *
12
- * Injecting a custom logger (e.g. from circle-pack):
12
+ * Injecting a custom logger (e.g. pino):
13
13
  * import pino from 'pino';
14
14
  * import { setLogger } from 'circle-ir';
15
15
  * setLogger(pino({ level: 'debug' }));
Binary file
package/docs/SPEC.md CHANGED
@@ -952,9 +952,9 @@ interface Vulnerability {
952
952
 
953
953
  ---
954
954
 
955
- ## TODO: TypeScript Implementation
955
+ ## Implementation Status
956
956
 
957
- ### Phase 1: Core (Must Have)
957
+ ### Phase 1: Core
958
958
  - [x] Meta extraction
959
959
  - [x] Type extraction (classes, interfaces, methods, fields)
960
960
  - [x] Call extraction (method invocations, arguments)
@@ -965,18 +965,18 @@ interface Vulnerability {
965
965
  - [x] Import extraction
966
966
  - [x] JSON serialization matching spec
967
967
 
968
- ### Phase 2: Enhanced (Should Have)
968
+ ### Phase 2: Enhanced
969
969
  - [x] Export extraction
970
970
  - [x] Call resolution tracking
971
971
  - [x] Sanitizer detection
972
972
  - [x] DFG chains computation
973
973
 
974
- ### Phase 3: LLM Integration (Nice to Have)
975
- - [x] Unresolved section population
976
- - [x] Enriched section from LLM
974
+ ### Phase 3: Extension Points
975
+ - [x] Unresolved section population (static analysis identifies unresolvable patterns)
976
+ - [x] Enriched section schema (optional, populated by analysis consumers)
977
977
  - [x] Finding generation
978
978
 
979
- ### Phase 4: Project-Level (Done)
979
+ ### Phase 4: Project-Level
980
980
  - [x] Cross-file call graph
981
981
  - [x] Type hierarchy
982
982
  - [x] Taint path enumeration
@@ -2,13 +2,17 @@
2
2
  /**
3
3
  * Node.js Example - Circle-IR Core Library
4
4
  *
5
- * This example demonstrates how to use circle-ir-core to:
5
+ * This example demonstrates how to use circle-ir to:
6
6
  * 1. Parse Java code
7
7
  * 2. Extract IR components (types, calls, CFG, DFG)
8
8
  * 3. Detect taint sources and sinks
9
9
  * 4. Find source-to-sink flows
10
10
  *
11
- * Run: npx tsx examples/node-example.ts
11
+ * Setup:
12
+ * npm install circle-ir
13
+ *
14
+ * Run:
15
+ * npx tsx node-example.ts
12
16
  */
13
17
 
14
18
  import {
@@ -25,7 +29,7 @@ import {
25
29
  analyzeConstantPropagation,
26
30
  isFalsePositive,
27
31
  getDefaultConfig,
28
- } from '../src/core-lib.js';
32
+ } from 'circle-ir/core';
29
33
 
30
34
  // Sample vulnerable Java code
31
35
  const vulnerableCode = `
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "circle-ir",
3
- "version": "3.8.0",
3
+ "version": "3.8.3",
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",
@@ -26,7 +26,6 @@
26
26
  "dist",
27
27
  "configs",
28
28
  "examples",
29
- "wasm",
30
29
  "docs/SPEC.md"
31
30
  ],
32
31
  "scripts": {
@@ -54,6 +53,8 @@
54
53
  "typescript",
55
54
  "python",
56
55
  "rust",
56
+ "bash",
57
+ "shell",
57
58
  "tree-sitter",
58
59
  "sql-injection",
59
60
  "xss",
@@ -65,7 +66,7 @@
65
66
  "name": "Cognium Labs",
66
67
  "url": "https://github.com/cogniumhq"
67
68
  },
68
- "license": "ISC",
69
+ "license": "MIT",
69
70
  "repository": {
70
71
  "type": "git",
71
72
  "url": "git+https://github.com/cogniumhq/circle-ir.git"
@@ -76,7 +77,7 @@
76
77
  },
77
78
  "funding": {
78
79
  "type": "github",
79
- "url": "https://github.com/sponsors/cognitim"
80
+ "url": "https://github.com/sponsors/cogniumhq"
80
81
  },
81
82
  "engines": {
82
83
  "node": ">=18.0.0"
@@ -87,21 +88,18 @@
87
88
  "registry": "https://registry.npmjs.org/"
88
89
  },
89
90
  "dependencies": {
90
- "web-tree-sitter": "^0.26.3",
91
+ "web-tree-sitter": "^0.26.7",
91
92
  "yaml": "^2.8.2"
92
93
  },
93
94
  "devDependencies": {
94
- "@types/node": "^25.0.10",
95
- "@types/unzipper": "^0.10.11",
96
- "@vitest/coverage-v8": "^3.0.0",
97
- "esbuild": "^0.27.2",
95
+ "@types/node": "^25.5.0",
96
+ "@vitest/coverage-v8": "^4.1.0",
97
+ "esbuild": "^0.27.4",
98
98
  "tree-sitter-bash": "^0.25.1",
99
99
  "tree-sitter-java": "^0.23.5",
100
100
  "tree-sitter-python": "^0.25.0",
101
101
  "tree-sitter-rust": "^0.24.0",
102
- "ts-node": "^10.9.2",
103
102
  "typescript": "^5.9.3",
104
- "unzipper": "^0.12.3",
105
- "vitest": "^3.0.0"
103
+ "vitest": "^4.1.0"
106
104
  }
107
105
  }
Binary file
Binary file
Binary file
Binary file
Binary file