@node9/proxy 2.14.1 → 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.
@@ -1775,6 +1775,9 @@ function classifySsrf(host) {
1775
1775
  return null;
1776
1776
  }
1777
1777
  }
1778
+ function stripTerminalEscapes(s) {
1779
+ return s.replace(TERMINAL_ESCAPE_RE, "");
1780
+ }
1778
1781
  function isShieldVerdict(v) {
1779
1782
  return v === "allow" || v === "review" || v === "block";
1780
1783
  }
@@ -1936,7 +1939,7 @@ function matchCanaryArgs(args, values) {
1936
1939
  return null;
1937
1940
  }
1938
1941
  }
1939
- var B58, B58_INDEX, XPRV_VERSIONS, ASSIGNMENT_CONTEXT_RE, DLP_STOPWORDS, DLP_PATTERNS, DLP_PATTERNS_GLOBAL, SENSITIVE_PATH_PATTERNS, MAX_DEPTH, MAX_STRING_BYTES, MAX_JSON_PARSE_BYTES, MAX_REGEX_LENGTH, REGEX_CACHE_MAX, regexCache, FORBIDDEN_PATH_SEGMENTS, syntax, sharedParser, MESSAGE_FLAGS, SHELL_INTERPRETERS, DOWNLOAD_CMDS, NORMALIZE_CACHE_MAX, normalizeCache, AST_CACHE_MAX, astCache, PARSE_FAIL, FS_READ_TOOLS, GREP_SHAPE, PATTERN_VERBS, PATTERN_VERB_NAMES, GREP_FILE_OPERANDS, READER_VALUE_LETTERS, FILE_OPERAND_FLAGS, SCP_VALUE_FLAGS, TAR_VALUE_LETTERS, ZIP_VALUE_LETTERS, RSYNC_VALUE_LETTERS, RSYNC_SKIP, CP_VALUE_LETTERS, INSTALL_VALUE_LETTERS, COPY_VERBS, TAR_MODE_WORD, COPY_VERB_HEADS, FS_OP_PRESCREEN_RE, HOME_CACHE_ALLOWLIST, SENSITIVE_PATH_RULES, BASH_TOOL_NAMES, AST_FS_REGEX_RULES, COMMAND_WRAPPERS, INLINE_INTERPRETER, RUNNER_WRAPPERS, WRAPPER_TAKES_TARGET, FIND_EXEC_FLAGS, FS_OP_CACHE_MAX, fsOpCache, REDIR_TRUNCATE_OPS, REDIR_FILE_IN_OPS, REDIR_HEREDOC_OPS, FIND_OPTIONS, COPY_RULE_OF, isReaderWord, positionalAfter, NONE, UNKNOWN, SOURCE_COMMANDS, SSRF_MAX_HOST, METADATA_ADDRESSES, METADATA_HOSTNAMES, v4Octets, aws_default, bash_safe_default, docker_default, filesystem_default, github_default, k8s_default, mongodb_default, postgres_default, project_jail_default, redis_default, BUILTIN_SHIELDS, CANARY_MIN_LENGTH, MAX_TEXT, MAX_DEPTH2, MAX_JSON_PARSE, URL_DEPTH, B64_DEPTH, MIN_SEGMENT, SEPARATORS, stripSeparators, looksText, CANARY_DECODERS, VIEWS, LONG_OUTPUT_THRESHOLD_BYTES;
1942
+ var B58, B58_INDEX, XPRV_VERSIONS, ASSIGNMENT_CONTEXT_RE, DLP_STOPWORDS, DLP_PATTERNS, DLP_PATTERNS_GLOBAL, SENSITIVE_PATH_PATTERNS, MAX_DEPTH, MAX_STRING_BYTES, MAX_JSON_PARSE_BYTES, MAX_REGEX_LENGTH, REGEX_CACHE_MAX, regexCache, FORBIDDEN_PATH_SEGMENTS, syntax, sharedParser, MESSAGE_FLAGS, SHELL_INTERPRETERS, DOWNLOAD_CMDS, NORMALIZE_CACHE_MAX, normalizeCache, AST_CACHE_MAX, astCache, PARSE_FAIL, FS_READ_TOOLS, GREP_SHAPE, PATTERN_VERBS, PATTERN_VERB_NAMES, GREP_FILE_OPERANDS, READER_VALUE_LETTERS, FILE_OPERAND_FLAGS, SCP_VALUE_FLAGS, TAR_VALUE_LETTERS, ZIP_VALUE_LETTERS, RSYNC_VALUE_LETTERS, RSYNC_SKIP, CP_VALUE_LETTERS, INSTALL_VALUE_LETTERS, COPY_VERBS, TAR_MODE_WORD, COPY_VERB_HEADS, FS_OP_PRESCREEN_RE, HOME_CACHE_ALLOWLIST, SENSITIVE_PATH_RULES, BASH_TOOL_NAMES, AST_FS_REGEX_RULES, COMMAND_WRAPPERS, INLINE_INTERPRETER, RUNNER_WRAPPERS, WRAPPER_TAKES_TARGET, FIND_EXEC_FLAGS, FS_OP_CACHE_MAX, fsOpCache, REDIR_TRUNCATE_OPS, REDIR_FILE_IN_OPS, REDIR_HEREDOC_OPS, FIND_OPTIONS, COPY_RULE_OF, isReaderWord, positionalAfter, NONE, UNKNOWN, SOURCE_COMMANDS, SSRF_MAX_HOST, METADATA_ADDRESSES, METADATA_HOSTNAMES, v4Octets, TERMINAL_ESCAPE_RE, aws_default, bash_safe_default, docker_default, filesystem_default, github_default, k8s_default, mongodb_default, postgres_default, project_jail_default, redis_default, BUILTIN_SHIELDS, CANARY_MIN_LENGTH, MAX_TEXT, MAX_DEPTH2, MAX_JSON_PARSE, URL_DEPTH, B64_DEPTH, MIN_SEGMENT, SEPARATORS, stripSeparators, looksText, CANARY_DECODERS, VIEWS, LONG_OUTPUT_THRESHOLD_BYTES;
1940
1943
  var init_dist = __esm({
1941
1944
  "packages/policy-engine/dist/index.mjs"() {
1942
1945
  "use strict";
@@ -3150,6 +3153,7 @@ var init_dist = __esm({
3150
3153
  const p = a.split(".");
3151
3154
  return p.length === 4 ? p.map(Number) : null;
3152
3155
  };
3156
+ TERMINAL_ESCAPE_RE = /\x1b\[[0-9;?]*[A-Za-z]|\x1b\][^\x07\x1b]*(?:\x07|\x1b\\)|\x1b[@-_]|[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]/g;
3153
3157
  aws_default = {
3154
3158
  name: "aws",
3155
3159
  description: "Protects AWS infrastructure from destructive AI operations",
@@ -4656,6 +4660,51 @@ var init_trusted_hosts = __esm({
4656
4660
  }
4657
4661
  });
4658
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
+
4659
4708
  // src/config/index.ts
4660
4709
  import fs6 from "fs";
4661
4710
  import path7 from "path";
@@ -4674,12 +4723,26 @@ function sanitizeSsrfAllow(entries, source) {
4674
4723
  }
4675
4724
  return kept;
4676
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
+ }
4677
4739
  function getCredentials() {
4678
- const DEFAULT_API_URL = "https://api.node9.ai/api/v1/intercept";
4679
4740
  if (process.env.NODE9_API_KEY) {
4680
4741
  return {
4681
4742
  apiKey: process.env.NODE9_API_KEY,
4682
- 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)
4683
4746
  };
4684
4747
  }
4685
4748
  try {
@@ -4691,14 +4754,14 @@ function getCredentials() {
4691
4754
  if (profile?.apiKey) {
4692
4755
  return {
4693
4756
  apiKey: profile.apiKey,
4694
- apiUrl: profile.apiUrl || DEFAULT_API_URL,
4757
+ apiUrl: safeApiUrl(profile.apiUrl || DEFAULT_API_URL, noteRejectedApiUrl),
4695
4758
  localOnly: profile.localOnly === true || profileName !== "default"
4696
4759
  };
4697
4760
  }
4698
4761
  if (creds.apiKey) {
4699
4762
  return {
4700
4763
  apiKey: creds.apiKey,
4701
- apiUrl: creds.apiUrl || DEFAULT_API_URL,
4764
+ apiUrl: safeApiUrl(creds.apiUrl || DEFAULT_API_URL, noteRejectedApiUrl),
4702
4765
  localOnly: creds.localOnly === true
4703
4766
  };
4704
4767
  }
@@ -5328,7 +5391,7 @@ ${error.replace("Invalid config:\n", "")}
5328
5391
  }
5329
5392
  return sanitized;
5330
5393
  }
5331
- 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;
5332
5395
  var init_config = __esm({
5333
5396
  "src/config/index.ts"() {
5334
5397
  "use strict";
@@ -5339,6 +5402,7 @@ var init_config = __esm({
5339
5402
  init_trusted_hosts();
5340
5403
  init_dist();
5341
5404
  init_dist();
5405
+ init_api_url();
5342
5406
  DANGEROUS_WORDS = [
5343
5407
  "mkfs",
5344
5408
  // formats/wipes a filesystem partition
@@ -5612,6 +5676,7 @@ var init_config = __esm({
5612
5676
  }
5613
5677
  ];
5614
5678
  cachedConfig = null;
5679
+ rejectedApiUrlsSeen = /* @__PURE__ */ new Set();
5615
5680
  lastParsedRulesCache = null;
5616
5681
  CACHE_LOG_REARM_MS = 5 * 60 * 1e3;
5617
5682
  cacheReadLastLoggedAt = 0;
@@ -5678,6 +5743,14 @@ var init_session_files = __esm({
5678
5743
  }
5679
5744
  });
5680
5745
 
5746
+ // src/utils/safe-text.ts
5747
+ var init_safe_text = __esm({
5748
+ "src/utils/safe-text.ts"() {
5749
+ "use strict";
5750
+ init_dist();
5751
+ }
5752
+ });
5753
+
5681
5754
  // src/costSync.ts
5682
5755
  function decodeProjectDirName(dirName) {
5683
5756
  return dirName.replace(/-/g, "/");
@@ -5693,6 +5766,7 @@ var init_costSync = __esm({
5693
5766
  init_cost_gemini();
5694
5767
  init_cost_copilot();
5695
5768
  init_session_files();
5769
+ init_safe_text();
5696
5770
  SYNC_INTERVAL_MS = 10 * 60 * 1e3;
5697
5771
  }
5698
5772
  });
@@ -6602,6 +6676,13 @@ var init_setup_pi_shim = __esm({
6602
6676
  }
6603
6677
  });
6604
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
+
6605
6686
  // src/setup.ts
6606
6687
  import chalk3 from "chalk";
6607
6688
  import { confirm as rawConfirm } from "@inquirer/prompts";
@@ -6614,6 +6695,7 @@ var init_setup = __esm({
6614
6695
  init_hook_baseline();
6615
6696
  init_setup_opencode_shim();
6616
6697
  init_setup_pi_shim();
6698
+ init_atomic_write();
6617
6699
  }
6618
6700
  });
6619
6701
 
@@ -6638,6 +6720,7 @@ var init_scan_json = __esm({
6638
6720
  var init_scan_history = __esm({
6639
6721
  "src/cli/render/scan-history.ts"() {
6640
6722
  "use strict";
6723
+ init_atomic_write();
6641
6724
  }
6642
6725
  });
6643
6726
 
@@ -6666,9 +6749,6 @@ function isNode9SelfOutput(text) {
6666
6749
  }
6667
6750
  return false;
6668
6751
  }
6669
- function stripTerminalEscapes(s) {
6670
- return s.replace(TERMINAL_ESCAPE_RE, "");
6671
- }
6672
6752
  function preview(input, max) {
6673
6753
  const cmd = input.command ?? input.query ?? input.file_path ?? JSON.stringify(input);
6674
6754
  const s = stripTerminalEscapes(String(cmd)).replace(/\s+/g, " ").trim();
@@ -7644,7 +7724,7 @@ function scanCodexHistory(startDate, onProgress, onLine) {
7644
7724
  }
7645
7725
  return result;
7646
7726
  }
7647
- var toolInspectionMap, CODE_EXTENSIONS, SELF_OUTPUT_MARKERS, TERMINAL_ESCAPE_RE, LOOP_TOOLS, LOOP_THRESHOLD, LOOP_TIMESPAN_THRESHOLD_MS;
7727
+ var toolInspectionMap, CODE_EXTENSIONS, SELF_OUTPUT_MARKERS, LOOP_TOOLS, LOOP_THRESHOLD, LOOP_TIMESPAN_THRESHOLD_MS;
7648
7728
  var init_scan = __esm({
7649
7729
  "src/cli/commands/scan.ts"() {
7650
7730
  "use strict";
@@ -7670,6 +7750,7 @@ var init_scan = __esm({
7670
7750
  init_scan_json();
7671
7751
  init_session_files();
7672
7752
  init_scan_history();
7753
+ init_safe_text();
7673
7754
  toolInspectionMap = DEFAULT_CONFIG.policy.toolInspection;
7674
7755
  CODE_EXTENSIONS = /* @__PURE__ */ new Set([
7675
7756
  ".ts",
@@ -7704,7 +7785,6 @@ var init_scan = __esm({
7704
7785
  /\bseverity:\s*['"](?:block|review|allow)['"]/,
7705
7786
  /NODE9 SECURITY ALERT/
7706
7787
  ];
7707
- TERMINAL_ESCAPE_RE = /\x1b\[[0-9;?]*[A-Za-z]|\x1b\][^\x07\x1b]*(?:\x07|\x1b\\)|\x1b[@-_]|[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]/g;
7708
7788
  LOOP_TOOLS = /* @__PURE__ */ new Set([
7709
7789
  "bash",
7710
7790
  "execute_bash",