autoremediator 0.1.2 → 0.2.1
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 +53 -76
- package/dist/{chunk-H4ICCI3K.js → chunk-DQKT2CUG.js} +191 -78
- package/dist/chunk-DQKT2CUG.js.map +1 -0
- package/dist/cli.js +4 -4
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +3 -31
- package/dist/index.js +1 -5
- package/dist/mcp/server.js +2 -2
- package/dist/mcp/server.js.map +1 -1
- package/dist/openapi/server.js +2 -2
- package/dist/openapi/server.js.map +1 -1
- package/llms.txt +127 -3
- package/package.json +1 -1
- package/dist/chunk-H4ICCI3K.js.map +0 -1
|
@@ -80,7 +80,7 @@ function getPackageManagerCommands(pm) {
|
|
|
80
80
|
installPreferOffline: ["pnpm", "install", "--prefer-offline"],
|
|
81
81
|
installDev: (pkg) => ["pnpm", "add", "-D", pkg],
|
|
82
82
|
test: ["pnpm", "test"],
|
|
83
|
-
list: ["pnpm", "list", "--json", "--depth
|
|
83
|
+
list: ["pnpm", "list", "--json", "--depth", "99"],
|
|
84
84
|
lockfileName: "pnpm-lock.yaml"
|
|
85
85
|
};
|
|
86
86
|
}
|
|
@@ -90,7 +90,7 @@ function getPackageManagerCommands(pm) {
|
|
|
90
90
|
installPreferOffline: ["yarn", "install"],
|
|
91
91
|
installDev: (pkg) => ["yarn", "add", "--dev", pkg],
|
|
92
92
|
test: ["yarn", "test"],
|
|
93
|
-
list: ["yarn", "list", "--json"
|
|
93
|
+
list: ["yarn", "list", "--json"],
|
|
94
94
|
lockfileName: "yarn.lock"
|
|
95
95
|
};
|
|
96
96
|
}
|
|
@@ -99,7 +99,7 @@ function getPackageManagerCommands(pm) {
|
|
|
99
99
|
installPreferOffline: ["npm", "install", "--prefer-offline"],
|
|
100
100
|
installDev: (pkg) => ["npm", "install", "--save-dev", pkg],
|
|
101
101
|
test: ["npm", "test"],
|
|
102
|
-
list: ["npm", "list", "--json", "--
|
|
102
|
+
list: ["npm", "list", "--json", "--all"],
|
|
103
103
|
lockfileName: "package-lock.json"
|
|
104
104
|
};
|
|
105
105
|
}
|
|
@@ -134,13 +134,18 @@ function parseListOutput(pm, stdout) {
|
|
|
134
134
|
return versions;
|
|
135
135
|
}
|
|
136
136
|
const root = Array.isArray(parsed) ? parsed[0] : parsed;
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
const
|
|
140
|
-
|
|
141
|
-
|
|
137
|
+
function collectDependencies(tree) {
|
|
138
|
+
if (!tree) return;
|
|
139
|
+
for (const [name, entry] of Object.entries(tree)) {
|
|
140
|
+
if (!entry || typeof entry !== "object") continue;
|
|
141
|
+
const version = entry.version;
|
|
142
|
+
if (typeof version === "string" && version) {
|
|
143
|
+
versions.set(name, version);
|
|
144
|
+
}
|
|
145
|
+
collectDependencies(entry.dependencies);
|
|
142
146
|
}
|
|
143
147
|
}
|
|
148
|
+
collectDependencies(root?.dependencies);
|
|
144
149
|
return versions;
|
|
145
150
|
}
|
|
146
151
|
|
|
@@ -506,11 +511,18 @@ async function fetchPackageVersions(packageName) {
|
|
|
506
511
|
const data = await res.json();
|
|
507
512
|
return Object.keys(data.versions);
|
|
508
513
|
}
|
|
509
|
-
async function findSafeUpgradeVersion(packageName, installedVersion, firstPatchedVersion) {
|
|
514
|
+
async function findSafeUpgradeVersion(packageName, installedVersion, firstPatchedVersion, vulnerableRange) {
|
|
510
515
|
const versions = await fetchPackageVersions(packageName);
|
|
511
516
|
if (!versions.length) return void 0;
|
|
512
517
|
const installedMajor = semver2.major(installedVersion);
|
|
513
|
-
const candidates = versions.filter((v) => semver2.valid(v) && semver2.gte(v, firstPatchedVersion)).
|
|
518
|
+
const candidates = versions.filter((v) => semver2.valid(v) && semver2.gte(v, firstPatchedVersion)).filter((v) => {
|
|
519
|
+
if (!vulnerableRange) return true;
|
|
520
|
+
try {
|
|
521
|
+
return !semver2.satisfies(v, vulnerableRange, { includePrerelease: false });
|
|
522
|
+
} catch {
|
|
523
|
+
return true;
|
|
524
|
+
}
|
|
525
|
+
}).sort(semver2.compare);
|
|
514
526
|
if (!candidates.length) return void 0;
|
|
515
527
|
const sameMajor = candidates.find(
|
|
516
528
|
(v) => semver2.major(v) === installedMajor
|
|
@@ -527,17 +539,20 @@ var findFixedVersionTool = tool4({
|
|
|
527
539
|
installedVersion: z4.string().describe("The currently installed version (exact semver)"),
|
|
528
540
|
firstPatchedVersion: z4.string().describe(
|
|
529
541
|
"The first version that is NOT vulnerable (from lookup-cve). Use this as the floor."
|
|
530
|
-
)
|
|
542
|
+
),
|
|
543
|
+
vulnerableRange: z4.string().optional().describe("Optional vulnerable semver range used to exclude still-vulnerable versions")
|
|
531
544
|
}),
|
|
532
545
|
execute: async ({
|
|
533
546
|
packageName,
|
|
534
547
|
installedVersion,
|
|
535
|
-
firstPatchedVersion
|
|
548
|
+
firstPatchedVersion,
|
|
549
|
+
vulnerableRange
|
|
536
550
|
}) => {
|
|
537
551
|
const safeVersion = await findSafeUpgradeVersion(
|
|
538
552
|
packageName,
|
|
539
553
|
installedVersion,
|
|
540
|
-
firstPatchedVersion
|
|
554
|
+
firstPatchedVersion,
|
|
555
|
+
vulnerableRange
|
|
541
556
|
);
|
|
542
557
|
if (!safeVersion) {
|
|
543
558
|
return {
|
|
@@ -570,8 +585,7 @@ import { join as join3 } from "path";
|
|
|
570
585
|
var DEFAULT_POLICY = {
|
|
571
586
|
allowMajorBumps: false,
|
|
572
587
|
denyPackages: [],
|
|
573
|
-
allowPackages: []
|
|
574
|
-
autoApply: true
|
|
588
|
+
allowPackages: []
|
|
575
589
|
};
|
|
576
590
|
function loadPolicy(cwd, explicitPath) {
|
|
577
591
|
const candidate = explicitPath ?? join3(cwd, ".autoremediator.json");
|
|
@@ -581,8 +595,7 @@ function loadPolicy(cwd, explicitPath) {
|
|
|
581
595
|
return {
|
|
582
596
|
allowMajorBumps: parsed.allowMajorBumps ?? DEFAULT_POLICY.allowMajorBumps,
|
|
583
597
|
denyPackages: parsed.denyPackages ?? DEFAULT_POLICY.denyPackages,
|
|
584
|
-
allowPackages: parsed.allowPackages ?? DEFAULT_POLICY.allowPackages
|
|
585
|
-
autoApply: parsed.autoApply ?? DEFAULT_POLICY.autoApply
|
|
598
|
+
allowPackages: parsed.allowPackages ?? DEFAULT_POLICY.allowPackages
|
|
586
599
|
};
|
|
587
600
|
} catch {
|
|
588
601
|
return DEFAULT_POLICY;
|
|
@@ -756,7 +769,7 @@ var applyVersionBumpTool = tool5({
|
|
|
756
769
|
// src/remediation/tools/fetch-package-source.ts
|
|
757
770
|
import { tool as tool6 } from "ai";
|
|
758
771
|
import { z as z6 } from "zod";
|
|
759
|
-
import { mkdir, readdir, readFile } from "fs/promises";
|
|
772
|
+
import { mkdir, readdir, readFile, rm } from "fs/promises";
|
|
760
773
|
import { join as join5 } from "path";
|
|
761
774
|
import { execa as execa3 } from "execa";
|
|
762
775
|
var fetchPackageSourceTool = tool6({
|
|
@@ -831,7 +844,7 @@ var fetchPackageSourceTool = tool6({
|
|
|
831
844
|
}
|
|
832
845
|
return {
|
|
833
846
|
success: true,
|
|
834
|
-
sourceCode,
|
|
847
|
+
sourceFiles: sourceCode,
|
|
835
848
|
packageDir: packageRootDir
|
|
836
849
|
};
|
|
837
850
|
} catch (err) {
|
|
@@ -846,6 +859,8 @@ var fetchPackageSourceTool = tool6({
|
|
|
846
859
|
success: false,
|
|
847
860
|
error: `Failed to fetch and extract package ${packageName}@${version}: ${message}`
|
|
848
861
|
};
|
|
862
|
+
} finally {
|
|
863
|
+
await rm(tempBaseDir, { recursive: true, force: true });
|
|
849
864
|
}
|
|
850
865
|
}
|
|
851
866
|
});
|
|
@@ -883,9 +898,19 @@ var generatePatchTool = tool7({
|
|
|
883
898
|
dryRun
|
|
884
899
|
}) => {
|
|
885
900
|
try {
|
|
901
|
+
const resolvedSourceFiles = sourceFiles;
|
|
902
|
+
if (Object.keys(resolvedSourceFiles).length === 0) {
|
|
903
|
+
return {
|
|
904
|
+
success: false,
|
|
905
|
+
llmModel: "unknown",
|
|
906
|
+
confidence: 0,
|
|
907
|
+
riskLevel: "high",
|
|
908
|
+
error: "No source files were provided. Call fetch-package-source first and pass sourceFiles."
|
|
909
|
+
};
|
|
910
|
+
}
|
|
886
911
|
const model = await createModel();
|
|
887
912
|
const modelName = model.modelId || "unknown-model";
|
|
888
|
-
const sourceContext = Object.entries(
|
|
913
|
+
const sourceContext = Object.entries(resolvedSourceFiles).map(([filePath, content]) => `
|
|
889
914
|
### File: ${filePath}
|
|
890
915
|
\`\`\`typescript
|
|
891
916
|
${content}
|
|
@@ -974,12 +999,12 @@ Important:
|
|
|
974
999
|
for (const [filePath, fixedCode] of Object.entries(
|
|
975
1000
|
analysis.fixedCode
|
|
976
1001
|
)) {
|
|
977
|
-
const
|
|
978
|
-
if (!
|
|
1002
|
+
const sourceFile = resolvedSourceFiles[filePath];
|
|
1003
|
+
if (!sourceFile) {
|
|
979
1004
|
continue;
|
|
980
1005
|
}
|
|
981
1006
|
const unifiedDiff = generateUnifiedDiff(
|
|
982
|
-
|
|
1007
|
+
sourceFile,
|
|
983
1008
|
fixedCode,
|
|
984
1009
|
filePath
|
|
985
1010
|
);
|
|
@@ -1002,6 +1027,7 @@ Important:
|
|
|
1002
1027
|
return {
|
|
1003
1028
|
success: true,
|
|
1004
1029
|
patches,
|
|
1030
|
+
patchContent: patches[0]?.unifiedDiff,
|
|
1005
1031
|
llmModel: modelName,
|
|
1006
1032
|
confidence: analysis.confidence,
|
|
1007
1033
|
riskLevel: analysis.riskLevel
|
|
@@ -1050,7 +1076,7 @@ function generateUnifiedDiff(original, fixed, filePath) {
|
|
|
1050
1076
|
import { tool as tool8 } from "ai";
|
|
1051
1077
|
import { z as z8 } from "zod";
|
|
1052
1078
|
import { existsSync as existsSync3 } from "fs";
|
|
1053
|
-
import { mkdir as mkdir2, mkdtemp, readFile as readFile2, rm, writeFile } from "fs/promises";
|
|
1079
|
+
import { mkdir as mkdir2, mkdtemp, readFile as readFile2, rm as rm2, writeFile } from "fs/promises";
|
|
1054
1080
|
import { tmpdir } from "os";
|
|
1055
1081
|
import { join as join6 } from "path";
|
|
1056
1082
|
import { execa as execa4 } from "execa";
|
|
@@ -1059,40 +1085,81 @@ var applyPatchFileTool = tool8({
|
|
|
1059
1085
|
parameters: z8.object({
|
|
1060
1086
|
packageName: z8.string().min(1).describe("The npm package name"),
|
|
1061
1087
|
vulnerableVersion: z8.string().describe("The vulnerable version string"),
|
|
1062
|
-
patchContent: z8.string().min(10).describe("Unified diff patch content from generate-patch"),
|
|
1088
|
+
patchContent: z8.string().min(10).optional().describe("Unified diff patch content from generate-patch"),
|
|
1089
|
+
patches: z8.array(
|
|
1090
|
+
z8.object({
|
|
1091
|
+
filePath: z8.string().min(1),
|
|
1092
|
+
unifiedDiff: z8.string().min(10)
|
|
1093
|
+
})
|
|
1094
|
+
).optional().describe("Patch list from generate-patch; first patch is applied"),
|
|
1063
1095
|
patchesDir: z8.string().optional().default("./patches").describe("Directory to store patch files"),
|
|
1064
1096
|
cwd: z8.string().describe("Project root directory (for package.json)"),
|
|
1065
1097
|
packageManager: z8.enum(["npm", "pnpm", "yarn"]).optional().describe("Package manager used by the target project (auto-detected if omitted)"),
|
|
1066
|
-
validateWithTests: z8.boolean().optional().default(true).describe("Run package manager test command to validate patch doesn't break anything")
|
|
1098
|
+
validateWithTests: z8.boolean().optional().default(true).describe("Run package manager test command to validate patch doesn't break anything"),
|
|
1099
|
+
dryRun: z8.boolean().optional().default(false).describe("If true, report but do not mutate files")
|
|
1100
|
+
}).refine((value) => Boolean(value.patchContent || value.patches && value.patches.length > 0), {
|
|
1101
|
+
message: "Either patchContent or patches must be provided"
|
|
1067
1102
|
}),
|
|
1068
1103
|
execute: async ({
|
|
1069
1104
|
packageName,
|
|
1070
1105
|
vulnerableVersion,
|
|
1071
1106
|
patchContent,
|
|
1107
|
+
patches,
|
|
1072
1108
|
patchesDir,
|
|
1073
1109
|
cwd,
|
|
1074
1110
|
packageManager,
|
|
1075
|
-
validateWithTests
|
|
1111
|
+
validateWithTests,
|
|
1112
|
+
dryRun
|
|
1076
1113
|
}) => {
|
|
1077
1114
|
try {
|
|
1078
1115
|
const pm = packageManager ?? detectPackageManager(cwd);
|
|
1116
|
+
const selectedPatch = patchContent ?? patches?.[0]?.unifiedDiff;
|
|
1117
|
+
if (!selectedPatch) {
|
|
1118
|
+
return {
|
|
1119
|
+
success: false,
|
|
1120
|
+
packageName,
|
|
1121
|
+
vulnerableVersion,
|
|
1122
|
+
applied: false,
|
|
1123
|
+
dryRun,
|
|
1124
|
+
message: "No patch content provided.",
|
|
1125
|
+
error: "No patch content provided."
|
|
1126
|
+
};
|
|
1127
|
+
}
|
|
1128
|
+
const patchFileName = buildPatchFileName(packageName, vulnerableVersion);
|
|
1129
|
+
const patchFilePath = join6(cwd, patchesDir, patchFileName);
|
|
1130
|
+
if (dryRun) {
|
|
1131
|
+
return {
|
|
1132
|
+
success: true,
|
|
1133
|
+
packageName,
|
|
1134
|
+
vulnerableVersion,
|
|
1135
|
+
applied: false,
|
|
1136
|
+
dryRun: true,
|
|
1137
|
+
message: `[DRY RUN] Would write and configure patch at ${patchFilePath}.`,
|
|
1138
|
+
patchFilePath,
|
|
1139
|
+
patchPath: patchFilePath
|
|
1140
|
+
};
|
|
1141
|
+
}
|
|
1079
1142
|
const patchesDirPath = join6(cwd, patchesDir);
|
|
1080
1143
|
await mkdir2(patchesDirPath, { recursive: true });
|
|
1081
|
-
|
|
1082
|
-
const patchFilePath = join6(patchesDirPath, patchFileName);
|
|
1083
|
-
await writeFile(patchFilePath, patchContent, "utf8");
|
|
1144
|
+
await writeFile(patchFilePath, selectedPatch, "utf8");
|
|
1084
1145
|
let validationResult;
|
|
1085
1146
|
const patchMode = await resolvePatchMode(pm, cwd);
|
|
1086
|
-
const applyResult = patchMode === "patch-package" ? await configurePatchPackagePostinstall(cwd) : await applyNativePatch({
|
|
1147
|
+
const applyResult = patchMode === "patch-package" ? await configurePatchPackagePostinstall(cwd, pm) : await applyNativePatch({
|
|
1087
1148
|
cwd,
|
|
1088
1149
|
packageName,
|
|
1089
1150
|
vulnerableVersion,
|
|
1090
|
-
patchContent,
|
|
1151
|
+
patchContent: selectedPatch,
|
|
1091
1152
|
patchMode
|
|
1092
1153
|
});
|
|
1093
1154
|
if (!applyResult.success) {
|
|
1094
1155
|
return {
|
|
1095
1156
|
success: false,
|
|
1157
|
+
packageName,
|
|
1158
|
+
vulnerableVersion,
|
|
1159
|
+
applied: false,
|
|
1160
|
+
dryRun: false,
|
|
1161
|
+
message: applyResult.error,
|
|
1162
|
+
patchFilePath,
|
|
1096
1163
|
patchPath: patchFilePath,
|
|
1097
1164
|
patchMode,
|
|
1098
1165
|
postinstallConfigured: patchMode === "patch-package" ? false : void 0,
|
|
@@ -1101,9 +1168,32 @@ var applyPatchFileTool = tool8({
|
|
|
1101
1168
|
}
|
|
1102
1169
|
if (validateWithTests) {
|
|
1103
1170
|
validationResult = await validatePatchWithTests(cwd, pm);
|
|
1171
|
+
if (!validationResult.passed) {
|
|
1172
|
+
const validationError = "Patch validation failed after apply; patch marked unresolved.";
|
|
1173
|
+
return {
|
|
1174
|
+
success: false,
|
|
1175
|
+
packageName,
|
|
1176
|
+
vulnerableVersion,
|
|
1177
|
+
applied: false,
|
|
1178
|
+
dryRun: false,
|
|
1179
|
+
message: validationError,
|
|
1180
|
+
patchFilePath,
|
|
1181
|
+
patchPath: patchFilePath,
|
|
1182
|
+
patchMode,
|
|
1183
|
+
postinstallConfigured: patchMode === "patch-package",
|
|
1184
|
+
validation: validationResult,
|
|
1185
|
+
error: validationError
|
|
1186
|
+
};
|
|
1187
|
+
}
|
|
1104
1188
|
}
|
|
1105
1189
|
return {
|
|
1106
1190
|
success: true,
|
|
1191
|
+
packageName,
|
|
1192
|
+
vulnerableVersion,
|
|
1193
|
+
applied: true,
|
|
1194
|
+
dryRun: false,
|
|
1195
|
+
message: `Patch applied successfully for ${packageName}@${vulnerableVersion}.`,
|
|
1196
|
+
patchFilePath,
|
|
1107
1197
|
patchPath: patchFilePath,
|
|
1108
1198
|
patchMode,
|
|
1109
1199
|
postinstallConfigured: patchMode === "patch-package",
|
|
@@ -1113,6 +1203,11 @@ var applyPatchFileTool = tool8({
|
|
|
1113
1203
|
const message = err instanceof Error ? err.message : String(err);
|
|
1114
1204
|
return {
|
|
1115
1205
|
success: false,
|
|
1206
|
+
packageName,
|
|
1207
|
+
vulnerableVersion,
|
|
1208
|
+
applied: false,
|
|
1209
|
+
dryRun,
|
|
1210
|
+
message: `Failed to apply patch file: ${message}`,
|
|
1116
1211
|
error: `Failed to apply patch file: ${message}`
|
|
1117
1212
|
};
|
|
1118
1213
|
}
|
|
@@ -1133,7 +1228,11 @@ async function resolvePatchMode(packageManager, cwd) {
|
|
|
1133
1228
|
return "patch-package";
|
|
1134
1229
|
}
|
|
1135
1230
|
}
|
|
1136
|
-
|
|
1231
|
+
function buildPatchFileName(packageName, vulnerableVersion) {
|
|
1232
|
+
const safeName = packageName.replace(/^@/, "").replace(/\//g, "+");
|
|
1233
|
+
return `${safeName}+${vulnerableVersion}.patch`;
|
|
1234
|
+
}
|
|
1235
|
+
async function configurePatchPackagePostinstall(cwd, packageManager) {
|
|
1137
1236
|
const pkgJsonPath = join6(cwd, "package.json");
|
|
1138
1237
|
let pkgJson;
|
|
1139
1238
|
try {
|
|
@@ -1144,6 +1243,22 @@ async function configurePatchPackagePostinstall(cwd) {
|
|
|
1144
1243
|
error: `Could not read package.json at ${pkgJsonPath}`
|
|
1145
1244
|
};
|
|
1146
1245
|
}
|
|
1246
|
+
const devDependencies = pkgJson.devDependencies ?? {};
|
|
1247
|
+
if (!devDependencies["patch-package"]) {
|
|
1248
|
+
try {
|
|
1249
|
+
const commands = getPackageManagerCommands(packageManager);
|
|
1250
|
+
const [cmd, ...args] = commands.installDev("patch-package");
|
|
1251
|
+
await execa4(cmd, args, {
|
|
1252
|
+
cwd,
|
|
1253
|
+
stdio: "pipe"
|
|
1254
|
+
});
|
|
1255
|
+
} catch (err) {
|
|
1256
|
+
return {
|
|
1257
|
+
success: false,
|
|
1258
|
+
error: `Failed to install patch-package: ${err instanceof Error ? err.message : String(err)}`
|
|
1259
|
+
};
|
|
1260
|
+
}
|
|
1261
|
+
}
|
|
1147
1262
|
if (!pkgJson.scripts) {
|
|
1148
1263
|
pkgJson.scripts = {};
|
|
1149
1264
|
}
|
|
@@ -1160,10 +1275,11 @@ async function configurePatchPackagePostinstall(cwd) {
|
|
|
1160
1275
|
async function applyNativePatch(params) {
|
|
1161
1276
|
const { cwd, packageName, vulnerableVersion, patchContent, patchMode } = params;
|
|
1162
1277
|
const packageSpec = `${packageName}@${vulnerableVersion}`;
|
|
1163
|
-
const
|
|
1278
|
+
const createCommand = patchMode === "native-pnpm" ? "pnpm" : "yarn";
|
|
1279
|
+
const createArgs = ["patch", packageSpec];
|
|
1164
1280
|
let patchDir;
|
|
1165
1281
|
try {
|
|
1166
|
-
const createResult = await execa4(
|
|
1282
|
+
const createResult = await execa4(createCommand, createArgs, {
|
|
1167
1283
|
cwd,
|
|
1168
1284
|
stdio: "pipe"
|
|
1169
1285
|
});
|
|
@@ -1189,8 +1305,9 @@ ${createResult.stderr}`);
|
|
|
1189
1305
|
cwd: patchDir,
|
|
1190
1306
|
stdio: "pipe"
|
|
1191
1307
|
});
|
|
1192
|
-
const
|
|
1193
|
-
|
|
1308
|
+
const commitCommand = patchMode === "native-pnpm" ? "pnpm" : "yarn";
|
|
1309
|
+
const commitArgs = patchMode === "native-pnpm" ? ["patch-commit", patchDir] : ["patch-commit", "-s", patchDir];
|
|
1310
|
+
await execa4(commitCommand, commitArgs, {
|
|
1194
1311
|
cwd,
|
|
1195
1312
|
stdio: "pipe"
|
|
1196
1313
|
});
|
|
@@ -1200,7 +1317,7 @@ ${createResult.stderr}`);
|
|
|
1200
1317
|
error: `Failed to apply native patch for ${packageSpec}: ${err instanceof Error ? err.message : String(err)}`
|
|
1201
1318
|
};
|
|
1202
1319
|
} finally {
|
|
1203
|
-
await
|
|
1320
|
+
await rm2(tempPatchDir, { recursive: true, force: true });
|
|
1204
1321
|
}
|
|
1205
1322
|
return { success: true };
|
|
1206
1323
|
}
|
|
@@ -1265,10 +1382,10 @@ function extractFailedTests(output) {
|
|
|
1265
1382
|
}
|
|
1266
1383
|
|
|
1267
1384
|
// src/remediation/pipeline.ts
|
|
1268
|
-
async function
|
|
1385
|
+
async function runRemediationPipeline(cveId, options = {}) {
|
|
1269
1386
|
const provider = resolveProvider(options);
|
|
1270
1387
|
if (provider === "local") {
|
|
1271
|
-
return
|
|
1388
|
+
return runLocalRemediationPipeline(cveId, options);
|
|
1272
1389
|
}
|
|
1273
1390
|
const cwd = options.cwd ?? process.cwd();
|
|
1274
1391
|
const packageManager = options.packageManager ?? detectPackageManager(cwd);
|
|
@@ -1291,7 +1408,6 @@ async function runHealAgent(cveId, options = {}) {
|
|
|
1291
1408
|
const vulnerablePackages = [];
|
|
1292
1409
|
let cveDetails = null;
|
|
1293
1410
|
let agentSteps = 0;
|
|
1294
|
-
let lastGeneratedPatches = null;
|
|
1295
1411
|
const result = await generateText2({
|
|
1296
1412
|
model,
|
|
1297
1413
|
system: systemPrompt,
|
|
@@ -1321,35 +1437,22 @@ async function runHealAgent(cveId, options = {}) {
|
|
|
1321
1437
|
if (tr.toolName === "apply-version-bump") {
|
|
1322
1438
|
collectedResults.push(toolResult);
|
|
1323
1439
|
}
|
|
1324
|
-
if (tr.toolName === "fetch-package-source" && toolResult?.success) {
|
|
1325
|
-
const sourceData = {
|
|
1326
|
-
success: toolResult.success,
|
|
1327
|
-
sourceFiles: toolResult.sourceFiles,
|
|
1328
|
-
packageDir: toolResult.packageDir
|
|
1329
|
-
};
|
|
1330
|
-
}
|
|
1331
|
-
if (tr.toolName === "generate-patch" && toolResult?.success) {
|
|
1332
|
-
const patchData = {
|
|
1333
|
-
success: toolResult.success,
|
|
1334
|
-
patches: toolResult.patches,
|
|
1335
|
-
confidence: toolResult.confidence,
|
|
1336
|
-
riskLevel: toolResult.riskLevel
|
|
1337
|
-
};
|
|
1338
|
-
lastGeneratedPatches = patchData.patches || null;
|
|
1339
|
-
}
|
|
1340
1440
|
if (tr.toolName === "apply-patch-file" && toolResult) {
|
|
1341
|
-
const
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1441
|
+
const validation = toolResult.validation;
|
|
1442
|
+
const message = typeof toolResult.message === "string" ? toolResult.message : typeof toolResult.error === "string" ? toolResult.error : "Patch-file strategy finished.";
|
|
1443
|
+
collectedResults.push({
|
|
1444
|
+
packageName: typeof toolResult.packageName === "string" ? toolResult.packageName : "unknown-package",
|
|
1445
|
+
strategy: "patch-file",
|
|
1446
|
+
fromVersion: typeof toolResult.vulnerableVersion === "string" ? toolResult.vulnerableVersion : "unknown",
|
|
1447
|
+
patchFilePath: typeof toolResult.patchFilePath === "string" ? toolResult.patchFilePath : typeof toolResult.patchPath === "string" ? toolResult.patchPath : void 0,
|
|
1448
|
+
applied: Boolean(toolResult.applied),
|
|
1449
|
+
dryRun: Boolean(toolResult.dryRun),
|
|
1450
|
+
message,
|
|
1451
|
+
validation: validation && typeof validation.passed === "boolean" ? {
|
|
1452
|
+
passed: validation.passed,
|
|
1453
|
+
error: typeof validation.error === "string" ? validation.error : void 0
|
|
1454
|
+
} : void 0
|
|
1455
|
+
});
|
|
1353
1456
|
}
|
|
1354
1457
|
}
|
|
1355
1458
|
}
|
|
@@ -1363,7 +1466,7 @@ async function runHealAgent(cveId, options = {}) {
|
|
|
1363
1466
|
summary: result.text
|
|
1364
1467
|
};
|
|
1365
1468
|
}
|
|
1366
|
-
async function
|
|
1469
|
+
async function runLocalRemediationPipeline(cveId, options = {}) {
|
|
1367
1470
|
const cwd = options.cwd ?? process.cwd();
|
|
1368
1471
|
const packageManager = options.packageManager ?? detectPackageManager(cwd);
|
|
1369
1472
|
const dryRun = options.dryRun ?? false;
|
|
@@ -1447,6 +1550,17 @@ async function runLocalHealPipeline(cveId, options = {}) {
|
|
|
1447
1550
|
for (const vulnerable of vulnerablePackages) {
|
|
1448
1551
|
const pkg = vulnerable.installed;
|
|
1449
1552
|
const firstPatchedVersion = vulnerable.affected.firstPatchedVersion;
|
|
1553
|
+
if (pkg.type === "indirect") {
|
|
1554
|
+
collectedResults.push({
|
|
1555
|
+
packageName: pkg.name,
|
|
1556
|
+
strategy: "none",
|
|
1557
|
+
fromVersion: pkg.version,
|
|
1558
|
+
applied: false,
|
|
1559
|
+
dryRun,
|
|
1560
|
+
message: `"${pkg.name}" is an indirect dependency; automatic version bump is limited to direct dependencies in local mode.`
|
|
1561
|
+
});
|
|
1562
|
+
continue;
|
|
1563
|
+
}
|
|
1450
1564
|
if (!firstPatchedVersion) {
|
|
1451
1565
|
collectedResults.push({
|
|
1452
1566
|
packageName: pkg.name,
|
|
@@ -1461,7 +1575,8 @@ async function runLocalHealPipeline(cveId, options = {}) {
|
|
|
1461
1575
|
const safeVersion = await findSafeUpgradeVersion(
|
|
1462
1576
|
pkg.name,
|
|
1463
1577
|
pkg.version,
|
|
1464
|
-
firstPatchedVersion
|
|
1578
|
+
firstPatchedVersion,
|
|
1579
|
+
vulnerable.affected.vulnerableRange
|
|
1465
1580
|
);
|
|
1466
1581
|
agentSteps += 1;
|
|
1467
1582
|
if (!safeVersion) {
|
|
@@ -1727,14 +1842,13 @@ function writeEvidenceLog(cwd, log) {
|
|
|
1727
1842
|
}
|
|
1728
1843
|
|
|
1729
1844
|
// src/api.ts
|
|
1730
|
-
var runRemediationPipeline = runHealAgent;
|
|
1731
1845
|
async function remediate(cveId, options = {}) {
|
|
1732
1846
|
if (!/^CVE-\d{4}-\d+$/i.test(cveId)) {
|
|
1733
1847
|
throw new Error(
|
|
1734
1848
|
`Invalid CVE ID: "${cveId}". Expected format: CVE-YYYY-NNNNN (e.g. CVE-2021-23337).`
|
|
1735
1849
|
);
|
|
1736
1850
|
}
|
|
1737
|
-
return
|
|
1851
|
+
return runRemediationPipeline(cveId.toUpperCase(), options);
|
|
1738
1852
|
}
|
|
1739
1853
|
async function remediateFromScan(inputPath, options = {}) {
|
|
1740
1854
|
const cwd = options.cwd ?? process.cwd();
|
|
@@ -1751,7 +1865,7 @@ async function remediateFromScan(inputPath, options = {}) {
|
|
|
1751
1865
|
let patchFileCount = 0;
|
|
1752
1866
|
for (const cveId of cveIds) {
|
|
1753
1867
|
try {
|
|
1754
|
-
addEvidenceStep(evidence, "
|
|
1868
|
+
addEvidenceStep(evidence, "remediate.start", { cveId });
|
|
1755
1869
|
const report = await remediate(cveId, {
|
|
1756
1870
|
...options,
|
|
1757
1871
|
patchesDir
|
|
@@ -1770,11 +1884,11 @@ async function remediateFromScan(inputPath, options = {}) {
|
|
|
1770
1884
|
}
|
|
1771
1885
|
}
|
|
1772
1886
|
reports.push(report);
|
|
1773
|
-
addEvidenceStep(evidence, "
|
|
1887
|
+
addEvidenceStep(evidence, "remediate.finish", { cveId }, { results: report.results.length });
|
|
1774
1888
|
} catch (error) {
|
|
1775
1889
|
const message = error instanceof Error ? error.message : String(error);
|
|
1776
1890
|
errors.push({ cveId, message });
|
|
1777
|
-
addEvidenceStep(evidence, "
|
|
1891
|
+
addEvidenceStep(evidence, "remediate.error", { cveId }, void 0, message);
|
|
1778
1892
|
}
|
|
1779
1893
|
}
|
|
1780
1894
|
let successCount = 0;
|
|
@@ -1837,11 +1951,10 @@ function ciExitCode(summary) {
|
|
|
1837
1951
|
}
|
|
1838
1952
|
|
|
1839
1953
|
export {
|
|
1840
|
-
runHealAgent,
|
|
1841
1954
|
runRemediationPipeline,
|
|
1842
1955
|
remediate,
|
|
1843
1956
|
remediateFromScan,
|
|
1844
1957
|
toCiSummary,
|
|
1845
1958
|
ciExitCode
|
|
1846
1959
|
};
|
|
1847
|
-
//# sourceMappingURL=chunk-
|
|
1960
|
+
//# sourceMappingURL=chunk-DQKT2CUG.js.map
|