cognium-dev 3.181.0 → 3.182.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 +208 -9
- package/package.json +2 -2
package/dist/cli.js
CHANGED
|
@@ -9440,6 +9440,10 @@ function processGoBlock(node, defs, uses, scopeStack, counters) {
|
|
|
9440
9440
|
} else if (child.type === "for_statement") {
|
|
9441
9441
|
const rangeClause = findChildByTypeGo(child, "range_clause");
|
|
9442
9442
|
if (rangeClause) {
|
|
9443
|
+
const right = rangeClause.childForFieldName("right");
|
|
9444
|
+
if (right) {
|
|
9445
|
+
extractGoUses(right, uses, scopeStack);
|
|
9446
|
+
}
|
|
9443
9447
|
const left = rangeClause.childForFieldName("left");
|
|
9444
9448
|
if (left) {
|
|
9445
9449
|
extractGoLhsDefs(left, defs, scopeStack, child.startPosition.row + 1);
|
|
@@ -9447,6 +9451,7 @@ function processGoBlock(node, defs, uses, scopeStack, counters) {
|
|
|
9447
9451
|
}
|
|
9448
9452
|
} else if (child.type === "call_expression") {
|
|
9449
9453
|
extractGoUses(child, uses, scopeStack);
|
|
9454
|
+
recordGoOpaqueCodecDestDef(child, defs, scopeStack);
|
|
9450
9455
|
} else if (child.type === "return_statement") {
|
|
9451
9456
|
for (let i2 = 0;i2 < child.childCount; i2++) {
|
|
9452
9457
|
const expr = child.child(i2);
|
|
@@ -9546,6 +9551,70 @@ function findChildByTypeGo(node, type) {
|
|
|
9546
9551
|
}
|
|
9547
9552
|
return null;
|
|
9548
9553
|
}
|
|
9554
|
+
var GO_OPAQUE_CODEC_METHODS = new Set([
|
|
9555
|
+
"Unmarshal",
|
|
9556
|
+
"Decode",
|
|
9557
|
+
"NewDecoder",
|
|
9558
|
+
"UnmarshalYAML",
|
|
9559
|
+
"UnmarshalJSON",
|
|
9560
|
+
"UnmarshalText",
|
|
9561
|
+
"UnmarshalBinary"
|
|
9562
|
+
]);
|
|
9563
|
+
function recordGoOpaqueCodecDestDef(call, defs, scopeStack) {
|
|
9564
|
+
const fn = call.childForFieldName("function");
|
|
9565
|
+
if (!fn || fn.type !== "selector_expression")
|
|
9566
|
+
return;
|
|
9567
|
+
const fieldNode = fn.childForFieldName("field");
|
|
9568
|
+
if (!fieldNode)
|
|
9569
|
+
return;
|
|
9570
|
+
const method = getNodeText(fieldNode);
|
|
9571
|
+
if (!GO_OPAQUE_CODEC_METHODS.has(method))
|
|
9572
|
+
return;
|
|
9573
|
+
const argsNode = call.childForFieldName("arguments");
|
|
9574
|
+
if (!argsNode)
|
|
9575
|
+
return;
|
|
9576
|
+
const args2 = [];
|
|
9577
|
+
for (let i2 = 0;i2 < argsNode.childCount; i2++) {
|
|
9578
|
+
const c = argsNode.child(i2);
|
|
9579
|
+
if (!c)
|
|
9580
|
+
continue;
|
|
9581
|
+
if (c.type === "," || c.type === "(" || c.type === ")")
|
|
9582
|
+
continue;
|
|
9583
|
+
args2.push(c);
|
|
9584
|
+
}
|
|
9585
|
+
if (args2.length === 0)
|
|
9586
|
+
return;
|
|
9587
|
+
const destArg = args2.length >= 2 ? args2[1] : args2[0];
|
|
9588
|
+
const destName = extractGoAddressableVarName(destArg);
|
|
9589
|
+
if (!destName || destName === "_")
|
|
9590
|
+
return;
|
|
9591
|
+
const line = call.startPosition.row + 1;
|
|
9592
|
+
const def = {
|
|
9593
|
+
id: defs.length + 1,
|
|
9594
|
+
variable: destName,
|
|
9595
|
+
kind: "local",
|
|
9596
|
+
line
|
|
9597
|
+
};
|
|
9598
|
+
defs.push(def);
|
|
9599
|
+
currentScope(scopeStack).set(destName, def.id);
|
|
9600
|
+
}
|
|
9601
|
+
function extractGoAddressableVarName(node) {
|
|
9602
|
+
if (node.type === "unary_expression") {
|
|
9603
|
+
const operand = node.childForFieldName("operand") ?? node.child(1) ?? null;
|
|
9604
|
+
if (operand)
|
|
9605
|
+
return extractGoAddressableVarName(operand);
|
|
9606
|
+
return null;
|
|
9607
|
+
}
|
|
9608
|
+
if (node.type === "identifier") {
|
|
9609
|
+
return getNodeText(node);
|
|
9610
|
+
}
|
|
9611
|
+
if (node.type === "selector_expression") {
|
|
9612
|
+
const field = node.childForFieldName("field");
|
|
9613
|
+
if (field)
|
|
9614
|
+
return getNodeText(field);
|
|
9615
|
+
}
|
|
9616
|
+
return null;
|
|
9617
|
+
}
|
|
9549
9618
|
// ../circle-ir/dist/core/extractors/runtime-registrations.js
|
|
9550
9619
|
var HTTP_VERB_METHODS = new Set([
|
|
9551
9620
|
"get",
|
|
@@ -16432,15 +16501,23 @@ function propagateTaint(graphOrDfg, callsOrSources, sourcesOrSinks, sinksOrSanit
|
|
|
16432
16501
|
const callsAtSink = callsByLine.get(sink.line) ?? [];
|
|
16433
16502
|
for (const call of callsAtSink) {
|
|
16434
16503
|
for (const arg of call.arguments) {
|
|
16435
|
-
if (
|
|
16436
|
-
if (sink.argPositions
|
|
16437
|
-
|
|
16438
|
-
continue;
|
|
16439
|
-
}
|
|
16504
|
+
if (sink.argPositions && sink.argPositions.length > 0) {
|
|
16505
|
+
if (!sink.argPositions.includes(arg.position)) {
|
|
16506
|
+
continue;
|
|
16440
16507
|
}
|
|
16441
|
-
|
|
16442
|
-
|
|
16508
|
+
}
|
|
16509
|
+
const candidateUses = arg.variable ? usesAtSink.filter((u) => u.variable === arg.variable) : usesAtSink;
|
|
16510
|
+
{
|
|
16511
|
+
for (const use of candidateUses) {
|
|
16512
|
+
if (use.def_id !== null) {
|
|
16443
16513
|
if (allTaintedDefIds.has(use.def_id)) {
|
|
16514
|
+
if (!arg.variable) {
|
|
16515
|
+
if (typeof arg.expression !== "string" || arg.expression.length === 0)
|
|
16516
|
+
continue;
|
|
16517
|
+
const re = new RegExp(`(?:^|[^A-Za-z0-9_$])${use.variable.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}(?:[^A-Za-z0-9_$]|$)`);
|
|
16518
|
+
if (!re.test(arg.expression))
|
|
16519
|
+
continue;
|
|
16520
|
+
}
|
|
16444
16521
|
const taintInfo = taintByDefId.get(use.def_id);
|
|
16445
16522
|
if (taintInfo) {
|
|
16446
16523
|
const isSanitized = checkSanitized(taintInfo.line, sink.line, sink.type, sanitizersByLine, {
|
|
@@ -16468,7 +16545,16 @@ function propagateTaint(graphOrDfg, callsOrSources, sourcesOrSinks, sinksOrSanit
|
|
|
16468
16545
|
}
|
|
16469
16546
|
}
|
|
16470
16547
|
}
|
|
16471
|
-
|
|
16548
|
+
const seen = new Set;
|
|
16549
|
+
const deduped = [];
|
|
16550
|
+
for (const f of flows) {
|
|
16551
|
+
const key = `${f.source.line}|${f.sink.line}|${f.sink.type}|${f.path.map((p) => p.variable).join(">")}`;
|
|
16552
|
+
if (seen.has(key))
|
|
16553
|
+
continue;
|
|
16554
|
+
seen.add(key);
|
|
16555
|
+
deduped.push(f);
|
|
16556
|
+
}
|
|
16557
|
+
return { taintedVars, flows: deduped, reachableSinks };
|
|
16472
16558
|
}
|
|
16473
16559
|
function findInitialTaint(sources, callsByLine, defsByLine) {
|
|
16474
16560
|
const tainted = [];
|
|
@@ -33393,6 +33479,12 @@ class TaintPropagationPass {
|
|
|
33393
33479
|
continue;
|
|
33394
33480
|
pushIfNew(f);
|
|
33395
33481
|
}
|
|
33482
|
+
if (ctx.language === "go" && typeof ctx.code === "string") {
|
|
33483
|
+
const goGlobalFlows = detectGoPackageGlobalFlows(ctx.code, calls, sources, sinks, constProp.unreachableLines) ?? [];
|
|
33484
|
+
for (const f of goGlobalFlows) {
|
|
33485
|
+
pushIfNew(f);
|
|
33486
|
+
}
|
|
33487
|
+
}
|
|
33396
33488
|
const sanitizedNames = constProp.sanitizedVars;
|
|
33397
33489
|
let finalFlows = sanitizedNames.size === 0 ? flows : flows.filter((f) => {
|
|
33398
33490
|
if (f.path.length === 0)
|
|
@@ -33649,6 +33741,113 @@ function isInJavaSanitizedMethod(code, types, sinkLine, sinkType) {
|
|
|
33649
33741
|
}
|
|
33650
33742
|
return false;
|
|
33651
33743
|
}
|
|
33744
|
+
function detectGoPackageGlobalFlows(code, _calls, sources, sinks, unreachableLines) {
|
|
33745
|
+
const flows = [];
|
|
33746
|
+
if (!code || sinks.length === 0)
|
|
33747
|
+
return flows;
|
|
33748
|
+
const lines = code.split(`
|
|
33749
|
+
`);
|
|
33750
|
+
const packageVars = new Set;
|
|
33751
|
+
let inVarBlock = false;
|
|
33752
|
+
for (const line of lines) {
|
|
33753
|
+
const stripped = line.replace(/\s+$/, "");
|
|
33754
|
+
if (!stripped)
|
|
33755
|
+
continue;
|
|
33756
|
+
if (/^[ \t]/.test(line)) {
|
|
33757
|
+
if (inVarBlock) {
|
|
33758
|
+
const m = stripped.match(/^\s*([A-Za-z_][A-Za-z0-9_]*)\s+/);
|
|
33759
|
+
if (m)
|
|
33760
|
+
packageVars.add(m[1]);
|
|
33761
|
+
if (/^\s*\)/.test(stripped))
|
|
33762
|
+
inVarBlock = false;
|
|
33763
|
+
}
|
|
33764
|
+
continue;
|
|
33765
|
+
}
|
|
33766
|
+
if (/^var\s*\(/.test(stripped)) {
|
|
33767
|
+
inVarBlock = true;
|
|
33768
|
+
continue;
|
|
33769
|
+
}
|
|
33770
|
+
if (inVarBlock && /^\)/.test(stripped)) {
|
|
33771
|
+
inVarBlock = false;
|
|
33772
|
+
continue;
|
|
33773
|
+
}
|
|
33774
|
+
const varOne = stripped.match(/^var\s+([A-Za-z_][A-Za-z0-9_]*)\b/);
|
|
33775
|
+
if (varOne)
|
|
33776
|
+
packageVars.add(varOne[1]);
|
|
33777
|
+
}
|
|
33778
|
+
if (packageVars.size === 0)
|
|
33779
|
+
return flows;
|
|
33780
|
+
const sourceVarNames = new Set;
|
|
33781
|
+
for (const s of sources) {
|
|
33782
|
+
if (typeof s.variable === "string" && s.variable.length > 0) {
|
|
33783
|
+
sourceVarNames.add(s.variable);
|
|
33784
|
+
}
|
|
33785
|
+
}
|
|
33786
|
+
const GO_SOURCE_RE = /\b(r\.(?:URL\.Query|Form|PostForm|Header|Cookie|Body)|mux\.Vars|c\.(?:Query|Param|PostForm|GetHeader|Cookie)|ctx\.(?:Query|Param|PostForm|GetHeader|Cookie))\b/;
|
|
33787
|
+
const writes = [];
|
|
33788
|
+
for (let i2 = 0;i2 < lines.length; i2++) {
|
|
33789
|
+
const line = lines[i2];
|
|
33790
|
+
if (unreachableLines.has(i2 + 1))
|
|
33791
|
+
continue;
|
|
33792
|
+
if (/:=/.test(line))
|
|
33793
|
+
continue;
|
|
33794
|
+
const m = line.match(/^\s*([A-Za-z_][A-Za-z0-9_]*)\s*=\s*(.+)$/);
|
|
33795
|
+
if (!m)
|
|
33796
|
+
continue;
|
|
33797
|
+
const [, lhs, rhs] = m;
|
|
33798
|
+
if (!packageVars.has(lhs))
|
|
33799
|
+
continue;
|
|
33800
|
+
let taintedBy = null;
|
|
33801
|
+
if (GO_SOURCE_RE.test(rhs)) {
|
|
33802
|
+
const src = sources.find((s) => s.line === i2 + 1);
|
|
33803
|
+
taintedBy = src ? { line: src.line, type: src.type } : { line: i2 + 1, type: "http_param" };
|
|
33804
|
+
} else {
|
|
33805
|
+
for (const v of sourceVarNames) {
|
|
33806
|
+
const re = new RegExp(`(?<![A-Za-z0-9_$])${v.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}(?![A-Za-z0-9_$])`);
|
|
33807
|
+
if (re.test(rhs)) {
|
|
33808
|
+
const src = sources.find((s) => s.variable === v);
|
|
33809
|
+
if (src) {
|
|
33810
|
+
taintedBy = { line: src.line, type: src.type };
|
|
33811
|
+
break;
|
|
33812
|
+
}
|
|
33813
|
+
}
|
|
33814
|
+
}
|
|
33815
|
+
}
|
|
33816
|
+
if (!taintedBy)
|
|
33817
|
+
continue;
|
|
33818
|
+
writes.push({ varName: lhs, line: i2 + 1, sourceLine: taintedBy.line, sourceType: taintedBy.type });
|
|
33819
|
+
}
|
|
33820
|
+
if (writes.length === 0)
|
|
33821
|
+
return flows;
|
|
33822
|
+
for (const sink of sinks) {
|
|
33823
|
+
if (unreachableLines.has(sink.line))
|
|
33824
|
+
continue;
|
|
33825
|
+
const sinkLineText = lines[sink.line - 1] ?? "";
|
|
33826
|
+
if (!sinkLineText)
|
|
33827
|
+
continue;
|
|
33828
|
+
for (const w of writes) {
|
|
33829
|
+
if (w.line >= sink.line)
|
|
33830
|
+
continue;
|
|
33831
|
+
const re = new RegExp(`(?<![A-Za-z0-9_$])${w.varName.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}(?![A-Za-z0-9_$])`);
|
|
33832
|
+
if (!re.test(sinkLineText))
|
|
33833
|
+
continue;
|
|
33834
|
+
flows.push({
|
|
33835
|
+
source_line: w.sourceLine,
|
|
33836
|
+
sink_line: sink.line,
|
|
33837
|
+
source_type: w.sourceType,
|
|
33838
|
+
sink_type: sink.type,
|
|
33839
|
+
path: [
|
|
33840
|
+
{ variable: w.varName, line: w.sourceLine, type: "source" },
|
|
33841
|
+
{ variable: w.varName, line: w.line, type: "assignment" },
|
|
33842
|
+
{ variable: w.varName, line: sink.line, type: "sink" }
|
|
33843
|
+
],
|
|
33844
|
+
confidence: 0.9,
|
|
33845
|
+
sanitized: false
|
|
33846
|
+
});
|
|
33847
|
+
}
|
|
33848
|
+
}
|
|
33849
|
+
return flows;
|
|
33850
|
+
}
|
|
33652
33851
|
function detectCollectionFlows(calls, sources, sinks, taintedVars, unreachableLines, code, types) {
|
|
33653
33852
|
const flows = [];
|
|
33654
33853
|
const callsByLine = new Map;
|
|
@@ -45451,7 +45650,7 @@ var colors = {
|
|
|
45451
45650
|
};
|
|
45452
45651
|
|
|
45453
45652
|
// src/version.ts
|
|
45454
|
-
var version = "3.
|
|
45653
|
+
var version = "3.182.0";
|
|
45455
45654
|
|
|
45456
45655
|
// src/formatters.ts
|
|
45457
45656
|
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.182.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.182.0"
|
|
70
70
|
},
|
|
71
71
|
"devDependencies": {
|
|
72
72
|
"@types/node": "^25.5.0",
|