@node9/proxy 1.45.0 → 1.47.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.
@@ -2280,6 +2280,11 @@ var init_config_schema = __esm({
2280
2280
  // must run them from the CLI. node9's threat model is the agent itself.
2281
2281
  mcpAllowWeakening: z.boolean().optional(),
2282
2282
  cloudSyncIntervalHours: z.number().positive().optional(),
2283
+ // Seconds-granular override for the cloud policy sync cadence. Wins over
2284
+ // cloudSyncIntervalHours when set. Lets you opt into fast apply (e.g. 20)
2285
+ // for an incident; clamped to a 15s floor so it can't hammer the API.
2286
+ // Unset → falls back to hours, then the 5h default.
2287
+ cloudSyncIntervalSeconds: z.number().positive().optional(),
2283
2288
  // Outbox shipper (audit.log → SaaS batch ingest). enabled defaults
2284
2289
  // to true; set false to fall back to local-only auditing.
2285
2290
  shipper: z.object({
package/dist/index.js CHANGED
@@ -322,6 +322,11 @@ var ConfigFileSchema = import_zod.z.object({
322
322
  // must run them from the CLI. node9's threat model is the agent itself.
323
323
  mcpAllowWeakening: import_zod.z.boolean().optional(),
324
324
  cloudSyncIntervalHours: import_zod.z.number().positive().optional(),
325
+ // Seconds-granular override for the cloud policy sync cadence. Wins over
326
+ // cloudSyncIntervalHours when set. Lets you opt into fast apply (e.g. 20)
327
+ // for an incident; clamped to a 15s floor so it can't hammer the API.
328
+ // Unset → falls back to hours, then the 5h default.
329
+ cloudSyncIntervalSeconds: import_zod.z.number().positive().optional(),
325
330
  // Outbox shipper (audit.log → SaaS batch ingest). enabled defaults
326
331
  // to true; set false to fall back to local-only auditing.
327
332
  shipper: import_zod.z.object({
@@ -2264,6 +2269,19 @@ function evaluateSmartConditions(args, rule) {
2264
2269
  });
2265
2270
  return mode === "any" ? results.some((r) => r) : results.every((r) => r);
2266
2271
  }
2272
+ var VERDICT_RANK = {
2273
+ allow: 0,
2274
+ review: 1,
2275
+ block: 2
2276
+ };
2277
+ function resolvePinned(matches) {
2278
+ if (matches.length === 0) return void 0;
2279
+ const pinned = matches.filter((r) => r.pinned);
2280
+ if (pinned.length === 0) return matches[0];
2281
+ return pinned.reduce(
2282
+ (best, r) => VERDICT_RANK[r.verdict] > VERDICT_RANK[best.verdict] ? r : best
2283
+ );
2284
+ }
2267
2285
  function tokenize2(toolName) {
2268
2286
  return toolName.toLowerCase().split(/[_.\-\s]+/).filter(Boolean);
2269
2287
  }
@@ -2376,9 +2394,10 @@ async function evaluatePolicy(config, toolName, args, context = {}, hooks = {})
2376
2394
  }
2377
2395
  }
2378
2396
  if (config.policy.smartRules.length > 0) {
2379
- const matchedRule = config.policy.smartRules.find(
2397
+ const matches = config.policy.smartRules.filter(
2380
2398
  (rule) => matchesPattern(toolName, rule.tool) && !(bashCommand !== null && rule.name && AST_FS_REGEX_RULES.has(rule.name)) && evaluateSmartConditions(args, rule)
2381
2399
  );
2400
+ const matchedRule = resolvePinned(matches);
2382
2401
  if (matchedRule) {
2383
2402
  if (matchedRule.verdict === "allow")
2384
2403
  return { decision: "allow", ruleName: matchedRule.name ?? matchedRule.tool };
package/dist/index.mjs CHANGED
@@ -292,6 +292,11 @@ var ConfigFileSchema = z.object({
292
292
  // must run them from the CLI. node9's threat model is the agent itself.
293
293
  mcpAllowWeakening: z.boolean().optional(),
294
294
  cloudSyncIntervalHours: z.number().positive().optional(),
295
+ // Seconds-granular override for the cloud policy sync cadence. Wins over
296
+ // cloudSyncIntervalHours when set. Lets you opt into fast apply (e.g. 20)
297
+ // for an incident; clamped to a 15s floor so it can't hammer the API.
298
+ // Unset → falls back to hours, then the 5h default.
299
+ cloudSyncIntervalSeconds: z.number().positive().optional(),
295
300
  // Outbox shipper (audit.log → SaaS batch ingest). enabled defaults
296
301
  // to true; set false to fall back to local-only auditing.
297
302
  shipper: z.object({
@@ -2234,6 +2239,19 @@ function evaluateSmartConditions(args, rule) {
2234
2239
  });
2235
2240
  return mode === "any" ? results.some((r) => r) : results.every((r) => r);
2236
2241
  }
2242
+ var VERDICT_RANK = {
2243
+ allow: 0,
2244
+ review: 1,
2245
+ block: 2
2246
+ };
2247
+ function resolvePinned(matches) {
2248
+ if (matches.length === 0) return void 0;
2249
+ const pinned = matches.filter((r) => r.pinned);
2250
+ if (pinned.length === 0) return matches[0];
2251
+ return pinned.reduce(
2252
+ (best, r) => VERDICT_RANK[r.verdict] > VERDICT_RANK[best.verdict] ? r : best
2253
+ );
2254
+ }
2237
2255
  function tokenize2(toolName) {
2238
2256
  return toolName.toLowerCase().split(/[_.\-\s]+/).filter(Boolean);
2239
2257
  }
@@ -2346,9 +2364,10 @@ async function evaluatePolicy(config, toolName, args, context = {}, hooks = {})
2346
2364
  }
2347
2365
  }
2348
2366
  if (config.policy.smartRules.length > 0) {
2349
- const matchedRule = config.policy.smartRules.find(
2367
+ const matches = config.policy.smartRules.filter(
2350
2368
  (rule) => matchesPattern(toolName, rule.tool) && !(bashCommand !== null && rule.name && AST_FS_REGEX_RULES.has(rule.name)) && evaluateSmartConditions(args, rule)
2351
2369
  );
2370
+ const matchedRule = resolvePinned(matches);
2352
2371
  if (matchedRule) {
2353
2372
  if (matchedRule.verdict === "allow")
2354
2373
  return { decision: "allow", ruleName: matchedRule.name ?? matchedRule.tool };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@node9/proxy",
3
- "version": "1.45.0",
3
+ "version": "1.47.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",