autoremediator 0.2.2 → 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 +16 -1
- package/dist/{chunk-DQKT2CUG.js → chunk-GBOD3DV6.js} +739 -159
- package/dist/chunk-GBOD3DV6.js.map +1 -0
- package/dist/cli.d.ts +5 -0
- package/dist/cli.js +55 -17
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +77 -11
- package/dist/index.js +3 -1
- package/dist/mcp/server.d.ts +292 -0
- package/dist/mcp/server.js +120 -16
- package/dist/mcp/server.js.map +1 -1
- package/dist/openapi/server.d.ts +445 -1
- package/dist/openapi/server.js +215 -54
- package/dist/openapi/server.js.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-DQKT2CUG.js.map +0 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// src/remediation/pipeline.ts
|
|
2
2
|
import { generateText as generateText2 } from "ai";
|
|
3
3
|
import { existsSync as existsSync4, readFileSync as readFileSync4 } from "fs";
|
|
4
|
-
import { join as
|
|
4
|
+
import { join as join8 } from "path";
|
|
5
5
|
import semver4 from "semver";
|
|
6
6
|
|
|
7
7
|
// src/platform/config.ts
|
|
@@ -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,
|
|
@@ -574,7 +899,7 @@ var findFixedVersionTool = tool4({
|
|
|
574
899
|
// src/remediation/tools/apply-version-bump.ts
|
|
575
900
|
import { tool as tool5 } from "ai";
|
|
576
901
|
import { z as z5 } from "zod";
|
|
577
|
-
import { join as
|
|
902
|
+
import { join as join5 } from "path";
|
|
578
903
|
import { readFileSync as readFileSync3, writeFileSync } from "fs";
|
|
579
904
|
import { execa as execa2 } from "execa";
|
|
580
905
|
import semver3 from "semver";
|
|
@@ -585,7 +910,11 @@ import { join as join3 } from "path";
|
|
|
585
910
|
var DEFAULT_POLICY = {
|
|
586
911
|
allowMajorBumps: false,
|
|
587
912
|
denyPackages: [],
|
|
588
|
-
allowPackages: []
|
|
913
|
+
allowPackages: [],
|
|
914
|
+
constraints: {
|
|
915
|
+
directDependenciesOnly: false,
|
|
916
|
+
preferVersionBump: false
|
|
917
|
+
}
|
|
589
918
|
};
|
|
590
919
|
function loadPolicy(cwd, explicitPath) {
|
|
591
920
|
const candidate = explicitPath ?? join3(cwd, ".autoremediator.json");
|
|
@@ -595,7 +924,11 @@ function loadPolicy(cwd, explicitPath) {
|
|
|
595
924
|
return {
|
|
596
925
|
allowMajorBumps: parsed.allowMajorBumps ?? DEFAULT_POLICY.allowMajorBumps,
|
|
597
926
|
denyPackages: parsed.denyPackages ?? DEFAULT_POLICY.denyPackages,
|
|
598
|
-
allowPackages: parsed.allowPackages ?? DEFAULT_POLICY.allowPackages
|
|
927
|
+
allowPackages: parsed.allowPackages ?? DEFAULT_POLICY.allowPackages,
|
|
928
|
+
constraints: {
|
|
929
|
+
directDependenciesOnly: parsed.constraints?.directDependenciesOnly ?? DEFAULT_POLICY.constraints?.directDependenciesOnly ?? false,
|
|
930
|
+
preferVersionBump: parsed.constraints?.preferVersionBump ?? DEFAULT_POLICY.constraints?.preferVersionBump ?? false
|
|
931
|
+
}
|
|
599
932
|
};
|
|
600
933
|
} catch {
|
|
601
934
|
return DEFAULT_POLICY;
|
|
@@ -609,6 +942,45 @@ function isPackageAllowed(policy, packageName) {
|
|
|
609
942
|
return true;
|
|
610
943
|
}
|
|
611
944
|
|
|
945
|
+
// src/platform/repo-lock.ts
|
|
946
|
+
import { mkdir, rm } from "fs/promises";
|
|
947
|
+
import { join as join4 } from "path";
|
|
948
|
+
async function sleep(ms) {
|
|
949
|
+
await new Promise((resolve) => setTimeout(resolve, ms));
|
|
950
|
+
}
|
|
951
|
+
async function acquireRepoLock(cwd, options = {}) {
|
|
952
|
+
const timeoutMs = options.timeoutMs ?? 15e3;
|
|
953
|
+
const retryDelayMs = options.retryDelayMs ?? 125;
|
|
954
|
+
const lockRoot = join4(cwd, ".autoremediator", "locks");
|
|
955
|
+
const lockPath = join4(cwd, ".autoremediator", "locks", "remediation.lock");
|
|
956
|
+
const startedAt = Date.now();
|
|
957
|
+
await mkdir(lockRoot, { recursive: true });
|
|
958
|
+
while (true) {
|
|
959
|
+
try {
|
|
960
|
+
await mkdir(lockPath, { recursive: false });
|
|
961
|
+
return {
|
|
962
|
+
lockPath,
|
|
963
|
+
release: async () => {
|
|
964
|
+
await rm(lockPath, { recursive: true, force: true });
|
|
965
|
+
}
|
|
966
|
+
};
|
|
967
|
+
} catch {
|
|
968
|
+
if (Date.now() - startedAt > timeoutMs) {
|
|
969
|
+
throw new Error(`Timed out waiting for repository lock at ${lockPath}.`);
|
|
970
|
+
}
|
|
971
|
+
await sleep(retryDelayMs);
|
|
972
|
+
}
|
|
973
|
+
}
|
|
974
|
+
}
|
|
975
|
+
async function withRepoLock(cwd, fn, options) {
|
|
976
|
+
const lock = await acquireRepoLock(cwd, options);
|
|
977
|
+
try {
|
|
978
|
+
return await fn();
|
|
979
|
+
} finally {
|
|
980
|
+
await lock.release();
|
|
981
|
+
}
|
|
982
|
+
}
|
|
983
|
+
|
|
612
984
|
// src/remediation/tools/apply-version-bump.ts
|
|
613
985
|
var applyVersionBumpTool = tool5({
|
|
614
986
|
description: "Update package.json to use the safe version of a vulnerable package and run the project's package manager install. In dry-run mode, only reports what would change.",
|
|
@@ -619,8 +991,8 @@ var applyVersionBumpTool = tool5({
|
|
|
619
991
|
fromVersion: z5.string().describe("The currently installed vulnerable version"),
|
|
620
992
|
toVersion: z5.string().describe("The safe target version to upgrade to"),
|
|
621
993
|
dryRun: z5.boolean().default(false).describe("If true, report changes but do not write"),
|
|
622
|
-
|
|
623
|
-
|
|
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")
|
|
624
996
|
}),
|
|
625
997
|
execute: async ({
|
|
626
998
|
cwd,
|
|
@@ -629,14 +1001,14 @@ var applyVersionBumpTool = tool5({
|
|
|
629
1001
|
fromVersion,
|
|
630
1002
|
toVersion,
|
|
631
1003
|
dryRun,
|
|
632
|
-
|
|
633
|
-
|
|
1004
|
+
policy,
|
|
1005
|
+
runTests
|
|
634
1006
|
}) => {
|
|
635
1007
|
const pm = packageManager ?? detectPackageManager(cwd);
|
|
636
1008
|
const commands = getPackageManagerCommands(pm);
|
|
637
|
-
const pkgPath =
|
|
638
|
-
const
|
|
639
|
-
if (!isPackageAllowed(
|
|
1009
|
+
const pkgPath = join5(cwd, "package.json");
|
|
1010
|
+
const loadedPolicy = loadPolicy(cwd, policy);
|
|
1011
|
+
if (!isPackageAllowed(loadedPolicy, packageName)) {
|
|
640
1012
|
return {
|
|
641
1013
|
packageName,
|
|
642
1014
|
strategy: "none",
|
|
@@ -648,7 +1020,7 @@ var applyVersionBumpTool = tool5({
|
|
|
648
1020
|
};
|
|
649
1021
|
}
|
|
650
1022
|
const isMajorBump = semver3.valid(fromVersion) && semver3.valid(toVersion) && semver3.major(toVersion) > semver3.major(fromVersion);
|
|
651
|
-
if (isMajorBump && !
|
|
1023
|
+
if (isMajorBump && !loadedPolicy.allowMajorBumps) {
|
|
652
1024
|
return {
|
|
653
1025
|
packageName,
|
|
654
1026
|
strategy: "none",
|
|
@@ -699,49 +1071,21 @@ var applyVersionBumpTool = tool5({
|
|
|
699
1071
|
toVersion,
|
|
700
1072
|
applied: false,
|
|
701
1073
|
dryRun: true,
|
|
702
|
-
message: `[DRY RUN] Would update ${depField}.${packageName}: "${currentRange}"
|
|
1074
|
+
message: `[DRY RUN] Would update ${depField}.${packageName}: "${currentRange}" -> "${newRange}", then run ${installCmd}${runTests ? ` and ${testCmd}` : ""}.`
|
|
703
1075
|
};
|
|
704
1076
|
}
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
try {
|
|
708
|
-
const [installCmd, ...installArgs] = commands.installPreferOffline;
|
|
709
|
-
await execa2(installCmd, installArgs, {
|
|
710
|
-
cwd,
|
|
711
|
-
stdio: "pipe"
|
|
712
|
-
});
|
|
713
|
-
} catch (err) {
|
|
714
|
-
pkgJson[depField][packageName] = currentRange;
|
|
1077
|
+
return withRepoLock(cwd, async () => {
|
|
1078
|
+
pkgJson[depField][packageName] = newRange;
|
|
715
1079
|
writeFileSync(pkgPath, JSON.stringify(pkgJson, null, 2) + "\n", "utf8");
|
|
716
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
717
|
-
return {
|
|
718
|
-
packageName,
|
|
719
|
-
strategy: "version-bump",
|
|
720
|
-
fromVersion,
|
|
721
|
-
toVersion,
|
|
722
|
-
applied: false,
|
|
723
|
-
dryRun: false,
|
|
724
|
-
message: `${commands.installPreferOffline.join(" ")} failed after updating "${packageName}" to ${toVersion}. Reverted. Error: ${message}`
|
|
725
|
-
};
|
|
726
|
-
}
|
|
727
|
-
if (!skipTests) {
|
|
728
1080
|
try {
|
|
729
|
-
const [
|
|
730
|
-
await execa2(
|
|
1081
|
+
const [installCmd, ...installArgs] = commands.installPreferOffline;
|
|
1082
|
+
await execa2(installCmd, installArgs, {
|
|
731
1083
|
cwd,
|
|
732
1084
|
stdio: "pipe"
|
|
733
1085
|
});
|
|
734
1086
|
} catch (err) {
|
|
735
1087
|
pkgJson[depField][packageName] = currentRange;
|
|
736
1088
|
writeFileSync(pkgPath, JSON.stringify(pkgJson, null, 2) + "\n", "utf8");
|
|
737
|
-
try {
|
|
738
|
-
const [rollbackCmd, ...rollbackArgs] = commands.installPreferOffline;
|
|
739
|
-
await execa2(rollbackCmd, rollbackArgs, {
|
|
740
|
-
cwd,
|
|
741
|
-
stdio: "pipe"
|
|
742
|
-
});
|
|
743
|
-
} catch {
|
|
744
|
-
}
|
|
745
1089
|
const message = err instanceof Error ? err.message : String(err);
|
|
746
1090
|
return {
|
|
747
1091
|
packageName,
|
|
@@ -750,27 +1094,57 @@ var applyVersionBumpTool = tool5({
|
|
|
750
1094
|
toVersion,
|
|
751
1095
|
applied: false,
|
|
752
1096
|
dryRun: false,
|
|
753
|
-
message: `${commands.
|
|
1097
|
+
message: `${commands.installPreferOffline.join(" ")} failed after updating "${packageName}" to ${toVersion}. Reverted. Error: ${message}`
|
|
754
1098
|
};
|
|
755
1099
|
}
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
1100
|
+
if (runTests) {
|
|
1101
|
+
try {
|
|
1102
|
+
const [testCmd, ...testArgs] = commands.test;
|
|
1103
|
+
await execa2(testCmd, testArgs, {
|
|
1104
|
+
cwd,
|
|
1105
|
+
stdio: "pipe"
|
|
1106
|
+
});
|
|
1107
|
+
} catch (err) {
|
|
1108
|
+
pkgJson[depField][packageName] = currentRange;
|
|
1109
|
+
writeFileSync(pkgPath, JSON.stringify(pkgJson, null, 2) + "\n", "utf8");
|
|
1110
|
+
try {
|
|
1111
|
+
const [rollbackCmd, ...rollbackArgs] = commands.installPreferOffline;
|
|
1112
|
+
await execa2(rollbackCmd, rollbackArgs, {
|
|
1113
|
+
cwd,
|
|
1114
|
+
stdio: "pipe"
|
|
1115
|
+
});
|
|
1116
|
+
} catch {
|
|
1117
|
+
}
|
|
1118
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
1119
|
+
return {
|
|
1120
|
+
packageName,
|
|
1121
|
+
strategy: "version-bump",
|
|
1122
|
+
fromVersion,
|
|
1123
|
+
toVersion,
|
|
1124
|
+
applied: false,
|
|
1125
|
+
dryRun: false,
|
|
1126
|
+
message: `${commands.test.join(" ")} failed after upgrading "${packageName}" to ${toVersion}. Rolled back to ${currentRange}. Error: ${message}`
|
|
1127
|
+
};
|
|
1128
|
+
}
|
|
1129
|
+
}
|
|
1130
|
+
return {
|
|
1131
|
+
packageName,
|
|
1132
|
+
strategy: "version-bump",
|
|
1133
|
+
fromVersion,
|
|
1134
|
+
toVersion,
|
|
1135
|
+
applied: true,
|
|
1136
|
+
dryRun: false,
|
|
1137
|
+
message: `Successfully upgraded "${packageName}" from ${fromVersion} to ${toVersion}, ran ${commands.installPreferOffline.join(" ")}${runTests ? `, and passed ${commands.test.join(" ")}` : ""}.`
|
|
1138
|
+
};
|
|
1139
|
+
});
|
|
766
1140
|
}
|
|
767
1141
|
});
|
|
768
1142
|
|
|
769
1143
|
// src/remediation/tools/fetch-package-source.ts
|
|
770
1144
|
import { tool as tool6 } from "ai";
|
|
771
1145
|
import { z as z6 } from "zod";
|
|
772
|
-
import { mkdir, readdir, readFile, rm } from "fs/promises";
|
|
773
|
-
import { join as
|
|
1146
|
+
import { mkdir as mkdir2, readdir, readFile, rm as rm2 } from "fs/promises";
|
|
1147
|
+
import { join as join6 } from "path";
|
|
774
1148
|
import { execa as execa3 } from "execa";
|
|
775
1149
|
var fetchPackageSourceTool = tool6({
|
|
776
1150
|
description: "Download package tarball from npm and extract source files for CVE analysis. Supports custom file patterns (default: *.js, *.ts).",
|
|
@@ -787,23 +1161,23 @@ var fetchPackageSourceTool = tool6({
|
|
|
787
1161
|
filePatterns
|
|
788
1162
|
}) => {
|
|
789
1163
|
const tempBaseDir = `/tmp/autoremediator-pkg-${Date.now()}`;
|
|
790
|
-
const extractDir =
|
|
1164
|
+
const extractDir = join6(tempBaseDir, "out");
|
|
791
1165
|
try {
|
|
792
1166
|
const npmUrl = `https://registry.npmjs.org/${packageName}/-/${packageName.split("/").pop()}-${version}.tgz`;
|
|
793
|
-
await
|
|
794
|
-
const tarballPath =
|
|
1167
|
+
await mkdir2(tempBaseDir, { recursive: true });
|
|
1168
|
+
const tarballPath = join6(tempBaseDir, "package.tgz");
|
|
795
1169
|
await execa3("curl", ["-L", "-o", tarballPath, npmUrl]);
|
|
796
|
-
await
|
|
1170
|
+
await mkdir2(extractDir, { recursive: true });
|
|
797
1171
|
await execa3("tar", ["-xzf", tarballPath, "-C", extractDir]);
|
|
798
1172
|
const extractedContents = await readdir(extractDir);
|
|
799
|
-
const packageRootDir = extractedContents.includes("package") ?
|
|
1173
|
+
const packageRootDir = extractedContents.includes("package") ? join6(extractDir, "package") : extractDir;
|
|
800
1174
|
const sourceCode = {};
|
|
801
1175
|
async function walkDir(dir, relativeBase) {
|
|
802
1176
|
try {
|
|
803
1177
|
const files = await readdir(dir, { withFileTypes: true });
|
|
804
1178
|
for (const file of files) {
|
|
805
|
-
const fullPath =
|
|
806
|
-
const relPath =
|
|
1179
|
+
const fullPath = join6(dir, file.name);
|
|
1180
|
+
const relPath = join6(relativeBase, file.name);
|
|
807
1181
|
if (file.isDirectory()) {
|
|
808
1182
|
if (![
|
|
809
1183
|
"node_modules",
|
|
@@ -860,7 +1234,7 @@ var fetchPackageSourceTool = tool6({
|
|
|
860
1234
|
error: `Failed to fetch and extract package ${packageName}@${version}: ${message}`
|
|
861
1235
|
};
|
|
862
1236
|
} finally {
|
|
863
|
-
await
|
|
1237
|
+
await rm2(tempBaseDir, { recursive: true, force: true });
|
|
864
1238
|
}
|
|
865
1239
|
}
|
|
866
1240
|
});
|
|
@@ -1076,9 +1450,9 @@ function generateUnifiedDiff(original, fixed, filePath) {
|
|
|
1076
1450
|
import { tool as tool8 } from "ai";
|
|
1077
1451
|
import { z as z8 } from "zod";
|
|
1078
1452
|
import { existsSync as existsSync3 } from "fs";
|
|
1079
|
-
import { mkdir as
|
|
1453
|
+
import { mkdir as mkdir3, mkdtemp, readFile as readFile2, rm as rm3, writeFile } from "fs/promises";
|
|
1080
1454
|
import { tmpdir } from "os";
|
|
1081
|
-
import { join as
|
|
1455
|
+
import { join as join7 } from "path";
|
|
1082
1456
|
import { execa as execa4 } from "execa";
|
|
1083
1457
|
var applyPatchFileTool = tool8({
|
|
1084
1458
|
description: "Write generated patch file and apply it using package-manager-native patch flow when available, falling back to patch-package when needed.",
|
|
@@ -1126,7 +1500,7 @@ var applyPatchFileTool = tool8({
|
|
|
1126
1500
|
};
|
|
1127
1501
|
}
|
|
1128
1502
|
const patchFileName = buildPatchFileName(packageName, vulnerableVersion);
|
|
1129
|
-
const patchFilePath =
|
|
1503
|
+
const patchFilePath = join7(cwd, patchesDir, patchFileName);
|
|
1130
1504
|
if (dryRun) {
|
|
1131
1505
|
return {
|
|
1132
1506
|
success: true,
|
|
@@ -1139,66 +1513,68 @@ var applyPatchFileTool = tool8({
|
|
|
1139
1513
|
patchPath: patchFilePath
|
|
1140
1514
|
};
|
|
1141
1515
|
}
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
cwd,
|
|
1149
|
-
|
|
1150
|
-
vulnerableVersion,
|
|
1151
|
-
patchContent: selectedPatch,
|
|
1152
|
-
patchMode
|
|
1153
|
-
});
|
|
1154
|
-
if (!applyResult.success) {
|
|
1155
|
-
return {
|
|
1156
|
-
success: false,
|
|
1516
|
+
return withRepoLock(cwd, async () => {
|
|
1517
|
+
const patchesDirPath = join7(cwd, patchesDir);
|
|
1518
|
+
await mkdir3(patchesDirPath, { recursive: true });
|
|
1519
|
+
await writeFile(patchFilePath, selectedPatch, "utf8");
|
|
1520
|
+
let validationResult;
|
|
1521
|
+
const patchMode = await resolvePatchMode(pm, cwd);
|
|
1522
|
+
const applyResult = patchMode === "patch-package" ? await configurePatchPackagePostinstall(cwd, pm) : await applyNativePatch({
|
|
1523
|
+
cwd,
|
|
1157
1524
|
packageName,
|
|
1158
1525
|
vulnerableVersion,
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
patchPath: patchFilePath,
|
|
1164
|
-
patchMode,
|
|
1165
|
-
postinstallConfigured: patchMode === "patch-package" ? false : void 0,
|
|
1166
|
-
error: applyResult.error
|
|
1167
|
-
};
|
|
1168
|
-
}
|
|
1169
|
-
if (validateWithTests) {
|
|
1170
|
-
validationResult = await validatePatchWithTests(cwd, pm);
|
|
1171
|
-
if (!validationResult.passed) {
|
|
1172
|
-
const validationError = "Patch validation failed after apply; patch marked unresolved.";
|
|
1526
|
+
patchContent: selectedPatch,
|
|
1527
|
+
patchMode
|
|
1528
|
+
});
|
|
1529
|
+
if (!applyResult.success) {
|
|
1173
1530
|
return {
|
|
1174
1531
|
success: false,
|
|
1175
1532
|
packageName,
|
|
1176
1533
|
vulnerableVersion,
|
|
1177
1534
|
applied: false,
|
|
1178
1535
|
dryRun: false,
|
|
1179
|
-
message:
|
|
1536
|
+
message: applyResult.error,
|
|
1180
1537
|
patchFilePath,
|
|
1181
1538
|
patchPath: patchFilePath,
|
|
1182
1539
|
patchMode,
|
|
1183
|
-
postinstallConfigured: patchMode === "patch-package",
|
|
1184
|
-
|
|
1185
|
-
error: validationError
|
|
1540
|
+
postinstallConfigured: patchMode === "patch-package" ? false : void 0,
|
|
1541
|
+
error: applyResult.error
|
|
1186
1542
|
};
|
|
1187
1543
|
}
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1544
|
+
if (validateWithTests) {
|
|
1545
|
+
validationResult = await validatePatchWithTests(cwd, pm);
|
|
1546
|
+
if (!validationResult.passed) {
|
|
1547
|
+
const validationError = "Patch validation failed after apply; patch marked unresolved.";
|
|
1548
|
+
return {
|
|
1549
|
+
success: false,
|
|
1550
|
+
packageName,
|
|
1551
|
+
vulnerableVersion,
|
|
1552
|
+
applied: false,
|
|
1553
|
+
dryRun: false,
|
|
1554
|
+
message: validationError,
|
|
1555
|
+
patchFilePath,
|
|
1556
|
+
patchPath: patchFilePath,
|
|
1557
|
+
patchMode,
|
|
1558
|
+
postinstallConfigured: patchMode === "patch-package",
|
|
1559
|
+
validation: validationResult,
|
|
1560
|
+
error: validationError
|
|
1561
|
+
};
|
|
1562
|
+
}
|
|
1563
|
+
}
|
|
1564
|
+
return {
|
|
1565
|
+
success: true,
|
|
1566
|
+
packageName,
|
|
1567
|
+
vulnerableVersion,
|
|
1568
|
+
applied: true,
|
|
1569
|
+
dryRun: false,
|
|
1570
|
+
message: `Patch applied successfully for ${packageName}@${vulnerableVersion}.`,
|
|
1571
|
+
patchFilePath,
|
|
1572
|
+
patchPath: patchFilePath,
|
|
1573
|
+
patchMode,
|
|
1574
|
+
postinstallConfigured: patchMode === "patch-package",
|
|
1575
|
+
validation: validationResult
|
|
1576
|
+
};
|
|
1577
|
+
});
|
|
1202
1578
|
} catch (err) {
|
|
1203
1579
|
const message = err instanceof Error ? err.message : String(err);
|
|
1204
1580
|
return {
|
|
@@ -1233,7 +1609,7 @@ function buildPatchFileName(packageName, vulnerableVersion) {
|
|
|
1233
1609
|
return `${safeName}+${vulnerableVersion}.patch`;
|
|
1234
1610
|
}
|
|
1235
1611
|
async function configurePatchPackagePostinstall(cwd, packageManager) {
|
|
1236
|
-
const pkgJsonPath =
|
|
1612
|
+
const pkgJsonPath = join7(cwd, "package.json");
|
|
1237
1613
|
let pkgJson;
|
|
1238
1614
|
try {
|
|
1239
1615
|
pkgJson = JSON.parse(await readFile2(pkgJsonPath, "utf8"));
|
|
@@ -1297,8 +1673,8 @@ ${createResult.stderr}`);
|
|
|
1297
1673
|
error: `Could not determine native patch directory for ${packageSpec}.`
|
|
1298
1674
|
};
|
|
1299
1675
|
}
|
|
1300
|
-
const tempPatchDir = await mkdtemp(
|
|
1301
|
-
const tempPatchFile =
|
|
1676
|
+
const tempPatchDir = await mkdtemp(join7(tmpdir(), "autoremediator-native-patch-"));
|
|
1677
|
+
const tempPatchFile = join7(tempPatchDir, "change.patch");
|
|
1302
1678
|
try {
|
|
1303
1679
|
await writeFile(tempPatchFile, patchContent, "utf8");
|
|
1304
1680
|
await execa4("patch", ["-p1", "-i", tempPatchFile], {
|
|
@@ -1317,7 +1693,7 @@ ${createResult.stderr}`);
|
|
|
1317
1693
|
error: `Failed to apply native patch for ${packageSpec}: ${err instanceof Error ? err.message : String(err)}`
|
|
1318
1694
|
};
|
|
1319
1695
|
} finally {
|
|
1320
|
-
await
|
|
1696
|
+
await rm3(tempPatchDir, { recursive: true, force: true });
|
|
1321
1697
|
}
|
|
1322
1698
|
return { success: true };
|
|
1323
1699
|
}
|
|
@@ -1389,17 +1765,18 @@ async function runRemediationPipeline(cveId, options = {}) {
|
|
|
1389
1765
|
}
|
|
1390
1766
|
const cwd = options.cwd ?? process.cwd();
|
|
1391
1767
|
const packageManager = options.packageManager ?? detectPackageManager(cwd);
|
|
1392
|
-
const
|
|
1393
|
-
const
|
|
1394
|
-
const
|
|
1768
|
+
const preview = options.preview ?? false;
|
|
1769
|
+
const dryRun = (options.dryRun ?? false) || preview;
|
|
1770
|
+
const runTests = options.runTests ?? false;
|
|
1771
|
+
const policy = options.policy ?? "";
|
|
1395
1772
|
const patchesDir = options.patchesDir || "./patches";
|
|
1396
1773
|
const model = await createModel(options);
|
|
1397
1774
|
const systemPrompt = loadOrchestrationPrompt({
|
|
1398
1775
|
cveId,
|
|
1399
1776
|
cwd,
|
|
1400
1777
|
dryRun,
|
|
1401
|
-
|
|
1402
|
-
|
|
1778
|
+
runTests,
|
|
1779
|
+
policy,
|
|
1403
1780
|
patchesDir,
|
|
1404
1781
|
packageManager
|
|
1405
1782
|
});
|
|
@@ -1408,6 +1785,14 @@ async function runRemediationPipeline(cveId, options = {}) {
|
|
|
1408
1785
|
const vulnerablePackages = [];
|
|
1409
1786
|
let cveDetails = null;
|
|
1410
1787
|
let agentSteps = 0;
|
|
1788
|
+
const applyVersionBumpToolForRun = preview ? {
|
|
1789
|
+
...applyVersionBumpTool,
|
|
1790
|
+
execute: async (input) => applyVersionBumpTool.execute({ ...input, dryRun: true })
|
|
1791
|
+
} : applyVersionBumpTool;
|
|
1792
|
+
const applyPatchFileToolForRun = preview ? {
|
|
1793
|
+
...applyPatchFileTool,
|
|
1794
|
+
execute: async (input) => applyPatchFileTool.execute({ ...input, dryRun: true })
|
|
1795
|
+
} : applyPatchFileTool;
|
|
1411
1796
|
const result = await generateText2({
|
|
1412
1797
|
model,
|
|
1413
1798
|
system: systemPrompt,
|
|
@@ -1417,10 +1802,10 @@ async function runRemediationPipeline(cveId, options = {}) {
|
|
|
1417
1802
|
"check-inventory": checkInventoryTool,
|
|
1418
1803
|
"check-version-match": checkVersionMatchTool,
|
|
1419
1804
|
"find-fixed-version": findFixedVersionTool,
|
|
1420
|
-
"apply-version-bump":
|
|
1805
|
+
"apply-version-bump": applyVersionBumpToolForRun,
|
|
1421
1806
|
"fetch-package-source": fetchPackageSourceTool,
|
|
1422
1807
|
"generate-patch": generatePatchTool,
|
|
1423
|
-
"apply-patch-file":
|
|
1808
|
+
"apply-patch-file": applyPatchFileToolForRun
|
|
1424
1809
|
},
|
|
1425
1810
|
maxSteps: 25,
|
|
1426
1811
|
onStepFinish(stepResult) {
|
|
@@ -1463,15 +1848,21 @@ async function runRemediationPipeline(cveId, options = {}) {
|
|
|
1463
1848
|
vulnerablePackages,
|
|
1464
1849
|
results: collectedResults,
|
|
1465
1850
|
agentSteps,
|
|
1466
|
-
summary: result.text
|
|
1851
|
+
summary: result.text,
|
|
1852
|
+
correlation: {
|
|
1853
|
+
requestId: options.requestId,
|
|
1854
|
+
sessionId: options.sessionId,
|
|
1855
|
+
parentRunId: options.parentRunId
|
|
1856
|
+
}
|
|
1467
1857
|
};
|
|
1468
1858
|
}
|
|
1469
1859
|
async function runLocalRemediationPipeline(cveId, options = {}) {
|
|
1470
1860
|
const cwd = options.cwd ?? process.cwd();
|
|
1471
1861
|
const packageManager = options.packageManager ?? detectPackageManager(cwd);
|
|
1472
|
-
const
|
|
1473
|
-
const
|
|
1474
|
-
const
|
|
1862
|
+
const preview = options.preview ?? false;
|
|
1863
|
+
const dryRun = (options.dryRun ?? false) || preview;
|
|
1864
|
+
const runTests = options.runTests ?? false;
|
|
1865
|
+
const policy = options.policy ?? "";
|
|
1475
1866
|
const collectedResults = [];
|
|
1476
1867
|
const vulnerablePackages = [];
|
|
1477
1868
|
let cveDetails = null;
|
|
@@ -1489,7 +1880,12 @@ async function runLocalRemediationPipeline(cveId, options = {}) {
|
|
|
1489
1880
|
vulnerablePackages,
|
|
1490
1881
|
results: collectedResults,
|
|
1491
1882
|
agentSteps,
|
|
1492
|
-
summary: `Local mode failed at lookup-cve: ${normalizedId} not found in OSV or GitHub advisory data
|
|
1883
|
+
summary: `Local mode failed at lookup-cve: ${normalizedId} not found in OSV or GitHub advisory data.`,
|
|
1884
|
+
correlation: {
|
|
1885
|
+
requestId: options.requestId,
|
|
1886
|
+
sessionId: options.sessionId,
|
|
1887
|
+
parentRunId: options.parentRunId
|
|
1888
|
+
}
|
|
1493
1889
|
};
|
|
1494
1890
|
}
|
|
1495
1891
|
cveDetails = osvDetails ?? {
|
|
@@ -1510,7 +1906,12 @@ async function runLocalRemediationPipeline(cveId, options = {}) {
|
|
|
1510
1906
|
vulnerablePackages,
|
|
1511
1907
|
results: collectedResults,
|
|
1512
1908
|
agentSteps,
|
|
1513
|
-
summary: `Local mode lookup succeeded but no npm affected packages were found for ${normalizedId}
|
|
1909
|
+
summary: `Local mode lookup succeeded but no npm affected packages were found for ${normalizedId}.`,
|
|
1910
|
+
correlation: {
|
|
1911
|
+
requestId: options.requestId,
|
|
1912
|
+
sessionId: options.sessionId,
|
|
1913
|
+
parentRunId: options.parentRunId
|
|
1914
|
+
}
|
|
1514
1915
|
};
|
|
1515
1916
|
}
|
|
1516
1917
|
const inventory = await checkInventoryTool.execute({ cwd, packageManager });
|
|
@@ -1522,7 +1923,12 @@ async function runLocalRemediationPipeline(cveId, options = {}) {
|
|
|
1522
1923
|
vulnerablePackages,
|
|
1523
1924
|
results: collectedResults,
|
|
1524
1925
|
agentSteps,
|
|
1525
|
-
summary: `Local mode failed at check-inventory: ${inventory.error}
|
|
1926
|
+
summary: `Local mode failed at check-inventory: ${inventory.error}`,
|
|
1927
|
+
correlation: {
|
|
1928
|
+
requestId: options.requestId,
|
|
1929
|
+
sessionId: options.sessionId,
|
|
1930
|
+
parentRunId: options.parentRunId
|
|
1931
|
+
}
|
|
1526
1932
|
};
|
|
1527
1933
|
}
|
|
1528
1934
|
const installedPackages = inventory.packages ?? [];
|
|
@@ -1597,8 +2003,8 @@ async function runLocalRemediationPipeline(cveId, options = {}) {
|
|
|
1597
2003
|
fromVersion: pkg.version,
|
|
1598
2004
|
toVersion: safeVersion,
|
|
1599
2005
|
dryRun,
|
|
1600
|
-
|
|
1601
|
-
|
|
2006
|
+
policy,
|
|
2007
|
+
runTests
|
|
1602
2008
|
});
|
|
1603
2009
|
agentSteps += 1;
|
|
1604
2010
|
collectedResults.push(applyResult);
|
|
@@ -1612,18 +2018,23 @@ async function runLocalRemediationPipeline(cveId, options = {}) {
|
|
|
1612
2018
|
vulnerablePackages,
|
|
1613
2019
|
results: collectedResults,
|
|
1614
2020
|
agentSteps,
|
|
1615
|
-
summary: `Local mode completed: vulnerable=${vulnerablePackages.length}, applied=${appliedCount}, dryRun=${dryRunCount}, unresolved=${unresolvedCount}
|
|
2021
|
+
summary: `Local mode completed: vulnerable=${vulnerablePackages.length}, applied=${appliedCount}, dryRun=${dryRunCount}, unresolved=${unresolvedCount}`,
|
|
2022
|
+
correlation: {
|
|
2023
|
+
requestId: options.requestId,
|
|
2024
|
+
sessionId: options.sessionId,
|
|
2025
|
+
parentRunId: options.parentRunId
|
|
2026
|
+
}
|
|
1616
2027
|
};
|
|
1617
2028
|
}
|
|
1618
2029
|
function loadOrchestrationPrompt(ctx) {
|
|
1619
|
-
const promptPath =
|
|
2030
|
+
const promptPath = join8(process.cwd(), ".github", "instructions", "orchestration.instructions.md");
|
|
1620
2031
|
if (!existsSync4(promptPath)) {
|
|
1621
2032
|
return `You are autoremediator, an agentic security remediation system for Node.js package dependencies.
|
|
1622
2033
|
Working directory: ${ctx.cwd}
|
|
1623
2034
|
Package manager: ${ctx.packageManager}
|
|
1624
2035
|
Dry run: ${ctx.dryRun}
|
|
1625
|
-
|
|
1626
|
-
Policy
|
|
2036
|
+
Run tests: ${ctx.runTests}
|
|
2037
|
+
Policy: ${ctx.policy || "undefined"}
|
|
1627
2038
|
Patches dir: ${ctx.patchesDir}
|
|
1628
2039
|
|
|
1629
2040
|
Required sequence:
|
|
@@ -1641,7 +2052,7 @@ Fallback sequence (when strategy="none"):
|
|
|
1641
2052
|
Always respect dryRun and policy constraints.`;
|
|
1642
2053
|
}
|
|
1643
2054
|
const template = readFileSync4(promptPath, "utf8");
|
|
1644
|
-
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);
|
|
1645
2056
|
}
|
|
1646
2057
|
|
|
1647
2058
|
// src/scanner/index.ts
|
|
@@ -1810,10 +2221,16 @@ function uniqueCveIds(findings) {
|
|
|
1810
2221
|
|
|
1811
2222
|
// src/platform/evidence.ts
|
|
1812
2223
|
import { mkdirSync, writeFileSync as writeFileSync2 } from "fs";
|
|
1813
|
-
import { join as
|
|
1814
|
-
function createEvidenceLog(cwd, cveIds) {
|
|
2224
|
+
import { join as join9 } from "path";
|
|
2225
|
+
function createEvidenceLog(cwd, cveIds, context = {}) {
|
|
1815
2226
|
return {
|
|
1816
|
-
runId: `${Date.now()}`,
|
|
2227
|
+
runId: `${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,
|
|
2228
|
+
requestId: context.requestId,
|
|
2229
|
+
sessionId: context.sessionId,
|
|
2230
|
+
parentRunId: context.parentRunId,
|
|
2231
|
+
actor: context.actor,
|
|
2232
|
+
source: context.source,
|
|
2233
|
+
idempotencyKey: context.idempotencyKey,
|
|
1817
2234
|
cveIds,
|
|
1818
2235
|
cwd,
|
|
1819
2236
|
startedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -1834,21 +2251,163 @@ function finalizeEvidence(log) {
|
|
|
1834
2251
|
return log;
|
|
1835
2252
|
}
|
|
1836
2253
|
function writeEvidenceLog(cwd, log) {
|
|
1837
|
-
const dir =
|
|
2254
|
+
const dir = join9(cwd, ".autoremediator", "evidence");
|
|
1838
2255
|
mkdirSync(dir, { recursive: true });
|
|
1839
|
-
const filePath =
|
|
2256
|
+
const filePath = join9(dir, `${log.runId}.json`);
|
|
1840
2257
|
writeFileSync2(filePath, JSON.stringify(log, null, 2) + "\n", "utf8");
|
|
1841
2258
|
return filePath;
|
|
1842
2259
|
}
|
|
1843
2260
|
|
|
2261
|
+
// src/platform/idempotency.ts
|
|
2262
|
+
import { existsSync as existsSync5, mkdirSync as mkdirSync2, readFileSync as readFileSync9, writeFileSync as writeFileSync3 } from "fs";
|
|
2263
|
+
import { join as join10 } from "path";
|
|
2264
|
+
var DEFAULT_INDEX = {
|
|
2265
|
+
schemaVersion: "1.0",
|
|
2266
|
+
entries: {}
|
|
2267
|
+
};
|
|
2268
|
+
function indexFilePath(cwd) {
|
|
2269
|
+
return join10(cwd, ".autoremediator", "state", "idempotency.json");
|
|
2270
|
+
}
|
|
2271
|
+
function entryKey(idempotencyKey, cveId) {
|
|
2272
|
+
return `${idempotencyKey}::${cveId.toUpperCase()}`;
|
|
2273
|
+
}
|
|
2274
|
+
function loadIndex(cwd) {
|
|
2275
|
+
const filePath = indexFilePath(cwd);
|
|
2276
|
+
if (!existsSync5(filePath)) return DEFAULT_INDEX;
|
|
2277
|
+
try {
|
|
2278
|
+
const parsed = JSON.parse(readFileSync9(filePath, "utf8"));
|
|
2279
|
+
if (parsed && parsed.schemaVersion === "1.0" && parsed.entries) {
|
|
2280
|
+
return parsed;
|
|
2281
|
+
}
|
|
2282
|
+
return DEFAULT_INDEX;
|
|
2283
|
+
} catch {
|
|
2284
|
+
return DEFAULT_INDEX;
|
|
2285
|
+
}
|
|
2286
|
+
}
|
|
2287
|
+
function saveIndex(cwd, index) {
|
|
2288
|
+
const filePath = indexFilePath(cwd);
|
|
2289
|
+
mkdirSync2(join10(cwd, ".autoremediator", "state"), { recursive: true });
|
|
2290
|
+
writeFileSync3(filePath, JSON.stringify(index, null, 2) + "\n", "utf8");
|
|
2291
|
+
}
|
|
2292
|
+
function readIdempotentReport(cwd, idempotencyKey, cveId) {
|
|
2293
|
+
const index = loadIndex(cwd);
|
|
2294
|
+
const key = entryKey(idempotencyKey, cveId);
|
|
2295
|
+
return index.entries[key]?.report;
|
|
2296
|
+
}
|
|
2297
|
+
function storeIdempotentReport(cwd, idempotencyKey, cveId, report) {
|
|
2298
|
+
const index = loadIndex(cwd);
|
|
2299
|
+
const key = entryKey(idempotencyKey, cveId);
|
|
2300
|
+
index.entries[key] = {
|
|
2301
|
+
key: idempotencyKey,
|
|
2302
|
+
cveId: cveId.toUpperCase(),
|
|
2303
|
+
report,
|
|
2304
|
+
savedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
2305
|
+
};
|
|
2306
|
+
saveIndex(cwd, index);
|
|
2307
|
+
}
|
|
2308
|
+
|
|
1844
2309
|
// src/api.ts
|
|
2310
|
+
function buildRequestId() {
|
|
2311
|
+
return `req-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
2312
|
+
}
|
|
2313
|
+
function resolveCorrelationContext(options) {
|
|
2314
|
+
return {
|
|
2315
|
+
requestId: options.requestId ?? buildRequestId(),
|
|
2316
|
+
sessionId: options.sessionId,
|
|
2317
|
+
parentRunId: options.parentRunId
|
|
2318
|
+
};
|
|
2319
|
+
}
|
|
2320
|
+
function resolveProvenanceContext(options) {
|
|
2321
|
+
return {
|
|
2322
|
+
actor: options.actor,
|
|
2323
|
+
source: options.source ?? "sdk"
|
|
2324
|
+
};
|
|
2325
|
+
}
|
|
2326
|
+
function resolveConstraints(options, cwd) {
|
|
2327
|
+
const policy = loadPolicy(cwd, options.policy);
|
|
2328
|
+
return {
|
|
2329
|
+
directDependenciesOnly: options.constraints?.directDependenciesOnly ?? policy.constraints?.directDependenciesOnly ?? false,
|
|
2330
|
+
preferVersionBump: options.constraints?.preferVersionBump ?? policy.constraints?.preferVersionBump ?? false
|
|
2331
|
+
};
|
|
2332
|
+
}
|
|
2333
|
+
function enforceConstraints(report, constraints) {
|
|
2334
|
+
const indirectPackages = new Set(
|
|
2335
|
+
report.vulnerablePackages.filter((vp) => vp.installed.type === "indirect").map((vp) => vp.installed.name)
|
|
2336
|
+
);
|
|
2337
|
+
const nextResults = report.results.map((result) => {
|
|
2338
|
+
if (constraints.directDependenciesOnly && indirectPackages.has(result.packageName)) {
|
|
2339
|
+
return {
|
|
2340
|
+
...result,
|
|
2341
|
+
strategy: "none",
|
|
2342
|
+
applied: false,
|
|
2343
|
+
message: `Constraint blocked remediation for indirect dependency "${result.packageName}".`
|
|
2344
|
+
};
|
|
2345
|
+
}
|
|
2346
|
+
if (constraints.preferVersionBump && result.strategy === "patch-file") {
|
|
2347
|
+
return {
|
|
2348
|
+
...result,
|
|
2349
|
+
strategy: "none",
|
|
2350
|
+
applied: false,
|
|
2351
|
+
message: `Constraint prefers version-bump and rejected patch-file remediation for "${result.packageName}".`
|
|
2352
|
+
};
|
|
2353
|
+
}
|
|
2354
|
+
return result;
|
|
2355
|
+
});
|
|
2356
|
+
return {
|
|
2357
|
+
...report,
|
|
2358
|
+
results: nextResults,
|
|
2359
|
+
constraints
|
|
2360
|
+
};
|
|
2361
|
+
}
|
|
1845
2362
|
async function remediate(cveId, options = {}) {
|
|
1846
2363
|
if (!/^CVE-\d{4}-\d+$/i.test(cveId)) {
|
|
1847
2364
|
throw new Error(
|
|
1848
2365
|
`Invalid CVE ID: "${cveId}". Expected format: CVE-YYYY-NNNNN (e.g. CVE-2021-23337).`
|
|
1849
2366
|
);
|
|
1850
2367
|
}
|
|
1851
|
-
|
|
2368
|
+
const cwd = options.cwd ?? process.cwd();
|
|
2369
|
+
const constraints = resolveConstraints(options, cwd);
|
|
2370
|
+
const provenance = resolveProvenanceContext(options);
|
|
2371
|
+
const correlation = resolveCorrelationContext(options);
|
|
2372
|
+
if (options.resume && options.idempotencyKey) {
|
|
2373
|
+
const cached = readIdempotentReport(cwd, options.idempotencyKey, cveId.toUpperCase());
|
|
2374
|
+
if (cached) {
|
|
2375
|
+
return {
|
|
2376
|
+
...cached,
|
|
2377
|
+
summary: `${cached.summary} (resumed from idempotency cache)`,
|
|
2378
|
+
correlation,
|
|
2379
|
+
provenance,
|
|
2380
|
+
constraints,
|
|
2381
|
+
resumedFromCache: true
|
|
2382
|
+
};
|
|
2383
|
+
}
|
|
2384
|
+
}
|
|
2385
|
+
const report = await runRemediationPipeline(cveId.toUpperCase(), {
|
|
2386
|
+
...options,
|
|
2387
|
+
...correlation,
|
|
2388
|
+
constraints
|
|
2389
|
+
});
|
|
2390
|
+
const constrainedReport = enforceConstraints(report, constraints);
|
|
2391
|
+
const finalReport = {
|
|
2392
|
+
...constrainedReport,
|
|
2393
|
+
correlation,
|
|
2394
|
+
provenance,
|
|
2395
|
+
constraints,
|
|
2396
|
+
resumedFromCache: false
|
|
2397
|
+
};
|
|
2398
|
+
if (options.idempotencyKey && !options.dryRun && !options.preview) {
|
|
2399
|
+
storeIdempotentReport(cwd, options.idempotencyKey, cveId.toUpperCase(), finalReport);
|
|
2400
|
+
}
|
|
2401
|
+
return {
|
|
2402
|
+
...finalReport
|
|
2403
|
+
};
|
|
2404
|
+
}
|
|
2405
|
+
async function planRemediation(cveId, options = {}) {
|
|
2406
|
+
return remediate(cveId, {
|
|
2407
|
+
...options,
|
|
2408
|
+
preview: true,
|
|
2409
|
+
dryRun: true
|
|
2410
|
+
});
|
|
1852
2411
|
}
|
|
1853
2412
|
async function remediateFromScan(inputPath, options = {}) {
|
|
1854
2413
|
const cwd = options.cwd ?? process.cwd();
|
|
@@ -1856,24 +2415,36 @@ async function remediateFromScan(inputPath, options = {}) {
|
|
|
1856
2415
|
const patchesDir = options.patchesDir ?? "./patches";
|
|
1857
2416
|
const findings = parseScanInput(inputPath, format);
|
|
1858
2417
|
const cveIds = uniqueCveIds(findings);
|
|
1859
|
-
const policy = loadPolicy(cwd, options.
|
|
1860
|
-
const
|
|
2418
|
+
const policy = loadPolicy(cwd, options.policy);
|
|
2419
|
+
const correlation = resolveCorrelationContext(options);
|
|
2420
|
+
const provenance = resolveProvenanceContext(options);
|
|
2421
|
+
const constraints = resolveConstraints(options, cwd);
|
|
2422
|
+
const evidence = createEvidenceLog(cwd, cveIds, {
|
|
2423
|
+
...correlation,
|
|
2424
|
+
actor: provenance.actor,
|
|
2425
|
+
source: provenance.source,
|
|
2426
|
+
idempotencyKey: options.idempotencyKey
|
|
2427
|
+
});
|
|
1861
2428
|
addEvidenceStep(evidence, "scan.parse", { inputPath, format }, { findingCount: findings.length, cveCount: cveIds.length });
|
|
1862
2429
|
const reports = [];
|
|
1863
2430
|
const errors = [];
|
|
1864
2431
|
const patchValidationFailures = [];
|
|
1865
|
-
let
|
|
2432
|
+
let patchCount = 0;
|
|
1866
2433
|
for (const cveId of cveIds) {
|
|
1867
2434
|
try {
|
|
1868
2435
|
addEvidenceStep(evidence, "remediate.start", { cveId });
|
|
1869
2436
|
const report = await remediate(cveId, {
|
|
1870
2437
|
...options,
|
|
1871
|
-
patchesDir
|
|
2438
|
+
patchesDir,
|
|
2439
|
+
...correlation,
|
|
2440
|
+
actor: provenance.actor,
|
|
2441
|
+
source: provenance.source,
|
|
2442
|
+
constraints
|
|
1872
2443
|
});
|
|
1873
2444
|
report.results = report.results.filter((r) => isPackageAllowed(policy, r.packageName));
|
|
1874
2445
|
for (const result of report.results) {
|
|
1875
2446
|
if (result.strategy === "patch-file") {
|
|
1876
|
-
|
|
2447
|
+
patchCount += 1;
|
|
1877
2448
|
}
|
|
1878
2449
|
if (result.validation?.passed === false && result.validation?.error) {
|
|
1879
2450
|
patchValidationFailures.push({
|
|
@@ -1910,7 +2481,7 @@ async function remediateFromScan(inputPath, options = {}) {
|
|
|
1910
2481
|
status = "failed";
|
|
1911
2482
|
}
|
|
1912
2483
|
finalizeEvidence(evidence);
|
|
1913
|
-
const evidenceFile = options.
|
|
2484
|
+
const evidenceFile = options.evidence === false ? void 0 : writeEvidenceLog(cwd, evidence);
|
|
1914
2485
|
return {
|
|
1915
2486
|
schemaVersion: "1.0",
|
|
1916
2487
|
status,
|
|
@@ -1921,9 +2492,13 @@ async function remediateFromScan(inputPath, options = {}) {
|
|
|
1921
2492
|
failedCount,
|
|
1922
2493
|
errors,
|
|
1923
2494
|
evidenceFile,
|
|
1924
|
-
|
|
2495
|
+
patchCount,
|
|
1925
2496
|
patchValidationFailures: patchValidationFailures.length > 0 ? patchValidationFailures : void 0,
|
|
1926
|
-
|
|
2497
|
+
patchesDir: patchCount > 0 ? patchesDir : void 0,
|
|
2498
|
+
correlation,
|
|
2499
|
+
provenance,
|
|
2500
|
+
constraints,
|
|
2501
|
+
idempotencyKey: options.idempotencyKey
|
|
1927
2502
|
};
|
|
1928
2503
|
}
|
|
1929
2504
|
function toCiSummary(report) {
|
|
@@ -1941,9 +2516,13 @@ function toCiSummary(report) {
|
|
|
1941
2516
|
failedCount: report.failedCount,
|
|
1942
2517
|
errors: report.errors,
|
|
1943
2518
|
evidenceFile: report.evidenceFile,
|
|
1944
|
-
|
|
2519
|
+
patchCount: report.patchCount || 0,
|
|
1945
2520
|
patchValidationFailures: report.patchValidationFailures,
|
|
1946
|
-
|
|
2521
|
+
patchesDir: report.patchesDir,
|
|
2522
|
+
correlation: report.correlation,
|
|
2523
|
+
provenance: report.provenance,
|
|
2524
|
+
constraints: report.constraints,
|
|
2525
|
+
idempotencyKey: report.idempotencyKey
|
|
1947
2526
|
};
|
|
1948
2527
|
}
|
|
1949
2528
|
function ciExitCode(summary) {
|
|
@@ -1953,8 +2532,9 @@ function ciExitCode(summary) {
|
|
|
1953
2532
|
export {
|
|
1954
2533
|
runRemediationPipeline,
|
|
1955
2534
|
remediate,
|
|
2535
|
+
planRemediation,
|
|
1956
2536
|
remediateFromScan,
|
|
1957
2537
|
toCiSummary,
|
|
1958
2538
|
ciExitCode
|
|
1959
2539
|
};
|
|
1960
|
-
//# sourceMappingURL=chunk-
|
|
2540
|
+
//# sourceMappingURL=chunk-GBOD3DV6.js.map
|