cognium-dev 3.90.1 → 3.93.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.
Files changed (3) hide show
  1. package/README.md +27 -0
  2. package/dist/cli.js +120 -75
  3. package/package.json +2 -2
package/README.md CHANGED
@@ -213,6 +213,33 @@ Found 2 vulnerability(ies) in 1 file(s)
213
213
 
214
214
  Use `-v` flag to see all scanned files including clean ones.
215
215
 
216
+ ### Output streams (stdout vs stderr)
217
+
218
+ `cognium-dev` follows the standard CLI convention: machine-readable payload on **stdout**, diagnostics on **stderr**.
219
+
220
+ | Output | Stream | Notes |
221
+ |--------|--------|-------|
222
+ | `--format json` document | **stdout** | Pure JSON starting at character 1. No banner, no preamble. Safe to pipe directly to `jq`, `json_pp`, etc. |
223
+ | `--format sarif` document | **stdout** | Pure SARIF 2.1.0 JSON. Same contract as `--format json`. |
224
+ | `--format text` report | **stdout** | The human-readable report (default). |
225
+ | Status lines (`Loaded config: …`, `Suppressed N finding(s) …`, `Results written to …`) | **stderr** | |
226
+ | Spinner animation and final status (`✔ Scanned N file(s)`) | **stderr** | |
227
+ | Error messages and usage hints | **stderr** | |
228
+ | Library log output (cross-file phase markers, budget warnings, etc.) | **stderr** | Silent by default; enable with `--log-level <level>` or `COGNIUM_LOG_LEVEL`. |
229
+ | Findings instrumentation (`CIRCLE_IR_INSTRUMENT_FINDINGS=1`) | **stderr** | JSONL `[finding] …` / `[findings-summary] …` lines. |
230
+
231
+ The stdout contract for `--format json` and `--format sarif` is **stable**: pure parseable payload, version included inside the JSON object (not as a stdout preamble). Consumers can safely pipe stdout to a parser without skip-the-first-line idioms.
232
+
233
+ ```bash
234
+ # Safe — stdout is pure JSON
235
+ cognium-dev scan ./src --format json | jq '.summary'
236
+
237
+ # Status lines and warnings on stderr, JSON on stdout
238
+ cognium-dev scan ./src --format json > results.json 2> scan.log
239
+ ```
240
+
241
+ If you previously relied on a `tail -n +2` or `split("\n",1)[1]` idiom against pre-3.89.2 builds, drop it — there is no longer a stdout banner to skip.
242
+
216
243
  ## Detected Vulnerabilities
217
244
 
218
245
  | Type | CWE | Severity | Description |
package/dist/cli.js CHANGED
@@ -17789,6 +17789,114 @@ function normalizeCondition(cond) {
17789
17789
  }
17790
17790
  return normalized;
17791
17791
  }
17792
+ // ../circle-ir/dist/utils/logger.js
17793
+ var LOG_LEVELS = {
17794
+ trace: 0,
17795
+ debug: 1,
17796
+ info: 2,
17797
+ warn: 3,
17798
+ error: 4,
17799
+ fatal: 5,
17800
+ silent: 6
17801
+ };
17802
+ var currentLevel = "silent";
17803
+ var customLogger = null;
17804
+ function shouldLog(level) {
17805
+ return LOG_LEVELS[level] >= LOG_LEVELS[currentLevel];
17806
+ }
17807
+ function setLogLevel(level) {
17808
+ currentLevel = level;
17809
+ }
17810
+ var logger = {
17811
+ trace: (msg, obj) => {
17812
+ if (customLogger) {
17813
+ customLogger.trace(msg, obj);
17814
+ return;
17815
+ }
17816
+ if (shouldLog("trace"))
17817
+ console.debug(obj ? `[TRACE] ${msg} ${JSON.stringify(obj)}` : `[TRACE] ${msg}`);
17818
+ },
17819
+ debug: (msg, obj) => {
17820
+ if (customLogger) {
17821
+ customLogger.debug(msg, obj);
17822
+ return;
17823
+ }
17824
+ if (shouldLog("debug"))
17825
+ console.debug(obj ? `[DEBUG] ${msg} ${JSON.stringify(obj)}` : `[DEBUG] ${msg}`);
17826
+ },
17827
+ info: (msg, obj) => {
17828
+ if (customLogger) {
17829
+ customLogger.info(msg, obj);
17830
+ return;
17831
+ }
17832
+ if (shouldLog("info"))
17833
+ console.error(obj ? `[INFO] ${msg} ${JSON.stringify(obj)}` : `[INFO] ${msg}`);
17834
+ },
17835
+ warn: (msg, obj) => {
17836
+ if (customLogger) {
17837
+ customLogger.warn(msg, obj);
17838
+ return;
17839
+ }
17840
+ if (shouldLog("warn"))
17841
+ console.warn(obj ? `[WARN] ${msg} ${JSON.stringify(obj)}` : `[WARN] ${msg}`);
17842
+ },
17843
+ error: (msg, obj) => {
17844
+ if (customLogger) {
17845
+ customLogger.error(msg, obj);
17846
+ return;
17847
+ }
17848
+ if (shouldLog("error"))
17849
+ console.error(obj ? `[ERROR] ${msg} ${JSON.stringify(obj)}` : `[ERROR] ${msg}`);
17850
+ },
17851
+ fatal: (msg, obj) => {
17852
+ if (customLogger) {
17853
+ customLogger.fatal(msg, obj);
17854
+ return;
17855
+ }
17856
+ if (shouldLog("fatal"))
17857
+ console.error(obj ? `[FATAL] ${msg} ${JSON.stringify(obj)}` : `[FATAL] ${msg}`);
17858
+ },
17859
+ isLevelEnabled: (level) => {
17860
+ return shouldLog(level);
17861
+ }
17862
+ };
17863
+
17864
+ // ../circle-ir/dist/analysis/per-file-finding-cap.js
17865
+ var DEFAULT_PER_FILE_FINDING_CAP = 1000;
17866
+ var SATURATED_FILE_RULE_ID = "saturated-file";
17867
+ function applyPerFileFindingCap(filePath, findings, cap) {
17868
+ if (cap <= 0)
17869
+ return findings;
17870
+ if (findings.length <= cap)
17871
+ return findings;
17872
+ const suppressedCount = findings.length;
17873
+ const byRule = {};
17874
+ const bySeverity = {};
17875
+ for (const f of findings) {
17876
+ byRule[f.rule_id] = (byRule[f.rule_id] ?? 0) + 1;
17877
+ bySeverity[f.severity] = (bySeverity[f.severity] ?? 0) + 1;
17878
+ }
17879
+ logger.warn(`File ${filePath} produced ${suppressedCount} findings (cap=${cap}); ` + "suppressing individual findings and emitting saturated-file advisory.", { file: filePath, suppressedCount, cap, byRule, bySeverity });
17880
+ const advisory = {
17881
+ id: `${SATURATED_FILE_RULE_ID}-${filePath}-1`,
17882
+ pass: SATURATED_FILE_RULE_ID,
17883
+ category: "maintainability",
17884
+ rule_id: SATURATED_FILE_RULE_ID,
17885
+ severity: "low",
17886
+ level: "note",
17887
+ message: `File suppressed: produced ${suppressedCount} findings, exceeding the ` + `per-file cap of ${cap}. This typically indicates cross-product noise, ` + "mislabelled sink class, or pathological generated code rather than a " + "legitimate detection burst. Individual findings dropped; re-run with " + "`perFileFindingCap: 0` to bypass the cap if the volume is intentional.",
17888
+ file: filePath,
17889
+ line: 1,
17890
+ evidence: {
17891
+ suppressed_count: suppressedCount,
17892
+ cap,
17893
+ by_rule: byRule,
17894
+ by_severity: bySeverity
17895
+ }
17896
+ };
17897
+ return [advisory];
17898
+ }
17899
+
17792
17900
  // ../circle-ir/dist/languages/registry.js
17793
17901
  class DefaultLanguageRegistry {
17794
17902
  plugins = new Map;
@@ -20800,78 +20908,6 @@ function registerBuiltinPlugins() {
20800
20908
  registerLanguage(new HtmlPlugin);
20801
20909
  registerLanguage(new GoPlugin);
20802
20910
  }
20803
- // ../circle-ir/dist/utils/logger.js
20804
- var LOG_LEVELS = {
20805
- trace: 0,
20806
- debug: 1,
20807
- info: 2,
20808
- warn: 3,
20809
- error: 4,
20810
- fatal: 5,
20811
- silent: 6
20812
- };
20813
- var currentLevel = "silent";
20814
- var customLogger = null;
20815
- function shouldLog(level) {
20816
- return LOG_LEVELS[level] >= LOG_LEVELS[currentLevel];
20817
- }
20818
- function setLogLevel(level) {
20819
- currentLevel = level;
20820
- }
20821
- var logger = {
20822
- trace: (msg, obj) => {
20823
- if (customLogger) {
20824
- customLogger.trace(msg, obj);
20825
- return;
20826
- }
20827
- if (shouldLog("trace"))
20828
- console.debug(obj ? `[TRACE] ${msg} ${JSON.stringify(obj)}` : `[TRACE] ${msg}`);
20829
- },
20830
- debug: (msg, obj) => {
20831
- if (customLogger) {
20832
- customLogger.debug(msg, obj);
20833
- return;
20834
- }
20835
- if (shouldLog("debug"))
20836
- console.debug(obj ? `[DEBUG] ${msg} ${JSON.stringify(obj)}` : `[DEBUG] ${msg}`);
20837
- },
20838
- info: (msg, obj) => {
20839
- if (customLogger) {
20840
- customLogger.info(msg, obj);
20841
- return;
20842
- }
20843
- if (shouldLog("info"))
20844
- console.error(obj ? `[INFO] ${msg} ${JSON.stringify(obj)}` : `[INFO] ${msg}`);
20845
- },
20846
- warn: (msg, obj) => {
20847
- if (customLogger) {
20848
- customLogger.warn(msg, obj);
20849
- return;
20850
- }
20851
- if (shouldLog("warn"))
20852
- console.warn(obj ? `[WARN] ${msg} ${JSON.stringify(obj)}` : `[WARN] ${msg}`);
20853
- },
20854
- error: (msg, obj) => {
20855
- if (customLogger) {
20856
- customLogger.error(msg, obj);
20857
- return;
20858
- }
20859
- if (shouldLog("error"))
20860
- console.error(obj ? `[ERROR] ${msg} ${JSON.stringify(obj)}` : `[ERROR] ${msg}`);
20861
- },
20862
- fatal: (msg, obj) => {
20863
- if (customLogger) {
20864
- customLogger.fatal(msg, obj);
20865
- return;
20866
- }
20867
- if (shouldLog("fatal"))
20868
- console.error(obj ? `[FATAL] ${msg} ${JSON.stringify(obj)}` : `[FATAL] ${msg}`);
20869
- },
20870
- isLevelEnabled: (level) => {
20871
- return shouldLog(level);
20872
- }
20873
- };
20874
-
20875
20911
  // ../circle-ir/dist/analysis/passes/cross-file-pass.js
20876
20912
  class CrossFilePass {
20877
20913
  run(projectGraph, sourceLines, options = {}) {
@@ -25001,6 +25037,9 @@ var TIER_1_METHOD_ANNOTATIONS = new Set([
25001
25037
  var TIER_1_CLASS_ANNOTATIONS = new Set([
25002
25038
  "RestController",
25003
25039
  "Controller",
25040
+ "Service",
25041
+ "Repository",
25042
+ "Component",
25004
25043
  "Path",
25005
25044
  "WebServlet",
25006
25045
  "ServerEndpoint",
@@ -25013,7 +25052,12 @@ var TIER_1_BY_SUPERTYPE = new Map([
25013
25052
  ["HandlerInterceptor", new Set(["preHandle", "postHandle", "afterCompletion"])],
25014
25053
  ["AsyncHandlerInterceptor", new Set(["preHandle", "postHandle", "afterCompletion", "afterConcurrentHandlingStarted"])],
25015
25054
  ["CommandLineRunner", new Set(["run"])],
25016
- ["ApplicationRunner", new Set(["run"])]
25055
+ ["ApplicationRunner", new Set(["run"])],
25056
+ ["SimpleChannelInboundHandler", new Set(["channelRead0", "messageReceived"])],
25057
+ ["ChannelInboundHandler", new Set(["channelRead", "channelReadComplete"])],
25058
+ ["ChannelInboundHandlerAdapter", new Set(["channelRead", "channelReadComplete"])],
25059
+ ["ChannelDuplexHandler", new Set(["channelRead", "channelReadComplete"])],
25060
+ ["NettyRequestProcessor", new Set(["process"])]
25017
25061
  ]);
25018
25062
  var TIER_3_CLASS_SUFFIXES = [
25019
25063
  "Util",
@@ -34339,6 +34383,7 @@ async function analyze(code, filePath, language, options = {}) {
34339
34383
  unresolvedItems: unresolved.length
34340
34384
  });
34341
34385
  emitFindingsInstrumentation(filePath, findings, taint);
34386
+ const cappedFindings = applyPerFileFindingCap(filePath, findings, options.perFileFindingCap ?? DEFAULT_PER_FILE_FINDING_CAP);
34342
34387
  return {
34343
34388
  meta,
34344
34389
  types,
@@ -34350,7 +34395,7 @@ async function analyze(code, filePath, language, options = {}) {
34350
34395
  exports,
34351
34396
  unresolved,
34352
34397
  enriched,
34353
- findings: findings.length > 0 ? findings : undefined,
34398
+ findings: cappedFindings.length > 0 ? cappedFindings : undefined,
34354
34399
  metrics: { file: filePath, metrics: metricValues },
34355
34400
  runtime_registrations: runtimeRegistrations.length > 0 ? runtimeRegistrations : undefined,
34356
34401
  parse_status: parseStatus
@@ -34517,7 +34562,7 @@ var colors = {
34517
34562
  };
34518
34563
 
34519
34564
  // src/version.ts
34520
- var version = "3.90.1";
34565
+ var version = "3.93.0";
34521
34566
 
34522
34567
  // src/formatters.ts
34523
34568
  var SINK_SEVERITY = {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cognium-dev",
3
- "version": "3.90.1",
3
+ "version": "3.93.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",
@@ -65,7 +65,7 @@
65
65
  "registry": "https://registry.npmjs.org/"
66
66
  },
67
67
  "dependencies": {
68
- "circle-ir": "^3.90.1"
68
+ "circle-ir": "^3.93.0"
69
69
  },
70
70
  "devDependencies": {
71
71
  "@types/node": "^25.5.0",