@node9/proxy 2.28.1 → 2.29.0

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
@@ -11103,6 +11103,14 @@ async function checkStatePredicates(predicates) {
11103
11103
  return null;
11104
11104
  }
11105
11105
  }
11106
+ function resolveDaemonPort(env = process.env) {
11107
+ if (env.NODE9_TESTING !== "1") return DEFAULT_DAEMON_PORT;
11108
+ const raw = env.NODE9_DAEMON_PORT;
11109
+ if (raw === void 0 || raw === "") return DEFAULT_DAEMON_PORT;
11110
+ const port = Number(raw);
11111
+ if (!Number.isInteger(port) || port < 1024 || port > 65535) return DEFAULT_DAEMON_PORT;
11112
+ return port;
11113
+ }
11106
11114
  function getInternalToken() {
11107
11115
  try {
11108
11116
  const pidFile = import_path10.default.join(import_os8.default.homedir(), ".node9", "daemon.pid");
@@ -11376,7 +11384,7 @@ async function resolveViaDaemon(id, decision, internalToken, source) {
11376
11384
  signal: AbortSignal.timeout(3e3)
11377
11385
  });
11378
11386
  }
11379
- var import_fs10, import_net, import_path10, import_os8, ACTIVITY_SOCKET_PATH, DAEMON_PORT, DAEMON_HOST;
11387
+ var import_fs10, import_net, import_path10, import_os8, ACTIVITY_SOCKET_PATH, DEFAULT_DAEMON_PORT, DAEMON_PORT, DAEMON_HOST;
11380
11388
  var init_daemon = __esm({
11381
11389
  "src/auth/daemon.ts"() {
11382
11390
  "use strict";
@@ -11385,7 +11393,8 @@ var init_daemon = __esm({
11385
11393
  import_path10 = __toESM(require("path"));
11386
11394
  import_os8 = __toESM(require("os"));
11387
11395
  ACTIVITY_SOCKET_PATH = process.platform === "win32" ? "\\\\.\\pipe\\node9-activity" : import_path10.default.join(import_os8.default.tmpdir(), "node9-activity.sock");
11388
- DAEMON_PORT = 7391;
11396
+ DEFAULT_DAEMON_PORT = 7391;
11397
+ DAEMON_PORT = resolveDaemonPort();
11389
11398
  DAEMON_HOST = "127.0.0.1";
11390
11399
  }
11391
11400
  });
@@ -58994,6 +59003,42 @@ function shellOutputText(toolResponse) {
58994
59003
  }
58995
59004
  return collectToolOutputText(toolResponse).text;
58996
59005
  }
59006
+ function mapToolOutputStrings(toolResponse, fn) {
59007
+ const walk = (v, depth) => {
59008
+ if (typeof v === "string") return fn(v);
59009
+ if (depth >= MAX_DEPTH3 || v === null || typeof v !== "object") return v;
59010
+ if (Array.isArray(v)) return v.map((x) => walk(x, depth + 1));
59011
+ const out = {};
59012
+ for (const [k, x] of Object.entries(v)) out[k] = walk(x, depth + 1);
59013
+ return out;
59014
+ };
59015
+ return walk(toolResponse, 0);
59016
+ }
59017
+ function frameToolOutputLeaves(toolResponse, isSuspect, frame, neutralize) {
59018
+ const suspect = [];
59019
+ let largestAt = -1;
59020
+ let largestLen = 0;
59021
+ mapToolOutputStrings(toolResponse, (s) => {
59022
+ const i2 = suspect.length;
59023
+ suspect.push(s.length > 0 && isSuspect(s));
59024
+ if (s.length > largestLen) {
59025
+ largestLen = s.length;
59026
+ largestAt = i2;
59027
+ }
59028
+ return s;
59029
+ });
59030
+ const anySuspect = suspect.some(Boolean);
59031
+ let i = 0;
59032
+ return mapToolOutputStrings(toolResponse, (s) => {
59033
+ const at = i++;
59034
+ const pick2 = anySuspect ? suspect[at] : at === largestAt;
59035
+ if (!pick2) return neutralize(s);
59036
+ const f = frame();
59037
+ return `${f.header}
59038
+ ${neutralize(s)}
59039
+ ${f.footer}`;
59040
+ });
59041
+ }
58997
59042
 
58998
59043
  // src/cli/commands/log.ts
58999
59044
  init_hasher();
@@ -59224,19 +59269,36 @@ function registerLogCommand(program2) {
59224
59269
  process.stdout.write(JSON.stringify({ redacted: out, found, injection }) + "\n");
59225
59270
  } else {
59226
59271
  const warnings = [];
59272
+ const inPlace = agent === "Claude Code" && config.policy.dlp.enabled;
59273
+ const redactedNames = /* @__PURE__ */ new Set();
59274
+ let updatedToolOutput = void 0;
59275
+ if (inPlace) {
59276
+ const mapped = mapToolOutputStrings(payload.tool_response, (leaf) => {
59277
+ const r = redactText(leaf);
59278
+ for (const name of r.found) redactedNames.add(name);
59279
+ return r.result;
59280
+ });
59281
+ if (redactedNames.size > 0) updatedToolOutput = mapped;
59282
+ }
59227
59283
  const hit = scanText(toolOutput);
59228
- if (hit) {
59229
- await notifySessionTaint(
59230
- payloadSessionId ?? "",
59231
- `output-secret:${hit.patternName}`
59232
- );
59284
+ const secretName = hit?.patternName ?? [...redactedNames][0];
59285
+ if (secretName) {
59286
+ await notifySessionTaint(payloadSessionId ?? "", `output-secret:${secretName}`);
59233
59287
  warnings.push(
59234
- `\u26A0\uFE0F node9: this tool output contained a credential (${hit.patternName}). Do not echo, store, or transmit it \u2014 treat it as compromised and rotate it. node9 has flagged this session: the next network or write action will require approval.`
59288
+ updatedToolOutput !== void 0 ? `\u26A0\uFE0F node9: this tool output contained a credential (${secretName}); node9 replaced it with [node9-redacted:\u2026] before you read it. Treat the credential as compromised. node9 has flagged this session: the next network or write action will require approval.` : `\u26A0\uFE0F node9: this tool output contained a credential (${secretName}). Do not echo, store, or transmit it \u2014 treat it as compromised and rotate it. node9 has flagged this session: the next network or write action will require approval.`
59235
59289
  );
59236
59290
  }
59237
59291
  if (injectionOn) {
59238
59292
  const m = scanInjection(toolOutput, { tool: rawToolName });
59239
59293
  if (m && atLeastConfidence(m.confidence, inj.minConfidence)) {
59294
+ if (agent === "Claude Code") {
59295
+ updatedToolOutput = frameToolOutputLeaves(
59296
+ updatedToolOutput ?? payload.tool_response,
59297
+ (leaf) => scanInjection(leaf, { tool: rawToolName }) !== null,
59298
+ newUntrustedFrame,
59299
+ neutralizeMarkers
59300
+ );
59301
+ }
59240
59302
  await notifySessionTaint(
59241
59303
  payloadSessionId ?? "",
59242
59304
  `output-injection:${m.signals.join("+")}`
@@ -59246,12 +59308,22 @@ function registerLogCommand(program2) {
59246
59308
  );
59247
59309
  }
59248
59310
  }
59249
- if (warnings.length > 0 && (agent === "Claude Code" || agent === "Codex")) {
59311
+ if (redactedNames.size > 0) {
59312
+ appendToLog(LOCAL_AUDIT_LOG, {
59313
+ ts: (/* @__PURE__ */ new Date()).toISOString(),
59314
+ tool,
59315
+ source: "output-redacted",
59316
+ outputRedacted: [...redactedNames],
59317
+ ...payloadSessionId ? { sessionId: payloadSessionId } : {}
59318
+ });
59319
+ }
59320
+ if ((warnings.length > 0 || updatedToolOutput !== void 0) && (agent === "Claude Code" || agent === "Codex")) {
59250
59321
  process.stdout.write(
59251
59322
  JSON.stringify({
59252
59323
  hookSpecificOutput: {
59253
59324
  hookEventName: "PostToolUse",
59254
- additionalContext: warnings.join("\n\n")
59325
+ ...warnings.length > 0 ? { additionalContext: warnings.join("\n\n") } : {},
59326
+ ...updatedToolOutput !== void 0 ? { updatedToolOutput } : {}
59255
59327
  }
59256
59328
  }) + "\n"
59257
59329
  );
package/dist/cli.mjs CHANGED
@@ -11109,6 +11109,14 @@ async function checkStatePredicates(predicates) {
11109
11109
  return null;
11110
11110
  }
11111
11111
  }
11112
+ function resolveDaemonPort(env = process.env) {
11113
+ if (env.NODE9_TESTING !== "1") return DEFAULT_DAEMON_PORT;
11114
+ const raw = env.NODE9_DAEMON_PORT;
11115
+ if (raw === void 0 || raw === "") return DEFAULT_DAEMON_PORT;
11116
+ const port = Number(raw);
11117
+ if (!Number.isInteger(port) || port < 1024 || port > 65535) return DEFAULT_DAEMON_PORT;
11118
+ return port;
11119
+ }
11112
11120
  function getInternalToken() {
11113
11121
  try {
11114
11122
  const pidFile = path10.join(os8.homedir(), ".node9", "daemon.pid");
@@ -11382,12 +11390,13 @@ async function resolveViaDaemon(id, decision, internalToken, source) {
11382
11390
  signal: AbortSignal.timeout(3e3)
11383
11391
  });
11384
11392
  }
11385
- var ACTIVITY_SOCKET_PATH, DAEMON_PORT, DAEMON_HOST;
11393
+ var ACTIVITY_SOCKET_PATH, DEFAULT_DAEMON_PORT, DAEMON_PORT, DAEMON_HOST;
11386
11394
  var init_daemon = __esm({
11387
11395
  "src/auth/daemon.ts"() {
11388
11396
  "use strict";
11389
11397
  ACTIVITY_SOCKET_PATH = process.platform === "win32" ? "\\\\.\\pipe\\node9-activity" : path10.join(os8.tmpdir(), "node9-activity.sock");
11390
- DAEMON_PORT = 7391;
11398
+ DEFAULT_DAEMON_PORT = 7391;
11399
+ DAEMON_PORT = resolveDaemonPort();
11391
11400
  DAEMON_HOST = "127.0.0.1";
11392
11401
  }
11393
11402
  });
@@ -58976,6 +58985,42 @@ function shellOutputText(toolResponse) {
58976
58985
  }
58977
58986
  return collectToolOutputText(toolResponse).text;
58978
58987
  }
58988
+ function mapToolOutputStrings(toolResponse, fn) {
58989
+ const walk = (v, depth) => {
58990
+ if (typeof v === "string") return fn(v);
58991
+ if (depth >= MAX_DEPTH3 || v === null || typeof v !== "object") return v;
58992
+ if (Array.isArray(v)) return v.map((x) => walk(x, depth + 1));
58993
+ const out = {};
58994
+ for (const [k, x] of Object.entries(v)) out[k] = walk(x, depth + 1);
58995
+ return out;
58996
+ };
58997
+ return walk(toolResponse, 0);
58998
+ }
58999
+ function frameToolOutputLeaves(toolResponse, isSuspect, frame, neutralize) {
59000
+ const suspect = [];
59001
+ let largestAt = -1;
59002
+ let largestLen = 0;
59003
+ mapToolOutputStrings(toolResponse, (s) => {
59004
+ const i2 = suspect.length;
59005
+ suspect.push(s.length > 0 && isSuspect(s));
59006
+ if (s.length > largestLen) {
59007
+ largestLen = s.length;
59008
+ largestAt = i2;
59009
+ }
59010
+ return s;
59011
+ });
59012
+ const anySuspect = suspect.some(Boolean);
59013
+ let i = 0;
59014
+ return mapToolOutputStrings(toolResponse, (s) => {
59015
+ const at = i++;
59016
+ const pick2 = anySuspect ? suspect[at] : at === largestAt;
59017
+ if (!pick2) return neutralize(s);
59018
+ const f = frame();
59019
+ return `${f.header}
59020
+ ${neutralize(s)}
59021
+ ${f.footer}`;
59022
+ });
59023
+ }
58979
59024
 
58980
59025
  // src/cli/commands/log.ts
58981
59026
  init_hasher();
@@ -59206,19 +59251,36 @@ function registerLogCommand(program2) {
59206
59251
  process.stdout.write(JSON.stringify({ redacted: out, found, injection }) + "\n");
59207
59252
  } else {
59208
59253
  const warnings = [];
59254
+ const inPlace = agent === "Claude Code" && config.policy.dlp.enabled;
59255
+ const redactedNames = /* @__PURE__ */ new Set();
59256
+ let updatedToolOutput = void 0;
59257
+ if (inPlace) {
59258
+ const mapped = mapToolOutputStrings(payload.tool_response, (leaf) => {
59259
+ const r = redactText(leaf);
59260
+ for (const name of r.found) redactedNames.add(name);
59261
+ return r.result;
59262
+ });
59263
+ if (redactedNames.size > 0) updatedToolOutput = mapped;
59264
+ }
59209
59265
  const hit = scanText(toolOutput);
59210
- if (hit) {
59211
- await notifySessionTaint(
59212
- payloadSessionId ?? "",
59213
- `output-secret:${hit.patternName}`
59214
- );
59266
+ const secretName = hit?.patternName ?? [...redactedNames][0];
59267
+ if (secretName) {
59268
+ await notifySessionTaint(payloadSessionId ?? "", `output-secret:${secretName}`);
59215
59269
  warnings.push(
59216
- `\u26A0\uFE0F node9: this tool output contained a credential (${hit.patternName}). Do not echo, store, or transmit it \u2014 treat it as compromised and rotate it. node9 has flagged this session: the next network or write action will require approval.`
59270
+ updatedToolOutput !== void 0 ? `\u26A0\uFE0F node9: this tool output contained a credential (${secretName}); node9 replaced it with [node9-redacted:\u2026] before you read it. Treat the credential as compromised. node9 has flagged this session: the next network or write action will require approval.` : `\u26A0\uFE0F node9: this tool output contained a credential (${secretName}). Do not echo, store, or transmit it \u2014 treat it as compromised and rotate it. node9 has flagged this session: the next network or write action will require approval.`
59217
59271
  );
59218
59272
  }
59219
59273
  if (injectionOn) {
59220
59274
  const m = scanInjection(toolOutput, { tool: rawToolName });
59221
59275
  if (m && atLeastConfidence(m.confidence, inj.minConfidence)) {
59276
+ if (agent === "Claude Code") {
59277
+ updatedToolOutput = frameToolOutputLeaves(
59278
+ updatedToolOutput ?? payload.tool_response,
59279
+ (leaf) => scanInjection(leaf, { tool: rawToolName }) !== null,
59280
+ newUntrustedFrame,
59281
+ neutralizeMarkers
59282
+ );
59283
+ }
59222
59284
  await notifySessionTaint(
59223
59285
  payloadSessionId ?? "",
59224
59286
  `output-injection:${m.signals.join("+")}`
@@ -59228,12 +59290,22 @@ function registerLogCommand(program2) {
59228
59290
  );
59229
59291
  }
59230
59292
  }
59231
- if (warnings.length > 0 && (agent === "Claude Code" || agent === "Codex")) {
59293
+ if (redactedNames.size > 0) {
59294
+ appendToLog(LOCAL_AUDIT_LOG, {
59295
+ ts: (/* @__PURE__ */ new Date()).toISOString(),
59296
+ tool,
59297
+ source: "output-redacted",
59298
+ outputRedacted: [...redactedNames],
59299
+ ...payloadSessionId ? { sessionId: payloadSessionId } : {}
59300
+ });
59301
+ }
59302
+ if ((warnings.length > 0 || updatedToolOutput !== void 0) && (agent === "Claude Code" || agent === "Codex")) {
59232
59303
  process.stdout.write(
59233
59304
  JSON.stringify({
59234
59305
  hookSpecificOutput: {
59235
59306
  hookEventName: "PostToolUse",
59236
- additionalContext: warnings.join("\n\n")
59307
+ ...warnings.length > 0 ? { additionalContext: warnings.join("\n\n") } : {},
59308
+ ...updatedToolOutput !== void 0 ? { updatedToolOutput } : {}
59237
59309
  }
59238
59310
  }) + "\n"
59239
59311
  );
@@ -5380,6 +5380,14 @@ var init_blast = __esm({
5380
5380
  import fs4 from "fs";
5381
5381
  import path5 from "path";
5382
5382
  import os5 from "os";
5383
+ function resolveDaemonPort(env = process.env) {
5384
+ if (env.NODE9_TESTING !== "1") return DEFAULT_DAEMON_PORT;
5385
+ const raw = env.NODE9_DAEMON_PORT;
5386
+ if (raw === void 0 || raw === "") return DEFAULT_DAEMON_PORT;
5387
+ const port = Number(raw);
5388
+ if (!Number.isInteger(port) || port < 1024 || port > 65535) return DEFAULT_DAEMON_PORT;
5389
+ return port;
5390
+ }
5383
5391
  function getInternalToken() {
5384
5392
  try {
5385
5393
  const pidFile = path5.join(os5.homedir(), ".node9", "daemon.pid");
@@ -5391,12 +5399,13 @@ function getInternalToken() {
5391
5399
  return null;
5392
5400
  }
5393
5401
  }
5394
- var ACTIVITY_SOCKET_PATH, DAEMON_PORT, DAEMON_HOST;
5402
+ var ACTIVITY_SOCKET_PATH, DEFAULT_DAEMON_PORT, DAEMON_PORT, DAEMON_HOST;
5395
5403
  var init_daemon = __esm({
5396
5404
  "src/auth/daemon.ts"() {
5397
5405
  "use strict";
5398
5406
  ACTIVITY_SOCKET_PATH = process.platform === "win32" ? "\\\\.\\pipe\\node9-activity" : path5.join(os5.tmpdir(), "node9-activity.sock");
5399
- DAEMON_PORT = 7391;
5407
+ DEFAULT_DAEMON_PORT = 7391;
5408
+ DAEMON_PORT = resolveDaemonPort();
5400
5409
  DAEMON_HOST = "127.0.0.1";
5401
5410
  }
5402
5411
  });
package/dist/index.js CHANGED
@@ -9625,7 +9625,16 @@ async function checkStatePredicates(predicates) {
9625
9625
  return null;
9626
9626
  }
9627
9627
  }
9628
- var DAEMON_PORT = 7391;
9628
+ var DEFAULT_DAEMON_PORT = 7391;
9629
+ function resolveDaemonPort(env = process.env) {
9630
+ if (env.NODE9_TESTING !== "1") return DEFAULT_DAEMON_PORT;
9631
+ const raw = env.NODE9_DAEMON_PORT;
9632
+ if (raw === void 0 || raw === "") return DEFAULT_DAEMON_PORT;
9633
+ const port = Number(raw);
9634
+ if (!Number.isInteger(port) || port < 1024 || port > 65535) return DEFAULT_DAEMON_PORT;
9635
+ return port;
9636
+ }
9637
+ var DAEMON_PORT = resolveDaemonPort();
9629
9638
  var DAEMON_HOST = "127.0.0.1";
9630
9639
  function getInternalToken() {
9631
9640
  try {
package/dist/index.mjs CHANGED
@@ -9595,7 +9595,16 @@ async function checkStatePredicates(predicates) {
9595
9595
  return null;
9596
9596
  }
9597
9597
  }
9598
- var DAEMON_PORT = 7391;
9598
+ var DEFAULT_DAEMON_PORT = 7391;
9599
+ function resolveDaemonPort(env = process.env) {
9600
+ if (env.NODE9_TESTING !== "1") return DEFAULT_DAEMON_PORT;
9601
+ const raw = env.NODE9_DAEMON_PORT;
9602
+ if (raw === void 0 || raw === "") return DEFAULT_DAEMON_PORT;
9603
+ const port = Number(raw);
9604
+ if (!Number.isInteger(port) || port < 1024 || port > 65535) return DEFAULT_DAEMON_PORT;
9605
+ return port;
9606
+ }
9607
+ var DAEMON_PORT = resolveDaemonPort();
9599
9608
  var DAEMON_HOST = "127.0.0.1";
9600
9609
  function getInternalToken() {
9601
9610
  try {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@node9/proxy",
3
- "version": "2.28.1",
3
+ "version": "2.29.0",
4
4
  "description": "Access control for AI agents. Set what Claude Code, Codex, Gemini, Cursor and any MCP server are allowed to do, review risky actions before they run, and keep every action on the record.",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",