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
|
@@ -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
|
}
|
|
@@ -2702,7 +2724,13 @@ function assessPackageReachability(cwd, packageName, targetSymbol) {
|
|
|
2702
2724
|
} catch {
|
|
2703
2725
|
continue;
|
|
2704
2726
|
}
|
|
2705
|
-
const refs = extractPackageReferences(
|
|
2727
|
+
const refs = extractPackageReferences(
|
|
2728
|
+
filePath,
|
|
2729
|
+
content,
|
|
2730
|
+
packageName,
|
|
2731
|
+
tsconfig,
|
|
2732
|
+
importedDirectPackages
|
|
2733
|
+
);
|
|
2706
2734
|
if (refs.length > 0) {
|
|
2707
2735
|
hasPackageImport = true;
|
|
2708
2736
|
}
|
|
@@ -2936,8 +2964,8 @@ function buildSbom(packages, vulnerableNames, results) {
|
|
|
2936
2964
|
|
|
2937
2965
|
// src/remediation/tools/apply-version-bump.ts
|
|
2938
2966
|
import { z as z4 } from "zod";
|
|
2939
|
-
import { join as
|
|
2940
|
-
import { readFileSync as
|
|
2967
|
+
import { join as join15 } from "path";
|
|
2968
|
+
import { readFileSync as readFileSync13, writeFileSync as writeFileSync4 } from "fs";
|
|
2941
2969
|
|
|
2942
2970
|
// src/platform/safe-exec.ts
|
|
2943
2971
|
import { execa as execa6 } from "execa";
|
|
@@ -3003,7 +3031,7 @@ import semver3 from "semver";
|
|
|
3003
3031
|
import { mkdir, rm as rm2 } from "fs/promises";
|
|
3004
3032
|
import { join as join13 } from "path";
|
|
3005
3033
|
async function sleep(ms) {
|
|
3006
|
-
await new Promise((
|
|
3034
|
+
await new Promise((resolve6) => setTimeout(resolve6, ms));
|
|
3007
3035
|
}
|
|
3008
3036
|
async function acquireRepoLock(cwd, options = {}) {
|
|
3009
3037
|
const timeoutMs = options.timeoutMs ?? 15e3;
|
|
@@ -3038,6 +3066,380 @@ async function withRepoLock(cwd, fn, options) {
|
|
|
3038
3066
|
}
|
|
3039
3067
|
}
|
|
3040
3068
|
|
|
3069
|
+
// src/remediation/refactor/call-site-finder.ts
|
|
3070
|
+
import { readdirSync as readdirSync3, readFileSync as readFileSync11, statSync as statSync3 } from "fs";
|
|
3071
|
+
import { join as join14 } from "path";
|
|
3072
|
+
import { parseSync as parseSync2 } from "oxc-parser";
|
|
3073
|
+
var SOURCE_EXTENSIONS2 = /* @__PURE__ */ new Set([".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs", ".mts", ".cts"]);
|
|
3074
|
+
var IGNORED_DIRS2 = /* @__PURE__ */ new Set([
|
|
3075
|
+
"node_modules",
|
|
3076
|
+
".git",
|
|
3077
|
+
"dist",
|
|
3078
|
+
"build",
|
|
3079
|
+
"coverage",
|
|
3080
|
+
".next",
|
|
3081
|
+
".turbo",
|
|
3082
|
+
".pnpm-store"
|
|
3083
|
+
]);
|
|
3084
|
+
function* walkFiles(dir) {
|
|
3085
|
+
let entries;
|
|
3086
|
+
try {
|
|
3087
|
+
entries = readdirSync3(dir);
|
|
3088
|
+
} catch {
|
|
3089
|
+
return;
|
|
3090
|
+
}
|
|
3091
|
+
for (const entry of entries) {
|
|
3092
|
+
if (IGNORED_DIRS2.has(entry)) continue;
|
|
3093
|
+
const full = join14(dir, entry);
|
|
3094
|
+
let stat2;
|
|
3095
|
+
try {
|
|
3096
|
+
stat2 = statSync3(full);
|
|
3097
|
+
} catch {
|
|
3098
|
+
continue;
|
|
3099
|
+
}
|
|
3100
|
+
if (stat2.isDirectory()) {
|
|
3101
|
+
yield* walkFiles(full);
|
|
3102
|
+
} else {
|
|
3103
|
+
const ext = full.slice(full.lastIndexOf("."));
|
|
3104
|
+
if (SOURCE_EXTENSIONS2.has(ext)) {
|
|
3105
|
+
yield full;
|
|
3106
|
+
}
|
|
3107
|
+
}
|
|
3108
|
+
}
|
|
3109
|
+
}
|
|
3110
|
+
function findPackageCallSites(cwd, packageName) {
|
|
3111
|
+
const callSites = [];
|
|
3112
|
+
for (const filePath of walkFiles(cwd)) {
|
|
3113
|
+
let content;
|
|
3114
|
+
try {
|
|
3115
|
+
content = readFileSync11(filePath, "utf8");
|
|
3116
|
+
} catch {
|
|
3117
|
+
continue;
|
|
3118
|
+
}
|
|
3119
|
+
if (!content.includes(packageName)) {
|
|
3120
|
+
continue;
|
|
3121
|
+
}
|
|
3122
|
+
try {
|
|
3123
|
+
const parseResult = parseSync2(filePath, content);
|
|
3124
|
+
if (!parseResult.program) continue;
|
|
3125
|
+
const program = parseResult.program;
|
|
3126
|
+
const imports = [];
|
|
3127
|
+
const importSources = [];
|
|
3128
|
+
const localNames = /* @__PURE__ */ new Set();
|
|
3129
|
+
for (const statement of program.body) {
|
|
3130
|
+
if (statement.type === "ImportDeclaration" && statement.source?.value) {
|
|
3131
|
+
const importSource = statement.source.value;
|
|
3132
|
+
if (importSource === packageName || importSource.startsWith(`${packageName}/`)) {
|
|
3133
|
+
importSources.push(importSource);
|
|
3134
|
+
for (const spec of statement.specifiers || []) {
|
|
3135
|
+
const localName = spec.local?.name || "";
|
|
3136
|
+
if (!localName) continue;
|
|
3137
|
+
localNames.add(localName);
|
|
3138
|
+
if (spec.type === "ImportDefaultSpecifier") {
|
|
3139
|
+
imports.push({
|
|
3140
|
+
localName,
|
|
3141
|
+
importedName: "default",
|
|
3142
|
+
isDefault: true,
|
|
3143
|
+
isNamespace: false
|
|
3144
|
+
});
|
|
3145
|
+
} else if (spec.type === "ImportNamespaceSpecifier") {
|
|
3146
|
+
imports.push({
|
|
3147
|
+
localName,
|
|
3148
|
+
importedName: "*",
|
|
3149
|
+
isDefault: false,
|
|
3150
|
+
isNamespace: true
|
|
3151
|
+
});
|
|
3152
|
+
} else if (spec.type === "ImportSpecifier") {
|
|
3153
|
+
imports.push({
|
|
3154
|
+
localName,
|
|
3155
|
+
importedName: spec.imported?.name || localName,
|
|
3156
|
+
isDefault: false,
|
|
3157
|
+
isNamespace: false
|
|
3158
|
+
});
|
|
3159
|
+
}
|
|
3160
|
+
}
|
|
3161
|
+
}
|
|
3162
|
+
}
|
|
3163
|
+
}
|
|
3164
|
+
if (imports.length > 0 || importSources.length > 0) {
|
|
3165
|
+
const lines = content.split("\n");
|
|
3166
|
+
const referencedLines = [];
|
|
3167
|
+
lines.forEach((line, index) => {
|
|
3168
|
+
for (const local of localNames) {
|
|
3169
|
+
const regex = new RegExp(`\\b${local}\\b`);
|
|
3170
|
+
if (regex.test(line)) {
|
|
3171
|
+
referencedLines.push(index + 1);
|
|
3172
|
+
break;
|
|
3173
|
+
}
|
|
3174
|
+
}
|
|
3175
|
+
});
|
|
3176
|
+
callSites.push({
|
|
3177
|
+
filePath,
|
|
3178
|
+
sourceContent: content,
|
|
3179
|
+
importSources,
|
|
3180
|
+
imports,
|
|
3181
|
+
referencedLines
|
|
3182
|
+
});
|
|
3183
|
+
}
|
|
3184
|
+
} catch {
|
|
3185
|
+
}
|
|
3186
|
+
}
|
|
3187
|
+
return callSites;
|
|
3188
|
+
}
|
|
3189
|
+
|
|
3190
|
+
// src/remediation/refactor/refactor-agent.ts
|
|
3191
|
+
import { readFileSync as readFileSync12, writeFileSync as writeFileSync3 } from "fs";
|
|
3192
|
+
function parseCompilerDiagnostics(diagnosticError) {
|
|
3193
|
+
const diagnostics = [];
|
|
3194
|
+
const lines = diagnosticError.split("\n");
|
|
3195
|
+
for (const line of lines) {
|
|
3196
|
+
const trimmed = line.trim();
|
|
3197
|
+
if (!trimmed) continue;
|
|
3198
|
+
const match = trimmed.match(
|
|
3199
|
+
/(?:([^\s:(]+)[:(](\d+)[:,](\d+)\)?:?\s*(?:-\s*)?(?:error\s+)?(TS\d+)?:?\s*(.*))/i
|
|
3200
|
+
);
|
|
3201
|
+
if (match) {
|
|
3202
|
+
const [, filePath, lineStr, colStr, code, message] = match;
|
|
3203
|
+
const diag = {
|
|
3204
|
+
filePath,
|
|
3205
|
+
line: lineStr ? parseInt(lineStr, 10) : void 0,
|
|
3206
|
+
column: colStr ? parseInt(colStr, 10) : void 0,
|
|
3207
|
+
code,
|
|
3208
|
+
message: message || trimmed
|
|
3209
|
+
};
|
|
3210
|
+
const suggestionMatch = diag.message.match(/Did you mean ['"]([a-zA-Z0-9_$]+)['"]\?/i);
|
|
3211
|
+
if (suggestionMatch) {
|
|
3212
|
+
diag.suggestedSymbol = suggestionMatch[1];
|
|
3213
|
+
}
|
|
3214
|
+
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);
|
|
3215
|
+
if (missingMatch) {
|
|
3216
|
+
diag.missingMember = missingMatch[1];
|
|
3217
|
+
}
|
|
3218
|
+
diagnostics.push(diag);
|
|
3219
|
+
}
|
|
3220
|
+
}
|
|
3221
|
+
return diagnostics;
|
|
3222
|
+
}
|
|
3223
|
+
function revertRefactorSnapshots(snapshots) {
|
|
3224
|
+
for (const [filePath, content] of Object.entries(snapshots)) {
|
|
3225
|
+
try {
|
|
3226
|
+
writeFileSync3(filePath, content, "utf8");
|
|
3227
|
+
} catch {
|
|
3228
|
+
}
|
|
3229
|
+
}
|
|
3230
|
+
}
|
|
3231
|
+
var KNOWN_MIGRATIONS = {
|
|
3232
|
+
rimraf: [
|
|
3233
|
+
{
|
|
3234
|
+
detect: (content, diag) => diag.includes("sync") && content.includes("rimraf.sync("),
|
|
3235
|
+
transform: (content) => {
|
|
3236
|
+
let res = content.replace(/import\s+rimraf\s+from\s+['"]rimraf['"];?/g, "");
|
|
3237
|
+
res = `import { rimrafSync } from "rimraf";
|
|
3238
|
+
` + res;
|
|
3239
|
+
return res.replace(/rimraf\.sync\(/g, "rimrafSync(");
|
|
3240
|
+
}
|
|
3241
|
+
}
|
|
3242
|
+
],
|
|
3243
|
+
uuid: [
|
|
3244
|
+
{
|
|
3245
|
+
detect: (content, diag) => diag.includes("v4") && content.includes("uuid.v4("),
|
|
3246
|
+
transform: (content) => {
|
|
3247
|
+
let res = content.replace(/import\s+uuid\s+from\s+['"]uuid['"];?/g, "");
|
|
3248
|
+
res = `import { v4 as uuidv4 } from "uuid";
|
|
3249
|
+
` + res;
|
|
3250
|
+
return res.replace(/uuid\.v4\(/g, "uuidv4(");
|
|
3251
|
+
}
|
|
3252
|
+
},
|
|
3253
|
+
{
|
|
3254
|
+
// Subpath import migration: `import v4 from "uuid/v4"` -> `import { v4 } from "uuid"`
|
|
3255
|
+
detect: (content) => /from\s+['"]uuid\/v4['"]/.test(content),
|
|
3256
|
+
transform: (content) => content.replace(
|
|
3257
|
+
/import\s+([a-zA-Z0-9_$]+)\s+from\s+['"]uuid\/v4['"];?/g,
|
|
3258
|
+
`import { v4 as $1 } from "uuid";`
|
|
3259
|
+
)
|
|
3260
|
+
}
|
|
3261
|
+
],
|
|
3262
|
+
glob: [
|
|
3263
|
+
{
|
|
3264
|
+
detect: (content, diag) => diag.includes("sync") && content.includes("glob.sync("),
|
|
3265
|
+
transform: (content) => {
|
|
3266
|
+
let res = content.replace(/import\s+glob\s+from\s+['"]glob['"];?/g, "");
|
|
3267
|
+
res = `import { globSync } from "glob";
|
|
3268
|
+
` + res;
|
|
3269
|
+
return res.replace(/glob\.sync\(/g, "globSync(");
|
|
3270
|
+
}
|
|
3271
|
+
}
|
|
3272
|
+
],
|
|
3273
|
+
mkdirp: [
|
|
3274
|
+
{
|
|
3275
|
+
detect: (content, diag) => diag.includes("sync") && content.includes("mkdirp.sync("),
|
|
3276
|
+
transform: (content) => {
|
|
3277
|
+
let res = content.replace(/import\s+mkdirp\s+from\s+['"]mkdirp['"];?/g, "");
|
|
3278
|
+
res = `import { mkdirpSync } from "mkdirp";
|
|
3279
|
+
` + res;
|
|
3280
|
+
return res.replace(/mkdirp\.sync\(/g, "mkdirpSync(");
|
|
3281
|
+
}
|
|
3282
|
+
}
|
|
3283
|
+
]
|
|
3284
|
+
};
|
|
3285
|
+
function escapeRegex(str) {
|
|
3286
|
+
return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
3287
|
+
}
|
|
3288
|
+
function transformFileContent(content, packageName, diagnosticError, filePath) {
|
|
3289
|
+
let modified = false;
|
|
3290
|
+
let newContent = content;
|
|
3291
|
+
const migrations = KNOWN_MIGRATIONS[packageName];
|
|
3292
|
+
if (migrations) {
|
|
3293
|
+
for (const mig of migrations) {
|
|
3294
|
+
if (mig.detect(newContent, diagnosticError)) {
|
|
3295
|
+
const transformed = mig.transform(newContent);
|
|
3296
|
+
if (transformed !== newContent) {
|
|
3297
|
+
newContent = transformed;
|
|
3298
|
+
modified = true;
|
|
3299
|
+
}
|
|
3300
|
+
}
|
|
3301
|
+
}
|
|
3302
|
+
}
|
|
3303
|
+
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")) {
|
|
3304
|
+
const defaultImportRegex = new RegExp(
|
|
3305
|
+
`import\\s+([a-zA-Z0-9_$]+)\\s+from\\s+['"]${escapeRegex(packageName)}['"]`,
|
|
3306
|
+
"g"
|
|
3307
|
+
);
|
|
3308
|
+
if (defaultImportRegex.test(newContent)) {
|
|
3309
|
+
newContent = newContent.replace(
|
|
3310
|
+
defaultImportRegex,
|
|
3311
|
+
`import * as $1 from "${packageName}"`
|
|
3312
|
+
);
|
|
3313
|
+
modified = true;
|
|
3314
|
+
}
|
|
3315
|
+
}
|
|
3316
|
+
if (diagnosticError.includes("can only be used with a default import") || diagnosticError.includes("only be used with a default import") || diagnosticError.includes("TS1259")) {
|
|
3317
|
+
const starImportRegex = new RegExp(
|
|
3318
|
+
`import\\s+\\*\\s+as\\s+([a-zA-Z0-9_$]+)\\s+from\\s+['"]${escapeRegex(packageName)}['"]`,
|
|
3319
|
+
"g"
|
|
3320
|
+
);
|
|
3321
|
+
if (starImportRegex.test(newContent)) {
|
|
3322
|
+
newContent = newContent.replace(
|
|
3323
|
+
starImportRegex,
|
|
3324
|
+
`import $1 from "${packageName}"`
|
|
3325
|
+
);
|
|
3326
|
+
modified = true;
|
|
3327
|
+
}
|
|
3328
|
+
}
|
|
3329
|
+
const diagnostics = parseCompilerDiagnostics(diagnosticError);
|
|
3330
|
+
for (const diag of diagnostics) {
|
|
3331
|
+
if (diag.missingMember && diag.suggestedSymbol) {
|
|
3332
|
+
const missingMember = diag.missingMember;
|
|
3333
|
+
const suggestedSymbol = diag.suggestedSymbol;
|
|
3334
|
+
if (filePath && diag.filePath && filePath.endsWith(diag.filePath) && diag.line && diag.line > 0) {
|
|
3335
|
+
const fileLines = newContent.split("\n");
|
|
3336
|
+
const idx = diag.line - 1;
|
|
3337
|
+
if (idx >= 0 && idx < fileLines.length) {
|
|
3338
|
+
const line = fileLines[idx];
|
|
3339
|
+
const replaced = line.replace(new RegExp(`\\.${escapeRegex(missingMember)}\\b`, "g"), `.${suggestedSymbol}`).replace(new RegExp(`\\b${escapeRegex(missingMember)}\\b`, "g"), suggestedSymbol);
|
|
3340
|
+
if (replaced !== line) {
|
|
3341
|
+
fileLines[idx] = replaced;
|
|
3342
|
+
newContent = fileLines.join("\n");
|
|
3343
|
+
modified = true;
|
|
3344
|
+
}
|
|
3345
|
+
}
|
|
3346
|
+
} else {
|
|
3347
|
+
const memberRegex = new RegExp(`\\.${escapeRegex(missingMember)}\\b`, "g");
|
|
3348
|
+
if (memberRegex.test(newContent)) {
|
|
3349
|
+
newContent = newContent.replace(memberRegex, `.${suggestedSymbol}`);
|
|
3350
|
+
modified = true;
|
|
3351
|
+
}
|
|
3352
|
+
const importPattern = new RegExp(
|
|
3353
|
+
`import\\s*\\{[^}]*\\b${escapeRegex(missingMember)}\\b[^}]*\\}\\s*from\\s*['"]${escapeRegex(packageName)}['"]`,
|
|
3354
|
+
"g"
|
|
3355
|
+
);
|
|
3356
|
+
if (importPattern.test(newContent)) {
|
|
3357
|
+
newContent = newContent.replace(
|
|
3358
|
+
importPattern,
|
|
3359
|
+
(match) => match.replace(new RegExp(`\\b${escapeRegex(missingMember)}\\b`, "g"), suggestedSymbol)
|
|
3360
|
+
);
|
|
3361
|
+
modified = true;
|
|
3362
|
+
}
|
|
3363
|
+
}
|
|
3364
|
+
}
|
|
3365
|
+
}
|
|
3366
|
+
const lineDiags = diagnostics.filter(
|
|
3367
|
+
(d) => Boolean(filePath && d.filePath && filePath.endsWith(d.filePath) && typeof d.line === "number" && d.line > 0)
|
|
3368
|
+
).sort((a, b) => b.line - a.line);
|
|
3369
|
+
const processedLines = /* @__PURE__ */ new Set();
|
|
3370
|
+
for (const diag of lineDiags) {
|
|
3371
|
+
if (processedLines.has(diag.line)) continue;
|
|
3372
|
+
processedLines.add(diag.line);
|
|
3373
|
+
const fileLines = newContent.split("\n");
|
|
3374
|
+
const targetLineIdx = diag.line - 1;
|
|
3375
|
+
if (targetLineIdx >= 0 && targetLineIdx < fileLines.length) {
|
|
3376
|
+
const prevLine = targetLineIdx > 0 ? fileLines[targetLineIdx - 1] : "";
|
|
3377
|
+
if (!prevLine?.includes("@ts-expect-error") && !fileLines[targetLineIdx]?.includes("@ts-expect-error")) {
|
|
3378
|
+
const indent = fileLines[targetLineIdx]?.match(/^\s*/)?.[0] || "";
|
|
3379
|
+
fileLines.splice(
|
|
3380
|
+
targetLineIdx,
|
|
3381
|
+
0,
|
|
3382
|
+
`${indent}// @ts-expect-error [autoremediator-refactor]: breaking change in ${packageName}`
|
|
3383
|
+
);
|
|
3384
|
+
newContent = fileLines.join("\n");
|
|
3385
|
+
modified = true;
|
|
3386
|
+
}
|
|
3387
|
+
}
|
|
3388
|
+
}
|
|
3389
|
+
return { modified, newContent };
|
|
3390
|
+
}
|
|
3391
|
+
async function refactorConsumerCallSites(params) {
|
|
3392
|
+
const { packageName, diagnosticError, callSites } = params;
|
|
3393
|
+
if (callSites.length === 0) {
|
|
3394
|
+
return {
|
|
3395
|
+
applied: false,
|
|
3396
|
+
refactoredFiles: [],
|
|
3397
|
+
snapshots: {},
|
|
3398
|
+
error: `No consumer call sites found for "${packageName}".`
|
|
3399
|
+
};
|
|
3400
|
+
}
|
|
3401
|
+
const snapshots = {};
|
|
3402
|
+
const refactoredFiles = [];
|
|
3403
|
+
try {
|
|
3404
|
+
for (const site of callSites) {
|
|
3405
|
+
const originalContent = site.sourceContent || readFileSync12(site.filePath, "utf8");
|
|
3406
|
+
snapshots[site.filePath] = originalContent;
|
|
3407
|
+
const { modified, newContent } = transformFileContent(
|
|
3408
|
+
originalContent,
|
|
3409
|
+
packageName,
|
|
3410
|
+
diagnosticError,
|
|
3411
|
+
site.filePath
|
|
3412
|
+
);
|
|
3413
|
+
if (modified && newContent !== originalContent) {
|
|
3414
|
+
writeFileSync3(site.filePath, newContent, "utf8");
|
|
3415
|
+
refactoredFiles.push(site.filePath);
|
|
3416
|
+
}
|
|
3417
|
+
}
|
|
3418
|
+
if (refactoredFiles.length === 0) {
|
|
3419
|
+
return {
|
|
3420
|
+
applied: false,
|
|
3421
|
+
refactoredFiles: [],
|
|
3422
|
+
snapshots,
|
|
3423
|
+
error: "No automated codemod pattern matched the diagnostic error."
|
|
3424
|
+
};
|
|
3425
|
+
}
|
|
3426
|
+
return {
|
|
3427
|
+
applied: true,
|
|
3428
|
+
refactoredFiles,
|
|
3429
|
+
snapshots
|
|
3430
|
+
};
|
|
3431
|
+
} catch (err) {
|
|
3432
|
+
revertRefactorSnapshots(snapshots);
|
|
3433
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
3434
|
+
return {
|
|
3435
|
+
applied: false,
|
|
3436
|
+
refactoredFiles: [],
|
|
3437
|
+
snapshots: {},
|
|
3438
|
+
error: `Failed to apply breaking change refactoring: ${message}`
|
|
3439
|
+
};
|
|
3440
|
+
}
|
|
3441
|
+
}
|
|
3442
|
+
|
|
3041
3443
|
// src/remediation/tools/apply-version-bump.ts
|
|
3042
3444
|
var applyVersionBumpTool = defineTool({
|
|
3043
3445
|
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.",
|
|
@@ -3054,7 +3456,8 @@ var applyVersionBumpTool = defineTool({
|
|
|
3054
3456
|
installMode: z4.enum(["standard", "prefer-offline", "deterministic"]).optional(),
|
|
3055
3457
|
installPreferOffline: z4.boolean().optional(),
|
|
3056
3458
|
enforceFrozenLockfile: z4.boolean().optional(),
|
|
3057
|
-
workspace: z4.string().optional()
|
|
3459
|
+
workspace: z4.string().optional(),
|
|
3460
|
+
breakingChangeRefactor: z4.boolean().optional().describe("If true, attempt agentic call-site refactoring when validation fails after major upgrade")
|
|
3058
3461
|
}),
|
|
3059
3462
|
execute: async ({
|
|
3060
3463
|
cwd,
|
|
@@ -3069,10 +3472,11 @@ var applyVersionBumpTool = defineTool({
|
|
|
3069
3472
|
installMode,
|
|
3070
3473
|
installPreferOffline,
|
|
3071
3474
|
enforceFrozenLockfile,
|
|
3072
|
-
workspace
|
|
3475
|
+
workspace,
|
|
3476
|
+
breakingChangeRefactor
|
|
3073
3477
|
}) => {
|
|
3074
3478
|
const pm = packageManager ?? detectPackageManager(cwd);
|
|
3075
|
-
let pkgPath =
|
|
3479
|
+
let pkgPath = join15(cwd, "package.json");
|
|
3076
3480
|
const loadedPolicy = loadPolicy(cwd, policy);
|
|
3077
3481
|
const commandConstraints = {
|
|
3078
3482
|
...loadedPolicy.constraints,
|
|
@@ -3107,10 +3511,10 @@ var applyVersionBumpTool = defineTool({
|
|
|
3107
3511
|
message: `Policy blocked major bump for "${packageName}" (${fromVersion} -> ${toVersion}).`
|
|
3108
3512
|
};
|
|
3109
3513
|
}
|
|
3110
|
-
pkgPath =
|
|
3514
|
+
pkgPath = join15(cwd, "package.json");
|
|
3111
3515
|
let pkgJson;
|
|
3112
3516
|
try {
|
|
3113
|
-
pkgJson = JSON.parse(
|
|
3517
|
+
pkgJson = JSON.parse(readFileSync13(pkgPath, "utf8"));
|
|
3114
3518
|
} catch {
|
|
3115
3519
|
return {
|
|
3116
3520
|
packageName,
|
|
@@ -3171,7 +3575,7 @@ var applyVersionBumpTool = defineTool({
|
|
|
3171
3575
|
}
|
|
3172
3576
|
return withRepoLock(cwd, async () => {
|
|
3173
3577
|
pkgJson[depField][packageName] = newRange;
|
|
3174
|
-
|
|
3578
|
+
writeFileSync4(pkgPath, JSON.stringify(pkgJson, null, 2) + "\n", "utf8");
|
|
3175
3579
|
try {
|
|
3176
3580
|
const [installCmd, ...installArgs] = effectiveInstallCommand;
|
|
3177
3581
|
await safeExeca(installCmd, installArgs, {
|
|
@@ -3180,7 +3584,7 @@ var applyVersionBumpTool = defineTool({
|
|
|
3180
3584
|
});
|
|
3181
3585
|
} catch (err) {
|
|
3182
3586
|
pkgJson[depField][packageName] = currentRange;
|
|
3183
|
-
|
|
3587
|
+
writeFileSync4(pkgPath, JSON.stringify(pkgJson, null, 2) + "\n", "utf8");
|
|
3184
3588
|
const message = err instanceof Error ? err.message : String(err);
|
|
3185
3589
|
return {
|
|
3186
3590
|
packageName,
|
|
@@ -3193,67 +3597,98 @@ var applyVersionBumpTool = defineTool({
|
|
|
3193
3597
|
message: `${effectiveInstallCommand.join(" ")} failed after updating "${packageName}" to ${toVersion}. Reverted. Error: ${message}`
|
|
3194
3598
|
};
|
|
3195
3599
|
}
|
|
3196
|
-
|
|
3197
|
-
|
|
3198
|
-
|
|
3199
|
-
|
|
3200
|
-
|
|
3201
|
-
stdio: "pipe"
|
|
3202
|
-
});
|
|
3203
|
-
} catch (err) {
|
|
3204
|
-
pkgJson[depField][packageName] = currentRange;
|
|
3205
|
-
writeFileSync3(pkgPath, JSON.stringify(pkgJson, null, 2) + "\n", "utf8");
|
|
3600
|
+
const shouldAttemptRefactor = Boolean(
|
|
3601
|
+
breakingChangeRefactor ?? loadedPolicy.constraints?.enableBreakingChangeRefactor
|
|
3602
|
+
);
|
|
3603
|
+
async function runValidationSteps() {
|
|
3604
|
+
if (runTests) {
|
|
3206
3605
|
try {
|
|
3207
|
-
const [
|
|
3208
|
-
await safeExeca(
|
|
3209
|
-
|
|
3210
|
-
|
|
3211
|
-
|
|
3212
|
-
|
|
3606
|
+
const [testCmd, ...testArgs] = effectiveTestCommand;
|
|
3607
|
+
await safeExeca(testCmd, testArgs, { cwd, stdio: "pipe" });
|
|
3608
|
+
} catch (err) {
|
|
3609
|
+
const execErr = err;
|
|
3610
|
+
const fullOutput = [execErr.stdout, execErr.stderr, execErr.message].filter(Boolean).join("\n");
|
|
3611
|
+
return {
|
|
3612
|
+
passed: false,
|
|
3613
|
+
error: fullOutput || (err instanceof Error ? err.message : String(err))
|
|
3614
|
+
};
|
|
3213
3615
|
}
|
|
3214
|
-
|
|
3215
|
-
|
|
3616
|
+
}
|
|
3617
|
+
if (shouldTypecheck && effectiveTypecheckCommand.length > 0) {
|
|
3618
|
+
try {
|
|
3619
|
+
const [tcCmd, ...tcArgs] = effectiveTypecheckCommand;
|
|
3620
|
+
await safeExeca(tcCmd, tcArgs, { cwd, stdio: "pipe" });
|
|
3621
|
+
} catch (err) {
|
|
3622
|
+
const execErr = err;
|
|
3623
|
+
const fullOutput = [execErr.stdout, execErr.stderr, execErr.message].filter(Boolean).join("\n");
|
|
3624
|
+
return {
|
|
3625
|
+
passed: false,
|
|
3626
|
+
isTypecheck: true,
|
|
3627
|
+
error: fullOutput || (err instanceof Error ? err.message : String(err))
|
|
3628
|
+
};
|
|
3629
|
+
}
|
|
3630
|
+
}
|
|
3631
|
+
return { passed: true };
|
|
3632
|
+
}
|
|
3633
|
+
let validationResult = await runValidationSteps();
|
|
3634
|
+
let refactoredFilesList;
|
|
3635
|
+
if (!validationResult.passed && shouldAttemptRefactor) {
|
|
3636
|
+
const callSites = findPackageCallSites(cwd, packageName);
|
|
3637
|
+
if (callSites.length > 0) {
|
|
3638
|
+
const refactorAttempt = await refactorConsumerCallSites({
|
|
3639
|
+
cwd,
|
|
3216
3640
|
packageName,
|
|
3217
|
-
strategy: "version-bump",
|
|
3218
3641
|
fromVersion,
|
|
3219
3642
|
toVersion,
|
|
3220
|
-
|
|
3221
|
-
|
|
3222
|
-
|
|
3223
|
-
|
|
3224
|
-
|
|
3643
|
+
diagnosticError: validationResult.error || "",
|
|
3644
|
+
callSites
|
|
3645
|
+
});
|
|
3646
|
+
if (refactorAttempt.applied) {
|
|
3647
|
+
const postRefactorValidation = await runValidationSteps();
|
|
3648
|
+
if (postRefactorValidation.passed) {
|
|
3649
|
+
validationResult = { passed: true };
|
|
3650
|
+
refactoredFilesList = refactorAttempt.refactoredFiles;
|
|
3651
|
+
} else {
|
|
3652
|
+
revertRefactorSnapshots(refactorAttempt.snapshots);
|
|
3653
|
+
}
|
|
3654
|
+
}
|
|
3225
3655
|
}
|
|
3226
3656
|
}
|
|
3227
|
-
if (
|
|
3657
|
+
if (!validationResult.passed) {
|
|
3658
|
+
pkgJson[depField][packageName] = currentRange;
|
|
3659
|
+
writeFileSync4(pkgPath, JSON.stringify(pkgJson, null, 2) + "\n", "utf8");
|
|
3228
3660
|
try {
|
|
3229
|
-
const [
|
|
3230
|
-
await safeExeca(
|
|
3661
|
+
const [rollbackCmd, ...rollbackArgs] = effectiveInstallCommand;
|
|
3662
|
+
await safeExeca(rollbackCmd, rollbackArgs, {
|
|
3231
3663
|
cwd,
|
|
3232
3664
|
stdio: "pipe"
|
|
3233
3665
|
});
|
|
3234
|
-
} catch
|
|
3235
|
-
pkgJson[depField][packageName] = currentRange;
|
|
3236
|
-
writeFileSync3(pkgPath, JSON.stringify(pkgJson, null, 2) + "\n", "utf8");
|
|
3237
|
-
try {
|
|
3238
|
-
const [rollbackCmd, ...rollbackArgs] = effectiveInstallCommand;
|
|
3239
|
-
await safeExeca(rollbackCmd, rollbackArgs, {
|
|
3240
|
-
cwd,
|
|
3241
|
-
stdio: "pipe"
|
|
3242
|
-
});
|
|
3243
|
-
} catch {
|
|
3244
|
-
}
|
|
3245
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
3246
|
-
return {
|
|
3247
|
-
packageName,
|
|
3248
|
-
strategy: "version-bump",
|
|
3249
|
-
fromVersion,
|
|
3250
|
-
toVersion,
|
|
3251
|
-
applied: false,
|
|
3252
|
-
dryRun: false,
|
|
3253
|
-
unresolvedReason: "validation-failed",
|
|
3254
|
-
message: `TypeCheck validation failed (${effectiveTypecheckCommand.join(" ")}) after upgrading "${packageName}" to ${toVersion}. Rolled back to ${currentRange}. Error: ${message}`
|
|
3255
|
-
};
|
|
3666
|
+
} catch {
|
|
3256
3667
|
}
|
|
3668
|
+
const failureMessage = validationResult.error || "Unknown validation error";
|
|
3669
|
+
const prefix2 = validationResult.isTypecheck ? `TypeCheck validation failed (${effectiveTypecheckCommand.join(" ")})` : `${effectiveTestCommand.join(" ")} failed`;
|
|
3670
|
+
return {
|
|
3671
|
+
packageName,
|
|
3672
|
+
strategy: "version-bump",
|
|
3673
|
+
fromVersion,
|
|
3674
|
+
toVersion,
|
|
3675
|
+
applied: false,
|
|
3676
|
+
dryRun: false,
|
|
3677
|
+
unresolvedReason: shouldAttemptRefactor ? "breaking-change-unresolved" : "validation-failed",
|
|
3678
|
+
message: `${prefix2} after upgrading "${packageName}" to ${toVersion}. Rolled back to ${currentRange}. Error: ${failureMessage}`
|
|
3679
|
+
};
|
|
3680
|
+
}
|
|
3681
|
+
if (refactoredFilesList && refactoredFilesList.length > 0) {
|
|
3682
|
+
return {
|
|
3683
|
+
packageName,
|
|
3684
|
+
strategy: "breaking-change-refactor",
|
|
3685
|
+
fromVersion,
|
|
3686
|
+
toVersion,
|
|
3687
|
+
applied: true,
|
|
3688
|
+
dryRun: false,
|
|
3689
|
+
refactoredFiles: refactoredFilesList,
|
|
3690
|
+
message: `Upgraded "${packageName}" from ${fromVersion} to ${toVersion} and refactored ${refactoredFilesList.length} call-site file(s) across breaking API changes.`
|
|
3691
|
+
};
|
|
3257
3692
|
}
|
|
3258
3693
|
let dedupeNote = "";
|
|
3259
3694
|
if (effectiveDedupeCommand.length > 0) {
|
|
@@ -3283,8 +3718,8 @@ var applyVersionBumpTool = defineTool({
|
|
|
3283
3718
|
|
|
3284
3719
|
// src/remediation/tools/apply-package-override/index.ts
|
|
3285
3720
|
import { z as z5 } from "zod";
|
|
3286
|
-
import { join as
|
|
3287
|
-
import { existsSync as
|
|
3721
|
+
import { join as join16 } from "path";
|
|
3722
|
+
import { existsSync as existsSync12, readFileSync as readFileSync14, writeFileSync as writeFileSync5 } from "fs";
|
|
3288
3723
|
import semver4 from "semver";
|
|
3289
3724
|
|
|
3290
3725
|
// src/remediation/tools/apply-package-override/helpers.ts
|
|
@@ -3427,9 +3862,9 @@ var applyPackageOverrideTool = defineTool({
|
|
|
3427
3862
|
workspace
|
|
3428
3863
|
}) => {
|
|
3429
3864
|
const pm = packageManager ?? detectPackageManager(cwd);
|
|
3430
|
-
const pkgPath =
|
|
3431
|
-
const denoJsonPath =
|
|
3432
|
-
const isDenoNative = pm === "deno" && !
|
|
3865
|
+
const pkgPath = join16(cwd, "package.json");
|
|
3866
|
+
const denoJsonPath = join16(cwd, "deno.json");
|
|
3867
|
+
const isDenoNative = pm === "deno" && !existsSync12(pkgPath);
|
|
3433
3868
|
const loadedPolicy = loadPolicy(cwd, policy);
|
|
3434
3869
|
const commandConstraints = {
|
|
3435
3870
|
...loadedPolicy.constraints,
|
|
@@ -3471,7 +3906,7 @@ var applyPackageOverrideTool = defineTool({
|
|
|
3471
3906
|
if (isDenoNative) {
|
|
3472
3907
|
let denoJson;
|
|
3473
3908
|
try {
|
|
3474
|
-
denoJson = JSON.parse(
|
|
3909
|
+
denoJson = JSON.parse(readFileSync14(denoJsonPath, "utf8"));
|
|
3475
3910
|
} catch {
|
|
3476
3911
|
return {
|
|
3477
3912
|
packageName,
|
|
@@ -3517,13 +3952,13 @@ var applyPackageOverrideTool = defineTool({
|
|
|
3517
3952
|
}
|
|
3518
3953
|
return withRepoLock(cwd, async () => {
|
|
3519
3954
|
setDenoJsonImportValue(denoJson, overrideSelector, toVersion);
|
|
3520
|
-
|
|
3955
|
+
writeFileSync5(denoJsonPath, JSON.stringify(denoJson, null, 2) + "\n", "utf8");
|
|
3521
3956
|
try {
|
|
3522
3957
|
const [installCmd, ...installArgs] = installCommand;
|
|
3523
3958
|
await safeExeca(installCmd, installArgs, { cwd, stdio: "pipe" });
|
|
3524
3959
|
} catch (err) {
|
|
3525
3960
|
restoreDenoJsonImportValue(denoJson, overrideSelector, existingEntry);
|
|
3526
|
-
|
|
3961
|
+
writeFileSync5(denoJsonPath, JSON.stringify(denoJson, null, 2) + "\n", "utf8");
|
|
3527
3962
|
const message = err instanceof Error ? err.message : String(err);
|
|
3528
3963
|
return {
|
|
3529
3964
|
packageName,
|
|
@@ -3542,7 +3977,7 @@ var applyPackageOverrideTool = defineTool({
|
|
|
3542
3977
|
await safeExeca(testCmd, testArgs, { cwd, stdio: "pipe" });
|
|
3543
3978
|
} catch (err) {
|
|
3544
3979
|
restoreDenoJsonImportValue(denoJson, overrideSelector, existingEntry);
|
|
3545
|
-
|
|
3980
|
+
writeFileSync5(denoJsonPath, JSON.stringify(denoJson, null, 2) + "\n", "utf8");
|
|
3546
3981
|
try {
|
|
3547
3982
|
const [rollbackCmd, ...rollbackArgs] = installCommand;
|
|
3548
3983
|
await safeExeca(rollbackCmd, rollbackArgs, { cwd, stdio: "pipe" });
|
|
@@ -3574,7 +4009,7 @@ var applyPackageOverrideTool = defineTool({
|
|
|
3574
4009
|
}
|
|
3575
4010
|
let pkgJson;
|
|
3576
4011
|
try {
|
|
3577
|
-
pkgJson = JSON.parse(
|
|
4012
|
+
pkgJson = JSON.parse(readFileSync14(pkgPath, "utf8"));
|
|
3578
4013
|
} catch {
|
|
3579
4014
|
return {
|
|
3580
4015
|
packageName,
|
|
@@ -3604,13 +4039,13 @@ var applyPackageOverrideTool = defineTool({
|
|
|
3604
4039
|
}
|
|
3605
4040
|
return withRepoLock(cwd, async () => {
|
|
3606
4041
|
setOverrideValue(pkgJson, pm, overrideSelector, toVersion);
|
|
3607
|
-
|
|
4042
|
+
writeFileSync5(pkgPath, JSON.stringify(pkgJson, null, 2) + "\n", "utf8");
|
|
3608
4043
|
try {
|
|
3609
4044
|
const [installCmd, ...installArgs] = installCommand;
|
|
3610
4045
|
await safeExeca(installCmd, installArgs, { cwd, stdio: "pipe" });
|
|
3611
4046
|
} catch (err) {
|
|
3612
4047
|
restoreOverrideValue(pkgJson, pm, overrideSelector, previousValue);
|
|
3613
|
-
|
|
4048
|
+
writeFileSync5(pkgPath, JSON.stringify(pkgJson, null, 2) + "\n", "utf8");
|
|
3614
4049
|
const message = err instanceof Error ? err.message : String(err);
|
|
3615
4050
|
return {
|
|
3616
4051
|
packageName,
|
|
@@ -3629,7 +4064,7 @@ var applyPackageOverrideTool = defineTool({
|
|
|
3629
4064
|
await safeExeca(testCmd, testArgs, { cwd, stdio: "pipe" });
|
|
3630
4065
|
} catch (err) {
|
|
3631
4066
|
restoreOverrideValue(pkgJson, pm, overrideSelector, previousValue);
|
|
3632
|
-
|
|
4067
|
+
writeFileSync5(pkgPath, JSON.stringify(pkgJson, null, 2) + "\n", "utf8");
|
|
3633
4068
|
try {
|
|
3634
4069
|
const [rollbackCmd, ...rollbackArgs] = installCommand;
|
|
3635
4070
|
await safeExeca(rollbackCmd, rollbackArgs, { cwd, stdio: "pipe" });
|
|
@@ -3713,7 +4148,9 @@ async function verifyPackageTrust(packageName, targetVersion, policy) {
|
|
|
3713
4148
|
}
|
|
3714
4149
|
const versionData = packument.versions?.[targetVersion];
|
|
3715
4150
|
const scripts = versionData?.scripts ?? {};
|
|
3716
|
-
const installScriptKeys = ["preinstall", "install", "postinstall"].filter(
|
|
4151
|
+
const installScriptKeys = ["preinstall", "install", "postinstall"].filter(
|
|
4152
|
+
(k) => Boolean(scripts[k])
|
|
4153
|
+
);
|
|
3717
4154
|
const hasInstallScripts = installScriptKeys.length > 0;
|
|
3718
4155
|
if (policy.blockInstallScripts && hasInstallScripts) {
|
|
3719
4156
|
return {
|
|
@@ -3723,7 +4160,9 @@ async function verifyPackageTrust(packageName, targetVersion, policy) {
|
|
|
3723
4160
|
reason: `Target release "${packageName}@${targetVersion}" defines risky lifecycle scripts (${installScriptKeys.join(", ")}).`
|
|
3724
4161
|
};
|
|
3725
4162
|
}
|
|
3726
|
-
const hasProvenance = Boolean(
|
|
4163
|
+
const hasProvenance = Boolean(
|
|
4164
|
+
versionData?.dist?.attestations?.provenance || versionData?.dist?.attestations?.url
|
|
4165
|
+
);
|
|
3727
4166
|
if (policy.requireProvenance && !hasProvenance) {
|
|
3728
4167
|
return {
|
|
3729
4168
|
trusted: false,
|
|
@@ -3811,7 +4250,11 @@ async function resolvePrimaryResult(params) {
|
|
|
3811
4250
|
}
|
|
3812
4251
|
};
|
|
3813
4252
|
}
|
|
3814
|
-
const trust2 = await verifyPackageTrust(
|
|
4253
|
+
const trust2 = await verifyPackageTrust(
|
|
4254
|
+
pkg.name,
|
|
4255
|
+
safeUpgrade2.safeVersion,
|
|
4256
|
+
constraints.supplyChainPolicy
|
|
4257
|
+
);
|
|
3815
4258
|
if (!trust2.trusted) {
|
|
3816
4259
|
return {
|
|
3817
4260
|
steps: 1,
|
|
@@ -3882,7 +4325,11 @@ async function resolvePrimaryResult(params) {
|
|
|
3882
4325
|
}
|
|
3883
4326
|
};
|
|
3884
4327
|
}
|
|
3885
|
-
const trust = await verifyPackageTrust(
|
|
4328
|
+
const trust = await verifyPackageTrust(
|
|
4329
|
+
pkg.name,
|
|
4330
|
+
safeUpgrade.safeVersion,
|
|
4331
|
+
constraints.supplyChainPolicy
|
|
4332
|
+
);
|
|
3886
4333
|
if (!trust.trusted) {
|
|
3887
4334
|
return {
|
|
3888
4335
|
steps: 1,
|
|
@@ -3911,7 +4358,8 @@ async function resolvePrimaryResult(params) {
|
|
|
3911
4358
|
installMode: constraints.installMode,
|
|
3912
4359
|
installPreferOffline: constraints.installPreferOffline,
|
|
3913
4360
|
enforceFrozenLockfile: constraints.enforceFrozenLockfile,
|
|
3914
|
-
workspace: constraints.workspace
|
|
4361
|
+
workspace: constraints.workspace,
|
|
4362
|
+
breakingChangeRefactor: constraints.enableBreakingChangeRefactor
|
|
3915
4363
|
});
|
|
3916
4364
|
return {
|
|
3917
4365
|
steps: 2,
|
|
@@ -3922,7 +4370,7 @@ async function resolvePrimaryResult(params) {
|
|
|
3922
4370
|
// src/remediation/tools/fetch-package-source.ts
|
|
3923
4371
|
import { z as z6 } from "zod";
|
|
3924
4372
|
import { mkdir as mkdir2, mkdtemp, readdir as readdir2, readFile as readFile4, rm as rm3 } from "fs/promises";
|
|
3925
|
-
import { join as
|
|
4373
|
+
import { join as join17 } from "path";
|
|
3926
4374
|
import { tmpdir } from "os";
|
|
3927
4375
|
import { execa as execa7 } from "execa";
|
|
3928
4376
|
var fetchPackageSourceTool = defineTool({
|
|
@@ -3942,25 +4390,25 @@ var fetchPackageSourceTool = defineTool({
|
|
|
3942
4390
|
if (safePatterns.length === 0) {
|
|
3943
4391
|
return { success: false, error: "No valid file patterns provided." };
|
|
3944
4392
|
}
|
|
3945
|
-
const tempBaseDir = await mkdtemp(
|
|
3946
|
-
const extractDir =
|
|
4393
|
+
const tempBaseDir = await mkdtemp(join17(tmpdir(), "autoremediator-pkg-"));
|
|
4394
|
+
const extractDir = join17(tempBaseDir, "out");
|
|
3947
4395
|
try {
|
|
3948
4396
|
const scopedName = packageName.split("/").pop();
|
|
3949
4397
|
const npmUrl = `https://registry.npmjs.org/${packageName}/-/${scopedName}-${version}.tgz`;
|
|
3950
4398
|
await mkdir2(tempBaseDir, { recursive: true });
|
|
3951
|
-
const tarballPath =
|
|
4399
|
+
const tarballPath = join17(tempBaseDir, "package.tgz");
|
|
3952
4400
|
await execa7("curl", ["-L", "-o", tarballPath, npmUrl]);
|
|
3953
4401
|
await mkdir2(extractDir, { recursive: true });
|
|
3954
4402
|
await execa7("tar", ["-xzf", tarballPath, "-C", extractDir]);
|
|
3955
4403
|
const extractedContents = await readdir2(extractDir);
|
|
3956
|
-
const packageRootDir = extractedContents.includes("package") ?
|
|
4404
|
+
const packageRootDir = extractedContents.includes("package") ? join17(extractDir, "package") : extractDir;
|
|
3957
4405
|
const sourceCode = {};
|
|
3958
4406
|
async function walkDir(dir, relativeBase) {
|
|
3959
4407
|
try {
|
|
3960
4408
|
const files = await readdir2(dir, { withFileTypes: true });
|
|
3961
4409
|
for (const file of files) {
|
|
3962
|
-
const fullPath =
|
|
3963
|
-
const relPath =
|
|
4410
|
+
const fullPath = join17(dir, file.name);
|
|
4411
|
+
const relPath = join17(relativeBase, file.name);
|
|
3964
4412
|
if (file.isDirectory()) {
|
|
3965
4413
|
if (!["node_modules", ".git", "dist", "build", "coverage", ".next", "out"].includes(
|
|
3966
4414
|
file.name
|
|
@@ -4090,7 +4538,7 @@ Important:
|
|
|
4090
4538
|
}
|
|
4091
4539
|
|
|
4092
4540
|
// src/remediation/tools/generate-patch/helpers.ts
|
|
4093
|
-
import { parseSync as
|
|
4541
|
+
import { parseSync as parseSync3 } from "oxc-parser";
|
|
4094
4542
|
import YAML from "yaml";
|
|
4095
4543
|
function parseLlmAnalysisResponse(text) {
|
|
4096
4544
|
try {
|
|
@@ -4113,7 +4561,7 @@ function validatePatchedCodeSyntax(filePath, code) {
|
|
|
4113
4561
|
const lowerPath = filePath.toLowerCase();
|
|
4114
4562
|
if (/\.[cm]?[jt]sx?$/i.test(lowerPath)) {
|
|
4115
4563
|
try {
|
|
4116
|
-
const res =
|
|
4564
|
+
const res = parseSync3(filePath, code);
|
|
4117
4565
|
if (res.errors && res.errors.length > 0) {
|
|
4118
4566
|
const errorMsg = res.errors.map((e) => e.message).join("; ");
|
|
4119
4567
|
return {
|
|
@@ -4385,15 +4833,124 @@ var generatePatchTool = defineTool({
|
|
|
4385
4833
|
// src/remediation/tools/apply-patch-file/index.ts
|
|
4386
4834
|
import { z as z8 } from "zod";
|
|
4387
4835
|
import { mkdir as mkdir3, writeFile as writeFile3 } from "fs/promises";
|
|
4388
|
-
import {
|
|
4836
|
+
import { existsSync as existsSync14 } from "fs";
|
|
4837
|
+
import { isAbsolute as isAbsolute4, join as join19 } from "path";
|
|
4389
4838
|
import { execa as execa9 } from "execa";
|
|
4390
4839
|
|
|
4840
|
+
// src/remediation/proof-of-fix/runner.ts
|
|
4841
|
+
async function runProofOfFixPhase(options) {
|
|
4842
|
+
const { stage, testFilePath, cwd, timeoutMs = 15e3 } = options;
|
|
4843
|
+
const started = Date.now();
|
|
4844
|
+
const isTs = testFilePath.endsWith(".ts");
|
|
4845
|
+
const nodeArgs = isTs ? ["--experimental-strip-types", testFilePath] : [testFilePath];
|
|
4846
|
+
try {
|
|
4847
|
+
const result = await safeExeca("node", nodeArgs, {
|
|
4848
|
+
cwd,
|
|
4849
|
+
timeoutMs,
|
|
4850
|
+
stdio: "pipe"
|
|
4851
|
+
});
|
|
4852
|
+
const durationMs = Date.now() - started;
|
|
4853
|
+
const output = `${result.stdout || ""}
|
|
4854
|
+
${result.stderr || ""}`.trim();
|
|
4855
|
+
if (stage === "negative") {
|
|
4856
|
+
return {
|
|
4857
|
+
executed: true,
|
|
4858
|
+
passed: false,
|
|
4859
|
+
exitCode: 0,
|
|
4860
|
+
output,
|
|
4861
|
+
durationMs,
|
|
4862
|
+
error: "Exploit reproduction test passed on vulnerable target (failed to reproduce vulnerability)."
|
|
4863
|
+
};
|
|
4864
|
+
}
|
|
4865
|
+
return {
|
|
4866
|
+
executed: true,
|
|
4867
|
+
passed: true,
|
|
4868
|
+
exitCode: 0,
|
|
4869
|
+
output,
|
|
4870
|
+
durationMs
|
|
4871
|
+
};
|
|
4872
|
+
} catch (err) {
|
|
4873
|
+
const durationMs = Date.now() - started;
|
|
4874
|
+
const execaErr = err;
|
|
4875
|
+
const exitCode = typeof execaErr.exitCode === "number" ? execaErr.exitCode : 1;
|
|
4876
|
+
const errorMsg = execaErr.message || String(err);
|
|
4877
|
+
const output = [execaErr.stdout, execaErr.stderr, errorMsg].filter(Boolean).join("\n").trim();
|
|
4878
|
+
if (stage === "negative") {
|
|
4879
|
+
if (execaErr.timedOut || errorMsg.includes("timed out")) {
|
|
4880
|
+
return {
|
|
4881
|
+
executed: true,
|
|
4882
|
+
passed: false,
|
|
4883
|
+
exitCode,
|
|
4884
|
+
output,
|
|
4885
|
+
durationMs,
|
|
4886
|
+
error: "Exploit reproduction test timed out (execution hung)."
|
|
4887
|
+
};
|
|
4888
|
+
}
|
|
4889
|
+
const isHarnessFailure = exitCode === 127 || errorMsg.includes("ENOENT") || output.includes("bad option:") || output.includes("Cannot find module") && output.includes(testFilePath);
|
|
4890
|
+
if (isHarnessFailure) {
|
|
4891
|
+
return {
|
|
4892
|
+
executed: false,
|
|
4893
|
+
passed: false,
|
|
4894
|
+
exitCode,
|
|
4895
|
+
output,
|
|
4896
|
+
durationMs,
|
|
4897
|
+
error: `Reproduction test failed to execute (harness error): ${errorMsg}`
|
|
4898
|
+
};
|
|
4899
|
+
}
|
|
4900
|
+
return {
|
|
4901
|
+
executed: true,
|
|
4902
|
+
passed: true,
|
|
4903
|
+
exitCode,
|
|
4904
|
+
output,
|
|
4905
|
+
durationMs
|
|
4906
|
+
};
|
|
4907
|
+
}
|
|
4908
|
+
return {
|
|
4909
|
+
executed: true,
|
|
4910
|
+
passed: false,
|
|
4911
|
+
exitCode,
|
|
4912
|
+
output,
|
|
4913
|
+
durationMs,
|
|
4914
|
+
error: `Reproduction test failed on patched target: ${errorMsg}`
|
|
4915
|
+
};
|
|
4916
|
+
}
|
|
4917
|
+
}
|
|
4918
|
+
function assembleProofOfFixVerification(params) {
|
|
4919
|
+
const { reproductionTestPath, negativePhase, positivePhase, regressionPhase } = params;
|
|
4920
|
+
const regPhase = regressionPhase ?? {
|
|
4921
|
+
executed: false,
|
|
4922
|
+
passed: true
|
|
4923
|
+
};
|
|
4924
|
+
let status = "verified";
|
|
4925
|
+
let failureReason;
|
|
4926
|
+
if (!negativePhase.passed) {
|
|
4927
|
+
status = "failed";
|
|
4928
|
+
failureReason = negativePhase.error || "negative-phase-failed";
|
|
4929
|
+
} else if (!positivePhase.passed) {
|
|
4930
|
+
status = "failed";
|
|
4931
|
+
failureReason = positivePhase.error || "positive-phase-failed";
|
|
4932
|
+
} else if (regPhase.executed && !regPhase.passed) {
|
|
4933
|
+
status = "failed";
|
|
4934
|
+
failureReason = regPhase.error || "regression-phase-failed";
|
|
4935
|
+
}
|
|
4936
|
+
const durationMs = (negativePhase.durationMs || 0) + (positivePhase.durationMs || 0) + (regPhase.durationMs || 0);
|
|
4937
|
+
return {
|
|
4938
|
+
status,
|
|
4939
|
+
reproductionTestPath,
|
|
4940
|
+
negativePhase,
|
|
4941
|
+
positivePhase,
|
|
4942
|
+
regressionPhase: regPhase,
|
|
4943
|
+
durationMs,
|
|
4944
|
+
failureReason
|
|
4945
|
+
};
|
|
4946
|
+
}
|
|
4947
|
+
|
|
4391
4948
|
// src/remediation/tools/apply-patch-file/helpers.ts
|
|
4392
|
-
import { existsSync as
|
|
4949
|
+
import { existsSync as existsSync13 } from "fs";
|
|
4393
4950
|
import { mkdtemp as mkdtemp2, readFile as readFile5, rm as rm4, writeFile as writeFile2 } from "fs/promises";
|
|
4394
4951
|
import { createHash } from "crypto";
|
|
4395
4952
|
import { tmpdir as tmpdir2 } from "os";
|
|
4396
|
-
import { join as
|
|
4953
|
+
import { join as join18 } from "path";
|
|
4397
4954
|
import { execa as execa8 } from "execa";
|
|
4398
4955
|
async function resolvePatchMode(packageManager, cwd) {
|
|
4399
4956
|
if (packageManager === "npm" || packageManager === "bun" || packageManager === "deno")
|
|
@@ -4428,7 +4985,7 @@ async function writePatchManifest(manifestFilePath, artifact) {
|
|
|
4428
4985
|
await writeFile2(manifestFilePath, JSON.stringify(artifact, null, 2) + "\n", "utf8");
|
|
4429
4986
|
}
|
|
4430
4987
|
async function configurePatchPackagePostinstall(cwd, packageManager) {
|
|
4431
|
-
const pkgJsonPath =
|
|
4988
|
+
const pkgJsonPath = join18(cwd, "package.json");
|
|
4432
4989
|
let pkgJson;
|
|
4433
4990
|
try {
|
|
4434
4991
|
pkgJson = JSON.parse(await readFile5(pkgJsonPath, "utf8"));
|
|
@@ -4468,7 +5025,7 @@ async function configurePatchPackagePostinstall(cwd, packageManager) {
|
|
|
4468
5025
|
return { success: true };
|
|
4469
5026
|
}
|
|
4470
5027
|
async function capturePackageJsonSnapshot(cwd) {
|
|
4471
|
-
const path =
|
|
5028
|
+
const path = join18(cwd, "package.json");
|
|
4472
5029
|
try {
|
|
4473
5030
|
const content = await readFile5(path, "utf8");
|
|
4474
5031
|
return { path, content };
|
|
@@ -4540,8 +5097,8 @@ ${createResult.stderr}`);
|
|
|
4540
5097
|
error: `Could not determine native patch directory for ${packageSpec}.`
|
|
4541
5098
|
};
|
|
4542
5099
|
}
|
|
4543
|
-
const tempPatchDir = await mkdtemp2(
|
|
4544
|
-
const tempPatchFile =
|
|
5100
|
+
const tempPatchDir = await mkdtemp2(join18(tmpdir2(), "autoremediator-native-patch-"));
|
|
5101
|
+
const tempPatchFile = join18(tempPatchDir, "change.patch");
|
|
4545
5102
|
try {
|
|
4546
5103
|
await writeFile2(tempPatchFile, patchContent, "utf8");
|
|
4547
5104
|
await execa8("patch", ["-p1", "-i", tempPatchFile], {
|
|
@@ -4572,12 +5129,12 @@ ${createResult.stderr}`);
|
|
|
4572
5129
|
function extractPatchDirectory(output) {
|
|
4573
5130
|
const lines = output.split(/\r?\n/).map((line) => line.trim()).filter(Boolean);
|
|
4574
5131
|
for (const line of lines) {
|
|
4575
|
-
if (
|
|
5132
|
+
if (existsSync13(line)) {
|
|
4576
5133
|
return line;
|
|
4577
5134
|
}
|
|
4578
5135
|
const tokens = line.split(/\s+/).map((token) => token.replace(/^['"]|['"]$/g, ""));
|
|
4579
5136
|
for (const token of tokens) {
|
|
4580
|
-
if (token.startsWith("/") &&
|
|
5137
|
+
if (token.startsWith("/") && existsSync13(token)) {
|
|
4581
5138
|
return token;
|
|
4582
5139
|
}
|
|
4583
5140
|
}
|
|
@@ -4648,6 +5205,10 @@ var applyPatchFileTool = defineTool({
|
|
|
4648
5205
|
workspace: z8.string().optional(),
|
|
4649
5206
|
validateWithTests: z8.boolean().optional().default(true).describe("Run package manager test command to validate patch doesn't break anything"),
|
|
4650
5207
|
reproductionTest: z8.string().optional().describe("Optional synthesized reproduction test script verifying fix"),
|
|
5208
|
+
proofOfFix: z8.union([z8.boolean(), z8.string()]).optional().describe(
|
|
5209
|
+
"If true or script path/content, enforce dual-stage exploit reproduction (assert failure pre-patch, assert pass post-patch)"
|
|
5210
|
+
),
|
|
5211
|
+
requireProofOfFix: z8.boolean().optional().describe("If true, fail and roll back patch if proof-of-fix verification fails"),
|
|
4651
5212
|
dryRun: z8.boolean().optional().default(false).describe("If true, report but do not mutate files")
|
|
4652
5213
|
}).refine((value) => Boolean(value.patchContent || value.patches && value.patches.length > 0), {
|
|
4653
5214
|
message: "Either patchContent or patches must be provided"
|
|
@@ -4670,6 +5231,8 @@ var applyPatchFileTool = defineTool({
|
|
|
4670
5231
|
riskLevel,
|
|
4671
5232
|
validateWithTests,
|
|
4672
5233
|
reproductionTest,
|
|
5234
|
+
proofOfFix,
|
|
5235
|
+
requireProofOfFix,
|
|
4673
5236
|
dryRun
|
|
4674
5237
|
}) => {
|
|
4675
5238
|
try {
|
|
@@ -4720,7 +5283,7 @@ var applyPatchFileTool = defineTool({
|
|
|
4720
5283
|
};
|
|
4721
5284
|
}
|
|
4722
5285
|
const patchFileName = buildPatchFileName(packageName, vulnerableVersion);
|
|
4723
|
-
const patchFilePath =
|
|
5286
|
+
const patchFilePath = join19(cwd, patchesDir, patchFileName);
|
|
4724
5287
|
const manifestFilePath = `${patchFilePath}.json`;
|
|
4725
5288
|
const generatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
4726
5289
|
const baseArtifact = {
|
|
@@ -4759,7 +5322,7 @@ var applyPatchFileTool = defineTool({
|
|
|
4759
5322
|
}
|
|
4760
5323
|
return withRepoLock(cwd, async () => {
|
|
4761
5324
|
const packageJsonSnapshot = patchModeRequiresPackageJsonSnapshot(pm) ? await capturePackageJsonSnapshot(cwd) : void 0;
|
|
4762
|
-
const patchesDirPath =
|
|
5325
|
+
const patchesDirPath = join19(cwd, patchesDir);
|
|
4763
5326
|
await mkdir3(patchesDirPath, { recursive: true });
|
|
4764
5327
|
await writeFile3(patchFilePath, selectedPatch, "utf8");
|
|
4765
5328
|
validationPhases.push({
|
|
@@ -4782,6 +5345,101 @@ var applyPatchFileTool = defineTool({
|
|
|
4782
5345
|
});
|
|
4783
5346
|
artifact.validationPhases = validationPhases;
|
|
4784
5347
|
await writePatchManifest(manifestFilePath, artifact);
|
|
5348
|
+
let reproductionTestPath;
|
|
5349
|
+
let negativePhaseResult;
|
|
5350
|
+
if (typeof proofOfFix === "string" && proofOfFix.trim().length > 0) {
|
|
5351
|
+
const resolvedPath = isAbsolute4(proofOfFix) ? proofOfFix : join19(cwd, proofOfFix);
|
|
5352
|
+
if (existsSync14(resolvedPath)) {
|
|
5353
|
+
reproductionTestPath = resolvedPath;
|
|
5354
|
+
} else {
|
|
5355
|
+
const safeName = packageName.replace(/^@/, "").replace(/\//g, "+");
|
|
5356
|
+
reproductionTestPath = join19(patchesDirPath, `${safeName}+${vulnerableVersion}.test.cjs`);
|
|
5357
|
+
await writeFile3(reproductionTestPath, proofOfFix, "utf8");
|
|
5358
|
+
}
|
|
5359
|
+
} else if (reproductionTest) {
|
|
5360
|
+
const safeName = packageName.replace(/^@/, "").replace(/\//g, "+");
|
|
5361
|
+
reproductionTestPath = join19(patchesDirPath, `${safeName}+${vulnerableVersion}.test.cjs`);
|
|
5362
|
+
await writeFile3(reproductionTestPath, reproductionTest, "utf8");
|
|
5363
|
+
}
|
|
5364
|
+
const isProofOfFixActive = Boolean(proofOfFix || requireProofOfFix);
|
|
5365
|
+
if (isProofOfFixActive) {
|
|
5366
|
+
if (!reproductionTestPath) {
|
|
5367
|
+
if (requireProofOfFix) {
|
|
5368
|
+
await cleanupPatchArtifacts({
|
|
5369
|
+
cwd,
|
|
5370
|
+
patchFilePath,
|
|
5371
|
+
manifestFilePath,
|
|
5372
|
+
patchMode,
|
|
5373
|
+
packageJsonSnapshot,
|
|
5374
|
+
installCommand,
|
|
5375
|
+
rerunInstall: false
|
|
5376
|
+
});
|
|
5377
|
+
return {
|
|
5378
|
+
success: false,
|
|
5379
|
+
packageName,
|
|
5380
|
+
vulnerableVersion,
|
|
5381
|
+
applied: false,
|
|
5382
|
+
dryRun: false,
|
|
5383
|
+
message: "Proof-of-fix verification required, but no reproduction test or script was supplied.",
|
|
5384
|
+
patchFilePath,
|
|
5385
|
+
manifestFilePath,
|
|
5386
|
+
patchPath: patchFilePath,
|
|
5387
|
+
patchMode,
|
|
5388
|
+
postinstallConfigured: false,
|
|
5389
|
+
patchArtifact: {
|
|
5390
|
+
...artifact,
|
|
5391
|
+
applied: false,
|
|
5392
|
+
validationPhases
|
|
5393
|
+
},
|
|
5394
|
+
validationPhases,
|
|
5395
|
+
error: "Proof-of-fix verification required, but no reproduction test or script was supplied."
|
|
5396
|
+
};
|
|
5397
|
+
}
|
|
5398
|
+
} else {
|
|
5399
|
+
negativePhaseResult = await runProofOfFixPhase({
|
|
5400
|
+
stage: "negative",
|
|
5401
|
+
testFilePath: reproductionTestPath,
|
|
5402
|
+
cwd
|
|
5403
|
+
});
|
|
5404
|
+
validationPhases.push({
|
|
5405
|
+
phase: "proof-of-fix-negative",
|
|
5406
|
+
passed: negativePhaseResult.passed,
|
|
5407
|
+
message: negativePhaseResult.passed ? "Reproduction test reproduced vulnerability on unpatched code." : void 0,
|
|
5408
|
+
error: negativePhaseResult.passed ? void 0 : negativePhaseResult.error
|
|
5409
|
+
});
|
|
5410
|
+
if (!negativePhaseResult.passed && requireProofOfFix) {
|
|
5411
|
+
await cleanupPatchArtifacts({
|
|
5412
|
+
cwd,
|
|
5413
|
+
patchFilePath,
|
|
5414
|
+
manifestFilePath,
|
|
5415
|
+
patchMode,
|
|
5416
|
+
packageJsonSnapshot,
|
|
5417
|
+
installCommand,
|
|
5418
|
+
rerunInstall: false
|
|
5419
|
+
});
|
|
5420
|
+
return {
|
|
5421
|
+
success: false,
|
|
5422
|
+
packageName,
|
|
5423
|
+
vulnerableVersion,
|
|
5424
|
+
applied: false,
|
|
5425
|
+
dryRun: false,
|
|
5426
|
+
message: `Proof-of-fix negative phase failed: ${negativePhaseResult.error}`,
|
|
5427
|
+
patchFilePath,
|
|
5428
|
+
manifestFilePath,
|
|
5429
|
+
patchPath: patchFilePath,
|
|
5430
|
+
patchMode,
|
|
5431
|
+
postinstallConfigured: false,
|
|
5432
|
+
patchArtifact: {
|
|
5433
|
+
...artifact,
|
|
5434
|
+
applied: false,
|
|
5435
|
+
validationPhases
|
|
5436
|
+
},
|
|
5437
|
+
validationPhases,
|
|
5438
|
+
error: `Proof-of-fix negative phase failed: ${negativePhaseResult.error}`
|
|
5439
|
+
};
|
|
5440
|
+
}
|
|
5441
|
+
}
|
|
5442
|
+
}
|
|
4785
5443
|
const applyResult = patchMode === "patch-package" ? await configurePatchPackagePostinstall(cwd, pm) : await applyNativePatch({
|
|
4786
5444
|
cwd,
|
|
4787
5445
|
packageName,
|
|
@@ -4922,34 +5580,90 @@ var applyPatchFileTool = defineTool({
|
|
|
4922
5580
|
message: "Patch validation tests passed."
|
|
4923
5581
|
});
|
|
4924
5582
|
}
|
|
4925
|
-
let
|
|
4926
|
-
if (
|
|
4927
|
-
|
|
4928
|
-
|
|
4929
|
-
|
|
4930
|
-
|
|
4931
|
-
|
|
4932
|
-
validationPhases.push({
|
|
4933
|
-
phase: "reproduction-test",
|
|
4934
|
-
passed: reproRun.exitCode === 0,
|
|
4935
|
-
message: "Reproduction test verified fix successfully."
|
|
5583
|
+
let positivePhaseResult;
|
|
5584
|
+
if (reproductionTestPath) {
|
|
5585
|
+
if (isProofOfFixActive) {
|
|
5586
|
+
positivePhaseResult = await runProofOfFixPhase({
|
|
5587
|
+
stage: "positive",
|
|
5588
|
+
testFilePath: reproductionTestPath,
|
|
5589
|
+
cwd
|
|
4936
5590
|
});
|
|
4937
|
-
} catch (err) {
|
|
4938
|
-
const reproErr = err instanceof Error ? err.message : String(err);
|
|
4939
5591
|
validationPhases.push({
|
|
4940
|
-
phase: "
|
|
4941
|
-
passed:
|
|
4942
|
-
|
|
5592
|
+
phase: "proof-of-fix-positive",
|
|
5593
|
+
passed: positivePhaseResult.passed,
|
|
5594
|
+
message: positivePhaseResult.passed ? "Reproduction test passed on patched code (vulnerability mitigated)." : void 0,
|
|
5595
|
+
error: positivePhaseResult.passed ? void 0 : positivePhaseResult.error
|
|
4943
5596
|
});
|
|
5597
|
+
if (!positivePhaseResult.passed && (requireProofOfFix || validateWithTests)) {
|
|
5598
|
+
await cleanupPatchArtifacts({
|
|
5599
|
+
cwd,
|
|
5600
|
+
patchFilePath,
|
|
5601
|
+
manifestFilePath,
|
|
5602
|
+
patchMode,
|
|
5603
|
+
packageJsonSnapshot,
|
|
5604
|
+
installCommand,
|
|
5605
|
+
rerunInstall: patchMode === "patch-package"
|
|
5606
|
+
});
|
|
5607
|
+
return {
|
|
5608
|
+
success: false,
|
|
5609
|
+
packageName,
|
|
5610
|
+
vulnerableVersion,
|
|
5611
|
+
applied: false,
|
|
5612
|
+
dryRun: false,
|
|
5613
|
+
message: `Proof-of-fix positive phase failed: ${positivePhaseResult.error}`,
|
|
5614
|
+
patchFilePath,
|
|
5615
|
+
manifestFilePath,
|
|
5616
|
+
patchPath: patchFilePath,
|
|
5617
|
+
patchMode,
|
|
5618
|
+
postinstallConfigured: false,
|
|
5619
|
+
patchArtifact: {
|
|
5620
|
+
...artifact,
|
|
5621
|
+
applied: false,
|
|
5622
|
+
validationPhases
|
|
5623
|
+
},
|
|
5624
|
+
validationPhases,
|
|
5625
|
+
error: `Proof-of-fix positive phase failed: ${positivePhaseResult.error}`
|
|
5626
|
+
};
|
|
5627
|
+
}
|
|
5628
|
+
} else {
|
|
5629
|
+
try {
|
|
5630
|
+
const reproRun = await execa9("node", [reproductionTestPath], { cwd, stdio: "pipe" });
|
|
5631
|
+
validationPhases.push({
|
|
5632
|
+
phase: "reproduction-test",
|
|
5633
|
+
passed: reproRun.exitCode === 0,
|
|
5634
|
+
message: "Reproduction test verified fix successfully."
|
|
5635
|
+
});
|
|
5636
|
+
} catch (err) {
|
|
5637
|
+
const reproErr = err instanceof Error ? err.message : String(err);
|
|
5638
|
+
validationPhases.push({
|
|
5639
|
+
phase: "reproduction-test",
|
|
5640
|
+
passed: false,
|
|
5641
|
+
error: `Reproduction test failed: ${reproErr}`
|
|
5642
|
+
});
|
|
5643
|
+
}
|
|
4944
5644
|
}
|
|
4945
5645
|
}
|
|
5646
|
+
let proofOfFixVerification;
|
|
5647
|
+
if (negativePhaseResult && positivePhaseResult) {
|
|
5648
|
+
proofOfFixVerification = assembleProofOfFixVerification({
|
|
5649
|
+
reproductionTestPath,
|
|
5650
|
+
negativePhase: negativePhaseResult,
|
|
5651
|
+
positivePhase: positivePhaseResult,
|
|
5652
|
+
regressionPhase: validationResult ? {
|
|
5653
|
+
executed: true,
|
|
5654
|
+
passed: validationResult.passed,
|
|
5655
|
+
error: validationResult.error
|
|
5656
|
+
} : void 0
|
|
5657
|
+
});
|
|
5658
|
+
}
|
|
4946
5659
|
const finalArtifact = {
|
|
4947
5660
|
...artifact,
|
|
4948
5661
|
applied: true,
|
|
4949
5662
|
dryRun: false,
|
|
4950
5663
|
reproductionTest,
|
|
4951
5664
|
reproductionTestPath,
|
|
4952
|
-
validationPhases
|
|
5665
|
+
validationPhases,
|
|
5666
|
+
proofOfFix: proofOfFixVerification
|
|
4953
5667
|
};
|
|
4954
5668
|
await writePatchManifest(manifestFilePath, finalArtifact);
|
|
4955
5669
|
return {
|
|
@@ -4966,7 +5680,8 @@ var applyPatchFileTool = defineTool({
|
|
|
4966
5680
|
postinstallConfigured: patchMode === "patch-package",
|
|
4967
5681
|
patchArtifact: finalArtifact,
|
|
4968
5682
|
validation: validationResult,
|
|
4969
|
-
validationPhases
|
|
5683
|
+
validationPhases,
|
|
5684
|
+
proofOfFix: proofOfFixVerification
|
|
4970
5685
|
};
|
|
4971
5686
|
});
|
|
4972
5687
|
} catch (err) {
|
|
@@ -5210,6 +5925,9 @@ async function tryLocalPatchFallback(params) {
|
|
|
5210
5925
|
workspace: params.workspace,
|
|
5211
5926
|
riskLevel: patchResult.riskLevel,
|
|
5212
5927
|
validateWithTests: params.runTests,
|
|
5928
|
+
reproductionTest: patchResult.reproductionTest,
|
|
5929
|
+
proofOfFix: params.proofOfFix,
|
|
5930
|
+
requireProofOfFix: params.requireProofOfFix,
|
|
5213
5931
|
dryRun: params.dryRun
|
|
5214
5932
|
});
|
|
5215
5933
|
steps += 1;
|
|
@@ -5258,12 +5976,14 @@ function resolveLocalRunOptions(options) {
|
|
|
5258
5976
|
const runTests = options.runTests ?? false;
|
|
5259
5977
|
const policy = options.policy ?? "";
|
|
5260
5978
|
const patchesDir = options.patchesDir || "./patches";
|
|
5979
|
+
const loadedPolicy = loadPolicy(cwd, options.policy);
|
|
5261
5980
|
const constraints = {
|
|
5262
5981
|
...options.constraints,
|
|
5263
|
-
typecheck: options.typecheck ?? options.constraints?.typecheck,
|
|
5264
|
-
supplyChainPolicy: options.supplyChainPolicy ?? options.constraints?.supplyChainPolicy
|
|
5982
|
+
typecheck: options.typecheck ?? options.constraints?.typecheck ?? loadedPolicy.constraints?.typecheck,
|
|
5983
|
+
supplyChainPolicy: options.supplyChainPolicy ?? options.constraints?.supplyChainPolicy ?? loadedPolicy.constraints?.supplyChainPolicy,
|
|
5984
|
+
enableBreakingChangeRefactor: options.breakingChangeRefactor ?? options.constraints?.enableBreakingChangeRefactor ?? loadedPolicy.constraints?.enableBreakingChangeRefactor,
|
|
5985
|
+
requireProofOfFix: options.requireProofOfFix ?? options.constraints?.requireProofOfFix ?? loadedPolicy.constraints?.requireProofOfFix
|
|
5265
5986
|
};
|
|
5266
|
-
const loadedPolicy = loadPolicy(cwd, options.policy);
|
|
5267
5987
|
const llmProvider = resolveProvider(options);
|
|
5268
5988
|
return {
|
|
5269
5989
|
cwd,
|
|
@@ -5294,7 +6014,10 @@ function resolveLocalRunOptions(options) {
|
|
|
5294
6014
|
regressionCheck: options.regressionCheck ?? false,
|
|
5295
6015
|
escalationGraph: options.escalationGraph ?? loadedPolicy.escalationGraph,
|
|
5296
6016
|
offlineIntelligence: options.offlineIntelligence ?? false,
|
|
5297
|
-
intelligenceSnapshotPath: options.intelligenceSnapshotPath
|
|
6017
|
+
intelligenceSnapshotPath: options.intelligenceSnapshotPath,
|
|
6018
|
+
proofOfFix: options.proofOfFix,
|
|
6019
|
+
requireProofOfFix: options.requireProofOfFix ?? constraints.requireProofOfFix,
|
|
6020
|
+
breakingChangeRefactor: options.breakingChangeRefactor ?? constraints.enableBreakingChangeRefactor
|
|
5298
6021
|
};
|
|
5299
6022
|
}
|
|
5300
6023
|
|
|
@@ -5462,7 +6185,9 @@ async function runLocalRemediationPipeline(cveId, options = {}) {
|
|
|
5462
6185
|
regressionCheck,
|
|
5463
6186
|
escalationGraph,
|
|
5464
6187
|
offlineIntelligence,
|
|
5465
|
-
intelligenceSnapshotPath
|
|
6188
|
+
intelligenceSnapshotPath,
|
|
6189
|
+
proofOfFix,
|
|
6190
|
+
requireProofOfFix
|
|
5466
6191
|
} = resolved;
|
|
5467
6192
|
const correlation = {
|
|
5468
6193
|
requestId: options.requestId,
|
|
@@ -5639,7 +6364,9 @@ async function runLocalRemediationPipeline(cveId, options = {}) {
|
|
|
5639
6364
|
installPreferOffline: constraints.installPreferOffline,
|
|
5640
6365
|
enforceFrozenLockfile: constraints.enforceFrozenLockfile,
|
|
5641
6366
|
workspace: constraints.workspace,
|
|
5642
|
-
vulnerableRange: vulnerable.affected.vulnerableRange
|
|
6367
|
+
vulnerableRange: vulnerable.affected.vulnerableRange,
|
|
6368
|
+
proofOfFix,
|
|
6369
|
+
requireProofOfFix
|
|
5643
6370
|
});
|
|
5644
6371
|
agentSteps += fallback.steps;
|
|
5645
6372
|
collectedResults.push(finalizePatchResult(fallback.result));
|
|
@@ -5745,8 +6472,8 @@ function accumulateStepResults(params) {
|
|
|
5745
6472
|
}
|
|
5746
6473
|
|
|
5747
6474
|
// src/remediation/orchestration-prompt.ts
|
|
5748
|
-
import { existsSync as
|
|
5749
|
-
import { join as
|
|
6475
|
+
import { existsSync as existsSync15, readFileSync as readFileSync15 } from "fs";
|
|
6476
|
+
import { join as join20 } from "path";
|
|
5750
6477
|
function buildProviderAddendum(provider, personality = "balanced") {
|
|
5751
6478
|
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.";
|
|
5752
6479
|
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.";
|
|
@@ -5757,13 +6484,13 @@ Provider profile:
|
|
|
5757
6484
|
- ${personalityDirective}`;
|
|
5758
6485
|
}
|
|
5759
6486
|
function loadOrchestrationPrompt(ctx) {
|
|
5760
|
-
const promptPath =
|
|
6487
|
+
const promptPath = join20(
|
|
5761
6488
|
process.cwd(),
|
|
5762
6489
|
".github",
|
|
5763
6490
|
"instructions",
|
|
5764
6491
|
"orchestration.instructions.md"
|
|
5765
6492
|
);
|
|
5766
|
-
if (!
|
|
6493
|
+
if (!existsSync15(promptPath)) {
|
|
5767
6494
|
return `You are autoremediator, an agentic security remediation system for Node.js package dependencies.
|
|
5768
6495
|
Working directory: ${ctx.cwd}
|
|
5769
6496
|
Package manager: ${ctx.packageManager}
|
|
@@ -5789,7 +6516,7 @@ Fallback sequence (when neither version bump nor override can be applied):
|
|
|
5789
6516
|
|
|
5790
6517
|
Always respect dryRun and policy constraints.`;
|
|
5791
6518
|
}
|
|
5792
|
-
const template =
|
|
6519
|
+
const template = readFileSync15(promptPath, "utf8");
|
|
5793
6520
|
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(
|
|
5794
6521
|
"{{directDependenciesOnly}}",
|
|
5795
6522
|
String(ctx.constraints.directDependenciesOnly ?? false)
|
|
@@ -6268,6 +6995,8 @@ function createRuntimeToolsForRun(options) {
|
|
|
6268
6995
|
installPreferOffline: constraints.installPreferOffline,
|
|
6269
6996
|
enforceFrozenLockfile: constraints.enforceFrozenLockfile,
|
|
6270
6997
|
workspace: constraints.workspace,
|
|
6998
|
+
typecheck: input.typecheck ?? options.typecheck ?? constraints.typecheck,
|
|
6999
|
+
breakingChangeRefactor: input.breakingChangeRefactor ?? options.breakingChangeRefactor ?? constraints.enableBreakingChangeRefactor,
|
|
6271
7000
|
dryRun: preview ? true : input.dryRun
|
|
6272
7001
|
})
|
|
6273
7002
|
};
|
|
@@ -6292,6 +7021,8 @@ function createRuntimeToolsForRun(options) {
|
|
|
6292
7021
|
installPreferOffline: constraints.installPreferOffline,
|
|
6293
7022
|
enforceFrozenLockfile: constraints.enforceFrozenLockfile,
|
|
6294
7023
|
workspace: constraints.workspace,
|
|
7024
|
+
proofOfFix: input.proofOfFix ?? options.proofOfFix,
|
|
7025
|
+
requireProofOfFix: input.requireProofOfFix ?? options.requireProofOfFix ?? constraints.requireProofOfFix,
|
|
6295
7026
|
dryRun: preview ? true : input.dryRun
|
|
6296
7027
|
})
|
|
6297
7028
|
};
|
|
@@ -6722,13 +7453,17 @@ function resolveConstraints(options, cwd) {
|
|
|
6722
7453
|
installMode: options.constraints?.installMode ?? policy.constraints?.installMode ?? "deterministic",
|
|
6723
7454
|
installPreferOffline: options.constraints?.installPreferOffline ?? policy.constraints?.installPreferOffline,
|
|
6724
7455
|
enforceFrozenLockfile: options.constraints?.enforceFrozenLockfile ?? policy.constraints?.enforceFrozenLockfile,
|
|
6725
|
-
workspace: options.constraints?.workspace ?? policy.constraints?.workspace
|
|
7456
|
+
workspace: options.constraints?.workspace ?? policy.constraints?.workspace,
|
|
7457
|
+
typecheck: options.typecheck ?? options.constraints?.typecheck ?? policy.constraints?.typecheck,
|
|
7458
|
+
requireProofOfFix: options.requireProofOfFix ?? options.constraints?.requireProofOfFix ?? policy.constraints?.requireProofOfFix,
|
|
7459
|
+
enableBreakingChangeRefactor: options.breakingChangeRefactor ?? options.constraints?.enableBreakingChangeRefactor ?? policy.constraints?.enableBreakingChangeRefactor,
|
|
7460
|
+
supplyChainPolicy: options.supplyChainPolicy ?? options.constraints?.supplyChainPolicy ?? policy.constraints?.supplyChainPolicy
|
|
6726
7461
|
};
|
|
6727
7462
|
}
|
|
6728
7463
|
|
|
6729
7464
|
// src/platform/evidence.ts
|
|
6730
|
-
import { mkdirSync as mkdirSync3, writeFileSync as
|
|
6731
|
-
import { join as
|
|
7465
|
+
import { mkdirSync as mkdirSync3, writeFileSync as writeFileSync6 } from "fs";
|
|
7466
|
+
import { join as join21 } from "path";
|
|
6732
7467
|
function createEvidenceLog(cwd, cveIds, context = {}) {
|
|
6733
7468
|
return {
|
|
6734
7469
|
runId: `${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,
|
|
@@ -6760,10 +7495,10 @@ function finalizeEvidence(log) {
|
|
|
6760
7495
|
return log;
|
|
6761
7496
|
}
|
|
6762
7497
|
function writeEvidenceLog(cwd, log) {
|
|
6763
|
-
const dir =
|
|
7498
|
+
const dir = join21(cwd, ".autoremediator", "evidence");
|
|
6764
7499
|
mkdirSync3(dir, { recursive: true });
|
|
6765
|
-
const filePath =
|
|
6766
|
-
|
|
7500
|
+
const filePath = join21(dir, `${log.runId}.json`);
|
|
7501
|
+
writeFileSync6(filePath, JSON.stringify(log, null, 2) + "\n", "utf8");
|
|
6767
7502
|
return filePath;
|
|
6768
7503
|
}
|
|
6769
7504
|
|
|
@@ -7366,12 +8101,12 @@ async function planRemediation(cveId, options = {}) {
|
|
|
7366
8101
|
}
|
|
7367
8102
|
|
|
7368
8103
|
// src/scanner/parse-input.ts
|
|
7369
|
-
import { extname, resolve as
|
|
7370
|
-
import { readFileSync as
|
|
8104
|
+
import { extname, resolve as resolve5 } from "path";
|
|
8105
|
+
import { readFileSync as readFileSync19 } from "fs";
|
|
7371
8106
|
import { execa as execa11 } from "execa";
|
|
7372
8107
|
|
|
7373
8108
|
// src/scanner/adapters/npm-audit.ts
|
|
7374
|
-
import { readFileSync as
|
|
8109
|
+
import { readFileSync as readFileSync16 } from "fs";
|
|
7375
8110
|
var CVE_REGEX = /CVE-\d{4}-\d{1,7}/gi;
|
|
7376
8111
|
var GHSA_REGEX = /GHSA-[a-z0-9]{4}-[a-z0-9]{4}-[a-z0-9]{4}/gi;
|
|
7377
8112
|
function normalizeSeverity(raw) {
|
|
@@ -7407,12 +8142,12 @@ function parseNpmAuditJsonFromString(content) {
|
|
|
7407
8142
|
return findings;
|
|
7408
8143
|
}
|
|
7409
8144
|
function parseNpmAuditJsonFile(filePath) {
|
|
7410
|
-
const content =
|
|
8145
|
+
const content = readFileSync16(filePath, "utf8");
|
|
7411
8146
|
return parseNpmAuditJsonFromString(content);
|
|
7412
8147
|
}
|
|
7413
8148
|
|
|
7414
8149
|
// src/scanner/adapters/yarn-audit.ts
|
|
7415
|
-
import { readFileSync as
|
|
8150
|
+
import { readFileSync as readFileSync17 } from "fs";
|
|
7416
8151
|
var CVE_REGEX2 = /CVE-\d{4}-\d{1,7}/gi;
|
|
7417
8152
|
var GHSA_REGEX2 = /GHSA-[a-z0-9]{4}-[a-z0-9]{4}-[a-z0-9]{4}/gi;
|
|
7418
8153
|
function normalizeSeverity2(raw) {
|
|
@@ -7457,12 +8192,12 @@ function parseYarnAuditJsonFromString(content) {
|
|
|
7457
8192
|
return findings;
|
|
7458
8193
|
}
|
|
7459
8194
|
function parseYarnAuditJsonFile(filePath) {
|
|
7460
|
-
const content =
|
|
8195
|
+
const content = readFileSync17(filePath, "utf8");
|
|
7461
8196
|
return parseYarnAuditJsonFromString(content);
|
|
7462
8197
|
}
|
|
7463
8198
|
|
|
7464
8199
|
// src/scanner/adapters/sarif.ts
|
|
7465
|
-
import { readFileSync as
|
|
8200
|
+
import { readFileSync as readFileSync18 } from "fs";
|
|
7466
8201
|
var CVE_REGEX3 = /CVE-\d{4}-\d{1,7}/gi;
|
|
7467
8202
|
function extractPackageName(result) {
|
|
7468
8203
|
const pkg = result.properties?.["packageName"];
|
|
@@ -7500,7 +8235,7 @@ function parseSarifFromString(content) {
|
|
|
7500
8235
|
return findings;
|
|
7501
8236
|
}
|
|
7502
8237
|
function parseSarifFile(filePath) {
|
|
7503
|
-
const content =
|
|
8238
|
+
const content = readFileSync18(filePath, "utf8");
|
|
7504
8239
|
return parseSarifFromString(content);
|
|
7505
8240
|
}
|
|
7506
8241
|
|
|
@@ -7509,7 +8244,7 @@ function parseScanInput(filePath, format) {
|
|
|
7509
8244
|
if (filePath.includes("\0")) {
|
|
7510
8245
|
throw new Error("Invalid scan input path: path contains null bytes");
|
|
7511
8246
|
}
|
|
7512
|
-
const resolvedPath =
|
|
8247
|
+
const resolvedPath = resolve5(filePath);
|
|
7513
8248
|
const resolved = format === "auto" ? inferFormat(resolvedPath) : format;
|
|
7514
8249
|
if (resolved === "npm-audit") {
|
|
7515
8250
|
return parseNpmAuditJsonFile(resolvedPath);
|
|
@@ -7585,7 +8320,7 @@ function inferFormat(filePath) {
|
|
|
7585
8320
|
const ext = extname(filePath).toLowerCase();
|
|
7586
8321
|
if (ext === ".sarif") return "sarif";
|
|
7587
8322
|
try {
|
|
7588
|
-
const content =
|
|
8323
|
+
const content = readFileSync19(filePath, "utf8");
|
|
7589
8324
|
const firstLine = content.split("\n").find((line) => line.trim().startsWith("{"));
|
|
7590
8325
|
if (firstLine) {
|
|
7591
8326
|
const parsed = JSON.parse(firstLine);
|
|
@@ -7996,14 +8731,14 @@ async function remediatePortfolio(targets, options = {}) {
|
|
|
7996
8731
|
}
|
|
7997
8732
|
|
|
7998
8733
|
// src/api/update-outdated/index.ts
|
|
7999
|
-
import { join as
|
|
8000
|
-
import { readFileSync as
|
|
8734
|
+
import { join as join22 } from "path";
|
|
8735
|
+
import { readFileSync as readFileSync20, writeFileSync as writeFileSync7 } from "fs";
|
|
8001
8736
|
import { execa as execa12 } from "execa";
|
|
8002
8737
|
async function applyBump(params) {
|
|
8003
|
-
const pkgPath =
|
|
8738
|
+
const pkgPath = join22(params.cwd, "package.json");
|
|
8004
8739
|
let pkgJson;
|
|
8005
8740
|
try {
|
|
8006
|
-
pkgJson = JSON.parse(
|
|
8741
|
+
pkgJson = JSON.parse(readFileSync20(pkgPath, "utf8"));
|
|
8007
8742
|
} catch {
|
|
8008
8743
|
return {
|
|
8009
8744
|
applied: false,
|
|
@@ -8025,13 +8760,13 @@ async function applyBump(params) {
|
|
|
8025
8760
|
const newRange = `${prefix}${params.toVersion}`;
|
|
8026
8761
|
return withRepoLock(params.cwd, async () => {
|
|
8027
8762
|
pkgJson[depField][params.packageName] = newRange;
|
|
8028
|
-
|
|
8763
|
+
writeFileSync7(pkgPath, JSON.stringify(pkgJson, null, 2) + "\n", "utf8");
|
|
8029
8764
|
try {
|
|
8030
8765
|
const [installCmd, ...installArgs] = params.installCommand;
|
|
8031
8766
|
await execa12(installCmd, installArgs, { cwd: params.cwd, stdio: "pipe" });
|
|
8032
8767
|
} catch (err) {
|
|
8033
8768
|
pkgJson[depField][params.packageName] = currentRange;
|
|
8034
|
-
|
|
8769
|
+
writeFileSync7(pkgPath, JSON.stringify(pkgJson, null, 2) + "\n", "utf8");
|
|
8035
8770
|
const message = err instanceof Error ? err.message : String(err);
|
|
8036
8771
|
return {
|
|
8037
8772
|
applied: false,
|
|
@@ -8044,7 +8779,7 @@ async function applyBump(params) {
|
|
|
8044
8779
|
await execa12(testCmd, testArgs, { cwd: params.cwd, stdio: "pipe" });
|
|
8045
8780
|
} catch (err) {
|
|
8046
8781
|
pkgJson[depField][params.packageName] = currentRange;
|
|
8047
|
-
|
|
8782
|
+
writeFileSync7(pkgPath, JSON.stringify(pkgJson, null, 2) + "\n", "utf8");
|
|
8048
8783
|
try {
|
|
8049
8784
|
const [rollbackCmd, ...rollbackArgs] = params.installCommand;
|
|
8050
8785
|
await execa12(rollbackCmd, rollbackArgs, { cwd: params.cwd, stdio: "pipe" });
|
|
@@ -8249,8 +8984,8 @@ async function checkReachability(options) {
|
|
|
8249
8984
|
}
|
|
8250
8985
|
|
|
8251
8986
|
// src/scanner/delta-scan.ts
|
|
8252
|
-
import { existsSync as
|
|
8253
|
-
import { join as
|
|
8987
|
+
import { existsSync as existsSync16, readFileSync as readFileSync21 } from "fs";
|
|
8988
|
+
import { join as join23 } from "path";
|
|
8254
8989
|
import { execa as execa13 } from "execa";
|
|
8255
8990
|
import semver9 from "semver";
|
|
8256
8991
|
|
|
@@ -8443,7 +9178,7 @@ async function getChangedManifestPaths(cwd, baseRef) {
|
|
|
8443
9178
|
}
|
|
8444
9179
|
}
|
|
8445
9180
|
} catch {
|
|
8446
|
-
if (
|
|
9181
|
+
if (existsSync16(join23(cwd, "package.json"))) {
|
|
8447
9182
|
manifests.add("package.json");
|
|
8448
9183
|
}
|
|
8449
9184
|
}
|
|
@@ -8462,10 +9197,10 @@ async function getOldManifestContent(cwd, baseRef, relPath) {
|
|
|
8462
9197
|
}
|
|
8463
9198
|
}
|
|
8464
9199
|
function getNewManifestContent(cwd, relPath) {
|
|
8465
|
-
const fullPath =
|
|
8466
|
-
if (!
|
|
9200
|
+
const fullPath = join23(cwd, relPath);
|
|
9201
|
+
if (!existsSync16(fullPath)) return null;
|
|
8467
9202
|
try {
|
|
8468
|
-
return JSON.parse(
|
|
9203
|
+
return JSON.parse(readFileSync21(fullPath, "utf8"));
|
|
8469
9204
|
} catch {
|
|
8470
9205
|
return null;
|
|
8471
9206
|
}
|
|
@@ -8579,8 +9314,8 @@ async function scanDelta(options = {}) {
|
|
|
8579
9314
|
}
|
|
8580
9315
|
for (const lockfileRel of changedLockfiles) {
|
|
8581
9316
|
const oldContent = await getFileContentAtRef(cwd, baseRef, lockfileRel);
|
|
8582
|
-
const fullPath =
|
|
8583
|
-
const newContent =
|
|
9317
|
+
const fullPath = join23(cwd, lockfileRel);
|
|
9318
|
+
const newContent = existsSync16(fullPath) ? readFileSync21(fullPath, "utf8") : null;
|
|
8584
9319
|
const lockDiffs = computeLockfileDiff(lockfileRel, oldContent, newContent);
|
|
8585
9320
|
for (const change of lockDiffs) {
|
|
8586
9321
|
if (!directPkgs.has(change.packageName)) {
|
|
@@ -8783,9 +9518,9 @@ function toSarifOutput(report) {
|
|
|
8783
9518
|
}
|
|
8784
9519
|
|
|
8785
9520
|
// src/version.ts
|
|
8786
|
-
import { readFileSync as
|
|
9521
|
+
import { readFileSync as readFileSync22 } from "fs";
|
|
8787
9522
|
function readPackageVersion() {
|
|
8788
|
-
const raw =
|
|
9523
|
+
const raw = readFileSync22(new URL("../package.json", import.meta.url), "utf8");
|
|
8789
9524
|
const metadata = JSON.parse(raw);
|
|
8790
9525
|
if (!metadata.version) {
|
|
8791
9526
|
throw new Error("packages/core/package.json is missing a version field.");
|