cognium-dev 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/cli.js +100 -17
- package/package.json +2 -2
package/dist/cli.js
CHANGED
|
@@ -9350,13 +9350,15 @@ function computeChains(defs, uses) {
|
|
|
9350
9350
|
existing.push(use);
|
|
9351
9351
|
usesByLine.set(use.line, existing);
|
|
9352
9352
|
}
|
|
9353
|
+
const seenChains = new Set;
|
|
9353
9354
|
for (const def of defs) {
|
|
9354
9355
|
if (def.kind === "local") {
|
|
9355
9356
|
const usesOnLine = usesByLine.get(def.line) ?? [];
|
|
9356
9357
|
for (const use of usesOnLine) {
|
|
9357
9358
|
if (use.def_id !== null && use.def_id !== def.id) {
|
|
9358
|
-
const
|
|
9359
|
-
if (!
|
|
9359
|
+
const key = `${use.def_id}\x00${def.id}\x00${use.variable}`;
|
|
9360
|
+
if (!seenChains.has(key)) {
|
|
9361
|
+
seenChains.add(key);
|
|
9360
9362
|
chains.push({
|
|
9361
9363
|
from_def: use.def_id,
|
|
9362
9364
|
to_def: def.id,
|
|
@@ -11531,7 +11533,7 @@ var DEFAULT_SINKS = [
|
|
|
11531
11533
|
{ method: "validate", type: "xss", cwe: "CWE-79", severity: "medium", arg_positions: [0] },
|
|
11532
11534
|
{ method: "startElement", class: "ContentHandler", type: "xss", cwe: "CWE-79", severity: "high", arg_positions: [0, 1, 2] },
|
|
11533
11535
|
{ method: "characters", class: "ContentHandler", type: "xss", cwe: "CWE-79", severity: "high", arg_positions: [0] },
|
|
11534
|
-
{ method: "output", type: "xss", cwe: "CWE-79", severity: "high", arg_positions: [0] },
|
|
11536
|
+
{ method: "output", type: "xss", cwe: "CWE-79", severity: "high", arg_positions: [0], exclude_languages: ["rust"] },
|
|
11535
11537
|
{ method: "setOutput", type: "xss", cwe: "CWE-79", severity: "high", arg_positions: [0] },
|
|
11536
11538
|
{ method: "writeAttribute", type: "xss", cwe: "CWE-79", severity: "high", arg_positions: [0, 1] },
|
|
11537
11539
|
{ method: "startElement", class: "MagicSAXFilter", type: "xss", cwe: "CWE-79", severity: "high", arg_positions: [0, 1, 2] },
|
|
@@ -25032,6 +25034,7 @@ class LanguageSourcesPass {
|
|
|
25032
25034
|
additionalSources.push(...findSetterChainSources(types, code, language));
|
|
25033
25035
|
additionalSources.push(...findJavaScriptAssignmentSources(code, language));
|
|
25034
25036
|
additionalSources.push(...findCSharpRequestSources(code, language));
|
|
25037
|
+
additionalSources.push(...findGoArgvSources(code, language));
|
|
25035
25038
|
const jsDOMSinks = findJavaScriptDOMSinks(code, language);
|
|
25036
25039
|
for (const s of jsDOMSinks) {
|
|
25037
25040
|
const alreadyExists = additionalSinks.some((x) => x.line === s.line && x.cwe === s.cwe);
|
|
@@ -25634,6 +25637,35 @@ function findSetterChainSources(types, sourceCode, language) {
|
|
|
25634
25637
|
}
|
|
25635
25638
|
return sources;
|
|
25636
25639
|
}
|
|
25640
|
+
function findGoArgvSources(sourceCode, language) {
|
|
25641
|
+
if (language !== "go")
|
|
25642
|
+
return [];
|
|
25643
|
+
const sources = [];
|
|
25644
|
+
const lines = sourceCode.split(`
|
|
25645
|
+
`);
|
|
25646
|
+
const rangeRe = /\bfor\b[^;{]*?,\s*([A-Za-z_]\w*)\s*:?=\s*range\s+os\.Args\b/;
|
|
25647
|
+
const assignRe = /\b([A-Za-z_]\w*)\s*:?=\s*os\.Args\b/;
|
|
25648
|
+
for (let i2 = 0;i2 < lines.length; i2++) {
|
|
25649
|
+
const line = lines[i2];
|
|
25650
|
+
const rm = rangeRe.exec(line);
|
|
25651
|
+
const m = rm ?? assignRe.exec(line);
|
|
25652
|
+
if (!m)
|
|
25653
|
+
continue;
|
|
25654
|
+
const varName = m[1];
|
|
25655
|
+
const lineNumber = i2 + 1;
|
|
25656
|
+
if (sources.some((s) => s.line === lineNumber && s.variable === varName))
|
|
25657
|
+
continue;
|
|
25658
|
+
sources.push({
|
|
25659
|
+
type: "io_input",
|
|
25660
|
+
location: `${varName} = os.Args`,
|
|
25661
|
+
severity: "high",
|
|
25662
|
+
line: lineNumber,
|
|
25663
|
+
confidence: 1,
|
|
25664
|
+
variable: varName
|
|
25665
|
+
});
|
|
25666
|
+
}
|
|
25667
|
+
return sources;
|
|
25668
|
+
}
|
|
25637
25669
|
function findCSharpRequestSources(sourceCode, language) {
|
|
25638
25670
|
if (language !== "csharp")
|
|
25639
25671
|
return [];
|
|
@@ -26597,7 +26629,7 @@ function findBashTaintSources(sourceCode, dfg) {
|
|
|
26597
26629
|
});
|
|
26598
26630
|
}
|
|
26599
26631
|
}
|
|
26600
|
-
const envRe = /\$([A-Z][A-Z0-9_]{2,})|\$\{([A-Z][A-Z0-9_]{2,})
|
|
26632
|
+
const envRe = /\$([A-Z][A-Z0-9_]{2,})|\$\{([A-Z][A-Z0-9_]{2,})(?:[:#%/^,@!*+?=-][^}]*)?\}/g;
|
|
26601
26633
|
let em;
|
|
26602
26634
|
while ((em = envRe.exec(line)) !== null) {
|
|
26603
26635
|
const envVar = em[1] ?? em[2];
|
|
@@ -36419,6 +36451,26 @@ function analyzeInterprocedural2(graphOrTypes, callsOrSources, dfgOrSinks, sourc
|
|
|
36419
36451
|
"Command",
|
|
36420
36452
|
"CommandContext"
|
|
36421
36453
|
]);
|
|
36454
|
+
const ormQueryBuilderMethods = new Set([
|
|
36455
|
+
"findMany",
|
|
36456
|
+
"findFirst",
|
|
36457
|
+
"findUnique",
|
|
36458
|
+
"findUniqueOrThrow",
|
|
36459
|
+
"findFirstOrThrow",
|
|
36460
|
+
"findOne",
|
|
36461
|
+
"findAll",
|
|
36462
|
+
"findByPk",
|
|
36463
|
+
"findAndCountAll",
|
|
36464
|
+
"findBy",
|
|
36465
|
+
"findOneBy",
|
|
36466
|
+
"where",
|
|
36467
|
+
"andWhere",
|
|
36468
|
+
"orWhere",
|
|
36469
|
+
"whereIn",
|
|
36470
|
+
"whereNot",
|
|
36471
|
+
"first",
|
|
36472
|
+
"bind"
|
|
36473
|
+
]);
|
|
36422
36474
|
const sanitizerMethods = new Set;
|
|
36423
36475
|
for (const san of sanitizers) {
|
|
36424
36476
|
sanitizerMethods.add(san.method);
|
|
@@ -36443,7 +36495,7 @@ function analyzeInterprocedural2(graphOrTypes, callsOrSources, dfgOrSinks, sourc
|
|
|
36443
36495
|
}
|
|
36444
36496
|
const targetMethod = getMethodNode(methodNodes, call.method_name);
|
|
36445
36497
|
if (!targetMethod) {
|
|
36446
|
-
if (taintedArgPositions.length > 0 && !collectionMethods.has(call.method_name) && !sanitizerMethods.has(call.method_name) && !safeUtilityMethods.has(call.method_name)) {
|
|
36498
|
+
if (taintedArgPositions.length > 0 && !collectionMethods.has(call.method_name) && !sanitizerMethods.has(call.method_name) && !safeUtilityMethods.has(call.method_name) && !ormQueryBuilderMethods.has(call.method_name)) {
|
|
36447
36499
|
const isBash = graph.ir.meta.language === "bash";
|
|
36448
36500
|
const bashSafeBuiltins = new Set([
|
|
36449
36501
|
"echo",
|
|
@@ -38233,6 +38285,7 @@ var SKIP_NAMES3 = new Set([
|
|
|
38233
38285
|
"unknown",
|
|
38234
38286
|
"bigint"
|
|
38235
38287
|
]);
|
|
38288
|
+
var EMPTY_LINES = [];
|
|
38236
38289
|
|
|
38237
38290
|
class UnusedVariablePass {
|
|
38238
38291
|
name = "unused-variable";
|
|
@@ -38247,6 +38300,35 @@ class UnusedVariablePass {
|
|
|
38247
38300
|
`);
|
|
38248
38301
|
const unusedVars = [];
|
|
38249
38302
|
const reported = new Set;
|
|
38303
|
+
const defsByVar = new Map;
|
|
38304
|
+
for (const d of graph.ir.dfg.defs) {
|
|
38305
|
+
const arr = defsByVar.get(d.variable);
|
|
38306
|
+
if (arr)
|
|
38307
|
+
arr.push(d);
|
|
38308
|
+
else
|
|
38309
|
+
defsByVar.set(d.variable, [d]);
|
|
38310
|
+
}
|
|
38311
|
+
const nameLines = new Map;
|
|
38312
|
+
const idRe = /[A-Za-z_][A-Za-z0-9_]*/g;
|
|
38313
|
+
for (let i2 = 0;i2 < codeLines.length; i2++) {
|
|
38314
|
+
const line = codeLines[i2];
|
|
38315
|
+
idRe.lastIndex = 0;
|
|
38316
|
+
let seen = null;
|
|
38317
|
+
let m;
|
|
38318
|
+
while ((m = idRe.exec(line)) !== null) {
|
|
38319
|
+
const w = m[0];
|
|
38320
|
+
if (seen === null)
|
|
38321
|
+
seen = new Set;
|
|
38322
|
+
if (seen.has(w))
|
|
38323
|
+
continue;
|
|
38324
|
+
seen.add(w);
|
|
38325
|
+
const arr = nameLines.get(w);
|
|
38326
|
+
if (arr)
|
|
38327
|
+
arr.push(i2 + 1);
|
|
38328
|
+
else
|
|
38329
|
+
nameLines.set(w, [i2 + 1]);
|
|
38330
|
+
}
|
|
38331
|
+
}
|
|
38250
38332
|
for (const def of graph.ir.dfg.defs) {
|
|
38251
38333
|
if (def.kind !== "local")
|
|
38252
38334
|
continue;
|
|
@@ -38263,19 +38345,20 @@ class UnusedVariablePass {
|
|
|
38263
38345
|
const uses = graph.usesOfDef(def.id);
|
|
38264
38346
|
if (uses.length > 0)
|
|
38265
38347
|
continue;
|
|
38266
|
-
const otherDefs =
|
|
38348
|
+
const otherDefs = (defsByVar.get(variable) ?? []).filter((d) => d.id !== def.id);
|
|
38267
38349
|
const otherDefLines = new Set(otherDefs.map((d) => d.line));
|
|
38268
|
-
const
|
|
38269
|
-
const
|
|
38350
|
+
const plainName = /^[A-Za-z_][A-Za-z0-9_]*$/.test(variable);
|
|
38351
|
+
const idxLines = plainName ? nameLines.get(variable) ?? EMPTY_LINES : null;
|
|
38352
|
+
const namePattern = plainName ? null : new RegExp(`\\b${variable.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}\\b`);
|
|
38270
38353
|
if (otherDefLines.size === 0) {
|
|
38271
|
-
const usedElsewhere = codeLines.some((line, idx) => idx !== def.line - 1 && namePattern.test(line));
|
|
38354
|
+
const usedElsewhere = idxLines !== null ? idxLines.some((ln) => ln !== def.line) : codeLines.some((line, idx) => idx !== def.line - 1 && namePattern.test(line));
|
|
38272
38355
|
if (usedElsewhere)
|
|
38273
38356
|
continue;
|
|
38274
38357
|
} else {
|
|
38275
38358
|
const lineText2 = codeLines[def.line - 1] ?? "";
|
|
38276
38359
|
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);
|
|
38277
38360
|
if (isTrueDeclaration) {
|
|
38278
|
-
const usedBeforeNextDef = codeLines.some((line, idx) => {
|
|
38361
|
+
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) => {
|
|
38279
38362
|
const lineNum = idx + 1;
|
|
38280
38363
|
if (lineNum === def.line || otherDefLines.has(lineNum))
|
|
38281
38364
|
return false;
|
|
@@ -38286,7 +38369,7 @@ class UnusedVariablePass {
|
|
|
38286
38369
|
if (usedBeforeNextDef)
|
|
38287
38370
|
continue;
|
|
38288
38371
|
} else {
|
|
38289
|
-
const usedOnNonDefLine = codeLines.some((line, idx) => {
|
|
38372
|
+
const usedOnNonDefLine = idxLines !== null ? idxLines.some((ln) => ln !== def.line && !otherDefLines.has(ln)) : codeLines.some((line, idx) => {
|
|
38290
38373
|
const lineNum = idx + 1;
|
|
38291
38374
|
return lineNum !== def.line && !otherDefLines.has(lineNum) && namePattern.test(line);
|
|
38292
38375
|
});
|
|
@@ -43712,9 +43795,7 @@ class InfoDisclosureStacktracePass {
|
|
|
43712
43795
|
// ../circle-ir/dist/analysis/passes/unrestricted-file-upload-pass.js
|
|
43713
43796
|
var UPLOAD_NAME_RE = /(?:getOriginalFilename|getSubmittedFileName|originalname|originalName|\.filename|\.Filename|FileHeader\.Filename|UploadFile)/;
|
|
43714
43797
|
var FILE_SAFE_CALL_RE = /(?:secure_filename|FilenameUtils\.getExtension|\.lastIndexOf\(['"]\.['"]\)|ALLOWED_EXT|ALLOWED_EXTENSIONS|allowedExtensions|\bfileFilter\b|filepath\.Ext|path\.extname)/;
|
|
43715
|
-
function lineWindow(
|
|
43716
|
-
const lines = code.split(`
|
|
43717
|
-
`);
|
|
43798
|
+
function lineWindow(lines, startLine, endLine) {
|
|
43718
43799
|
const s = Math.max(0, startLine - 1);
|
|
43719
43800
|
const e = Math.min(lines.length, endLine);
|
|
43720
43801
|
return lines.slice(s, e).join(`
|
|
@@ -43738,10 +43819,12 @@ class UnrestrictedFileUploadPass {
|
|
|
43738
43819
|
const { graph, language, code } = ctx;
|
|
43739
43820
|
const file = graph.ir.meta.file;
|
|
43740
43821
|
const findings = [];
|
|
43822
|
+
const codeLines = code.split(`
|
|
43823
|
+
`);
|
|
43741
43824
|
const safeFunctionRanges = [];
|
|
43742
43825
|
for (const t of graph.ir.types) {
|
|
43743
43826
|
for (const m of t.methods) {
|
|
43744
|
-
const body2 = lineWindow(
|
|
43827
|
+
const body2 = lineWindow(codeLines, m.start_line, m.end_line);
|
|
43745
43828
|
if (FILE_SAFE_CALL_RE.test(body2)) {
|
|
43746
43829
|
safeFunctionRanges.push({ start: m.start_line, end: m.end_line });
|
|
43747
43830
|
}
|
|
@@ -43752,7 +43835,7 @@ class UnrestrictedFileUploadPass {
|
|
|
43752
43835
|
if (line >= r.start && line <= r.end)
|
|
43753
43836
|
return true;
|
|
43754
43837
|
}
|
|
43755
|
-
const win = lineWindow(
|
|
43838
|
+
const win = lineWindow(codeLines, Math.max(1, line - 20), line + 5);
|
|
43756
43839
|
return FILE_SAFE_CALL_RE.test(win);
|
|
43757
43840
|
};
|
|
43758
43841
|
if (language === "java") {
|
|
@@ -48002,7 +48085,7 @@ var colors = {
|
|
|
48002
48085
|
};
|
|
48003
48086
|
|
|
48004
48087
|
// src/version.ts
|
|
48005
|
-
var version = "4.7.
|
|
48088
|
+
var version = "4.7.1";
|
|
48006
48089
|
|
|
48007
48090
|
// src/formatters.ts
|
|
48008
48091
|
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": "4.7.
|
|
3
|
+
"version": "4.7.1",
|
|
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": "^4.7.
|
|
69
|
+
"circle-ir": "^4.7.1"
|
|
70
70
|
},
|
|
71
71
|
"devDependencies": {
|
|
72
72
|
"@types/node": "^25.5.0",
|