circle-ir 4.7.0 → 4.7.1
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/analysis/config-loader.d.ts.map +1 -1
- package/dist/analysis/config-loader.js +5 -2
- package/dist/analysis/config-loader.js.map +1 -1
- package/dist/analysis/interprocedural.d.ts.map +1 -1
- package/dist/analysis/interprocedural.js +16 -1
- package/dist/analysis/interprocedural.js.map +1 -1
- package/dist/analysis/passes/language-sources-pass.d.ts.map +1 -1
- package/dist/analysis/passes/language-sources-pass.js +42 -2
- package/dist/analysis/passes/language-sources-pass.js.map +1 -1
- package/dist/analysis/passes/unrestricted-file-upload-pass.d.ts.map +1 -1
- package/dist/analysis/passes/unrestricted-file-upload-pass.js +6 -4
- package/dist/analysis/passes/unrestricted-file-upload-pass.js.map +1 -1
- package/dist/analysis/passes/unused-variable-pass.d.ts.map +1 -1
- package/dist/analysis/passes/unused-variable-pass.js +69 -20
- package/dist/analysis/passes/unused-variable-pass.js.map +1 -1
- package/dist/browser/circle-ir.js +92 -22
- package/dist/core/circle-ir-core.cjs +9 -6
- package/dist/core/circle-ir-core.js +9 -6
- package/dist/core/extractors/dfg.js +6 -3
- package/dist/core/extractors/dfg.js.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"unrestricted-file-upload-pass.d.ts","sourceRoot":"","sources":["../../../src/analysis/passes/unrestricted-file-upload-pass.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAC;AAG9E,MAAM,WAAW,4BAA4B;IAC3C,QAAQ,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CAClE;
|
|
1
|
+
{"version":3,"file":"unrestricted-file-upload-pass.d.ts","sourceRoot":"","sources":["../../../src/analysis/passes/unrestricted-file-upload-pass.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAC;AAG9E,MAAM,WAAW,4BAA4B;IAC3C,QAAQ,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CAClE;AA4BD,qBAAa,0BAA2B,YAAW,YAAY,CAAC,4BAA4B,CAAC;IAC3F,QAAQ,CAAC,IAAI,8BAA8B;IAC3C,QAAQ,CAAC,QAAQ,EAAG,UAAU,CAAU;IAExC,GAAG,CAAC,GAAG,EAAE,WAAW,GAAG,4BAA4B;IA0InD,OAAO,CAAC,IAAI;CAgCb"}
|
|
@@ -34,8 +34,9 @@
|
|
|
34
34
|
const UPLOAD_NAME_RE = /(?:getOriginalFilename|getSubmittedFileName|originalname|originalName|\.filename|\.Filename|FileHeader\.Filename|UploadFile)/;
|
|
35
35
|
/** Identifier-level allow-list / canonicalization calls that defang upload paths. */
|
|
36
36
|
const FILE_SAFE_CALL_RE = /(?:secure_filename|FilenameUtils\.getExtension|\.lastIndexOf\(['"]\.['"]\)|ALLOWED_EXT|ALLOWED_EXTENSIONS|allowedExtensions|\bfileFilter\b|filepath\.Ext|path\.extname)/;
|
|
37
|
-
|
|
38
|
-
|
|
37
|
+
// Takes the pre-split source lines so the whole file is not re-split on every
|
|
38
|
+
// call (was O(methods · lines) ≈ O(n²) on large files — cognium-ai#305).
|
|
39
|
+
function lineWindow(lines, startLine, endLine) {
|
|
39
40
|
const s = Math.max(0, startLine - 1);
|
|
40
41
|
const e = Math.min(lines.length, endLine);
|
|
41
42
|
return lines.slice(s, e).join('\n');
|
|
@@ -58,13 +59,14 @@ export class UnrestrictedFileUploadPass {
|
|
|
58
59
|
const { graph, language, code } = ctx;
|
|
59
60
|
const file = graph.ir.meta.file;
|
|
60
61
|
const findings = [];
|
|
62
|
+
const codeLines = code.split('\n'); // split once; reused by every lineWindow
|
|
61
63
|
// Build per-function safety windows: a function is "safe" if its body
|
|
62
64
|
// contains any FILE_SAFE_CALL_RE marker. Used to suppress findings inside
|
|
63
65
|
// that scope.
|
|
64
66
|
const safeFunctionRanges = [];
|
|
65
67
|
for (const t of graph.ir.types) {
|
|
66
68
|
for (const m of t.methods) {
|
|
67
|
-
const body = lineWindow(
|
|
69
|
+
const body = lineWindow(codeLines, m.start_line, m.end_line);
|
|
68
70
|
if (FILE_SAFE_CALL_RE.test(body)) {
|
|
69
71
|
safeFunctionRanges.push({ start: m.start_line, end: m.end_line });
|
|
70
72
|
}
|
|
@@ -76,7 +78,7 @@ export class UnrestrictedFileUploadPass {
|
|
|
76
78
|
return true;
|
|
77
79
|
}
|
|
78
80
|
// No method information — fall back to a ±20-line window around the call.
|
|
79
|
-
const win = lineWindow(
|
|
81
|
+
const win = lineWindow(codeLines, Math.max(1, line - 20), line + 5);
|
|
80
82
|
return FILE_SAFE_CALL_RE.test(win);
|
|
81
83
|
};
|
|
82
84
|
// --- Per-language detection -----------------------------------------------
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"unrestricted-file-upload-pass.js","sourceRoot":"","sources":["../../../src/analysis/passes/unrestricted-file-upload-pass.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AASH,qEAAqE;AACrE,MAAM,cAAc,GAClB,8HAA8H,CAAC;AAEjI,qFAAqF;AACrF,MAAM,iBAAiB,GACrB,yKAAyK,CAAC;AAE5K,SAAS,UAAU,CAAC,
|
|
1
|
+
{"version":3,"file":"unrestricted-file-upload-pass.js","sourceRoot":"","sources":["../../../src/analysis/passes/unrestricted-file-upload-pass.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"unused-variable-pass.d.ts","sourceRoot":"","sources":["../../../src/analysis/passes/unused-variable-pass.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAC;
|
|
1
|
+
{"version":3,"file":"unused-variable-pass.d.ts","sourceRoot":"","sources":["../../../src/analysis/passes/unused-variable-pass.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAC;AAgB9E,MAAM,WAAW,oBAAoB;IACnC,UAAU,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACvD;AAED,qBAAa,kBAAmB,YAAW,YAAY,CAAC,oBAAoB,CAAC;IAC3E,QAAQ,CAAC,IAAI,qBAAqB;IAClC,QAAQ,CAAC,QAAQ,EAAG,aAAa,CAAU;IAE3C,GAAG,CAAC,GAAG,EAAE,WAAW,GAAG,oBAAoB;CAyJ5C"}
|
|
@@ -32,6 +32,8 @@ const SKIP_NAMES = new Set([
|
|
|
32
32
|
'boolean', 'string', 'number', 'object', 'symbol', 'undefined',
|
|
33
33
|
'null', 'never', 'void', 'any', 'unknown', 'bigint',
|
|
34
34
|
]);
|
|
35
|
+
/** Shared empty line-index for names absent from the source text. */
|
|
36
|
+
const EMPTY_LINES = [];
|
|
35
37
|
export class UnusedVariablePass {
|
|
36
38
|
name = 'unused-variable';
|
|
37
39
|
category = 'reliability';
|
|
@@ -45,6 +47,43 @@ export class UnusedVariablePass {
|
|
|
45
47
|
const codeLines = code.split('\n');
|
|
46
48
|
const unusedVars = [];
|
|
47
49
|
const reported = new Set(); // deduplicate by variable+line
|
|
50
|
+
// Precompute two indexes ONCE so the per-def fallback is not quadratic on
|
|
51
|
+
// large files (cognium-ai#305): grouping defs by variable avoids an O(defs)
|
|
52
|
+
// rescan per candidate, and a name→line-numbers map avoids an O(lines)
|
|
53
|
+
// text scan per candidate. Both replace the previous O(defs²)+O(defs·lines)
|
|
54
|
+
// work — the dominant cost on files where the DFG surfaces few uses.
|
|
55
|
+
const defsByVar = new Map();
|
|
56
|
+
for (const d of graph.ir.dfg.defs) {
|
|
57
|
+
const arr = defsByVar.get(d.variable);
|
|
58
|
+
if (arr)
|
|
59
|
+
arr.push(d);
|
|
60
|
+
else
|
|
61
|
+
defsByVar.set(d.variable, [d]);
|
|
62
|
+
}
|
|
63
|
+
// 1-indexed line numbers on which each identifier token appears (deduped
|
|
64
|
+
// per line). Equivalent to a `\bname\b` scan for identifier names; names
|
|
65
|
+
// containing non-identifier chars (dotted paths, `$`) fall back to regex.
|
|
66
|
+
const nameLines = new Map();
|
|
67
|
+
const idRe = /[A-Za-z_][A-Za-z0-9_]*/g;
|
|
68
|
+
for (let i = 0; i < codeLines.length; i++) {
|
|
69
|
+
const line = codeLines[i];
|
|
70
|
+
idRe.lastIndex = 0;
|
|
71
|
+
let seen = null;
|
|
72
|
+
let m;
|
|
73
|
+
while ((m = idRe.exec(line)) !== null) {
|
|
74
|
+
const w = m[0];
|
|
75
|
+
if (seen === null)
|
|
76
|
+
seen = new Set();
|
|
77
|
+
if (seen.has(w))
|
|
78
|
+
continue;
|
|
79
|
+
seen.add(w);
|
|
80
|
+
const arr = nameLines.get(w);
|
|
81
|
+
if (arr)
|
|
82
|
+
arr.push(i + 1);
|
|
83
|
+
else
|
|
84
|
+
nameLines.set(w, [i + 1]);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
48
87
|
for (const def of graph.ir.dfg.defs) {
|
|
49
88
|
if (def.kind !== 'local')
|
|
50
89
|
continue;
|
|
@@ -70,13 +109,19 @@ export class UnusedVariablePass {
|
|
|
70
109
|
// module-level constants referenced inside a class method body, or
|
|
71
110
|
// conditional reassignments where the DFG links the final read only to
|
|
72
111
|
// the last def so earlier defs appear unused).
|
|
73
|
-
const otherDefs =
|
|
112
|
+
const otherDefs = (defsByVar.get(variable) ?? []).filter(d => d.id !== def.id);
|
|
74
113
|
const otherDefLines = new Set(otherDefs.map(d => d.line));
|
|
75
|
-
|
|
76
|
-
|
|
114
|
+
// Plain identifier names use the precomputed index; others fall back to a
|
|
115
|
+
// per-line regex (rare — dotted paths / `$`).
|
|
116
|
+
const plainName = /^[A-Za-z_][A-Za-z0-9_]*$/.test(variable);
|
|
117
|
+
const idxLines = plainName ? (nameLines.get(variable) ?? EMPTY_LINES) : null;
|
|
118
|
+
const namePattern = plainName ? null
|
|
119
|
+
: new RegExp(`\\b${variable.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}\\b`);
|
|
77
120
|
if (otherDefLines.size === 0) {
|
|
78
|
-
// Single def —
|
|
79
|
-
const usedElsewhere =
|
|
121
|
+
// Single def — any occurrence on another line suppresses.
|
|
122
|
+
const usedElsewhere = idxLines !== null
|
|
123
|
+
? idxLines.some(ln => ln !== def.line)
|
|
124
|
+
: codeLines.some((line, idx) => idx !== def.line - 1 && namePattern.test(line));
|
|
80
125
|
if (usedElsewhere)
|
|
81
126
|
continue;
|
|
82
127
|
}
|
|
@@ -99,26 +144,30 @@ export class UnusedVariablePass {
|
|
|
99
144
|
|| /\b[A-Z]\w*(?:<[^>]*>)?\s+\w/.test(lineText)
|
|
100
145
|
|| /\blet\s+(?:mut\s+)?\w/.test(lineText);
|
|
101
146
|
if (isTrueDeclaration) {
|
|
102
|
-
// Case A: suppress only if use appears before any intermediate def
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
147
|
+
// Case A: suppress only if a use appears before any intermediate def
|
|
148
|
+
// (an intermediate def would mean the value was overwritten first).
|
|
149
|
+
const usedBeforeNextDef = idxLines !== null
|
|
150
|
+
? idxLines.some(ln => ln !== def.line && !otherDefLines.has(ln) &&
|
|
151
|
+
!otherDefs.some(d => d.line > def.line && d.line < ln))
|
|
152
|
+
: codeLines.some((line, idx) => {
|
|
153
|
+
const lineNum = idx + 1;
|
|
154
|
+
if (lineNum === def.line || otherDefLines.has(lineNum))
|
|
155
|
+
return false;
|
|
156
|
+
if (!namePattern.test(line))
|
|
157
|
+
return false;
|
|
158
|
+
return !otherDefs.some(d => d.line > def.line && d.line < lineNum);
|
|
159
|
+
});
|
|
113
160
|
if (usedBeforeNextDef)
|
|
114
161
|
continue;
|
|
115
162
|
}
|
|
116
163
|
else {
|
|
117
164
|
// Case B: bare reassignment — suppress if used on any non-def line.
|
|
118
|
-
const usedOnNonDefLine =
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
165
|
+
const usedOnNonDefLine = idxLines !== null
|
|
166
|
+
? idxLines.some(ln => ln !== def.line && !otherDefLines.has(ln))
|
|
167
|
+
: codeLines.some((line, idx) => {
|
|
168
|
+
const lineNum = idx + 1;
|
|
169
|
+
return lineNum !== def.line && !otherDefLines.has(lineNum) && namePattern.test(line);
|
|
170
|
+
});
|
|
122
171
|
if (usedOnNonDefLine)
|
|
123
172
|
continue;
|
|
124
173
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"unused-variable-pass.js","sourceRoot":"","sources":["../../../src/analysis/passes/unused-variable-pass.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAIH,mEAAmE;AACnE,MAAM,UAAU,GAAG,IAAI,GAAG,CAAC;IACzB,GAAG,EAAE,QAAQ;IACb,GAAG,EAAE,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,WAAW;IACtC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,KAAK,EAAE,OAAO;IAClC,0EAA0E;IAC1E,uEAAuE;IACvE,SAAS,EAAE,QAAQ,EAAE,QAAQ,EAAE,QAAQ,EAAE,QAAQ,EAAE,WAAW;IAC9D,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,EAAE,QAAQ;CACpD,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"unused-variable-pass.js","sourceRoot":"","sources":["../../../src/analysis/passes/unused-variable-pass.ts"],"names":[],"mappings":"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"}
|
|
@@ -9918,15 +9918,15 @@ function computeChains(defs, uses) {
|
|
|
9918
9918
|
existing.push(use);
|
|
9919
9919
|
usesByLine.set(use.line, existing);
|
|
9920
9920
|
}
|
|
9921
|
+
const seenChains = /* @__PURE__ */ new Set();
|
|
9921
9922
|
for (const def of defs) {
|
|
9922
9923
|
if (def.kind === "local") {
|
|
9923
9924
|
const usesOnLine = usesByLine.get(def.line) ?? [];
|
|
9924
9925
|
for (const use of usesOnLine) {
|
|
9925
9926
|
if (use.def_id !== null && use.def_id !== def.id) {
|
|
9926
|
-
const
|
|
9927
|
-
|
|
9928
|
-
|
|
9929
|
-
if (!exists) {
|
|
9927
|
+
const key = `${use.def_id}\0${def.id}\0${use.variable}`;
|
|
9928
|
+
if (!seenChains.has(key)) {
|
|
9929
|
+
seenChains.add(key);
|
|
9930
9930
|
chains.push({
|
|
9931
9931
|
from_def: use.def_id,
|
|
9932
9932
|
to_def: def.id,
|
|
@@ -12501,8 +12501,11 @@ var DEFAULT_SINKS = [
|
|
|
12501
12501
|
// SAX handler sinks (can lead to XSS in parsed content)
|
|
12502
12502
|
{ method: "startElement", class: "ContentHandler", type: "xss", cwe: "CWE-79", severity: "high", arg_positions: [0, 1, 2] },
|
|
12503
12503
|
{ method: "characters", class: "ContentHandler", type: "xss", cwe: "CWE-79", severity: "high", arg_positions: [0] },
|
|
12504
|
-
// Template output sinks
|
|
12505
|
-
|
|
12504
|
+
// Template output sinks. `output` is classless (Java template engines /
|
|
12505
|
+
// Velocity), but Rust `std::process::Command::…output()` is process
|
|
12506
|
+
// execution, not HTML — it already fires the CWE-78 command_injection sink,
|
|
12507
|
+
// so exclude Rust here to drop the spurious xss (cognium-dev#146).
|
|
12508
|
+
{ method: "output", type: "xss", cwe: "CWE-79", severity: "high", arg_positions: [0], exclude_languages: ["rust"] },
|
|
12506
12509
|
{ method: "setOutput", type: "xss", cwe: "CWE-79", severity: "high", arg_positions: [0] },
|
|
12507
12510
|
{ method: "writeAttribute", type: "xss", cwe: "CWE-79", severity: "high", arg_positions: [0, 1] },
|
|
12508
12511
|
// AntiSamy specific (SAX filters)
|
|
@@ -18581,6 +18584,26 @@ function analyzeInterprocedural(graphOrTypes, callsOrSources, dfgOrSinks, source
|
|
|
18581
18584
|
"Command",
|
|
18582
18585
|
"CommandContext"
|
|
18583
18586
|
]);
|
|
18587
|
+
const ormQueryBuilderMethods = /* @__PURE__ */ new Set([
|
|
18588
|
+
"findMany",
|
|
18589
|
+
"findFirst",
|
|
18590
|
+
"findUnique",
|
|
18591
|
+
"findUniqueOrThrow",
|
|
18592
|
+
"findFirstOrThrow",
|
|
18593
|
+
"findOne",
|
|
18594
|
+
"findAll",
|
|
18595
|
+
"findByPk",
|
|
18596
|
+
"findAndCountAll",
|
|
18597
|
+
"findBy",
|
|
18598
|
+
"findOneBy",
|
|
18599
|
+
"where",
|
|
18600
|
+
"andWhere",
|
|
18601
|
+
"orWhere",
|
|
18602
|
+
"whereIn",
|
|
18603
|
+
"whereNot",
|
|
18604
|
+
"first",
|
|
18605
|
+
"bind"
|
|
18606
|
+
]);
|
|
18584
18607
|
const sanitizerMethods = /* @__PURE__ */ new Set();
|
|
18585
18608
|
for (const san of sanitizers) {
|
|
18586
18609
|
sanitizerMethods.add(san.method);
|
|
@@ -18605,7 +18628,7 @@ function analyzeInterprocedural(graphOrTypes, callsOrSources, dfgOrSinks, source
|
|
|
18605
18628
|
}
|
|
18606
18629
|
const targetMethod = getMethodNode(methodNodes, call.method_name);
|
|
18607
18630
|
if (!targetMethod) {
|
|
18608
|
-
if (taintedArgPositions.length > 0 && !collectionMethods.has(call.method_name) && !sanitizerMethods.has(call.method_name) && !safeUtilityMethods.has(call.method_name)) {
|
|
18631
|
+
if (taintedArgPositions.length > 0 && !collectionMethods.has(call.method_name) && !sanitizerMethods.has(call.method_name) && !safeUtilityMethods.has(call.method_name) && !ormQueryBuilderMethods.has(call.method_name)) {
|
|
18609
18632
|
const isBash = graph.ir.meta.language === "bash";
|
|
18610
18633
|
const bashSafeBuiltins = /* @__PURE__ */ new Set([
|
|
18611
18634
|
"echo",
|
|
@@ -26394,6 +26417,7 @@ var LanguageSourcesPass = class {
|
|
|
26394
26417
|
additionalSources.push(...findSetterChainSources(types, code, language));
|
|
26395
26418
|
additionalSources.push(...findJavaScriptAssignmentSources(code, language));
|
|
26396
26419
|
additionalSources.push(...findCSharpRequestSources(code, language));
|
|
26420
|
+
additionalSources.push(...findGoArgvSources(code, language));
|
|
26397
26421
|
const jsDOMSinks = findJavaScriptDOMSinks(code, language);
|
|
26398
26422
|
for (const s of jsDOMSinks) {
|
|
26399
26423
|
const alreadyExists = additionalSinks.some((x) => x.line === s.line && x.cwe === s.cwe);
|
|
@@ -27048,6 +27072,31 @@ function findSetterChainSources(types, sourceCode, language) {
|
|
|
27048
27072
|
}
|
|
27049
27073
|
return sources;
|
|
27050
27074
|
}
|
|
27075
|
+
function findGoArgvSources(sourceCode, language) {
|
|
27076
|
+
if (language !== "go") return [];
|
|
27077
|
+
const sources = [];
|
|
27078
|
+
const lines = sourceCode.split("\n");
|
|
27079
|
+
const rangeRe = /\bfor\b[^;{]*?,\s*([A-Za-z_]\w*)\s*:?=\s*range\s+os\.Args\b/;
|
|
27080
|
+
const assignRe = /\b([A-Za-z_]\w*)\s*:?=\s*os\.Args\b/;
|
|
27081
|
+
for (let i2 = 0; i2 < lines.length; i2++) {
|
|
27082
|
+
const line = lines[i2];
|
|
27083
|
+
const rm = rangeRe.exec(line);
|
|
27084
|
+
const m = rm ?? assignRe.exec(line);
|
|
27085
|
+
if (!m) continue;
|
|
27086
|
+
const varName = m[1];
|
|
27087
|
+
const lineNumber = i2 + 1;
|
|
27088
|
+
if (sources.some((s) => s.line === lineNumber && s.variable === varName)) continue;
|
|
27089
|
+
sources.push({
|
|
27090
|
+
type: "io_input",
|
|
27091
|
+
location: `${varName} = os.Args`,
|
|
27092
|
+
severity: "high",
|
|
27093
|
+
line: lineNumber,
|
|
27094
|
+
confidence: 1,
|
|
27095
|
+
variable: varName
|
|
27096
|
+
});
|
|
27097
|
+
}
|
|
27098
|
+
return sources;
|
|
27099
|
+
}
|
|
27051
27100
|
function findCSharpRequestSources(sourceCode, language) {
|
|
27052
27101
|
if (language !== "csharp") return [];
|
|
27053
27102
|
const sources = [];
|
|
@@ -27892,7 +27941,7 @@ function findBashTaintSources(sourceCode, dfg) {
|
|
|
27892
27941
|
});
|
|
27893
27942
|
}
|
|
27894
27943
|
}
|
|
27895
|
-
const envRe = /\$([A-Z][A-Z0-9_]{2,})|\$\{([A-Z][A-Z0-9_]{2,})
|
|
27944
|
+
const envRe = /\$([A-Z][A-Z0-9_]{2,})|\$\{([A-Z][A-Z0-9_]{2,})(?:[:#%/^,@!*+?=-][^}]*)?\}/g;
|
|
27896
27945
|
let em;
|
|
27897
27946
|
while ((em = envRe.exec(line)) !== null) {
|
|
27898
27947
|
const envVar = em[1] ?? em[2];
|
|
@@ -38125,6 +38174,7 @@ var SKIP_NAMES3 = /* @__PURE__ */ new Set([
|
|
|
38125
38174
|
"unknown",
|
|
38126
38175
|
"bigint"
|
|
38127
38176
|
]);
|
|
38177
|
+
var EMPTY_LINES = [];
|
|
38128
38178
|
var UnusedVariablePass = class {
|
|
38129
38179
|
name = "unused-variable";
|
|
38130
38180
|
category = "reliability";
|
|
@@ -38137,6 +38187,29 @@ var UnusedVariablePass = class {
|
|
|
38137
38187
|
const codeLines = code.split("\n");
|
|
38138
38188
|
const unusedVars = [];
|
|
38139
38189
|
const reported = /* @__PURE__ */ new Set();
|
|
38190
|
+
const defsByVar = /* @__PURE__ */ new Map();
|
|
38191
|
+
for (const d of graph.ir.dfg.defs) {
|
|
38192
|
+
const arr = defsByVar.get(d.variable);
|
|
38193
|
+
if (arr) arr.push(d);
|
|
38194
|
+
else defsByVar.set(d.variable, [d]);
|
|
38195
|
+
}
|
|
38196
|
+
const nameLines = /* @__PURE__ */ new Map();
|
|
38197
|
+
const idRe = /[A-Za-z_][A-Za-z0-9_]*/g;
|
|
38198
|
+
for (let i2 = 0; i2 < codeLines.length; i2++) {
|
|
38199
|
+
const line = codeLines[i2];
|
|
38200
|
+
idRe.lastIndex = 0;
|
|
38201
|
+
let seen = null;
|
|
38202
|
+
let m;
|
|
38203
|
+
while ((m = idRe.exec(line)) !== null) {
|
|
38204
|
+
const w = m[0];
|
|
38205
|
+
if (seen === null) seen = /* @__PURE__ */ new Set();
|
|
38206
|
+
if (seen.has(w)) continue;
|
|
38207
|
+
seen.add(w);
|
|
38208
|
+
const arr = nameLines.get(w);
|
|
38209
|
+
if (arr) arr.push(i2 + 1);
|
|
38210
|
+
else nameLines.set(w, [i2 + 1]);
|
|
38211
|
+
}
|
|
38212
|
+
}
|
|
38140
38213
|
for (const def of graph.ir.dfg.defs) {
|
|
38141
38214
|
if (def.kind !== "local") continue;
|
|
38142
38215
|
const variable = def.variable;
|
|
@@ -38147,22 +38220,19 @@ var UnusedVariablePass = class {
|
|
|
38147
38220
|
if (/\bexport\b/.test(lineText)) continue;
|
|
38148
38221
|
const uses = graph.usesOfDef(def.id);
|
|
38149
38222
|
if (uses.length > 0) continue;
|
|
38150
|
-
const otherDefs =
|
|
38151
|
-
(d) => d.variable === variable && d.id !== def.id
|
|
38152
|
-
);
|
|
38223
|
+
const otherDefs = (defsByVar.get(variable) ?? []).filter((d) => d.id !== def.id);
|
|
38153
38224
|
const otherDefLines = new Set(otherDefs.map((d) => d.line));
|
|
38154
|
-
const
|
|
38155
|
-
const
|
|
38225
|
+
const plainName = /^[A-Za-z_][A-Za-z0-9_]*$/.test(variable);
|
|
38226
|
+
const idxLines = plainName ? nameLines.get(variable) ?? EMPTY_LINES : null;
|
|
38227
|
+
const namePattern = plainName ? null : new RegExp(`\\b${variable.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}\\b`);
|
|
38156
38228
|
if (otherDefLines.size === 0) {
|
|
38157
|
-
const usedElsewhere = codeLines.some(
|
|
38158
|
-
(line, idx) => idx !== def.line - 1 && namePattern.test(line)
|
|
38159
|
-
);
|
|
38229
|
+
const usedElsewhere = idxLines !== null ? idxLines.some((ln) => ln !== def.line) : codeLines.some((line, idx) => idx !== def.line - 1 && namePattern.test(line));
|
|
38160
38230
|
if (usedElsewhere) continue;
|
|
38161
38231
|
} else {
|
|
38162
38232
|
const lineText2 = codeLines[def.line - 1] ?? "";
|
|
38163
38233
|
const isTrueDeclaration = /\b(?:let|const|var)\s+[\w{[]/.test(lineText2) || /\b(?:int|long|float|double|boolean|byte|char|short|var|final)\b/.test(lineText2) || /\b[A-Z]\w*(?:<[^>]*>)?\s+\w/.test(lineText2) || /\blet\s+(?:mut\s+)?\w/.test(lineText2);
|
|
38164
38234
|
if (isTrueDeclaration) {
|
|
38165
|
-
const usedBeforeNextDef = codeLines.some((line, idx) => {
|
|
38235
|
+
const usedBeforeNextDef = idxLines !== null ? idxLines.some((ln) => ln !== def.line && !otherDefLines.has(ln) && !otherDefs.some((d) => d.line > def.line && d.line < ln)) : codeLines.some((line, idx) => {
|
|
38166
38236
|
const lineNum = idx + 1;
|
|
38167
38237
|
if (lineNum === def.line || otherDefLines.has(lineNum)) return false;
|
|
38168
38238
|
if (!namePattern.test(line)) return false;
|
|
@@ -38170,7 +38240,7 @@ var UnusedVariablePass = class {
|
|
|
38170
38240
|
});
|
|
38171
38241
|
if (usedBeforeNextDef) continue;
|
|
38172
38242
|
} else {
|
|
38173
|
-
const usedOnNonDefLine = codeLines.some((line, idx) => {
|
|
38243
|
+
const usedOnNonDefLine = idxLines !== null ? idxLines.some((ln) => ln !== def.line && !otherDefLines.has(ln)) : codeLines.some((line, idx) => {
|
|
38174
38244
|
const lineNum = idx + 1;
|
|
38175
38245
|
return lineNum !== def.line && !otherDefLines.has(lineNum) && namePattern.test(line);
|
|
38176
38246
|
});
|
|
@@ -42940,8 +43010,7 @@ var InfoDisclosureStacktracePass = class {
|
|
|
42940
43010
|
// src/analysis/passes/unrestricted-file-upload-pass.ts
|
|
42941
43011
|
var UPLOAD_NAME_RE = /(?:getOriginalFilename|getSubmittedFileName|originalname|originalName|\.filename|\.Filename|FileHeader\.Filename|UploadFile)/;
|
|
42942
43012
|
var FILE_SAFE_CALL_RE = /(?:secure_filename|FilenameUtils\.getExtension|\.lastIndexOf\(['"]\.['"]\)|ALLOWED_EXT|ALLOWED_EXTENSIONS|allowedExtensions|\bfileFilter\b|filepath\.Ext|path\.extname)/;
|
|
42943
|
-
function lineWindow(
|
|
42944
|
-
const lines = code.split("\n");
|
|
43013
|
+
function lineWindow(lines, startLine, endLine) {
|
|
42945
43014
|
const s = Math.max(0, startLine - 1);
|
|
42946
43015
|
const e = Math.min(lines.length, endLine);
|
|
42947
43016
|
return lines.slice(s, e).join("\n");
|
|
@@ -42961,10 +43030,11 @@ var UnrestrictedFileUploadPass = class {
|
|
|
42961
43030
|
const { graph, language, code } = ctx;
|
|
42962
43031
|
const file = graph.ir.meta.file;
|
|
42963
43032
|
const findings = [];
|
|
43033
|
+
const codeLines = code.split("\n");
|
|
42964
43034
|
const safeFunctionRanges = [];
|
|
42965
43035
|
for (const t of graph.ir.types) {
|
|
42966
43036
|
for (const m of t.methods) {
|
|
42967
|
-
const body2 = lineWindow(
|
|
43037
|
+
const body2 = lineWindow(codeLines, m.start_line, m.end_line);
|
|
42968
43038
|
if (FILE_SAFE_CALL_RE.test(body2)) {
|
|
42969
43039
|
safeFunctionRanges.push({ start: m.start_line, end: m.end_line });
|
|
42970
43040
|
}
|
|
@@ -42974,7 +43044,7 @@ var UnrestrictedFileUploadPass = class {
|
|
|
42974
43044
|
for (const r of safeFunctionRanges) {
|
|
42975
43045
|
if (line >= r.start && line <= r.end) return true;
|
|
42976
43046
|
}
|
|
42977
|
-
const win = lineWindow(
|
|
43047
|
+
const win = lineWindow(codeLines, Math.max(1, line - 20), line + 5);
|
|
42978
43048
|
return FILE_SAFE_CALL_RE.test(win);
|
|
42979
43049
|
};
|
|
42980
43050
|
if (language === "java") {
|
|
@@ -9964,15 +9964,15 @@ function computeChains(defs, uses) {
|
|
|
9964
9964
|
existing.push(use);
|
|
9965
9965
|
usesByLine.set(use.line, existing);
|
|
9966
9966
|
}
|
|
9967
|
+
const seenChains = /* @__PURE__ */ new Set();
|
|
9967
9968
|
for (const def of defs) {
|
|
9968
9969
|
if (def.kind === "local") {
|
|
9969
9970
|
const usesOnLine = usesByLine.get(def.line) ?? [];
|
|
9970
9971
|
for (const use of usesOnLine) {
|
|
9971
9972
|
if (use.def_id !== null && use.def_id !== def.id) {
|
|
9972
|
-
const
|
|
9973
|
-
|
|
9974
|
-
|
|
9975
|
-
if (!exists) {
|
|
9973
|
+
const key = `${use.def_id}\0${def.id}\0${use.variable}`;
|
|
9974
|
+
if (!seenChains.has(key)) {
|
|
9975
|
+
seenChains.add(key);
|
|
9976
9976
|
chains.push({
|
|
9977
9977
|
from_def: use.def_id,
|
|
9978
9978
|
to_def: def.id,
|
|
@@ -11896,8 +11896,11 @@ var DEFAULT_SINKS = [
|
|
|
11896
11896
|
// SAX handler sinks (can lead to XSS in parsed content)
|
|
11897
11897
|
{ method: "startElement", class: "ContentHandler", type: "xss", cwe: "CWE-79", severity: "high", arg_positions: [0, 1, 2] },
|
|
11898
11898
|
{ method: "characters", class: "ContentHandler", type: "xss", cwe: "CWE-79", severity: "high", arg_positions: [0] },
|
|
11899
|
-
// Template output sinks
|
|
11900
|
-
|
|
11899
|
+
// Template output sinks. `output` is classless (Java template engines /
|
|
11900
|
+
// Velocity), but Rust `std::process::Command::…output()` is process
|
|
11901
|
+
// execution, not HTML — it already fires the CWE-78 command_injection sink,
|
|
11902
|
+
// so exclude Rust here to drop the spurious xss (cognium-dev#146).
|
|
11903
|
+
{ method: "output", type: "xss", cwe: "CWE-79", severity: "high", arg_positions: [0], exclude_languages: ["rust"] },
|
|
11901
11904
|
{ method: "setOutput", type: "xss", cwe: "CWE-79", severity: "high", arg_positions: [0] },
|
|
11902
11905
|
{ method: "writeAttribute", type: "xss", cwe: "CWE-79", severity: "high", arg_positions: [0, 1] },
|
|
11903
11906
|
// AntiSamy specific (SAX filters)
|
|
@@ -9898,15 +9898,15 @@ function computeChains(defs, uses) {
|
|
|
9898
9898
|
existing.push(use);
|
|
9899
9899
|
usesByLine.set(use.line, existing);
|
|
9900
9900
|
}
|
|
9901
|
+
const seenChains = /* @__PURE__ */ new Set();
|
|
9901
9902
|
for (const def of defs) {
|
|
9902
9903
|
if (def.kind === "local") {
|
|
9903
9904
|
const usesOnLine = usesByLine.get(def.line) ?? [];
|
|
9904
9905
|
for (const use of usesOnLine) {
|
|
9905
9906
|
if (use.def_id !== null && use.def_id !== def.id) {
|
|
9906
|
-
const
|
|
9907
|
-
|
|
9908
|
-
|
|
9909
|
-
if (!exists) {
|
|
9907
|
+
const key = `${use.def_id}\0${def.id}\0${use.variable}`;
|
|
9908
|
+
if (!seenChains.has(key)) {
|
|
9909
|
+
seenChains.add(key);
|
|
9910
9910
|
chains.push({
|
|
9911
9911
|
from_def: use.def_id,
|
|
9912
9912
|
to_def: def.id,
|
|
@@ -11830,8 +11830,11 @@ var DEFAULT_SINKS = [
|
|
|
11830
11830
|
// SAX handler sinks (can lead to XSS in parsed content)
|
|
11831
11831
|
{ method: "startElement", class: "ContentHandler", type: "xss", cwe: "CWE-79", severity: "high", arg_positions: [0, 1, 2] },
|
|
11832
11832
|
{ method: "characters", class: "ContentHandler", type: "xss", cwe: "CWE-79", severity: "high", arg_positions: [0] },
|
|
11833
|
-
// Template output sinks
|
|
11834
|
-
|
|
11833
|
+
// Template output sinks. `output` is classless (Java template engines /
|
|
11834
|
+
// Velocity), but Rust `std::process::Command::…output()` is process
|
|
11835
|
+
// execution, not HTML — it already fires the CWE-78 command_injection sink,
|
|
11836
|
+
// so exclude Rust here to drop the spurious xss (cognium-dev#146).
|
|
11837
|
+
{ method: "output", type: "xss", cwe: "CWE-79", severity: "high", arg_positions: [0], exclude_languages: ["rust"] },
|
|
11835
11838
|
{ method: "setOutput", type: "xss", cwe: "CWE-79", severity: "high", arg_positions: [0] },
|
|
11836
11839
|
{ method: "writeAttribute", type: "xss", cwe: "CWE-79", severity: "high", arg_positions: [0, 1] },
|
|
11837
11840
|
// AntiSamy specific (SAX filters)
|
|
@@ -1014,15 +1014,18 @@ function computeChains(defs, uses) {
|
|
|
1014
1014
|
}
|
|
1015
1015
|
// For each definition, find uses on the same line that might be in its initializer
|
|
1016
1016
|
// This is a heuristic - more precise would require tracking def-use relationships in AST
|
|
1017
|
+
// Dedup via a Set keyed on (from_def, to_def, via) — an `Array.some` scan per
|
|
1018
|
+
// insertion was O(chains²) on large files (cognium-ai#305).
|
|
1019
|
+
const seenChains = new Set();
|
|
1017
1020
|
for (const def of defs) {
|
|
1018
1021
|
if (def.kind === 'local') {
|
|
1019
1022
|
const usesOnLine = usesByLine.get(def.line) ?? [];
|
|
1020
1023
|
for (const use of usesOnLine) {
|
|
1021
1024
|
// If this use has a reaching def, create a chain
|
|
1022
1025
|
if (use.def_id !== null && use.def_id !== def.id) {
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
+
const key = `${use.def_id}${def.id}${use.variable}`;
|
|
1027
|
+
if (!seenChains.has(key)) {
|
|
1028
|
+
seenChains.add(key);
|
|
1026
1029
|
chains.push({
|
|
1027
1030
|
from_def: use.def_id,
|
|
1028
1031
|
to_def: def.id,
|