cognium-dev 3.153.0 → 3.155.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.
- package/dist/cli.js +262 -16
- package/package.json +2 -2
package/dist/cli.js
CHANGED
|
@@ -10623,9 +10623,6 @@ var DEFAULT_SINKS = [
|
|
|
10623
10623
|
{ method: "newBufferedWriter", class: "Files", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0] },
|
|
10624
10624
|
{ method: "copy", class: "Files", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0, 1] },
|
|
10625
10625
|
{ method: "move", class: "Files", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0, 1] },
|
|
10626
|
-
{ method: "exists", class: "Files", type: "path_traversal", cwe: "CWE-22", severity: "medium", arg_positions: [0] },
|
|
10627
|
-
{ method: "isDirectory", class: "Files", type: "path_traversal", cwe: "CWE-22", severity: "medium", arg_positions: [0] },
|
|
10628
|
-
{ method: "isRegularFile", class: "Files", type: "path_traversal", cwe: "CWE-22", severity: "medium", arg_positions: [0] },
|
|
10629
10626
|
{ method: "RandomAccessFile", class: "constructor", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0] },
|
|
10630
10627
|
{ method: "resolveURI", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0] },
|
|
10631
10628
|
{ method: "resolve", class: "SourceResolver", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0] },
|
|
@@ -11662,6 +11659,7 @@ var DEFAULT_SANITIZERS = [
|
|
|
11662
11659
|
{ method: "parseDouble", class: "Double", removes: ["sql_injection", "command_injection", "path_traversal", "code_injection"] },
|
|
11663
11660
|
{ method: "parseShort", class: "Short", removes: ["sql_injection", "command_injection", "path_traversal", "code_injection"] },
|
|
11664
11661
|
{ method: "parseByte", class: "Byte", removes: ["sql_injection", "command_injection", "path_traversal", "code_injection"] },
|
|
11662
|
+
{ method: "parseBoolean", class: "Boolean", removes: ["sql_injection", "command_injection", "path_traversal", "code_injection", "xss", "crlf", "log_injection", "xpath_injection", "ldap_injection", "xxe"] },
|
|
11665
11663
|
{ method: "fromString", class: "UUID", removes: ["sql_injection", "command_injection", "path_traversal", "code_injection"] },
|
|
11666
11664
|
{ method: "BigInt", removes: ["sql_injection", "nosql_injection", "command_injection", "path_traversal", "code_injection"] },
|
|
11667
11665
|
{ method: "Atoi", class: "strconv", removes: ["sql_injection", "command_injection", "path_traversal", "code_injection"] },
|
|
@@ -13773,6 +13771,18 @@ class CodeGraph {
|
|
|
13773
13771
|
}
|
|
13774
13772
|
return this._chainsByFromDef;
|
|
13775
13773
|
}
|
|
13774
|
+
_chainsByToDef = null;
|
|
13775
|
+
get chainsByToDef() {
|
|
13776
|
+
if (!this._chainsByToDef) {
|
|
13777
|
+
this._chainsByToDef = new Map;
|
|
13778
|
+
for (const chain of this.ir.dfg.chains ?? []) {
|
|
13779
|
+
const arr = this._chainsByToDef.get(chain.to_def) ?? [];
|
|
13780
|
+
arr.push(chain);
|
|
13781
|
+
this._chainsByToDef.set(chain.to_def, arr);
|
|
13782
|
+
}
|
|
13783
|
+
}
|
|
13784
|
+
return this._chainsByToDef;
|
|
13785
|
+
}
|
|
13776
13786
|
_callsByLine = null;
|
|
13777
13787
|
get callsByLine() {
|
|
13778
13788
|
if (!this._callsByLine) {
|
|
@@ -15643,6 +15653,45 @@ class AnalysisPipeline {
|
|
|
15643
15653
|
return { results, findings };
|
|
15644
15654
|
}
|
|
15645
15655
|
}
|
|
15656
|
+
// ../circle-ir/dist/analysis/dfg-walk.js
|
|
15657
|
+
function walkBackwardDefs(startDefId, chainsByToDef, defById, options = {}) {
|
|
15658
|
+
const maxHops = options.maxHops ?? 32;
|
|
15659
|
+
const visited = new Set;
|
|
15660
|
+
const lines = new Set;
|
|
15661
|
+
let hopCapReached = false;
|
|
15662
|
+
const startDef = defById.get(startDefId);
|
|
15663
|
+
if (!startDef) {
|
|
15664
|
+
return { visited, lines, hopCapReached: false };
|
|
15665
|
+
}
|
|
15666
|
+
visited.add(startDefId);
|
|
15667
|
+
lines.add(startDef.line);
|
|
15668
|
+
const queue = [startDefId];
|
|
15669
|
+
let hops = 0;
|
|
15670
|
+
while (queue.length > 0) {
|
|
15671
|
+
if (hops >= maxHops) {
|
|
15672
|
+
hopCapReached = true;
|
|
15673
|
+
break;
|
|
15674
|
+
}
|
|
15675
|
+
hops++;
|
|
15676
|
+
const currentId = queue.shift();
|
|
15677
|
+
const incoming = chainsByToDef.get(currentId);
|
|
15678
|
+
if (!incoming)
|
|
15679
|
+
continue;
|
|
15680
|
+
for (const chain of incoming) {
|
|
15681
|
+
const fromId = chain.from_def;
|
|
15682
|
+
if (visited.has(fromId))
|
|
15683
|
+
continue;
|
|
15684
|
+
visited.add(fromId);
|
|
15685
|
+
const fromDef = defById.get(fromId);
|
|
15686
|
+
if (fromDef) {
|
|
15687
|
+
lines.add(fromDef.line);
|
|
15688
|
+
}
|
|
15689
|
+
queue.push(fromId);
|
|
15690
|
+
}
|
|
15691
|
+
}
|
|
15692
|
+
return { visited, lines, hopCapReached };
|
|
15693
|
+
}
|
|
15694
|
+
|
|
15646
15695
|
// ../circle-ir/dist/analysis/taint-propagation.js
|
|
15647
15696
|
function buildSanitizersByLine(sanitizers) {
|
|
15648
15697
|
const out2 = new Map;
|
|
@@ -15724,7 +15773,11 @@ function propagateTaint(graphOrDfg, callsOrSources, sourcesOrSinks, sinksOrSanit
|
|
|
15724
15773
|
if (allTaintedDefIds.has(use.def_id)) {
|
|
15725
15774
|
const taintInfo = taintByDefId.get(use.def_id);
|
|
15726
15775
|
if (taintInfo) {
|
|
15727
|
-
const isSanitized = checkSanitized(taintInfo.line, sink.line, sink.type, sanitizersByLine
|
|
15776
|
+
const isSanitized = checkSanitized(taintInfo.line, sink.line, sink.type, sanitizersByLine, {
|
|
15777
|
+
startDefId: use.def_id,
|
|
15778
|
+
chainsByToDef: graph.chainsByToDef,
|
|
15779
|
+
defById
|
|
15780
|
+
});
|
|
15728
15781
|
if (!isSanitized.sanitized) {
|
|
15729
15782
|
const source = sources.find((s) => s.line === taintInfo.sourceLine);
|
|
15730
15783
|
if (source) {
|
|
@@ -15854,20 +15907,36 @@ var KNOWN_SINK_TYPES = new Set([
|
|
|
15854
15907
|
"crlf",
|
|
15855
15908
|
"mass_assignment"
|
|
15856
15909
|
]);
|
|
15857
|
-
function checkSanitized(_fromLine, toLine, sinkType, sanitizersByLine) {
|
|
15858
|
-
const sanitizersAtTarget = sanitizersByLine.get(toLine);
|
|
15859
|
-
if (!sanitizersAtTarget || sanitizersAtTarget.length === 0) {
|
|
15860
|
-
return { sanitized: false };
|
|
15861
|
-
}
|
|
15910
|
+
function checkSanitized(_fromLine, toLine, sinkType, sanitizersByLine, ctx) {
|
|
15862
15911
|
const isKnownSinkType = KNOWN_SINK_TYPES.has(sinkType);
|
|
15863
|
-
|
|
15864
|
-
|
|
15865
|
-
|
|
15912
|
+
const sanitizersAtTarget = sanitizersByLine.get(toLine);
|
|
15913
|
+
if (sanitizersAtTarget && sanitizersAtTarget.length > 0) {
|
|
15914
|
+
for (const san of sanitizersAtTarget) {
|
|
15915
|
+
if (isKnownSinkType) {
|
|
15916
|
+
if (san.sanitizes.includes(sinkType)) {
|
|
15917
|
+
return { sanitized: true, sanitizer: san };
|
|
15918
|
+
}
|
|
15919
|
+
} else if (san.sanitizes.length > 0) {
|
|
15866
15920
|
return { sanitized: true, sanitizer: san };
|
|
15867
15921
|
}
|
|
15868
|
-
}
|
|
15869
|
-
|
|
15870
|
-
|
|
15922
|
+
}
|
|
15923
|
+
}
|
|
15924
|
+
if (ctx) {
|
|
15925
|
+
const walk = walkBackwardDefs(ctx.startDefId, ctx.chainsByToDef, ctx.defById, { maxHops: ctx.maxHops ?? 32 });
|
|
15926
|
+
for (const line of walk.lines) {
|
|
15927
|
+
if (line === toLine)
|
|
15928
|
+
continue;
|
|
15929
|
+
const sansAtLine = sanitizersByLine.get(line);
|
|
15930
|
+
if (!sansAtLine || sansAtLine.length === 0)
|
|
15931
|
+
continue;
|
|
15932
|
+
for (const san of sansAtLine) {
|
|
15933
|
+
if (isKnownSinkType) {
|
|
15934
|
+
if (san.sanitizes.includes(sinkType)) {
|
|
15935
|
+
return { sanitized: true, sanitizer: san };
|
|
15936
|
+
}
|
|
15937
|
+
} else if (san.sanitizes.length > 0) {
|
|
15938
|
+
return { sanitized: true, sanitizer: san };
|
|
15939
|
+
}
|
|
15871
15940
|
}
|
|
15872
15941
|
}
|
|
15873
15942
|
}
|
|
@@ -24135,6 +24204,20 @@ function findJavaScriptDOMSinks(sourceCode, language) {
|
|
|
24135
24204
|
method = "cssText";
|
|
24136
24205
|
else if (line.includes("style.textContent"))
|
|
24137
24206
|
method = "textContent";
|
|
24207
|
+
if (method === "document.write" || method === "document.writeln") {
|
|
24208
|
+
const argMatch = line.match(/document\.write(?:ln)?\s*\(\s*([^)]*)\)/);
|
|
24209
|
+
if (argMatch) {
|
|
24210
|
+
const arg = argMatch[1].trim();
|
|
24211
|
+
if (!arg.includes("+")) {
|
|
24212
|
+
const doubleQ = /^"(?:[^"\\]|\\.)*"$/;
|
|
24213
|
+
const singleQ = /^'(?:[^'\\]|\\.)*'$/;
|
|
24214
|
+
const backtick = /^`(?:[^`\\$]|\\.|\$(?!\{))*`$/;
|
|
24215
|
+
if (doubleQ.test(arg) || singleQ.test(arg) || backtick.test(arg)) {
|
|
24216
|
+
break;
|
|
24217
|
+
}
|
|
24218
|
+
}
|
|
24219
|
+
}
|
|
24220
|
+
}
|
|
24138
24221
|
const alreadyExists = sinks.some((s) => s.line === lineNumber && s.cwe === cwe);
|
|
24139
24222
|
if (!alreadyExists) {
|
|
24140
24223
|
sinks.push({ type, cwe, severity, line: lineNumber, location: line.trim().substring(0, 80), method });
|
|
@@ -31144,6 +31227,137 @@ class LibraryProfileSinkGatePass {
|
|
|
31144
31227
|
};
|
|
31145
31228
|
}
|
|
31146
31229
|
}
|
|
31230
|
+
var CWE22_SPECULATIVE_SOURCE_TYPES = new Set([
|
|
31231
|
+
"interprocedural_param",
|
|
31232
|
+
"constructor_field"
|
|
31233
|
+
]);
|
|
31234
|
+
|
|
31235
|
+
class LibraryProfileCwe22PathGatePass {
|
|
31236
|
+
name = "library-profile-cwe22-path-gate";
|
|
31237
|
+
category = "security";
|
|
31238
|
+
run(ctx) {
|
|
31239
|
+
const { graph } = ctx;
|
|
31240
|
+
const profile = graph.ir.meta.projectProfile;
|
|
31241
|
+
if (!isLibraryShape2(profile)) {
|
|
31242
|
+
return {
|
|
31243
|
+
profile,
|
|
31244
|
+
applied: false,
|
|
31245
|
+
dropped: 0,
|
|
31246
|
+
droppedBySourceType: {}
|
|
31247
|
+
};
|
|
31248
|
+
}
|
|
31249
|
+
const flows = graph.ir.taint.flows;
|
|
31250
|
+
if (!flows || flows.length === 0) {
|
|
31251
|
+
return {
|
|
31252
|
+
profile,
|
|
31253
|
+
applied: true,
|
|
31254
|
+
dropped: 0,
|
|
31255
|
+
droppedBySourceType: {}
|
|
31256
|
+
};
|
|
31257
|
+
}
|
|
31258
|
+
const droppedBySourceType = {};
|
|
31259
|
+
const kept = [];
|
|
31260
|
+
for (const flow of flows) {
|
|
31261
|
+
if (flow.sink_type === "path_traversal" && CWE22_SPECULATIVE_SOURCE_TYPES.has(flow.source_type)) {
|
|
31262
|
+
droppedBySourceType[flow.source_type] = (droppedBySourceType[flow.source_type] ?? 0) + 1;
|
|
31263
|
+
continue;
|
|
31264
|
+
}
|
|
31265
|
+
kept.push(flow);
|
|
31266
|
+
}
|
|
31267
|
+
const dropped = flows.length - kept.length;
|
|
31268
|
+
if (dropped > 0) {
|
|
31269
|
+
flows.length = 0;
|
|
31270
|
+
flows.push(...kept);
|
|
31271
|
+
}
|
|
31272
|
+
return {
|
|
31273
|
+
profile,
|
|
31274
|
+
applied: true,
|
|
31275
|
+
dropped,
|
|
31276
|
+
droppedBySourceType
|
|
31277
|
+
};
|
|
31278
|
+
}
|
|
31279
|
+
}
|
|
31280
|
+
|
|
31281
|
+
// ../circle-ir/dist/analysis/passes/library-profile-xss-gate-pass.js
|
|
31282
|
+
var XSS_NON_HTML_OUTPUT_CLASSES = new Set([
|
|
31283
|
+
"StringBuilder",
|
|
31284
|
+
"StringBuffer",
|
|
31285
|
+
"CharArrayWriter",
|
|
31286
|
+
"ByteArrayOutputStream",
|
|
31287
|
+
"PrintStream",
|
|
31288
|
+
"System",
|
|
31289
|
+
"HttpRequest",
|
|
31290
|
+
"HttpRequestBuilder",
|
|
31291
|
+
"HttpResponse",
|
|
31292
|
+
"HttpSession",
|
|
31293
|
+
"ServletRequest",
|
|
31294
|
+
"HttpServletRequest",
|
|
31295
|
+
"RedisOutputStream",
|
|
31296
|
+
"SafeEncoder",
|
|
31297
|
+
"RESP2",
|
|
31298
|
+
"Protocol",
|
|
31299
|
+
"JSONUtil",
|
|
31300
|
+
"JSON",
|
|
31301
|
+
"ObjectMapper",
|
|
31302
|
+
"JsonReader",
|
|
31303
|
+
"Logger",
|
|
31304
|
+
"LoggerFactory",
|
|
31305
|
+
"Log",
|
|
31306
|
+
"Slf4jLogger",
|
|
31307
|
+
"RequestContext",
|
|
31308
|
+
"Context"
|
|
31309
|
+
]);
|
|
31310
|
+
function isLibraryShape3(profile) {
|
|
31311
|
+
if (!profile || profile === "unknown")
|
|
31312
|
+
return false;
|
|
31313
|
+
return profile.startsWith("library/");
|
|
31314
|
+
}
|
|
31315
|
+
|
|
31316
|
+
class LibraryProfileXssGatePass {
|
|
31317
|
+
name = "library-profile-xss-gate";
|
|
31318
|
+
category = "security";
|
|
31319
|
+
run(ctx) {
|
|
31320
|
+
const { graph } = ctx;
|
|
31321
|
+
const profile = graph.ir.meta.projectProfile;
|
|
31322
|
+
if (!isLibraryShape3(profile)) {
|
|
31323
|
+
return {
|
|
31324
|
+
profile,
|
|
31325
|
+
applied: false,
|
|
31326
|
+
dropped: 0,
|
|
31327
|
+
droppedByClass: {}
|
|
31328
|
+
};
|
|
31329
|
+
}
|
|
31330
|
+
const sinks = ctx.hasResult("sink-filter") ? ctx.getResult("sink-filter").sinks : graph.ir.taint.sinks;
|
|
31331
|
+
if (sinks.length === 0) {
|
|
31332
|
+
return {
|
|
31333
|
+
profile,
|
|
31334
|
+
applied: true,
|
|
31335
|
+
dropped: 0,
|
|
31336
|
+
droppedByClass: {}
|
|
31337
|
+
};
|
|
31338
|
+
}
|
|
31339
|
+
const droppedByClass = {};
|
|
31340
|
+
const kept = [];
|
|
31341
|
+
for (const sink of sinks) {
|
|
31342
|
+
if (sink.type === "xss" && sink.class && XSS_NON_HTML_OUTPUT_CLASSES.has(sink.class)) {
|
|
31343
|
+
droppedByClass[sink.class] = (droppedByClass[sink.class] ?? 0) + 1;
|
|
31344
|
+
continue;
|
|
31345
|
+
}
|
|
31346
|
+
kept.push(sink);
|
|
31347
|
+
}
|
|
31348
|
+
const dropped = sinks.length - kept.length;
|
|
31349
|
+
if (dropped > 0) {
|
|
31350
|
+
sinks.length = 0;
|
|
31351
|
+
sinks.push(...kept);
|
|
31352
|
+
}
|
|
31353
|
+
return {
|
|
31354
|
+
profile,
|
|
31355
|
+
applied: true,
|
|
31356
|
+
dropped,
|
|
31357
|
+
droppedByClass
|
|
31358
|
+
};
|
|
31359
|
+
}
|
|
31360
|
+
}
|
|
31147
31361
|
|
|
31148
31362
|
// ../circle-ir/dist/analysis/passes/taint-propagation-pass.js
|
|
31149
31363
|
class TaintPropagationPass {
|
|
@@ -38079,6 +38293,31 @@ class WeakHashPass {
|
|
|
38079
38293
|
}
|
|
38080
38294
|
}
|
|
38081
38295
|
|
|
38296
|
+
// ../circle-ir/dist/analysis/path-classification.js
|
|
38297
|
+
var TEST_PATH_PATTERNS = [
|
|
38298
|
+
/(^|\/)test\//i,
|
|
38299
|
+
/(^|\/)tests\//i,
|
|
38300
|
+
/(^|\/)testing\//i,
|
|
38301
|
+
/(^|\/)__tests__\//i,
|
|
38302
|
+
/(^|\/)spec\//i,
|
|
38303
|
+
/_test\.go$/i,
|
|
38304
|
+
/(^|\/)test_[^/]+\.py$/i,
|
|
38305
|
+
/_test\.py$/i,
|
|
38306
|
+
/\.test\.(?:tsx?|jsx?|mjs|cjs)$/i,
|
|
38307
|
+
/\.spec\.(?:tsx?|jsx?|mjs|cjs)$/i,
|
|
38308
|
+
/\.test\.(?:java|kt)$/i
|
|
38309
|
+
];
|
|
38310
|
+
function isTestPath(filepath) {
|
|
38311
|
+
if (!filepath)
|
|
38312
|
+
return false;
|
|
38313
|
+
const normalized = filepath.replace(/\\/g, "/");
|
|
38314
|
+
for (const pattern of TEST_PATH_PATTERNS) {
|
|
38315
|
+
if (pattern.test(normalized))
|
|
38316
|
+
return true;
|
|
38317
|
+
}
|
|
38318
|
+
return false;
|
|
38319
|
+
}
|
|
38320
|
+
|
|
38082
38321
|
// ../circle-ir/dist/analysis/passes/weak-crypto-pass.js
|
|
38083
38322
|
var WEAK_CIPHER_BASES = new Set([
|
|
38084
38323
|
"des",
|
|
@@ -38294,6 +38533,9 @@ class WeakCryptoPass {
|
|
|
38294
38533
|
if (isProtocolMandatedCryptoFile(file, code)) {
|
|
38295
38534
|
return { findings: [] };
|
|
38296
38535
|
}
|
|
38536
|
+
if (isTestPath(file)) {
|
|
38537
|
+
return { findings: [] };
|
|
38538
|
+
}
|
|
38297
38539
|
const findings = [];
|
|
38298
38540
|
const constProp = ctx.hasResult("constant-propagation") ? ctx.getResult("constant-propagation") : null;
|
|
38299
38541
|
const literalBindings = scanLiteralBindings(code, language);
|
|
@@ -42059,10 +42301,14 @@ async function analyze(code, filePath, language, options = {}) {
|
|
|
42059
42301
|
pipeline.add(new CliMainReflectionSuppressPass);
|
|
42060
42302
|
if (!disabledPasses.has("library-profile-sink-gate"))
|
|
42061
42303
|
pipeline.add(new LibraryProfileSinkGatePass);
|
|
42304
|
+
if (!disabledPasses.has("library-profile-xss-gate"))
|
|
42305
|
+
pipeline.add(new LibraryProfileXssGatePass);
|
|
42062
42306
|
pipeline.add(new TaintPropagationPass);
|
|
42063
42307
|
pipeline.add(new InterproceduralPass({
|
|
42064
42308
|
enableEntryPointGate: options.enableEntryPointGate ?? true
|
|
42065
42309
|
}));
|
|
42310
|
+
if (!disabledPasses.has("library-profile-cwe22-path-gate"))
|
|
42311
|
+
pipeline.add(new LibraryProfileCwe22PathGatePass);
|
|
42066
42312
|
if (!disabledPasses.has("scan-secrets"))
|
|
42067
42313
|
pipeline.add(new ScanSecretsPass);
|
|
42068
42314
|
if (!disabledPasses.has("dead-code"))
|
|
@@ -42993,7 +43239,7 @@ var colors = {
|
|
|
42993
43239
|
};
|
|
42994
43240
|
|
|
42995
43241
|
// src/version.ts
|
|
42996
|
-
var version = "3.
|
|
43242
|
+
var version = "3.155.0";
|
|
42997
43243
|
|
|
42998
43244
|
// src/formatters.ts
|
|
42999
43245
|
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.
|
|
3
|
+
"version": "3.155.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.
|
|
69
|
+
"circle-ir": "^3.155.0"
|
|
70
70
|
},
|
|
71
71
|
"devDependencies": {
|
|
72
72
|
"@types/node": "^25.5.0",
|