@node9/proxy 2.27.1 → 2.28.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.
@@ -1748,7 +1748,15 @@ function tuningToLegacyPolicy(policy, tuning) {
1748
1748
  const unknown = [];
1749
1749
  for (const [id, fields] of Object.entries(tuning)) {
1750
1750
  if (!fields || typeof fields !== "object") continue;
1751
- for (const [name, value] of Object.entries(fields)) {
1751
+ const normalized = { ...fields };
1752
+ if (id === "loading.malicious-package") {
1753
+ if (normalized.registrySignals === false) {
1754
+ normalized.newPackage ??= "off";
1755
+ normalized.installScript ??= "off";
1756
+ }
1757
+ delete normalized.registrySignals;
1758
+ }
1759
+ for (const [name, value] of Object.entries(normalized)) {
1752
1760
  const field = TUNING_FIELDS.find((f) => f.checkId === id && f.name === name);
1753
1761
  if (!field) {
1754
1762
  unknown.push(`${id}.${name}`);
@@ -4685,14 +4693,19 @@ var init_dist = __esm({
4685
4693
  { checkId: "loading.skill-tamper", name: "roots", legacy: ["skillPinning", "roots"] },
4686
4694
  {
4687
4695
  checkId: "loading.malicious-package",
4688
- name: "registrySignals",
4689
- legacy: ["packageCheck", "registrySignals"]
4696
+ name: "newPackage",
4697
+ legacy: ["packageCheck", "newPackage"]
4690
4698
  },
4691
4699
  {
4692
4700
  checkId: "loading.malicious-package",
4693
4701
  name: "maxAgeHours",
4694
4702
  legacy: ["packageCheck", "maxAgeHours"]
4695
4703
  },
4704
+ {
4705
+ checkId: "loading.malicious-package",
4706
+ name: "installScript",
4707
+ legacy: ["packageCheck", "installScript"]
4708
+ },
4696
4709
  {
4697
4710
  checkId: "loading.malicious-package",
4698
4711
  name: "onlineFallback",
@@ -5628,7 +5641,10 @@ var init_config_schema = __esm({
5628
5641
  packageCheck: z.object({
5629
5642
  enabled: z.boolean().optional(),
5630
5643
  onMalicious: z.enum(["block", "review"]).optional(),
5644
+ // 2.27.0 shape, still accepted: false = newPackage and installScript off.
5631
5645
  registrySignals: z.boolean().optional(),
5646
+ newPackage: z.enum(["review", "off"]).optional(),
5647
+ installScript: z.enum(["review", "off"]).optional(),
5632
5648
  maxAgeHours: z.number().min(0).optional(),
5633
5649
  onlineFallback: z.boolean().optional(),
5634
5650
  allow: z.array(z.string()).optional()
@@ -6398,7 +6414,13 @@ function getConfig(cwd) {
6398
6414
  if (typeof pc.enabled === "boolean") cur.enabled = pc.enabled;
6399
6415
  if (pc.onMalicious === "block" || pc.onMalicious === "review")
6400
6416
  cur.onMalicious = pc.onMalicious;
6401
- if (typeof pc.registrySignals === "boolean") cur.registrySignals = pc.registrySignals;
6417
+ if (pc.registrySignals === false) {
6418
+ cur.newPackage = "off";
6419
+ cur.installScript = "off";
6420
+ }
6421
+ if (pc.newPackage === "review" || pc.newPackage === "off") cur.newPackage = pc.newPackage;
6422
+ if (pc.installScript === "review" || pc.installScript === "off")
6423
+ cur.installScript = pc.installScript;
6402
6424
  if (typeof pc.maxAgeHours === "number" && pc.maxAgeHours >= 0)
6403
6425
  cur.maxAgeHours = pc.maxAgeHours;
6404
6426
  if (typeof pc.onlineFallback === "boolean") cur.onlineFallback = pc.onlineFallback;
@@ -6608,7 +6630,10 @@ function getConfig(cwd) {
6608
6630
  mergedPolicy.packageCheck = {
6609
6631
  enabled: typeof pc.enabled === "boolean" ? pc.enabled : cur.enabled,
6610
6632
  onMalicious: pc.onMalicious === "block" || pc.onMalicious === "review" ? pc.onMalicious : cur.onMalicious,
6611
- registrySignals: typeof pc.registrySignals === "boolean" ? pc.registrySignals : cur.registrySignals,
6633
+ // The 2.27.0 boolean still arrives from older dashboard columns:
6634
+ // false turns both signals off unless the new keys say otherwise.
6635
+ newPackage: pc.newPackage === "review" || pc.newPackage === "off" ? pc.newPackage : pc.registrySignals === false ? "off" : cur.newPackage,
6636
+ installScript: pc.installScript === "review" || pc.installScript === "off" ? pc.installScript : pc.registrySignals === false ? "off" : cur.installScript,
6612
6637
  maxAgeHours: typeof pc.maxAgeHours === "number" && Number.isFinite(pc.maxAgeHours) && pc.maxAgeHours >= 0 ? pc.maxAgeHours : cur.maxAgeHours,
6613
6638
  onlineFallback: typeof pc.onlineFallback === "boolean" ? pc.onlineFallback : cur.onlineFallback,
6614
6639
  allow: Array.isArray(pc.allow) ? pc.allow.filter((a) => typeof a === "string" && a.length > 0) : cur.allow
@@ -7122,8 +7147,9 @@ var init_config = __esm({
7122
7147
  packageCheck: {
7123
7148
  enabled: true,
7124
7149
  onMalicious: "block",
7125
- registrySignals: true,
7150
+ newPackage: "review",
7126
7151
  maxAgeHours: 48,
7152
+ installScript: "review",
7127
7153
  onlineFallback: true,
7128
7154
  allow: []
7129
7155
  },
package/dist/index.js CHANGED
@@ -379,9 +379,9 @@ function matchesPattern(text, patterns) {
379
379
  const withoutDotSlash = text.replace(/^\.\//, "");
380
380
  return isMatch(withoutDotSlash) || isMatch(`./${withoutDotSlash}`);
381
381
  }
382
- function getNestedValue(obj, path17) {
382
+ function getNestedValue(obj, path19) {
383
383
  if (!obj || typeof obj !== "object") return null;
384
- const segments2 = path17.split(".");
384
+ const segments2 = path19.split(".");
385
385
  for (const seg of segments2) {
386
386
  if (FORBIDDEN_PATH_SEGMENTS.has(seg)) return null;
387
387
  }
@@ -2191,14 +2191,54 @@ function pipInstall(words, from, manager, out) {
2191
2191
  if (v >= 0 && lower(words[v]) === "install")
2192
2192
  pushPy(out, manager, operands(words, v + 1, PIP_VALUE_FLAGS));
2193
2193
  }
2194
+ function localContexts(file) {
2195
+ const contexts = /* @__PURE__ */ new Map();
2196
+ const sample = parseShared("cd . && true");
2197
+ if (typeof sample === "symbol") return contexts;
2198
+ const andOp = sample.Stmts[0].Cmd.Op;
2199
+ function visit(stmt, steps, depth = 0) {
2200
+ if (depth > 64) return null;
2201
+ if (!stmt || stmt.Background || stmt.Negated || stmt.Redirs?.length) return null;
2202
+ const cmd = stmt.Cmd;
2203
+ const kind = syntax2.NodeType(cmd);
2204
+ if (kind === "BinaryCmd" && cmd.Op === andOp) {
2205
+ const next = visit(cmd.X, steps, depth + 1);
2206
+ return next === null ? null : visit(cmd.Y, next, depth + 1);
2207
+ }
2208
+ if (kind !== "CallExpr" || cmd.Assigns?.length) return null;
2209
+ const words = (cmd.Args ?? []).map((w) => resolveWordLiteral(w));
2210
+ if (words[0] === "cd" && words.length === 2 && typeof words[1] === "string" && words[1] && !/^[-~]/.test(words[1]))
2211
+ return [...steps, words[1]];
2212
+ if (!["npx", "npm", "bunx", "bun"].includes(words[0])) return null;
2213
+ if (words.some(
2214
+ (w) => w === null || /^(?:--(?:prefix|cwd|directory|workspace|workspaces|global|ignore-existing)(?:=|$)|-[Cwg])/.test(
2215
+ w
2216
+ )
2217
+ ))
2218
+ return null;
2219
+ contexts.set(cmd.Pos().Offset(), steps);
2220
+ return null;
2221
+ }
2222
+ if (file.Stmts?.length === 1) visit(file.Stmts[0], []);
2223
+ return contexts;
2224
+ }
2194
2225
  function collect(command, out, depth) {
2195
2226
  if (!command || command.length > 5e4) return;
2196
2227
  const f = parseShared(command);
2197
2228
  if (typeof f === "symbol") return;
2229
+ const contexts = depth === 0 ? localContexts(f) : /* @__PURE__ */ new Map();
2198
2230
  syntax2.Walk(f, (node) => {
2199
2231
  if (node && syntax2.NodeType(node) === "CallExpr") {
2200
2232
  const words = (node.Args || []).map((a) => resolveWordLiteral(a));
2233
+ const start = out.length;
2201
2234
  if (words.length > 0) fromCall(words, out, depth);
2235
+ const steps = contexts.get(node.Pos().Offset());
2236
+ for (let i = start; i < out.length; i++) {
2237
+ if (["npx", "bunx", "npm exec"].includes(out[i].manager)) {
2238
+ if (!steps) out[i].localCwd = null;
2239
+ else if (steps.length) out[i].localCwd = steps;
2240
+ }
2241
+ }
2202
2242
  }
2203
2243
  return true;
2204
2244
  });
@@ -2208,7 +2248,7 @@ function extractPackageInstalls(command) {
2208
2248
  collect(command, out, 0);
2209
2249
  const seen = /* @__PURE__ */ new Set();
2210
2250
  return out.filter((r) => {
2211
- const k = `${r.ecosystem}\0${r.name}\0${r.version ?? ""}`;
2251
+ const k = JSON.stringify([r.ecosystem, r.manager, r.name, r.raw, r.localCwd]);
2212
2252
  if (seen.has(k)) return false;
2213
2253
  seen.add(k);
2214
2254
  return true;
@@ -2641,7 +2681,15 @@ function tuningToLegacyPolicy(policy, tuning) {
2641
2681
  const unknown = [];
2642
2682
  for (const [id, fields] of Object.entries(tuning)) {
2643
2683
  if (!fields || typeof fields !== "object") continue;
2644
- for (const [name, value] of Object.entries(fields)) {
2684
+ const normalized = { ...fields };
2685
+ if (id === "loading.malicious-package") {
2686
+ if (normalized.registrySignals === false) {
2687
+ normalized.newPackage ??= "off";
2688
+ normalized.installScript ??= "off";
2689
+ }
2690
+ delete normalized.registrySignals;
2691
+ }
2692
+ for (const [name, value] of Object.entries(normalized)) {
2645
2693
  const field = TUNING_FIELDS.find((f) => f.checkId === id && f.name === name);
2646
2694
  if (!field) {
2647
2695
  unknown.push(`${id}.${name}`);
@@ -3000,9 +3048,9 @@ function bareToolName(toolName) {
3000
3048
  const parts = toolName.split("__");
3001
3049
  return (parts.length >= 3 ? parts.slice(2).join("__") : toolName).toLowerCase();
3002
3050
  }
3003
- function valuesAt(args, path17) {
3051
+ function valuesAt(args, path19) {
3004
3052
  let cursors = [args];
3005
- for (const rawSegment of path17.split(".")) {
3053
+ for (const rawSegment of path19.split(".")) {
3006
3054
  const isArray = rawSegment.endsWith("[]");
3007
3055
  const key = isArray ? rawSegment.slice(0, -2) : rawSegment;
3008
3056
  const next = [];
@@ -3034,8 +3082,8 @@ function extractToolDestinations(toolName, args) {
3034
3082
  const paths = DESTINATION_ARGS.get(bareToolName(toolName));
3035
3083
  if (!paths) return [];
3036
3084
  const out = [];
3037
- for (const path17 of paths) {
3038
- for (const value of valuesAt(args, path17)) {
3085
+ for (const path19 of paths) {
3086
+ for (const value of valuesAt(args, path19)) {
3039
3087
  const host = hostOf(value);
3040
3088
  if (host) out.push({ host, binary: toolName, raw: value });
3041
3089
  }
@@ -3049,15 +3097,15 @@ function ssrfDestinationFloor(toolName, args, opts = {}) {
3049
3097
  try {
3050
3098
  const paths = DESTINATION_ARGS.get(bareToolName(toolName));
3051
3099
  if (!paths) return null;
3052
- for (const path17 of paths) {
3053
- for (const value of valuesAt(args, path17)) {
3100
+ for (const path19 of paths) {
3101
+ for (const value of valuesAt(args, path19)) {
3054
3102
  const host = hostOf(value);
3055
3103
  if (!host) continue;
3056
3104
  const m = classifySsrf(host);
3057
3105
  if (!m) continue;
3058
3106
  if (isStrictGatedTier(m.tier) && !opts.ssrfStrict) continue;
3059
3107
  if (m.overridable && ssrfExemptMatches(opts.ssrfAllow, m.normalized)) continue;
3060
- return { ...m, argPath: path17, host, reason: ssrfReason(m, host) };
3108
+ return { ...m, argPath: path19, host, reason: ssrfReason(m, host) };
3061
3109
  }
3062
3110
  }
3063
3111
  return null;
@@ -6963,14 +7011,19 @@ var init_dist = __esm({
6963
7011
  { checkId: "loading.skill-tamper", name: "roots", legacy: ["skillPinning", "roots"] },
6964
7012
  {
6965
7013
  checkId: "loading.malicious-package",
6966
- name: "registrySignals",
6967
- legacy: ["packageCheck", "registrySignals"]
7014
+ name: "newPackage",
7015
+ legacy: ["packageCheck", "newPackage"]
6968
7016
  },
6969
7017
  {
6970
7018
  checkId: "loading.malicious-package",
6971
7019
  name: "maxAgeHours",
6972
7020
  legacy: ["packageCheck", "maxAgeHours"]
6973
7021
  },
7022
+ {
7023
+ checkId: "loading.malicious-package",
7024
+ name: "installScript",
7025
+ legacy: ["packageCheck", "installScript"]
7026
+ },
6974
7027
  {
6975
7028
  checkId: "loading.malicious-package",
6976
7029
  name: "onlineFallback",
@@ -7253,7 +7306,8 @@ __export(audit_exports, {
7253
7306
  buildArgsPreview: () => buildArgsPreview,
7254
7307
  filePathFromArgs: () => filePathFromArgs,
7255
7308
  generateEventId: () => generateEventId,
7256
- redactSecrets: () => redactSecrets
7309
+ redactSecrets: () => redactSecrets,
7310
+ truncateAuditPreview: () => truncateAuditPreview
7257
7311
  });
7258
7312
  function generateEventId() {
7259
7313
  return `${Date.now().toString(36)}-${import_crypto4.default.randomBytes(6).toString("hex")}`;
@@ -7282,11 +7336,14 @@ function buildArgsPreview(args) {
7282
7336
  const primary = o && (o.command ?? o.file_path ?? o.path ?? o.url ?? o.query);
7283
7337
  const text = typeof primary === "string" ? primary : args ? JSON.stringify(args) : "";
7284
7338
  if (!text) return void 0;
7285
- return redactSecrets(text).slice(0, 120);
7339
+ return truncateAuditPreview(redactSecrets(text));
7286
7340
  } catch {
7287
7341
  return void 0;
7288
7342
  }
7289
7343
  }
7344
+ function truncateAuditPreview(text) {
7345
+ return text.slice(0, 120).replace(/[\uD800-\uDBFF]$/, "");
7346
+ }
7290
7347
  function appendToLog(logPath, entry) {
7291
7348
  try {
7292
7349
  const dir = import_path.default.dirname(logPath);
@@ -7580,7 +7637,10 @@ var ConfigFileSchema = import_zod.z.object({
7580
7637
  packageCheck: import_zod.z.object({
7581
7638
  enabled: import_zod.z.boolean().optional(),
7582
7639
  onMalicious: import_zod.z.enum(["block", "review"]).optional(),
7640
+ // 2.27.0 shape, still accepted: false = newPackage and installScript off.
7583
7641
  registrySignals: import_zod.z.boolean().optional(),
7642
+ newPackage: import_zod.z.enum(["review", "off"]).optional(),
7643
+ installScript: import_zod.z.enum(["review", "off"]).optional(),
7584
7644
  maxAgeHours: import_zod.z.number().min(0).optional(),
7585
7645
  onlineFallback: import_zod.z.boolean().optional(),
7586
7646
  allow: import_zod.z.array(import_zod.z.string()).optional()
@@ -7595,8 +7655,8 @@ var ConfigFileSchema = import_zod.z.object({
7595
7655
  }).strict();
7596
7656
  function formatIssues(issues) {
7597
7657
  const lines = issues.map((issue) => {
7598
- const path17 = issue.path.length > 0 ? issue.path.map(String).join(".") : "root";
7599
- return ` \u2022 ${path17}: ${issue.message}`;
7658
+ const path19 = issue.path.length > 0 ? issue.path.map(String).join(".") : "root";
7659
+ return ` \u2022 ${path19}: ${issue.message}`;
7600
7660
  });
7601
7661
  return `Invalid config:
7602
7662
  ${lines.join("\n")}`;
@@ -7609,9 +7669,9 @@ function prunePaths(root, paths) {
7609
7669
  const yi = y[y.length - 1];
7610
7670
  return typeof xi === "number" && typeof yi === "number" ? yi - xi : 0;
7611
7671
  });
7612
- for (const path17 of ordered) {
7672
+ for (const path19 of ordered) {
7613
7673
  let cur = root;
7614
- for (const key of path17.slice(0, -1)) {
7674
+ for (const key of path19.slice(0, -1)) {
7615
7675
  if (cur === null || typeof cur !== "object") {
7616
7676
  cur = void 0;
7617
7677
  break;
@@ -7619,7 +7679,7 @@ function prunePaths(root, paths) {
7619
7679
  cur = cur[key];
7620
7680
  }
7621
7681
  if (cur === null || typeof cur !== "object") continue;
7622
- const last = path17[path17.length - 1];
7682
+ const last = path19[path19.length - 1];
7623
7683
  if (Array.isArray(cur)) {
7624
7684
  const i = Number(last);
7625
7685
  if (Number.isInteger(i) && i >= 0 && i < cur.length) {
@@ -7655,7 +7715,7 @@ function sanitizeConfig(raw) {
7655
7715
  return issue.keys.map((k) => [...at, k]);
7656
7716
  }
7657
7717
  return [at.slice(0, -level || void 0)];
7658
- }).filter((path17) => path17.length > 0);
7718
+ }).filter((path19) => path19.length > 0);
7659
7719
  if (paths.length === 0 || !prunePaths(working, paths)) break;
7660
7720
  }
7661
7721
  if (ConfigFileSchema.safeParse(working).success) break;
@@ -8391,8 +8451,9 @@ var DEFAULT_CONFIG = {
8391
8451
  packageCheck: {
8392
8452
  enabled: true,
8393
8453
  onMalicious: "block",
8394
- registrySignals: true,
8454
+ newPackage: "review",
8395
8455
  maxAgeHours: 48,
8456
+ installScript: "review",
8396
8457
  onlineFallback: true,
8397
8458
  allow: []
8398
8459
  },
@@ -8754,7 +8815,13 @@ function getConfig(cwd) {
8754
8815
  if (typeof pc.enabled === "boolean") cur.enabled = pc.enabled;
8755
8816
  if (pc.onMalicious === "block" || pc.onMalicious === "review")
8756
8817
  cur.onMalicious = pc.onMalicious;
8757
- if (typeof pc.registrySignals === "boolean") cur.registrySignals = pc.registrySignals;
8818
+ if (pc.registrySignals === false) {
8819
+ cur.newPackage = "off";
8820
+ cur.installScript = "off";
8821
+ }
8822
+ if (pc.newPackage === "review" || pc.newPackage === "off") cur.newPackage = pc.newPackage;
8823
+ if (pc.installScript === "review" || pc.installScript === "off")
8824
+ cur.installScript = pc.installScript;
8758
8825
  if (typeof pc.maxAgeHours === "number" && pc.maxAgeHours >= 0)
8759
8826
  cur.maxAgeHours = pc.maxAgeHours;
8760
8827
  if (typeof pc.onlineFallback === "boolean") cur.onlineFallback = pc.onlineFallback;
@@ -8964,7 +9031,10 @@ function getConfig(cwd) {
8964
9031
  mergedPolicy.packageCheck = {
8965
9032
  enabled: typeof pc.enabled === "boolean" ? pc.enabled : cur.enabled,
8966
9033
  onMalicious: pc.onMalicious === "block" || pc.onMalicious === "review" ? pc.onMalicious : cur.onMalicious,
8967
- registrySignals: typeof pc.registrySignals === "boolean" ? pc.registrySignals : cur.registrySignals,
9034
+ // The 2.27.0 boolean still arrives from older dashboard columns:
9035
+ // false turns both signals off unless the new keys say otherwise.
9036
+ newPackage: pc.newPackage === "review" || pc.newPackage === "off" ? pc.newPackage : pc.registrySignals === false ? "off" : cur.newPackage,
9037
+ installScript: pc.installScript === "review" || pc.installScript === "off" ? pc.installScript : pc.registrySignals === false ? "off" : cur.installScript,
8968
9038
  maxAgeHours: typeof pc.maxAgeHours === "number" && Number.isFinite(pc.maxAgeHours) && pc.maxAgeHours >= 0 ? pc.maxAgeHours : cur.maxAgeHours,
8969
9039
  onlineFallback: typeof pc.onlineFallback === "boolean" ? pc.onlineFallback : cur.onlineFallback,
8970
9040
  allow: Array.isArray(pc.allow) ? pc.allow.filter((a) => typeof a === "string" && a.length > 0) : cur.allow
@@ -8990,13 +9060,13 @@ function getConfig(cwd) {
8990
9060
  }
8991
9061
  if (Array.isArray(mc.jailPaths)) {
8992
9062
  for (const jp of mc.jailPaths) {
8993
- const path17 = typeof jp?.path === "string" ? jp.path.trim() : "";
8994
- if (!path17) continue;
9063
+ const path19 = typeof jp?.path === "string" ? jp.path.trim() : "";
9064
+ if (!path19) continue;
8995
9065
  const verdict = jp?.verdict === "review" ? "review" : "block";
8996
- for (const r of pathRules(path17, verdict, "org-managed jail")) {
9066
+ for (const r of pathRules(path19, verdict, "org-managed jail")) {
8997
9067
  mergedPolicy.smartRules.push({ ...r, name: `org:${r.name}` });
8998
9068
  }
8999
- mergedPolicy.managedJailPaths.push({ path: path17, verdict });
9069
+ mergedPolicy.managedJailPaths.push({ path: path19, verdict });
9000
9070
  }
9001
9071
  }
9002
9072
  if (Array.isArray(mc.trustedHosts)) {
@@ -10392,6 +10462,7 @@ function recordAndCheck(tool, args, threshold = 3, windowMs = 12e4) {
10392
10462
 
10393
10463
  // src/supply-chain/check.ts
10394
10464
  var import_picomatch2 = __toESM(require("picomatch"));
10465
+ var import_path18 = __toESM(require("path"));
10395
10466
  init_dist();
10396
10467
 
10397
10468
  // src/supply-chain/osv-index.ts
@@ -10553,30 +10624,58 @@ async function queryOsvMalicious(queries) {
10553
10624
  }
10554
10625
 
10555
10626
  // src/supply-chain/registry.ts
10627
+ var import_semver = require("semver");
10556
10628
  var TIMEOUT_MS2 = 1500;
10557
10629
  var MAX_BYTES = 2 * 1024 * 1024;
10558
- async function npmInfo(name, version) {
10630
+ async function npmInfo(name, version, freshWindowMs, raw) {
10559
10631
  const base = endpoints().npmRegistry;
10560
10632
  if (!base) return null;
10561
- const doc = await fetchJson(`${base}/${name.replaceAll("/", "%2F")}`, {
10633
+ const url = `${base}/${name.replaceAll("/", "%2F")}`;
10634
+ const doc = await fetchJson(url, {
10562
10635
  timeoutMs: TIMEOUT_MS2,
10563
10636
  maxBytes: MAX_BYTES,
10564
10637
  headers: { Accept: "application/vnd.npm.install-v1+json" }
10565
10638
  });
10566
10639
  if (!doc) return null;
10567
10640
  const latest = doc["dist-tags"]?.latest;
10568
- const resolved = version ?? latest;
10641
+ const aliasStart = raw?.indexOf("@npm:") ?? -1;
10642
+ const target = aliasStart > 0 ? raw.slice(aliasStart + 5) : raw?.replace(/^npm:/, "");
10643
+ const specStart = target?.indexOf("@", 1) ?? -1;
10644
+ const spec = specStart > 0 ? target.slice(specStart + 1) : "";
10645
+ let resolved = version;
10646
+ if (!resolved) {
10647
+ if (!spec) resolved = latest;
10648
+ else if (doc["dist-tags"]?.[spec]) resolved = doc["dist-tags"][spec];
10649
+ else if ((0, import_semver.validRange)(spec)) {
10650
+ resolved = latest && (0, import_semver.satisfies)(latest, spec) ? latest : (0, import_semver.maxSatisfying)(Object.keys(doc.versions ?? {}), spec) ?? void 0;
10651
+ }
10652
+ }
10569
10653
  if (!resolved) return null;
10570
10654
  const v = doc.versions?.[resolved];
10571
10655
  const modifiedMs = doc.modified ? Date.parse(doc.modified) : NaN;
10572
- return {
10656
+ const info = {
10573
10657
  version: resolved,
10574
- // `modified` bounds the publish time of EVERY version from above, but it
10575
- // only approximates the age of the newest one: an older pinned version
10576
- // gets no age (it is at least as old as `latest`, so never "new").
10577
- publishedAtMs: Number.isFinite(modifiedMs) && resolved === latest ? modifiedMs : void 0,
10578
10658
  hasInstallScript: v?.hasInstallScript === true
10579
10659
  };
10660
+ if (!Number.isFinite(modifiedMs)) return info;
10661
+ if (Date.now() - modifiedMs >= freshWindowMs) {
10662
+ info.publishedAtMs = resolved === latest ? modifiedMs : void 0;
10663
+ return info;
10664
+ }
10665
+ try {
10666
+ const full = await fetchJson(url, { timeoutMs: TIMEOUT_MS2, maxBytes: MAX_BYTES });
10667
+ const t = full?.time?.[resolved];
10668
+ const tMs = t ? Date.parse(t) : NaN;
10669
+ if (Number.isFinite(tMs)) {
10670
+ info.publishedAtMs = tMs;
10671
+ return info;
10672
+ }
10673
+ info.ageFallback = `no publish time for ${resolved} in the full document`;
10674
+ } catch (err) {
10675
+ info.ageFallback = `full document ${err.message}`;
10676
+ }
10677
+ info.publishedAtMs = modifiedMs;
10678
+ return info;
10580
10679
  }
10581
10680
  async function pypiInfo(name, version) {
10582
10681
  const base = endpoints().pypi;
@@ -10591,8 +10690,36 @@ async function pypiInfo(name, version) {
10591
10690
  publishedAtMs: times.length > 0 ? Math.min(...times) : void 0
10592
10691
  };
10593
10692
  }
10594
- async function registryInfo(eco, name, version) {
10595
- return eco === "npm" ? npmInfo(name, version) : pypiInfo(name, version);
10693
+ async function registryInfo(eco, name, version, freshWindowMs, raw) {
10694
+ return eco === "npm" ? npmInfo(name, version, freshWindowMs, raw) : pypiInfo(name, version);
10695
+ }
10696
+
10697
+ // src/supply-chain/local-resolve.ts
10698
+ var import_fs16 = __toESM(require("fs"));
10699
+ var import_path17 = __toESM(require("path"));
10700
+ var MAX_LEVELS = 32;
10701
+ var LOCAL_FIRST_MANAGERS = /* @__PURE__ */ new Set(["npx", "bunx", "npm exec"]);
10702
+ function resolveInstalled(name, cwd) {
10703
+ if (!cwd || !import_path17.default.isAbsolute(cwd)) return null;
10704
+ if (!/^(?:@[^/\\]+\/)?[^/\\]+$/.test(name)) return null;
10705
+ let dir = import_path17.default.resolve(cwd);
10706
+ for (let i = 0; i < MAX_LEVELS; i++) {
10707
+ const pkgDir = import_path17.default.join(dir, "node_modules", ...name.split("/"));
10708
+ const manifest = import_path17.default.join(pkgDir, "package.json");
10709
+ try {
10710
+ const raw = import_fs16.default.readFileSync(manifest, "utf8");
10711
+ const parsed = JSON.parse(raw);
10712
+ if (typeof parsed.version === "string" && parsed.version.length > 0) {
10713
+ return { version: parsed.version, dir: pkgDir };
10714
+ }
10715
+ return null;
10716
+ } catch {
10717
+ }
10718
+ const parent = import_path17.default.dirname(dir);
10719
+ if (parent === dir) break;
10720
+ dir = parent;
10721
+ }
10722
+ return null;
10596
10723
  }
10597
10724
 
10598
10725
  // src/supply-chain/check.ts
@@ -10663,13 +10790,45 @@ async function maliciousFor(p, version, cfg, misses, networkCapped) {
10663
10790
  }
10664
10791
  return null;
10665
10792
  }
10666
- async function checkOne(p, cfg, misses, networkCapped) {
10793
+ function checkInstalled(p, version, misses) {
10794
+ const local = lookupIndex(p.ecosystem, p.name);
10795
+ if (local.status === "unavailable") {
10796
+ misses.push(`${label(p, version)}: installed copy, local index unavailable`);
10797
+ return [];
10798
+ }
10799
+ if (local.status !== "hit") return [];
10800
+ const covering = local.entries.filter((e) => entryCovers(e, version) === true).map((e) => e.id);
10801
+ if (covering.length === 0) return [];
10802
+ return [
10803
+ {
10804
+ pkg: p,
10805
+ version,
10806
+ installed: true,
10807
+ kind: "malicious",
10808
+ advisories: covering,
10809
+ detail: `installed ${label(p, version)} (${covering.join(", ")})`
10810
+ }
10811
+ ];
10812
+ }
10813
+ async function checkOne(p, cfg, misses, networkCapped, cwd) {
10814
+ if (p.ecosystem === "npm" && LOCAL_FIRST_MANAGERS.has(p.manager) && p.localCwd !== null && (p.raw === p.name || p.version)) {
10815
+ const localCwd = cwd && import_path18.default.isAbsolute(cwd) ? (p.localCwd ?? []).reduce((dir, step) => import_path18.default.resolve(dir, step), cwd) : void 0;
10816
+ const installed = resolveInstalled(p.name, localCwd);
10817
+ if (installed && (!p.version || p.version === installed.version))
10818
+ return checkInstalled(p, installed.version, misses);
10819
+ }
10667
10820
  const findings = [];
10668
10821
  let info = null;
10669
- if (cfg.registrySignals && !networkCapped) {
10822
+ const wantsRegistry = cfg.newPackage !== "off" || cfg.installScript !== "off";
10823
+ if (wantsRegistry && !networkCapped) {
10670
10824
  try {
10671
- info = await registryInfo(p.ecosystem, p.name, p.version);
10825
+ const windowMs = cfg.newPackage === "off" ? 0 : cfg.maxAgeHours * 36e5;
10826
+ info = await registryInfo(p.ecosystem, p.name, p.version, windowMs, p.raw);
10672
10827
  if (!info) misses.push(`${label(p, p.version)}: registry metadata unavailable`);
10828
+ else if (info.ageFallback)
10829
+ misses.push(
10830
+ `${label(p, info.version)}: age from the package modified time (${info.ageFallback})`
10831
+ );
10673
10832
  } catch (err) {
10674
10833
  misses.push(`${label(p, p.version)}: registry ${err.message}`);
10675
10834
  }
@@ -10677,18 +10836,18 @@ async function checkOne(p, cfg, misses, networkCapped) {
10677
10836
  const version = p.version ?? info?.version;
10678
10837
  const mal = await maliciousFor(p, version, cfg, misses, networkCapped);
10679
10838
  if (mal) findings.push(mal);
10680
- if (info?.publishedAtMs !== void 0) {
10839
+ if (cfg.newPackage !== "off" && info?.publishedAtMs !== void 0) {
10681
10840
  const ageH = (Date.now() - info.publishedAtMs) / 36e5;
10682
10841
  if (ageH >= 0 && ageH < cfg.maxAgeHours) {
10683
10842
  findings.push({
10684
10843
  pkg: p,
10685
10844
  version,
10686
10845
  kind: "new",
10687
- detail: `${label(p, version)} was published ${Math.max(1, Math.round(ageH))}h ago (under ${cfg.maxAgeHours}h)`
10846
+ detail: info.ageFallback ? `${label(p, version)} has package metadata updated ${Math.max(1, Math.round(ageH))}h ago (under ${cfg.maxAgeHours}h); version publish time unavailable` : `${label(p, version)} was published ${Math.max(1, Math.round(ageH))}h ago (under ${cfg.maxAgeHours}h)`
10688
10847
  });
10689
10848
  }
10690
10849
  }
10691
- if (info?.hasInstallScript) {
10850
+ if (cfg.installScript !== "off" && info?.hasInstallScript) {
10692
10851
  findings.push({
10693
10852
  pkg: p,
10694
10853
  version,
@@ -10698,7 +10857,7 @@ async function checkOne(p, cfg, misses, networkCapped) {
10698
10857
  }
10699
10858
  return findings;
10700
10859
  }
10701
- async function runPackageCheck(toolName, args, cfg) {
10860
+ async function runPackageCheck(toolName, args, cfg, cwd) {
10702
10861
  if (!cfg.enabled) return ALLOW;
10703
10862
  try {
10704
10863
  const command = commandOf(toolName, args);
@@ -10709,7 +10868,7 @@ async function runPackageCheck(toolName, args, cfg) {
10709
10868
  const misses = [];
10710
10869
  const checked = requests;
10711
10870
  const settled = await Promise.allSettled(
10712
- checked.map((p, i) => checkOne(p, cfg, misses, i >= MAX_NETWORK_PACKAGES))
10871
+ checked.map((p, i) => checkOne(p, cfg, misses, i >= MAX_NETWORK_PACKAGES, cwd))
10713
10872
  );
10714
10873
  const findings = [];
10715
10874
  settled.forEach((s, i) => {
@@ -11117,7 +11276,7 @@ async function _authorizeHeadlessCore(toolName, args, metaArg, options) {
11117
11276
  }
11118
11277
  const packageCheck = config.policy.packageCheck;
11119
11278
  if (packageCheck?.enabled && !isManual) {
11120
- const pkg = await runPackageCheck(toolName, args, packageCheck);
11279
+ const pkg = await runPackageCheck(toolName, args, packageCheck, options?.cwd);
11121
11280
  if (pkg.misses.length > 0) {
11122
11281
  appendToLog(HOOK_DEBUG_LOG, {
11123
11282
  ts: (/* @__PURE__ */ new Date()).toISOString(),