autoremediator 0.18.0 → 0.19.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 +20 -28
- package/dist/api/change-request/index.d.ts.map +1 -1
- package/dist/api/context.d.ts.map +1 -1
- package/dist/api/options-schema.d.ts +3 -0
- package/dist/api/options-schema.d.ts.map +1 -1
- package/dist/{chunk-FXOUJDQ3.js → chunk-7BORIBPY.js} +897 -162
- package/dist/cli/program.d.ts.map +1 -1
- package/dist/cli/runners.d.ts.map +1 -1
- package/dist/cli/types.d.ts +3 -0
- package/dist/cli/types.d.ts.map +1 -1
- package/dist/cli.js +14 -4
- package/dist/index.js +895 -160
- package/dist/index.js.map +1 -1
- package/dist/intelligence/sources/github-advisory.d.ts.map +1 -1
- package/dist/mcp/server.js +1 -1
- package/dist/openapi/server.js +1 -1
- package/dist/platform/evidence.d.ts +2 -0
- package/dist/platform/evidence.d.ts.map +1 -1
- package/dist/platform/package-manager/index.d.ts.map +1 -1
- package/dist/platform/supply-chain.d.ts.map +1 -1
- package/dist/platform/types.d.ts +32 -3
- package/dist/platform/types.d.ts.map +1 -1
- package/dist/remediation/local/fallback.d.ts +2 -0
- package/dist/remediation/local/fallback.d.ts.map +1 -1
- package/dist/remediation/local/options.d.ts +3 -0
- package/dist/remediation/local/options.d.ts.map +1 -1
- package/dist/remediation/local/primary-strategy.d.ts +2 -11
- package/dist/remediation/local/primary-strategy.d.ts.map +1 -1
- package/dist/remediation/local/run.d.ts.map +1 -1
- package/dist/remediation/pipeline-runtime/tool-wrappers.d.ts.map +1 -1
- package/dist/remediation/proof-of-fix/runner.d.ts +16 -0
- package/dist/remediation/proof-of-fix/runner.d.ts.map +1 -0
- package/dist/remediation/proof-of-fix/runner.test.d.ts +2 -0
- package/dist/remediation/proof-of-fix/runner.test.d.ts.map +1 -0
- package/dist/remediation/refactor/call-site-finder.d.ts +15 -0
- package/dist/remediation/refactor/call-site-finder.d.ts.map +1 -0
- package/dist/remediation/refactor/refactor-agent.d.ts +34 -0
- package/dist/remediation/refactor/refactor-agent.d.ts.map +1 -0
- package/dist/remediation/refactor/refactor.test.d.ts +2 -0
- package/dist/remediation/refactor/refactor.test.d.ts.map +1 -0
- package/dist/remediation/tools/apply-patch-file/index.d.ts +4 -1
- package/dist/remediation/tools/apply-patch-file/index.d.ts.map +1 -1
- package/dist/remediation/tools/apply-version-bump.d.ts +1 -0
- package/dist/remediation/tools/apply-version-bump.d.ts.map +1 -1
- package/dist/remediation/tools/check-inventory.d.ts.map +1 -1
- package/dist/remediation/tools/check-reachability.d.ts.map +1 -1
- package/dist/scanner/adapters/yarn-audit.d.ts.map +1 -1
- package/dist/scanner/lockfile-diff.d.ts.map +1 -1
- package/package.json +8 -8
package/dist/index.js
CHANGED
|
@@ -65,7 +65,10 @@ var OPTION_DESCRIPTIONS = {
|
|
|
65
65
|
campaignMode: "Enable portfolio campaign mode to risk-rank targets before execution",
|
|
66
66
|
escalationGraph: "Optional mapping from unresolved reasons to intended escalation actions",
|
|
67
67
|
offlineIntelligence: "Skip all network calls to intelligence sources. Only local inventory and npm-registry version resolution proceed.",
|
|
68
|
-
intelligenceSnapshotPath: "Path to a local JSON snapshot of pre-fetched CVE intelligence (keyed by CVE ID) used when offline mode is active."
|
|
68
|
+
intelligenceSnapshotPath: "Path to a local JSON snapshot of pre-fetched CVE intelligence (keyed by CVE ID) used when offline mode is active.",
|
|
69
|
+
proofOfFix: "If true, enforce dual-stage exploit reproduction proof-of-fix verification (assert failure pre-patch, assert pass post-patch)",
|
|
70
|
+
requireProofOfFix: "If true, reject patches and roll back if proof-of-fix verification fails",
|
|
71
|
+
breakingChangeRefactor: "If true, attempt agentic call-site refactoring when major version upgrades introduce breaking API changes"
|
|
69
72
|
};
|
|
70
73
|
function createConstraintSchemaProperties() {
|
|
71
74
|
return {
|
|
@@ -87,7 +90,12 @@ function createConstraintSchemaProperties() {
|
|
|
87
90
|
type: "boolean",
|
|
88
91
|
description: OPTION_DESCRIPTIONS.enforceFrozenLockfile
|
|
89
92
|
},
|
|
90
|
-
workspace: { type: "string", description: OPTION_DESCRIPTIONS.workspace }
|
|
93
|
+
workspace: { type: "string", description: OPTION_DESCRIPTIONS.workspace },
|
|
94
|
+
requireProofOfFix: { type: "boolean", description: OPTION_DESCRIPTIONS.requireProofOfFix },
|
|
95
|
+
enableBreakingChangeRefactor: {
|
|
96
|
+
type: "boolean",
|
|
97
|
+
description: OPTION_DESCRIPTIONS.breakingChangeRefactor
|
|
98
|
+
}
|
|
91
99
|
};
|
|
92
100
|
}
|
|
93
101
|
function createRemediateOptionSchemaProperties(options) {
|
|
@@ -306,6 +314,14 @@ function createRemediateOptionSchemaProperties(options) {
|
|
|
306
314
|
"validation-failed": {
|
|
307
315
|
type: "string",
|
|
308
316
|
enum: ["open-issue", "notify-channel", "create-draft-pr", "hold-branch", "none"]
|
|
317
|
+
},
|
|
318
|
+
"proof-of-fix-failed": {
|
|
319
|
+
type: "string",
|
|
320
|
+
enum: ["open-issue", "notify-channel", "create-draft-pr", "hold-branch", "none"]
|
|
321
|
+
},
|
|
322
|
+
"breaking-change-unresolved": {
|
|
323
|
+
type: "string",
|
|
324
|
+
enum: ["open-issue", "notify-channel", "create-draft-pr", "hold-branch", "none"]
|
|
309
325
|
}
|
|
310
326
|
},
|
|
311
327
|
additionalProperties: false
|
|
@@ -314,6 +330,12 @@ function createRemediateOptionSchemaProperties(options) {
|
|
|
314
330
|
intelligenceSnapshotPath: {
|
|
315
331
|
type: "string",
|
|
316
332
|
description: OPTION_DESCRIPTIONS.intelligenceSnapshotPath
|
|
333
|
+
},
|
|
334
|
+
proofOfFix: { type: "boolean", description: OPTION_DESCRIPTIONS.proofOfFix },
|
|
335
|
+
requireProofOfFix: { type: "boolean", description: OPTION_DESCRIPTIONS.requireProofOfFix },
|
|
336
|
+
breakingChangeRefactor: {
|
|
337
|
+
type: "boolean",
|
|
338
|
+
description: OPTION_DESCRIPTIONS.breakingChangeRefactor
|
|
317
339
|
}
|
|
318
340
|
};
|
|
319
341
|
}
|
|
@@ -3128,7 +3150,13 @@ function assessPackageReachability(cwd, packageName, targetSymbol) {
|
|
|
3128
3150
|
} catch {
|
|
3129
3151
|
continue;
|
|
3130
3152
|
}
|
|
3131
|
-
const refs = extractPackageReferences(
|
|
3153
|
+
const refs = extractPackageReferences(
|
|
3154
|
+
filePath,
|
|
3155
|
+
content,
|
|
3156
|
+
packageName,
|
|
3157
|
+
tsconfig,
|
|
3158
|
+
importedDirectPackages
|
|
3159
|
+
);
|
|
3132
3160
|
if (refs.length > 0) {
|
|
3133
3161
|
hasPackageImport = true;
|
|
3134
3162
|
}
|
|
@@ -3362,8 +3390,8 @@ function buildSbom(packages, vulnerableNames, results) {
|
|
|
3362
3390
|
|
|
3363
3391
|
// src/remediation/tools/apply-version-bump.ts
|
|
3364
3392
|
import { z as z4 } from "zod";
|
|
3365
|
-
import { join as
|
|
3366
|
-
import { readFileSync as
|
|
3393
|
+
import { join as join15 } from "path";
|
|
3394
|
+
import { readFileSync as readFileSync13, writeFileSync as writeFileSync4 } from "fs";
|
|
3367
3395
|
|
|
3368
3396
|
// src/platform/safe-exec.ts
|
|
3369
3397
|
import { execa as execa6 } from "execa";
|
|
@@ -3429,7 +3457,7 @@ import semver3 from "semver";
|
|
|
3429
3457
|
import { mkdir, rm as rm2 } from "fs/promises";
|
|
3430
3458
|
import { join as join13 } from "path";
|
|
3431
3459
|
async function sleep(ms) {
|
|
3432
|
-
await new Promise((
|
|
3460
|
+
await new Promise((resolve6) => setTimeout(resolve6, ms));
|
|
3433
3461
|
}
|
|
3434
3462
|
async function acquireRepoLock(cwd, options = {}) {
|
|
3435
3463
|
const timeoutMs = options.timeoutMs ?? 15e3;
|
|
@@ -3464,6 +3492,380 @@ async function withRepoLock(cwd, fn, options) {
|
|
|
3464
3492
|
}
|
|
3465
3493
|
}
|
|
3466
3494
|
|
|
3495
|
+
// src/remediation/refactor/call-site-finder.ts
|
|
3496
|
+
import { readdirSync as readdirSync3, readFileSync as readFileSync11, statSync as statSync3 } from "fs";
|
|
3497
|
+
import { join as join14 } from "path";
|
|
3498
|
+
import { parseSync as parseSync2 } from "oxc-parser";
|
|
3499
|
+
var SOURCE_EXTENSIONS2 = /* @__PURE__ */ new Set([".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs", ".mts", ".cts"]);
|
|
3500
|
+
var IGNORED_DIRS2 = /* @__PURE__ */ new Set([
|
|
3501
|
+
"node_modules",
|
|
3502
|
+
".git",
|
|
3503
|
+
"dist",
|
|
3504
|
+
"build",
|
|
3505
|
+
"coverage",
|
|
3506
|
+
".next",
|
|
3507
|
+
".turbo",
|
|
3508
|
+
".pnpm-store"
|
|
3509
|
+
]);
|
|
3510
|
+
function* walkFiles(dir) {
|
|
3511
|
+
let entries;
|
|
3512
|
+
try {
|
|
3513
|
+
entries = readdirSync3(dir);
|
|
3514
|
+
} catch {
|
|
3515
|
+
return;
|
|
3516
|
+
}
|
|
3517
|
+
for (const entry of entries) {
|
|
3518
|
+
if (IGNORED_DIRS2.has(entry)) continue;
|
|
3519
|
+
const full = join14(dir, entry);
|
|
3520
|
+
let stat2;
|
|
3521
|
+
try {
|
|
3522
|
+
stat2 = statSync3(full);
|
|
3523
|
+
} catch {
|
|
3524
|
+
continue;
|
|
3525
|
+
}
|
|
3526
|
+
if (stat2.isDirectory()) {
|
|
3527
|
+
yield* walkFiles(full);
|
|
3528
|
+
} else {
|
|
3529
|
+
const ext = full.slice(full.lastIndexOf("."));
|
|
3530
|
+
if (SOURCE_EXTENSIONS2.has(ext)) {
|
|
3531
|
+
yield full;
|
|
3532
|
+
}
|
|
3533
|
+
}
|
|
3534
|
+
}
|
|
3535
|
+
}
|
|
3536
|
+
function findPackageCallSites(cwd, packageName) {
|
|
3537
|
+
const callSites = [];
|
|
3538
|
+
for (const filePath of walkFiles(cwd)) {
|
|
3539
|
+
let content;
|
|
3540
|
+
try {
|
|
3541
|
+
content = readFileSync11(filePath, "utf8");
|
|
3542
|
+
} catch {
|
|
3543
|
+
continue;
|
|
3544
|
+
}
|
|
3545
|
+
if (!content.includes(packageName)) {
|
|
3546
|
+
continue;
|
|
3547
|
+
}
|
|
3548
|
+
try {
|
|
3549
|
+
const parseResult = parseSync2(filePath, content);
|
|
3550
|
+
if (!parseResult.program) continue;
|
|
3551
|
+
const program = parseResult.program;
|
|
3552
|
+
const imports = [];
|
|
3553
|
+
const importSources = [];
|
|
3554
|
+
const localNames = /* @__PURE__ */ new Set();
|
|
3555
|
+
for (const statement of program.body) {
|
|
3556
|
+
if (statement.type === "ImportDeclaration" && statement.source?.value) {
|
|
3557
|
+
const importSource = statement.source.value;
|
|
3558
|
+
if (importSource === packageName || importSource.startsWith(`${packageName}/`)) {
|
|
3559
|
+
importSources.push(importSource);
|
|
3560
|
+
for (const spec of statement.specifiers || []) {
|
|
3561
|
+
const localName = spec.local?.name || "";
|
|
3562
|
+
if (!localName) continue;
|
|
3563
|
+
localNames.add(localName);
|
|
3564
|
+
if (spec.type === "ImportDefaultSpecifier") {
|
|
3565
|
+
imports.push({
|
|
3566
|
+
localName,
|
|
3567
|
+
importedName: "default",
|
|
3568
|
+
isDefault: true,
|
|
3569
|
+
isNamespace: false
|
|
3570
|
+
});
|
|
3571
|
+
} else if (spec.type === "ImportNamespaceSpecifier") {
|
|
3572
|
+
imports.push({
|
|
3573
|
+
localName,
|
|
3574
|
+
importedName: "*",
|
|
3575
|
+
isDefault: false,
|
|
3576
|
+
isNamespace: true
|
|
3577
|
+
});
|
|
3578
|
+
} else if (spec.type === "ImportSpecifier") {
|
|
3579
|
+
imports.push({
|
|
3580
|
+
localName,
|
|
3581
|
+
importedName: spec.imported?.name || localName,
|
|
3582
|
+
isDefault: false,
|
|
3583
|
+
isNamespace: false
|
|
3584
|
+
});
|
|
3585
|
+
}
|
|
3586
|
+
}
|
|
3587
|
+
}
|
|
3588
|
+
}
|
|
3589
|
+
}
|
|
3590
|
+
if (imports.length > 0 || importSources.length > 0) {
|
|
3591
|
+
const lines = content.split("\n");
|
|
3592
|
+
const referencedLines = [];
|
|
3593
|
+
lines.forEach((line, index) => {
|
|
3594
|
+
for (const local of localNames) {
|
|
3595
|
+
const regex = new RegExp(`\\b${local}\\b`);
|
|
3596
|
+
if (regex.test(line)) {
|
|
3597
|
+
referencedLines.push(index + 1);
|
|
3598
|
+
break;
|
|
3599
|
+
}
|
|
3600
|
+
}
|
|
3601
|
+
});
|
|
3602
|
+
callSites.push({
|
|
3603
|
+
filePath,
|
|
3604
|
+
sourceContent: content,
|
|
3605
|
+
importSources,
|
|
3606
|
+
imports,
|
|
3607
|
+
referencedLines
|
|
3608
|
+
});
|
|
3609
|
+
}
|
|
3610
|
+
} catch {
|
|
3611
|
+
}
|
|
3612
|
+
}
|
|
3613
|
+
return callSites;
|
|
3614
|
+
}
|
|
3615
|
+
|
|
3616
|
+
// src/remediation/refactor/refactor-agent.ts
|
|
3617
|
+
import { readFileSync as readFileSync12, writeFileSync as writeFileSync3 } from "fs";
|
|
3618
|
+
function parseCompilerDiagnostics(diagnosticError) {
|
|
3619
|
+
const diagnostics = [];
|
|
3620
|
+
const lines = diagnosticError.split("\n");
|
|
3621
|
+
for (const line of lines) {
|
|
3622
|
+
const trimmed = line.trim();
|
|
3623
|
+
if (!trimmed) continue;
|
|
3624
|
+
const match = trimmed.match(
|
|
3625
|
+
/(?:([^\s:(]+)[:(](\d+)[:,](\d+)\)?:?\s*(?:-\s*)?(?:error\s+)?(TS\d+)?:?\s*(.*))/i
|
|
3626
|
+
);
|
|
3627
|
+
if (match) {
|
|
3628
|
+
const [, filePath, lineStr, colStr, code, message] = match;
|
|
3629
|
+
const diag = {
|
|
3630
|
+
filePath,
|
|
3631
|
+
line: lineStr ? parseInt(lineStr, 10) : void 0,
|
|
3632
|
+
column: colStr ? parseInt(colStr, 10) : void 0,
|
|
3633
|
+
code,
|
|
3634
|
+
message: message || trimmed
|
|
3635
|
+
};
|
|
3636
|
+
const suggestionMatch = diag.message.match(/Did you mean ['"]([a-zA-Z0-9_$]+)['"]\?/i);
|
|
3637
|
+
if (suggestionMatch) {
|
|
3638
|
+
diag.suggestedSymbol = suggestionMatch[1];
|
|
3639
|
+
}
|
|
3640
|
+
const missingMatch = diag.message.match(/has no exported member ['"]([a-zA-Z0-9_$]+)['"]/i) || diag.message.match(/Property ['"]([a-zA-Z0-9_$]+)['"] does not exist/i);
|
|
3641
|
+
if (missingMatch) {
|
|
3642
|
+
diag.missingMember = missingMatch[1];
|
|
3643
|
+
}
|
|
3644
|
+
diagnostics.push(diag);
|
|
3645
|
+
}
|
|
3646
|
+
}
|
|
3647
|
+
return diagnostics;
|
|
3648
|
+
}
|
|
3649
|
+
function revertRefactorSnapshots(snapshots) {
|
|
3650
|
+
for (const [filePath, content] of Object.entries(snapshots)) {
|
|
3651
|
+
try {
|
|
3652
|
+
writeFileSync3(filePath, content, "utf8");
|
|
3653
|
+
} catch {
|
|
3654
|
+
}
|
|
3655
|
+
}
|
|
3656
|
+
}
|
|
3657
|
+
var KNOWN_MIGRATIONS = {
|
|
3658
|
+
rimraf: [
|
|
3659
|
+
{
|
|
3660
|
+
detect: (content, diag) => diag.includes("sync") && content.includes("rimraf.sync("),
|
|
3661
|
+
transform: (content) => {
|
|
3662
|
+
let res = content.replace(/import\s+rimraf\s+from\s+['"]rimraf['"];?/g, "");
|
|
3663
|
+
res = `import { rimrafSync } from "rimraf";
|
|
3664
|
+
` + res;
|
|
3665
|
+
return res.replace(/rimraf\.sync\(/g, "rimrafSync(");
|
|
3666
|
+
}
|
|
3667
|
+
}
|
|
3668
|
+
],
|
|
3669
|
+
uuid: [
|
|
3670
|
+
{
|
|
3671
|
+
detect: (content, diag) => diag.includes("v4") && content.includes("uuid.v4("),
|
|
3672
|
+
transform: (content) => {
|
|
3673
|
+
let res = content.replace(/import\s+uuid\s+from\s+['"]uuid['"];?/g, "");
|
|
3674
|
+
res = `import { v4 as uuidv4 } from "uuid";
|
|
3675
|
+
` + res;
|
|
3676
|
+
return res.replace(/uuid\.v4\(/g, "uuidv4(");
|
|
3677
|
+
}
|
|
3678
|
+
},
|
|
3679
|
+
{
|
|
3680
|
+
// Subpath import migration: `import v4 from "uuid/v4"` -> `import { v4 } from "uuid"`
|
|
3681
|
+
detect: (content) => /from\s+['"]uuid\/v4['"]/.test(content),
|
|
3682
|
+
transform: (content) => content.replace(
|
|
3683
|
+
/import\s+([a-zA-Z0-9_$]+)\s+from\s+['"]uuid\/v4['"];?/g,
|
|
3684
|
+
`import { v4 as $1 } from "uuid";`
|
|
3685
|
+
)
|
|
3686
|
+
}
|
|
3687
|
+
],
|
|
3688
|
+
glob: [
|
|
3689
|
+
{
|
|
3690
|
+
detect: (content, diag) => diag.includes("sync") && content.includes("glob.sync("),
|
|
3691
|
+
transform: (content) => {
|
|
3692
|
+
let res = content.replace(/import\s+glob\s+from\s+['"]glob['"];?/g, "");
|
|
3693
|
+
res = `import { globSync } from "glob";
|
|
3694
|
+
` + res;
|
|
3695
|
+
return res.replace(/glob\.sync\(/g, "globSync(");
|
|
3696
|
+
}
|
|
3697
|
+
}
|
|
3698
|
+
],
|
|
3699
|
+
mkdirp: [
|
|
3700
|
+
{
|
|
3701
|
+
detect: (content, diag) => diag.includes("sync") && content.includes("mkdirp.sync("),
|
|
3702
|
+
transform: (content) => {
|
|
3703
|
+
let res = content.replace(/import\s+mkdirp\s+from\s+['"]mkdirp['"];?/g, "");
|
|
3704
|
+
res = `import { mkdirpSync } from "mkdirp";
|
|
3705
|
+
` + res;
|
|
3706
|
+
return res.replace(/mkdirp\.sync\(/g, "mkdirpSync(");
|
|
3707
|
+
}
|
|
3708
|
+
}
|
|
3709
|
+
]
|
|
3710
|
+
};
|
|
3711
|
+
function escapeRegex(str) {
|
|
3712
|
+
return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
3713
|
+
}
|
|
3714
|
+
function transformFileContent(content, packageName, diagnosticError, filePath) {
|
|
3715
|
+
let modified = false;
|
|
3716
|
+
let newContent = content;
|
|
3717
|
+
const migrations = KNOWN_MIGRATIONS[packageName];
|
|
3718
|
+
if (migrations) {
|
|
3719
|
+
for (const mig of migrations) {
|
|
3720
|
+
if (mig.detect(newContent, diagnosticError)) {
|
|
3721
|
+
const transformed = mig.transform(newContent);
|
|
3722
|
+
if (transformed !== newContent) {
|
|
3723
|
+
newContent = transformed;
|
|
3724
|
+
modified = true;
|
|
3725
|
+
}
|
|
3726
|
+
}
|
|
3727
|
+
}
|
|
3728
|
+
}
|
|
3729
|
+
if (diagnosticError.includes("no default export") || diagnosticError.includes("has no exported member 'default'") || diagnosticError.includes("has no exported member default") || diagnosticError.includes("TS2614") || diagnosticError.includes("TS1192")) {
|
|
3730
|
+
const defaultImportRegex = new RegExp(
|
|
3731
|
+
`import\\s+([a-zA-Z0-9_$]+)\\s+from\\s+['"]${escapeRegex(packageName)}['"]`,
|
|
3732
|
+
"g"
|
|
3733
|
+
);
|
|
3734
|
+
if (defaultImportRegex.test(newContent)) {
|
|
3735
|
+
newContent = newContent.replace(
|
|
3736
|
+
defaultImportRegex,
|
|
3737
|
+
`import * as $1 from "${packageName}"`
|
|
3738
|
+
);
|
|
3739
|
+
modified = true;
|
|
3740
|
+
}
|
|
3741
|
+
}
|
|
3742
|
+
if (diagnosticError.includes("can only be used with a default import") || diagnosticError.includes("only be used with a default import") || diagnosticError.includes("TS1259")) {
|
|
3743
|
+
const starImportRegex = new RegExp(
|
|
3744
|
+
`import\\s+\\*\\s+as\\s+([a-zA-Z0-9_$]+)\\s+from\\s+['"]${escapeRegex(packageName)}['"]`,
|
|
3745
|
+
"g"
|
|
3746
|
+
);
|
|
3747
|
+
if (starImportRegex.test(newContent)) {
|
|
3748
|
+
newContent = newContent.replace(
|
|
3749
|
+
starImportRegex,
|
|
3750
|
+
`import $1 from "${packageName}"`
|
|
3751
|
+
);
|
|
3752
|
+
modified = true;
|
|
3753
|
+
}
|
|
3754
|
+
}
|
|
3755
|
+
const diagnostics = parseCompilerDiagnostics(diagnosticError);
|
|
3756
|
+
for (const diag of diagnostics) {
|
|
3757
|
+
if (diag.missingMember && diag.suggestedSymbol) {
|
|
3758
|
+
const missingMember = diag.missingMember;
|
|
3759
|
+
const suggestedSymbol = diag.suggestedSymbol;
|
|
3760
|
+
if (filePath && diag.filePath && filePath.endsWith(diag.filePath) && diag.line && diag.line > 0) {
|
|
3761
|
+
const fileLines = newContent.split("\n");
|
|
3762
|
+
const idx = diag.line - 1;
|
|
3763
|
+
if (idx >= 0 && idx < fileLines.length) {
|
|
3764
|
+
const line = fileLines[idx];
|
|
3765
|
+
const replaced = line.replace(new RegExp(`\\.${escapeRegex(missingMember)}\\b`, "g"), `.${suggestedSymbol}`).replace(new RegExp(`\\b${escapeRegex(missingMember)}\\b`, "g"), suggestedSymbol);
|
|
3766
|
+
if (replaced !== line) {
|
|
3767
|
+
fileLines[idx] = replaced;
|
|
3768
|
+
newContent = fileLines.join("\n");
|
|
3769
|
+
modified = true;
|
|
3770
|
+
}
|
|
3771
|
+
}
|
|
3772
|
+
} else {
|
|
3773
|
+
const memberRegex = new RegExp(`\\.${escapeRegex(missingMember)}\\b`, "g");
|
|
3774
|
+
if (memberRegex.test(newContent)) {
|
|
3775
|
+
newContent = newContent.replace(memberRegex, `.${suggestedSymbol}`);
|
|
3776
|
+
modified = true;
|
|
3777
|
+
}
|
|
3778
|
+
const importPattern = new RegExp(
|
|
3779
|
+
`import\\s*\\{[^}]*\\b${escapeRegex(missingMember)}\\b[^}]*\\}\\s*from\\s*['"]${escapeRegex(packageName)}['"]`,
|
|
3780
|
+
"g"
|
|
3781
|
+
);
|
|
3782
|
+
if (importPattern.test(newContent)) {
|
|
3783
|
+
newContent = newContent.replace(
|
|
3784
|
+
importPattern,
|
|
3785
|
+
(match) => match.replace(new RegExp(`\\b${escapeRegex(missingMember)}\\b`, "g"), suggestedSymbol)
|
|
3786
|
+
);
|
|
3787
|
+
modified = true;
|
|
3788
|
+
}
|
|
3789
|
+
}
|
|
3790
|
+
}
|
|
3791
|
+
}
|
|
3792
|
+
const lineDiags = diagnostics.filter(
|
|
3793
|
+
(d) => Boolean(filePath && d.filePath && filePath.endsWith(d.filePath) && typeof d.line === "number" && d.line > 0)
|
|
3794
|
+
).sort((a, b) => b.line - a.line);
|
|
3795
|
+
const processedLines = /* @__PURE__ */ new Set();
|
|
3796
|
+
for (const diag of lineDiags) {
|
|
3797
|
+
if (processedLines.has(diag.line)) continue;
|
|
3798
|
+
processedLines.add(diag.line);
|
|
3799
|
+
const fileLines = newContent.split("\n");
|
|
3800
|
+
const targetLineIdx = diag.line - 1;
|
|
3801
|
+
if (targetLineIdx >= 0 && targetLineIdx < fileLines.length) {
|
|
3802
|
+
const prevLine = targetLineIdx > 0 ? fileLines[targetLineIdx - 1] : "";
|
|
3803
|
+
if (!prevLine?.includes("@ts-expect-error") && !fileLines[targetLineIdx]?.includes("@ts-expect-error")) {
|
|
3804
|
+
const indent = fileLines[targetLineIdx]?.match(/^\s*/)?.[0] || "";
|
|
3805
|
+
fileLines.splice(
|
|
3806
|
+
targetLineIdx,
|
|
3807
|
+
0,
|
|
3808
|
+
`${indent}// @ts-expect-error [autoremediator-refactor]: breaking change in ${packageName}`
|
|
3809
|
+
);
|
|
3810
|
+
newContent = fileLines.join("\n");
|
|
3811
|
+
modified = true;
|
|
3812
|
+
}
|
|
3813
|
+
}
|
|
3814
|
+
}
|
|
3815
|
+
return { modified, newContent };
|
|
3816
|
+
}
|
|
3817
|
+
async function refactorConsumerCallSites(params) {
|
|
3818
|
+
const { packageName, diagnosticError, callSites } = params;
|
|
3819
|
+
if (callSites.length === 0) {
|
|
3820
|
+
return {
|
|
3821
|
+
applied: false,
|
|
3822
|
+
refactoredFiles: [],
|
|
3823
|
+
snapshots: {},
|
|
3824
|
+
error: `No consumer call sites found for "${packageName}".`
|
|
3825
|
+
};
|
|
3826
|
+
}
|
|
3827
|
+
const snapshots = {};
|
|
3828
|
+
const refactoredFiles = [];
|
|
3829
|
+
try {
|
|
3830
|
+
for (const site of callSites) {
|
|
3831
|
+
const originalContent = site.sourceContent || readFileSync12(site.filePath, "utf8");
|
|
3832
|
+
snapshots[site.filePath] = originalContent;
|
|
3833
|
+
const { modified, newContent } = transformFileContent(
|
|
3834
|
+
originalContent,
|
|
3835
|
+
packageName,
|
|
3836
|
+
diagnosticError,
|
|
3837
|
+
site.filePath
|
|
3838
|
+
);
|
|
3839
|
+
if (modified && newContent !== originalContent) {
|
|
3840
|
+
writeFileSync3(site.filePath, newContent, "utf8");
|
|
3841
|
+
refactoredFiles.push(site.filePath);
|
|
3842
|
+
}
|
|
3843
|
+
}
|
|
3844
|
+
if (refactoredFiles.length === 0) {
|
|
3845
|
+
return {
|
|
3846
|
+
applied: false,
|
|
3847
|
+
refactoredFiles: [],
|
|
3848
|
+
snapshots,
|
|
3849
|
+
error: "No automated codemod pattern matched the diagnostic error."
|
|
3850
|
+
};
|
|
3851
|
+
}
|
|
3852
|
+
return {
|
|
3853
|
+
applied: true,
|
|
3854
|
+
refactoredFiles,
|
|
3855
|
+
snapshots
|
|
3856
|
+
};
|
|
3857
|
+
} catch (err) {
|
|
3858
|
+
revertRefactorSnapshots(snapshots);
|
|
3859
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
3860
|
+
return {
|
|
3861
|
+
applied: false,
|
|
3862
|
+
refactoredFiles: [],
|
|
3863
|
+
snapshots: {},
|
|
3864
|
+
error: `Failed to apply breaking change refactoring: ${message}`
|
|
3865
|
+
};
|
|
3866
|
+
}
|
|
3867
|
+
}
|
|
3868
|
+
|
|
3467
3869
|
// src/remediation/tools/apply-version-bump.ts
|
|
3468
3870
|
var applyVersionBumpTool = defineTool({
|
|
3469
3871
|
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.",
|
|
@@ -3480,7 +3882,8 @@ var applyVersionBumpTool = defineTool({
|
|
|
3480
3882
|
installMode: z4.enum(["standard", "prefer-offline", "deterministic"]).optional(),
|
|
3481
3883
|
installPreferOffline: z4.boolean().optional(),
|
|
3482
3884
|
enforceFrozenLockfile: z4.boolean().optional(),
|
|
3483
|
-
workspace: z4.string().optional()
|
|
3885
|
+
workspace: z4.string().optional(),
|
|
3886
|
+
breakingChangeRefactor: z4.boolean().optional().describe("If true, attempt agentic call-site refactoring when validation fails after major upgrade")
|
|
3484
3887
|
}),
|
|
3485
3888
|
execute: async ({
|
|
3486
3889
|
cwd,
|
|
@@ -3495,10 +3898,11 @@ var applyVersionBumpTool = defineTool({
|
|
|
3495
3898
|
installMode,
|
|
3496
3899
|
installPreferOffline,
|
|
3497
3900
|
enforceFrozenLockfile,
|
|
3498
|
-
workspace
|
|
3901
|
+
workspace,
|
|
3902
|
+
breakingChangeRefactor
|
|
3499
3903
|
}) => {
|
|
3500
3904
|
const pm = packageManager ?? detectPackageManager(cwd);
|
|
3501
|
-
let pkgPath =
|
|
3905
|
+
let pkgPath = join15(cwd, "package.json");
|
|
3502
3906
|
const loadedPolicy = loadPolicy(cwd, policy);
|
|
3503
3907
|
const commandConstraints = {
|
|
3504
3908
|
...loadedPolicy.constraints,
|
|
@@ -3533,10 +3937,10 @@ var applyVersionBumpTool = defineTool({
|
|
|
3533
3937
|
message: `Policy blocked major bump for "${packageName}" (${fromVersion} -> ${toVersion}).`
|
|
3534
3938
|
};
|
|
3535
3939
|
}
|
|
3536
|
-
pkgPath =
|
|
3940
|
+
pkgPath = join15(cwd, "package.json");
|
|
3537
3941
|
let pkgJson;
|
|
3538
3942
|
try {
|
|
3539
|
-
pkgJson = JSON.parse(
|
|
3943
|
+
pkgJson = JSON.parse(readFileSync13(pkgPath, "utf8"));
|
|
3540
3944
|
} catch {
|
|
3541
3945
|
return {
|
|
3542
3946
|
packageName,
|
|
@@ -3597,7 +4001,7 @@ var applyVersionBumpTool = defineTool({
|
|
|
3597
4001
|
}
|
|
3598
4002
|
return withRepoLock(cwd, async () => {
|
|
3599
4003
|
pkgJson[depField][packageName] = newRange;
|
|
3600
|
-
|
|
4004
|
+
writeFileSync4(pkgPath, JSON.stringify(pkgJson, null, 2) + "\n", "utf8");
|
|
3601
4005
|
try {
|
|
3602
4006
|
const [installCmd, ...installArgs] = effectiveInstallCommand;
|
|
3603
4007
|
await safeExeca(installCmd, installArgs, {
|
|
@@ -3606,7 +4010,7 @@ var applyVersionBumpTool = defineTool({
|
|
|
3606
4010
|
});
|
|
3607
4011
|
} catch (err) {
|
|
3608
4012
|
pkgJson[depField][packageName] = currentRange;
|
|
3609
|
-
|
|
4013
|
+
writeFileSync4(pkgPath, JSON.stringify(pkgJson, null, 2) + "\n", "utf8");
|
|
3610
4014
|
const message = err instanceof Error ? err.message : String(err);
|
|
3611
4015
|
return {
|
|
3612
4016
|
packageName,
|
|
@@ -3619,67 +4023,98 @@ var applyVersionBumpTool = defineTool({
|
|
|
3619
4023
|
message: `${effectiveInstallCommand.join(" ")} failed after updating "${packageName}" to ${toVersion}. Reverted. Error: ${message}`
|
|
3620
4024
|
};
|
|
3621
4025
|
}
|
|
3622
|
-
|
|
3623
|
-
|
|
3624
|
-
|
|
3625
|
-
|
|
3626
|
-
|
|
3627
|
-
stdio: "pipe"
|
|
3628
|
-
});
|
|
3629
|
-
} catch (err) {
|
|
3630
|
-
pkgJson[depField][packageName] = currentRange;
|
|
3631
|
-
writeFileSync3(pkgPath, JSON.stringify(pkgJson, null, 2) + "\n", "utf8");
|
|
4026
|
+
const shouldAttemptRefactor = Boolean(
|
|
4027
|
+
breakingChangeRefactor ?? loadedPolicy.constraints?.enableBreakingChangeRefactor
|
|
4028
|
+
);
|
|
4029
|
+
async function runValidationSteps() {
|
|
4030
|
+
if (runTests) {
|
|
3632
4031
|
try {
|
|
3633
|
-
const [
|
|
3634
|
-
await safeExeca(
|
|
3635
|
-
|
|
3636
|
-
|
|
3637
|
-
|
|
3638
|
-
|
|
4032
|
+
const [testCmd, ...testArgs] = effectiveTestCommand;
|
|
4033
|
+
await safeExeca(testCmd, testArgs, { cwd, stdio: "pipe" });
|
|
4034
|
+
} catch (err) {
|
|
4035
|
+
const execErr = err;
|
|
4036
|
+
const fullOutput = [execErr.stdout, execErr.stderr, execErr.message].filter(Boolean).join("\n");
|
|
4037
|
+
return {
|
|
4038
|
+
passed: false,
|
|
4039
|
+
error: fullOutput || (err instanceof Error ? err.message : String(err))
|
|
4040
|
+
};
|
|
3639
4041
|
}
|
|
3640
|
-
|
|
3641
|
-
|
|
4042
|
+
}
|
|
4043
|
+
if (shouldTypecheck && effectiveTypecheckCommand.length > 0) {
|
|
4044
|
+
try {
|
|
4045
|
+
const [tcCmd, ...tcArgs] = effectiveTypecheckCommand;
|
|
4046
|
+
await safeExeca(tcCmd, tcArgs, { cwd, stdio: "pipe" });
|
|
4047
|
+
} catch (err) {
|
|
4048
|
+
const execErr = err;
|
|
4049
|
+
const fullOutput = [execErr.stdout, execErr.stderr, execErr.message].filter(Boolean).join("\n");
|
|
4050
|
+
return {
|
|
4051
|
+
passed: false,
|
|
4052
|
+
isTypecheck: true,
|
|
4053
|
+
error: fullOutput || (err instanceof Error ? err.message : String(err))
|
|
4054
|
+
};
|
|
4055
|
+
}
|
|
4056
|
+
}
|
|
4057
|
+
return { passed: true };
|
|
4058
|
+
}
|
|
4059
|
+
let validationResult = await runValidationSteps();
|
|
4060
|
+
let refactoredFilesList;
|
|
4061
|
+
if (!validationResult.passed && shouldAttemptRefactor) {
|
|
4062
|
+
const callSites = findPackageCallSites(cwd, packageName);
|
|
4063
|
+
if (callSites.length > 0) {
|
|
4064
|
+
const refactorAttempt = await refactorConsumerCallSites({
|
|
4065
|
+
cwd,
|
|
3642
4066
|
packageName,
|
|
3643
|
-
strategy: "version-bump",
|
|
3644
4067
|
fromVersion,
|
|
3645
4068
|
toVersion,
|
|
3646
|
-
|
|
3647
|
-
|
|
3648
|
-
|
|
3649
|
-
|
|
3650
|
-
|
|
4069
|
+
diagnosticError: validationResult.error || "",
|
|
4070
|
+
callSites
|
|
4071
|
+
});
|
|
4072
|
+
if (refactorAttempt.applied) {
|
|
4073
|
+
const postRefactorValidation = await runValidationSteps();
|
|
4074
|
+
if (postRefactorValidation.passed) {
|
|
4075
|
+
validationResult = { passed: true };
|
|
4076
|
+
refactoredFilesList = refactorAttempt.refactoredFiles;
|
|
4077
|
+
} else {
|
|
4078
|
+
revertRefactorSnapshots(refactorAttempt.snapshots);
|
|
4079
|
+
}
|
|
4080
|
+
}
|
|
3651
4081
|
}
|
|
3652
4082
|
}
|
|
3653
|
-
if (
|
|
4083
|
+
if (!validationResult.passed) {
|
|
4084
|
+
pkgJson[depField][packageName] = currentRange;
|
|
4085
|
+
writeFileSync4(pkgPath, JSON.stringify(pkgJson, null, 2) + "\n", "utf8");
|
|
3654
4086
|
try {
|
|
3655
|
-
const [
|
|
3656
|
-
await safeExeca(
|
|
4087
|
+
const [rollbackCmd, ...rollbackArgs] = effectiveInstallCommand;
|
|
4088
|
+
await safeExeca(rollbackCmd, rollbackArgs, {
|
|
3657
4089
|
cwd,
|
|
3658
4090
|
stdio: "pipe"
|
|
3659
4091
|
});
|
|
3660
|
-
} catch
|
|
3661
|
-
pkgJson[depField][packageName] = currentRange;
|
|
3662
|
-
writeFileSync3(pkgPath, JSON.stringify(pkgJson, null, 2) + "\n", "utf8");
|
|
3663
|
-
try {
|
|
3664
|
-
const [rollbackCmd, ...rollbackArgs] = effectiveInstallCommand;
|
|
3665
|
-
await safeExeca(rollbackCmd, rollbackArgs, {
|
|
3666
|
-
cwd,
|
|
3667
|
-
stdio: "pipe"
|
|
3668
|
-
});
|
|
3669
|
-
} catch {
|
|
3670
|
-
}
|
|
3671
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
3672
|
-
return {
|
|
3673
|
-
packageName,
|
|
3674
|
-
strategy: "version-bump",
|
|
3675
|
-
fromVersion,
|
|
3676
|
-
toVersion,
|
|
3677
|
-
applied: false,
|
|
3678
|
-
dryRun: false,
|
|
3679
|
-
unresolvedReason: "validation-failed",
|
|
3680
|
-
message: `TypeCheck validation failed (${effectiveTypecheckCommand.join(" ")}) after upgrading "${packageName}" to ${toVersion}. Rolled back to ${currentRange}. Error: ${message}`
|
|
3681
|
-
};
|
|
4092
|
+
} catch {
|
|
3682
4093
|
}
|
|
4094
|
+
const failureMessage = validationResult.error || "Unknown validation error";
|
|
4095
|
+
const prefix2 = validationResult.isTypecheck ? `TypeCheck validation failed (${effectiveTypecheckCommand.join(" ")})` : `${effectiveTestCommand.join(" ")} failed`;
|
|
4096
|
+
return {
|
|
4097
|
+
packageName,
|
|
4098
|
+
strategy: "version-bump",
|
|
4099
|
+
fromVersion,
|
|
4100
|
+
toVersion,
|
|
4101
|
+
applied: false,
|
|
4102
|
+
dryRun: false,
|
|
4103
|
+
unresolvedReason: shouldAttemptRefactor ? "breaking-change-unresolved" : "validation-failed",
|
|
4104
|
+
message: `${prefix2} after upgrading "${packageName}" to ${toVersion}. Rolled back to ${currentRange}. Error: ${failureMessage}`
|
|
4105
|
+
};
|
|
4106
|
+
}
|
|
4107
|
+
if (refactoredFilesList && refactoredFilesList.length > 0) {
|
|
4108
|
+
return {
|
|
4109
|
+
packageName,
|
|
4110
|
+
strategy: "breaking-change-refactor",
|
|
4111
|
+
fromVersion,
|
|
4112
|
+
toVersion,
|
|
4113
|
+
applied: true,
|
|
4114
|
+
dryRun: false,
|
|
4115
|
+
refactoredFiles: refactoredFilesList,
|
|
4116
|
+
message: `Upgraded "${packageName}" from ${fromVersion} to ${toVersion} and refactored ${refactoredFilesList.length} call-site file(s) across breaking API changes.`
|
|
4117
|
+
};
|
|
3683
4118
|
}
|
|
3684
4119
|
let dedupeNote = "";
|
|
3685
4120
|
if (effectiveDedupeCommand.length > 0) {
|
|
@@ -3709,8 +4144,8 @@ var applyVersionBumpTool = defineTool({
|
|
|
3709
4144
|
|
|
3710
4145
|
// src/remediation/tools/apply-package-override/index.ts
|
|
3711
4146
|
import { z as z5 } from "zod";
|
|
3712
|
-
import { join as
|
|
3713
|
-
import { existsSync as
|
|
4147
|
+
import { join as join16 } from "path";
|
|
4148
|
+
import { existsSync as existsSync12, readFileSync as readFileSync14, writeFileSync as writeFileSync5 } from "fs";
|
|
3714
4149
|
import semver4 from "semver";
|
|
3715
4150
|
|
|
3716
4151
|
// src/remediation/tools/apply-package-override/helpers.ts
|
|
@@ -3853,9 +4288,9 @@ var applyPackageOverrideTool = defineTool({
|
|
|
3853
4288
|
workspace
|
|
3854
4289
|
}) => {
|
|
3855
4290
|
const pm = packageManager ?? detectPackageManager(cwd);
|
|
3856
|
-
const pkgPath =
|
|
3857
|
-
const denoJsonPath =
|
|
3858
|
-
const isDenoNative = pm === "deno" && !
|
|
4291
|
+
const pkgPath = join16(cwd, "package.json");
|
|
4292
|
+
const denoJsonPath = join16(cwd, "deno.json");
|
|
4293
|
+
const isDenoNative = pm === "deno" && !existsSync12(pkgPath);
|
|
3859
4294
|
const loadedPolicy = loadPolicy(cwd, policy);
|
|
3860
4295
|
const commandConstraints = {
|
|
3861
4296
|
...loadedPolicy.constraints,
|
|
@@ -3897,7 +4332,7 @@ var applyPackageOverrideTool = defineTool({
|
|
|
3897
4332
|
if (isDenoNative) {
|
|
3898
4333
|
let denoJson;
|
|
3899
4334
|
try {
|
|
3900
|
-
denoJson = JSON.parse(
|
|
4335
|
+
denoJson = JSON.parse(readFileSync14(denoJsonPath, "utf8"));
|
|
3901
4336
|
} catch {
|
|
3902
4337
|
return {
|
|
3903
4338
|
packageName,
|
|
@@ -3943,13 +4378,13 @@ var applyPackageOverrideTool = defineTool({
|
|
|
3943
4378
|
}
|
|
3944
4379
|
return withRepoLock(cwd, async () => {
|
|
3945
4380
|
setDenoJsonImportValue(denoJson, overrideSelector, toVersion);
|
|
3946
|
-
|
|
4381
|
+
writeFileSync5(denoJsonPath, JSON.stringify(denoJson, null, 2) + "\n", "utf8");
|
|
3947
4382
|
try {
|
|
3948
4383
|
const [installCmd, ...installArgs] = installCommand;
|
|
3949
4384
|
await safeExeca(installCmd, installArgs, { cwd, stdio: "pipe" });
|
|
3950
4385
|
} catch (err) {
|
|
3951
4386
|
restoreDenoJsonImportValue(denoJson, overrideSelector, existingEntry);
|
|
3952
|
-
|
|
4387
|
+
writeFileSync5(denoJsonPath, JSON.stringify(denoJson, null, 2) + "\n", "utf8");
|
|
3953
4388
|
const message = err instanceof Error ? err.message : String(err);
|
|
3954
4389
|
return {
|
|
3955
4390
|
packageName,
|
|
@@ -3968,7 +4403,7 @@ var applyPackageOverrideTool = defineTool({
|
|
|
3968
4403
|
await safeExeca(testCmd, testArgs, { cwd, stdio: "pipe" });
|
|
3969
4404
|
} catch (err) {
|
|
3970
4405
|
restoreDenoJsonImportValue(denoJson, overrideSelector, existingEntry);
|
|
3971
|
-
|
|
4406
|
+
writeFileSync5(denoJsonPath, JSON.stringify(denoJson, null, 2) + "\n", "utf8");
|
|
3972
4407
|
try {
|
|
3973
4408
|
const [rollbackCmd, ...rollbackArgs] = installCommand;
|
|
3974
4409
|
await safeExeca(rollbackCmd, rollbackArgs, { cwd, stdio: "pipe" });
|
|
@@ -4000,7 +4435,7 @@ var applyPackageOverrideTool = defineTool({
|
|
|
4000
4435
|
}
|
|
4001
4436
|
let pkgJson;
|
|
4002
4437
|
try {
|
|
4003
|
-
pkgJson = JSON.parse(
|
|
4438
|
+
pkgJson = JSON.parse(readFileSync14(pkgPath, "utf8"));
|
|
4004
4439
|
} catch {
|
|
4005
4440
|
return {
|
|
4006
4441
|
packageName,
|
|
@@ -4030,13 +4465,13 @@ var applyPackageOverrideTool = defineTool({
|
|
|
4030
4465
|
}
|
|
4031
4466
|
return withRepoLock(cwd, async () => {
|
|
4032
4467
|
setOverrideValue(pkgJson, pm, overrideSelector, toVersion);
|
|
4033
|
-
|
|
4468
|
+
writeFileSync5(pkgPath, JSON.stringify(pkgJson, null, 2) + "\n", "utf8");
|
|
4034
4469
|
try {
|
|
4035
4470
|
const [installCmd, ...installArgs] = installCommand;
|
|
4036
4471
|
await safeExeca(installCmd, installArgs, { cwd, stdio: "pipe" });
|
|
4037
4472
|
} catch (err) {
|
|
4038
4473
|
restoreOverrideValue(pkgJson, pm, overrideSelector, previousValue);
|
|
4039
|
-
|
|
4474
|
+
writeFileSync5(pkgPath, JSON.stringify(pkgJson, null, 2) + "\n", "utf8");
|
|
4040
4475
|
const message = err instanceof Error ? err.message : String(err);
|
|
4041
4476
|
return {
|
|
4042
4477
|
packageName,
|
|
@@ -4055,7 +4490,7 @@ var applyPackageOverrideTool = defineTool({
|
|
|
4055
4490
|
await safeExeca(testCmd, testArgs, { cwd, stdio: "pipe" });
|
|
4056
4491
|
} catch (err) {
|
|
4057
4492
|
restoreOverrideValue(pkgJson, pm, overrideSelector, previousValue);
|
|
4058
|
-
|
|
4493
|
+
writeFileSync5(pkgPath, JSON.stringify(pkgJson, null, 2) + "\n", "utf8");
|
|
4059
4494
|
try {
|
|
4060
4495
|
const [rollbackCmd, ...rollbackArgs] = installCommand;
|
|
4061
4496
|
await safeExeca(rollbackCmd, rollbackArgs, { cwd, stdio: "pipe" });
|
|
@@ -4139,7 +4574,9 @@ async function verifyPackageTrust(packageName, targetVersion, policy) {
|
|
|
4139
4574
|
}
|
|
4140
4575
|
const versionData = packument.versions?.[targetVersion];
|
|
4141
4576
|
const scripts = versionData?.scripts ?? {};
|
|
4142
|
-
const installScriptKeys = ["preinstall", "install", "postinstall"].filter(
|
|
4577
|
+
const installScriptKeys = ["preinstall", "install", "postinstall"].filter(
|
|
4578
|
+
(k) => Boolean(scripts[k])
|
|
4579
|
+
);
|
|
4143
4580
|
const hasInstallScripts = installScriptKeys.length > 0;
|
|
4144
4581
|
if (policy.blockInstallScripts && hasInstallScripts) {
|
|
4145
4582
|
return {
|
|
@@ -4149,7 +4586,9 @@ async function verifyPackageTrust(packageName, targetVersion, policy) {
|
|
|
4149
4586
|
reason: `Target release "${packageName}@${targetVersion}" defines risky lifecycle scripts (${installScriptKeys.join(", ")}).`
|
|
4150
4587
|
};
|
|
4151
4588
|
}
|
|
4152
|
-
const hasProvenance = Boolean(
|
|
4589
|
+
const hasProvenance = Boolean(
|
|
4590
|
+
versionData?.dist?.attestations?.provenance || versionData?.dist?.attestations?.url
|
|
4591
|
+
);
|
|
4153
4592
|
if (policy.requireProvenance && !hasProvenance) {
|
|
4154
4593
|
return {
|
|
4155
4594
|
trusted: false,
|
|
@@ -4237,7 +4676,11 @@ async function resolvePrimaryResult(params) {
|
|
|
4237
4676
|
}
|
|
4238
4677
|
};
|
|
4239
4678
|
}
|
|
4240
|
-
const trust2 = await verifyPackageTrust(
|
|
4679
|
+
const trust2 = await verifyPackageTrust(
|
|
4680
|
+
pkg.name,
|
|
4681
|
+
safeUpgrade2.safeVersion,
|
|
4682
|
+
constraints.supplyChainPolicy
|
|
4683
|
+
);
|
|
4241
4684
|
if (!trust2.trusted) {
|
|
4242
4685
|
return {
|
|
4243
4686
|
steps: 1,
|
|
@@ -4308,7 +4751,11 @@ async function resolvePrimaryResult(params) {
|
|
|
4308
4751
|
}
|
|
4309
4752
|
};
|
|
4310
4753
|
}
|
|
4311
|
-
const trust = await verifyPackageTrust(
|
|
4754
|
+
const trust = await verifyPackageTrust(
|
|
4755
|
+
pkg.name,
|
|
4756
|
+
safeUpgrade.safeVersion,
|
|
4757
|
+
constraints.supplyChainPolicy
|
|
4758
|
+
);
|
|
4312
4759
|
if (!trust.trusted) {
|
|
4313
4760
|
return {
|
|
4314
4761
|
steps: 1,
|
|
@@ -4337,7 +4784,8 @@ async function resolvePrimaryResult(params) {
|
|
|
4337
4784
|
installMode: constraints.installMode,
|
|
4338
4785
|
installPreferOffline: constraints.installPreferOffline,
|
|
4339
4786
|
enforceFrozenLockfile: constraints.enforceFrozenLockfile,
|
|
4340
|
-
workspace: constraints.workspace
|
|
4787
|
+
workspace: constraints.workspace,
|
|
4788
|
+
breakingChangeRefactor: constraints.enableBreakingChangeRefactor
|
|
4341
4789
|
});
|
|
4342
4790
|
return {
|
|
4343
4791
|
steps: 2,
|
|
@@ -4348,7 +4796,7 @@ async function resolvePrimaryResult(params) {
|
|
|
4348
4796
|
// src/remediation/tools/fetch-package-source.ts
|
|
4349
4797
|
import { z as z6 } from "zod";
|
|
4350
4798
|
import { mkdir as mkdir2, mkdtemp, readdir as readdir2, readFile as readFile4, rm as rm3 } from "fs/promises";
|
|
4351
|
-
import { join as
|
|
4799
|
+
import { join as join17 } from "path";
|
|
4352
4800
|
import { tmpdir } from "os";
|
|
4353
4801
|
import { execa as execa7 } from "execa";
|
|
4354
4802
|
var fetchPackageSourceTool = defineTool({
|
|
@@ -4368,25 +4816,25 @@ var fetchPackageSourceTool = defineTool({
|
|
|
4368
4816
|
if (safePatterns.length === 0) {
|
|
4369
4817
|
return { success: false, error: "No valid file patterns provided." };
|
|
4370
4818
|
}
|
|
4371
|
-
const tempBaseDir = await mkdtemp(
|
|
4372
|
-
const extractDir =
|
|
4819
|
+
const tempBaseDir = await mkdtemp(join17(tmpdir(), "autoremediator-pkg-"));
|
|
4820
|
+
const extractDir = join17(tempBaseDir, "out");
|
|
4373
4821
|
try {
|
|
4374
4822
|
const scopedName = packageName.split("/").pop();
|
|
4375
4823
|
const npmUrl = `https://registry.npmjs.org/${packageName}/-/${scopedName}-${version}.tgz`;
|
|
4376
4824
|
await mkdir2(tempBaseDir, { recursive: true });
|
|
4377
|
-
const tarballPath =
|
|
4825
|
+
const tarballPath = join17(tempBaseDir, "package.tgz");
|
|
4378
4826
|
await execa7("curl", ["-L", "-o", tarballPath, npmUrl]);
|
|
4379
4827
|
await mkdir2(extractDir, { recursive: true });
|
|
4380
4828
|
await execa7("tar", ["-xzf", tarballPath, "-C", extractDir]);
|
|
4381
4829
|
const extractedContents = await readdir2(extractDir);
|
|
4382
|
-
const packageRootDir = extractedContents.includes("package") ?
|
|
4830
|
+
const packageRootDir = extractedContents.includes("package") ? join17(extractDir, "package") : extractDir;
|
|
4383
4831
|
const sourceCode = {};
|
|
4384
4832
|
async function walkDir(dir, relativeBase) {
|
|
4385
4833
|
try {
|
|
4386
4834
|
const files = await readdir2(dir, { withFileTypes: true });
|
|
4387
4835
|
for (const file of files) {
|
|
4388
|
-
const fullPath =
|
|
4389
|
-
const relPath =
|
|
4836
|
+
const fullPath = join17(dir, file.name);
|
|
4837
|
+
const relPath = join17(relativeBase, file.name);
|
|
4390
4838
|
if (file.isDirectory()) {
|
|
4391
4839
|
if (!["node_modules", ".git", "dist", "build", "coverage", ".next", "out"].includes(
|
|
4392
4840
|
file.name
|
|
@@ -4516,7 +4964,7 @@ Important:
|
|
|
4516
4964
|
}
|
|
4517
4965
|
|
|
4518
4966
|
// src/remediation/tools/generate-patch/helpers.ts
|
|
4519
|
-
import { parseSync as
|
|
4967
|
+
import { parseSync as parseSync3 } from "oxc-parser";
|
|
4520
4968
|
import YAML from "yaml";
|
|
4521
4969
|
function parseLlmAnalysisResponse(text) {
|
|
4522
4970
|
try {
|
|
@@ -4539,7 +4987,7 @@ function validatePatchedCodeSyntax(filePath, code) {
|
|
|
4539
4987
|
const lowerPath = filePath.toLowerCase();
|
|
4540
4988
|
if (/\.[cm]?[jt]sx?$/i.test(lowerPath)) {
|
|
4541
4989
|
try {
|
|
4542
|
-
const res =
|
|
4990
|
+
const res = parseSync3(filePath, code);
|
|
4543
4991
|
if (res.errors && res.errors.length > 0) {
|
|
4544
4992
|
const errorMsg = res.errors.map((e) => e.message).join("; ");
|
|
4545
4993
|
return {
|
|
@@ -4811,15 +5259,124 @@ var generatePatchTool = defineTool({
|
|
|
4811
5259
|
// src/remediation/tools/apply-patch-file/index.ts
|
|
4812
5260
|
import { z as z8 } from "zod";
|
|
4813
5261
|
import { mkdir as mkdir3, writeFile as writeFile3 } from "fs/promises";
|
|
4814
|
-
import {
|
|
5262
|
+
import { existsSync as existsSync14 } from "fs";
|
|
5263
|
+
import { isAbsolute as isAbsolute4, join as join19 } from "path";
|
|
4815
5264
|
import { execa as execa9 } from "execa";
|
|
4816
5265
|
|
|
5266
|
+
// src/remediation/proof-of-fix/runner.ts
|
|
5267
|
+
async function runProofOfFixPhase(options) {
|
|
5268
|
+
const { stage, testFilePath, cwd, timeoutMs = 15e3 } = options;
|
|
5269
|
+
const started = Date.now();
|
|
5270
|
+
const isTs = testFilePath.endsWith(".ts");
|
|
5271
|
+
const nodeArgs = isTs ? ["--experimental-strip-types", testFilePath] : [testFilePath];
|
|
5272
|
+
try {
|
|
5273
|
+
const result = await safeExeca("node", nodeArgs, {
|
|
5274
|
+
cwd,
|
|
5275
|
+
timeoutMs,
|
|
5276
|
+
stdio: "pipe"
|
|
5277
|
+
});
|
|
5278
|
+
const durationMs = Date.now() - started;
|
|
5279
|
+
const output = `${result.stdout || ""}
|
|
5280
|
+
${result.stderr || ""}`.trim();
|
|
5281
|
+
if (stage === "negative") {
|
|
5282
|
+
return {
|
|
5283
|
+
executed: true,
|
|
5284
|
+
passed: false,
|
|
5285
|
+
exitCode: 0,
|
|
5286
|
+
output,
|
|
5287
|
+
durationMs,
|
|
5288
|
+
error: "Exploit reproduction test passed on vulnerable target (failed to reproduce vulnerability)."
|
|
5289
|
+
};
|
|
5290
|
+
}
|
|
5291
|
+
return {
|
|
5292
|
+
executed: true,
|
|
5293
|
+
passed: true,
|
|
5294
|
+
exitCode: 0,
|
|
5295
|
+
output,
|
|
5296
|
+
durationMs
|
|
5297
|
+
};
|
|
5298
|
+
} catch (err) {
|
|
5299
|
+
const durationMs = Date.now() - started;
|
|
5300
|
+
const execaErr = err;
|
|
5301
|
+
const exitCode = typeof execaErr.exitCode === "number" ? execaErr.exitCode : 1;
|
|
5302
|
+
const errorMsg = execaErr.message || String(err);
|
|
5303
|
+
const output = [execaErr.stdout, execaErr.stderr, errorMsg].filter(Boolean).join("\n").trim();
|
|
5304
|
+
if (stage === "negative") {
|
|
5305
|
+
if (execaErr.timedOut || errorMsg.includes("timed out")) {
|
|
5306
|
+
return {
|
|
5307
|
+
executed: true,
|
|
5308
|
+
passed: false,
|
|
5309
|
+
exitCode,
|
|
5310
|
+
output,
|
|
5311
|
+
durationMs,
|
|
5312
|
+
error: "Exploit reproduction test timed out (execution hung)."
|
|
5313
|
+
};
|
|
5314
|
+
}
|
|
5315
|
+
const isHarnessFailure = exitCode === 127 || errorMsg.includes("ENOENT") || output.includes("bad option:") || output.includes("Cannot find module") && output.includes(testFilePath);
|
|
5316
|
+
if (isHarnessFailure) {
|
|
5317
|
+
return {
|
|
5318
|
+
executed: false,
|
|
5319
|
+
passed: false,
|
|
5320
|
+
exitCode,
|
|
5321
|
+
output,
|
|
5322
|
+
durationMs,
|
|
5323
|
+
error: `Reproduction test failed to execute (harness error): ${errorMsg}`
|
|
5324
|
+
};
|
|
5325
|
+
}
|
|
5326
|
+
return {
|
|
5327
|
+
executed: true,
|
|
5328
|
+
passed: true,
|
|
5329
|
+
exitCode,
|
|
5330
|
+
output,
|
|
5331
|
+
durationMs
|
|
5332
|
+
};
|
|
5333
|
+
}
|
|
5334
|
+
return {
|
|
5335
|
+
executed: true,
|
|
5336
|
+
passed: false,
|
|
5337
|
+
exitCode,
|
|
5338
|
+
output,
|
|
5339
|
+
durationMs,
|
|
5340
|
+
error: `Reproduction test failed on patched target: ${errorMsg}`
|
|
5341
|
+
};
|
|
5342
|
+
}
|
|
5343
|
+
}
|
|
5344
|
+
function assembleProofOfFixVerification(params) {
|
|
5345
|
+
const { reproductionTestPath, negativePhase, positivePhase, regressionPhase } = params;
|
|
5346
|
+
const regPhase = regressionPhase ?? {
|
|
5347
|
+
executed: false,
|
|
5348
|
+
passed: true
|
|
5349
|
+
};
|
|
5350
|
+
let status = "verified";
|
|
5351
|
+
let failureReason;
|
|
5352
|
+
if (!negativePhase.passed) {
|
|
5353
|
+
status = "failed";
|
|
5354
|
+
failureReason = negativePhase.error || "negative-phase-failed";
|
|
5355
|
+
} else if (!positivePhase.passed) {
|
|
5356
|
+
status = "failed";
|
|
5357
|
+
failureReason = positivePhase.error || "positive-phase-failed";
|
|
5358
|
+
} else if (regPhase.executed && !regPhase.passed) {
|
|
5359
|
+
status = "failed";
|
|
5360
|
+
failureReason = regPhase.error || "regression-phase-failed";
|
|
5361
|
+
}
|
|
5362
|
+
const durationMs = (negativePhase.durationMs || 0) + (positivePhase.durationMs || 0) + (regPhase.durationMs || 0);
|
|
5363
|
+
return {
|
|
5364
|
+
status,
|
|
5365
|
+
reproductionTestPath,
|
|
5366
|
+
negativePhase,
|
|
5367
|
+
positivePhase,
|
|
5368
|
+
regressionPhase: regPhase,
|
|
5369
|
+
durationMs,
|
|
5370
|
+
failureReason
|
|
5371
|
+
};
|
|
5372
|
+
}
|
|
5373
|
+
|
|
4817
5374
|
// src/remediation/tools/apply-patch-file/helpers.ts
|
|
4818
|
-
import { existsSync as
|
|
5375
|
+
import { existsSync as existsSync13 } from "fs";
|
|
4819
5376
|
import { mkdtemp as mkdtemp2, readFile as readFile5, rm as rm4, writeFile as writeFile2 } from "fs/promises";
|
|
4820
5377
|
import { createHash } from "crypto";
|
|
4821
5378
|
import { tmpdir as tmpdir2 } from "os";
|
|
4822
|
-
import { join as
|
|
5379
|
+
import { join as join18 } from "path";
|
|
4823
5380
|
import { execa as execa8 } from "execa";
|
|
4824
5381
|
async function resolvePatchMode(packageManager, cwd) {
|
|
4825
5382
|
if (packageManager === "npm" || packageManager === "bun" || packageManager === "deno")
|
|
@@ -4854,7 +5411,7 @@ async function writePatchManifest(manifestFilePath, artifact) {
|
|
|
4854
5411
|
await writeFile2(manifestFilePath, JSON.stringify(artifact, null, 2) + "\n", "utf8");
|
|
4855
5412
|
}
|
|
4856
5413
|
async function configurePatchPackagePostinstall(cwd, packageManager) {
|
|
4857
|
-
const pkgJsonPath =
|
|
5414
|
+
const pkgJsonPath = join18(cwd, "package.json");
|
|
4858
5415
|
let pkgJson;
|
|
4859
5416
|
try {
|
|
4860
5417
|
pkgJson = JSON.parse(await readFile5(pkgJsonPath, "utf8"));
|
|
@@ -4894,7 +5451,7 @@ async function configurePatchPackagePostinstall(cwd, packageManager) {
|
|
|
4894
5451
|
return { success: true };
|
|
4895
5452
|
}
|
|
4896
5453
|
async function capturePackageJsonSnapshot(cwd) {
|
|
4897
|
-
const path =
|
|
5454
|
+
const path = join18(cwd, "package.json");
|
|
4898
5455
|
try {
|
|
4899
5456
|
const content = await readFile5(path, "utf8");
|
|
4900
5457
|
return { path, content };
|
|
@@ -4966,8 +5523,8 @@ ${createResult.stderr}`);
|
|
|
4966
5523
|
error: `Could not determine native patch directory for ${packageSpec}.`
|
|
4967
5524
|
};
|
|
4968
5525
|
}
|
|
4969
|
-
const tempPatchDir = await mkdtemp2(
|
|
4970
|
-
const tempPatchFile =
|
|
5526
|
+
const tempPatchDir = await mkdtemp2(join18(tmpdir2(), "autoremediator-native-patch-"));
|
|
5527
|
+
const tempPatchFile = join18(tempPatchDir, "change.patch");
|
|
4971
5528
|
try {
|
|
4972
5529
|
await writeFile2(tempPatchFile, patchContent, "utf8");
|
|
4973
5530
|
await execa8("patch", ["-p1", "-i", tempPatchFile], {
|
|
@@ -4998,12 +5555,12 @@ ${createResult.stderr}`);
|
|
|
4998
5555
|
function extractPatchDirectory(output) {
|
|
4999
5556
|
const lines = output.split(/\r?\n/).map((line) => line.trim()).filter(Boolean);
|
|
5000
5557
|
for (const line of lines) {
|
|
5001
|
-
if (
|
|
5558
|
+
if (existsSync13(line)) {
|
|
5002
5559
|
return line;
|
|
5003
5560
|
}
|
|
5004
5561
|
const tokens = line.split(/\s+/).map((token) => token.replace(/^['"]|['"]$/g, ""));
|
|
5005
5562
|
for (const token of tokens) {
|
|
5006
|
-
if (token.startsWith("/") &&
|
|
5563
|
+
if (token.startsWith("/") && existsSync13(token)) {
|
|
5007
5564
|
return token;
|
|
5008
5565
|
}
|
|
5009
5566
|
}
|
|
@@ -5074,6 +5631,10 @@ var applyPatchFileTool = defineTool({
|
|
|
5074
5631
|
workspace: z8.string().optional(),
|
|
5075
5632
|
validateWithTests: z8.boolean().optional().default(true).describe("Run package manager test command to validate patch doesn't break anything"),
|
|
5076
5633
|
reproductionTest: z8.string().optional().describe("Optional synthesized reproduction test script verifying fix"),
|
|
5634
|
+
proofOfFix: z8.union([z8.boolean(), z8.string()]).optional().describe(
|
|
5635
|
+
"If true or script path/content, enforce dual-stage exploit reproduction (assert failure pre-patch, assert pass post-patch)"
|
|
5636
|
+
),
|
|
5637
|
+
requireProofOfFix: z8.boolean().optional().describe("If true, fail and roll back patch if proof-of-fix verification fails"),
|
|
5077
5638
|
dryRun: z8.boolean().optional().default(false).describe("If true, report but do not mutate files")
|
|
5078
5639
|
}).refine((value) => Boolean(value.patchContent || value.patches && value.patches.length > 0), {
|
|
5079
5640
|
message: "Either patchContent or patches must be provided"
|
|
@@ -5096,6 +5657,8 @@ var applyPatchFileTool = defineTool({
|
|
|
5096
5657
|
riskLevel,
|
|
5097
5658
|
validateWithTests,
|
|
5098
5659
|
reproductionTest,
|
|
5660
|
+
proofOfFix,
|
|
5661
|
+
requireProofOfFix,
|
|
5099
5662
|
dryRun
|
|
5100
5663
|
}) => {
|
|
5101
5664
|
try {
|
|
@@ -5146,7 +5709,7 @@ var applyPatchFileTool = defineTool({
|
|
|
5146
5709
|
};
|
|
5147
5710
|
}
|
|
5148
5711
|
const patchFileName = buildPatchFileName(packageName, vulnerableVersion);
|
|
5149
|
-
const patchFilePath =
|
|
5712
|
+
const patchFilePath = join19(cwd, patchesDir, patchFileName);
|
|
5150
5713
|
const manifestFilePath = `${patchFilePath}.json`;
|
|
5151
5714
|
const generatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
5152
5715
|
const baseArtifact = {
|
|
@@ -5185,7 +5748,7 @@ var applyPatchFileTool = defineTool({
|
|
|
5185
5748
|
}
|
|
5186
5749
|
return withRepoLock(cwd, async () => {
|
|
5187
5750
|
const packageJsonSnapshot = patchModeRequiresPackageJsonSnapshot(pm) ? await capturePackageJsonSnapshot(cwd) : void 0;
|
|
5188
|
-
const patchesDirPath =
|
|
5751
|
+
const patchesDirPath = join19(cwd, patchesDir);
|
|
5189
5752
|
await mkdir3(patchesDirPath, { recursive: true });
|
|
5190
5753
|
await writeFile3(patchFilePath, selectedPatch, "utf8");
|
|
5191
5754
|
validationPhases.push({
|
|
@@ -5208,6 +5771,101 @@ var applyPatchFileTool = defineTool({
|
|
|
5208
5771
|
});
|
|
5209
5772
|
artifact.validationPhases = validationPhases;
|
|
5210
5773
|
await writePatchManifest(manifestFilePath, artifact);
|
|
5774
|
+
let reproductionTestPath;
|
|
5775
|
+
let negativePhaseResult;
|
|
5776
|
+
if (typeof proofOfFix === "string" && proofOfFix.trim().length > 0) {
|
|
5777
|
+
const resolvedPath = isAbsolute4(proofOfFix) ? proofOfFix : join19(cwd, proofOfFix);
|
|
5778
|
+
if (existsSync14(resolvedPath)) {
|
|
5779
|
+
reproductionTestPath = resolvedPath;
|
|
5780
|
+
} else {
|
|
5781
|
+
const safeName = packageName.replace(/^@/, "").replace(/\//g, "+");
|
|
5782
|
+
reproductionTestPath = join19(patchesDirPath, `${safeName}+${vulnerableVersion}.test.cjs`);
|
|
5783
|
+
await writeFile3(reproductionTestPath, proofOfFix, "utf8");
|
|
5784
|
+
}
|
|
5785
|
+
} else if (reproductionTest) {
|
|
5786
|
+
const safeName = packageName.replace(/^@/, "").replace(/\//g, "+");
|
|
5787
|
+
reproductionTestPath = join19(patchesDirPath, `${safeName}+${vulnerableVersion}.test.cjs`);
|
|
5788
|
+
await writeFile3(reproductionTestPath, reproductionTest, "utf8");
|
|
5789
|
+
}
|
|
5790
|
+
const isProofOfFixActive = Boolean(proofOfFix || requireProofOfFix);
|
|
5791
|
+
if (isProofOfFixActive) {
|
|
5792
|
+
if (!reproductionTestPath) {
|
|
5793
|
+
if (requireProofOfFix) {
|
|
5794
|
+
await cleanupPatchArtifacts({
|
|
5795
|
+
cwd,
|
|
5796
|
+
patchFilePath,
|
|
5797
|
+
manifestFilePath,
|
|
5798
|
+
patchMode,
|
|
5799
|
+
packageJsonSnapshot,
|
|
5800
|
+
installCommand,
|
|
5801
|
+
rerunInstall: false
|
|
5802
|
+
});
|
|
5803
|
+
return {
|
|
5804
|
+
success: false,
|
|
5805
|
+
packageName,
|
|
5806
|
+
vulnerableVersion,
|
|
5807
|
+
applied: false,
|
|
5808
|
+
dryRun: false,
|
|
5809
|
+
message: "Proof-of-fix verification required, but no reproduction test or script was supplied.",
|
|
5810
|
+
patchFilePath,
|
|
5811
|
+
manifestFilePath,
|
|
5812
|
+
patchPath: patchFilePath,
|
|
5813
|
+
patchMode,
|
|
5814
|
+
postinstallConfigured: false,
|
|
5815
|
+
patchArtifact: {
|
|
5816
|
+
...artifact,
|
|
5817
|
+
applied: false,
|
|
5818
|
+
validationPhases
|
|
5819
|
+
},
|
|
5820
|
+
validationPhases,
|
|
5821
|
+
error: "Proof-of-fix verification required, but no reproduction test or script was supplied."
|
|
5822
|
+
};
|
|
5823
|
+
}
|
|
5824
|
+
} else {
|
|
5825
|
+
negativePhaseResult = await runProofOfFixPhase({
|
|
5826
|
+
stage: "negative",
|
|
5827
|
+
testFilePath: reproductionTestPath,
|
|
5828
|
+
cwd
|
|
5829
|
+
});
|
|
5830
|
+
validationPhases.push({
|
|
5831
|
+
phase: "proof-of-fix-negative",
|
|
5832
|
+
passed: negativePhaseResult.passed,
|
|
5833
|
+
message: negativePhaseResult.passed ? "Reproduction test reproduced vulnerability on unpatched code." : void 0,
|
|
5834
|
+
error: negativePhaseResult.passed ? void 0 : negativePhaseResult.error
|
|
5835
|
+
});
|
|
5836
|
+
if (!negativePhaseResult.passed && requireProofOfFix) {
|
|
5837
|
+
await cleanupPatchArtifacts({
|
|
5838
|
+
cwd,
|
|
5839
|
+
patchFilePath,
|
|
5840
|
+
manifestFilePath,
|
|
5841
|
+
patchMode,
|
|
5842
|
+
packageJsonSnapshot,
|
|
5843
|
+
installCommand,
|
|
5844
|
+
rerunInstall: false
|
|
5845
|
+
});
|
|
5846
|
+
return {
|
|
5847
|
+
success: false,
|
|
5848
|
+
packageName,
|
|
5849
|
+
vulnerableVersion,
|
|
5850
|
+
applied: false,
|
|
5851
|
+
dryRun: false,
|
|
5852
|
+
message: `Proof-of-fix negative phase failed: ${negativePhaseResult.error}`,
|
|
5853
|
+
patchFilePath,
|
|
5854
|
+
manifestFilePath,
|
|
5855
|
+
patchPath: patchFilePath,
|
|
5856
|
+
patchMode,
|
|
5857
|
+
postinstallConfigured: false,
|
|
5858
|
+
patchArtifact: {
|
|
5859
|
+
...artifact,
|
|
5860
|
+
applied: false,
|
|
5861
|
+
validationPhases
|
|
5862
|
+
},
|
|
5863
|
+
validationPhases,
|
|
5864
|
+
error: `Proof-of-fix negative phase failed: ${negativePhaseResult.error}`
|
|
5865
|
+
};
|
|
5866
|
+
}
|
|
5867
|
+
}
|
|
5868
|
+
}
|
|
5211
5869
|
const applyResult = patchMode === "patch-package" ? await configurePatchPackagePostinstall(cwd, pm) : await applyNativePatch({
|
|
5212
5870
|
cwd,
|
|
5213
5871
|
packageName,
|
|
@@ -5348,34 +6006,90 @@ var applyPatchFileTool = defineTool({
|
|
|
5348
6006
|
message: "Patch validation tests passed."
|
|
5349
6007
|
});
|
|
5350
6008
|
}
|
|
5351
|
-
let
|
|
5352
|
-
if (
|
|
5353
|
-
|
|
5354
|
-
|
|
5355
|
-
|
|
5356
|
-
|
|
5357
|
-
|
|
5358
|
-
validationPhases.push({
|
|
5359
|
-
phase: "reproduction-test",
|
|
5360
|
-
passed: reproRun.exitCode === 0,
|
|
5361
|
-
message: "Reproduction test verified fix successfully."
|
|
6009
|
+
let positivePhaseResult;
|
|
6010
|
+
if (reproductionTestPath) {
|
|
6011
|
+
if (isProofOfFixActive) {
|
|
6012
|
+
positivePhaseResult = await runProofOfFixPhase({
|
|
6013
|
+
stage: "positive",
|
|
6014
|
+
testFilePath: reproductionTestPath,
|
|
6015
|
+
cwd
|
|
5362
6016
|
});
|
|
5363
|
-
} catch (err) {
|
|
5364
|
-
const reproErr = err instanceof Error ? err.message : String(err);
|
|
5365
6017
|
validationPhases.push({
|
|
5366
|
-
phase: "
|
|
5367
|
-
passed:
|
|
5368
|
-
|
|
6018
|
+
phase: "proof-of-fix-positive",
|
|
6019
|
+
passed: positivePhaseResult.passed,
|
|
6020
|
+
message: positivePhaseResult.passed ? "Reproduction test passed on patched code (vulnerability mitigated)." : void 0,
|
|
6021
|
+
error: positivePhaseResult.passed ? void 0 : positivePhaseResult.error
|
|
5369
6022
|
});
|
|
6023
|
+
if (!positivePhaseResult.passed && (requireProofOfFix || validateWithTests)) {
|
|
6024
|
+
await cleanupPatchArtifacts({
|
|
6025
|
+
cwd,
|
|
6026
|
+
patchFilePath,
|
|
6027
|
+
manifestFilePath,
|
|
6028
|
+
patchMode,
|
|
6029
|
+
packageJsonSnapshot,
|
|
6030
|
+
installCommand,
|
|
6031
|
+
rerunInstall: patchMode === "patch-package"
|
|
6032
|
+
});
|
|
6033
|
+
return {
|
|
6034
|
+
success: false,
|
|
6035
|
+
packageName,
|
|
6036
|
+
vulnerableVersion,
|
|
6037
|
+
applied: false,
|
|
6038
|
+
dryRun: false,
|
|
6039
|
+
message: `Proof-of-fix positive phase failed: ${positivePhaseResult.error}`,
|
|
6040
|
+
patchFilePath,
|
|
6041
|
+
manifestFilePath,
|
|
6042
|
+
patchPath: patchFilePath,
|
|
6043
|
+
patchMode,
|
|
6044
|
+
postinstallConfigured: false,
|
|
6045
|
+
patchArtifact: {
|
|
6046
|
+
...artifact,
|
|
6047
|
+
applied: false,
|
|
6048
|
+
validationPhases
|
|
6049
|
+
},
|
|
6050
|
+
validationPhases,
|
|
6051
|
+
error: `Proof-of-fix positive phase failed: ${positivePhaseResult.error}`
|
|
6052
|
+
};
|
|
6053
|
+
}
|
|
6054
|
+
} else {
|
|
6055
|
+
try {
|
|
6056
|
+
const reproRun = await execa9("node", [reproductionTestPath], { cwd, stdio: "pipe" });
|
|
6057
|
+
validationPhases.push({
|
|
6058
|
+
phase: "reproduction-test",
|
|
6059
|
+
passed: reproRun.exitCode === 0,
|
|
6060
|
+
message: "Reproduction test verified fix successfully."
|
|
6061
|
+
});
|
|
6062
|
+
} catch (err) {
|
|
6063
|
+
const reproErr = err instanceof Error ? err.message : String(err);
|
|
6064
|
+
validationPhases.push({
|
|
6065
|
+
phase: "reproduction-test",
|
|
6066
|
+
passed: false,
|
|
6067
|
+
error: `Reproduction test failed: ${reproErr}`
|
|
6068
|
+
});
|
|
6069
|
+
}
|
|
5370
6070
|
}
|
|
5371
6071
|
}
|
|
6072
|
+
let proofOfFixVerification;
|
|
6073
|
+
if (negativePhaseResult && positivePhaseResult) {
|
|
6074
|
+
proofOfFixVerification = assembleProofOfFixVerification({
|
|
6075
|
+
reproductionTestPath,
|
|
6076
|
+
negativePhase: negativePhaseResult,
|
|
6077
|
+
positivePhase: positivePhaseResult,
|
|
6078
|
+
regressionPhase: validationResult ? {
|
|
6079
|
+
executed: true,
|
|
6080
|
+
passed: validationResult.passed,
|
|
6081
|
+
error: validationResult.error
|
|
6082
|
+
} : void 0
|
|
6083
|
+
});
|
|
6084
|
+
}
|
|
5372
6085
|
const finalArtifact = {
|
|
5373
6086
|
...artifact,
|
|
5374
6087
|
applied: true,
|
|
5375
6088
|
dryRun: false,
|
|
5376
6089
|
reproductionTest,
|
|
5377
6090
|
reproductionTestPath,
|
|
5378
|
-
validationPhases
|
|
6091
|
+
validationPhases,
|
|
6092
|
+
proofOfFix: proofOfFixVerification
|
|
5379
6093
|
};
|
|
5380
6094
|
await writePatchManifest(manifestFilePath, finalArtifact);
|
|
5381
6095
|
return {
|
|
@@ -5392,7 +6106,8 @@ var applyPatchFileTool = defineTool({
|
|
|
5392
6106
|
postinstallConfigured: patchMode === "patch-package",
|
|
5393
6107
|
patchArtifact: finalArtifact,
|
|
5394
6108
|
validation: validationResult,
|
|
5395
|
-
validationPhases
|
|
6109
|
+
validationPhases,
|
|
6110
|
+
proofOfFix: proofOfFixVerification
|
|
5396
6111
|
};
|
|
5397
6112
|
});
|
|
5398
6113
|
} catch (err) {
|
|
@@ -5636,6 +6351,9 @@ async function tryLocalPatchFallback(params) {
|
|
|
5636
6351
|
workspace: params.workspace,
|
|
5637
6352
|
riskLevel: patchResult.riskLevel,
|
|
5638
6353
|
validateWithTests: params.runTests,
|
|
6354
|
+
reproductionTest: patchResult.reproductionTest,
|
|
6355
|
+
proofOfFix: params.proofOfFix,
|
|
6356
|
+
requireProofOfFix: params.requireProofOfFix,
|
|
5639
6357
|
dryRun: params.dryRun
|
|
5640
6358
|
});
|
|
5641
6359
|
steps += 1;
|
|
@@ -5684,12 +6402,14 @@ function resolveLocalRunOptions(options) {
|
|
|
5684
6402
|
const runTests = options.runTests ?? false;
|
|
5685
6403
|
const policy = options.policy ?? "";
|
|
5686
6404
|
const patchesDir = options.patchesDir || "./patches";
|
|
6405
|
+
const loadedPolicy = loadPolicy(cwd, options.policy);
|
|
5687
6406
|
const constraints = {
|
|
5688
6407
|
...options.constraints,
|
|
5689
|
-
typecheck: options.typecheck ?? options.constraints?.typecheck,
|
|
5690
|
-
supplyChainPolicy: options.supplyChainPolicy ?? options.constraints?.supplyChainPolicy
|
|
6408
|
+
typecheck: options.typecheck ?? options.constraints?.typecheck ?? loadedPolicy.constraints?.typecheck,
|
|
6409
|
+
supplyChainPolicy: options.supplyChainPolicy ?? options.constraints?.supplyChainPolicy ?? loadedPolicy.constraints?.supplyChainPolicy,
|
|
6410
|
+
enableBreakingChangeRefactor: options.breakingChangeRefactor ?? options.constraints?.enableBreakingChangeRefactor ?? loadedPolicy.constraints?.enableBreakingChangeRefactor,
|
|
6411
|
+
requireProofOfFix: options.requireProofOfFix ?? options.constraints?.requireProofOfFix ?? loadedPolicy.constraints?.requireProofOfFix
|
|
5691
6412
|
};
|
|
5692
|
-
const loadedPolicy = loadPolicy(cwd, options.policy);
|
|
5693
6413
|
const llmProvider = resolveProvider(options);
|
|
5694
6414
|
return {
|
|
5695
6415
|
cwd,
|
|
@@ -5720,7 +6440,10 @@ function resolveLocalRunOptions(options) {
|
|
|
5720
6440
|
regressionCheck: options.regressionCheck ?? false,
|
|
5721
6441
|
escalationGraph: options.escalationGraph ?? loadedPolicy.escalationGraph,
|
|
5722
6442
|
offlineIntelligence: options.offlineIntelligence ?? false,
|
|
5723
|
-
intelligenceSnapshotPath: options.intelligenceSnapshotPath
|
|
6443
|
+
intelligenceSnapshotPath: options.intelligenceSnapshotPath,
|
|
6444
|
+
proofOfFix: options.proofOfFix,
|
|
6445
|
+
requireProofOfFix: options.requireProofOfFix ?? constraints.requireProofOfFix,
|
|
6446
|
+
breakingChangeRefactor: options.breakingChangeRefactor ?? constraints.enableBreakingChangeRefactor
|
|
5724
6447
|
};
|
|
5725
6448
|
}
|
|
5726
6449
|
|
|
@@ -5888,7 +6611,9 @@ async function runLocalRemediationPipeline(cveId, options = {}) {
|
|
|
5888
6611
|
regressionCheck,
|
|
5889
6612
|
escalationGraph,
|
|
5890
6613
|
offlineIntelligence,
|
|
5891
|
-
intelligenceSnapshotPath
|
|
6614
|
+
intelligenceSnapshotPath,
|
|
6615
|
+
proofOfFix,
|
|
6616
|
+
requireProofOfFix
|
|
5892
6617
|
} = resolved;
|
|
5893
6618
|
const correlation = {
|
|
5894
6619
|
requestId: options.requestId,
|
|
@@ -6065,7 +6790,9 @@ async function runLocalRemediationPipeline(cveId, options = {}) {
|
|
|
6065
6790
|
installPreferOffline: constraints.installPreferOffline,
|
|
6066
6791
|
enforceFrozenLockfile: constraints.enforceFrozenLockfile,
|
|
6067
6792
|
workspace: constraints.workspace,
|
|
6068
|
-
vulnerableRange: vulnerable.affected.vulnerableRange
|
|
6793
|
+
vulnerableRange: vulnerable.affected.vulnerableRange,
|
|
6794
|
+
proofOfFix,
|
|
6795
|
+
requireProofOfFix
|
|
6069
6796
|
});
|
|
6070
6797
|
agentSteps += fallback.steps;
|
|
6071
6798
|
collectedResults.push(finalizePatchResult(fallback.result));
|
|
@@ -6171,8 +6898,8 @@ function accumulateStepResults(params) {
|
|
|
6171
6898
|
}
|
|
6172
6899
|
|
|
6173
6900
|
// src/remediation/orchestration-prompt.ts
|
|
6174
|
-
import { existsSync as
|
|
6175
|
-
import { join as
|
|
6901
|
+
import { existsSync as existsSync15, readFileSync as readFileSync15 } from "fs";
|
|
6902
|
+
import { join as join20 } from "path";
|
|
6176
6903
|
function buildProviderAddendum(provider, personality = "balanced") {
|
|
6177
6904
|
const personalityDirective = personality === "analytical" ? "Use concise, explicit rationale for tool decisions and unresolved outcomes." : personality === "pragmatic" ? "Prefer the smallest safe remediation path while preserving policy and validation gates." : "Balance concise execution with brief rationale for risky or unresolved outcomes.";
|
|
6178
6905
|
const providerDirective = provider === "remote" ? "Use strict structured output and deterministic reporting fields." : "Use deterministic-first behavior and only rely on remote model fallback when required by patch generation.";
|
|
@@ -6183,13 +6910,13 @@ Provider profile:
|
|
|
6183
6910
|
- ${personalityDirective}`;
|
|
6184
6911
|
}
|
|
6185
6912
|
function loadOrchestrationPrompt(ctx) {
|
|
6186
|
-
const promptPath =
|
|
6913
|
+
const promptPath = join20(
|
|
6187
6914
|
process.cwd(),
|
|
6188
6915
|
".github",
|
|
6189
6916
|
"instructions",
|
|
6190
6917
|
"orchestration.instructions.md"
|
|
6191
6918
|
);
|
|
6192
|
-
if (!
|
|
6919
|
+
if (!existsSync15(promptPath)) {
|
|
6193
6920
|
return `You are autoremediator, an agentic security remediation system for Node.js package dependencies.
|
|
6194
6921
|
Working directory: ${ctx.cwd}
|
|
6195
6922
|
Package manager: ${ctx.packageManager}
|
|
@@ -6215,7 +6942,7 @@ Fallback sequence (when neither version bump nor override can be applied):
|
|
|
6215
6942
|
|
|
6216
6943
|
Always respect dryRun and policy constraints.`;
|
|
6217
6944
|
}
|
|
6218
|
-
const template =
|
|
6945
|
+
const template = readFileSync15(promptPath, "utf8");
|
|
6219
6946
|
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).replaceAll(
|
|
6220
6947
|
"{{directDependenciesOnly}}",
|
|
6221
6948
|
String(ctx.constraints.directDependenciesOnly ?? false)
|
|
@@ -6694,6 +7421,8 @@ function createRuntimeToolsForRun(options) {
|
|
|
6694
7421
|
installPreferOffline: constraints.installPreferOffline,
|
|
6695
7422
|
enforceFrozenLockfile: constraints.enforceFrozenLockfile,
|
|
6696
7423
|
workspace: constraints.workspace,
|
|
7424
|
+
typecheck: input.typecheck ?? options.typecheck ?? constraints.typecheck,
|
|
7425
|
+
breakingChangeRefactor: input.breakingChangeRefactor ?? options.breakingChangeRefactor ?? constraints.enableBreakingChangeRefactor,
|
|
6697
7426
|
dryRun: preview ? true : input.dryRun
|
|
6698
7427
|
})
|
|
6699
7428
|
};
|
|
@@ -6718,6 +7447,8 @@ function createRuntimeToolsForRun(options) {
|
|
|
6718
7447
|
installPreferOffline: constraints.installPreferOffline,
|
|
6719
7448
|
enforceFrozenLockfile: constraints.enforceFrozenLockfile,
|
|
6720
7449
|
workspace: constraints.workspace,
|
|
7450
|
+
proofOfFix: input.proofOfFix ?? options.proofOfFix,
|
|
7451
|
+
requireProofOfFix: input.requireProofOfFix ?? options.requireProofOfFix ?? constraints.requireProofOfFix,
|
|
6721
7452
|
dryRun: preview ? true : input.dryRun
|
|
6722
7453
|
})
|
|
6723
7454
|
};
|
|
@@ -7148,13 +7879,17 @@ function resolveConstraints(options, cwd) {
|
|
|
7148
7879
|
installMode: options.constraints?.installMode ?? policy.constraints?.installMode ?? "deterministic",
|
|
7149
7880
|
installPreferOffline: options.constraints?.installPreferOffline ?? policy.constraints?.installPreferOffline,
|
|
7150
7881
|
enforceFrozenLockfile: options.constraints?.enforceFrozenLockfile ?? policy.constraints?.enforceFrozenLockfile,
|
|
7151
|
-
workspace: options.constraints?.workspace ?? policy.constraints?.workspace
|
|
7882
|
+
workspace: options.constraints?.workspace ?? policy.constraints?.workspace,
|
|
7883
|
+
typecheck: options.typecheck ?? options.constraints?.typecheck ?? policy.constraints?.typecheck,
|
|
7884
|
+
requireProofOfFix: options.requireProofOfFix ?? options.constraints?.requireProofOfFix ?? policy.constraints?.requireProofOfFix,
|
|
7885
|
+
enableBreakingChangeRefactor: options.breakingChangeRefactor ?? options.constraints?.enableBreakingChangeRefactor ?? policy.constraints?.enableBreakingChangeRefactor,
|
|
7886
|
+
supplyChainPolicy: options.supplyChainPolicy ?? options.constraints?.supplyChainPolicy ?? policy.constraints?.supplyChainPolicy
|
|
7152
7887
|
};
|
|
7153
7888
|
}
|
|
7154
7889
|
|
|
7155
7890
|
// src/platform/evidence.ts
|
|
7156
|
-
import { mkdirSync as mkdirSync3, writeFileSync as
|
|
7157
|
-
import { join as
|
|
7891
|
+
import { mkdirSync as mkdirSync3, writeFileSync as writeFileSync6 } from "fs";
|
|
7892
|
+
import { join as join21 } from "path";
|
|
7158
7893
|
function createEvidenceLog(cwd, cveIds, context = {}) {
|
|
7159
7894
|
return {
|
|
7160
7895
|
runId: `${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,
|
|
@@ -7186,10 +7921,10 @@ function finalizeEvidence(log) {
|
|
|
7186
7921
|
return log;
|
|
7187
7922
|
}
|
|
7188
7923
|
function writeEvidenceLog(cwd, log) {
|
|
7189
|
-
const dir =
|
|
7924
|
+
const dir = join21(cwd, ".autoremediator", "evidence");
|
|
7190
7925
|
mkdirSync3(dir, { recursive: true });
|
|
7191
|
-
const filePath =
|
|
7192
|
-
|
|
7926
|
+
const filePath = join21(dir, `${log.runId}.json`);
|
|
7927
|
+
writeFileSync6(filePath, JSON.stringify(log, null, 2) + "\n", "utf8");
|
|
7193
7928
|
return filePath;
|
|
7194
7929
|
}
|
|
7195
7930
|
|
|
@@ -7431,12 +8166,12 @@ async function planRemediation(cveId, options = {}) {
|
|
|
7431
8166
|
}
|
|
7432
8167
|
|
|
7433
8168
|
// src/scanner/parse-input.ts
|
|
7434
|
-
import { extname, resolve as
|
|
7435
|
-
import { readFileSync as
|
|
8169
|
+
import { extname, resolve as resolve5 } from "path";
|
|
8170
|
+
import { readFileSync as readFileSync19 } from "fs";
|
|
7436
8171
|
import { execa as execa11 } from "execa";
|
|
7437
8172
|
|
|
7438
8173
|
// src/scanner/adapters/npm-audit.ts
|
|
7439
|
-
import { readFileSync as
|
|
8174
|
+
import { readFileSync as readFileSync16 } from "fs";
|
|
7440
8175
|
var CVE_REGEX = /CVE-\d{4}-\d{1,7}/gi;
|
|
7441
8176
|
var GHSA_REGEX = /GHSA-[a-z0-9]{4}-[a-z0-9]{4}-[a-z0-9]{4}/gi;
|
|
7442
8177
|
function normalizeSeverity(raw) {
|
|
@@ -7472,12 +8207,12 @@ function parseNpmAuditJsonFromString(content) {
|
|
|
7472
8207
|
return findings;
|
|
7473
8208
|
}
|
|
7474
8209
|
function parseNpmAuditJsonFile(filePath) {
|
|
7475
|
-
const content =
|
|
8210
|
+
const content = readFileSync16(filePath, "utf8");
|
|
7476
8211
|
return parseNpmAuditJsonFromString(content);
|
|
7477
8212
|
}
|
|
7478
8213
|
|
|
7479
8214
|
// src/scanner/adapters/yarn-audit.ts
|
|
7480
|
-
import { readFileSync as
|
|
8215
|
+
import { readFileSync as readFileSync17 } from "fs";
|
|
7481
8216
|
var CVE_REGEX2 = /CVE-\d{4}-\d{1,7}/gi;
|
|
7482
8217
|
var GHSA_REGEX2 = /GHSA-[a-z0-9]{4}-[a-z0-9]{4}-[a-z0-9]{4}/gi;
|
|
7483
8218
|
function normalizeSeverity2(raw) {
|
|
@@ -7522,12 +8257,12 @@ function parseYarnAuditJsonFromString(content) {
|
|
|
7522
8257
|
return findings;
|
|
7523
8258
|
}
|
|
7524
8259
|
function parseYarnAuditJsonFile(filePath) {
|
|
7525
|
-
const content =
|
|
8260
|
+
const content = readFileSync17(filePath, "utf8");
|
|
7526
8261
|
return parseYarnAuditJsonFromString(content);
|
|
7527
8262
|
}
|
|
7528
8263
|
|
|
7529
8264
|
// src/scanner/adapters/sarif.ts
|
|
7530
|
-
import { readFileSync as
|
|
8265
|
+
import { readFileSync as readFileSync18 } from "fs";
|
|
7531
8266
|
var CVE_REGEX3 = /CVE-\d{4}-\d{1,7}/gi;
|
|
7532
8267
|
function extractPackageName(result) {
|
|
7533
8268
|
const pkg = result.properties?.["packageName"];
|
|
@@ -7565,7 +8300,7 @@ function parseSarifFromString(content) {
|
|
|
7565
8300
|
return findings;
|
|
7566
8301
|
}
|
|
7567
8302
|
function parseSarifFile(filePath) {
|
|
7568
|
-
const content =
|
|
8303
|
+
const content = readFileSync18(filePath, "utf8");
|
|
7569
8304
|
return parseSarifFromString(content);
|
|
7570
8305
|
}
|
|
7571
8306
|
|
|
@@ -7574,7 +8309,7 @@ function parseScanInput(filePath, format) {
|
|
|
7574
8309
|
if (filePath.includes("\0")) {
|
|
7575
8310
|
throw new Error("Invalid scan input path: path contains null bytes");
|
|
7576
8311
|
}
|
|
7577
|
-
const resolvedPath =
|
|
8312
|
+
const resolvedPath = resolve5(filePath);
|
|
7578
8313
|
const resolved = format === "auto" ? inferFormat(resolvedPath) : format;
|
|
7579
8314
|
if (resolved === "npm-audit") {
|
|
7580
8315
|
return parseNpmAuditJsonFile(resolvedPath);
|
|
@@ -7650,7 +8385,7 @@ function inferFormat(filePath) {
|
|
|
7650
8385
|
const ext = extname(filePath).toLowerCase();
|
|
7651
8386
|
if (ext === ".sarif") return "sarif";
|
|
7652
8387
|
try {
|
|
7653
|
-
const content =
|
|
8388
|
+
const content = readFileSync19(filePath, "utf8");
|
|
7654
8389
|
const firstLine = content.split("\n").find((line) => line.trim().startsWith("{"));
|
|
7655
8390
|
if (firstLine) {
|
|
7656
8391
|
const parsed = JSON.parse(firstLine);
|
|
@@ -8061,14 +8796,14 @@ async function remediatePortfolio(targets, options = {}) {
|
|
|
8061
8796
|
}
|
|
8062
8797
|
|
|
8063
8798
|
// src/api/update-outdated/index.ts
|
|
8064
|
-
import { join as
|
|
8065
|
-
import { readFileSync as
|
|
8799
|
+
import { join as join22 } from "path";
|
|
8800
|
+
import { readFileSync as readFileSync20, writeFileSync as writeFileSync7 } from "fs";
|
|
8066
8801
|
import { execa as execa12 } from "execa";
|
|
8067
8802
|
async function applyBump(params) {
|
|
8068
|
-
const pkgPath =
|
|
8803
|
+
const pkgPath = join22(params.cwd, "package.json");
|
|
8069
8804
|
let pkgJson;
|
|
8070
8805
|
try {
|
|
8071
|
-
pkgJson = JSON.parse(
|
|
8806
|
+
pkgJson = JSON.parse(readFileSync20(pkgPath, "utf8"));
|
|
8072
8807
|
} catch {
|
|
8073
8808
|
return {
|
|
8074
8809
|
applied: false,
|
|
@@ -8090,13 +8825,13 @@ async function applyBump(params) {
|
|
|
8090
8825
|
const newRange = `${prefix}${params.toVersion}`;
|
|
8091
8826
|
return withRepoLock(params.cwd, async () => {
|
|
8092
8827
|
pkgJson[depField][params.packageName] = newRange;
|
|
8093
|
-
|
|
8828
|
+
writeFileSync7(pkgPath, JSON.stringify(pkgJson, null, 2) + "\n", "utf8");
|
|
8094
8829
|
try {
|
|
8095
8830
|
const [installCmd, ...installArgs] = params.installCommand;
|
|
8096
8831
|
await execa12(installCmd, installArgs, { cwd: params.cwd, stdio: "pipe" });
|
|
8097
8832
|
} catch (err) {
|
|
8098
8833
|
pkgJson[depField][params.packageName] = currentRange;
|
|
8099
|
-
|
|
8834
|
+
writeFileSync7(pkgPath, JSON.stringify(pkgJson, null, 2) + "\n", "utf8");
|
|
8100
8835
|
const message = err instanceof Error ? err.message : String(err);
|
|
8101
8836
|
return {
|
|
8102
8837
|
applied: false,
|
|
@@ -8109,7 +8844,7 @@ async function applyBump(params) {
|
|
|
8109
8844
|
await execa12(testCmd, testArgs, { cwd: params.cwd, stdio: "pipe" });
|
|
8110
8845
|
} catch (err) {
|
|
8111
8846
|
pkgJson[depField][params.packageName] = currentRange;
|
|
8112
|
-
|
|
8847
|
+
writeFileSync7(pkgPath, JSON.stringify(pkgJson, null, 2) + "\n", "utf8");
|
|
8113
8848
|
try {
|
|
8114
8849
|
const [rollbackCmd, ...rollbackArgs] = params.installCommand;
|
|
8115
8850
|
await execa12(rollbackCmd, rollbackArgs, { cwd: params.cwd, stdio: "pipe" });
|
|
@@ -8314,8 +9049,8 @@ async function checkReachability(options) {
|
|
|
8314
9049
|
}
|
|
8315
9050
|
|
|
8316
9051
|
// src/scanner/delta-scan.ts
|
|
8317
|
-
import { existsSync as
|
|
8318
|
-
import { join as
|
|
9052
|
+
import { existsSync as existsSync16, readFileSync as readFileSync21 } from "fs";
|
|
9053
|
+
import { join as join23 } from "path";
|
|
8319
9054
|
import { execa as execa13 } from "execa";
|
|
8320
9055
|
import semver9 from "semver";
|
|
8321
9056
|
|
|
@@ -8508,7 +9243,7 @@ async function getChangedManifestPaths(cwd, baseRef) {
|
|
|
8508
9243
|
}
|
|
8509
9244
|
}
|
|
8510
9245
|
} catch {
|
|
8511
|
-
if (
|
|
9246
|
+
if (existsSync16(join23(cwd, "package.json"))) {
|
|
8512
9247
|
manifests.add("package.json");
|
|
8513
9248
|
}
|
|
8514
9249
|
}
|
|
@@ -8527,10 +9262,10 @@ async function getOldManifestContent(cwd, baseRef, relPath) {
|
|
|
8527
9262
|
}
|
|
8528
9263
|
}
|
|
8529
9264
|
function getNewManifestContent(cwd, relPath) {
|
|
8530
|
-
const fullPath =
|
|
8531
|
-
if (!
|
|
9265
|
+
const fullPath = join23(cwd, relPath);
|
|
9266
|
+
if (!existsSync16(fullPath)) return null;
|
|
8532
9267
|
try {
|
|
8533
|
-
return JSON.parse(
|
|
9268
|
+
return JSON.parse(readFileSync21(fullPath, "utf8"));
|
|
8534
9269
|
} catch {
|
|
8535
9270
|
return null;
|
|
8536
9271
|
}
|
|
@@ -8644,8 +9379,8 @@ async function scanDelta(options = {}) {
|
|
|
8644
9379
|
}
|
|
8645
9380
|
for (const lockfileRel of changedLockfiles) {
|
|
8646
9381
|
const oldContent = await getFileContentAtRef(cwd, baseRef, lockfileRel);
|
|
8647
|
-
const fullPath =
|
|
8648
|
-
const newContent =
|
|
9382
|
+
const fullPath = join23(cwd, lockfileRel);
|
|
9383
|
+
const newContent = existsSync16(fullPath) ? readFileSync21(fullPath, "utf8") : null;
|
|
8649
9384
|
const lockDiffs = computeLockfileDiff(lockfileRel, oldContent, newContent);
|
|
8650
9385
|
for (const change of lockDiffs) {
|
|
8651
9386
|
if (!directPkgs.has(change.packageName)) {
|