autoremediator 0.13.0 → 0.14.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/dist/{chunk-4CVVRAQM.js → chunk-IXCNMTOO.js} +456 -133
- package/dist/cli.js +3 -4
- package/dist/index.d.ts +5 -4
- package/dist/index.js +454 -130
- package/dist/index.js.map +1 -1
- package/dist/mcp/server.js +3 -4
- package/dist/openapi/server.js +4 -5
- package/llms.txt +1 -1
- package/package.json +1 -1
- package/dist/chunk-4CVVRAQM.js.map +0 -1
- package/dist/cli.js.map +0 -1
- package/dist/mcp/server.js.map +0 -1
- package/dist/openapi/server.js.map +0 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// src/api/options-schema.ts
|
|
2
|
-
var PACKAGE_MANAGER_VALUES = ["npm", "pnpm", "yarn"];
|
|
2
|
+
var PACKAGE_MANAGER_VALUES = ["npm", "pnpm", "yarn", "bun", "deno"];
|
|
3
3
|
var LLM_PROVIDER_VALUES = ["remote", "local"];
|
|
4
4
|
var PROVENANCE_SOURCE_VALUES = ["cli", "sdk", "mcp", "openapi", "unknown"];
|
|
5
5
|
var OPTION_DESCRIPTIONS = {
|
|
@@ -296,21 +296,26 @@ function createUpdateOutdatedOptionSchemaProperties() {
|
|
|
296
296
|
// src/api/patches/inspection.ts
|
|
297
297
|
import { existsSync as existsSync4 } from "fs";
|
|
298
298
|
import { readdir, readFile as readFile2, stat } from "fs/promises";
|
|
299
|
-
import { join as
|
|
299
|
+
import { join as join4 } from "path";
|
|
300
300
|
|
|
301
301
|
// src/remediation/strategies/patch-utils.ts
|
|
302
|
-
import { existsSync as existsSync2, mkdirSync, writeFileSync, readFileSync } from "fs";
|
|
303
|
-
import { join as
|
|
302
|
+
import { existsSync as existsSync2, mkdirSync, writeFileSync, readFileSync as readFileSync2 } from "fs";
|
|
303
|
+
import { join as join3 } from "path";
|
|
304
304
|
import { execa as execa2 } from "execa";
|
|
305
305
|
|
|
306
306
|
// src/platform/package-manager/index.ts
|
|
307
307
|
import { existsSync } from "fs";
|
|
308
|
-
import { join } from "path";
|
|
308
|
+
import { join as join2 } from "path";
|
|
309
309
|
import { execa } from "execa";
|
|
310
310
|
|
|
311
311
|
// src/platform/package-manager/list-parser.ts
|
|
312
|
+
import { readFileSync } from "fs";
|
|
313
|
+
import { join } from "path";
|
|
312
314
|
function parsePackageManagerListOutput(pm, stdout) {
|
|
313
315
|
const versions = /* @__PURE__ */ new Map();
|
|
316
|
+
if (pm === "bun") {
|
|
317
|
+
return parseBunListOutput(stdout, versions);
|
|
318
|
+
}
|
|
314
319
|
if (!stdout.trim()) return versions;
|
|
315
320
|
if (pm === "yarn") {
|
|
316
321
|
return parseYarnListOutput(stdout, versions);
|
|
@@ -360,6 +365,61 @@ function collectDependencyTree(tree, versions) {
|
|
|
360
365
|
collectDependencyTree(entry.dependencies, versions);
|
|
361
366
|
}
|
|
362
367
|
}
|
|
368
|
+
function parseBunListOutput(stdout, versions) {
|
|
369
|
+
const lines = stdout.split("\n").map((line) => line.replace(/^[\s│├└─]+/, "").trim()).filter(Boolean);
|
|
370
|
+
for (const line of lines) {
|
|
371
|
+
const at = line.lastIndexOf("@");
|
|
372
|
+
if (at <= 0) continue;
|
|
373
|
+
const name = line.slice(0, at);
|
|
374
|
+
const version = line.slice(at + 1);
|
|
375
|
+
if (name && version) {
|
|
376
|
+
versions.set(name, version);
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
return versions;
|
|
380
|
+
}
|
|
381
|
+
function resolveDenoInventory(cwd) {
|
|
382
|
+
const versions = /* @__PURE__ */ new Map();
|
|
383
|
+
let raw;
|
|
384
|
+
try {
|
|
385
|
+
raw = readFileSync(join(cwd, "deno.lock"), "utf8");
|
|
386
|
+
} catch {
|
|
387
|
+
return versions;
|
|
388
|
+
}
|
|
389
|
+
let lock;
|
|
390
|
+
try {
|
|
391
|
+
lock = JSON.parse(raw);
|
|
392
|
+
} catch {
|
|
393
|
+
return versions;
|
|
394
|
+
}
|
|
395
|
+
if (!lock || typeof lock !== "object") return versions;
|
|
396
|
+
const lockObj = lock;
|
|
397
|
+
const packages = lockObj["packages"];
|
|
398
|
+
if (packages && typeof packages === "object") {
|
|
399
|
+
const pkgsObj = packages;
|
|
400
|
+
const npmMap = pkgsObj["npm"];
|
|
401
|
+
if (npmMap && typeof npmMap === "object") {
|
|
402
|
+
for (const key of Object.keys(npmMap)) {
|
|
403
|
+
extractNameVersion(key, versions);
|
|
404
|
+
}
|
|
405
|
+
return versions;
|
|
406
|
+
}
|
|
407
|
+
for (const key of Object.keys(pkgsObj)) {
|
|
408
|
+
const stripped = key.startsWith("npm:") ? key.slice(4) : key;
|
|
409
|
+
extractNameVersion(stripped, versions);
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
return versions;
|
|
413
|
+
}
|
|
414
|
+
function extractNameVersion(spec, versions) {
|
|
415
|
+
const at = spec.lastIndexOf("@");
|
|
416
|
+
if (at <= 0) return;
|
|
417
|
+
const name = spec.slice(0, at);
|
|
418
|
+
const version = spec.slice(at + 1);
|
|
419
|
+
if (name && version) {
|
|
420
|
+
versions.set(name, version);
|
|
421
|
+
}
|
|
422
|
+
}
|
|
363
423
|
|
|
364
424
|
// src/platform/package-manager/index.ts
|
|
365
425
|
async function getYarnMajorVersion(cwd) {
|
|
@@ -372,8 +432,10 @@ async function getYarnMajorVersion(cwd) {
|
|
|
372
432
|
}
|
|
373
433
|
}
|
|
374
434
|
function detectPackageManager(cwd) {
|
|
375
|
-
if (existsSync(
|
|
376
|
-
if (existsSync(
|
|
435
|
+
if (existsSync(join2(cwd, "pnpm-lock.yaml"))) return "pnpm";
|
|
436
|
+
if (existsSync(join2(cwd, "yarn.lock"))) return "yarn";
|
|
437
|
+
if (existsSync(join2(cwd, "bun.lockb")) || existsSync(join2(cwd, "bun.lock"))) return "bun";
|
|
438
|
+
if (existsSync(join2(cwd, "deno.lock"))) return "deno";
|
|
377
439
|
return "npm";
|
|
378
440
|
}
|
|
379
441
|
function withWorkspace(command, pm, workspace) {
|
|
@@ -390,6 +452,20 @@ function resolveInstallCommand(pm, constraints, yarnMajor) {
|
|
|
390
452
|
const installMode = constraints?.installMode ?? "deterministic";
|
|
391
453
|
const preferOfflineOverride = constraints?.installPreferOffline;
|
|
392
454
|
const frozenOverride = constraints?.enforceFrozenLockfile;
|
|
455
|
+
if (pm === "bun") {
|
|
456
|
+
const frozen = frozenOverride ?? installMode === "deterministic";
|
|
457
|
+
const command2 = ["bun", "install"];
|
|
458
|
+
if (frozen) command2.push("--frozen-lockfile");
|
|
459
|
+
return withWorkspace(command2, pm, constraints?.workspace);
|
|
460
|
+
}
|
|
461
|
+
if (pm === "deno") {
|
|
462
|
+
const frozen = frozenOverride ?? installMode === "deterministic";
|
|
463
|
+
const preferOffline = preferOfflineOverride ?? installMode === "prefer-offline";
|
|
464
|
+
const command2 = ["deno", "install"];
|
|
465
|
+
if (frozen) command2.push("--frozen");
|
|
466
|
+
if (preferOffline && !frozen) command2.push("--cache-only");
|
|
467
|
+
return withWorkspace(command2, pm, constraints?.workspace);
|
|
468
|
+
}
|
|
393
469
|
const includePreferOffline = pm !== "yarn" && (preferOfflineOverride ?? installMode !== "standard");
|
|
394
470
|
let includeFrozenLockfile = pm !== "npm" && (frozenOverride ?? installMode === "deterministic");
|
|
395
471
|
if (frozenOverride === false) {
|
|
@@ -410,22 +486,36 @@ function resolveInstallCommand(pm, constraints, yarnMajor) {
|
|
|
410
486
|
return withWorkspace(command, pm, constraints?.workspace);
|
|
411
487
|
}
|
|
412
488
|
function resolveListCommand(pm, constraints) {
|
|
413
|
-
|
|
489
|
+
if (pm === "deno") {
|
|
490
|
+
return [];
|
|
491
|
+
}
|
|
492
|
+
const base = pm === "pnpm" ? ["pnpm", "list", "--json", "--depth", "99"] : pm === "yarn" ? ["yarn", "list", "--json"] : pm === "bun" ? ["bun", "pm", "ls", "--all"] : ["npm", "list", "--json", "--all"];
|
|
414
493
|
return withWorkspace(base, pm, constraints?.workspace);
|
|
415
494
|
}
|
|
416
495
|
function resolveTestCommand(pm, constraints) {
|
|
417
|
-
const base = pm === "pnpm" ? ["pnpm", "test"] : pm === "yarn" ? ["yarn", "test"] : ["npm", "test"];
|
|
496
|
+
const base = pm === "pnpm" ? ["pnpm", "test"] : pm === "yarn" ? ["yarn", "test"] : pm === "bun" ? ["bun", "test"] : pm === "deno" ? ["deno", "test"] : ["npm", "test"];
|
|
418
497
|
return withWorkspace(base, pm, constraints?.workspace);
|
|
419
498
|
}
|
|
420
499
|
function resolveAuditCommand(pm, constraints) {
|
|
500
|
+
if (pm === "deno") {
|
|
501
|
+
throw new Error(
|
|
502
|
+
"Deno does not support a native audit command. Use --input with a SARIF or npm-audit scan file instead."
|
|
503
|
+
);
|
|
504
|
+
}
|
|
421
505
|
const base = pm === "yarn" ? ["yarn", "audit", "--json"] : [pm, "audit", "--json"];
|
|
422
506
|
return withWorkspace(base, pm, constraints?.workspace);
|
|
423
507
|
}
|
|
424
508
|
function resolveWhyCommand(pm, packageName, constraints) {
|
|
509
|
+
if (pm === "deno") return [];
|
|
510
|
+
if (pm === "bun") {
|
|
511
|
+
const base2 = ["bun", "pm", "why", packageName];
|
|
512
|
+
return withWorkspace(base2, pm, constraints?.workspace);
|
|
513
|
+
}
|
|
425
514
|
const base = pm === "npm" ? ["npm", "explain", packageName] : [pm, "why", packageName];
|
|
426
515
|
return withWorkspace(base, pm, constraints?.workspace);
|
|
427
516
|
}
|
|
428
517
|
function resolveDedupeCommand(pm, constraints) {
|
|
518
|
+
if (pm === "bun" || pm === "deno") return [];
|
|
429
519
|
const base = [pm, "dedupe"];
|
|
430
520
|
return withWorkspace(base, pm, constraints?.workspace);
|
|
431
521
|
}
|
|
@@ -452,6 +542,28 @@ function getPackageManagerCommands(pm) {
|
|
|
452
542
|
lockfileName: "yarn.lock"
|
|
453
543
|
};
|
|
454
544
|
}
|
|
545
|
+
if (pm === "bun") {
|
|
546
|
+
return {
|
|
547
|
+
install: ["bun", "install"],
|
|
548
|
+
installPreferOffline: ["bun", "install"],
|
|
549
|
+
installDeterministic: resolveInstallCommand("bun", { installMode: "deterministic" }),
|
|
550
|
+
installDev: (pkg) => ["bun", "add", "-d", pkg],
|
|
551
|
+
test: ["bun", "test"],
|
|
552
|
+
list: ["bun", "pm", "ls", "--all"],
|
|
553
|
+
lockfileName: "bun.lockb"
|
|
554
|
+
};
|
|
555
|
+
}
|
|
556
|
+
if (pm === "deno") {
|
|
557
|
+
return {
|
|
558
|
+
install: ["deno", "install"],
|
|
559
|
+
installPreferOffline: ["deno", "install", "--cache-only"],
|
|
560
|
+
installDeterministic: resolveInstallCommand("deno", { installMode: "deterministic" }),
|
|
561
|
+
installDev: (pkg) => ["deno", "add", pkg],
|
|
562
|
+
test: ["deno", "test"],
|
|
563
|
+
list: [],
|
|
564
|
+
lockfileName: "deno.lock"
|
|
565
|
+
};
|
|
566
|
+
}
|
|
455
567
|
return {
|
|
456
568
|
install: ["npm", "install"],
|
|
457
569
|
installPreferOffline: ["npm", "install", "--prefer-offline"],
|
|
@@ -501,13 +613,21 @@ function validatePatchDiff(patchContent) {
|
|
|
501
613
|
// src/api/patches/helpers.ts
|
|
502
614
|
import { existsSync as existsSync3 } from "fs";
|
|
503
615
|
import { readFile } from "fs/promises";
|
|
504
|
-
import { isAbsolute, resolve } from "path";
|
|
616
|
+
import { isAbsolute, resolve, sep } from "path";
|
|
505
617
|
var DEFAULT_PATCHES_DIR = "./patches";
|
|
506
618
|
function resolvePatchesDir(cwd, patchesDir = DEFAULT_PATCHES_DIR) {
|
|
507
619
|
return isAbsolute(patchesDir) ? patchesDir : resolve(cwd, patchesDir);
|
|
508
620
|
}
|
|
509
621
|
function resolveArtifactPath(cwd, patchFilePath) {
|
|
510
|
-
|
|
622
|
+
const resolved = isAbsolute(patchFilePath) ? patchFilePath : resolve(cwd, patchFilePath);
|
|
623
|
+
if (!resolved.endsWith(".patch")) {
|
|
624
|
+
throw new Error(`patchFilePath must point to a .patch file: ${patchFilePath}`);
|
|
625
|
+
}
|
|
626
|
+
const patchesRoot = resolvePatchesDir(cwd);
|
|
627
|
+
if (!resolved.startsWith(patchesRoot + sep)) {
|
|
628
|
+
throw new Error(`patchFilePath must be inside the patches directory: ${patchFilePath}`);
|
|
629
|
+
}
|
|
630
|
+
return resolved;
|
|
511
631
|
}
|
|
512
632
|
async function readManifest(manifestFilePath) {
|
|
513
633
|
if (!existsSync3(manifestFilePath)) {
|
|
@@ -515,7 +635,11 @@ async function readManifest(manifestFilePath) {
|
|
|
515
635
|
}
|
|
516
636
|
try {
|
|
517
637
|
const raw = await readFile(manifestFilePath, "utf8");
|
|
518
|
-
|
|
638
|
+
const parsed = JSON.parse(raw);
|
|
639
|
+
if (parsed === null || typeof parsed !== "object" || parsed.schemaVersion !== "1.0" || typeof parsed.packageName !== "string" || typeof parsed.vulnerableVersion !== "string" || typeof parsed.patchFilePath !== "string" || typeof parsed.patchFileName !== "string" || typeof parsed.applied !== "boolean" || typeof parsed.dryRun !== "boolean" || typeof parsed.generatedAt !== "string") {
|
|
640
|
+
return void 0;
|
|
641
|
+
}
|
|
642
|
+
return parsed;
|
|
519
643
|
} catch {
|
|
520
644
|
return void 0;
|
|
521
645
|
}
|
|
@@ -555,7 +679,7 @@ async function listPatchArtifacts(options = {}) {
|
|
|
555
679
|
return [];
|
|
556
680
|
}
|
|
557
681
|
const entries = await readdir(patchesDirPath, { withFileTypes: true });
|
|
558
|
-
const patchFiles = entries.filter((entry) => entry.isFile() && entry.name.endsWith(".patch")).map((entry) =>
|
|
682
|
+
const patchFiles = entries.filter((entry) => entry.isFile() && entry.name.endsWith(".patch")).map((entry) => join4(patchesDirPath, entry.name)).sort((left, right) => left.localeCompare(right));
|
|
559
683
|
const summaries = await Promise.all(
|
|
560
684
|
patchFiles.map(async (patchFilePath) => {
|
|
561
685
|
const inspection = await inspectPatchArtifact(patchFilePath, options);
|
|
@@ -616,13 +740,13 @@ function defineTool(config) {
|
|
|
616
740
|
|
|
617
741
|
// src/remediation/tools/check-inventory.ts
|
|
618
742
|
import { z } from "zod";
|
|
619
|
-
import { readFileSync as
|
|
620
|
-
import { join as
|
|
743
|
+
import { readFileSync as readFileSync4 } from "fs";
|
|
744
|
+
import { join as join6 } from "path";
|
|
621
745
|
import { execa as execa3 } from "execa";
|
|
622
746
|
|
|
623
747
|
// src/platform/policy.ts
|
|
624
|
-
import { existsSync as existsSync5, readFileSync as
|
|
625
|
-
import { join as
|
|
748
|
+
import { existsSync as existsSync5, readFileSync as readFileSync3 } from "fs";
|
|
749
|
+
import { join as join5 } from "path";
|
|
626
750
|
import { parse as yamlParse } from "yaml";
|
|
627
751
|
var DEFAULT_POLICY = {
|
|
628
752
|
allowMajorBumps: false,
|
|
@@ -653,10 +777,10 @@ var DEFAULT_POLICY = {
|
|
|
653
777
|
escalationGraph: void 0
|
|
654
778
|
};
|
|
655
779
|
function loadPolicy(cwd, explicitPath) {
|
|
656
|
-
const candidate = explicitPath ??
|
|
780
|
+
const candidate = explicitPath ?? join5(cwd, ".github", "autoremediator.yml");
|
|
657
781
|
if (!existsSync5(candidate)) return DEFAULT_POLICY;
|
|
658
782
|
try {
|
|
659
|
-
const parsed = yamlParse(
|
|
783
|
+
const parsed = yamlParse(readFileSync3(candidate, "utf8"));
|
|
660
784
|
return {
|
|
661
785
|
allowMajorBumps: parsed.allowMajorBumps ?? DEFAULT_POLICY.allowMajorBumps,
|
|
662
786
|
denyPackages: parsed.denyPackages ?? DEFAULT_POLICY.denyPackages,
|
|
@@ -717,7 +841,7 @@ function isActiveSuppression(suppression) {
|
|
|
717
841
|
}
|
|
718
842
|
function loadSuppressionsFile(filePath) {
|
|
719
843
|
try {
|
|
720
|
-
const content =
|
|
844
|
+
const content = readFileSync3(filePath, "utf8");
|
|
721
845
|
const parsed = yamlParse(content);
|
|
722
846
|
return Array.isArray(parsed?.suppressions) ? parsed.suppressions : [];
|
|
723
847
|
} catch {
|
|
@@ -748,14 +872,14 @@ var checkInventoryTool = defineTool({
|
|
|
748
872
|
description: "Read the project's package.json and installed dependencies to list packages and exact versions. Must be called before checking version matches.",
|
|
749
873
|
parameters: z.object({
|
|
750
874
|
cwd: z.string().describe("Absolute path to the consumer project's root directory"),
|
|
751
|
-
packageManager: z.enum(["npm", "pnpm", "yarn"]).optional().describe("Package manager used by the target project (auto-detected if omitted)"),
|
|
875
|
+
packageManager: z.enum(["npm", "pnpm", "yarn", "bun", "deno"]).optional().describe("Package manager used by the target project (auto-detected if omitted)"),
|
|
752
876
|
policy: z.string().optional().describe("Optional path to .github/autoremediator.yml policy file"),
|
|
753
877
|
workspace: z.string().optional().describe("Optional workspace/package selector for monorepos")
|
|
754
878
|
}),
|
|
755
879
|
execute: async ({ cwd, packageManager, policy, workspace }) => {
|
|
756
880
|
let pkgJson;
|
|
757
881
|
try {
|
|
758
|
-
pkgJson = JSON.parse(
|
|
882
|
+
pkgJson = JSON.parse(readFileSync4(join6(cwd, "package.json"), "utf8"));
|
|
759
883
|
} catch {
|
|
760
884
|
return {
|
|
761
885
|
packages: [],
|
|
@@ -769,15 +893,21 @@ var checkInventoryTool = defineTool({
|
|
|
769
893
|
workspace: workspace ?? loadedPolicy.constraints?.workspace
|
|
770
894
|
});
|
|
771
895
|
let installedVersions = /* @__PURE__ */ new Map();
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
896
|
+
if (pm === "deno") {
|
|
897
|
+
installedVersions = resolveDenoInventory(cwd);
|
|
898
|
+
} else {
|
|
899
|
+
try {
|
|
900
|
+
const [cmd, ...args] = listCommand;
|
|
901
|
+
if (cmd) {
|
|
902
|
+
const listResult = await execa3(cmd, args, {
|
|
903
|
+
cwd,
|
|
904
|
+
stdio: "pipe",
|
|
905
|
+
reject: false
|
|
906
|
+
});
|
|
907
|
+
installedVersions = parseListOutput(pm, listResult.stdout || "");
|
|
908
|
+
}
|
|
909
|
+
} catch {
|
|
910
|
+
}
|
|
781
911
|
}
|
|
782
912
|
const packages = [];
|
|
783
913
|
for (const [name, version] of installedVersions.entries()) {
|
|
@@ -944,6 +1074,11 @@ async function loadRemoteFactory() {
|
|
|
944
1074
|
"AUTOREMEDIATOR_REMOTE_CLIENT_MODULE is required for remote provider model loading."
|
|
945
1075
|
);
|
|
946
1076
|
}
|
|
1077
|
+
if (moduleName.startsWith("./") || moduleName.startsWith("../") || moduleName.startsWith("/") || moduleName.startsWith("file:")) {
|
|
1078
|
+
throw new Error(
|
|
1079
|
+
`AUTOREMEDIATOR_REMOTE_CLIENT_MODULE must be a package name, not a file path: ${moduleName}`
|
|
1080
|
+
);
|
|
1081
|
+
}
|
|
947
1082
|
const loaded = await import(moduleName);
|
|
948
1083
|
const factory = loaded[exportName];
|
|
949
1084
|
if (typeof factory !== "function") {
|
|
@@ -1016,14 +1151,14 @@ function getIntelligenceSourceConfig() {
|
|
|
1016
1151
|
}
|
|
1017
1152
|
|
|
1018
1153
|
// src/platform/idempotency.ts
|
|
1019
|
-
import { existsSync as existsSync6, mkdirSync as mkdirSync2, readFileSync as
|
|
1020
|
-
import { join as
|
|
1154
|
+
import { existsSync as existsSync6, mkdirSync as mkdirSync2, readFileSync as readFileSync5, writeFileSync as writeFileSync2 } from "fs";
|
|
1155
|
+
import { join as join7 } from "path";
|
|
1021
1156
|
var DEFAULT_INDEX = {
|
|
1022
1157
|
schemaVersion: "1.0",
|
|
1023
1158
|
entries: {}
|
|
1024
1159
|
};
|
|
1025
1160
|
function indexFilePath(cwd) {
|
|
1026
|
-
return
|
|
1161
|
+
return join7(cwd, ".autoremediator", "state", "idempotency.json");
|
|
1027
1162
|
}
|
|
1028
1163
|
function entryKey(idempotencyKey, cveId) {
|
|
1029
1164
|
return `${idempotencyKey}::${cveId.toUpperCase()}`;
|
|
@@ -1032,7 +1167,7 @@ function loadIndex(cwd) {
|
|
|
1032
1167
|
const filePath = indexFilePath(cwd);
|
|
1033
1168
|
if (!existsSync6(filePath)) return DEFAULT_INDEX;
|
|
1034
1169
|
try {
|
|
1035
|
-
const parsed = JSON.parse(
|
|
1170
|
+
const parsed = JSON.parse(readFileSync5(filePath, "utf8"));
|
|
1036
1171
|
if (parsed && parsed.schemaVersion === "1.0" && parsed.entries) {
|
|
1037
1172
|
return parsed;
|
|
1038
1173
|
}
|
|
@@ -1043,7 +1178,7 @@ function loadIndex(cwd) {
|
|
|
1043
1178
|
}
|
|
1044
1179
|
function saveIndex(cwd, index) {
|
|
1045
1180
|
const filePath = indexFilePath(cwd);
|
|
1046
|
-
mkdirSync2(
|
|
1181
|
+
mkdirSync2(join7(cwd, ".autoremediator", "state"), { recursive: true });
|
|
1047
1182
|
writeFileSync2(filePath, JSON.stringify(index, null, 2) + "\n", "utf8");
|
|
1048
1183
|
}
|
|
1049
1184
|
function readIdempotentReport(cwd, idempotencyKey, cveId) {
|
|
@@ -1105,7 +1240,15 @@ async function httpClient(request) {
|
|
|
1105
1240
|
}
|
|
1106
1241
|
try {
|
|
1107
1242
|
const res = await requestWithTimeout(url, init, timeout);
|
|
1243
|
+
const MAX_RESPONSE_BYTES = 10 * 1024 * 1024;
|
|
1244
|
+
const contentLength = res.headers?.get?.("content-length") ?? null;
|
|
1245
|
+
if (contentLength && parseInt(contentLength, 10) > MAX_RESPONSE_BYTES) {
|
|
1246
|
+
throw new HttpError(`Response too large (content-length: ${contentLength})`, "HTTP_ERROR");
|
|
1247
|
+
}
|
|
1108
1248
|
const text = await res.text();
|
|
1249
|
+
if (Buffer.byteLength(text) > MAX_RESPONSE_BYTES) {
|
|
1250
|
+
throw new HttpError("Response body exceeds 10 MB limit", "HTTP_ERROR");
|
|
1251
|
+
}
|
|
1109
1252
|
let data;
|
|
1110
1253
|
try {
|
|
1111
1254
|
data = text ? JSON.parse(text) : {};
|
|
@@ -1331,8 +1474,8 @@ async function enrichWithNvd(details) {
|
|
|
1331
1474
|
|
|
1332
1475
|
// src/remediation/tools/check-reachability.ts
|
|
1333
1476
|
import { z as z2 } from "zod";
|
|
1334
|
-
import { readdirSync, readFileSync as
|
|
1335
|
-
import { join as
|
|
1477
|
+
import { readdirSync, readFileSync as readFileSync6, statSync } from "fs";
|
|
1478
|
+
import { join as join8, extname } from "path";
|
|
1336
1479
|
var SOURCE_EXTENSIONS = /* @__PURE__ */ new Set([".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs"]);
|
|
1337
1480
|
var SKIP_DIRS = /* @__PURE__ */ new Set(["node_modules", "dist", "build", "out", ".git", "coverage", ".cache"]);
|
|
1338
1481
|
var MAX_FILES = 500;
|
|
@@ -1346,7 +1489,7 @@ function collectSourceFiles(dir, files = []) {
|
|
|
1346
1489
|
}
|
|
1347
1490
|
for (const entry of entries) {
|
|
1348
1491
|
if (files.length >= MAX_FILES) break;
|
|
1349
|
-
const full =
|
|
1492
|
+
const full = join8(dir, entry);
|
|
1350
1493
|
let stat2;
|
|
1351
1494
|
try {
|
|
1352
1495
|
stat2 = statSync(full);
|
|
@@ -1379,7 +1522,7 @@ function assessPackageReachability(cwd, packageName) {
|
|
|
1379
1522
|
for (const filePath of files) {
|
|
1380
1523
|
let content;
|
|
1381
1524
|
try {
|
|
1382
|
-
content =
|
|
1525
|
+
content = readFileSync6(filePath, "utf8");
|
|
1383
1526
|
} catch {
|
|
1384
1527
|
continue;
|
|
1385
1528
|
}
|
|
@@ -1535,8 +1678,8 @@ function buildSbom(packages, vulnerableNames, results) {
|
|
|
1535
1678
|
}
|
|
1536
1679
|
|
|
1537
1680
|
// src/intelligence/sources/registry.ts
|
|
1538
|
-
import { readFileSync as
|
|
1539
|
-
import { join as
|
|
1681
|
+
import { readFileSync as readFileSync7 } from "fs";
|
|
1682
|
+
import { join as join9 } from "path";
|
|
1540
1683
|
import { execa as execa4 } from "execa";
|
|
1541
1684
|
import semver from "semver";
|
|
1542
1685
|
var NPM_REGISTRY = "https://registry.npmjs.org";
|
|
@@ -1705,7 +1848,7 @@ async function queryOutdatedPackages(cwd, options = {}) {
|
|
|
1705
1848
|
}
|
|
1706
1849
|
let directDeps;
|
|
1707
1850
|
try {
|
|
1708
|
-
const pkgRaw = JSON.parse(
|
|
1851
|
+
const pkgRaw = JSON.parse(readFileSync7(join9(cwd, "package.json"), "utf8"));
|
|
1709
1852
|
directDeps = /* @__PURE__ */ new Set([
|
|
1710
1853
|
...Object.keys(pkgRaw.dependencies ?? {}),
|
|
1711
1854
|
...Object.keys(pkgRaw.devDependencies ?? {})
|
|
@@ -1735,22 +1878,22 @@ async function queryOutdatedPackages(cwd, options = {}) {
|
|
|
1735
1878
|
|
|
1736
1879
|
// src/remediation/tools/apply-version-bump.ts
|
|
1737
1880
|
import { z as z4 } from "zod";
|
|
1738
|
-
import { join as
|
|
1739
|
-
import { readFileSync as
|
|
1881
|
+
import { join as join11 } from "path";
|
|
1882
|
+
import { readFileSync as readFileSync8, writeFileSync as writeFileSync3 } from "fs";
|
|
1740
1883
|
import { execa as execa5 } from "execa";
|
|
1741
1884
|
import semver2 from "semver";
|
|
1742
1885
|
|
|
1743
1886
|
// src/platform/repo-lock.ts
|
|
1744
1887
|
import { mkdir, rm } from "fs/promises";
|
|
1745
|
-
import { join as
|
|
1888
|
+
import { join as join10 } from "path";
|
|
1746
1889
|
async function sleep(ms) {
|
|
1747
|
-
await new Promise((
|
|
1890
|
+
await new Promise((resolve3) => setTimeout(resolve3, ms));
|
|
1748
1891
|
}
|
|
1749
1892
|
async function acquireRepoLock(cwd, options = {}) {
|
|
1750
1893
|
const timeoutMs = options.timeoutMs ?? 15e3;
|
|
1751
1894
|
const retryDelayMs = options.retryDelayMs ?? 125;
|
|
1752
|
-
const lockRoot =
|
|
1753
|
-
const lockPath =
|
|
1895
|
+
const lockRoot = join10(cwd, ".autoremediator", "locks");
|
|
1896
|
+
const lockPath = join10(cwd, ".autoremediator", "locks", "remediation.lock");
|
|
1754
1897
|
const startedAt = Date.now();
|
|
1755
1898
|
await mkdir(lockRoot, { recursive: true });
|
|
1756
1899
|
while (true) {
|
|
@@ -1784,7 +1927,7 @@ var applyVersionBumpTool = defineTool({
|
|
|
1784
1927
|
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.",
|
|
1785
1928
|
parameters: z4.object({
|
|
1786
1929
|
cwd: z4.string().describe("Absolute path to the consumer project root"),
|
|
1787
|
-
packageManager: z4.enum(["npm", "pnpm", "yarn"]).optional().describe("Package manager used by the target project (auto-detected if omitted)"),
|
|
1930
|
+
packageManager: z4.enum(["npm", "pnpm", "yarn", "bun", "deno"]).optional().describe("Package manager used by the target project (auto-detected if omitted)"),
|
|
1788
1931
|
packageName: z4.string().describe("The npm package to upgrade"),
|
|
1789
1932
|
fromVersion: z4.string().describe("The currently installed vulnerable version"),
|
|
1790
1933
|
toVersion: z4.string().describe("The safe target version to upgrade to"),
|
|
@@ -1811,7 +1954,7 @@ var applyVersionBumpTool = defineTool({
|
|
|
1811
1954
|
workspace
|
|
1812
1955
|
}) => {
|
|
1813
1956
|
const pm = packageManager ?? detectPackageManager(cwd);
|
|
1814
|
-
const pkgPath =
|
|
1957
|
+
const pkgPath = join11(cwd, "package.json");
|
|
1815
1958
|
const loadedPolicy = loadPolicy(cwd, policy);
|
|
1816
1959
|
const commandConstraints = {
|
|
1817
1960
|
...loadedPolicy.constraints,
|
|
@@ -1851,7 +1994,7 @@ var applyVersionBumpTool = defineTool({
|
|
|
1851
1994
|
}
|
|
1852
1995
|
let pkgJson;
|
|
1853
1996
|
try {
|
|
1854
|
-
pkgJson = JSON.parse(
|
|
1997
|
+
pkgJson = JSON.parse(readFileSync8(pkgPath, "utf8"));
|
|
1855
1998
|
} catch {
|
|
1856
1999
|
return {
|
|
1857
2000
|
packageName,
|
|
@@ -1975,8 +2118,8 @@ var applyVersionBumpTool = defineTool({
|
|
|
1975
2118
|
|
|
1976
2119
|
// src/remediation/tools/apply-package-override/index.ts
|
|
1977
2120
|
import { z as z5 } from "zod";
|
|
1978
|
-
import { join as
|
|
1979
|
-
import { readFileSync as
|
|
2121
|
+
import { join as join12 } from "path";
|
|
2122
|
+
import { existsSync as existsSync7, readFileSync as readFileSync9, writeFileSync as writeFileSync4 } from "fs";
|
|
1980
2123
|
import { execa as execa7 } from "execa";
|
|
1981
2124
|
import semver3 from "semver";
|
|
1982
2125
|
|
|
@@ -1985,6 +2128,7 @@ import { execa as execa6 } from "execa";
|
|
|
1985
2128
|
async function collectDependencyTrace(cwd, pm, packageName, constraints) {
|
|
1986
2129
|
try {
|
|
1987
2130
|
const whyCommand = resolveWhyCommand(pm, packageName, constraints);
|
|
2131
|
+
if (whyCommand.length === 0) return void 0;
|
|
1988
2132
|
const [whyCmd, ...whyArgs] = whyCommand;
|
|
1989
2133
|
const result = await execa6(whyCmd, whyArgs, {
|
|
1990
2134
|
cwd,
|
|
@@ -1999,17 +2143,20 @@ async function collectDependencyTrace(cwd, pm, packageName, constraints) {
|
|
|
1999
2143
|
}
|
|
2000
2144
|
}
|
|
2001
2145
|
function describeOverrideField(packageManager) {
|
|
2002
|
-
if (packageManager === "npm") return "overrides";
|
|
2146
|
+
if (packageManager === "npm" || packageManager === "bun") return "overrides";
|
|
2003
2147
|
if (packageManager === "pnpm") return "pnpm.overrides";
|
|
2148
|
+
if (packageManager === "deno") return "overrides";
|
|
2004
2149
|
return "resolutions";
|
|
2005
2150
|
}
|
|
2006
2151
|
function getOverrideValue(pkgJson, packageManager, packageName) {
|
|
2007
|
-
if (packageManager === "npm")
|
|
2152
|
+
if (packageManager === "npm" || packageManager === "bun" || packageManager === "deno") {
|
|
2153
|
+
return pkgJson.overrides?.[packageName];
|
|
2154
|
+
}
|
|
2008
2155
|
if (packageManager === "pnpm") return pkgJson.pnpm?.overrides?.[packageName];
|
|
2009
2156
|
return pkgJson.resolutions?.[packageName];
|
|
2010
2157
|
}
|
|
2011
2158
|
function setOverrideValue(pkgJson, packageManager, packageName, version) {
|
|
2012
|
-
if (packageManager === "npm") {
|
|
2159
|
+
if (packageManager === "npm" || packageManager === "bun" || packageManager === "deno") {
|
|
2013
2160
|
pkgJson.overrides = { ...pkgJson.overrides ?? {}, [packageName]: version };
|
|
2014
2161
|
return;
|
|
2015
2162
|
}
|
|
@@ -2026,7 +2173,7 @@ function setOverrideValue(pkgJson, packageManager, packageName, version) {
|
|
|
2026
2173
|
pkgJson.resolutions = { ...pkgJson.resolutions ?? {}, [packageName]: version };
|
|
2027
2174
|
}
|
|
2028
2175
|
function restoreOverrideValue(pkgJson, packageManager, packageName, previousValue) {
|
|
2029
|
-
if (packageManager === "npm") {
|
|
2176
|
+
if (packageManager === "npm" || packageManager === "bun" || packageManager === "deno") {
|
|
2030
2177
|
pkgJson.overrides = restoreRecord(pkgJson.overrides, packageName, previousValue);
|
|
2031
2178
|
return;
|
|
2032
2179
|
}
|
|
@@ -2045,6 +2192,34 @@ function restoreOverrideValue(pkgJson, packageManager, packageName, previousValu
|
|
|
2045
2192
|
}
|
|
2046
2193
|
pkgJson.resolutions = restoreRecord(pkgJson.resolutions, packageName, previousValue);
|
|
2047
2194
|
}
|
|
2195
|
+
function getDenoJsonImportValue(denoJson, packageName) {
|
|
2196
|
+
const imports = denoJson.imports ?? {};
|
|
2197
|
+
if (imports[packageName] !== void 0) {
|
|
2198
|
+
return { key: packageName, value: imports[packageName] };
|
|
2199
|
+
}
|
|
2200
|
+
const npmKey = `npm:${packageName}`;
|
|
2201
|
+
if (imports[npmKey] !== void 0) {
|
|
2202
|
+
return { key: npmKey, value: imports[npmKey] };
|
|
2203
|
+
}
|
|
2204
|
+
return void 0;
|
|
2205
|
+
}
|
|
2206
|
+
function setDenoJsonImportValue(denoJson, packageName, version) {
|
|
2207
|
+
const existing = getDenoJsonImportValue(denoJson, packageName);
|
|
2208
|
+
const key = existing?.key ?? `npm:${packageName}`;
|
|
2209
|
+
denoJson.imports = { ...denoJson.imports ?? {}, [key]: `npm:${packageName}@${version}` };
|
|
2210
|
+
}
|
|
2211
|
+
function restoreDenoJsonImportValue(denoJson, packageName, previousEntry) {
|
|
2212
|
+
const existing = getDenoJsonImportValue(denoJson, packageName);
|
|
2213
|
+
if (!existing) return;
|
|
2214
|
+
const imports = { ...denoJson.imports ?? {} };
|
|
2215
|
+
if (previousEntry === void 0) {
|
|
2216
|
+
delete imports[existing.key];
|
|
2217
|
+
} else {
|
|
2218
|
+
delete imports[existing.key];
|
|
2219
|
+
imports[previousEntry.key] = previousEntry.value;
|
|
2220
|
+
}
|
|
2221
|
+
denoJson.imports = Object.keys(imports).length > 0 ? imports : void 0;
|
|
2222
|
+
}
|
|
2048
2223
|
function restoreRecord(record, key, previousValue) {
|
|
2049
2224
|
const nextRecord = { ...record ?? {} };
|
|
2050
2225
|
if (previousValue === void 0) {
|
|
@@ -2057,10 +2232,10 @@ function restoreRecord(record, key, previousValue) {
|
|
|
2057
2232
|
|
|
2058
2233
|
// src/remediation/tools/apply-package-override/index.ts
|
|
2059
2234
|
var applyPackageOverrideTool = defineTool({
|
|
2060
|
-
description: "Apply a package-manager-native package.json override for a vulnerable transitive dependency and reinstall. Uses npm overrides, pnpm.overrides,
|
|
2235
|
+
description: "Apply a package-manager-native package.json override for a vulnerable transitive dependency and reinstall. Uses npm overrides, pnpm.overrides, yarn resolutions, bun overrides, or deno.json imports.",
|
|
2061
2236
|
parameters: z5.object({
|
|
2062
2237
|
cwd: z5.string().describe("Absolute path to the consumer project root"),
|
|
2063
|
-
packageManager: z5.enum(["npm", "pnpm", "yarn"]).optional().describe("Package manager used by the target project (auto-detected if omitted)"),
|
|
2238
|
+
packageManager: z5.enum(["npm", "pnpm", "yarn", "bun", "deno"]).optional().describe("Package manager used by the target project (auto-detected if omitted)"),
|
|
2064
2239
|
packageName: z5.string().describe("The npm package to override"),
|
|
2065
2240
|
selector: z5.string().optional().describe("Optional manager-native override selector key (for nested or scoped overrides)"),
|
|
2066
2241
|
fromVersion: z5.string().describe("The currently installed vulnerable version"),
|
|
@@ -2089,7 +2264,9 @@ var applyPackageOverrideTool = defineTool({
|
|
|
2089
2264
|
workspace
|
|
2090
2265
|
}) => {
|
|
2091
2266
|
const pm = packageManager ?? detectPackageManager(cwd);
|
|
2092
|
-
const pkgPath =
|
|
2267
|
+
const pkgPath = join12(cwd, "package.json");
|
|
2268
|
+
const denoJsonPath = join12(cwd, "deno.json");
|
|
2269
|
+
const isDenoNative = pm === "deno" && !existsSync7(pkgPath);
|
|
2093
2270
|
const loadedPolicy = loadPolicy(cwd, policy);
|
|
2094
2271
|
const commandConstraints = {
|
|
2095
2272
|
...loadedPolicy.constraints,
|
|
@@ -2128,9 +2305,108 @@ var applyPackageOverrideTool = defineTool({
|
|
|
2128
2305
|
message: `Policy blocked major override for "${packageName}" (${fromVersion} -> ${toVersion}).`
|
|
2129
2306
|
};
|
|
2130
2307
|
}
|
|
2308
|
+
if (isDenoNative) {
|
|
2309
|
+
let denoJson;
|
|
2310
|
+
try {
|
|
2311
|
+
denoJson = JSON.parse(readFileSync9(denoJsonPath, "utf8"));
|
|
2312
|
+
} catch {
|
|
2313
|
+
return {
|
|
2314
|
+
packageName,
|
|
2315
|
+
strategy: "none",
|
|
2316
|
+
fromVersion,
|
|
2317
|
+
toVersion,
|
|
2318
|
+
applied: false,
|
|
2319
|
+
dryRun,
|
|
2320
|
+
unresolvedReason: "package-json-not-found",
|
|
2321
|
+
message: `Could not read deno.json at "${denoJsonPath}".`
|
|
2322
|
+
};
|
|
2323
|
+
}
|
|
2324
|
+
const existingEntry = getDenoJsonImportValue(denoJson, overrideSelector);
|
|
2325
|
+
if (!existingEntry) {
|
|
2326
|
+
return {
|
|
2327
|
+
packageName,
|
|
2328
|
+
strategy: "none",
|
|
2329
|
+
fromVersion,
|
|
2330
|
+
toVersion,
|
|
2331
|
+
applied: false,
|
|
2332
|
+
dryRun,
|
|
2333
|
+
unresolvedReason: "transitive-override-unsupported-deno-native",
|
|
2334
|
+
message: `Cannot apply transitive override for "${overrideSelector}" in a native Deno project (no package.json). Only direct dependencies declared in deno.json imports can be overridden.`
|
|
2335
|
+
};
|
|
2336
|
+
}
|
|
2337
|
+
const dependencyTrace2 = await collectDependencyTrace(cwd, pm, packageName, commandConstraints);
|
|
2338
|
+
const dependencyTraceSuffix2 = dependencyTrace2 ? ` Dependency trace: ${dependencyTrace2}` : "";
|
|
2339
|
+
if (dryRun) {
|
|
2340
|
+
return {
|
|
2341
|
+
packageName,
|
|
2342
|
+
strategy: "override",
|
|
2343
|
+
fromVersion,
|
|
2344
|
+
toVersion,
|
|
2345
|
+
applied: false,
|
|
2346
|
+
dryRun: true,
|
|
2347
|
+
message: `[DRY RUN] Would update deno.json imports["${existingEntry.key}"] to "npm:${overrideSelector}@${toVersion}", then run ${installCommand.join(" ")}.${dependencyTraceSuffix2}`
|
|
2348
|
+
};
|
|
2349
|
+
}
|
|
2350
|
+
return withRepoLock(cwd, async () => {
|
|
2351
|
+
setDenoJsonImportValue(denoJson, overrideSelector, toVersion);
|
|
2352
|
+
writeFileSync4(denoJsonPath, JSON.stringify(denoJson, null, 2) + "\n", "utf8");
|
|
2353
|
+
try {
|
|
2354
|
+
const [installCmd, ...installArgs] = installCommand;
|
|
2355
|
+
await execa7(installCmd, installArgs, { cwd, stdio: "pipe" });
|
|
2356
|
+
} catch (err) {
|
|
2357
|
+
restoreDenoJsonImportValue(denoJson, overrideSelector, existingEntry);
|
|
2358
|
+
writeFileSync4(denoJsonPath, JSON.stringify(denoJson, null, 2) + "\n", "utf8");
|
|
2359
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
2360
|
+
return {
|
|
2361
|
+
packageName,
|
|
2362
|
+
strategy: "override",
|
|
2363
|
+
fromVersion,
|
|
2364
|
+
toVersion,
|
|
2365
|
+
applied: false,
|
|
2366
|
+
dryRun: false,
|
|
2367
|
+
unresolvedReason: "override-apply-failed",
|
|
2368
|
+
message: `${installCommand.join(" ")} failed after updating deno.json imports for "${overrideSelector}" to ${toVersion}. Reverted. Error: ${message}${dependencyTraceSuffix2}`
|
|
2369
|
+
};
|
|
2370
|
+
}
|
|
2371
|
+
if (runTests) {
|
|
2372
|
+
try {
|
|
2373
|
+
const [testCmd, ...testArgs] = testCommand;
|
|
2374
|
+
await execa7(testCmd, testArgs, { cwd, stdio: "pipe" });
|
|
2375
|
+
} catch (err) {
|
|
2376
|
+
restoreDenoJsonImportValue(denoJson, overrideSelector, existingEntry);
|
|
2377
|
+
writeFileSync4(denoJsonPath, JSON.stringify(denoJson, null, 2) + "\n", "utf8");
|
|
2378
|
+
try {
|
|
2379
|
+
const [rollbackCmd, ...rollbackArgs] = installCommand;
|
|
2380
|
+
await execa7(rollbackCmd, rollbackArgs, { cwd, stdio: "pipe" });
|
|
2381
|
+
} catch {
|
|
2382
|
+
}
|
|
2383
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
2384
|
+
return {
|
|
2385
|
+
packageName,
|
|
2386
|
+
strategy: "override",
|
|
2387
|
+
fromVersion,
|
|
2388
|
+
toVersion,
|
|
2389
|
+
applied: false,
|
|
2390
|
+
dryRun: false,
|
|
2391
|
+
unresolvedReason: "validation-failed",
|
|
2392
|
+
message: `${testCommand.join(" ")} failed after updating deno.json imports for "${overrideSelector}" to ${toVersion}. Reverted. Error: ${message}${dependencyTraceSuffix2}`
|
|
2393
|
+
};
|
|
2394
|
+
}
|
|
2395
|
+
}
|
|
2396
|
+
return {
|
|
2397
|
+
packageName,
|
|
2398
|
+
strategy: "override",
|
|
2399
|
+
fromVersion,
|
|
2400
|
+
toVersion,
|
|
2401
|
+
applied: true,
|
|
2402
|
+
dryRun: false,
|
|
2403
|
+
message: `Successfully updated deno.json imports["${existingEntry.key}"] for "${overrideSelector}" from ${fromVersion} to ${toVersion}, then ran ${installCommand.join(" ")}${runTests ? ` and passed ${testCommand.join(" ")}` : ""}.${dependencyTraceSuffix2}`
|
|
2404
|
+
};
|
|
2405
|
+
});
|
|
2406
|
+
}
|
|
2131
2407
|
let pkgJson;
|
|
2132
2408
|
try {
|
|
2133
|
-
pkgJson = JSON.parse(
|
|
2409
|
+
pkgJson = JSON.parse(readFileSync9(pkgPath, "utf8"));
|
|
2134
2410
|
} catch {
|
|
2135
2411
|
return {
|
|
2136
2412
|
packageName,
|
|
@@ -2205,12 +2481,14 @@ var applyPackageOverrideTool = defineTool({
|
|
|
2205
2481
|
}
|
|
2206
2482
|
}
|
|
2207
2483
|
let dedupeNote = "";
|
|
2208
|
-
|
|
2209
|
-
|
|
2210
|
-
|
|
2211
|
-
|
|
2212
|
-
|
|
2213
|
-
|
|
2484
|
+
if (dedupeCommand.length > 0) {
|
|
2485
|
+
try {
|
|
2486
|
+
const [dedupeCmd, ...dedupeArgs] = dedupeCommand;
|
|
2487
|
+
await execa7(dedupeCmd, dedupeArgs, { cwd, stdio: "pipe" });
|
|
2488
|
+
} catch (err) {
|
|
2489
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
2490
|
+
dedupeNote = ` Dedupe warning: ${dedupeCommand.join(" ")} failed (${message}).`;
|
|
2491
|
+
}
|
|
2214
2492
|
}
|
|
2215
2493
|
return {
|
|
2216
2494
|
packageName,
|
|
@@ -2368,8 +2646,9 @@ async function resolvePrimaryResult(params) {
|
|
|
2368
2646
|
|
|
2369
2647
|
// src/remediation/tools/fetch-package-source.ts
|
|
2370
2648
|
import { z as z6 } from "zod";
|
|
2371
|
-
import { mkdir as mkdir2, readdir as readdir2, readFile as readFile3, rm as rm2 } from "fs/promises";
|
|
2372
|
-
import { join as
|
|
2649
|
+
import { mkdir as mkdir2, mkdtemp, readdir as readdir2, readFile as readFile3, rm as rm2 } from "fs/promises";
|
|
2650
|
+
import { join as join13 } from "path";
|
|
2651
|
+
import { tmpdir } from "os";
|
|
2373
2652
|
import { execa as execa8 } from "execa";
|
|
2374
2653
|
var fetchPackageSourceTool = defineTool({
|
|
2375
2654
|
description: "Download package tarball from npm and extract source files for CVE analysis. Supports custom file patterns (default: *.js, *.ts).",
|
|
@@ -2385,24 +2664,34 @@ var fetchPackageSourceTool = defineTool({
|
|
|
2385
2664
|
version,
|
|
2386
2665
|
filePatterns
|
|
2387
2666
|
}) => {
|
|
2388
|
-
|
|
2389
|
-
|
|
2667
|
+
if (!/^(@[a-z0-9][a-z0-9._-]*\/)?[a-z0-9][a-z0-9._-]*$/i.test(packageName)) {
|
|
2668
|
+
return { success: false, error: `Invalid package name: ${packageName}` };
|
|
2669
|
+
}
|
|
2670
|
+
const safePatterns = (filePatterns ?? ["*.js", "*.ts"]).filter(
|
|
2671
|
+
(p) => /^[a-zA-Z0-9._/*?-]+$/.test(p)
|
|
2672
|
+
);
|
|
2673
|
+
if (safePatterns.length === 0) {
|
|
2674
|
+
return { success: false, error: "No valid file patterns provided." };
|
|
2675
|
+
}
|
|
2676
|
+
const tempBaseDir = await mkdtemp(join13(tmpdir(), "autoremediator-pkg-"));
|
|
2677
|
+
const extractDir = join13(tempBaseDir, "out");
|
|
2390
2678
|
try {
|
|
2391
|
-
const
|
|
2679
|
+
const scopedName = packageName.split("/").pop();
|
|
2680
|
+
const npmUrl = `https://registry.npmjs.org/${packageName}/-/${scopedName}-${version}.tgz`;
|
|
2392
2681
|
await mkdir2(tempBaseDir, { recursive: true });
|
|
2393
|
-
const tarballPath =
|
|
2682
|
+
const tarballPath = join13(tempBaseDir, "package.tgz");
|
|
2394
2683
|
await execa8("curl", ["-L", "-o", tarballPath, npmUrl]);
|
|
2395
2684
|
await mkdir2(extractDir, { recursive: true });
|
|
2396
2685
|
await execa8("tar", ["-xzf", tarballPath, "-C", extractDir]);
|
|
2397
2686
|
const extractedContents = await readdir2(extractDir);
|
|
2398
|
-
const packageRootDir = extractedContents.includes("package") ?
|
|
2687
|
+
const packageRootDir = extractedContents.includes("package") ? join13(extractDir, "package") : extractDir;
|
|
2399
2688
|
const sourceCode = {};
|
|
2400
2689
|
async function walkDir(dir, relativeBase) {
|
|
2401
2690
|
try {
|
|
2402
2691
|
const files = await readdir2(dir, { withFileTypes: true });
|
|
2403
2692
|
for (const file of files) {
|
|
2404
|
-
const fullPath =
|
|
2405
|
-
const relPath =
|
|
2693
|
+
const fullPath = join13(dir, file.name);
|
|
2694
|
+
const relPath = join13(relativeBase, file.name);
|
|
2406
2695
|
if (file.isDirectory()) {
|
|
2407
2696
|
if (![
|
|
2408
2697
|
"node_modules",
|
|
@@ -2416,7 +2705,7 @@ var fetchPackageSourceTool = defineTool({
|
|
|
2416
2705
|
await walkDir(fullPath, relPath);
|
|
2417
2706
|
}
|
|
2418
2707
|
} else if (file.isFile()) {
|
|
2419
|
-
const matches =
|
|
2708
|
+
const matches = safePatterns.some((pattern) => {
|
|
2420
2709
|
const regex = new RegExp(
|
|
2421
2710
|
`^${pattern.replace(/\*/g, ".*").replace(/\./g, "\\.")}$`
|
|
2422
2711
|
);
|
|
@@ -2438,7 +2727,7 @@ var fetchPackageSourceTool = defineTool({
|
|
|
2438
2727
|
if (Object.keys(sourceCode).length === 0) {
|
|
2439
2728
|
return {
|
|
2440
2729
|
success: false,
|
|
2441
|
-
error: `No source files matching patterns [${
|
|
2730
|
+
error: `No source files matching patterns [${safePatterns.join(", ")}] found in ${packageName}@${version}. Download succeeded but extraction yielded no matching files.`
|
|
2442
2731
|
};
|
|
2443
2732
|
}
|
|
2444
2733
|
return {
|
|
@@ -2774,24 +3063,23 @@ var generatePatchTool = defineTool({
|
|
|
2774
3063
|
// src/remediation/tools/apply-patch-file/index.ts
|
|
2775
3064
|
import { z as z8 } from "zod";
|
|
2776
3065
|
import { mkdir as mkdir3, writeFile as writeFile2 } from "fs/promises";
|
|
2777
|
-
import { join as
|
|
3066
|
+
import { join as join15 } from "path";
|
|
2778
3067
|
import { execa as execa10 } from "execa";
|
|
2779
3068
|
|
|
2780
3069
|
// src/remediation/tools/apply-patch-file/helpers.ts
|
|
2781
|
-
import { existsSync as
|
|
2782
|
-
import { mkdtemp, readFile as readFile4, rm as rm3, writeFile } from "fs/promises";
|
|
3070
|
+
import { existsSync as existsSync8 } from "fs";
|
|
3071
|
+
import { mkdtemp as mkdtemp2, readFile as readFile4, rm as rm3, writeFile } from "fs/promises";
|
|
2783
3072
|
import { createHash } from "crypto";
|
|
2784
|
-
import { tmpdir } from "os";
|
|
2785
|
-
import { join as
|
|
3073
|
+
import { tmpdir as tmpdir2 } from "os";
|
|
3074
|
+
import { join as join14 } from "path";
|
|
2786
3075
|
import { execa as execa9 } from "execa";
|
|
2787
3076
|
async function resolvePatchMode(packageManager, cwd) {
|
|
2788
|
-
if (packageManager === "npm") return "patch-package";
|
|
3077
|
+
if (packageManager === "npm" || packageManager === "bun" || packageManager === "deno") return "patch-package";
|
|
2789
3078
|
if (packageManager === "pnpm") return "native-pnpm";
|
|
2790
3079
|
const major = await getYarnMajorVersion(cwd);
|
|
2791
3080
|
return major >= 2 ? "native-yarn" : "patch-package";
|
|
2792
3081
|
}
|
|
2793
3082
|
function patchModeRequiresPackageJsonSnapshot(packageManager) {
|
|
2794
|
-
if (packageManager === "npm") return true;
|
|
2795
3083
|
if (packageManager === "pnpm") return false;
|
|
2796
3084
|
return true;
|
|
2797
3085
|
}
|
|
@@ -2817,7 +3105,7 @@ async function writePatchManifest(manifestFilePath, artifact) {
|
|
|
2817
3105
|
await writeFile(manifestFilePath, JSON.stringify(artifact, null, 2) + "\n", "utf8");
|
|
2818
3106
|
}
|
|
2819
3107
|
async function configurePatchPackagePostinstall(cwd, packageManager) {
|
|
2820
|
-
const pkgJsonPath =
|
|
3108
|
+
const pkgJsonPath = join14(cwd, "package.json");
|
|
2821
3109
|
let pkgJson;
|
|
2822
3110
|
try {
|
|
2823
3111
|
pkgJson = JSON.parse(await readFile4(pkgJsonPath, "utf8"));
|
|
@@ -2857,7 +3145,7 @@ async function configurePatchPackagePostinstall(cwd, packageManager) {
|
|
|
2857
3145
|
return { success: true };
|
|
2858
3146
|
}
|
|
2859
3147
|
async function capturePackageJsonSnapshot(cwd) {
|
|
2860
|
-
const path =
|
|
3148
|
+
const path = join14(cwd, "package.json");
|
|
2861
3149
|
try {
|
|
2862
3150
|
const content = await readFile4(path, "utf8");
|
|
2863
3151
|
return { path, content };
|
|
@@ -2927,8 +3215,8 @@ ${createResult.stderr}`);
|
|
|
2927
3215
|
error: `Could not determine native patch directory for ${packageSpec}.`
|
|
2928
3216
|
};
|
|
2929
3217
|
}
|
|
2930
|
-
const tempPatchDir = await
|
|
2931
|
-
const tempPatchFile =
|
|
3218
|
+
const tempPatchDir = await mkdtemp2(join14(tmpdir2(), "autoremediator-native-patch-"));
|
|
3219
|
+
const tempPatchFile = join14(tempPatchDir, "change.patch");
|
|
2932
3220
|
try {
|
|
2933
3221
|
await writeFile(tempPatchFile, patchContent, "utf8");
|
|
2934
3222
|
await execa9("patch", ["-p1", "-i", tempPatchFile], {
|
|
@@ -2959,12 +3247,12 @@ ${createResult.stderr}`);
|
|
|
2959
3247
|
function extractPatchDirectory(output) {
|
|
2960
3248
|
const lines = output.split(/\r?\n/).map((line) => line.trim()).filter(Boolean);
|
|
2961
3249
|
for (const line of lines) {
|
|
2962
|
-
if (
|
|
3250
|
+
if (existsSync8(line)) {
|
|
2963
3251
|
return line;
|
|
2964
3252
|
}
|
|
2965
3253
|
const tokens = line.split(/\s+/).map((token) => token.replace(/^['"]|['"]$/g, ""));
|
|
2966
3254
|
for (const token of tokens) {
|
|
2967
|
-
if (token.startsWith("/") &&
|
|
3255
|
+
if (token.startsWith("/") && existsSync8(token)) {
|
|
2968
3256
|
return token;
|
|
2969
3257
|
}
|
|
2970
3258
|
}
|
|
@@ -3030,7 +3318,7 @@ var applyPatchFileTool = defineTool({
|
|
|
3030
3318
|
).optional().describe("Patch list from generate-patch; first patch is applied"),
|
|
3031
3319
|
patchesDir: z8.string().optional().default("./patches").describe("Directory to store patch files"),
|
|
3032
3320
|
cwd: z8.string().describe("Project root directory (for package.json)"),
|
|
3033
|
-
packageManager: z8.enum(["npm", "pnpm", "yarn"]).optional().describe("Package manager used by the target project (auto-detected if omitted)"),
|
|
3321
|
+
packageManager: z8.enum(["npm", "pnpm", "yarn", "bun", "deno"]).optional().describe("Package manager used by the target project (auto-detected if omitted)"),
|
|
3034
3322
|
policy: z8.string().optional().describe("Optional path to .autoremediator policy file"),
|
|
3035
3323
|
installMode: z8.enum(["standard", "prefer-offline", "deterministic"]).optional(),
|
|
3036
3324
|
installPreferOffline: z8.boolean().optional(),
|
|
@@ -3108,7 +3396,7 @@ var applyPatchFileTool = defineTool({
|
|
|
3108
3396
|
};
|
|
3109
3397
|
}
|
|
3110
3398
|
const patchFileName = buildPatchFileName(packageName, vulnerableVersion);
|
|
3111
|
-
const patchFilePath =
|
|
3399
|
+
const patchFilePath = join15(cwd, patchesDir, patchFileName);
|
|
3112
3400
|
const manifestFilePath = `${patchFilePath}.json`;
|
|
3113
3401
|
const generatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
3114
3402
|
const baseArtifact = {
|
|
@@ -3147,7 +3435,7 @@ var applyPatchFileTool = defineTool({
|
|
|
3147
3435
|
}
|
|
3148
3436
|
return withRepoLock(cwd, async () => {
|
|
3149
3437
|
const packageJsonSnapshot = patchModeRequiresPackageJsonSnapshot(pm) ? await capturePackageJsonSnapshot(cwd) : void 0;
|
|
3150
|
-
const patchesDirPath =
|
|
3438
|
+
const patchesDirPath = join15(cwd, patchesDir);
|
|
3151
3439
|
await mkdir3(patchesDirPath, { recursive: true });
|
|
3152
3440
|
await writeFile2(patchFilePath, selectedPatch, "utf8");
|
|
3153
3441
|
validationPhases.push({
|
|
@@ -4107,8 +4395,8 @@ function accumulateStepResults(params) {
|
|
|
4107
4395
|
}
|
|
4108
4396
|
|
|
4109
4397
|
// src/remediation/orchestration-prompt.ts
|
|
4110
|
-
import { existsSync as
|
|
4111
|
-
import { join as
|
|
4398
|
+
import { existsSync as existsSync9, readFileSync as readFileSync10 } from "fs";
|
|
4399
|
+
import { join as join16 } from "path";
|
|
4112
4400
|
function buildProviderAddendum(provider, personality = "balanced") {
|
|
4113
4401
|
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.";
|
|
4114
4402
|
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.";
|
|
@@ -4119,8 +4407,8 @@ Provider profile:
|
|
|
4119
4407
|
- ${personalityDirective}`;
|
|
4120
4408
|
}
|
|
4121
4409
|
function loadOrchestrationPrompt(ctx) {
|
|
4122
|
-
const promptPath =
|
|
4123
|
-
if (!
|
|
4410
|
+
const promptPath = join16(process.cwd(), ".github", "instructions", "orchestration.instructions.md");
|
|
4411
|
+
if (!existsSync9(promptPath)) {
|
|
4124
4412
|
return `You are autoremediator, an agentic security remediation system for Node.js package dependencies.
|
|
4125
4413
|
Working directory: ${ctx.cwd}
|
|
4126
4414
|
Package manager: ${ctx.packageManager}
|
|
@@ -4146,7 +4434,7 @@ Fallback sequence (when neither version bump nor override can be applied):
|
|
|
4146
4434
|
|
|
4147
4435
|
Always respect dryRun and policy constraints.`;
|
|
4148
4436
|
}
|
|
4149
|
-
const template =
|
|
4437
|
+
const template = readFileSync10(promptPath, "utf8");
|
|
4150
4438
|
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("{{directDependenciesOnly}}", String(ctx.constraints.directDependenciesOnly ?? false)).replaceAll("{{preferVersionBump}}", String(ctx.constraints.preferVersionBump ?? false)) + buildProviderAddendum(ctx.llmProvider, ctx.modelPersonality);
|
|
4151
4439
|
}
|
|
4152
4440
|
|
|
@@ -4409,6 +4697,13 @@ async function enrichWithOssfScorecard(details) {
|
|
|
4409
4697
|
async function probeFeed(url, cveId, token) {
|
|
4410
4698
|
try {
|
|
4411
4699
|
const feedUrl = new URL(url);
|
|
4700
|
+
const hostname = feedUrl.hostname.toLowerCase();
|
|
4701
|
+
if (hostname === "localhost" || /^127\./.test(hostname) || /^10\./.test(hostname) || /^172\.(1[6-9]|2\d|3[01])\./.test(hostname) || /^192\.168\./.test(hostname) || hostname === "0.0.0.0" || /^169\.254\./.test(hostname) || hostname === "[::1]" || hostname === "::1") {
|
|
4702
|
+
return void 0;
|
|
4703
|
+
}
|
|
4704
|
+
if (feedUrl.protocol !== "https:" && feedUrl.protocol !== "http:") {
|
|
4705
|
+
return void 0;
|
|
4706
|
+
}
|
|
4412
4707
|
feedUrl.searchParams.set("cve", cveId);
|
|
4413
4708
|
const headers = {};
|
|
4414
4709
|
if (token) headers.Authorization = `Bearer ${token}`;
|
|
@@ -4839,6 +5134,7 @@ async function runRemediationPipeline(cveId, options = {}) {
|
|
|
4839
5134
|
|
|
4840
5135
|
// src/api/change-request/index.ts
|
|
4841
5136
|
import { execa as execa11 } from "execa";
|
|
5137
|
+
import { randomBytes } from "crypto";
|
|
4842
5138
|
function sanitizeBranchToken(value) {
|
|
4843
5139
|
return value.toLowerCase().replace(/[^a-z0-9._/-]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 80);
|
|
4844
5140
|
}
|
|
@@ -4891,7 +5187,7 @@ async function ensureRepoHasChanges(cwd) {
|
|
|
4891
5187
|
}
|
|
4892
5188
|
function toBranchName(prefix, cveIds) {
|
|
4893
5189
|
const token = sanitizeBranchToken(cveIds.join("-") || "remediation");
|
|
4894
|
-
return `${sanitizeBranchToken(prefix)}/${token}-${
|
|
5190
|
+
return `${sanitizeBranchToken(prefix)}/${token}-${randomBytes(6).toString("hex")}`;
|
|
4895
5191
|
}
|
|
4896
5192
|
async function createGitHubRequest(params) {
|
|
4897
5193
|
const args = ["pr", "create", "--head", params.head, "--base", params.base, "--title", params.title, "--body", params.body];
|
|
@@ -4937,11 +5233,12 @@ async function createChangeRequestsForReports(params) {
|
|
|
4937
5233
|
const provider = resolveProvider2(options);
|
|
4938
5234
|
const grouping = resolveGrouping(options);
|
|
4939
5235
|
const plan = buildPlan(reports);
|
|
4940
|
-
const titlePrefix = options.titlePrefix?.trim();
|
|
5236
|
+
const titlePrefix = options.titlePrefix?.trim().slice(0, 200);
|
|
5237
|
+
const bodyFooter = options.bodyFooter ? options.bodyFooter.slice(0, 2e3) : void 0;
|
|
4941
5238
|
const title = titlePrefix ? `${titlePrefix} ${plan.title}` : plan.title;
|
|
4942
|
-
const body =
|
|
5239
|
+
const body = bodyFooter ? `${plan.body}
|
|
4943
5240
|
|
|
4944
|
-
${
|
|
5241
|
+
${bodyFooter}` : plan.body;
|
|
4945
5242
|
try {
|
|
4946
5243
|
const hasChanges = await ensureRepoHasChanges(cwd);
|
|
4947
5244
|
if (!hasChanges) {
|
|
@@ -4964,6 +5261,12 @@ ${options.bodyFooter}` : plan.body;
|
|
|
4964
5261
|
const branchPrefix = options.branchPrefix ?? "autoremediator";
|
|
4965
5262
|
const branchName = toBranchName(branchPrefix, plan.cveIds);
|
|
4966
5263
|
const pushRemote = options.pushRemote ?? "origin";
|
|
5264
|
+
if (options.pushRemote && !/^[a-zA-Z0-9._-]+$/.test(options.pushRemote)) {
|
|
5265
|
+
throw new Error(`Invalid pushRemote value: ${options.pushRemote}`);
|
|
5266
|
+
}
|
|
5267
|
+
if (options.repository && !/^[a-zA-Z0-9._-]+\/[a-zA-Z0-9._-]+$/.test(options.repository)) {
|
|
5268
|
+
throw new Error(`Invalid repository format: ${options.repository}`);
|
|
5269
|
+
}
|
|
4967
5270
|
await execa11("git", ["checkout", "-b", branchName], { cwd, stdio: "pipe" });
|
|
4968
5271
|
await execa11("git", ["add", "-A"], { cwd, stdio: "pipe" });
|
|
4969
5272
|
await execa11("git", ["commit", "-m", title], { cwd, stdio: "pipe" });
|
|
@@ -5050,7 +5353,7 @@ function resolveConstraints(options, cwd) {
|
|
|
5050
5353
|
|
|
5051
5354
|
// src/platform/evidence.ts
|
|
5052
5355
|
import { mkdirSync as mkdirSync3, writeFileSync as writeFileSync5 } from "fs";
|
|
5053
|
-
import { join as
|
|
5356
|
+
import { join as join17 } from "path";
|
|
5054
5357
|
function createEvidenceLog(cwd, cveIds, context = {}) {
|
|
5055
5358
|
return {
|
|
5056
5359
|
runId: `${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,
|
|
@@ -5081,9 +5384,9 @@ function finalizeEvidence(log) {
|
|
|
5081
5384
|
return log;
|
|
5082
5385
|
}
|
|
5083
5386
|
function writeEvidenceLog(cwd, log) {
|
|
5084
|
-
const dir =
|
|
5387
|
+
const dir = join17(cwd, ".autoremediator", "evidence");
|
|
5085
5388
|
mkdirSync3(dir, { recursive: true });
|
|
5086
|
-
const filePath =
|
|
5389
|
+
const filePath = join17(dir, `${log.runId}.json`);
|
|
5087
5390
|
writeFileSync5(filePath, JSON.stringify(log, null, 2) + "\n", "utf8");
|
|
5088
5391
|
return filePath;
|
|
5089
5392
|
}
|
|
@@ -5651,13 +5954,13 @@ async function planRemediation(cveId, options = {}) {
|
|
|
5651
5954
|
}
|
|
5652
5955
|
|
|
5653
5956
|
// src/scanner/parse-input.ts
|
|
5654
|
-
import { extname as extname2 } from "path";
|
|
5655
|
-
import { readFileSync as
|
|
5957
|
+
import { extname as extname2, resolve as resolve2 } from "path";
|
|
5958
|
+
import { readFileSync as readFileSync14 } from "fs";
|
|
5656
5959
|
import { execa as execa12 } from "execa";
|
|
5657
5960
|
|
|
5658
5961
|
// src/scanner/adapters/npm-audit.ts
|
|
5659
|
-
import { readFileSync as
|
|
5660
|
-
var CVE_REGEX = /CVE-\d{4}-\d
|
|
5962
|
+
import { readFileSync as readFileSync11 } from "fs";
|
|
5963
|
+
var CVE_REGEX = /CVE-\d{4}-\d{1,7}/gi;
|
|
5661
5964
|
function normalizeSeverity(raw) {
|
|
5662
5965
|
if (!raw) return "UNKNOWN";
|
|
5663
5966
|
const up = raw.toUpperCase();
|
|
@@ -5690,13 +5993,13 @@ function parseNpmAuditJsonFromString(content) {
|
|
|
5690
5993
|
return findings;
|
|
5691
5994
|
}
|
|
5692
5995
|
function parseNpmAuditJsonFile(filePath) {
|
|
5693
|
-
const content =
|
|
5996
|
+
const content = readFileSync11(filePath, "utf8");
|
|
5694
5997
|
return parseNpmAuditJsonFromString(content);
|
|
5695
5998
|
}
|
|
5696
5999
|
|
|
5697
6000
|
// src/scanner/adapters/yarn-audit.ts
|
|
5698
|
-
import { readFileSync as
|
|
5699
|
-
var CVE_REGEX2 = /CVE-\d{4}-\d
|
|
6001
|
+
import { readFileSync as readFileSync12 } from "fs";
|
|
6002
|
+
var CVE_REGEX2 = /CVE-\d{4}-\d{1,7}/gi;
|
|
5700
6003
|
function normalizeSeverity2(raw) {
|
|
5701
6004
|
if (!raw) return "UNKNOWN";
|
|
5702
6005
|
const up = raw.toUpperCase();
|
|
@@ -5738,13 +6041,13 @@ function parseYarnAuditJsonFromString(content) {
|
|
|
5738
6041
|
return findings;
|
|
5739
6042
|
}
|
|
5740
6043
|
function parseYarnAuditJsonFile(filePath) {
|
|
5741
|
-
const content =
|
|
6044
|
+
const content = readFileSync12(filePath, "utf8");
|
|
5742
6045
|
return parseYarnAuditJsonFromString(content);
|
|
5743
6046
|
}
|
|
5744
6047
|
|
|
5745
6048
|
// src/scanner/adapters/sarif.ts
|
|
5746
|
-
import { readFileSync as
|
|
5747
|
-
var CVE_REGEX3 = /CVE-\d{4}-\d
|
|
6049
|
+
import { readFileSync as readFileSync13 } from "fs";
|
|
6050
|
+
var CVE_REGEX3 = /CVE-\d{4}-\d{1,7}/gi;
|
|
5748
6051
|
function extractPackageName(result) {
|
|
5749
6052
|
const pkg = result.properties?.["packageName"];
|
|
5750
6053
|
return typeof pkg === "string" ? pkg : void 0;
|
|
@@ -5753,9 +6056,15 @@ function parseSarifFromString(content) {
|
|
|
5753
6056
|
const report = JSON.parse(content);
|
|
5754
6057
|
const findings = [];
|
|
5755
6058
|
const seen = /* @__PURE__ */ new Set();
|
|
5756
|
-
|
|
6059
|
+
const MAX_RUNS = 100;
|
|
6060
|
+
const MAX_TOTAL_RESULTS = 1e4;
|
|
6061
|
+
let totalResults = 0;
|
|
6062
|
+
for (const run of (report.runs ?? []).slice(0, MAX_RUNS)) {
|
|
5757
6063
|
for (const result of run.results ?? []) {
|
|
5758
|
-
|
|
6064
|
+
if (totalResults++ >= MAX_TOTAL_RESULTS) break;
|
|
6065
|
+
const ruleId = (result.ruleId ?? "").slice(0, 1024);
|
|
6066
|
+
const messageText = (result.message?.text ?? "").slice(0, 4096);
|
|
6067
|
+
const combined = `${ruleId} ${messageText}`;
|
|
5759
6068
|
const matches = combined.match(CVE_REGEX3) ?? [];
|
|
5760
6069
|
for (const match of matches) {
|
|
5761
6070
|
const cveId = match.toUpperCase();
|
|
@@ -5775,21 +6084,25 @@ function parseSarifFromString(content) {
|
|
|
5775
6084
|
return findings;
|
|
5776
6085
|
}
|
|
5777
6086
|
function parseSarifFile(filePath) {
|
|
5778
|
-
const content =
|
|
6087
|
+
const content = readFileSync13(filePath, "utf8");
|
|
5779
6088
|
return parseSarifFromString(content);
|
|
5780
6089
|
}
|
|
5781
6090
|
|
|
5782
6091
|
// src/scanner/parse-input.ts
|
|
5783
6092
|
function parseScanInput(filePath, format) {
|
|
5784
|
-
|
|
6093
|
+
if (filePath.includes("\0")) {
|
|
6094
|
+
throw new Error("Invalid scan input path: path contains null bytes");
|
|
6095
|
+
}
|
|
6096
|
+
const resolvedPath = resolve2(filePath);
|
|
6097
|
+
const resolved = format === "auto" ? inferFormat(resolvedPath) : format;
|
|
5785
6098
|
if (resolved === "npm-audit") {
|
|
5786
|
-
return parseNpmAuditJsonFile(
|
|
6099
|
+
return parseNpmAuditJsonFile(resolvedPath);
|
|
5787
6100
|
}
|
|
5788
6101
|
if (resolved === "yarn-audit") {
|
|
5789
|
-
return parseYarnAuditJsonFile(
|
|
6102
|
+
return parseYarnAuditJsonFile(resolvedPath);
|
|
5790
6103
|
}
|
|
5791
6104
|
if (resolved === "sarif") {
|
|
5792
|
-
return parseSarifFile(
|
|
6105
|
+
return parseSarifFile(resolvedPath);
|
|
5793
6106
|
}
|
|
5794
6107
|
throw new Error(`Unsupported input format: ${resolved}`);
|
|
5795
6108
|
}
|
|
@@ -5826,10 +6139,21 @@ async function parseScanInputFromAudit(params) {
|
|
|
5826
6139
|
}
|
|
5827
6140
|
}
|
|
5828
6141
|
function defaultAuditFormat(pm) {
|
|
5829
|
-
|
|
6142
|
+
if (pm === "yarn") return "yarn-audit";
|
|
6143
|
+
if (pm === "deno") {
|
|
6144
|
+
throw new Error(
|
|
6145
|
+
"Deno does not support a native audit command. Use --input with a SARIF or npm-audit scan file instead."
|
|
6146
|
+
);
|
|
6147
|
+
}
|
|
6148
|
+
return "npm-audit";
|
|
5830
6149
|
}
|
|
5831
6150
|
function ensureAuditFormatCompatibility(pm, resolved) {
|
|
5832
6151
|
if (resolved === "sarif") return;
|
|
6152
|
+
if (pm === "deno") {
|
|
6153
|
+
throw new Error(
|
|
6154
|
+
"Deno does not support a native audit command. Use --input with a SARIF or npm-audit scan file instead."
|
|
6155
|
+
);
|
|
6156
|
+
}
|
|
5833
6157
|
if (pm === "yarn" && resolved !== "yarn-audit") {
|
|
5834
6158
|
throw new Error('Format "npm-audit" is not supported with package manager "yarn" in --audit mode. Use --format yarn-audit or --format auto.');
|
|
5835
6159
|
}
|
|
@@ -5841,7 +6165,7 @@ function inferFormat(filePath) {
|
|
|
5841
6165
|
const ext = extname2(filePath).toLowerCase();
|
|
5842
6166
|
if (ext === ".sarif") return "sarif";
|
|
5843
6167
|
try {
|
|
5844
|
-
const content =
|
|
6168
|
+
const content = readFileSync14(filePath, "utf8");
|
|
5845
6169
|
const firstLine = content.split("\n").find((line) => line.trim().startsWith("{"));
|
|
5846
6170
|
if (firstLine) {
|
|
5847
6171
|
const parsed = JSON.parse(firstLine);
|
|
@@ -6239,14 +6563,14 @@ async function remediatePortfolio(targets, options = {}) {
|
|
|
6239
6563
|
}
|
|
6240
6564
|
|
|
6241
6565
|
// src/api/update-outdated/index.ts
|
|
6242
|
-
import { join as
|
|
6243
|
-
import { readFileSync as
|
|
6566
|
+
import { join as join18 } from "path";
|
|
6567
|
+
import { readFileSync as readFileSync15, writeFileSync as writeFileSync6 } from "fs";
|
|
6244
6568
|
import { execa as execa13 } from "execa";
|
|
6245
6569
|
async function applyBump(params) {
|
|
6246
|
-
const pkgPath =
|
|
6570
|
+
const pkgPath = join18(params.cwd, "package.json");
|
|
6247
6571
|
let pkgJson;
|
|
6248
6572
|
try {
|
|
6249
|
-
pkgJson = JSON.parse(
|
|
6573
|
+
pkgJson = JSON.parse(readFileSync15(pkgPath, "utf8"));
|
|
6250
6574
|
} catch {
|
|
6251
6575
|
return {
|
|
6252
6576
|
applied: false,
|
|
@@ -6547,9 +6871,9 @@ function toSarifOutput(report) {
|
|
|
6547
6871
|
}
|
|
6548
6872
|
|
|
6549
6873
|
// src/version.ts
|
|
6550
|
-
import { readFileSync as
|
|
6874
|
+
import { readFileSync as readFileSync16 } from "fs";
|
|
6551
6875
|
function readPackageVersion() {
|
|
6552
|
-
const raw =
|
|
6876
|
+
const raw = readFileSync16(new URL("../package.json", import.meta.url), "utf8");
|
|
6553
6877
|
const metadata = JSON.parse(raw);
|
|
6554
6878
|
if (!metadata.version) {
|
|
6555
6879
|
throw new Error("packages/core/package.json is missing a version field.");
|
|
@@ -6577,4 +6901,3 @@ export {
|
|
|
6577
6901
|
updateOutdated,
|
|
6578
6902
|
PACKAGE_VERSION
|
|
6579
6903
|
};
|
|
6580
|
-
//# sourceMappingURL=chunk-4CVVRAQM.js.map
|