@node9/proxy 1.37.0 → 1.39.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.
@@ -2301,6 +2301,11 @@ var init_config_schema = __esm({
2301
2301
  threshold: z.number().min(2).optional(),
2302
2302
  windowSeconds: z.number().min(10).optional()
2303
2303
  }).optional(),
2304
+ injectionScan: z.object({
2305
+ enabled: z.boolean().optional(),
2306
+ minConfidence: z.enum(["medium", "high"]).optional(),
2307
+ allow: z.array(z.string()).optional()
2308
+ }).optional(),
2304
2309
  skillPinning: z.object({
2305
2310
  enabled: z.boolean().optional(),
2306
2311
  mode: z.enum(["warn", "block"]).optional(),
@@ -2602,6 +2607,7 @@ var init_config = __esm({
2602
2607
  dlp: { enabled: true, scanIgnoredTools: true, pii: "off" },
2603
2608
  egress: { enabled: false, mode: "review", allow: [], deny: [], allowPrivate: true },
2604
2609
  loopDetection: { enabled: true, threshold: 5, windowSeconds: 120 },
2610
+ injectionScan: { enabled: false, minConfidence: "medium", allow: [] },
2605
2611
  skillPinning: { enabled: false, mode: "warn", roots: [] }
2606
2612
  },
2607
2613
  environments: {}
package/dist/index.js CHANGED
@@ -314,6 +314,11 @@ var ConfigFileSchema = import_zod.z.object({
314
314
  threshold: import_zod.z.number().min(2).optional(),
315
315
  windowSeconds: import_zod.z.number().min(10).optional()
316
316
  }).optional(),
317
+ injectionScan: import_zod.z.object({
318
+ enabled: import_zod.z.boolean().optional(),
319
+ minConfidence: import_zod.z.enum(["medium", "high"]).optional(),
320
+ allow: import_zod.z.array(import_zod.z.string()).optional()
321
+ }).optional(),
317
322
  skillPinning: import_zod.z.object({
318
323
  enabled: import_zod.z.boolean().optional(),
319
324
  mode: import_zod.z.enum(["warn", "block"]).optional(),
@@ -3570,6 +3575,7 @@ var DEFAULT_CONFIG = {
3570
3575
  dlp: { enabled: true, scanIgnoredTools: true, pii: "off" },
3571
3576
  egress: { enabled: false, mode: "review", allow: [], deny: [], allowPrivate: true },
3572
3577
  loopDetection: { enabled: true, threshold: 5, windowSeconds: 120 },
3578
+ injectionScan: { enabled: false, minConfidence: "medium", allow: [] },
3573
3579
  skillPinning: { enabled: false, mode: "warn", roots: [] }
3574
3580
  },
3575
3581
  environments: {}
@@ -3700,6 +3706,10 @@ function getConfig(cwd) {
3700
3706
  deny: [...DEFAULT_CONFIG.policy.egress.deny]
3701
3707
  },
3702
3708
  loopDetection: { ...DEFAULT_CONFIG.policy.loopDetection },
3709
+ injectionScan: {
3710
+ ...DEFAULT_CONFIG.policy.injectionScan,
3711
+ allow: [...DEFAULT_CONFIG.policy.injectionScan.allow]
3712
+ },
3703
3713
  skillPinning: {
3704
3714
  ...DEFAULT_CONFIG.policy.skillPinning,
3705
3715
  roots: [...DEFAULT_CONFIG.policy.skillPinning.roots]
@@ -3766,6 +3776,17 @@ function getConfig(cwd) {
3766
3776
  if (ld.windowSeconds !== void 0)
3767
3777
  mergedPolicy.loopDetection.windowSeconds = ld.windowSeconds;
3768
3778
  }
3779
+ if (p.injectionScan && typeof p.injectionScan === "object") {
3780
+ const is = p.injectionScan;
3781
+ if (is.enabled !== void 0) mergedPolicy.injectionScan.enabled = is.enabled;
3782
+ if (is.minConfidence !== void 0)
3783
+ mergedPolicy.injectionScan.minConfidence = is.minConfidence;
3784
+ if (Array.isArray(is.allow)) {
3785
+ for (const t of is.allow) {
3786
+ if (typeof t === "string" && t.length > 0) mergedPolicy.injectionScan.allow.push(t);
3787
+ }
3788
+ }
3789
+ }
3769
3790
  if (p.skillPinning && typeof p.skillPinning === "object") {
3770
3791
  const sp = p.skillPinning;
3771
3792
  if (sp.enabled !== void 0) mergedPolicy.skillPinning.enabled = sp.enabled;
@@ -4377,6 +4398,21 @@ async function checkTaint(paths) {
4377
4398
  return { tainted: false, daemonUnavailable: true };
4378
4399
  }
4379
4400
  }
4401
+ async function checkSessionTaint(sessionId) {
4402
+ if (!sessionId || !isDaemonRunning()) return { tainted: false };
4403
+ const base = `http://${DAEMON_HOST}:${DAEMON_PORT}`;
4404
+ try {
4405
+ const res = await fetch(`${base}/session-taint/check`, {
4406
+ method: "POST",
4407
+ headers: { "Content-Type": "application/json" },
4408
+ body: JSON.stringify({ sessionId }),
4409
+ signal: AbortSignal.timeout(2e3)
4410
+ });
4411
+ return await res.json();
4412
+ } catch {
4413
+ return { tainted: false, daemonUnavailable: true };
4414
+ }
4415
+ }
4380
4416
  async function resolveViaDaemon(id, decision, internalToken, source) {
4381
4417
  const base = `http://${DAEMON_HOST}:${DAEMON_PORT}`;
4382
4418
  await fetch(`${base}/resolve/${id}`, {
@@ -5056,6 +5092,12 @@ async function _authorizeHeadlessCore(toolName, args, metaArg, options) {
5056
5092
  }
5057
5093
  }
5058
5094
  }
5095
+ if (!taintWarning && meta?.sessionId && (isNetworkTool(toolName, args) || isWriteTool(toolName))) {
5096
+ const sessionTaint = await checkSessionTaint(meta.sessionId);
5097
+ if (sessionTaint.tainted && sessionTaint.record) {
5098
+ taintWarning = `\u26A0\uFE0F node9 flagged this session \u2014 earlier tool output contained ${sessionTaint.record.source}. Approve this ${isWriteTool(toolName) ? "write" : "network"} action before it proceeds.`;
5099
+ }
5100
+ }
5059
5101
  if (config.policy.dlp.enabled && (!isIgnoredTool2(toolName) || config.policy.dlp.scanIgnoredTools)) {
5060
5102
  const argsObj = args && typeof args === "object" && !Array.isArray(args) ? args : {};
5061
5103
  const filePath = String(argsObj.file_path ?? argsObj.path ?? argsObj.filename ?? "");
package/dist/index.mjs CHANGED
@@ -284,6 +284,11 @@ var ConfigFileSchema = z.object({
284
284
  threshold: z.number().min(2).optional(),
285
285
  windowSeconds: z.number().min(10).optional()
286
286
  }).optional(),
287
+ injectionScan: z.object({
288
+ enabled: z.boolean().optional(),
289
+ minConfidence: z.enum(["medium", "high"]).optional(),
290
+ allow: z.array(z.string()).optional()
291
+ }).optional(),
287
292
  skillPinning: z.object({
288
293
  enabled: z.boolean().optional(),
289
294
  mode: z.enum(["warn", "block"]).optional(),
@@ -3540,6 +3545,7 @@ var DEFAULT_CONFIG = {
3540
3545
  dlp: { enabled: true, scanIgnoredTools: true, pii: "off" },
3541
3546
  egress: { enabled: false, mode: "review", allow: [], deny: [], allowPrivate: true },
3542
3547
  loopDetection: { enabled: true, threshold: 5, windowSeconds: 120 },
3548
+ injectionScan: { enabled: false, minConfidence: "medium", allow: [] },
3543
3549
  skillPinning: { enabled: false, mode: "warn", roots: [] }
3544
3550
  },
3545
3551
  environments: {}
@@ -3670,6 +3676,10 @@ function getConfig(cwd) {
3670
3676
  deny: [...DEFAULT_CONFIG.policy.egress.deny]
3671
3677
  },
3672
3678
  loopDetection: { ...DEFAULT_CONFIG.policy.loopDetection },
3679
+ injectionScan: {
3680
+ ...DEFAULT_CONFIG.policy.injectionScan,
3681
+ allow: [...DEFAULT_CONFIG.policy.injectionScan.allow]
3682
+ },
3673
3683
  skillPinning: {
3674
3684
  ...DEFAULT_CONFIG.policy.skillPinning,
3675
3685
  roots: [...DEFAULT_CONFIG.policy.skillPinning.roots]
@@ -3736,6 +3746,17 @@ function getConfig(cwd) {
3736
3746
  if (ld.windowSeconds !== void 0)
3737
3747
  mergedPolicy.loopDetection.windowSeconds = ld.windowSeconds;
3738
3748
  }
3749
+ if (p.injectionScan && typeof p.injectionScan === "object") {
3750
+ const is = p.injectionScan;
3751
+ if (is.enabled !== void 0) mergedPolicy.injectionScan.enabled = is.enabled;
3752
+ if (is.minConfidence !== void 0)
3753
+ mergedPolicy.injectionScan.minConfidence = is.minConfidence;
3754
+ if (Array.isArray(is.allow)) {
3755
+ for (const t of is.allow) {
3756
+ if (typeof t === "string" && t.length > 0) mergedPolicy.injectionScan.allow.push(t);
3757
+ }
3758
+ }
3759
+ }
3739
3760
  if (p.skillPinning && typeof p.skillPinning === "object") {
3740
3761
  const sp = p.skillPinning;
3741
3762
  if (sp.enabled !== void 0) mergedPolicy.skillPinning.enabled = sp.enabled;
@@ -4347,6 +4368,21 @@ async function checkTaint(paths) {
4347
4368
  return { tainted: false, daemonUnavailable: true };
4348
4369
  }
4349
4370
  }
4371
+ async function checkSessionTaint(sessionId) {
4372
+ if (!sessionId || !isDaemonRunning()) return { tainted: false };
4373
+ const base = `http://${DAEMON_HOST}:${DAEMON_PORT}`;
4374
+ try {
4375
+ const res = await fetch(`${base}/session-taint/check`, {
4376
+ method: "POST",
4377
+ headers: { "Content-Type": "application/json" },
4378
+ body: JSON.stringify({ sessionId }),
4379
+ signal: AbortSignal.timeout(2e3)
4380
+ });
4381
+ return await res.json();
4382
+ } catch {
4383
+ return { tainted: false, daemonUnavailable: true };
4384
+ }
4385
+ }
4350
4386
  async function resolveViaDaemon(id, decision, internalToken, source) {
4351
4387
  const base = `http://${DAEMON_HOST}:${DAEMON_PORT}`;
4352
4388
  await fetch(`${base}/resolve/${id}`, {
@@ -5026,6 +5062,12 @@ async function _authorizeHeadlessCore(toolName, args, metaArg, options) {
5026
5062
  }
5027
5063
  }
5028
5064
  }
5065
+ if (!taintWarning && meta?.sessionId && (isNetworkTool(toolName, args) || isWriteTool(toolName))) {
5066
+ const sessionTaint = await checkSessionTaint(meta.sessionId);
5067
+ if (sessionTaint.tainted && sessionTaint.record) {
5068
+ taintWarning = `\u26A0\uFE0F node9 flagged this session \u2014 earlier tool output contained ${sessionTaint.record.source}. Approve this ${isWriteTool(toolName) ? "write" : "network"} action before it proceeds.`;
5069
+ }
5070
+ }
5029
5071
  if (config.policy.dlp.enabled && (!isIgnoredTool2(toolName) || config.policy.dlp.scanIgnoredTools)) {
5030
5072
  const argsObj = args && typeof args === "object" && !Array.isArray(args) ? args : {};
5031
5073
  const filePath = String(argsObj.file_path ?? argsObj.path ?? argsObj.filename ?? "");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@node9/proxy",
3
- "version": "1.37.0",
3
+ "version": "1.39.0",
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",