@node9/proxy 2.14.1 → 2.14.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -2934,6 +2934,17 @@ async function evaluatePolicy(config, toolName, args, context = {}, hooks = {})
2934
2934
  function isIgnoredTool(toolName, config) {
2935
2935
  return matchesPattern(toolName, config.policy.ignoredTools);
2936
2936
  }
2937
+ function stripTerminalEscapes(s) {
2938
+ return s.replace(TERMINAL_ESCAPE_RE, "");
2939
+ }
2940
+ function stripControlChars(s) {
2941
+ return s.replace(CONTROL_CHAR_RE, "");
2942
+ }
2943
+ function safeMessage(value, max = 300) {
2944
+ const raw = typeof value === "string" ? value : value instanceof Error ? value.message : String(value ?? "");
2945
+ const s = stripTerminalEscapes(raw).replace(/\s+/g, " ").trim();
2946
+ return s.length > max ? s.slice(0, max - 1) + "\u2026" : s;
2947
+ }
2937
2948
  function isShieldVerdict(v) {
2938
2949
  return v === "allow" || v === "review" || v === "block";
2939
2950
  }
@@ -3151,7 +3162,10 @@ function computeSecurityScore(opts) {
3151
3162
  }
3152
3163
  function truncateBlastPath(full) {
3153
3164
  if (!full) return "";
3154
- const cleaned = full.replace(/[/\\]+$/, "");
3165
+ if (full.length > MAX_BLAST_PATH) full = full.slice(-MAX_BLAST_PATH);
3166
+ let end = full.length;
3167
+ while (end > 0 && (full[end - 1] === "/" || full[end - 1] === "\\")) end--;
3168
+ const cleaned = full.slice(0, end);
3155
3169
  const parts = cleaned.split(/[/\\]+/).filter((p) => p.length > 0);
3156
3170
  if (parts.length <= 2) {
3157
3171
  return cleaned.startsWith("~") && !cleaned.startsWith("~/") ? cleaned : cleaned.startsWith("~/") ? cleaned : parts.join("/");
@@ -3647,7 +3661,7 @@ function toScanFinding(c) {
3647
3661
  }
3648
3662
  function previewArgs(input, max) {
3649
3663
  const cmd = input.command ?? input.query ?? input.file_path ?? JSON.stringify(input);
3650
- const s = String(cmd).replace(TERMINAL_ESCAPE_RE, "").replace(/\s+/g, " ").trim();
3664
+ const s = stripTerminalEscapes(String(cmd)).replace(/\s+/g, " ").trim();
3651
3665
  return s.length > max ? s.slice(0, max - 1) + "\u2026" : s;
3652
3666
  }
3653
3667
  function makeFinding(args) {
@@ -3687,7 +3701,7 @@ function* stringValues(obj, depth = 0) {
3687
3701
  }
3688
3702
  for (const v of Object.values(obj)) yield* stringValues(v, depth + 1);
3689
3703
  }
3690
- var import_safe_regex2, import_crypto3, import_mvdan_sh, import_picomatch, import_safe_regex22, import_safe_regex23, import_crypto4, IBAN_LENGTH, B58, B58_INDEX, XPRV_VERSIONS, MAX, UNTRUSTED_TOOLS, SIGNALS, ASSIGNMENT_CONTEXT_RE, DLP_STOPWORDS, DLP_PATTERNS, DLP_PATTERNS_GLOBAL, SENSITIVE_PATH_PATTERNS, MAX_DEPTH, MAX_STRING_BYTES, MAX_JSON_PARSE_BYTES, DLP_SCAN_LIMITS, 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, SQL_DB_CLIS, SQL_DDL_RE, CHMOD_OPEN_PERM_TOKENS, COMMAND_WRAPPERS, INLINE_INTERPRETER, RUNNER_WRAPPERS, _redirStdinOps, _listOps, WRAPPER_TAKES_TARGET, FIND_EXEC_FLAGS, INTERP_LEADING_TARGET, NET_BINARIES, VALUE_FLAGS, FS_OP_CACHE_MAX, fsOpCache, stripDotSlash, REDIR_TRUNCATE_OPS, REDIR_FILE_IN_OPS, REDIR_HEREDOC_OPS, FIND_OPTIONS, COPY_RULE_OF, isReaderWord, positionalAfter, NONE, UNKNOWN, DEFAULT_EGRESS_ALLOWLIST, SOURCE_COMMANDS, SINK_COMMANDS, OBFUSCATORS, SENSITIVE_PATTERNS, FLAGS_WITH_VALUES, SSRF_MAX_HOST, METADATA_ADDRESSES, STRICT_TIERS, METADATA_HOSTNAMES, v4Octets, TIER_REASON, DESTINATION_ARGS, VERDICT_RANK, SQL_DML_KEYWORDS, aws_default, bash_safe_default, docker_default, filesystem_default, github_default, k8s_default, mongodb_default, postgres_default, project_jail_default, redis_default, BUILTIN_SHIELDS, LOOP_MAX_RECORDS, FINDING_TO_SIGNAL, SCAN_SIGNAL_WEIGHTS, LOOP_THRESHOLD_FOR_WASTE, DESTRUCTIVE_OP_RE, SENSITIVE_PATH_RE, FILE_TOOLS, PII_EMAIL_RE, PII_SSN_RE, PII_PHONE_RE, PII_CC16_RE, PII_CC15_RE, PII_IBAN_RE, REALTIME_PII_PATTERNS, MAX_PII_SCAN_BYTES, 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, CANONICAL_EXTRACTOR_VERSION, DEDUPE_PREVIEW_LEN, TERMINAL_ESCAPE_RE, ENGINE_VERSION;
3704
+ var import_safe_regex2, import_crypto3, import_mvdan_sh, import_picomatch, import_safe_regex22, import_safe_regex23, import_crypto4, IBAN_LENGTH, B58, B58_INDEX, XPRV_VERSIONS, MAX, UNTRUSTED_TOOLS, SIGNALS, ASSIGNMENT_CONTEXT_RE, DLP_STOPWORDS, DLP_PATTERNS, DLP_PATTERNS_GLOBAL, SENSITIVE_PATH_PATTERNS, MAX_DEPTH, MAX_STRING_BYTES, MAX_JSON_PARSE_BYTES, DLP_SCAN_LIMITS, 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, SQL_DB_CLIS, SQL_DDL_RE, CHMOD_OPEN_PERM_TOKENS, COMMAND_WRAPPERS, INLINE_INTERPRETER, RUNNER_WRAPPERS, _redirStdinOps, _listOps, WRAPPER_TAKES_TARGET, FIND_EXEC_FLAGS, INTERP_LEADING_TARGET, NET_BINARIES, VALUE_FLAGS, FS_OP_CACHE_MAX, fsOpCache, stripDotSlash, REDIR_TRUNCATE_OPS, REDIR_FILE_IN_OPS, REDIR_HEREDOC_OPS, FIND_OPTIONS, COPY_RULE_OF, isReaderWord, positionalAfter, NONE, UNKNOWN, DEFAULT_EGRESS_ALLOWLIST, SOURCE_COMMANDS, SINK_COMMANDS, OBFUSCATORS, SENSITIVE_PATTERNS, FLAGS_WITH_VALUES, SSRF_MAX_HOST, METADATA_ADDRESSES, STRICT_TIERS, METADATA_HOSTNAMES, v4Octets, TIER_REASON, DESTINATION_ARGS, VERDICT_RANK, SQL_DML_KEYWORDS, TERMINAL_ESCAPE_RE, CONTROL_CHAR_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, LOOP_MAX_RECORDS, FINDING_TO_SIGNAL, SCAN_SIGNAL_WEIGHTS, LOOP_THRESHOLD_FOR_WASTE, MAX_BLAST_PATH, DESTRUCTIVE_OP_RE, SENSITIVE_PATH_RE, FILE_TOOLS, PII_EMAIL_RE, PII_SSN_RE, PII_PHONE_RE, PII_CC16_RE, PII_CC15_RE, PII_IBAN_RE, REALTIME_PII_PATTERNS, MAX_PII_SCAN_BYTES, 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, CANONICAL_EXTRACTOR_VERSION, DEDUPE_PREVIEW_LEN, ENGINE_VERSION;
3691
3705
  var init_dist = __esm({
3692
3706
  "packages/policy-engine/dist/index.mjs"() {
3693
3707
  "use strict";
@@ -5321,6 +5335,8 @@ var init_dist = __esm({
5321
5335
  block: 2
5322
5336
  };
5323
5337
  SQL_DML_KEYWORDS = /* @__PURE__ */ new Set(["select", "insert", "update", "delete", "merge", "upsert"]);
5338
+ TERMINAL_ESCAPE_RE = /\x1b\[[0-9;?]*[A-Za-z]|\x1b\][^\x07\x1b]*(?:\x07|\x1b\\)|\x1b[@-_]|[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]/g;
5339
+ CONTROL_CHAR_RE = /[\x00-\x1F\x7F]/g;
5324
5340
  aws_default = {
5325
5341
  name: "aws",
5326
5342
  description: "Protects AWS infrastructure from destructive AI operations",
@@ -6098,6 +6114,7 @@ var init_dist = __esm({
6098
6114
  longOutputRedactions: 1
6099
6115
  };
6100
6116
  LOOP_THRESHOLD_FOR_WASTE = 3;
6117
+ MAX_BLAST_PATH = 4096;
6101
6118
  DESTRUCTIVE_OP_RE = /\brm\s+-[rRf]+\b|\bDROP\s+(TABLE|DATABASE|COLLECTION|SCHEMA)\b|\bTRUNCATE\s+TABLE\b|\bgit\s+push\s+(--force|-f)\b|\bFLUSHALL\b|\bFLUSHDB\b|\bkubectl\s+delete\b|\bhelm\s+uninstall\b/i;
6102
6119
  SENSITIVE_PATH_RE = /[\\/]\.aws(?:[\\/]|$)|^\.aws[\\/]|[\\/]\.ssh(?:[\\/]|$)|^\.ssh[\\/]|(?:^|[\\/])\.env(?![\w-])(?:[\w.-]*\.local$|(?!\.(?:example|sample|template)\b)(?!\.test$)[\w.-]*$)|\.config\/gcloud\/credentials\.db\b|\.docker\/config\.json\b|\.netrc\b|\.npmrc\b|\.node9\/credentials\.json\b/i;
6103
6120
  FILE_TOOLS = /* @__PURE__ */ new Set([
@@ -6178,8 +6195,6 @@ var init_dist = __esm({
6178
6195
  LONG_OUTPUT_THRESHOLD_BYTES = 100 * 1024;
6179
6196
  CANONICAL_EXTRACTOR_VERSION = "canonical-v15";
6180
6197
  DEDUPE_PREVIEW_LEN = 120;
6181
- TERMINAL_ESCAPE_RE = // eslint-disable-next-line no-control-regex
6182
- /\x1b\[[0-9;?]*[A-Za-z]|\x1b\][^\x07\x1b]*(?:\x07|\x1b\\)|\x1b[@-_]|[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]/g;
6183
6198
  ENGINE_VERSION = "1.4.0";
6184
6199
  }
6185
6200
  });
@@ -8728,6 +8743,14 @@ var init_context_sniper = __esm({
8728
8743
  }
8729
8744
  });
8730
8745
 
8746
+ // src/utils/safe-text.ts
8747
+ var init_safe_text = __esm({
8748
+ "src/utils/safe-text.ts"() {
8749
+ "use strict";
8750
+ init_dist();
8751
+ }
8752
+ });
8753
+
8731
8754
  // src/ui/native.ts
8732
8755
  function resolveNativeDecision(opts) {
8733
8756
  const { code, output, elapsedMs, locked } = opts;
@@ -8837,7 +8860,7 @@ function escapePango(text) {
8837
8860
  function buildPlainMessage(toolName, formattedArgs, agent, explainableLabel, locked, allowCount = 1, ruleDescription) {
8838
8861
  const lines = [];
8839
8862
  if (locked) lines.push("\u26A0\uFE0F LOCKED BY ADMIN POLICY\n");
8840
- const safeAgent = (agent ?? "AI Agent").replace(/\x1b(?:\[[0-9;?]*[a-zA-Z]|\][^\x07\x1b]*(?:\x07|\x1b\\)|[@-_])/g, "").slice(0, 80);
8863
+ const safeAgent = safeMessage(agent ?? "AI Agent", 80);
8841
8864
  lines.push(`\u{1F916} ${safeAgent} | \u{1F527} ${toolName}`);
8842
8865
  lines.push(`\u{1F6E1}\uFE0F ${explainableLabel || "Security Policy"}`);
8843
8866
  if (ruleDescription) lines.push(`\u2139 ${ruleDescription}`);
@@ -8974,6 +8997,7 @@ var init_native = __esm({
8974
8997
  import_child_process = require("child_process");
8975
8998
  import_path11 = __toESM(require("path"));
8976
8999
  init_context_sniper();
9000
+ init_safe_text();
8977
9001
  isTestEnv = () => {
8978
9002
  return process.env.NODE_ENV === "test" || process.env.VITEST === "true" || !!process.env.VITEST || process.env.CI === "true" || !!process.env.CI || process.env.NODE9_TESTING === "1";
8979
9003
  };
@@ -9360,14 +9384,14 @@ async function resolveNode9SaaS(requestId, creds, approved, decidedBy) {
9360
9384
  if (!res.ok) {
9361
9385
  import_fs12.default.appendFileSync(
9362
9386
  HOOK_DEBUG_LOG,
9363
- `[resolve-cloud] PATCH ${resolveUrl} \u2192 HTTP ${res.status}
9387
+ `[resolve-cloud] PATCH ${safeMessage(resolveUrl, 200)} \u2192 HTTP ${res.status}
9364
9388
  `
9365
9389
  );
9366
9390
  }
9367
9391
  } catch (err2) {
9368
9392
  import_fs12.default.appendFileSync(
9369
9393
  HOOK_DEBUG_LOG,
9370
- `[resolve-cloud] PATCH failed for ${requestId}: ${err2.message}
9394
+ `[resolve-cloud] PATCH failed for ${safeMessage(requestId, 64)}: ${safeMessage(err2)}
9371
9395
  `
9372
9396
  );
9373
9397
  }
@@ -9380,6 +9404,7 @@ var init_cloud = __esm({
9380
9404
  import_os11 = __toESM(require("os"));
9381
9405
  import_path14 = __toESM(require("path"));
9382
9406
  init_audit();
9407
+ init_safe_text();
9383
9408
  DLP_SAMPLE_MAX_LEN = 200;
9384
9409
  DLP_PATTERN_MAX_LEN = 100;
9385
9410
  KNOWN_CHECKED_BY = /* @__PURE__ */ new Set([
@@ -9512,9 +9537,8 @@ async function authorizeHeadless(toolName, args, meta, options) {
9512
9537
  if (!options?.calledFromDaemon) {
9513
9538
  const actId = (0, import_crypto7.randomUUID)();
9514
9539
  const actTs = Date.now();
9515
- const stripAnsi2 = (s) => s.replace(/\x1b(?:\[[0-9;?]*[a-zA-Z]|\][^\x07\x1b]*(?:\x07|\x1b\\)|[@-_])/g, "");
9516
- const sanitizedAgent = meta?.agent ? stripAnsi2(meta.agent).slice(0, 80) : void 0;
9517
- const sanitizedMcpServer = meta?.mcpServer ? stripAnsi2(meta.mcpServer).slice(0, 40) : void 0;
9540
+ const sanitizedAgent = meta?.agent ? safeMessage(meta.agent, 80) : void 0;
9541
+ const sanitizedMcpServer = meta?.mcpServer ? safeMessage(meta.mcpServer, 40) : void 0;
9518
9542
  const socketOk = await notifyActivity({
9519
9543
  id: actId,
9520
9544
  ts: actTs,
@@ -10390,6 +10414,7 @@ var init_orchestrator = __esm({
10390
10414
  init_loop_detector();
10391
10415
  init_shields();
10392
10416
  init_jail();
10417
+ init_safe_text();
10393
10418
  WRITE_TOOLS = /* @__PURE__ */ new Set([
10394
10419
  "write",
10395
10420
  "write_file",
@@ -14995,7 +15020,7 @@ async function postCostBatches(apiUrl, apiKey, machineId, entries) {
14995
15020
  }
14996
15021
  }
14997
15022
  } catch (err2) {
14998
- import_fs24.default.appendFileSync(HOOK_DEBUG_LOG, `[cost-sync] ${err2.message}
15023
+ import_fs24.default.appendFileSync(HOOK_DEBUG_LOG, `[cost-sync] ${safeMessage(err2)}
14999
15024
  `);
15000
15025
  }
15001
15026
  }
@@ -15037,6 +15062,7 @@ var init_costSync = __esm({
15037
15062
  init_cost_gemini();
15038
15063
  init_cost_copilot();
15039
15064
  init_session_files();
15065
+ init_safe_text();
15040
15066
  SYNC_INTERVAL_MS = 10 * 60 * 1e3;
15041
15067
  claudeSource = {
15042
15068
  id: "claude",
@@ -15867,9 +15893,6 @@ function fmtTs(ts) {
15867
15893
  return ts.slice(0, 10);
15868
15894
  }
15869
15895
  }
15870
- function stripTerminalEscapes(s) {
15871
- return s.replace(TERMINAL_ESCAPE_RE2, "");
15872
- }
15873
15896
  function preview(input, max) {
15874
15897
  const cmd = input.command ?? input.query ?? input.file_path ?? JSON.stringify(input);
15875
15898
  const s = stripTerminalEscapes(String(cmd)).replace(/\s+/g, " ").trim();
@@ -18584,7 +18607,7 @@ function registerScanCommand(program2) {
18584
18607
  }
18585
18608
  );
18586
18609
  }
18587
- var import_chalk6, import_fs27, import_path29, import_os26, import_string_width2, toolInspectionMap, CODE_EXTENSIONS, SELF_OUTPUT_MARKERS, TERMINAL_ESCAPE_RE2, LOOP_TOOLS, LOOP_THRESHOLD, LOOP_TIMESPAN_THRESHOLD_MS, STUCK_TOOLS_MIN_WASTE, STUCK_TOOLS_LIMIT, RECURRING_SESSION_THRESHOLD, STALE_AGE_DAYS, classifyRuleSeverity2, narrativeRuleLabel2;
18610
+ var import_chalk6, import_fs27, import_path29, import_os26, import_string_width2, toolInspectionMap, CODE_EXTENSIONS, SELF_OUTPUT_MARKERS, LOOP_TOOLS, LOOP_THRESHOLD, LOOP_TIMESPAN_THRESHOLD_MS, STUCK_TOOLS_MIN_WASTE, STUCK_TOOLS_LIMIT, RECURRING_SESSION_THRESHOLD, STALE_AGE_DAYS, classifyRuleSeverity2, narrativeRuleLabel2;
18588
18611
  var init_scan = __esm({
18589
18612
  "src/cli/commands/scan.ts"() {
18590
18613
  "use strict";
@@ -18615,6 +18638,7 @@ var init_scan = __esm({
18615
18638
  init_scan_json();
18616
18639
  init_session_files();
18617
18640
  init_scan_history();
18641
+ init_safe_text();
18618
18642
  toolInspectionMap = DEFAULT_CONFIG.policy.toolInspection;
18619
18643
  CODE_EXTENSIONS = /* @__PURE__ */ new Set([
18620
18644
  ".ts",
@@ -18649,7 +18673,6 @@ var init_scan = __esm({
18649
18673
  /\bseverity:\s*['"](?:block|review|allow)['"]/,
18650
18674
  /NODE9 SECURITY ALERT/
18651
18675
  ];
18652
- TERMINAL_ESCAPE_RE2 = /\x1b\[[0-9;?]*[A-Za-z]|\x1b\][^\x07\x1b]*(?:\x07|\x1b\\)|\x1b[@-_]|[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]/g;
18653
18676
  LOOP_TOOLS = /* @__PURE__ */ new Set([
18654
18677
  "bash",
18655
18678
  "execute_bash",
@@ -19135,7 +19158,7 @@ function atomicWriteSync2(filePath, data, options) {
19135
19158
  function redactArgs(value) {
19136
19159
  if (!value || typeof value !== "object") return value;
19137
19160
  if (Array.isArray(value)) return value.map(redactArgs);
19138
- const result = {};
19161
+ const result = /* @__PURE__ */ Object.create(null);
19139
19162
  for (const [k, v] of Object.entries(value)) {
19140
19163
  result[k] = SECRET_KEY_RE.test(k) ? "[REDACTED]" : redactArgs(v);
19141
19164
  }
@@ -23491,7 +23514,7 @@ data: ${JSON.stringify(item.data)}
23491
23514
  if (req.method === "GET" && pathname === "/state/check") {
23492
23515
  const predicatesParam = reqUrl.searchParams.get("predicates") ?? "";
23493
23516
  const predicates = predicatesParam.split(",").filter(Boolean);
23494
- const results = {};
23517
+ const results = /* @__PURE__ */ Object.create(null);
23495
23518
  for (const p of predicates) {
23496
23519
  results[p] = sessionHistory.checkPredicate(p);
23497
23520
  }
@@ -51227,7 +51250,7 @@ async function startTail(options = {}) {
51227
51250
  req.on("error", (err2) => {
51228
51251
  const msg = err2.code === "ECONNREFUSED" ? "Daemon is not running. Start it with: node9 daemon start" : err2.message;
51229
51252
  console.error(import_chalk44.default.red(`
51230
- \u274C ${msg}`));
51253
+ \u274C ${safeMessage(msg)}`));
51231
51254
  process.exit(1);
51232
51255
  });
51233
51256
  }
@@ -51245,6 +51268,7 @@ var init_tail = __esm({
51245
51268
  init_startup_log();
51246
51269
  init_daemon2();
51247
51270
  init_daemon();
51271
+ init_safe_text();
51248
51272
  PID_FILE = import_path70.default.join(import_os62.default.homedir(), ".node9", "daemon.pid");
51249
51273
  ICONS = {
51250
51274
  bash: "\u{1F4BB}",
@@ -51891,9 +51915,7 @@ function shellInvocation(command) {
51891
51915
  }
51892
51916
 
51893
51917
  // src/proxy/index.ts
51894
- function sanitize(value) {
51895
- return value.replace(/[\x00-\x1F\x7F]/g, "");
51896
- }
51918
+ init_safe_text();
51897
51919
  async function runProxy(targetCommand) {
51898
51920
  const commandParts = (0, import_execa2.parseCommandString)(targetCommand);
51899
51921
  const cmd = commandParts[0];
@@ -51929,7 +51951,7 @@ async function runProxy(targetCommand) {
51929
51951
  try {
51930
51952
  const name = message.params?.name || message.params?.tool_name || "unknown";
51931
51953
  const toolArgs = message.params?.arguments || message.params?.tool_input || {};
51932
- const result = await authorizeHeadless(sanitize(name), toolArgs, {
51954
+ const result = await authorizeHeadless(stripControlChars(name), toolArgs, {
51933
51955
  agent: "Proxy/MCP"
51934
51956
  });
51935
51957
  if (!result.approved) {
@@ -52177,17 +52199,27 @@ init_machine_id();
52177
52199
 
52178
52200
  // src/utils/open-browser.ts
52179
52201
  var import_child_process6 = require("child_process");
52202
+ function isOpenableUrl(url) {
52203
+ let u;
52204
+ try {
52205
+ u = new URL(url);
52206
+ } catch {
52207
+ return false;
52208
+ }
52209
+ if (u.protocol !== "https:" && u.protocol !== "http:") return false;
52210
+ return !/[\x00-\x20\x7F"'`]/.test(url);
52211
+ }
52180
52212
  function openBrowser(url) {
52213
+ if (!isOpenableUrl(url)) return false;
52181
52214
  if (process.env.SSH_CONNECTION || process.env.SSH_TTY) return false;
52182
52215
  if (process.platform === "linux" && !process.env.DISPLAY && !process.env.WAYLAND_DISPLAY) {
52183
52216
  return false;
52184
52217
  }
52185
- const opener = process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open";
52218
+ const [cmd, args] = process.platform === "darwin" ? ["open", [url]] : process.platform === "win32" ? ["rundll32.exe", ["url.dll,FileProtocolHandler", url]] : ["xdg-open", [url]];
52186
52219
  try {
52187
- const child = (0, import_child_process6.spawn)(opener, [url], {
52220
+ const child = (0, import_child_process6.spawn)(cmd, args, {
52188
52221
  stdio: "ignore",
52189
- detached: true,
52190
- shell: process.platform === "win32"
52222
+ detached: true
52191
52223
  });
52192
52224
  child.on("error", () => {
52193
52225
  });
@@ -52248,6 +52280,7 @@ function postJson2(url, body, bearer) {
52248
52280
  }
52249
52281
 
52250
52282
  // src/auth/device-login.ts
52283
+ init_safe_text();
52251
52284
  var sleep = (ms) => new Promise((r) => setTimeout(r, ms));
52252
52285
  async function runDeviceLogin(opts = {}) {
52253
52286
  const startUrl = resolveCloudEndpoint("/device/start", opts.apiUrl);
@@ -52263,14 +52296,16 @@ async function runDeviceLogin(opts = {}) {
52263
52296
  } catch (e) {
52264
52297
  return {
52265
52298
  ok: false,
52266
- reason: `Could not reach the node9 cloud: ${e instanceof Error ? e.message : String(e)}`
52299
+ reason: `Could not reach the node9 cloud: ${safeMessage(e)}`
52267
52300
  };
52268
52301
  }
52269
52302
  console.log("");
52270
52303
  console.log(` Open this link to approve the connection:`);
52271
- console.log(` ${import_chalk11.default.cyan.underline(start.verificationUrl)}`);
52304
+ console.log(` ${import_chalk11.default.cyan.underline(safeMessage(start.verificationUrl, 200))}`);
52272
52305
  console.log("");
52273
- console.log(` Code: ${import_chalk11.default.bold(start.userCode)} ${import_chalk11.default.gray("(match it in the browser)")}`);
52306
+ console.log(
52307
+ ` Code: ${import_chalk11.default.bold(safeMessage(start.userCode, 40))} ${import_chalk11.default.gray("(match it in the browser)")}`
52308
+ );
52274
52309
  console.log("");
52275
52310
  const opened = opts.noBrowser ? false : openBrowser(start.verificationUrl);
52276
52311
  console.log(
@@ -52316,6 +52351,7 @@ var fs50 = __toESM(require("fs"));
52316
52351
  var os45 = __toESM(require("os"));
52317
52352
  var path48 = __toESM(require("path"));
52318
52353
  var import_chalk12 = __toESM(require("chalk"));
52354
+ init_safe_text();
52319
52355
  async function revokeSelf(creds) {
52320
52356
  const url = creds.apiUrl.replace(/\/$/, "") + "/machines/self/disconnect";
52321
52357
  try {
@@ -52356,7 +52392,7 @@ function registerLogoutCommand(program2) {
52356
52392
  } else if (res.outcome === "already") {
52357
52393
  console.log(import_chalk12.default.gray("\u2713 Cloud: this machine was already disconnected."));
52358
52394
  } else {
52359
- console.log(import_chalk12.default.yellow(`\u26A0 Could not reach the cloud (${res.detail}).`));
52395
+ console.log(import_chalk12.default.yellow(`\u26A0 Could not reach the cloud (${safeMessage(res.detail)}).`));
52360
52396
  console.log(
52361
52397
  import_chalk12.default.yellow(" The key was removed locally, but is still listed in the dashboard \u2014")
52362
52398
  );
@@ -52663,9 +52699,7 @@ function discardPendingReview(key, now = Date.now()) {
52663
52699
 
52664
52700
  // src/cli/commands/check.ts
52665
52701
  init_hook_payload();
52666
- function sanitize2(value) {
52667
- return value.replace(/[\x00-\x1F\x7F]/g, "");
52668
- }
52702
+ init_safe_text();
52669
52703
  function detectAiAgent(payload) {
52670
52704
  const meta = payload.meta;
52671
52705
  if (meta && typeof meta === "object") {
@@ -52911,10 +52945,13 @@ RAW: ${raw}
52911
52945
  const logPath = import_path47.default.join(import_os44.default.homedir(), ".node9", "hook-debug.log");
52912
52946
  if (!import_fs49.default.existsSync(import_path47.default.dirname(logPath)))
52913
52947
  import_fs49.default.mkdirSync(import_path47.default.dirname(logPath), { recursive: true });
52914
- import_fs49.default.appendFileSync(logPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] STDIN: ${raw}
52915
- `);
52948
+ import_fs49.default.appendFileSync(
52949
+ logPath,
52950
+ `[${(/* @__PURE__ */ new Date()).toISOString()}] STDIN: ${JSON.stringify(raw)}
52951
+ `
52952
+ );
52916
52953
  }
52917
- const rawToolName = sanitize2(extractToolName(payload));
52954
+ const rawToolName = stripControlChars(extractToolName(payload));
52918
52955
  const toolName = canonicalToolName(rawToolName);
52919
52956
  const toolInput = canonicalToolInput(rawToolName, extractToolInput(payload));
52920
52957
  const agent = agentOverride ?? detectAiAgent(payload);
@@ -53304,6 +53341,7 @@ function containsShellMetachar(token) {
53304
53341
 
53305
53342
  // src/cli/commands/log.ts
53306
53343
  init_hook_payload();
53344
+ init_safe_text();
53307
53345
  var TEST_COMMAND_RE2 = /(?:^|\s)(npm\s+(?:run\s+)?test|npx\s+(?:vitest|jest|mocha)|yarn\s+(?:run\s+)?test|pnpm\s+(?:run\s+)?test|vitest|jest|mocha|pytest|py\.test|cargo\s+test|go\s+test|bundle\s+exec\s+rspec|rspec|phpunit|dotnet\s+test)\b/i;
53308
53346
  function detectTestResult(command, output) {
53309
53347
  if (!TEST_COMMAND_RE2.test(command)) return null;
@@ -53322,9 +53360,6 @@ var CONFIDENCE_RANK = { low: 0, medium: 1, high: 2 };
53322
53360
  function atLeastConfidence(c, min) {
53323
53361
  return CONFIDENCE_RANK[c] >= CONFIDENCE_RANK[min];
53324
53362
  }
53325
- function sanitize3(value) {
53326
- return value.replace(/[\x00-\x1F\x7F]/g, "");
53327
- }
53328
53363
  function scanCoveredEverything(value, depth = 0) {
53329
53364
  if (value === null || value === void 0) return true;
53330
53365
  if (typeof value === "string") return value.length <= DLP_SCAN_LIMITS.maxStringBytes;
@@ -53363,7 +53398,7 @@ function registerLogCommand(program2) {
53363
53398
  if (!raw || raw.trim() === "") process.exit(0);
53364
53399
  const payload = JSON.parse(raw);
53365
53400
  if (payload.toolCall === null) process.exit(0);
53366
- const rawToolName = sanitize3(extractToolName(payload, "unknown"));
53401
+ const rawToolName = stripControlChars(extractToolName(payload, "unknown"));
53367
53402
  const tool = canonicalToolName(rawToolName);
53368
53403
  const rawInput = canonicalToolInput(rawToolName, extractToolInput(payload));
53369
53404
  const metaTag = (() => {
@@ -56195,6 +56230,7 @@ var import_http4 = __toESM(require("http"));
56195
56230
  var import_https7 = __toESM(require("https"));
56196
56231
  var import_url4 = require("url");
56197
56232
  var import_chalk22 = __toESM(require("chalk"));
56233
+ init_safe_text();
56198
56234
  function resolveConnectUrl(apiUrl) {
56199
56235
  return resolveCloudEndpoint("/cli/connect", apiUrl);
56200
56236
  }
@@ -56253,7 +56289,7 @@ function registerConnectCommand(program2) {
56253
56289
  try {
56254
56290
  resp = await postConnect(resolveConnectUrl(options.apiUrl), token);
56255
56291
  } catch (e) {
56256
- console.error(import_chalk22.default.red(`\u2717 ${e instanceof Error ? e.message : "Connect failed."}`));
56292
+ console.error(import_chalk22.default.red(`\u2717 ${safeMessage(e) || "Connect failed."}`));
56257
56293
  process.exitCode = 1;
56258
56294
  return;
56259
56295
  }
@@ -56340,9 +56376,7 @@ init_mcp_pin();
56340
56376
  init_mcp_cmd();
56341
56377
  init_mcp_tools();
56342
56378
  init_daemon();
56343
- function sanitize4(value) {
56344
- return value.replace(/[\x00-\x1F\x7F]/g, "");
56345
- }
56379
+ init_safe_text();
56346
56380
  var RPC_INVALID_REQUEST = -32600;
56347
56381
  var RPC_SERVER_ERROR = -32e3;
56348
56382
  function isValidId(id) {
@@ -56366,7 +56400,7 @@ function normalizeClientName(name) {
56366
56400
  if (lower.includes("gemini")) return "Gemini";
56367
56401
  if (lower.includes("cline")) return "Cline";
56368
56402
  if (lower.includes("continue")) return "Continue";
56369
- const sanitized = sanitize4(name).slice(0, 40);
56403
+ const sanitized = stripControlChars(name).slice(0, 40);
56370
56404
  return sanitized.length > 0 ? sanitized : void 0;
56371
56405
  }
56372
56406
  function reportPinMismatchToCloud(serverKey, serverLabel, agent) {
@@ -56546,7 +56580,7 @@ async function runMcpGateway(upstreamCommand, configName) {
56546
56580
  if (!deferredStdinEnd) agentIn.pause();
56547
56581
  authPending = true;
56548
56582
  try {
56549
- const toolName = sanitize4(
56583
+ const toolName = stripControlChars(
56550
56584
  String(message.params?.name ?? message.params?.tool_name ?? "unknown")
56551
56585
  );
56552
56586
  const toolArgs = message.params?.arguments ?? message.params?.tool_input ?? {};
@@ -61990,12 +62024,9 @@ var import_chalk42 = __toESM(require("chalk"));
61990
62024
  var import_fs73 = __toESM(require("fs"));
61991
62025
  var import_path68 = __toESM(require("path"));
61992
62026
  var import_os60 = __toESM(require("os"));
62027
+ init_safe_text();
61993
62028
  var AUDIT_LOG = import_path68.default.join(import_os60.default.homedir(), ".node9", "audit.log");
61994
62029
  var RESOLVED_FILE = import_path68.default.join(import_os60.default.homedir(), ".node9", "dlp-resolved.json");
61995
- var ANSI_RE = /\x1b(?:\[[0-9;?]*[a-zA-Z]|\][^\x07\x1b]*(?:\x07|\x1b\\)|[@-_])/g;
61996
- function stripAnsi(s) {
61997
- return s.replace(ANSI_RE, "");
61998
- }
61999
62030
  function loadResolved() {
62000
62031
  try {
62001
62032
  const raw = JSON.parse(import_fs73.default.readFileSync(RESOLVED_FILE, "utf-8"));
@@ -62089,10 +62120,10 @@ function registerDlpCommand(program2) {
62089
62120
  " " + import_chalk42.default.red("\u25CF") + " " + import_chalk42.default.white(e.dlpPattern ?? "Secret") + import_chalk42.default.dim(" " + fmtDate3(e.ts))
62090
62121
  );
62091
62122
  if (e.dlpSample) {
62092
- console.log(" " + import_chalk42.default.dim("Sample: ") + import_chalk42.default.yellow(stripAnsi(e.dlpSample)));
62123
+ console.log(" " + import_chalk42.default.dim("Sample: ") + import_chalk42.default.yellow(safeMessage(e.dlpSample)));
62093
62124
  }
62094
62125
  if (e.project) {
62095
- console.log(" " + import_chalk42.default.dim("Project: ") + import_chalk42.default.dim(stripAnsi(e.project)));
62126
+ console.log(" " + import_chalk42.default.dim("Project: ") + import_chalk42.default.dim(safeMessage(e.project)));
62096
62127
  }
62097
62128
  console.log("");
62098
62129
  }
@@ -62295,6 +62326,7 @@ function registerMaskCommand(program2) {
62295
62326
 
62296
62327
  // src/cli.ts
62297
62328
  init_blast();
62329
+ init_safe_text();
62298
62330
  var { version } = JSON.parse(
62299
62331
  import_fs77.default.readFileSync(import_path72.default.join(__dirname, "../package.json"), "utf-8")
62300
62332
  );
@@ -62327,7 +62359,7 @@ program.command("login").argument("[apiKey]", "Service/legacy key. Omit to log i
62327
62359
  cliVersion: version
62328
62360
  });
62329
62361
  if (!res.ok) {
62330
- console.error(import_chalk45.default.red(`\u2717 ${res.reason}`));
62362
+ console.error(import_chalk45.default.red(`\u2717 ${safeMessage(res.reason)}`));
62331
62363
  process.exitCode = 1;
62332
62364
  return;
62333
62365
  }
package/dist/cli.mjs CHANGED
@@ -2945,6 +2945,17 @@ async function evaluatePolicy(config, toolName, args, context = {}, hooks = {})
2945
2945
  function isIgnoredTool(toolName, config) {
2946
2946
  return matchesPattern(toolName, config.policy.ignoredTools);
2947
2947
  }
2948
+ function stripTerminalEscapes(s) {
2949
+ return s.replace(TERMINAL_ESCAPE_RE, "");
2950
+ }
2951
+ function stripControlChars(s) {
2952
+ return s.replace(CONTROL_CHAR_RE, "");
2953
+ }
2954
+ function safeMessage(value, max = 300) {
2955
+ const raw = typeof value === "string" ? value : value instanceof Error ? value.message : String(value ?? "");
2956
+ const s = stripTerminalEscapes(raw).replace(/\s+/g, " ").trim();
2957
+ return s.length > max ? s.slice(0, max - 1) + "\u2026" : s;
2958
+ }
2948
2959
  function isShieldVerdict(v) {
2949
2960
  return v === "allow" || v === "review" || v === "block";
2950
2961
  }
@@ -3162,7 +3173,10 @@ function computeSecurityScore(opts) {
3162
3173
  }
3163
3174
  function truncateBlastPath(full) {
3164
3175
  if (!full) return "";
3165
- const cleaned = full.replace(/[/\\]+$/, "");
3176
+ if (full.length > MAX_BLAST_PATH) full = full.slice(-MAX_BLAST_PATH);
3177
+ let end = full.length;
3178
+ while (end > 0 && (full[end - 1] === "/" || full[end - 1] === "\\")) end--;
3179
+ const cleaned = full.slice(0, end);
3166
3180
  const parts = cleaned.split(/[/\\]+/).filter((p) => p.length > 0);
3167
3181
  if (parts.length <= 2) {
3168
3182
  return cleaned.startsWith("~") && !cleaned.startsWith("~/") ? cleaned : cleaned.startsWith("~/") ? cleaned : parts.join("/");
@@ -3658,7 +3672,7 @@ function toScanFinding(c) {
3658
3672
  }
3659
3673
  function previewArgs(input, max) {
3660
3674
  const cmd = input.command ?? input.query ?? input.file_path ?? JSON.stringify(input);
3661
- const s = String(cmd).replace(TERMINAL_ESCAPE_RE, "").replace(/\s+/g, " ").trim();
3675
+ const s = stripTerminalEscapes(String(cmd)).replace(/\s+/g, " ").trim();
3662
3676
  return s.length > max ? s.slice(0, max - 1) + "\u2026" : s;
3663
3677
  }
3664
3678
  function makeFinding(args) {
@@ -3698,7 +3712,7 @@ function* stringValues(obj, depth = 0) {
3698
3712
  }
3699
3713
  for (const v of Object.values(obj)) yield* stringValues(v, depth + 1);
3700
3714
  }
3701
- var IBAN_LENGTH, B58, B58_INDEX, XPRV_VERSIONS, MAX, UNTRUSTED_TOOLS, SIGNALS, ASSIGNMENT_CONTEXT_RE, DLP_STOPWORDS, DLP_PATTERNS, DLP_PATTERNS_GLOBAL, SENSITIVE_PATH_PATTERNS, MAX_DEPTH, MAX_STRING_BYTES, MAX_JSON_PARSE_BYTES, DLP_SCAN_LIMITS, 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, SQL_DB_CLIS, SQL_DDL_RE, CHMOD_OPEN_PERM_TOKENS, COMMAND_WRAPPERS, INLINE_INTERPRETER, RUNNER_WRAPPERS, _redirStdinOps, _listOps, WRAPPER_TAKES_TARGET, FIND_EXEC_FLAGS, INTERP_LEADING_TARGET, NET_BINARIES, VALUE_FLAGS, FS_OP_CACHE_MAX, fsOpCache, stripDotSlash, REDIR_TRUNCATE_OPS, REDIR_FILE_IN_OPS, REDIR_HEREDOC_OPS, FIND_OPTIONS, COPY_RULE_OF, isReaderWord, positionalAfter, NONE, UNKNOWN, DEFAULT_EGRESS_ALLOWLIST, SOURCE_COMMANDS, SINK_COMMANDS, OBFUSCATORS, SENSITIVE_PATTERNS, FLAGS_WITH_VALUES, SSRF_MAX_HOST, METADATA_ADDRESSES, STRICT_TIERS, METADATA_HOSTNAMES, v4Octets, TIER_REASON, DESTINATION_ARGS, VERDICT_RANK, SQL_DML_KEYWORDS, aws_default, bash_safe_default, docker_default, filesystem_default, github_default, k8s_default, mongodb_default, postgres_default, project_jail_default, redis_default, BUILTIN_SHIELDS, LOOP_MAX_RECORDS, FINDING_TO_SIGNAL, SCAN_SIGNAL_WEIGHTS, LOOP_THRESHOLD_FOR_WASTE, DESTRUCTIVE_OP_RE, SENSITIVE_PATH_RE, FILE_TOOLS, PII_EMAIL_RE, PII_SSN_RE, PII_PHONE_RE, PII_CC16_RE, PII_CC15_RE, PII_IBAN_RE, REALTIME_PII_PATTERNS, MAX_PII_SCAN_BYTES, 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, CANONICAL_EXTRACTOR_VERSION, DEDUPE_PREVIEW_LEN, TERMINAL_ESCAPE_RE, ENGINE_VERSION;
3715
+ var IBAN_LENGTH, B58, B58_INDEX, XPRV_VERSIONS, MAX, UNTRUSTED_TOOLS, SIGNALS, ASSIGNMENT_CONTEXT_RE, DLP_STOPWORDS, DLP_PATTERNS, DLP_PATTERNS_GLOBAL, SENSITIVE_PATH_PATTERNS, MAX_DEPTH, MAX_STRING_BYTES, MAX_JSON_PARSE_BYTES, DLP_SCAN_LIMITS, 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, SQL_DB_CLIS, SQL_DDL_RE, CHMOD_OPEN_PERM_TOKENS, COMMAND_WRAPPERS, INLINE_INTERPRETER, RUNNER_WRAPPERS, _redirStdinOps, _listOps, WRAPPER_TAKES_TARGET, FIND_EXEC_FLAGS, INTERP_LEADING_TARGET, NET_BINARIES, VALUE_FLAGS, FS_OP_CACHE_MAX, fsOpCache, stripDotSlash, REDIR_TRUNCATE_OPS, REDIR_FILE_IN_OPS, REDIR_HEREDOC_OPS, FIND_OPTIONS, COPY_RULE_OF, isReaderWord, positionalAfter, NONE, UNKNOWN, DEFAULT_EGRESS_ALLOWLIST, SOURCE_COMMANDS, SINK_COMMANDS, OBFUSCATORS, SENSITIVE_PATTERNS, FLAGS_WITH_VALUES, SSRF_MAX_HOST, METADATA_ADDRESSES, STRICT_TIERS, METADATA_HOSTNAMES, v4Octets, TIER_REASON, DESTINATION_ARGS, VERDICT_RANK, SQL_DML_KEYWORDS, TERMINAL_ESCAPE_RE, CONTROL_CHAR_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, LOOP_MAX_RECORDS, FINDING_TO_SIGNAL, SCAN_SIGNAL_WEIGHTS, LOOP_THRESHOLD_FOR_WASTE, MAX_BLAST_PATH, DESTRUCTIVE_OP_RE, SENSITIVE_PATH_RE, FILE_TOOLS, PII_EMAIL_RE, PII_SSN_RE, PII_PHONE_RE, PII_CC16_RE, PII_CC15_RE, PII_IBAN_RE, REALTIME_PII_PATTERNS, MAX_PII_SCAN_BYTES, 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, CANONICAL_EXTRACTOR_VERSION, DEDUPE_PREVIEW_LEN, ENGINE_VERSION;
3702
3716
  var init_dist = __esm({
3703
3717
  "packages/policy-engine/dist/index.mjs"() {
3704
3718
  "use strict";
@@ -5325,6 +5339,8 @@ var init_dist = __esm({
5325
5339
  block: 2
5326
5340
  };
5327
5341
  SQL_DML_KEYWORDS = /* @__PURE__ */ new Set(["select", "insert", "update", "delete", "merge", "upsert"]);
5342
+ TERMINAL_ESCAPE_RE = /\x1b\[[0-9;?]*[A-Za-z]|\x1b\][^\x07\x1b]*(?:\x07|\x1b\\)|\x1b[@-_]|[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]/g;
5343
+ CONTROL_CHAR_RE = /[\x00-\x1F\x7F]/g;
5328
5344
  aws_default = {
5329
5345
  name: "aws",
5330
5346
  description: "Protects AWS infrastructure from destructive AI operations",
@@ -6102,6 +6118,7 @@ var init_dist = __esm({
6102
6118
  longOutputRedactions: 1
6103
6119
  };
6104
6120
  LOOP_THRESHOLD_FOR_WASTE = 3;
6121
+ MAX_BLAST_PATH = 4096;
6105
6122
  DESTRUCTIVE_OP_RE = /\brm\s+-[rRf]+\b|\bDROP\s+(TABLE|DATABASE|COLLECTION|SCHEMA)\b|\bTRUNCATE\s+TABLE\b|\bgit\s+push\s+(--force|-f)\b|\bFLUSHALL\b|\bFLUSHDB\b|\bkubectl\s+delete\b|\bhelm\s+uninstall\b/i;
6106
6123
  SENSITIVE_PATH_RE = /[\\/]\.aws(?:[\\/]|$)|^\.aws[\\/]|[\\/]\.ssh(?:[\\/]|$)|^\.ssh[\\/]|(?:^|[\\/])\.env(?![\w-])(?:[\w.-]*\.local$|(?!\.(?:example|sample|template)\b)(?!\.test$)[\w.-]*$)|\.config\/gcloud\/credentials\.db\b|\.docker\/config\.json\b|\.netrc\b|\.npmrc\b|\.node9\/credentials\.json\b/i;
6107
6124
  FILE_TOOLS = /* @__PURE__ */ new Set([
@@ -6182,8 +6199,6 @@ var init_dist = __esm({
6182
6199
  LONG_OUTPUT_THRESHOLD_BYTES = 100 * 1024;
6183
6200
  CANONICAL_EXTRACTOR_VERSION = "canonical-v15";
6184
6201
  DEDUPE_PREVIEW_LEN = 120;
6185
- TERMINAL_ESCAPE_RE = // eslint-disable-next-line no-control-regex
6186
- /\x1b\[[0-9;?]*[A-Za-z]|\x1b\][^\x07\x1b]*(?:\x07|\x1b\\)|\x1b[@-_]|[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]/g;
6187
6202
  ENGINE_VERSION = "1.4.0";
6188
6203
  }
6189
6204
  });
@@ -8731,6 +8746,14 @@ var init_context_sniper = __esm({
8731
8746
  }
8732
8747
  });
8733
8748
 
8749
+ // src/utils/safe-text.ts
8750
+ var init_safe_text = __esm({
8751
+ "src/utils/safe-text.ts"() {
8752
+ "use strict";
8753
+ init_dist();
8754
+ }
8755
+ });
8756
+
8734
8757
  // src/ui/native.ts
8735
8758
  import { spawn } from "child_process";
8736
8759
  import path11 from "path";
@@ -8842,7 +8865,7 @@ function escapePango(text) {
8842
8865
  function buildPlainMessage(toolName, formattedArgs, agent, explainableLabel, locked, allowCount = 1, ruleDescription) {
8843
8866
  const lines = [];
8844
8867
  if (locked) lines.push("\u26A0\uFE0F LOCKED BY ADMIN POLICY\n");
8845
- const safeAgent = (agent ?? "AI Agent").replace(/\x1b(?:\[[0-9;?]*[a-zA-Z]|\][^\x07\x1b]*(?:\x07|\x1b\\)|[@-_])/g, "").slice(0, 80);
8868
+ const safeAgent = safeMessage(agent ?? "AI Agent", 80);
8846
8869
  lines.push(`\u{1F916} ${safeAgent} | \u{1F527} ${toolName}`);
8847
8870
  lines.push(`\u{1F6E1}\uFE0F ${explainableLabel || "Security Policy"}`);
8848
8871
  if (ruleDescription) lines.push(`\u2139 ${ruleDescription}`);
@@ -8977,6 +9000,7 @@ var init_native = __esm({
8977
9000
  "src/ui/native.ts"() {
8978
9001
  "use strict";
8979
9002
  init_context_sniper();
9003
+ init_safe_text();
8980
9004
  isTestEnv = () => {
8981
9005
  return process.env.NODE_ENV === "test" || process.env.VITEST === "true" || !!process.env.VITEST || process.env.CI === "true" || !!process.env.CI || process.env.NODE9_TESTING === "1";
8982
9006
  };
@@ -9366,14 +9390,14 @@ async function resolveNode9SaaS(requestId, creds, approved, decidedBy) {
9366
9390
  if (!res.ok) {
9367
9391
  fs12.appendFileSync(
9368
9392
  HOOK_DEBUG_LOG,
9369
- `[resolve-cloud] PATCH ${resolveUrl} \u2192 HTTP ${res.status}
9393
+ `[resolve-cloud] PATCH ${safeMessage(resolveUrl, 200)} \u2192 HTTP ${res.status}
9370
9394
  `
9371
9395
  );
9372
9396
  }
9373
9397
  } catch (err2) {
9374
9398
  fs12.appendFileSync(
9375
9399
  HOOK_DEBUG_LOG,
9376
- `[resolve-cloud] PATCH failed for ${requestId}: ${err2.message}
9400
+ `[resolve-cloud] PATCH failed for ${safeMessage(requestId, 64)}: ${safeMessage(err2)}
9377
9401
  `
9378
9402
  );
9379
9403
  }
@@ -9383,6 +9407,7 @@ var init_cloud = __esm({
9383
9407
  "src/auth/cloud.ts"() {
9384
9408
  "use strict";
9385
9409
  init_audit();
9410
+ init_safe_text();
9386
9411
  DLP_SAMPLE_MAX_LEN = 200;
9387
9412
  DLP_PATTERN_MAX_LEN = 100;
9388
9413
  KNOWN_CHECKED_BY = /* @__PURE__ */ new Set([
@@ -9515,9 +9540,8 @@ async function authorizeHeadless(toolName, args, meta, options) {
9515
9540
  if (!options?.calledFromDaemon) {
9516
9541
  const actId = randomUUID2();
9517
9542
  const actTs = Date.now();
9518
- const stripAnsi2 = (s) => s.replace(/\x1b(?:\[[0-9;?]*[a-zA-Z]|\][^\x07\x1b]*(?:\x07|\x1b\\)|[@-_])/g, "");
9519
- const sanitizedAgent = meta?.agent ? stripAnsi2(meta.agent).slice(0, 80) : void 0;
9520
- const sanitizedMcpServer = meta?.mcpServer ? stripAnsi2(meta.mcpServer).slice(0, 40) : void 0;
9543
+ const sanitizedAgent = meta?.agent ? safeMessage(meta.agent, 80) : void 0;
9544
+ const sanitizedMcpServer = meta?.mcpServer ? safeMessage(meta.mcpServer, 40) : void 0;
9521
9545
  const socketOk = await notifyActivity({
9522
9546
  id: actId,
9523
9547
  ts: actTs,
@@ -10392,6 +10416,7 @@ var init_orchestrator = __esm({
10392
10416
  init_loop_detector();
10393
10417
  init_shields();
10394
10418
  init_jail();
10419
+ init_safe_text();
10395
10420
  WRITE_TOOLS = /* @__PURE__ */ new Set([
10396
10421
  "write",
10397
10422
  "write_file",
@@ -14997,7 +15022,7 @@ async function postCostBatches(apiUrl, apiKey, machineId, entries) {
14997
15022
  }
14998
15023
  }
14999
15024
  } catch (err2) {
15000
- fs26.appendFileSync(HOOK_DEBUG_LOG, `[cost-sync] ${err2.message}
15025
+ fs26.appendFileSync(HOOK_DEBUG_LOG, `[cost-sync] ${safeMessage(err2)}
15001
15026
  `);
15002
15027
  }
15003
15028
  }
@@ -15036,6 +15061,7 @@ var init_costSync = __esm({
15036
15061
  init_cost_gemini();
15037
15062
  init_cost_copilot();
15038
15063
  init_session_files();
15064
+ init_safe_text();
15039
15065
  SYNC_INTERVAL_MS = 10 * 60 * 1e3;
15040
15066
  claudeSource = {
15041
15067
  id: "claude",
@@ -15871,9 +15897,6 @@ function fmtTs(ts) {
15871
15897
  return ts.slice(0, 10);
15872
15898
  }
15873
15899
  }
15874
- function stripTerminalEscapes(s) {
15875
- return s.replace(TERMINAL_ESCAPE_RE2, "");
15876
- }
15877
15900
  function preview(input, max) {
15878
15901
  const cmd = input.command ?? input.query ?? input.file_path ?? JSON.stringify(input);
15879
15902
  const s = stripTerminalEscapes(String(cmd)).replace(/\s+/g, " ").trim();
@@ -18588,7 +18611,7 @@ function registerScanCommand(program2) {
18588
18611
  }
18589
18612
  );
18590
18613
  }
18591
- var toolInspectionMap, CODE_EXTENSIONS, SELF_OUTPUT_MARKERS, TERMINAL_ESCAPE_RE2, LOOP_TOOLS, LOOP_THRESHOLD, LOOP_TIMESPAN_THRESHOLD_MS, STUCK_TOOLS_MIN_WASTE, STUCK_TOOLS_LIMIT, RECURRING_SESSION_THRESHOLD, STALE_AGE_DAYS, classifyRuleSeverity2, narrativeRuleLabel2;
18614
+ var toolInspectionMap, CODE_EXTENSIONS, SELF_OUTPUT_MARKERS, LOOP_TOOLS, LOOP_THRESHOLD, LOOP_TIMESPAN_THRESHOLD_MS, STUCK_TOOLS_MIN_WASTE, STUCK_TOOLS_LIMIT, RECURRING_SESSION_THRESHOLD, STALE_AGE_DAYS, classifyRuleSeverity2, narrativeRuleLabel2;
18592
18615
  var init_scan = __esm({
18593
18616
  "src/cli/commands/scan.ts"() {
18594
18617
  "use strict";
@@ -18614,6 +18637,7 @@ var init_scan = __esm({
18614
18637
  init_scan_json();
18615
18638
  init_session_files();
18616
18639
  init_scan_history();
18640
+ init_safe_text();
18617
18641
  toolInspectionMap = DEFAULT_CONFIG.policy.toolInspection;
18618
18642
  CODE_EXTENSIONS = /* @__PURE__ */ new Set([
18619
18643
  ".ts",
@@ -18648,7 +18672,6 @@ var init_scan = __esm({
18648
18672
  /\bseverity:\s*['"](?:block|review|allow)['"]/,
18649
18673
  /NODE9 SECURITY ALERT/
18650
18674
  ];
18651
- TERMINAL_ESCAPE_RE2 = /\x1b\[[0-9;?]*[A-Za-z]|\x1b\][^\x07\x1b]*(?:\x07|\x1b\\)|\x1b[@-_]|[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]/g;
18652
18675
  LOOP_TOOLS = /* @__PURE__ */ new Set([
18653
18676
  "bash",
18654
18677
  "execute_bash",
@@ -19139,7 +19162,7 @@ function atomicWriteSync2(filePath, data, options) {
19139
19162
  function redactArgs(value) {
19140
19163
  if (!value || typeof value !== "object") return value;
19141
19164
  if (Array.isArray(value)) return value.map(redactArgs);
19142
- const result = {};
19165
+ const result = /* @__PURE__ */ Object.create(null);
19143
19166
  for (const [k, v] of Object.entries(value)) {
19144
19167
  result[k] = SECRET_KEY_RE.test(k) ? "[REDACTED]" : redactArgs(v);
19145
19168
  }
@@ -23491,7 +23514,7 @@ data: ${JSON.stringify(item.data)}
23491
23514
  if (req.method === "GET" && pathname === "/state/check") {
23492
23515
  const predicatesParam = reqUrl.searchParams.get("predicates") ?? "";
23493
23516
  const predicates = predicatesParam.split(",").filter(Boolean);
23494
- const results = {};
23517
+ const results = /* @__PURE__ */ Object.create(null);
23495
23518
  for (const p of predicates) {
23496
23519
  results[p] = sessionHistory.checkPredicate(p);
23497
23520
  }
@@ -51226,7 +51249,7 @@ async function startTail(options = {}) {
51226
51249
  req.on("error", (err2) => {
51227
51250
  const msg = err2.code === "ECONNREFUSED" ? "Daemon is not running. Start it with: node9 daemon start" : err2.message;
51228
51251
  console.error(chalk44.red(`
51229
- \u274C ${msg}`));
51252
+ \u274C ${safeMessage(msg)}`));
51230
51253
  process.exit(1);
51231
51254
  });
51232
51255
  }
@@ -51237,6 +51260,7 @@ var init_tail = __esm({
51237
51260
  init_startup_log();
51238
51261
  init_daemon2();
51239
51262
  init_daemon();
51263
+ init_safe_text();
51240
51264
  PID_FILE = path74.join(os66.homedir(), ".node9", "daemon.pid");
51241
51265
  ICONS = {
51242
51266
  bash: "\u{1F4BB}",
@@ -51883,9 +51907,7 @@ function shellInvocation(command) {
51883
51907
  }
51884
51908
 
51885
51909
  // src/proxy/index.ts
51886
- function sanitize(value) {
51887
- return value.replace(/[\x00-\x1F\x7F]/g, "");
51888
- }
51910
+ init_safe_text();
51889
51911
  async function runProxy(targetCommand) {
51890
51912
  const commandParts = parseCommandString(targetCommand);
51891
51913
  const cmd = commandParts[0];
@@ -51921,7 +51943,7 @@ async function runProxy(targetCommand) {
51921
51943
  try {
51922
51944
  const name = message.params?.name || message.params?.tool_name || "unknown";
51923
51945
  const toolArgs = message.params?.arguments || message.params?.tool_input || {};
51924
- const result = await authorizeHeadless(sanitize(name), toolArgs, {
51946
+ const result = await authorizeHeadless(stripControlChars(name), toolArgs, {
51925
51947
  agent: "Proxy/MCP"
51926
51948
  });
51927
51949
  if (!result.approved) {
@@ -52169,17 +52191,27 @@ init_machine_id();
52169
52191
 
52170
52192
  // src/utils/open-browser.ts
52171
52193
  import { spawn as spawn4 } from "child_process";
52194
+ function isOpenableUrl(url) {
52195
+ let u;
52196
+ try {
52197
+ u = new URL(url);
52198
+ } catch {
52199
+ return false;
52200
+ }
52201
+ if (u.protocol !== "https:" && u.protocol !== "http:") return false;
52202
+ return !/[\x00-\x20\x7F"'`]/.test(url);
52203
+ }
52172
52204
  function openBrowser(url) {
52205
+ if (!isOpenableUrl(url)) return false;
52173
52206
  if (process.env.SSH_CONNECTION || process.env.SSH_TTY) return false;
52174
52207
  if (process.platform === "linux" && !process.env.DISPLAY && !process.env.WAYLAND_DISPLAY) {
52175
52208
  return false;
52176
52209
  }
52177
- const opener = process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open";
52210
+ const [cmd, args] = process.platform === "darwin" ? ["open", [url]] : process.platform === "win32" ? ["rundll32.exe", ["url.dll,FileProtocolHandler", url]] : ["xdg-open", [url]];
52178
52211
  try {
52179
- const child = spawn4(opener, [url], {
52212
+ const child = spawn4(cmd, args, {
52180
52213
  stdio: "ignore",
52181
- detached: true,
52182
- shell: process.platform === "win32"
52214
+ detached: true
52183
52215
  });
52184
52216
  child.on("error", () => {
52185
52217
  });
@@ -52240,6 +52272,7 @@ function postJson2(url, body, bearer) {
52240
52272
  }
52241
52273
 
52242
52274
  // src/auth/device-login.ts
52275
+ init_safe_text();
52243
52276
  var sleep = (ms) => new Promise((r) => setTimeout(r, ms));
52244
52277
  async function runDeviceLogin(opts = {}) {
52245
52278
  const startUrl = resolveCloudEndpoint("/device/start", opts.apiUrl);
@@ -52255,14 +52288,16 @@ async function runDeviceLogin(opts = {}) {
52255
52288
  } catch (e) {
52256
52289
  return {
52257
52290
  ok: false,
52258
- reason: `Could not reach the node9 cloud: ${e instanceof Error ? e.message : String(e)}`
52291
+ reason: `Could not reach the node9 cloud: ${safeMessage(e)}`
52259
52292
  };
52260
52293
  }
52261
52294
  console.log("");
52262
52295
  console.log(` Open this link to approve the connection:`);
52263
- console.log(` ${chalk11.cyan.underline(start.verificationUrl)}`);
52296
+ console.log(` ${chalk11.cyan.underline(safeMessage(start.verificationUrl, 200))}`);
52264
52297
  console.log("");
52265
- console.log(` Code: ${chalk11.bold(start.userCode)} ${chalk11.gray("(match it in the browser)")}`);
52298
+ console.log(
52299
+ ` Code: ${chalk11.bold(safeMessage(start.userCode, 40))} ${chalk11.gray("(match it in the browser)")}`
52300
+ );
52266
52301
  console.log("");
52267
52302
  const opened = opts.noBrowser ? false : openBrowser(start.verificationUrl);
52268
52303
  console.log(
@@ -52308,6 +52343,7 @@ import * as fs50 from "fs";
52308
52343
  import * as os45 from "os";
52309
52344
  import * as path48 from "path";
52310
52345
  import chalk12 from "chalk";
52346
+ init_safe_text();
52311
52347
  async function revokeSelf(creds) {
52312
52348
  const url = creds.apiUrl.replace(/\/$/, "") + "/machines/self/disconnect";
52313
52349
  try {
@@ -52348,7 +52384,7 @@ function registerLogoutCommand(program2) {
52348
52384
  } else if (res.outcome === "already") {
52349
52385
  console.log(chalk12.gray("\u2713 Cloud: this machine was already disconnected."));
52350
52386
  } else {
52351
- console.log(chalk12.yellow(`\u26A0 Could not reach the cloud (${res.detail}).`));
52387
+ console.log(chalk12.yellow(`\u26A0 Could not reach the cloud (${safeMessage(res.detail)}).`));
52352
52388
  console.log(
52353
52389
  chalk12.yellow(" The key was removed locally, but is still listed in the dashboard \u2014")
52354
52390
  );
@@ -52655,9 +52691,7 @@ function discardPendingReview(key, now = Date.now()) {
52655
52691
 
52656
52692
  // src/cli/commands/check.ts
52657
52693
  init_hook_payload();
52658
- function sanitize2(value) {
52659
- return value.replace(/[\x00-\x1F\x7F]/g, "");
52660
- }
52694
+ init_safe_text();
52661
52695
  function detectAiAgent(payload) {
52662
52696
  const meta = payload.meta;
52663
52697
  if (meta && typeof meta === "object") {
@@ -52903,10 +52937,13 @@ RAW: ${raw}
52903
52937
  const logPath = path51.join(os48.homedir(), ".node9", "hook-debug.log");
52904
52938
  if (!fs53.existsSync(path51.dirname(logPath)))
52905
52939
  fs53.mkdirSync(path51.dirname(logPath), { recursive: true });
52906
- fs53.appendFileSync(logPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] STDIN: ${raw}
52907
- `);
52940
+ fs53.appendFileSync(
52941
+ logPath,
52942
+ `[${(/* @__PURE__ */ new Date()).toISOString()}] STDIN: ${JSON.stringify(raw)}
52943
+ `
52944
+ );
52908
52945
  }
52909
- const rawToolName = sanitize2(extractToolName(payload));
52946
+ const rawToolName = stripControlChars(extractToolName(payload));
52910
52947
  const toolName = canonicalToolName(rawToolName);
52911
52948
  const toolInput = canonicalToolInput(rawToolName, extractToolInput(payload));
52912
52949
  const agent = agentOverride ?? detectAiAgent(payload);
@@ -53296,6 +53333,7 @@ function containsShellMetachar(token) {
53296
53333
 
53297
53334
  // src/cli/commands/log.ts
53298
53335
  init_hook_payload();
53336
+ init_safe_text();
53299
53337
  var TEST_COMMAND_RE2 = /(?:^|\s)(npm\s+(?:run\s+)?test|npx\s+(?:vitest|jest|mocha)|yarn\s+(?:run\s+)?test|pnpm\s+(?:run\s+)?test|vitest|jest|mocha|pytest|py\.test|cargo\s+test|go\s+test|bundle\s+exec\s+rspec|rspec|phpunit|dotnet\s+test)\b/i;
53300
53338
  function detectTestResult(command, output) {
53301
53339
  if (!TEST_COMMAND_RE2.test(command)) return null;
@@ -53314,9 +53352,6 @@ var CONFIDENCE_RANK = { low: 0, medium: 1, high: 2 };
53314
53352
  function atLeastConfidence(c, min) {
53315
53353
  return CONFIDENCE_RANK[c] >= CONFIDENCE_RANK[min];
53316
53354
  }
53317
- function sanitize3(value) {
53318
- return value.replace(/[\x00-\x1F\x7F]/g, "");
53319
- }
53320
53355
  function scanCoveredEverything(value, depth = 0) {
53321
53356
  if (value === null || value === void 0) return true;
53322
53357
  if (typeof value === "string") return value.length <= DLP_SCAN_LIMITS.maxStringBytes;
@@ -53355,7 +53390,7 @@ function registerLogCommand(program2) {
53355
53390
  if (!raw || raw.trim() === "") process.exit(0);
53356
53391
  const payload = JSON.parse(raw);
53357
53392
  if (payload.toolCall === null) process.exit(0);
53358
- const rawToolName = sanitize3(extractToolName(payload, "unknown"));
53393
+ const rawToolName = stripControlChars(extractToolName(payload, "unknown"));
53359
53394
  const tool = canonicalToolName(rawToolName);
53360
53395
  const rawInput = canonicalToolInput(rawToolName, extractToolInput(payload));
53361
53396
  const metaTag = (() => {
@@ -56187,6 +56222,7 @@ import http5 from "http";
56187
56222
  import https8 from "https";
56188
56223
  import { URL as URL5 } from "url";
56189
56224
  import chalk22 from "chalk";
56225
+ init_safe_text();
56190
56226
  function resolveConnectUrl(apiUrl) {
56191
56227
  return resolveCloudEndpoint("/cli/connect", apiUrl);
56192
56228
  }
@@ -56245,7 +56281,7 @@ function registerConnectCommand(program2) {
56245
56281
  try {
56246
56282
  resp = await postConnect(resolveConnectUrl(options.apiUrl), token);
56247
56283
  } catch (e) {
56248
- console.error(chalk22.red(`\u2717 ${e instanceof Error ? e.message : "Connect failed."}`));
56284
+ console.error(chalk22.red(`\u2717 ${safeMessage(e) || "Connect failed."}`));
56249
56285
  process.exitCode = 1;
56250
56286
  return;
56251
56287
  }
@@ -56332,9 +56368,7 @@ init_mcp_pin();
56332
56368
  init_mcp_cmd();
56333
56369
  init_mcp_tools();
56334
56370
  init_daemon();
56335
- function sanitize4(value) {
56336
- return value.replace(/[\x00-\x1F\x7F]/g, "");
56337
- }
56371
+ init_safe_text();
56338
56372
  var RPC_INVALID_REQUEST = -32600;
56339
56373
  var RPC_SERVER_ERROR = -32e3;
56340
56374
  function isValidId(id) {
@@ -56358,7 +56392,7 @@ function normalizeClientName(name) {
56358
56392
  if (lower.includes("gemini")) return "Gemini";
56359
56393
  if (lower.includes("cline")) return "Cline";
56360
56394
  if (lower.includes("continue")) return "Continue";
56361
- const sanitized = sanitize4(name).slice(0, 40);
56395
+ const sanitized = stripControlChars(name).slice(0, 40);
56362
56396
  return sanitized.length > 0 ? sanitized : void 0;
56363
56397
  }
56364
56398
  function reportPinMismatchToCloud(serverKey, serverLabel, agent) {
@@ -56538,7 +56572,7 @@ async function runMcpGateway(upstreamCommand, configName) {
56538
56572
  if (!deferredStdinEnd) agentIn.pause();
56539
56573
  authPending = true;
56540
56574
  try {
56541
- const toolName = sanitize4(
56575
+ const toolName = stripControlChars(
56542
56576
  String(message.params?.name ?? message.params?.tool_name ?? "unknown")
56543
56577
  );
56544
56578
  const toolArgs = message.params?.arguments ?? message.params?.tool_input ?? {};
@@ -61978,16 +62012,13 @@ Persistent decisions (${entries.length})
61978
62012
  }
61979
62013
 
61980
62014
  // src/cli/commands/dlp.ts
62015
+ init_safe_text();
61981
62016
  import chalk42 from "chalk";
61982
62017
  import fs77 from "fs";
61983
62018
  import path72 from "path";
61984
62019
  import os64 from "os";
61985
62020
  var AUDIT_LOG = path72.join(os64.homedir(), ".node9", "audit.log");
61986
62021
  var RESOLVED_FILE = path72.join(os64.homedir(), ".node9", "dlp-resolved.json");
61987
- var ANSI_RE = /\x1b(?:\[[0-9;?]*[a-zA-Z]|\][^\x07\x1b]*(?:\x07|\x1b\\)|[@-_])/g;
61988
- function stripAnsi(s) {
61989
- return s.replace(ANSI_RE, "");
61990
- }
61991
62022
  function loadResolved() {
61992
62023
  try {
61993
62024
  const raw = JSON.parse(fs77.readFileSync(RESOLVED_FILE, "utf-8"));
@@ -62081,10 +62112,10 @@ function registerDlpCommand(program2) {
62081
62112
  " " + chalk42.red("\u25CF") + " " + chalk42.white(e.dlpPattern ?? "Secret") + chalk42.dim(" " + fmtDate3(e.ts))
62082
62113
  );
62083
62114
  if (e.dlpSample) {
62084
- console.log(" " + chalk42.dim("Sample: ") + chalk42.yellow(stripAnsi(e.dlpSample)));
62115
+ console.log(" " + chalk42.dim("Sample: ") + chalk42.yellow(safeMessage(e.dlpSample)));
62085
62116
  }
62086
62117
  if (e.project) {
62087
- console.log(" " + chalk42.dim("Project: ") + chalk42.dim(stripAnsi(e.project)));
62118
+ console.log(" " + chalk42.dim("Project: ") + chalk42.dim(safeMessage(e.project)));
62088
62119
  }
62089
62120
  console.log("");
62090
62121
  }
@@ -62287,6 +62318,7 @@ function registerMaskCommand(program2) {
62287
62318
 
62288
62319
  // src/cli.ts
62289
62320
  init_blast();
62321
+ init_safe_text();
62290
62322
  var { version } = JSON.parse(
62291
62323
  fs81.readFileSync(path76.join(__dirname, "../package.json"), "utf-8")
62292
62324
  );
@@ -62319,7 +62351,7 @@ program.command("login").argument("[apiKey]", "Service/legacy key. Omit to log i
62319
62351
  cliVersion: version
62320
62352
  });
62321
62353
  if (!res.ok) {
62322
- console.error(chalk45.red(`\u2717 ${res.reason}`));
62354
+ console.error(chalk45.red(`\u2717 ${safeMessage(res.reason)}`));
62323
62355
  process.exitCode = 1;
62324
62356
  return;
62325
62357
  }
@@ -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",
@@ -5678,6 +5682,14 @@ var init_session_files = __esm({
5678
5682
  }
5679
5683
  });
5680
5684
 
5685
+ // src/utils/safe-text.ts
5686
+ var init_safe_text = __esm({
5687
+ "src/utils/safe-text.ts"() {
5688
+ "use strict";
5689
+ init_dist();
5690
+ }
5691
+ });
5692
+
5681
5693
  // src/costSync.ts
5682
5694
  function decodeProjectDirName(dirName) {
5683
5695
  return dirName.replace(/-/g, "/");
@@ -5693,6 +5705,7 @@ var init_costSync = __esm({
5693
5705
  init_cost_gemini();
5694
5706
  init_cost_copilot();
5695
5707
  init_session_files();
5708
+ init_safe_text();
5696
5709
  SYNC_INTERVAL_MS = 10 * 60 * 1e3;
5697
5710
  }
5698
5711
  });
@@ -6666,9 +6679,6 @@ function isNode9SelfOutput(text) {
6666
6679
  }
6667
6680
  return false;
6668
6681
  }
6669
- function stripTerminalEscapes(s) {
6670
- return s.replace(TERMINAL_ESCAPE_RE, "");
6671
- }
6672
6682
  function preview(input, max) {
6673
6683
  const cmd = input.command ?? input.query ?? input.file_path ?? JSON.stringify(input);
6674
6684
  const s = stripTerminalEscapes(String(cmd)).replace(/\s+/g, " ").trim();
@@ -7644,7 +7654,7 @@ function scanCodexHistory(startDate, onProgress, onLine) {
7644
7654
  }
7645
7655
  return result;
7646
7656
  }
7647
- var toolInspectionMap, CODE_EXTENSIONS, SELF_OUTPUT_MARKERS, TERMINAL_ESCAPE_RE, LOOP_TOOLS, LOOP_THRESHOLD, LOOP_TIMESPAN_THRESHOLD_MS;
7657
+ var toolInspectionMap, CODE_EXTENSIONS, SELF_OUTPUT_MARKERS, LOOP_TOOLS, LOOP_THRESHOLD, LOOP_TIMESPAN_THRESHOLD_MS;
7648
7658
  var init_scan = __esm({
7649
7659
  "src/cli/commands/scan.ts"() {
7650
7660
  "use strict";
@@ -7670,6 +7680,7 @@ var init_scan = __esm({
7670
7680
  init_scan_json();
7671
7681
  init_session_files();
7672
7682
  init_scan_history();
7683
+ init_safe_text();
7673
7684
  toolInspectionMap = DEFAULT_CONFIG.policy.toolInspection;
7674
7685
  CODE_EXTENSIONS = /* @__PURE__ */ new Set([
7675
7686
  ".ts",
@@ -7704,7 +7715,6 @@ var init_scan = __esm({
7704
7715
  /\bseverity:\s*['"](?:block|review|allow)['"]/,
7705
7716
  /NODE9 SECURITY ALERT/
7706
7717
  ];
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
7718
  LOOP_TOOLS = /* @__PURE__ */ new Set([
7709
7719
  "bash",
7710
7720
  "execute_bash",
package/dist/index.js CHANGED
@@ -4484,6 +4484,15 @@ async function evaluatePolicy(config, toolName, args, context = {}, hooks = {})
4484
4484
  function isIgnoredTool(toolName, config) {
4485
4485
  return matchesPattern(toolName, config.policy.ignoredTools);
4486
4486
  }
4487
+ var TERMINAL_ESCAPE_RE = /\x1b\[[0-9;?]*[A-Za-z]|\x1b\][^\x07\x1b]*(?:\x07|\x1b\\)|\x1b[@-_]|[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]/g;
4488
+ function stripTerminalEscapes(s) {
4489
+ return s.replace(TERMINAL_ESCAPE_RE, "");
4490
+ }
4491
+ function safeMessage(value, max = 300) {
4492
+ const raw = typeof value === "string" ? value : value instanceof Error ? value.message : String(value ?? "");
4493
+ const s = stripTerminalEscapes(raw).replace(/\s+/g, " ").trim();
4494
+ return s.length > max ? s.slice(0, max - 1) + "\u2026" : s;
4495
+ }
4487
4496
  var aws_default = {
4488
4497
  name: "aws",
4489
4498
  description: "Protects AWS infrastructure from destructive AI operations",
@@ -7483,7 +7492,7 @@ function escapePango(text) {
7483
7492
  function buildPlainMessage(toolName, formattedArgs, agent, explainableLabel, locked, allowCount = 1, ruleDescription) {
7484
7493
  const lines = [];
7485
7494
  if (locked) lines.push("\u26A0\uFE0F LOCKED BY ADMIN POLICY\n");
7486
- const safeAgent = (agent ?? "AI Agent").replace(/\x1b(?:\[[0-9;?]*[a-zA-Z]|\][^\x07\x1b]*(?:\x07|\x1b\\)|[@-_])/g, "").slice(0, 80);
7495
+ const safeAgent = safeMessage(agent ?? "AI Agent", 80);
7487
7496
  lines.push(`\u{1F916} ${safeAgent} | \u{1F527} ${toolName}`);
7488
7497
  lines.push(`\u{1F6E1}\uFE0F ${explainableLabel || "Security Policy"}`);
7489
7498
  if (ruleDescription) lines.push(`\u2139 ${ruleDescription}`);
@@ -7811,14 +7820,14 @@ async function resolveNode9SaaS(requestId, creds, approved, decidedBy) {
7811
7820
  if (!res.ok) {
7812
7821
  import_fs11.default.appendFileSync(
7813
7822
  HOOK_DEBUG_LOG,
7814
- `[resolve-cloud] PATCH ${resolveUrl} \u2192 HTTP ${res.status}
7823
+ `[resolve-cloud] PATCH ${safeMessage(resolveUrl, 200)} \u2192 HTTP ${res.status}
7815
7824
  `
7816
7825
  );
7817
7826
  }
7818
7827
  } catch (err) {
7819
7828
  import_fs11.default.appendFileSync(
7820
7829
  HOOK_DEBUG_LOG,
7821
- `[resolve-cloud] PATCH failed for ${requestId}: ${err.message}
7830
+ `[resolve-cloud] PATCH failed for ${safeMessage(requestId, 64)}: ${safeMessage(err)}
7822
7831
  `
7823
7832
  );
7824
7833
  }
@@ -7919,9 +7928,8 @@ async function authorizeHeadless(toolName, args, meta, options) {
7919
7928
  if (!options?.calledFromDaemon) {
7920
7929
  const actId = (0, import_crypto5.randomUUID)();
7921
7930
  const actTs = Date.now();
7922
- const stripAnsi = (s) => s.replace(/\x1b(?:\[[0-9;?]*[a-zA-Z]|\][^\x07\x1b]*(?:\x07|\x1b\\)|[@-_])/g, "");
7923
- const sanitizedAgent = meta?.agent ? stripAnsi(meta.agent).slice(0, 80) : void 0;
7924
- const sanitizedMcpServer = meta?.mcpServer ? stripAnsi(meta.mcpServer).slice(0, 40) : void 0;
7931
+ const sanitizedAgent = meta?.agent ? safeMessage(meta.agent, 80) : void 0;
7932
+ const sanitizedMcpServer = meta?.mcpServer ? safeMessage(meta.mcpServer, 40) : void 0;
7925
7933
  const socketOk = await notifyActivity({
7926
7934
  id: actId,
7927
7935
  ts: actTs,
package/dist/index.mjs CHANGED
@@ -4454,6 +4454,15 @@ async function evaluatePolicy(config, toolName, args, context = {}, hooks = {})
4454
4454
  function isIgnoredTool(toolName, config) {
4455
4455
  return matchesPattern(toolName, config.policy.ignoredTools);
4456
4456
  }
4457
+ var TERMINAL_ESCAPE_RE = /\x1b\[[0-9;?]*[A-Za-z]|\x1b\][^\x07\x1b]*(?:\x07|\x1b\\)|\x1b[@-_]|[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]/g;
4458
+ function stripTerminalEscapes(s) {
4459
+ return s.replace(TERMINAL_ESCAPE_RE, "");
4460
+ }
4461
+ function safeMessage(value, max = 300) {
4462
+ const raw = typeof value === "string" ? value : value instanceof Error ? value.message : String(value ?? "");
4463
+ const s = stripTerminalEscapes(raw).replace(/\s+/g, " ").trim();
4464
+ return s.length > max ? s.slice(0, max - 1) + "\u2026" : s;
4465
+ }
4457
4466
  var aws_default = {
4458
4467
  name: "aws",
4459
4468
  description: "Protects AWS infrastructure from destructive AI operations",
@@ -7453,7 +7462,7 @@ function escapePango(text) {
7453
7462
  function buildPlainMessage(toolName, formattedArgs, agent, explainableLabel, locked, allowCount = 1, ruleDescription) {
7454
7463
  const lines = [];
7455
7464
  if (locked) lines.push("\u26A0\uFE0F LOCKED BY ADMIN POLICY\n");
7456
- const safeAgent = (agent ?? "AI Agent").replace(/\x1b(?:\[[0-9;?]*[a-zA-Z]|\][^\x07\x1b]*(?:\x07|\x1b\\)|[@-_])/g, "").slice(0, 80);
7465
+ const safeAgent = safeMessage(agent ?? "AI Agent", 80);
7457
7466
  lines.push(`\u{1F916} ${safeAgent} | \u{1F527} ${toolName}`);
7458
7467
  lines.push(`\u{1F6E1}\uFE0F ${explainableLabel || "Security Policy"}`);
7459
7468
  if (ruleDescription) lines.push(`\u2139 ${ruleDescription}`);
@@ -7781,14 +7790,14 @@ async function resolveNode9SaaS(requestId, creds, approved, decidedBy) {
7781
7790
  if (!res.ok) {
7782
7791
  fs11.appendFileSync(
7783
7792
  HOOK_DEBUG_LOG,
7784
- `[resolve-cloud] PATCH ${resolveUrl} \u2192 HTTP ${res.status}
7793
+ `[resolve-cloud] PATCH ${safeMessage(resolveUrl, 200)} \u2192 HTTP ${res.status}
7785
7794
  `
7786
7795
  );
7787
7796
  }
7788
7797
  } catch (err) {
7789
7798
  fs11.appendFileSync(
7790
7799
  HOOK_DEBUG_LOG,
7791
- `[resolve-cloud] PATCH failed for ${requestId}: ${err.message}
7800
+ `[resolve-cloud] PATCH failed for ${safeMessage(requestId, 64)}: ${safeMessage(err)}
7792
7801
  `
7793
7802
  );
7794
7803
  }
@@ -7889,9 +7898,8 @@ async function authorizeHeadless(toolName, args, meta, options) {
7889
7898
  if (!options?.calledFromDaemon) {
7890
7899
  const actId = randomUUID();
7891
7900
  const actTs = Date.now();
7892
- const stripAnsi = (s) => s.replace(/\x1b(?:\[[0-9;?]*[a-zA-Z]|\][^\x07\x1b]*(?:\x07|\x1b\\)|[@-_])/g, "");
7893
- const sanitizedAgent = meta?.agent ? stripAnsi(meta.agent).slice(0, 80) : void 0;
7894
- const sanitizedMcpServer = meta?.mcpServer ? stripAnsi(meta.mcpServer).slice(0, 40) : void 0;
7901
+ const sanitizedAgent = meta?.agent ? safeMessage(meta.agent, 80) : void 0;
7902
+ const sanitizedMcpServer = meta?.mcpServer ? safeMessage(meta.mcpServer, 40) : void 0;
7895
7903
  const socketOk = await notifyActivity({
7896
7904
  id: actId,
7897
7905
  ts: actTs,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@node9/proxy",
3
- "version": "2.14.1",
3
+ "version": "2.14.2",
4
4
  "description": "The Sudo Command for AI Agents. Execution Security for Claude Code, Codex, Gemini, Cursor, Opencode, Pi, and any MCP server.",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",