supply-chain-guard 5.2.23 → 5.2.24

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/README.md CHANGED
@@ -342,6 +342,31 @@ See [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines. The most impactful contri
342
342
 
343
343
  ## Changelog
344
344
 
345
+ ### v5.2.24 (2026-05-24)
346
+ **`RISK_TRAJECTORY_UNSTABLE` no longer flags monotone improvement as instability**
347
+
348
+ The risk-forecast engine used `Math.abs(slope) > 5` to detect "volatile risk", which conflated two opposite situations:
349
+
350
+ - Score rising fast (real degradation) → should fire
351
+ - Score falling fast (active remediation) → should NOT fire, that is exactly what we want
352
+ - Score bouncing back and forth (true volatility) → should fire
353
+
354
+ The v5.2.23 self-scan reported "slope -13.9/scan, highly volatile" after six consecutive releases each fixing real bugs - a strict monotone decrease being labelled as instability.
355
+
356
+ The detection is now split into orthogonal concerns:
357
+
358
+ - `RISK_TRAJECTORY_DEGRADING` (severity high): `slope > +5`, score consistently rising
359
+ - `RISK_TRAJECTORY_UNSTABLE` (severity medium): high stdev around the linear-fit trend **and** at least 2 direction reversals in the sequence (true oscillation, not just non-linear improvement)
360
+ - Fast improvement (`slope < -5` with no oscillation): silent, surfaced in the score itself
361
+
362
+ 5 new tests in `bugfix-v5_2_24.test.ts` verify:
363
+ - Strict monotone decrease (including the v5.2.18-v5.2.23 release trajectory) does NOT fire UNSTABLE
364
+ - Fast-rising score DOES fire DEGRADING
365
+ - Real oscillation (e.g. `[20, 80, 25, 75, 30, 70]`) DOES fire UNSTABLE
366
+ - Stable flat trajectory fires neither
367
+
368
+ Expected impact on the self-scan: drops the spurious `RISK_TRAJECTORY_UNSTABLE` finding. Score should fall from 17/MEDIUM to roughly 5-10/LOW.
369
+
345
370
  ### v5.2.23 (2026-05-24)
346
371
  **Fix `WORKFLOW_UNTRUSTED_ACTION_IN_RELEASE_PATH` false positive on `npm@latest`**
347
372
 
package/dist/cli.js CHANGED
@@ -20,7 +20,7 @@ const program = new commander_1.Command();
20
20
  program
21
21
  .name("supply-chain-guard")
22
22
  .description("Open-source supply-chain security scanner. Detects GlassWorm and similar malware campaigns in npm packages, PyPI packages, code repos, VS Code extensions, and project dependencies.")
23
- .version("5.2.23");
23
+ .version("5.2.24");
24
24
  // ── scan command ────────────────────────────────────────────────────
25
25
  program
26
26
  .command("scan")
package/dist/reporter.js CHANGED
@@ -55,7 +55,7 @@ function formatJson(report) {
55
55
  function formatText(report) {
56
56
  const lines = [];
57
57
  // ── layout constants ───────────────────────────────────────────────────────
58
- const VERSION = "5.2.23";
58
+ const VERSION = "5.2.24";
59
59
  const W = 76; // visible chars between "│ " and " │" (total line = 80)
60
60
  // ── ANSI helpers ───────────────────────────────────────────────────────────
61
61
  const stripAnsi = (s) => s.replace(/\x1b\[[0-9;]*m/g, "");
@@ -462,7 +462,7 @@ function formatSarif(report) {
462
462
  tool: {
463
463
  driver: {
464
464
  name: "supply-chain-guard",
465
- version: "5.2.23",
465
+ version: "5.2.24",
466
466
  informationUri: "https://github.com/homeofe/supply-chain-guard",
467
467
  rules,
468
468
  },
@@ -524,7 +524,7 @@ function formatSbom(report) {
524
524
  timestamp: report.timestamp,
525
525
  tools: {
526
526
  components: [
527
- { type: "application", name: "supply-chain-guard", version: "5.2.23" },
527
+ { type: "application", name: "supply-chain-guard", version: "5.2.24" },
528
528
  ],
529
529
  },
530
530
  component: {
@@ -676,7 +676,7 @@ footer{text-align:center;padding:24px;color:#94a3b8;font-size:13px}
676
676
  ` : ""}
677
677
 
678
678
  <footer>
679
- Generated by <a href="https://github.com/homeofe/supply-chain-guard">supply-chain-guard</a> v5.2.23
679
+ Generated by <a href="https://github.com/homeofe/supply-chain-guard">supply-chain-guard</a> v5.2.24
680
680
  </footer>
681
681
  </div>
682
682
  <script>
@@ -1 +1 @@
1
- {"version":3,"file":"risk-forecast.d.ts","sourceRoot":"","sources":["../src/risk-forecast.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAE5D;;GAEG;AACH,wBAAgB,YAAY,CAC1B,OAAO,EAAE,gBAAgB,EAAE,EAC3B,YAAY,EAAE,MAAM,GACnB,OAAO,EAAE,CA6CX"}
1
+ {"version":3,"file":"risk-forecast.d.ts","sourceRoot":"","sources":["../src/risk-forecast.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAE5D;;GAEG;AACH,wBAAgB,YAAY,CAC1B,OAAO,EAAE,gBAAgB,EAAE,EAC3B,YAAY,EAAE,MAAM,GACnB,OAAO,EAAE,CA6FX"}
@@ -38,14 +38,59 @@ function forecastRisk(history, currentScore) {
38
38
  recommendation: "Risk trajectory is heading toward critical. Proactive remediation recommended now.",
39
39
  });
40
40
  }
41
- if (Math.abs(slope) > 5) {
41
+ // v5.2.24: split trajectory analysis into direction (slope sign) and
42
+ // volatility (residuals from the linear fit). Previously a single
43
+ // |slope| > 5 check flagged "unstable" both for runaway-bad trends and
44
+ // for fast-improving trends - the latter is the opposite of unstable.
45
+ // The self-scan on v5.2.23 reported -13.9/scan as "highly volatile"
46
+ // even though every release that day strictly improved the score.
47
+ // Compute stdev of residuals from the linear regression line.
48
+ // High stdev means the score bounces unpredictably around the trend.
49
+ let sumSq = 0;
50
+ for (let i = 0; i < n; i++) {
51
+ const predicted = yMean + slope * (i - xMean);
52
+ const residual = recent[i].score - predicted;
53
+ sumSq += residual * residual;
54
+ }
55
+ const stdev = Math.sqrt(sumSq / n);
56
+ if (slope > 5) {
57
+ // Getting worse fast - real degradation worth flagging.
58
+ findings.push({
59
+ rule: "RISK_TRAJECTORY_DEGRADING",
60
+ description: `Risk score is rising fast (slope: +${slope.toFixed(1)}/scan). Security posture is worsening over the last ${n} scans.`,
61
+ severity: "high",
62
+ confidence: 0.6,
63
+ category: "trust",
64
+ recommendation: "Investigate the cause of the upward trend. Recent commits, dependency updates, or new findings should be triaged immediately.",
65
+ });
66
+ }
67
+ // Fast-improving trend (slope < -5) emits no finding - it is the
68
+ // outcome we want, surfaced silently in the score itself.
69
+ // Count direction changes in the sequence. A monotonic sequence (always
70
+ // dropping or always rising) has 0 direction changes even if the rate is
71
+ // non-linear (e.g. flat then a knee). True volatility requires the score
72
+ // to actually reverse direction, not just decelerate or accelerate.
73
+ let directionChanges = 0;
74
+ let lastDelta = 0;
75
+ for (let i = 1; i < n; i++) {
76
+ const delta = recent[i].score - recent[i - 1].score;
77
+ if (lastDelta !== 0 && delta !== 0 && Math.sign(delta) !== Math.sign(lastDelta)) {
78
+ directionChanges++;
79
+ }
80
+ if (delta !== 0)
81
+ lastDelta = delta;
82
+ }
83
+ if (stdev > 10 && directionChanges >= 2) {
84
+ // True volatility: high residuals AND multiple direction reversals.
85
+ // The score bounces back and forth instead of moving consistently
86
+ // in one direction. This is what "unstable" was meant to detect.
42
87
  findings.push({
43
88
  rule: "RISK_TRAJECTORY_UNSTABLE",
44
- description: `Risk score is highly volatile (slope: ${slope > 0 ? "+" : ""}${slope.toFixed(1)}/scan). Unstable risk indicates inconsistent security practices.`,
89
+ description: `Risk score is volatile (stdev ${stdev.toFixed(1)} around trend, ${directionChanges} direction reversals). Score bounces unpredictably across scans.`,
45
90
  severity: "medium",
46
91
  confidence: 0.5,
47
92
  category: "trust",
48
- recommendation: "Stabilize risk by implementing consistent policies and baseline enforcement.",
93
+ recommendation: "Stabilize risk by implementing consistent policies and baseline enforcement. Investigate why scores fluctuate.",
49
94
  });
50
95
  }
51
96
  return findings;
@@ -1 +1 @@
1
- {"version":3,"file":"risk-forecast.js","sourceRoot":"","sources":["../src/risk-forecast.ts"],"names":[],"mappings":";AAAA;;;;;GAKG;;AAOH,oCAgDC;AAnDD;;GAEG;AACH,SAAgB,YAAY,CAC1B,OAA2B,EAC3B,YAAoB;IAEpB,MAAM,QAAQ,GAAc,EAAE,CAAC;IAC/B,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO,QAAQ,CAAC;IAExC,kDAAkD;IAClD,MAAM,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC;IAClC,MAAM,CAAC,GAAG,MAAM,CAAC,MAAM,CAAC;IACxB,MAAM,KAAK,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC;IAC1B,MAAM,KAAK,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC;IAE1D,IAAI,SAAS,GAAG,CAAC,CAAC;IAClB,IAAI,WAAW,GAAG,CAAC,CAAC;IACpB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;QAC3B,SAAS,IAAI,CAAC,CAAC,GAAG,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,KAAK,GAAG,KAAK,CAAC,CAAC;QACrD,WAAW,IAAI,CAAC,CAAC,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC;IAClC,CAAC;IAED,MAAM,KAAK,GAAG,WAAW,KAAK,CAAC,CAAC,CAAC,CAAC,SAAS,GAAG,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC;IAE9D,yBAAyB;IACzB,MAAM,aAAa,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,GAAG,KAAK,GAAG,CAAC,CAAC,CAAC;IAE3D,IAAI,aAAa,GAAG,EAAE,IAAI,YAAY,IAAI,EAAE,EAAE,CAAC;QAC7C,QAAQ,CAAC,IAAI,CAAC;YACZ,IAAI,EAAE,wBAAwB;YAC9B,WAAW,EAAE,8CAA8C,aAAa,4DAA4D,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS;YAC7J,QAAQ,EAAE,MAAM;YAChB,UAAU,EAAE,GAAG;YACf,QAAQ,EAAE,OAAO;YACjB,cAAc,EAAE,oFAAoF;SACrG,CAAC,CAAC;IACL,CAAC;IAED,IAAI,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC;QACxB,QAAQ,CAAC,IAAI,CAAC;YACZ,IAAI,EAAE,0BAA0B;YAChC,WAAW,EAAE,yCAAyC,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,GAAG,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,kEAAkE;YAC/J,QAAQ,EAAE,QAAQ;YAClB,UAAU,EAAE,GAAG;YACf,QAAQ,EAAE,OAAO;YACjB,cAAc,EAAE,8EAA8E;SAC/F,CAAC,CAAC;IACL,CAAC;IAED,OAAO,QAAQ,CAAC;AAClB,CAAC"}
1
+ {"version":3,"file":"risk-forecast.js","sourceRoot":"","sources":["../src/risk-forecast.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "supply-chain-guard",
3
- "version": "5.2.23",
3
+ "version": "5.2.24",
4
4
  "description": "Open-source supply-chain security scanner for npm, PyPI, Cargo, Go, Docker, VS Code extensions, GitHub Actions, IaC and Solana C2. Detects GlassWorm, Shai-Hulud, PPE attacks, dependency confusion and 120+ malware indicators. Generates CycloneDX 1.6 SBOMs and verifies SLSA provenance.",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",