aienvmp 0.1.57 → 0.1.58
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/BUGFIXES.md +6 -0
- package/CHANGELOG.md +8 -0
- package/package.json +1 -1
- package/src/commands/sbom.js +1 -1
- package/src/commands/summary.js +2 -0
- package/src/dependencies.js +29 -0
- package/src/render.js +3 -1
package/BUGFIXES.md
CHANGED
|
@@ -136,6 +136,12 @@ Short record of bugs, fixes, and follow-up checks.
|
|
|
136
136
|
- Fix: `aiDependencyReview` now gives a top-level advisory review block with read order, safe actions, review targets, before-change commands, and after-change checkpoint guidance.
|
|
137
137
|
- Verification: regression tests cover the new SBOM block and schema metadata; local sync smoke confirmed generated `sbom.json` separates before-change commands from after-change checkpoint commands.
|
|
138
138
|
|
|
139
|
+
### SBOM dependency review was not visible across all review surfaces
|
|
140
|
+
|
|
141
|
+
- Issue: `aiDependencyReview` was available in standalone SBOM output, but the manifest-backed dashboard and compact summary did not surface the same dependency-review guidance.
|
|
142
|
+
- Fix: manifest generation now includes `lightSbom.aiDependencyReview`; the dashboard Light SBOM card and `summary.md` show the AI dependency review status and commands.
|
|
143
|
+
- Verification: regression tests cover generation, dashboard rendering, SBOM artifact reuse, and summary output; local sync smoke confirmed dashboard and SBOM artifacts include the block.
|
|
144
|
+
|
|
139
145
|
## Template
|
|
140
146
|
|
|
141
147
|
### Title
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.1.58
|
|
4
|
+
|
|
5
|
+
- Added `lightSbom.aiDependencyReview` during manifest generation so all generated artifacts share the same dependency-review block.
|
|
6
|
+
- Reused generated `aiDependencyReview` in standalone `.aienvmp/sbom.json`.
|
|
7
|
+
- Added an `AI Dependency Review` section to the dashboard Light SBOM card.
|
|
8
|
+
- Added the AI dependency review status and first command to `.aienvmp/summary.md`.
|
|
9
|
+
- Added regression tests for light SBOM generation, dashboard rendering, SBOM artifact output, and summary output.
|
|
10
|
+
|
|
3
11
|
## 0.1.57
|
|
4
12
|
|
|
5
13
|
- Added `aiDependencyReview` to the standalone `.aienvmp/sbom.json` artifact.
|
package/package.json
CHANGED
package/src/commands/sbom.js
CHANGED
|
@@ -36,7 +36,7 @@ export function buildSbomArtifact(manifest = {}) {
|
|
|
36
36
|
topRisk: (lightSbom.topRisk || []).slice(0, 20),
|
|
37
37
|
packageManagerPolicy: lightSbom.packageManagerPolicy || {},
|
|
38
38
|
dependencyChangeHints: (lightSbom.dependencyChangeHints || []).slice(0, 20),
|
|
39
|
-
aiDependencyReview: aiDependencyReview(lightSbom),
|
|
39
|
+
aiDependencyReview: lightSbom.aiDependencyReview || aiDependencyReview(lightSbom),
|
|
40
40
|
aiUse: {
|
|
41
41
|
purpose: "Standalone AI-readable light SBOM artifact.",
|
|
42
42
|
readBefore: "Dependency changes, vulnerability remediation, release review, or shared AI handoff.",
|
package/src/commands/summary.js
CHANGED
|
@@ -55,6 +55,7 @@ export function renderSummary(status = {}, manifest = {}) {
|
|
|
55
55
|
const aiReadiness = status.aiReadiness || {};
|
|
56
56
|
const aiSignals = toList(aiReadiness.signals).slice(0, 5);
|
|
57
57
|
const aiNext = aiReadiness.next || "Run aienvmp context --json for details.";
|
|
58
|
+
const aiDependencyReview = manifest.lightSbom?.aiDependencyReview || {};
|
|
58
59
|
|
|
59
60
|
return [
|
|
60
61
|
"# aienvmp summary",
|
|
@@ -85,6 +86,7 @@ export function renderSummary(status = {}, manifest = {}) {
|
|
|
85
86
|
"",
|
|
86
87
|
`- source: ${manifest.lightSbom?.source?.dependencies || "project manifests"}`,
|
|
87
88
|
`- confidence: transitive ${manifest.lightSbom?.confidence?.transitiveDependencies || "not-resolved"}`,
|
|
89
|
+
`- AI dependency review: ${aiDependencyReview.status || "unknown"} / ${aiDependencyReview.beforeDependencyChange?.[0] || "aienvmp sbom --json"}`,
|
|
88
90
|
`- signals: ${riskSignals.length ? riskSignals.join("; ") : "none"}`,
|
|
89
91
|
`- verify: ${sbomRisk.next || "Use a dedicated scanner for security decisions."}`,
|
|
90
92
|
"",
|
package/src/dependencies.js
CHANGED
|
@@ -111,6 +111,7 @@ export function buildLightSbom(snapshot = {}, security = {}) {
|
|
|
111
111
|
riskSummary: lightSbomRiskSummary({ packages, security, directVulnerable, transitiveOrUnmatched, topRisk, lockfiles: snapshot.lockfiles || [] }),
|
|
112
112
|
packageManagerPolicy: packageManagerPolicy(snapshot.lockfiles || []),
|
|
113
113
|
dependencyChangeHints: dependencyChangeHints(packages, topRisk, snapshot.lockfiles || []),
|
|
114
|
+
aiDependencyReview: aiDependencyReview({ topRisk, lockfiles: snapshot.lockfiles || [] }),
|
|
114
115
|
aiUse: {
|
|
115
116
|
beforeDependencyChanges: "Read lightSbom.summary and lightSbom.topRisk before changing dependencies.",
|
|
116
117
|
securityMode: security.enabled ? "scanner-summary" : "scanner-off",
|
|
@@ -120,6 +121,34 @@ export function buildLightSbom(snapshot = {}, security = {}) {
|
|
|
120
121
|
};
|
|
121
122
|
}
|
|
122
123
|
|
|
124
|
+
function aiDependencyReview({ topRisk = [], lockfiles = [] } = {}) {
|
|
125
|
+
const risk = lightSbomRiskSummary({ topRisk, lockfiles });
|
|
126
|
+
const review = ["urgent", "high", "medium"].includes(risk.level) || packageManagerPolicy(lockfiles).status === "review-required";
|
|
127
|
+
return {
|
|
128
|
+
status: review ? "review" : "ready",
|
|
129
|
+
mode: "advisory",
|
|
130
|
+
readFirst: ["riskSummary", "dependencyChangeHints", "packageManagerPolicy", "topRisk"],
|
|
131
|
+
reviewTargets: risk.reviewTargets,
|
|
132
|
+
safeActions: [
|
|
133
|
+
"read SBOM, status, summary, context, and dependency manifests before dependency changes",
|
|
134
|
+
"plan remediation without installing, upgrading, downgrading, or switching package managers",
|
|
135
|
+
"record intent before dependency or lockfile changes when another AI may be working"
|
|
136
|
+
],
|
|
137
|
+
beforeDependencyChange: [
|
|
138
|
+
"aienvmp sync --security",
|
|
139
|
+
"aienvmp intent --actor agent:id --action dependency-review --target dependency",
|
|
140
|
+
"aienvmp plan --write"
|
|
141
|
+
],
|
|
142
|
+
afterDependencyChange: [
|
|
143
|
+
"run the narrowest relevant project validation",
|
|
144
|
+
"aienvmp checkpoint --actor agent:id --summary dependency-change --target dependency"
|
|
145
|
+
],
|
|
146
|
+
rule: review
|
|
147
|
+
? "Review SBOM risk and package manager policy before dependency changes; default behavior is advisory and non-blocking."
|
|
148
|
+
: "No light SBOM signal requires action; still record intent before dependency or lockfile changes."
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
|
|
123
152
|
export function lightSbomRiskSummary({ packages = [], security = {}, directVulnerable = [], transitiveOrUnmatched = [], topRisk = [], lockfiles = [] } = {}) {
|
|
124
153
|
const signals = [];
|
|
125
154
|
let score = 0;
|
package/src/render.js
CHANGED
|
@@ -430,10 +430,12 @@ const pmPolicy=lightSbom.packageManagerPolicy||{};
|
|
|
430
430
|
const topRisk=lightSbom.topRisk||[];
|
|
431
431
|
const riskSummary=lightSbom.riskSummary||{};
|
|
432
432
|
const dependencyHints=lightSbom.dependencyChangeHints||[];
|
|
433
|
+
const aiDependencyReview=lightSbom.aiDependencyReview||{};
|
|
433
434
|
const dependencyHintsHtml=dependencyHints.length?'<div class="timeline">'+dependencyHints.slice(0,5).map(h=>\`<div class="event"><time>\${esc(h.ecosystem||'deps')}</time><div><b>\${esc(h.manifest)}</b> <code>\${esc(h.manager||'unknown')}</code> \${esc(h.packages||0)} packages\${h.riskPackages?.length?\`<div class="path">risk: \${esc(h.riskPackages.map(p=>p.name).join(', '))}</div>\`:''}<div class="path">\${esc((h.groups||[]).join(', ')||'no groups')}\${h.lockfiles?.length?\` / lockfiles: \${esc(h.lockfiles.map(l=>l.file).join(', '))}\`:''}</div></div></div>\`).join('')+'</div>':'<div class="okline">No dependency change hints available.</div>';
|
|
434
435
|
const pmPolicyHtml='<table><tr><th>Status</th><td><code>'+esc(pmPolicy.status||'no-lockfile')+'</code></td></tr><tr><th>Guidance</th><td>'+esc(pmPolicy.guidance||'No lockfile policy detected.')+'</td></tr></table>';
|
|
435
436
|
const riskSummaryHtml=riskSummary.level?\`<table><tr><th>Level</th><td><code>\${esc(riskSummary.level)}</code> \${esc(riskSummary.score||0)}</td></tr><tr><th>Scanner</th><td><code>\${esc(riskSummary.scanner||'unknown')}</code></td></tr><tr><th>Next</th><td>\${esc(riskSummary.next||'No SBOM action required.')}</td></tr><tr><th>Targets</th><td>\${esc((riskSummary.reviewTargets||[]).join(', ')||'none')}</td></tr></table>\${riskSummary.signals?.length?'<div class="timeline">'+riskSummary.signals.slice(0,5).map(s=>\`<div class="event"><time>risk</time><div>\${esc(s)}</div></div>\`).join('')+'</div>':''}\`:'<div class="okline">No risk summary available.</div>';
|
|
436
|
-
const
|
|
437
|
+
const aiDependencyReviewHtml=aiDependencyReview.status?\`<table><tr><th>Status</th><td><code>\${esc(aiDependencyReview.status)}</code> \${esc(aiDependencyReview.mode||'advisory')}</td></tr><tr><th>Read first</th><td>\${esc((aiDependencyReview.readFirst||[]).join(', ')||'riskSummary')}</td></tr><tr><th>Targets</th><td>\${esc((aiDependencyReview.reviewTargets||[]).join(', ')||'none')}</td></tr><tr><th>Before</th><td><code>\${esc((aiDependencyReview.beforeDependencyChange||[]).slice(0,3).join(' -> ')||'aienvmp intent --actor agent:id --action dependency-review --target dependency')}</code></td></tr><tr><th>After</th><td><code>\${esc((aiDependencyReview.afterDependencyChange||[]).slice(-1)[0]||'aienvmp checkpoint --actor agent:id --summary dependency-change --target dependency')}</code></td></tr></table><div class="path">\${esc(aiDependencyReview.rule||'Dependency review is advisory and non-blocking.')}</div>\`:'<div class="okline">No AI dependency review block available. Run <code>aienvmp sync</code>.</div>';
|
|
438
|
+
const lightSbomHtml=\`<table><tr><th>Packages</th><td><code>\${esc(lightSbomSummary.packages||0)}</code></td></tr><tr><th>Vulnerabilities</th><td><code>\${esc(lightSbomSummary.vulnerabilities||0)}</code></td></tr><tr><th>Direct vulnerable</th><td><code>\${esc(lightSbomSummary.directVulnerablePackages||0)}</code></td></tr><tr><th>Manifests</th><td><code>\${esc((lightSbomSummary.manifests||[]).join(', ')||'none')}</code></td></tr><tr><th>Lockfiles</th><td><code>\${esc((lightSbomSummary.lockfiles||[]).map(l=>l.file).join(', ')||'none')}</code></td></tr></table><h3 style="margin-top:12px">AI Dependency Review</h3>\${aiDependencyReviewHtml}<h3 style="margin-top:12px">Risk summary</h3>\${riskSummaryHtml}<h3 style="margin-top:12px">Package manager policy</h3>\${pmPolicyHtml}\${topRisk.length?'<div class="timeline">'+topRisk.slice(0,5).map(p=>\`<div class="event"><time>\${esc(p.priority)}</time><div><b>\${esc(p.name)}</b> \${esc(p.severity)} \${p.directDependency?'<code>direct</code>':'<code>transitive</code>'}<div class="path">\${esc(p.manifest||p.ecosystem)} \${esc(p.version||'')}</div></div></div>\`).join('')+'</div>':'<div class="okline">No high-risk package summary in the current light SBOM.</div>'}<h3 style="margin-top:12px">Dependency change hints</h3>\${dependencyHintsHtml}\`;
|
|
437
439
|
const sec=manifest.security||{};
|
|
438
440
|
const secSummary=sec.summary||{total:0,critical:0,high:0,moderate:0,low:0,info:0};
|
|
439
441
|
const secPackages=sec.topPackages||[];
|