@node9/proxy 2.14.2 → 2.14.3

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.
@@ -4660,6 +4660,51 @@ var init_trusted_hosts = __esm({
4660
4660
  }
4661
4661
  });
4662
4662
 
4663
+ // src/auth/api-url.ts
4664
+ function defaultHostSuffixes() {
4665
+ const host = new URL(DEFAULT_API_URL).hostname.toLowerCase();
4666
+ const parent = host.split(".").slice(1).join(".");
4667
+ return parent.includes(".") ? [host, parent] : [host];
4668
+ }
4669
+ function allowedSuffixes() {
4670
+ const extra = (process.env[HOST_ALLOW_ENV] ?? "").split(",").map(
4671
+ (s) => s.trim().toLowerCase().replace(/^\*?\./, "")
4672
+ ).filter(Boolean);
4673
+ return [...defaultHostSuffixes(), ...extra];
4674
+ }
4675
+ function validateApiUrl(raw) {
4676
+ if (typeof raw !== "string" || raw.length === 0 || raw.length > 2048) return null;
4677
+ let u;
4678
+ try {
4679
+ u = new URL(raw);
4680
+ } catch {
4681
+ return null;
4682
+ }
4683
+ if (u.username || u.password) return null;
4684
+ if (u.protocol !== "https:" && u.protocol !== "http:") return null;
4685
+ const host = u.hostname.toLowerCase();
4686
+ if (LOOPBACK.has(host)) return u;
4687
+ if (u.protocol !== "https:") return null;
4688
+ const suffixes = allowedSuffixes();
4689
+ const ok = suffixes.some((s) => host === s || host.endsWith("." + s));
4690
+ return ok ? u : null;
4691
+ }
4692
+ function safeApiUrl(raw, onReject) {
4693
+ const ok = validateApiUrl(raw);
4694
+ if (ok) return typeof raw === "string" ? raw : ok.toString();
4695
+ onReject?.(raw);
4696
+ return DEFAULT_API_URL;
4697
+ }
4698
+ var DEFAULT_API_URL, LOOPBACK, HOST_ALLOW_ENV;
4699
+ var init_api_url = __esm({
4700
+ "src/auth/api-url.ts"() {
4701
+ "use strict";
4702
+ DEFAULT_API_URL = "https://api.node9.ai/api/v1/intercept";
4703
+ LOOPBACK = /* @__PURE__ */ new Set(["127.0.0.1", "localhost", "::1", "[::1]", "0.0.0.0"]);
4704
+ HOST_ALLOW_ENV = "NODE9_API_HOST_ALLOW";
4705
+ }
4706
+ });
4707
+
4663
4708
  // src/config/index.ts
4664
4709
  import fs6 from "fs";
4665
4710
  import path7 from "path";
@@ -4678,12 +4723,26 @@ function sanitizeSsrfAllow(entries, source) {
4678
4723
  }
4679
4724
  return kept;
4680
4725
  }
4726
+ function noteRejectedApiUrl(raw) {
4727
+ const key = String(raw).slice(0, 200);
4728
+ if (rejectedApiUrlsSeen.has(key)) return;
4729
+ rejectedApiUrlsSeen.add(key);
4730
+ try {
4731
+ fs6.appendFileSync(
4732
+ path7.join(os7.homedir(), ".node9", "hook-debug.log"),
4733
+ `[${(/* @__PURE__ */ new Date()).toISOString()}] REFUSED apiUrl, using the default instead: ${String(raw).slice(0, 200).replace(/[\r\n]+/g, " ")}
4734
+ `
4735
+ );
4736
+ } catch {
4737
+ }
4738
+ }
4681
4739
  function getCredentials() {
4682
- const DEFAULT_API_URL = "https://api.node9.ai/api/v1/intercept";
4683
4740
  if (process.env.NODE9_API_KEY) {
4684
4741
  return {
4685
4742
  apiKey: process.env.NODE9_API_KEY,
4686
- apiUrl: process.env.NODE9_API_URL || DEFAULT_API_URL
4743
+ // NODE9_API_URL is env, and a hook inherits the agent's env, so it is
4744
+ // no more trusted than the file below.
4745
+ apiUrl: safeApiUrl(process.env.NODE9_API_URL || DEFAULT_API_URL, noteRejectedApiUrl)
4687
4746
  };
4688
4747
  }
4689
4748
  try {
@@ -4695,14 +4754,14 @@ function getCredentials() {
4695
4754
  if (profile?.apiKey) {
4696
4755
  return {
4697
4756
  apiKey: profile.apiKey,
4698
- apiUrl: profile.apiUrl || DEFAULT_API_URL,
4757
+ apiUrl: safeApiUrl(profile.apiUrl || DEFAULT_API_URL, noteRejectedApiUrl),
4699
4758
  localOnly: profile.localOnly === true || profileName !== "default"
4700
4759
  };
4701
4760
  }
4702
4761
  if (creds.apiKey) {
4703
4762
  return {
4704
4763
  apiKey: creds.apiKey,
4705
- apiUrl: creds.apiUrl || DEFAULT_API_URL,
4764
+ apiUrl: safeApiUrl(creds.apiUrl || DEFAULT_API_URL, noteRejectedApiUrl),
4706
4765
  localOnly: creds.localOnly === true
4707
4766
  };
4708
4767
  }
@@ -5332,7 +5391,7 @@ ${error.replace("Invalid config:\n", "")}
5332
5391
  }
5333
5392
  return sanitized;
5334
5393
  }
5335
- var DANGEROUS_WORDS, DEFAULT_CONFIG, RM_SAFE_PATH_PATTERN, VERDICT_ORDER, ADVISORY_SMART_RULES, cachedConfig, lastParsedRulesCache, CACHE_LOG_REARM_MS, cacheReadLastLoggedAt;
5394
+ var DANGEROUS_WORDS, DEFAULT_CONFIG, RM_SAFE_PATH_PATTERN, VERDICT_ORDER, ADVISORY_SMART_RULES, cachedConfig, rejectedApiUrlsSeen, lastParsedRulesCache, CACHE_LOG_REARM_MS, cacheReadLastLoggedAt;
5336
5395
  var init_config = __esm({
5337
5396
  "src/config/index.ts"() {
5338
5397
  "use strict";
@@ -5343,6 +5402,7 @@ var init_config = __esm({
5343
5402
  init_trusted_hosts();
5344
5403
  init_dist();
5345
5404
  init_dist();
5405
+ init_api_url();
5346
5406
  DANGEROUS_WORDS = [
5347
5407
  "mkfs",
5348
5408
  // formats/wipes a filesystem partition
@@ -5616,6 +5676,7 @@ var init_config = __esm({
5616
5676
  }
5617
5677
  ];
5618
5678
  cachedConfig = null;
5679
+ rejectedApiUrlsSeen = /* @__PURE__ */ new Set();
5619
5680
  lastParsedRulesCache = null;
5620
5681
  CACHE_LOG_REARM_MS = 5 * 60 * 1e3;
5621
5682
  cacheReadLastLoggedAt = 0;
@@ -6615,6 +6676,13 @@ var init_setup_pi_shim = __esm({
6615
6676
  }
6616
6677
  });
6617
6678
 
6679
+ // src/utils/atomic-write.ts
6680
+ var init_atomic_write = __esm({
6681
+ "src/utils/atomic-write.ts"() {
6682
+ "use strict";
6683
+ }
6684
+ });
6685
+
6618
6686
  // src/setup.ts
6619
6687
  import chalk3 from "chalk";
6620
6688
  import { confirm as rawConfirm } from "@inquirer/prompts";
@@ -6627,6 +6695,7 @@ var init_setup = __esm({
6627
6695
  init_hook_baseline();
6628
6696
  init_setup_opencode_shim();
6629
6697
  init_setup_pi_shim();
6698
+ init_atomic_write();
6630
6699
  }
6631
6700
  });
6632
6701
 
@@ -6651,6 +6720,7 @@ var init_scan_json = __esm({
6651
6720
  var init_scan_history = __esm({
6652
6721
  "src/cli/render/scan-history.ts"() {
6653
6722
  "use strict";
6723
+ init_atomic_write();
6654
6724
  }
6655
6725
  });
6656
6726