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.
@@ -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 join3 } from "path";
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 join2 } from "path";
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(join(cwd, "pnpm-lock.yaml"))) return "pnpm";
376
- if (existsSync(join(cwd, "yarn.lock"))) return "yarn";
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
- const base = pm === "pnpm" ? ["pnpm", "list", "--json", "--depth", "99"] : pm === "yarn" ? ["yarn", "list", "--json"] : ["npm", "list", "--json", "--all"];
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
- return isAbsolute(patchFilePath) ? patchFilePath : resolve(cwd, patchFilePath);
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
- return JSON.parse(raw);
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) => join3(patchesDirPath, entry.name)).sort((left, right) => left.localeCompare(right));
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 readFileSync3 } from "fs";
620
- import { join as join5 } from "path";
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 readFileSync2 } from "fs";
625
- import { join as join4 } from "path";
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 ?? join4(cwd, ".github", "autoremediator.yml");
780
+ const candidate = explicitPath ?? join5(cwd, ".github", "autoremediator.yml");
657
781
  if (!existsSync5(candidate)) return DEFAULT_POLICY;
658
782
  try {
659
- const parsed = yamlParse(readFileSync2(candidate, "utf8"));
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 = readFileSync2(filePath, "utf8");
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(readFileSync3(join5(cwd, "package.json"), "utf8"));
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
- try {
773
- const [cmd, ...args] = listCommand;
774
- const listResult = await execa3(cmd, args, {
775
- cwd,
776
- stdio: "pipe",
777
- reject: false
778
- });
779
- installedVersions = parseListOutput(pm, listResult.stdout || "");
780
- } catch {
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 readFileSync4, writeFileSync as writeFileSync2 } from "fs";
1020
- import { join as join6 } from "path";
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 join6(cwd, ".autoremediator", "state", "idempotency.json");
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(readFileSync4(filePath, "utf8"));
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(join6(cwd, ".autoremediator", "state"), { recursive: true });
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 readFileSync5, statSync } from "fs";
1335
- import { join as join7, extname } from "path";
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 = join7(dir, entry);
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 = readFileSync5(filePath, "utf8");
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 readFileSync6 } from "fs";
1539
- import { join as join8 } from "path";
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(readFileSync6(join8(cwd, "package.json"), "utf8"));
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 join10 } from "path";
1739
- import { readFileSync as readFileSync7, writeFileSync as writeFileSync3 } from "fs";
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 join9 } from "path";
1888
+ import { join as join10 } from "path";
1746
1889
  async function sleep(ms) {
1747
- await new Promise((resolve2) => setTimeout(resolve2, ms));
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 = join9(cwd, ".autoremediator", "locks");
1753
- const lockPath = join9(cwd, ".autoremediator", "locks", "remediation.lock");
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 = join10(cwd, "package.json");
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(readFileSync7(pkgPath, "utf8"));
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 join11 } from "path";
1979
- import { readFileSync as readFileSync8, writeFileSync as writeFileSync4 } from "fs";
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") return pkgJson.overrides?.[packageName];
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, or yarn resolutions.",
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 = join11(cwd, "package.json");
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(readFileSync8(pkgPath, "utf8"));
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
- try {
2209
- const [dedupeCmd, ...dedupeArgs] = dedupeCommand;
2210
- await execa7(dedupeCmd, dedupeArgs, { cwd, stdio: "pipe" });
2211
- } catch (err) {
2212
- const message = err instanceof Error ? err.message : String(err);
2213
- dedupeNote = ` Dedupe warning: ${dedupeCommand.join(" ")} failed (${message}).`;
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 join12 } from "path";
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
- const tempBaseDir = `/tmp/autoremediator-pkg-${Date.now()}`;
2389
- const extractDir = join12(tempBaseDir, "out");
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 npmUrl = `https://registry.npmjs.org/${packageName}/-/${packageName.split("/").pop()}-${version}.tgz`;
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 = join12(tempBaseDir, "package.tgz");
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") ? join12(extractDir, "package") : extractDir;
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 = join12(dir, file.name);
2405
- const relPath = join12(relativeBase, file.name);
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 = filePatterns.some((pattern) => {
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 [${filePatterns.join(", ")}] found in ${packageName}@${version}. Download succeeded but extraction yielded no matching files.`
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 join14 } from "path";
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 existsSync7 } from "fs";
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 join13 } from "path";
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 = join13(cwd, "package.json");
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 = join13(cwd, "package.json");
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 mkdtemp(join13(tmpdir(), "autoremediator-native-patch-"));
2931
- const tempPatchFile = join13(tempPatchDir, "change.patch");
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 (existsSync7(line)) {
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("/") && existsSync7(token)) {
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 = join14(cwd, patchesDir, patchFileName);
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 = join14(cwd, patchesDir);
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 existsSync8, readFileSync as readFileSync9 } from "fs";
4111
- import { join as join15 } from "path";
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 = join15(process.cwd(), ".github", "instructions", "orchestration.instructions.md");
4123
- if (!existsSync8(promptPath)) {
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 = readFileSync9(promptPath, "utf8");
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}-${Date.now()}`;
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 = options.bodyFooter ? `${plan.body}
5239
+ const body = bodyFooter ? `${plan.body}
4943
5240
 
4944
- ${options.bodyFooter}` : plan.body;
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 join16 } from "path";
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 = join16(cwd, ".autoremediator", "evidence");
5387
+ const dir = join17(cwd, ".autoremediator", "evidence");
5085
5388
  mkdirSync3(dir, { recursive: true });
5086
- const filePath = join16(dir, `${log.runId}.json`);
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 readFileSync13 } from "fs";
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 readFileSync10 } from "fs";
5660
- var CVE_REGEX = /CVE-\d{4}-\d+/gi;
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 = readFileSync10(filePath, "utf8");
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 readFileSync11 } from "fs";
5699
- var CVE_REGEX2 = /CVE-\d{4}-\d+/gi;
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 = readFileSync11(filePath, "utf8");
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 readFileSync12 } from "fs";
5747
- var CVE_REGEX3 = /CVE-\d{4}-\d+/gi;
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
- for (const run of report.runs ?? []) {
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
- const combined = `${result.ruleId ?? ""} ${result.message?.text ?? ""}`;
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 = readFileSync12(filePath, "utf8");
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
- const resolved = format === "auto" ? inferFormat(filePath) : format;
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(filePath);
6099
+ return parseNpmAuditJsonFile(resolvedPath);
5787
6100
  }
5788
6101
  if (resolved === "yarn-audit") {
5789
- return parseYarnAuditJsonFile(filePath);
6102
+ return parseYarnAuditJsonFile(resolvedPath);
5790
6103
  }
5791
6104
  if (resolved === "sarif") {
5792
- return parseSarifFile(filePath);
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
- return pm === "yarn" ? "yarn-audit" : "npm-audit";
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 = readFileSync13(filePath, "utf8");
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 join17 } from "path";
6243
- import { readFileSync as readFileSync14, writeFileSync as writeFileSync6 } from "fs";
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 = join17(params.cwd, "package.json");
6570
+ const pkgPath = join18(params.cwd, "package.json");
6247
6571
  let pkgJson;
6248
6572
  try {
6249
- pkgJson = JSON.parse(readFileSync14(pkgPath, "utf8"));
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 readFileSync15 } from "fs";
6874
+ import { readFileSync as readFileSync16 } from "fs";
6551
6875
  function readPackageVersion() {
6552
- const raw = readFileSync15(new URL("../package.json", import.meta.url), "utf8");
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