autoremediator 0.3.0 → 0.4.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.
- package/README.md +13 -0
- package/dist/{chunk-URM53GSJ.js → chunk-GBOD3DV6.js} +357 -32
- package/dist/chunk-GBOD3DV6.js.map +1 -0
- package/dist/cli.js +8 -8
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +35 -9
- package/dist/index.js +1 -1
- package/dist/mcp/server.d.ts +21 -6
- package/dist/mcp/server.js +8 -4
- package/dist/mcp/server.js.map +1 -1
- package/dist/openapi/server.d.ts +52 -7
- package/dist/openapi/server.js +28 -9
- package/dist/openapi/server.js.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-URM53GSJ.js.map +0 -1
package/README.md
CHANGED
|
@@ -56,6 +56,17 @@ Primary sources:
|
|
|
56
56
|
- [GitHub Advisory Database](https://github.com/advisories)
|
|
57
57
|
- [NVD](https://nvd.nist.gov)
|
|
58
58
|
|
|
59
|
+
Supplemental enrichment and prioritization sources:
|
|
60
|
+
|
|
61
|
+
- [CISA KEV](https://www.cisa.gov/known-exploited-vulnerabilities-catalog)
|
|
62
|
+
- [FIRST EPSS](https://www.first.org/epss/)
|
|
63
|
+
- [CVE Services](https://www.cve.org/)
|
|
64
|
+
- [GitLab Advisory Database](https://advisories.gitlab.com)
|
|
65
|
+
- [CERT/CC Vulnerability Notes](https://www.kb.cert.org/vuls/)
|
|
66
|
+
- [deps.dev](https://deps.dev)
|
|
67
|
+
- [OpenSSF Scorecard](https://securityscorecards.dev)
|
|
68
|
+
- Optional vendor and commercial feeds via environment-configured connectors
|
|
69
|
+
|
|
59
70
|
Trust controls:
|
|
60
71
|
|
|
61
72
|
- correlate advisory data with local dependency inventory before action
|
|
@@ -70,6 +81,8 @@ Trust controls:
|
|
|
70
81
|
- MCP: AI host integrations
|
|
71
82
|
- OpenAPI: service-based automation
|
|
72
83
|
|
|
84
|
+
Public API naming canon: `runTests`, `policy`, `evidence`, `patchCount`, and `patchesDir`.
|
|
85
|
+
|
|
73
86
|
## Documentation
|
|
74
87
|
|
|
75
88
|
- [Docs Home](https://rawlings.github.io/autoremediator/)
|
|
@@ -64,6 +64,19 @@ function getNvdConfig() {
|
|
|
64
64
|
function getGitHubToken() {
|
|
65
65
|
return process.env.GITHUB_TOKEN;
|
|
66
66
|
}
|
|
67
|
+
function getIntelligenceSourceConfig() {
|
|
68
|
+
return {
|
|
69
|
+
gitLabAdvisoryApi: process.env.AUTOREMEDIATOR_GITLAB_ADVISORY_API ?? "https://advisories.gitlab.com/api/v1/advisories",
|
|
70
|
+
certCcSearchUrl: process.env.AUTOREMEDIATOR_CERTCC_SEARCH_URL ?? "https://www.kb.cert.org/vuls/search",
|
|
71
|
+
epssApi: process.env.AUTOREMEDIATOR_EPSS_API ?? "https://api.first.org/data/v1/epss",
|
|
72
|
+
cveServicesApi: process.env.AUTOREMEDIATOR_CVE_SERVICES_API ?? "https://cveawg.mitre.org/api/cve",
|
|
73
|
+
depsDevApi: process.env.AUTOREMEDIATOR_DEPSDEV_API ?? "https://api.deps.dev/v3",
|
|
74
|
+
scorecardApi: process.env.AUTOREMEDIATOR_SCORECARD_API ?? "https://api.securityscorecards.dev",
|
|
75
|
+
vendorAdvisoryFeeds: (process.env.AUTOREMEDIATOR_VENDOR_ADVISORY_FEEDS ?? "").split(",").map((v) => v.trim()).filter(Boolean),
|
|
76
|
+
commercialFeeds: (process.env.AUTOREMEDIATOR_COMMERCIAL_FEEDS ?? "").split(",").map((v) => v.trim()).filter(Boolean),
|
|
77
|
+
commercialFeedToken: process.env.AUTOREMEDIATOR_COMMERCIAL_FEED_TOKEN
|
|
78
|
+
};
|
|
79
|
+
}
|
|
67
80
|
|
|
68
81
|
// src/platform/package-manager.ts
|
|
69
82
|
import { existsSync } from "fs";
|
|
@@ -343,6 +356,288 @@ async function enrichWithNvd(details) {
|
|
|
343
356
|
return details;
|
|
344
357
|
}
|
|
345
358
|
|
|
359
|
+
// src/intelligence/sources/cisa-kev.ts
|
|
360
|
+
var CISA_KEV_URL = "https://www.cisa.gov/sites/default/files/feeds/known_exploited_vulnerabilities.json";
|
|
361
|
+
async function fetchCisaKevFeed() {
|
|
362
|
+
try {
|
|
363
|
+
const res = await fetch(CISA_KEV_URL, {
|
|
364
|
+
headers: { Accept: "application/json" }
|
|
365
|
+
});
|
|
366
|
+
if (!res.ok) return void 0;
|
|
367
|
+
return await res.json();
|
|
368
|
+
} catch {
|
|
369
|
+
return void 0;
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
function findKevEntry(feed, cveId) {
|
|
373
|
+
if (!feed?.vulnerabilities?.length) return void 0;
|
|
374
|
+
const normalized = cveId.toUpperCase();
|
|
375
|
+
return feed.vulnerabilities.find((v) => v.cveID.toUpperCase() === normalized);
|
|
376
|
+
}
|
|
377
|
+
async function enrichWithCisaKev(details) {
|
|
378
|
+
const feed = await fetchCisaKevFeed();
|
|
379
|
+
const entry = findKevEntry(feed, details.id);
|
|
380
|
+
if (!entry) return details;
|
|
381
|
+
details.kev = {
|
|
382
|
+
knownExploited: true,
|
|
383
|
+
dateAdded: entry.dateAdded,
|
|
384
|
+
dueDate: entry.dueDate,
|
|
385
|
+
requiredAction: entry.requiredAction,
|
|
386
|
+
knownRansomwareCampaignUse: entry.knownRansomwareCampaignUse
|
|
387
|
+
};
|
|
388
|
+
if (!details.references.includes(CISA_KEV_URL)) {
|
|
389
|
+
details.references.push(CISA_KEV_URL);
|
|
390
|
+
}
|
|
391
|
+
return details;
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
// src/intelligence/sources/epss.ts
|
|
395
|
+
async function fetchEpss(cveId) {
|
|
396
|
+
const { epssApi } = getIntelligenceSourceConfig();
|
|
397
|
+
if (!epssApi) return void 0;
|
|
398
|
+
try {
|
|
399
|
+
const url = new URL(epssApi);
|
|
400
|
+
url.searchParams.set("cve", cveId);
|
|
401
|
+
const res = await fetch(url.toString(), {
|
|
402
|
+
headers: { Accept: "application/json" }
|
|
403
|
+
});
|
|
404
|
+
if (!res.ok) return void 0;
|
|
405
|
+
const body = await res.json();
|
|
406
|
+
return body.data?.[0];
|
|
407
|
+
} catch {
|
|
408
|
+
return void 0;
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
async function enrichWithEpss(details) {
|
|
412
|
+
const row = await fetchEpss(details.id);
|
|
413
|
+
if (!row) return details;
|
|
414
|
+
const score = Number.parseFloat(row.epss);
|
|
415
|
+
const percentile = Number.parseFloat(row.percentile);
|
|
416
|
+
if (!Number.isFinite(score) || !Number.isFinite(percentile)) {
|
|
417
|
+
return details;
|
|
418
|
+
}
|
|
419
|
+
details.epss = {
|
|
420
|
+
score,
|
|
421
|
+
percentile,
|
|
422
|
+
date: row.date
|
|
423
|
+
};
|
|
424
|
+
return details;
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
// src/intelligence/sources/cve-services.ts
|
|
428
|
+
function pickEnglishDescription(container) {
|
|
429
|
+
if (!container?.descriptions?.length) return void 0;
|
|
430
|
+
const en = container.descriptions.find((d) => d.lang === "en" && d.value);
|
|
431
|
+
return (en?.value ?? container.descriptions[0]?.value)?.trim() || void 0;
|
|
432
|
+
}
|
|
433
|
+
function collectReferences(record) {
|
|
434
|
+
const refs = /* @__PURE__ */ new Set();
|
|
435
|
+
const cnaRefs = record.containers?.cna?.references ?? [];
|
|
436
|
+
const adpRefs = (record.containers?.adp ?? []).flatMap((c) => c.references ?? []);
|
|
437
|
+
for (const ref of [...cnaRefs, ...adpRefs]) {
|
|
438
|
+
if (ref.url) refs.add(ref.url);
|
|
439
|
+
}
|
|
440
|
+
return Array.from(refs);
|
|
441
|
+
}
|
|
442
|
+
async function fetchCveServicesRecord(cveId) {
|
|
443
|
+
const { cveServicesApi } = getIntelligenceSourceConfig();
|
|
444
|
+
if (!cveServicesApi) return void 0;
|
|
445
|
+
try {
|
|
446
|
+
const res = await fetch(`${cveServicesApi}/${encodeURIComponent(cveId)}`, {
|
|
447
|
+
headers: { Accept: "application/json" }
|
|
448
|
+
});
|
|
449
|
+
if (!res.ok) return void 0;
|
|
450
|
+
return await res.json();
|
|
451
|
+
} catch {
|
|
452
|
+
return void 0;
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
async function enrichWithCveServices(details) {
|
|
456
|
+
const record = await fetchCveServicesRecord(details.id);
|
|
457
|
+
if (!record) return details;
|
|
458
|
+
const summary = pickEnglishDescription(record.containers?.cna);
|
|
459
|
+
if (summary && (!details.summary || details.summary.includes("No summary available"))) {
|
|
460
|
+
details.summary = summary;
|
|
461
|
+
}
|
|
462
|
+
const refs = collectReferences(record);
|
|
463
|
+
if (refs.length > 0) {
|
|
464
|
+
const merged = /* @__PURE__ */ new Set([...details.references, ...refs]);
|
|
465
|
+
details.references = Array.from(merged);
|
|
466
|
+
}
|
|
467
|
+
details.intelligence = {
|
|
468
|
+
...details.intelligence ?? {},
|
|
469
|
+
cveServicesEnriched: true
|
|
470
|
+
};
|
|
471
|
+
return details;
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
// src/intelligence/sources/gitlab-advisory.ts
|
|
475
|
+
function advisoryMatchesCve(advisory, cveId) {
|
|
476
|
+
const normalized = cveId.toUpperCase();
|
|
477
|
+
return (advisory.identifiers ?? []).some(
|
|
478
|
+
(id) => id.type?.toUpperCase() === "CVE" && id.value?.toUpperCase() === normalized
|
|
479
|
+
);
|
|
480
|
+
}
|
|
481
|
+
async function fetchGitLabAdvisories(cveId) {
|
|
482
|
+
const { gitLabAdvisoryApi } = getIntelligenceSourceConfig();
|
|
483
|
+
if (!gitLabAdvisoryApi) return [];
|
|
484
|
+
try {
|
|
485
|
+
const url = new URL(gitLabAdvisoryApi);
|
|
486
|
+
url.searchParams.set("identifier", cveId);
|
|
487
|
+
url.searchParams.set("ecosystem", "npm");
|
|
488
|
+
const res = await fetch(url.toString(), {
|
|
489
|
+
headers: { Accept: "application/json" }
|
|
490
|
+
});
|
|
491
|
+
if (!res.ok) return [];
|
|
492
|
+
const body = await res.json();
|
|
493
|
+
return Array.isArray(body) ? body : [];
|
|
494
|
+
} catch {
|
|
495
|
+
return [];
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
async function enrichWithGitLabAdvisory(details) {
|
|
499
|
+
const advisories = await fetchGitLabAdvisories(details.id);
|
|
500
|
+
const matched = advisories.filter((a) => advisoryMatchesCve(a, details.id));
|
|
501
|
+
if (matched.length === 0) return details;
|
|
502
|
+
const refs = matched.flatMap((m) => m.references ?? []);
|
|
503
|
+
if (refs.length > 0) {
|
|
504
|
+
const merged = /* @__PURE__ */ new Set([...details.references, ...refs]);
|
|
505
|
+
details.references = Array.from(merged);
|
|
506
|
+
}
|
|
507
|
+
details.intelligence = {
|
|
508
|
+
...details.intelligence ?? {},
|
|
509
|
+
gitlabAdvisoryMatched: true
|
|
510
|
+
};
|
|
511
|
+
return details;
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
// src/intelligence/sources/certcc.ts
|
|
515
|
+
var CERTCC_HOME = "https://www.kb.cert.org/vuls/";
|
|
516
|
+
async function findCertCcReference(cveId) {
|
|
517
|
+
const { certCcSearchUrl } = getIntelligenceSourceConfig();
|
|
518
|
+
if (!certCcSearchUrl) return void 0;
|
|
519
|
+
try {
|
|
520
|
+
const url = new URL(certCcSearchUrl);
|
|
521
|
+
url.searchParams.set("query", cveId);
|
|
522
|
+
const res = await fetch(url.toString(), {
|
|
523
|
+
headers: { Accept: "text/html" }
|
|
524
|
+
});
|
|
525
|
+
if (!res.ok) return void 0;
|
|
526
|
+
const html = await res.text();
|
|
527
|
+
const match = html.match(/https:\/\/www\.kb\.cert\.org\/vuls\/id\/\d+/i);
|
|
528
|
+
return match?.[0] ?? void 0;
|
|
529
|
+
} catch {
|
|
530
|
+
return void 0;
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
async function enrichWithCertCc(details) {
|
|
534
|
+
const ref = await findCertCcReference(details.id);
|
|
535
|
+
if (!ref) return details;
|
|
536
|
+
if (!details.references.includes(ref)) {
|
|
537
|
+
details.references.push(ref);
|
|
538
|
+
}
|
|
539
|
+
details.intelligence = {
|
|
540
|
+
...details.intelligence ?? {},
|
|
541
|
+
certCcMatched: true
|
|
542
|
+
};
|
|
543
|
+
if (!details.references.includes(CERTCC_HOME)) {
|
|
544
|
+
details.references.push(CERTCC_HOME);
|
|
545
|
+
}
|
|
546
|
+
return details;
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
// src/intelligence/sources/deps-dev.ts
|
|
550
|
+
async function fetchDepsDevPackage(name) {
|
|
551
|
+
const { depsDevApi } = getIntelligenceSourceConfig();
|
|
552
|
+
if (!depsDevApi) return false;
|
|
553
|
+
try {
|
|
554
|
+
const url = `${depsDevApi}/systems/npm/packages/${encodeURIComponent(name)}`;
|
|
555
|
+
const res = await fetch(url, { headers: { Accept: "application/json" } });
|
|
556
|
+
return res.ok;
|
|
557
|
+
} catch {
|
|
558
|
+
return false;
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
async function enrichWithDepsDev(details) {
|
|
562
|
+
const names = Array.from(new Set(details.affectedPackages.map((p) => p.name))).slice(0, 20);
|
|
563
|
+
if (names.length === 0) return details;
|
|
564
|
+
const checks = await Promise.all(names.map((name) => fetchDepsDevPackage(name)));
|
|
565
|
+
const matched = checks.filter(Boolean).length;
|
|
566
|
+
if (matched === 0) return details;
|
|
567
|
+
details.intelligence = {
|
|
568
|
+
...details.intelligence ?? {},
|
|
569
|
+
depsDevEnrichedPackages: matched
|
|
570
|
+
};
|
|
571
|
+
return details;
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
// src/intelligence/sources/ossf-scorecard.ts
|
|
575
|
+
async function checkProject(project) {
|
|
576
|
+
const { scorecardApi } = getIntelligenceSourceConfig();
|
|
577
|
+
if (!scorecardApi) return false;
|
|
578
|
+
try {
|
|
579
|
+
const url = new URL(`${scorecardApi}/projects`);
|
|
580
|
+
url.searchParams.set("project", project);
|
|
581
|
+
const res = await fetch(url.toString(), {
|
|
582
|
+
headers: { Accept: "application/json" }
|
|
583
|
+
});
|
|
584
|
+
return res.ok;
|
|
585
|
+
} catch {
|
|
586
|
+
return false;
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
async function enrichWithOssfScorecard(details) {
|
|
590
|
+
const projects = Array.from(
|
|
591
|
+
new Set(details.affectedPackages.map((p) => `github.com/${p.name}/${p.name}`))
|
|
592
|
+
).slice(0, 10);
|
|
593
|
+
if (projects.length === 0) return details;
|
|
594
|
+
const checks = await Promise.all(projects.map((project) => checkProject(project)));
|
|
595
|
+
const matched = checks.filter(Boolean).length;
|
|
596
|
+
if (matched === 0) return details;
|
|
597
|
+
details.intelligence = {
|
|
598
|
+
...details.intelligence ?? {},
|
|
599
|
+
scorecardProjects: matched
|
|
600
|
+
};
|
|
601
|
+
return details;
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
// src/intelligence/sources/external-feeds.ts
|
|
605
|
+
async function probeFeed(url, cveId, token) {
|
|
606
|
+
try {
|
|
607
|
+
const feedUrl = new URL(url);
|
|
608
|
+
feedUrl.searchParams.set("cve", cveId);
|
|
609
|
+
const headers = { Accept: "application/json" };
|
|
610
|
+
if (token) headers.Authorization = `Bearer ${token}`;
|
|
611
|
+
const res = await fetch(feedUrl.toString(), { headers });
|
|
612
|
+
if (!res.ok) return void 0;
|
|
613
|
+
return feedUrl.toString();
|
|
614
|
+
} catch {
|
|
615
|
+
return void 0;
|
|
616
|
+
}
|
|
617
|
+
}
|
|
618
|
+
async function enrichWithExternalFeeds(details) {
|
|
619
|
+
const {
|
|
620
|
+
vendorAdvisoryFeeds,
|
|
621
|
+
commercialFeeds,
|
|
622
|
+
commercialFeedToken
|
|
623
|
+
} = getIntelligenceSourceConfig();
|
|
624
|
+
const vendorHits = (await Promise.all(vendorAdvisoryFeeds.map((url) => probeFeed(url, details.id)))).filter((v) => Boolean(v));
|
|
625
|
+
const commercialHits = (await Promise.all(
|
|
626
|
+
commercialFeeds.map((url) => probeFeed(url, details.id, commercialFeedToken))
|
|
627
|
+
)).filter((v) => Boolean(v));
|
|
628
|
+
if (vendorHits.length === 0 && commercialHits.length === 0) {
|
|
629
|
+
return details;
|
|
630
|
+
}
|
|
631
|
+
details.intelligence = {
|
|
632
|
+
...details.intelligence ?? {},
|
|
633
|
+
vendorAdvisories: vendorHits.length > 0 ? vendorHits : details.intelligence?.vendorAdvisories,
|
|
634
|
+
commercialFeeds: commercialHits.length > 0 ? commercialHits : details.intelligence?.commercialFeeds
|
|
635
|
+
};
|
|
636
|
+
const mergedRefs = /* @__PURE__ */ new Set([...details.references, ...vendorHits, ...commercialHits]);
|
|
637
|
+
details.references = Array.from(mergedRefs);
|
|
638
|
+
return details;
|
|
639
|
+
}
|
|
640
|
+
|
|
346
641
|
// src/remediation/tools/lookup-cve.ts
|
|
347
642
|
var lookupCveTool = tool({
|
|
348
643
|
description: "Look up a CVE ID and return the list of affected npm packages, their vulnerable version ranges, and the first patched version. Always call this first.",
|
|
@@ -371,7 +666,37 @@ var lookupCveTool = tool({
|
|
|
371
666
|
if (ghPackages.length > 0) {
|
|
372
667
|
details = mergeGhDataIntoCveDetails(details, ghPackages);
|
|
373
668
|
}
|
|
374
|
-
|
|
669
|
+
const sourceHealth = {};
|
|
670
|
+
const applyEnricher = async (sourceName, enricher) => {
|
|
671
|
+
const before = JSON.stringify(details);
|
|
672
|
+
try {
|
|
673
|
+
details = await enricher(details);
|
|
674
|
+
const after = JSON.stringify(details);
|
|
675
|
+
sourceHealth[sourceName] = {
|
|
676
|
+
attempted: true,
|
|
677
|
+
changed: before !== after
|
|
678
|
+
};
|
|
679
|
+
} catch (error) {
|
|
680
|
+
sourceHealth[sourceName] = {
|
|
681
|
+
attempted: true,
|
|
682
|
+
changed: false,
|
|
683
|
+
error: error instanceof Error ? error.message : String(error)
|
|
684
|
+
};
|
|
685
|
+
}
|
|
686
|
+
};
|
|
687
|
+
await applyEnricher("nvd", enrichWithNvd);
|
|
688
|
+
await applyEnricher("cisa-kev", enrichWithCisaKev);
|
|
689
|
+
await applyEnricher("epss", enrichWithEpss);
|
|
690
|
+
await applyEnricher("cve-services", enrichWithCveServices);
|
|
691
|
+
await applyEnricher("gitlab-advisory", enrichWithGitLabAdvisory);
|
|
692
|
+
await applyEnricher("certcc", enrichWithCertCc);
|
|
693
|
+
await applyEnricher("deps-dev", enrichWithDepsDev);
|
|
694
|
+
await applyEnricher("ossf-scorecard", enrichWithOssfScorecard);
|
|
695
|
+
await applyEnricher("external-feeds", enrichWithExternalFeeds);
|
|
696
|
+
details.intelligence = {
|
|
697
|
+
...details.intelligence ?? {},
|
|
698
|
+
sourceHealth
|
|
699
|
+
};
|
|
375
700
|
if (details.affectedPackages.length === 0) {
|
|
376
701
|
return {
|
|
377
702
|
success: false,
|
|
@@ -666,8 +991,8 @@ var applyVersionBumpTool = tool5({
|
|
|
666
991
|
fromVersion: z5.string().describe("The currently installed vulnerable version"),
|
|
667
992
|
toVersion: z5.string().describe("The safe target version to upgrade to"),
|
|
668
993
|
dryRun: z5.boolean().default(false).describe("If true, report changes but do not write"),
|
|
669
|
-
|
|
670
|
-
|
|
994
|
+
policy: z5.string().optional().describe("Optional path to .autoremediator policy file"),
|
|
995
|
+
runTests: z5.boolean().default(false).describe("If true, run test validation after applying the fix")
|
|
671
996
|
}),
|
|
672
997
|
execute: async ({
|
|
673
998
|
cwd,
|
|
@@ -676,14 +1001,14 @@ var applyVersionBumpTool = tool5({
|
|
|
676
1001
|
fromVersion,
|
|
677
1002
|
toVersion,
|
|
678
1003
|
dryRun,
|
|
679
|
-
|
|
680
|
-
|
|
1004
|
+
policy,
|
|
1005
|
+
runTests
|
|
681
1006
|
}) => {
|
|
682
1007
|
const pm = packageManager ?? detectPackageManager(cwd);
|
|
683
1008
|
const commands = getPackageManagerCommands(pm);
|
|
684
1009
|
const pkgPath = join5(cwd, "package.json");
|
|
685
|
-
const
|
|
686
|
-
if (!isPackageAllowed(
|
|
1010
|
+
const loadedPolicy = loadPolicy(cwd, policy);
|
|
1011
|
+
if (!isPackageAllowed(loadedPolicy, packageName)) {
|
|
687
1012
|
return {
|
|
688
1013
|
packageName,
|
|
689
1014
|
strategy: "none",
|
|
@@ -695,7 +1020,7 @@ var applyVersionBumpTool = tool5({
|
|
|
695
1020
|
};
|
|
696
1021
|
}
|
|
697
1022
|
const isMajorBump = semver3.valid(fromVersion) && semver3.valid(toVersion) && semver3.major(toVersion) > semver3.major(fromVersion);
|
|
698
|
-
if (isMajorBump && !
|
|
1023
|
+
if (isMajorBump && !loadedPolicy.allowMajorBumps) {
|
|
699
1024
|
return {
|
|
700
1025
|
packageName,
|
|
701
1026
|
strategy: "none",
|
|
@@ -746,7 +1071,7 @@ var applyVersionBumpTool = tool5({
|
|
|
746
1071
|
toVersion,
|
|
747
1072
|
applied: false,
|
|
748
1073
|
dryRun: true,
|
|
749
|
-
message: `[DRY RUN] Would update ${depField}.${packageName}: "${currentRange}"
|
|
1074
|
+
message: `[DRY RUN] Would update ${depField}.${packageName}: "${currentRange}" -> "${newRange}", then run ${installCmd}${runTests ? ` and ${testCmd}` : ""}.`
|
|
750
1075
|
};
|
|
751
1076
|
}
|
|
752
1077
|
return withRepoLock(cwd, async () => {
|
|
@@ -772,7 +1097,7 @@ var applyVersionBumpTool = tool5({
|
|
|
772
1097
|
message: `${commands.installPreferOffline.join(" ")} failed after updating "${packageName}" to ${toVersion}. Reverted. Error: ${message}`
|
|
773
1098
|
};
|
|
774
1099
|
}
|
|
775
|
-
if (
|
|
1100
|
+
if (runTests) {
|
|
776
1101
|
try {
|
|
777
1102
|
const [testCmd, ...testArgs] = commands.test;
|
|
778
1103
|
await execa2(testCmd, testArgs, {
|
|
@@ -809,7 +1134,7 @@ var applyVersionBumpTool = tool5({
|
|
|
809
1134
|
toVersion,
|
|
810
1135
|
applied: true,
|
|
811
1136
|
dryRun: false,
|
|
812
|
-
message: `Successfully upgraded "${packageName}" from ${fromVersion} to ${toVersion}, ran ${commands.installPreferOffline.join(" ")}${
|
|
1137
|
+
message: `Successfully upgraded "${packageName}" from ${fromVersion} to ${toVersion}, ran ${commands.installPreferOffline.join(" ")}${runTests ? `, and passed ${commands.test.join(" ")}` : ""}.`
|
|
813
1138
|
};
|
|
814
1139
|
});
|
|
815
1140
|
}
|
|
@@ -1442,16 +1767,16 @@ async function runRemediationPipeline(cveId, options = {}) {
|
|
|
1442
1767
|
const packageManager = options.packageManager ?? detectPackageManager(cwd);
|
|
1443
1768
|
const preview = options.preview ?? false;
|
|
1444
1769
|
const dryRun = (options.dryRun ?? false) || preview;
|
|
1445
|
-
const
|
|
1446
|
-
const
|
|
1770
|
+
const runTests = options.runTests ?? false;
|
|
1771
|
+
const policy = options.policy ?? "";
|
|
1447
1772
|
const patchesDir = options.patchesDir || "./patches";
|
|
1448
1773
|
const model = await createModel(options);
|
|
1449
1774
|
const systemPrompt = loadOrchestrationPrompt({
|
|
1450
1775
|
cveId,
|
|
1451
1776
|
cwd,
|
|
1452
1777
|
dryRun,
|
|
1453
|
-
|
|
1454
|
-
|
|
1778
|
+
runTests,
|
|
1779
|
+
policy,
|
|
1455
1780
|
patchesDir,
|
|
1456
1781
|
packageManager
|
|
1457
1782
|
});
|
|
@@ -1536,8 +1861,8 @@ async function runLocalRemediationPipeline(cveId, options = {}) {
|
|
|
1536
1861
|
const packageManager = options.packageManager ?? detectPackageManager(cwd);
|
|
1537
1862
|
const preview = options.preview ?? false;
|
|
1538
1863
|
const dryRun = (options.dryRun ?? false) || preview;
|
|
1539
|
-
const
|
|
1540
|
-
const
|
|
1864
|
+
const runTests = options.runTests ?? false;
|
|
1865
|
+
const policy = options.policy ?? "";
|
|
1541
1866
|
const collectedResults = [];
|
|
1542
1867
|
const vulnerablePackages = [];
|
|
1543
1868
|
let cveDetails = null;
|
|
@@ -1678,8 +2003,8 @@ async function runLocalRemediationPipeline(cveId, options = {}) {
|
|
|
1678
2003
|
fromVersion: pkg.version,
|
|
1679
2004
|
toVersion: safeVersion,
|
|
1680
2005
|
dryRun,
|
|
1681
|
-
|
|
1682
|
-
|
|
2006
|
+
policy,
|
|
2007
|
+
runTests
|
|
1683
2008
|
});
|
|
1684
2009
|
agentSteps += 1;
|
|
1685
2010
|
collectedResults.push(applyResult);
|
|
@@ -1708,8 +2033,8 @@ function loadOrchestrationPrompt(ctx) {
|
|
|
1708
2033
|
Working directory: ${ctx.cwd}
|
|
1709
2034
|
Package manager: ${ctx.packageManager}
|
|
1710
2035
|
Dry run: ${ctx.dryRun}
|
|
1711
|
-
|
|
1712
|
-
Policy
|
|
2036
|
+
Run tests: ${ctx.runTests}
|
|
2037
|
+
Policy: ${ctx.policy || "undefined"}
|
|
1713
2038
|
Patches dir: ${ctx.patchesDir}
|
|
1714
2039
|
|
|
1715
2040
|
Required sequence:
|
|
@@ -1727,7 +2052,7 @@ Fallback sequence (when strategy="none"):
|
|
|
1727
2052
|
Always respect dryRun and policy constraints.`;
|
|
1728
2053
|
}
|
|
1729
2054
|
const template = readFileSync4(promptPath, "utf8");
|
|
1730
|
-
return template.replaceAll("{{cveId}}", ctx.cveId).replaceAll("{{cwd}}", ctx.cwd).replaceAll("{{packageManager}}", ctx.packageManager).replaceAll("{{dryRun}}", String(ctx.dryRun)).replaceAll("{{
|
|
2055
|
+
return template.replaceAll("{{cveId}}", ctx.cveId).replaceAll("{{cwd}}", ctx.cwd).replaceAll("{{packageManager}}", ctx.packageManager).replaceAll("{{dryRun}}", String(ctx.dryRun)).replaceAll("{{runTests}}", String(ctx.runTests)).replaceAll("{{policy}}", ctx.policy || "undefined").replaceAll("{{patchesDir}}", ctx.patchesDir);
|
|
1731
2056
|
}
|
|
1732
2057
|
|
|
1733
2058
|
// src/scanner/index.ts
|
|
@@ -1999,7 +2324,7 @@ function resolveProvenanceContext(options) {
|
|
|
1999
2324
|
};
|
|
2000
2325
|
}
|
|
2001
2326
|
function resolveConstraints(options, cwd) {
|
|
2002
|
-
const policy = loadPolicy(cwd, options.
|
|
2327
|
+
const policy = loadPolicy(cwd, options.policy);
|
|
2003
2328
|
return {
|
|
2004
2329
|
directDependenciesOnly: options.constraints?.directDependenciesOnly ?? policy.constraints?.directDependenciesOnly ?? false,
|
|
2005
2330
|
preferVersionBump: options.constraints?.preferVersionBump ?? policy.constraints?.preferVersionBump ?? false
|
|
@@ -2090,7 +2415,7 @@ async function remediateFromScan(inputPath, options = {}) {
|
|
|
2090
2415
|
const patchesDir = options.patchesDir ?? "./patches";
|
|
2091
2416
|
const findings = parseScanInput(inputPath, format);
|
|
2092
2417
|
const cveIds = uniqueCveIds(findings);
|
|
2093
|
-
const policy = loadPolicy(cwd, options.
|
|
2418
|
+
const policy = loadPolicy(cwd, options.policy);
|
|
2094
2419
|
const correlation = resolveCorrelationContext(options);
|
|
2095
2420
|
const provenance = resolveProvenanceContext(options);
|
|
2096
2421
|
const constraints = resolveConstraints(options, cwd);
|
|
@@ -2104,7 +2429,7 @@ async function remediateFromScan(inputPath, options = {}) {
|
|
|
2104
2429
|
const reports = [];
|
|
2105
2430
|
const errors = [];
|
|
2106
2431
|
const patchValidationFailures = [];
|
|
2107
|
-
let
|
|
2432
|
+
let patchCount = 0;
|
|
2108
2433
|
for (const cveId of cveIds) {
|
|
2109
2434
|
try {
|
|
2110
2435
|
addEvidenceStep(evidence, "remediate.start", { cveId });
|
|
@@ -2119,7 +2444,7 @@ async function remediateFromScan(inputPath, options = {}) {
|
|
|
2119
2444
|
report.results = report.results.filter((r) => isPackageAllowed(policy, r.packageName));
|
|
2120
2445
|
for (const result of report.results) {
|
|
2121
2446
|
if (result.strategy === "patch-file") {
|
|
2122
|
-
|
|
2447
|
+
patchCount += 1;
|
|
2123
2448
|
}
|
|
2124
2449
|
if (result.validation?.passed === false && result.validation?.error) {
|
|
2125
2450
|
patchValidationFailures.push({
|
|
@@ -2156,7 +2481,7 @@ async function remediateFromScan(inputPath, options = {}) {
|
|
|
2156
2481
|
status = "failed";
|
|
2157
2482
|
}
|
|
2158
2483
|
finalizeEvidence(evidence);
|
|
2159
|
-
const evidenceFile = options.
|
|
2484
|
+
const evidenceFile = options.evidence === false ? void 0 : writeEvidenceLog(cwd, evidence);
|
|
2160
2485
|
return {
|
|
2161
2486
|
schemaVersion: "1.0",
|
|
2162
2487
|
status,
|
|
@@ -2167,9 +2492,9 @@ async function remediateFromScan(inputPath, options = {}) {
|
|
|
2167
2492
|
failedCount,
|
|
2168
2493
|
errors,
|
|
2169
2494
|
evidenceFile,
|
|
2170
|
-
|
|
2495
|
+
patchCount,
|
|
2171
2496
|
patchValidationFailures: patchValidationFailures.length > 0 ? patchValidationFailures : void 0,
|
|
2172
|
-
|
|
2497
|
+
patchesDir: patchCount > 0 ? patchesDir : void 0,
|
|
2173
2498
|
correlation,
|
|
2174
2499
|
provenance,
|
|
2175
2500
|
constraints,
|
|
@@ -2191,9 +2516,9 @@ function toCiSummary(report) {
|
|
|
2191
2516
|
failedCount: report.failedCount,
|
|
2192
2517
|
errors: report.errors,
|
|
2193
2518
|
evidenceFile: report.evidenceFile,
|
|
2194
|
-
|
|
2519
|
+
patchCount: report.patchCount || 0,
|
|
2195
2520
|
patchValidationFailures: report.patchValidationFailures,
|
|
2196
|
-
|
|
2521
|
+
patchesDir: report.patchesDir,
|
|
2197
2522
|
correlation: report.correlation,
|
|
2198
2523
|
provenance: report.provenance,
|
|
2199
2524
|
constraints: report.constraints,
|
|
@@ -2212,4 +2537,4 @@ export {
|
|
|
2212
2537
|
toCiSummary,
|
|
2213
2538
|
ciExitCode
|
|
2214
2539
|
};
|
|
2215
|
-
//# sourceMappingURL=chunk-
|
|
2540
|
+
//# sourceMappingURL=chunk-GBOD3DV6.js.map
|