cognium-dev 3.84.0 → 3.85.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 +177 -40
- package/package.json +2 -2
package/dist/cli.js
CHANGED
|
@@ -28957,6 +28957,11 @@ var TEST_FILENAME_RE = /(?:\.(?:test|spec)\.[cm]?[jt]sx?|_test\.go|_test\.py|Tes
|
|
|
28957
28957
|
function isTestFile(file) {
|
|
28958
28958
|
return TEST_PATH_RE3.test(file) || TEST_FILENAME_RE.test(file);
|
|
28959
28959
|
}
|
|
28960
|
+
var GENERATED_PATH_RE = /(?:^|[\\/])(?:gen|generated|build[\\/]generated|src[\\/](?:main|test)[\\/]generated|target[\\/]generated-sources|target[\\/]generated-test-sources|node_modules[\\/]\.cache)(?:[\\/]|$)/i;
|
|
28961
|
+
var GENERATED_FILENAME_RE = /__[ch]\.java$|\.pb\.go$|_pb2\.py$|\.generated\.[cm]?[jt]sx?$/i;
|
|
28962
|
+
function isGeneratedFile(file) {
|
|
28963
|
+
return GENERATED_PATH_RE.test(file) || GENERATED_FILENAME_RE.test(file);
|
|
28964
|
+
}
|
|
28960
28965
|
var PROVIDER_PATTERNS = [
|
|
28961
28966
|
{
|
|
28962
28967
|
name: "AWS access key",
|
|
@@ -29137,15 +29142,136 @@ function shannonEntropy(s) {
|
|
|
29137
29142
|
return h;
|
|
29138
29143
|
}
|
|
29139
29144
|
var CREDENTIAL_NAME_RE = /(?:key|secret|token|password|passwd|credential|api[_-]?key)/i;
|
|
29145
|
+
function findAnnotationLineRanges(code) {
|
|
29146
|
+
const lines = code.split(`
|
|
29147
|
+
`);
|
|
29148
|
+
const inAnnotation = new Set;
|
|
29149
|
+
const OPEN_RE = /(?:@[A-Za-z_]\w*(?:\.[A-Za-z_]\w*)*\s*\(|#\[)/g;
|
|
29150
|
+
for (let i2 = 0;i2 < lines.length; i2++) {
|
|
29151
|
+
OPEN_RE.lastIndex = 0;
|
|
29152
|
+
let m;
|
|
29153
|
+
while ((m = OPEN_RE.exec(lines[i2])) !== null) {
|
|
29154
|
+
const isRustAttr = m[0].startsWith("#[");
|
|
29155
|
+
const openCh = isRustAttr ? "[" : "(";
|
|
29156
|
+
const closeCh = isRustAttr ? "]" : ")";
|
|
29157
|
+
let depth = 1;
|
|
29158
|
+
let li = i2;
|
|
29159
|
+
let col = m.index + m[0].length;
|
|
29160
|
+
let lineBudget = 200;
|
|
29161
|
+
inAnnotation.add(li + 1);
|
|
29162
|
+
while (depth > 0 && li < lines.length && lineBudget > 0) {
|
|
29163
|
+
const ln = lines[li];
|
|
29164
|
+
let inStr = null;
|
|
29165
|
+
while (col < ln.length && depth > 0) {
|
|
29166
|
+
const ch = ln[col];
|
|
29167
|
+
if (inStr !== null) {
|
|
29168
|
+
if (ch === "\\") {
|
|
29169
|
+
col += 2;
|
|
29170
|
+
continue;
|
|
29171
|
+
}
|
|
29172
|
+
if (ch === inStr)
|
|
29173
|
+
inStr = null;
|
|
29174
|
+
} else if (ch === '"' || ch === "'" || ch === "`") {
|
|
29175
|
+
inStr = ch;
|
|
29176
|
+
} else if (ch === openCh) {
|
|
29177
|
+
depth++;
|
|
29178
|
+
} else if (ch === closeCh) {
|
|
29179
|
+
depth--;
|
|
29180
|
+
}
|
|
29181
|
+
col++;
|
|
29182
|
+
}
|
|
29183
|
+
if (depth > 0) {
|
|
29184
|
+
li++;
|
|
29185
|
+
col = 0;
|
|
29186
|
+
lineBudget--;
|
|
29187
|
+
if (li < lines.length)
|
|
29188
|
+
inAnnotation.add(li + 1);
|
|
29189
|
+
}
|
|
29190
|
+
}
|
|
29191
|
+
}
|
|
29192
|
+
}
|
|
29193
|
+
return inAnnotation;
|
|
29194
|
+
}
|
|
29195
|
+
function findStringArrayLineRanges(code) {
|
|
29196
|
+
const lines = code.split(`
|
|
29197
|
+
`);
|
|
29198
|
+
const inArray = new Set;
|
|
29199
|
+
const OPEN_RE = /=\s*([{\[])/g;
|
|
29200
|
+
const STR_LITERAL_COUNT_RE = /(["'`])(?:\\.|(?!\1).)*\1/g;
|
|
29201
|
+
for (let i2 = 0;i2 < lines.length; i2++) {
|
|
29202
|
+
OPEN_RE.lastIndex = 0;
|
|
29203
|
+
let m;
|
|
29204
|
+
while ((m = OPEN_RE.exec(lines[i2])) !== null) {
|
|
29205
|
+
const openCh = m[1];
|
|
29206
|
+
const closeCh = openCh === "{" ? "}" : "]";
|
|
29207
|
+
let depth = 1;
|
|
29208
|
+
let li = i2;
|
|
29209
|
+
let col = m.index + m[0].length;
|
|
29210
|
+
let lineBudget = 100;
|
|
29211
|
+
const spanLines = [li + 1];
|
|
29212
|
+
let spanText = "";
|
|
29213
|
+
while (depth > 0 && li < lines.length && lineBudget > 0) {
|
|
29214
|
+
const ln = lines[li];
|
|
29215
|
+
let inStr = null;
|
|
29216
|
+
const start2 = col;
|
|
29217
|
+
while (col < ln.length && depth > 0) {
|
|
29218
|
+
const ch = ln[col];
|
|
29219
|
+
if (inStr !== null) {
|
|
29220
|
+
if (ch === "\\") {
|
|
29221
|
+
col += 2;
|
|
29222
|
+
continue;
|
|
29223
|
+
}
|
|
29224
|
+
if (ch === inStr)
|
|
29225
|
+
inStr = null;
|
|
29226
|
+
} else if (ch === '"' || ch === "'" || ch === "`") {
|
|
29227
|
+
inStr = ch;
|
|
29228
|
+
} else if (ch === openCh) {
|
|
29229
|
+
depth++;
|
|
29230
|
+
} else if (ch === closeCh) {
|
|
29231
|
+
depth--;
|
|
29232
|
+
}
|
|
29233
|
+
col++;
|
|
29234
|
+
}
|
|
29235
|
+
spanText += ln.substring(start2, col) + `
|
|
29236
|
+
`;
|
|
29237
|
+
if (depth > 0) {
|
|
29238
|
+
li++;
|
|
29239
|
+
col = 0;
|
|
29240
|
+
lineBudget--;
|
|
29241
|
+
if (li < lines.length)
|
|
29242
|
+
spanLines.push(li + 1);
|
|
29243
|
+
}
|
|
29244
|
+
}
|
|
29245
|
+
STR_LITERAL_COUNT_RE.lastIndex = 0;
|
|
29246
|
+
let strCount = 0;
|
|
29247
|
+
while (STR_LITERAL_COUNT_RE.exec(spanText) !== null) {
|
|
29248
|
+
strCount++;
|
|
29249
|
+
if (strCount >= 3)
|
|
29250
|
+
break;
|
|
29251
|
+
}
|
|
29252
|
+
if (strCount >= 3) {
|
|
29253
|
+
for (const ln of spanLines)
|
|
29254
|
+
inArray.add(ln);
|
|
29255
|
+
}
|
|
29256
|
+
}
|
|
29257
|
+
}
|
|
29258
|
+
return inArray;
|
|
29259
|
+
}
|
|
29260
|
+
var FIELD_ASSIGN_RE = /(?:^|[\s,(])([A-Za-z_$][\w$]*)\s*[:=]\s*["'`]/;
|
|
29261
|
+
function extractEnclosingFieldName(lineText) {
|
|
29262
|
+
const m = FIELD_ASSIGN_RE.exec(lineText);
|
|
29263
|
+
return m ? m[1] : null;
|
|
29264
|
+
}
|
|
29140
29265
|
var TEST_CALL_RE = /\b(?:expect|assert|describe|it|test)\s*\(/;
|
|
29141
29266
|
var COMMENT_EXAMPLE_RE = /(?:\/\/|#)\s*(?:example|sample|test|fixture)/i;
|
|
29267
|
+
var FAST_CANDIDATE_PROBE_RE = /["'`][A-Za-z0-9+/=_-]{32,}["'`]/;
|
|
29142
29268
|
|
|
29143
29269
|
class ScanSecretsPass {
|
|
29144
29270
|
name = "scan-secrets";
|
|
29145
29271
|
category = "security";
|
|
29146
29272
|
run(ctx) {
|
|
29147
29273
|
const file = ctx.graph.ir.meta.file;
|
|
29148
|
-
if (isTestFile(file)) {
|
|
29274
|
+
if (isTestFile(file) || isGeneratedFile(file)) {
|
|
29149
29275
|
return { providerFindings: 0, entropyFindings: 0 };
|
|
29150
29276
|
}
|
|
29151
29277
|
const lines = ctx.code.split(`
|
|
@@ -29159,6 +29285,9 @@ class ScanSecretsPass {
|
|
|
29159
29285
|
seen.add(`${f.line}:${f.rule_id}`);
|
|
29160
29286
|
}
|
|
29161
29287
|
}
|
|
29288
|
+
const hasEntropyCandidate = FAST_CANDIDATE_PROBE_RE.test(ctx.code);
|
|
29289
|
+
const annotationLines = hasEntropyCandidate ? findAnnotationLineRanges(ctx.code) : new Set;
|
|
29290
|
+
const arrayLines = hasEntropyCandidate ? findStringArrayLineRanges(ctx.code) : new Set;
|
|
29162
29291
|
let providerFindings = 0;
|
|
29163
29292
|
let entropyFindings = 0;
|
|
29164
29293
|
for (let i2 = 0;i2 < lines.length; i2++) {
|
|
@@ -29218,45 +29347,52 @@ class ScanSecretsPass {
|
|
|
29218
29347
|
});
|
|
29219
29348
|
providerFindings += 1;
|
|
29220
29349
|
}
|
|
29221
|
-
|
|
29222
|
-
|
|
29223
|
-
|
|
29224
|
-
|
|
29225
|
-
|
|
29226
|
-
if (COMMENT_EXAMPLE_RE.test(lineText))
|
|
29227
|
-
continue;
|
|
29228
|
-
STRING_LITERAL_RE.lastIndex = 0;
|
|
29229
|
-
let match;
|
|
29230
|
-
while ((match = STRING_LITERAL_RE.exec(lineText)) !== null) {
|
|
29231
|
-
const value = match[2];
|
|
29232
|
-
if (!this.isCandidate(value))
|
|
29350
|
+
if (hasEntropyCandidate)
|
|
29351
|
+
for (let i2 = 0;i2 < lines.length; i2++) {
|
|
29352
|
+
const lineText = lines[i2];
|
|
29353
|
+
const lineNum = i2 + 1;
|
|
29354
|
+
if (TEST_CALL_RE.test(lineText))
|
|
29233
29355
|
continue;
|
|
29234
|
-
if (
|
|
29356
|
+
if (COMMENT_EXAMPLE_RE.test(lineText))
|
|
29235
29357
|
continue;
|
|
29236
|
-
|
|
29237
|
-
if (seen.has(key))
|
|
29358
|
+
if (annotationLines.has(lineNum))
|
|
29238
29359
|
continue;
|
|
29239
|
-
if (
|
|
29360
|
+
if (arrayLines.has(lineNum))
|
|
29240
29361
|
continue;
|
|
29241
|
-
|
|
29242
|
-
|
|
29243
|
-
|
|
29244
|
-
|
|
29245
|
-
|
|
29246
|
-
|
|
29247
|
-
|
|
29248
|
-
|
|
29249
|
-
|
|
29250
|
-
|
|
29251
|
-
|
|
29252
|
-
|
|
29253
|
-
|
|
29254
|
-
|
|
29255
|
-
|
|
29256
|
-
|
|
29257
|
-
|
|
29362
|
+
STRING_LITERAL_RE.lastIndex = 0;
|
|
29363
|
+
let match;
|
|
29364
|
+
while ((match = STRING_LITERAL_RE.exec(lineText)) !== null) {
|
|
29365
|
+
const value = match[2];
|
|
29366
|
+
if (!this.isCandidate(value))
|
|
29367
|
+
continue;
|
|
29368
|
+
if (value.length < 32)
|
|
29369
|
+
continue;
|
|
29370
|
+
if (!this.passesEntropyGate(value, lineText))
|
|
29371
|
+
continue;
|
|
29372
|
+
const key = `${lineNum}:hardcoded-credential-entropy`;
|
|
29373
|
+
if (seen.has(key))
|
|
29374
|
+
continue;
|
|
29375
|
+
if (seen.has(`${lineNum}:hardcoded-credential`))
|
|
29376
|
+
continue;
|
|
29377
|
+
seen.add(key);
|
|
29378
|
+
ctx.addFinding({
|
|
29379
|
+
id: `hardcoded-credential-entropy-${file}-${lineNum}`,
|
|
29380
|
+
pass: this.name,
|
|
29381
|
+
category: this.category,
|
|
29382
|
+
rule_id: "hardcoded-credential-entropy",
|
|
29383
|
+
cwe: "CWE-798",
|
|
29384
|
+
severity: "high",
|
|
29385
|
+
level: "warning",
|
|
29386
|
+
message: `Possible hardcoded secret: high-entropy string literal (${value.length} chars)`,
|
|
29387
|
+
file,
|
|
29388
|
+
line: lineNum,
|
|
29389
|
+
snippet: lineText.trim().substring(0, 120),
|
|
29390
|
+
fix: "If this is a credential, move it to environment / secrets manager. If it is sample data, add an `example` / `test` marker or disable this pass via `disabledPasses: ['scan-secrets']`.",
|
|
29391
|
+
evidence: { kind: "entropy", length: value.length }
|
|
29392
|
+
});
|
|
29393
|
+
entropyFindings += 1;
|
|
29394
|
+
}
|
|
29258
29395
|
}
|
|
29259
|
-
}
|
|
29260
29396
|
return { providerFindings, entropyFindings };
|
|
29261
29397
|
}
|
|
29262
29398
|
isCandidate(s) {
|
|
@@ -29277,11 +29413,12 @@ class ScanSecretsPass {
|
|
|
29277
29413
|
return true;
|
|
29278
29414
|
}
|
|
29279
29415
|
passesEntropyGate(value, lineText) {
|
|
29416
|
+
const fieldName = extractEnclosingFieldName(lineText);
|
|
29417
|
+
if (fieldName === null || !CREDENTIAL_NAME_RE.test(fieldName))
|
|
29418
|
+
return false;
|
|
29280
29419
|
const isHex = HEXISH_RE.test(value);
|
|
29281
|
-
const
|
|
29282
|
-
|
|
29283
|
-
const h = shannonEntropy(value);
|
|
29284
|
-
return h >= threshold;
|
|
29420
|
+
const threshold = isHex ? 3.3 : 4.1;
|
|
29421
|
+
return shannonEntropy(value) >= threshold;
|
|
29285
29422
|
}
|
|
29286
29423
|
}
|
|
29287
29424
|
|
|
@@ -33856,7 +33993,7 @@ var colors = {
|
|
|
33856
33993
|
};
|
|
33857
33994
|
|
|
33858
33995
|
// src/version.ts
|
|
33859
|
-
var version = "3.
|
|
33996
|
+
var version = "3.85.1";
|
|
33860
33997
|
|
|
33861
33998
|
// src/formatters.ts
|
|
33862
33999
|
var SINK_SEVERITY = {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cognium-dev",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.85.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",
|
|
@@ -65,7 +65,7 @@
|
|
|
65
65
|
"registry": "https://registry.npmjs.org/"
|
|
66
66
|
},
|
|
67
67
|
"dependencies": {
|
|
68
|
-
"circle-ir": "^3.
|
|
68
|
+
"circle-ir": "^3.85.1"
|
|
69
69
|
},
|
|
70
70
|
"devDependencies": {
|
|
71
71
|
"@types/node": "^25.5.0",
|