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.
Files changed (2) hide show
  1. package/dist/cli.js +177 -40
  2. 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
- for (let i2 = 0;i2 < lines.length; i2++) {
29222
- const lineText = lines[i2];
29223
- const lineNum = i2 + 1;
29224
- if (TEST_CALL_RE.test(lineText))
29225
- continue;
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 (!this.passesEntropyGate(value, lineText))
29356
+ if (COMMENT_EXAMPLE_RE.test(lineText))
29235
29357
  continue;
29236
- const key = `${lineNum}:hardcoded-credential-entropy`;
29237
- if (seen.has(key))
29358
+ if (annotationLines.has(lineNum))
29238
29359
  continue;
29239
- if (seen.has(`${lineNum}:hardcoded-credential`))
29360
+ if (arrayLines.has(lineNum))
29240
29361
  continue;
29241
- seen.add(key);
29242
- ctx.addFinding({
29243
- id: `hardcoded-credential-entropy-${file}-${lineNum}`,
29244
- pass: this.name,
29245
- category: this.category,
29246
- rule_id: "hardcoded-credential-entropy",
29247
- cwe: "CWE-798",
29248
- severity: "high",
29249
- level: "warning",
29250
- message: `Possible hardcoded secret: high-entropy string literal (${value.length} chars)`,
29251
- file,
29252
- line: lineNum,
29253
- snippet: lineText.trim().substring(0, 120),
29254
- 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']`.",
29255
- evidence: { kind: "entropy", length: value.length }
29256
- });
29257
- entropyFindings += 1;
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 boost = CREDENTIAL_NAME_RE.test(lineText) ? 0.2 : 0;
29282
- const threshold = isHex ? 3.5 - boost : 4.3 - boost;
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.84.0";
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.84.0",
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.84.0"
68
+ "circle-ir": "^3.85.1"
69
69
  },
70
70
  "devDependencies": {
71
71
  "@types/node": "^25.5.0",