aienvmp 0.1.28 → 0.1.29
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/CHANGELOG.md +6 -0
- package/README.md +1 -1
- package/package.json +1 -1
- package/src/actions.js +2 -1
- package/src/commands/plan.js +3 -0
- package/src/dependencies.js +25 -2
- package/src/render.js +6 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.1.29
|
|
4
|
+
|
|
5
|
+
- Added lightweight remediation priority scoring for vulnerable packages.
|
|
6
|
+
- Exposed priority level, score, and reasons in security summaries, plans, compact context, and dashboard rows.
|
|
7
|
+
- Kept scoring advisory-only so AI agents can choose safer next steps without blocking local operation.
|
|
8
|
+
|
|
3
9
|
## 0.1.28
|
|
4
10
|
|
|
5
11
|
- Linked vulnerable package summaries to the dependency snapshot when the package is directly declared.
|
package/README.md
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
|
|
11
11
|
It helps Codex, Claude, Gemini, and humans avoid silent Node, Python, package manager, dependency, Docker, and security drift.
|
|
12
12
|
|
|
13
|
-
Core loop: scan once, link runtime/dependency/security context, give AI a shared decision contract, and hand off safe next steps.
|
|
13
|
+
Core loop: scan once, link runtime/dependency/security context, give AI a shared decision contract with advisory priorities, and hand off safe next steps.
|
|
14
14
|
|
|
15
15
|
## Quick Start
|
|
16
16
|
|
package/package.json
CHANGED
package/src/actions.js
CHANGED
|
@@ -42,7 +42,8 @@ function securityActions(security = {}) {
|
|
|
42
42
|
|
|
43
43
|
const packageHints = packages.map((pkg) => {
|
|
44
44
|
const fix = pkg.fixVersions?.length ? `fix ${pkg.fixVersions.slice(0, 2).join(", ")}` : pkg.fixAvailable ? "fix available" : "review required";
|
|
45
|
-
|
|
45
|
+
const priority = pkg.remediationPriority ? `${pkg.remediationPriority.level}/${pkg.remediationPriority.score}, ` : "";
|
|
46
|
+
return `${pkg.name} (${priority}${pkg.severity}, ${fix})`;
|
|
46
47
|
});
|
|
47
48
|
|
|
48
49
|
return [action(
|
package/src/commands/plan.js
CHANGED
|
@@ -73,6 +73,8 @@ export function compactStepSummary(plan = {}) {
|
|
|
73
73
|
remediation: (plan.remediationSteps || []).slice(0, 3).map((item) => ({
|
|
74
74
|
package: item.package,
|
|
75
75
|
severity: item.severity,
|
|
76
|
+
priority: item.remediationPriority?.level || "low",
|
|
77
|
+
score: item.remediationPriority?.score || 0,
|
|
76
78
|
fixVersions: (item.fixVersions || []).slice(0, 3),
|
|
77
79
|
advisoryIds: (item.advisories || []).map((advisory) => advisory.id || advisory.title).filter(Boolean).slice(0, 3)
|
|
78
80
|
})),
|
|
@@ -175,6 +177,7 @@ function remediationSteps(security = {}) {
|
|
|
175
177
|
package: pkg.name,
|
|
176
178
|
scanner: pkg.scanner || "unknown",
|
|
177
179
|
severity: pkg.severity || "unknown",
|
|
180
|
+
remediationPriority: pkg.remediationPriority || { level: "low", score: 0, reasons: [] },
|
|
178
181
|
directDependency: pkg.directDependency === true,
|
|
179
182
|
dependency: pkg.dependency || null,
|
|
180
183
|
fixAvailable: pkg.fixAvailable === true,
|
package/src/dependencies.js
CHANGED
|
@@ -29,20 +29,43 @@ export function linkVulnerableDependencies(security = {}, snapshot = {}) {
|
|
|
29
29
|
...security,
|
|
30
30
|
topPackages: (security.topPackages || []).map((pkg) => {
|
|
31
31
|
const dependency = dependencyIndex.get(dependencyKey(scannerEcosystem(pkg.scanner), pkg.name));
|
|
32
|
+
const directDependency = Boolean(dependency);
|
|
32
33
|
return {
|
|
33
34
|
...pkg,
|
|
34
|
-
directDependency
|
|
35
|
+
directDependency,
|
|
35
36
|
dependency: dependency ? {
|
|
36
37
|
ecosystem: dependency.ecosystem,
|
|
37
38
|
manifest: dependency.manifest,
|
|
38
39
|
group: dependency.group,
|
|
39
40
|
version: dependency.version
|
|
40
|
-
} : null
|
|
41
|
+
} : null,
|
|
42
|
+
remediationPriority: remediationPriority(pkg, { directDependency })
|
|
41
43
|
};
|
|
42
44
|
})
|
|
43
45
|
};
|
|
44
46
|
}
|
|
45
47
|
|
|
48
|
+
export function remediationPriority(pkg = {}, context = {}) {
|
|
49
|
+
const severityScore = { critical: 90, high: 70, moderate: 45, low: 20, info: 5, unknown: 30 };
|
|
50
|
+
const severity = String(pkg.severity || "unknown").toLowerCase();
|
|
51
|
+
const reasons = [`severity:${severity}`];
|
|
52
|
+
let score = severityScore[severity] ?? severityScore.unknown;
|
|
53
|
+
if (context.directDependency) {
|
|
54
|
+
score += 15;
|
|
55
|
+
reasons.push("direct-dependency");
|
|
56
|
+
} else {
|
|
57
|
+
reasons.push("not-direct-in-snapshot");
|
|
58
|
+
}
|
|
59
|
+
if (pkg.fixAvailable === true || (Array.isArray(pkg.fixVersions) && pkg.fixVersions.length)) {
|
|
60
|
+
score += 5;
|
|
61
|
+
reasons.push("fix-available");
|
|
62
|
+
} else {
|
|
63
|
+
reasons.push("fix-review-needed");
|
|
64
|
+
}
|
|
65
|
+
const level = score >= 95 ? "urgent" : score >= 75 ? "high" : score >= 50 ? "medium" : "low";
|
|
66
|
+
return { level, score, reasons };
|
|
67
|
+
}
|
|
68
|
+
|
|
46
69
|
async function scanNodeDependencies(dir) {
|
|
47
70
|
const file = path.join(dir, "package.json");
|
|
48
71
|
if (!(await exists(file))) return { manifests: [], packages: [] };
|
package/src/render.js
CHANGED
|
@@ -212,8 +212,9 @@ function remediationLines(item) {
|
|
|
212
212
|
const fix = item.fixVersions?.length ? `fix ${item.fixVersions.join(", ")}` : item.fixAvailable ? "fix available" : "review required";
|
|
213
213
|
const advisories = (item.advisories || []).map((advisory) => advisory.id || advisory.title).filter(Boolean).slice(0, 2);
|
|
214
214
|
const dependency = item.directDependency && item.dependency ? `; declared in ${item.dependency.manifest} ${item.dependency.version}` : "; not found in dependency snapshot";
|
|
215
|
+
const priority = item.remediationPriority ? `priority ${item.remediationPriority.level}/${item.remediationPriority.score}` : "priority unscored";
|
|
215
216
|
return [
|
|
216
|
-
`- ${item.package}: ${item.severity}; ${fix}${dependency}${advisories.length ? `; advisories ${advisories.join(", ")}` : ""}`,
|
|
217
|
+
`- ${item.package}: ${item.severity}; ${priority}; ${fix}${dependency}${advisories.length ? `; advisories ${advisories.join(", ")}` : ""}`,
|
|
217
218
|
...item.steps.slice(0, 4).map((step) => ` - ${step}`)
|
|
218
219
|
];
|
|
219
220
|
}
|
|
@@ -287,7 +288,8 @@ const secPackages=sec.topPackages||[];
|
|
|
287
288
|
const securityFix=p=>p.fixVersions?.length?\`fix \${p.fixVersions.slice(0,3).join(', ')}\`:(p.fixAvailable?'fix available':'review required');
|
|
288
289
|
const securityRefs=p=>p.advisories?.length?\` - \${p.advisories.map(a=>a.id||a.title).filter(Boolean).slice(0,2).join(', ')}\`:'';
|
|
289
290
|
const securityDep=p=>p.directDependency&&p.dependency?\`<div class="path">\${esc(p.dependency.manifest)} / \${esc(p.dependency.group)} / \${esc(p.dependency.version)}</div>\`:'<div class="path">not found in dependency snapshot</div>';
|
|
290
|
-
const
|
|
291
|
+
const securityPriority=p=>p.remediationPriority?\`<code>\${esc(p.remediationPriority.level)} \${esc(p.remediationPriority.score)}</code> \`:'';
|
|
292
|
+
const securityHtml=sec.enabled?\`<table><tr><th>Total</th><td><code>\${esc(secSummary.total||0)}</code></td></tr><tr><th>Critical</th><td><code>\${esc(secSummary.critical||0)}</code></td></tr><tr><th>High</th><td><code>\${esc(secSummary.high||0)}</code></td></tr><tr><th>Moderate</th><td><code>\${esc(secSummary.moderate||0)}</code></td></tr><tr><th>Low</th><td><code>\${esc(secSummary.low||0)}</code></td></tr></table>\${secPackages.length?'<div class="timeline">'+secPackages.slice(0,5).map(p=>\`<div class="event"><time>\${esc(p.severity)}</time><div><b>\${esc(p.name)}</b> \${securityPriority(p)}\${esc(securityFix(p))}\${esc(securityRefs(p))}\${securityDep(p)}</div></div>\`).join('')+'</div>':'<div class="okline" style="margin-top:10px">No vulnerable packages reported.</div>'}\`:'<div class="okline">Security scan is off. Run <code>aienvmp sync --security</code> for read-only vulnerability summary.</div>';
|
|
291
293
|
const change=c=>c.type==='changed'?\`\${c.scope} \${c.key}: \${c.before} -> \${c.after}\`:\`\${c.scope} \${c.key}: \${c.type} \${c.after||c.before}\`;
|
|
292
294
|
const timelineLabel=t=>t.change?change(t.change):(t.summary||t.action||t.type||'recorded change');
|
|
293
295
|
const agentNames={agents:'Codex',claude:'Claude',gemini:'Gemini'};
|
|
@@ -465,12 +467,13 @@ function securityLines(security = {}) {
|
|
|
465
467
|
|
|
466
468
|
function securityPackageNote(pkg) {
|
|
467
469
|
const fix = pkg.fixVersions?.length ? `fix ${pkg.fixVersions.slice(0, 3).join(", ")}` : pkg.fixAvailable ? "fix available" : "review required";
|
|
470
|
+
const priority = pkg.remediationPriority ? `priority ${pkg.remediationPriority.level}/${pkg.remediationPriority.score}; ` : "";
|
|
468
471
|
const dependency = pkg.directDependency && pkg.dependency ? `; declared in ${pkg.dependency.manifest} ${pkg.dependency.version}` : "; not found in dependency snapshot";
|
|
469
472
|
const advisories = (pkg.advisories || [])
|
|
470
473
|
.map((item) => item.id || item.title)
|
|
471
474
|
.filter(Boolean)
|
|
472
475
|
.slice(0, 2);
|
|
473
|
-
return advisories.length ? `${fix}${dependency}; advisories ${advisories.join(", ")}` : `${fix}${dependency}`;
|
|
476
|
+
return advisories.length ? `${priority}${fix}${dependency}; advisories ${advisories.join(", ")}` : `${priority}${fix}${dependency}`;
|
|
474
477
|
}
|
|
475
478
|
|
|
476
479
|
function policyLines(policy) {
|