@node9/proxy 1.0.1 → 1.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +28 -4
- package/dist/cli.js +211 -148
- package/dist/cli.mjs +211 -148
- package/dist/index.js +194 -76
- package/dist/index.mjs +194 -76
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -79,21 +79,47 @@ function sendDesktopNotification(title, body) {
|
|
|
79
79
|
} catch {
|
|
80
80
|
}
|
|
81
81
|
}
|
|
82
|
+
function escapePango(text) {
|
|
83
|
+
return text.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
84
|
+
}
|
|
85
|
+
function buildPlainMessage(toolName, formattedArgs, agent, explainableLabel, locked) {
|
|
86
|
+
const lines = [];
|
|
87
|
+
if (locked) lines.push("\u26A0\uFE0F LOCKED BY ADMIN POLICY\n");
|
|
88
|
+
lines.push(`\u{1F916} ${agent || "AI Agent"} | \u{1F527} ${toolName}`);
|
|
89
|
+
lines.push(`\u{1F6E1}\uFE0F ${explainableLabel || "Security Policy"}`);
|
|
90
|
+
lines.push("");
|
|
91
|
+
lines.push(formattedArgs);
|
|
92
|
+
if (!locked) {
|
|
93
|
+
lines.push("");
|
|
94
|
+
lines.push('\u21B5 Enter = Allow \u21B5 | \u238B Esc = Block \u238B | "Always Allow" = never ask again');
|
|
95
|
+
}
|
|
96
|
+
return lines.join("\n");
|
|
97
|
+
}
|
|
98
|
+
function buildPangoMessage(toolName, formattedArgs, agent, explainableLabel, locked) {
|
|
99
|
+
const lines = [];
|
|
100
|
+
if (locked) {
|
|
101
|
+
lines.push('<span foreground="red" weight="bold">\u26A0\uFE0F LOCKED BY ADMIN POLICY</span>');
|
|
102
|
+
lines.push("");
|
|
103
|
+
}
|
|
104
|
+
lines.push(
|
|
105
|
+
`<b>\u{1F916} ${escapePango(agent || "AI Agent")}</b> | <b>\u{1F527} <tt>${escapePango(toolName)}</tt></b>`
|
|
106
|
+
);
|
|
107
|
+
lines.push(`<i>\u{1F6E1}\uFE0F ${escapePango(explainableLabel || "Security Policy")}</i>`);
|
|
108
|
+
lines.push("");
|
|
109
|
+
lines.push(`<tt>${escapePango(formattedArgs)}</tt>`);
|
|
110
|
+
if (!locked) {
|
|
111
|
+
lines.push("");
|
|
112
|
+
lines.push(
|
|
113
|
+
'<small>\u21B5 Enter = <b>Allow \u21B5</b> | \u238B Esc = <b>Block \u238B</b> | "Always Allow" = never ask again</small>'
|
|
114
|
+
);
|
|
115
|
+
}
|
|
116
|
+
return lines.join("\n");
|
|
117
|
+
}
|
|
82
118
|
async function askNativePopup(toolName, args, agent, explainableLabel, locked = false, signal) {
|
|
83
119
|
if (isTestEnv()) return "deny";
|
|
84
120
|
const formattedArgs = formatArgs(args);
|
|
85
121
|
const title = locked ? `\u26A1 Node9 \u2014 Locked` : `\u{1F6E1}\uFE0F Node9 \u2014 Action Approval`;
|
|
86
|
-
|
|
87
|
-
if (locked) message += `\u26A0\uFE0F LOCKED BY ADMIN POLICY
|
|
88
|
-
`;
|
|
89
|
-
message += `Tool: ${toolName}
|
|
90
|
-
`;
|
|
91
|
-
message += `Agent: ${agent || "AI Agent"}
|
|
92
|
-
`;
|
|
93
|
-
message += `Rule: ${explainableLabel || "Security Policy"}
|
|
94
|
-
|
|
95
|
-
`;
|
|
96
|
-
message += `${formattedArgs}`;
|
|
122
|
+
const message = buildPlainMessage(toolName, formattedArgs, agent, explainableLabel, locked);
|
|
97
123
|
process.stderr.write(chalk.yellow(`
|
|
98
124
|
\u{1F6E1}\uFE0F Node9: Intercepted "${toolName}" \u2014 awaiting user...
|
|
99
125
|
`));
|
|
@@ -114,7 +140,7 @@ async function askNativePopup(toolName, args, agent, explainableLabel, locked =
|
|
|
114
140
|
}
|
|
115
141
|
try {
|
|
116
142
|
if (process.platform === "darwin") {
|
|
117
|
-
const buttons = locked ? `buttons {"Waiting\u2026"} default button "Waiting\u2026"` : `buttons {"Block", "Always Allow", "Allow"} default button "Allow" cancel button "Block"`;
|
|
143
|
+
const buttons = locked ? `buttons {"Waiting\u2026"} default button "Waiting\u2026"` : `buttons {"Block \u238B", "Always Allow", "Allow \u21B5"} default button "Allow \u21B5" cancel button "Block \u238B"`;
|
|
118
144
|
const script = `on run argv
|
|
119
145
|
tell application "System Events"
|
|
120
146
|
activate
|
|
@@ -123,21 +149,28 @@ end tell
|
|
|
123
149
|
end run`;
|
|
124
150
|
childProcess = spawn("osascript", ["-e", script, "--", message, title]);
|
|
125
151
|
} else if (process.platform === "linux") {
|
|
152
|
+
const pangoMessage = buildPangoMessage(
|
|
153
|
+
toolName,
|
|
154
|
+
formattedArgs,
|
|
155
|
+
agent,
|
|
156
|
+
explainableLabel,
|
|
157
|
+
locked
|
|
158
|
+
);
|
|
126
159
|
const argsList = [
|
|
127
160
|
locked ? "--info" : "--question",
|
|
128
161
|
"--modal",
|
|
129
|
-
"--width=
|
|
162
|
+
"--width=480",
|
|
130
163
|
"--title",
|
|
131
164
|
title,
|
|
132
165
|
"--text",
|
|
133
|
-
|
|
166
|
+
pangoMessage,
|
|
134
167
|
"--ok-label",
|
|
135
|
-
locked ? "Waiting..." : "Allow",
|
|
168
|
+
locked ? "Waiting..." : "Allow \u21B5",
|
|
136
169
|
"--timeout",
|
|
137
170
|
"300"
|
|
138
171
|
];
|
|
139
172
|
if (!locked) {
|
|
140
|
-
argsList.push("--cancel-label", "Block");
|
|
173
|
+
argsList.push("--cancel-label", "Block \u238B");
|
|
141
174
|
argsList.push("--extra-button", "Always Allow");
|
|
142
175
|
}
|
|
143
176
|
childProcess = spawn("zenity", argsList);
|
|
@@ -165,6 +198,8 @@ end run`;
|
|
|
165
198
|
// src/core.ts
|
|
166
199
|
var PAUSED_FILE = path.join(os.homedir(), ".node9", "PAUSED");
|
|
167
200
|
var TRUST_FILE = path.join(os.homedir(), ".node9", "trust.json");
|
|
201
|
+
var LOCAL_AUDIT_LOG = path.join(os.homedir(), ".node9", "audit.log");
|
|
202
|
+
var HOOK_DEBUG_LOG = path.join(os.homedir(), ".node9", "hook-debug.log");
|
|
168
203
|
function checkPause() {
|
|
169
204
|
try {
|
|
170
205
|
if (!fs.existsSync(PAUSED_FILE)) return { paused: false };
|
|
@@ -221,36 +256,39 @@ function writeTrustSession(toolName, durationMs) {
|
|
|
221
256
|
}
|
|
222
257
|
}
|
|
223
258
|
}
|
|
224
|
-
function
|
|
259
|
+
function appendToLog(logPath, entry) {
|
|
225
260
|
try {
|
|
226
|
-
const entry = JSON.stringify({
|
|
227
|
-
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
228
|
-
tool: toolName,
|
|
229
|
-
args,
|
|
230
|
-
decision: "would-have-blocked",
|
|
231
|
-
source: "audit-mode"
|
|
232
|
-
});
|
|
233
|
-
const logPath = path.join(os.homedir(), ".node9", "audit.log");
|
|
234
261
|
const dir = path.dirname(logPath);
|
|
235
262
|
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
|
|
236
|
-
fs.appendFileSync(logPath, entry + "\n");
|
|
263
|
+
fs.appendFileSync(logPath, JSON.stringify(entry) + "\n");
|
|
237
264
|
} catch {
|
|
238
265
|
}
|
|
239
266
|
}
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
267
|
+
function appendHookDebug(toolName, args, meta) {
|
|
268
|
+
const safeArgs = args ? JSON.parse(redactSecrets(JSON.stringify(args))) : {};
|
|
269
|
+
appendToLog(HOOK_DEBUG_LOG, {
|
|
270
|
+
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
271
|
+
tool: toolName,
|
|
272
|
+
args: safeArgs,
|
|
273
|
+
agent: meta?.agent,
|
|
274
|
+
mcpServer: meta?.mcpServer,
|
|
275
|
+
hostname: os.hostname(),
|
|
276
|
+
cwd: process.cwd()
|
|
277
|
+
});
|
|
278
|
+
}
|
|
279
|
+
function appendLocalAudit(toolName, args, decision, checkedBy, meta) {
|
|
280
|
+
const safeArgs = args ? JSON.parse(redactSecrets(JSON.stringify(args))) : {};
|
|
281
|
+
appendToLog(LOCAL_AUDIT_LOG, {
|
|
282
|
+
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
283
|
+
tool: toolName,
|
|
284
|
+
args: safeArgs,
|
|
285
|
+
decision,
|
|
286
|
+
checkedBy,
|
|
287
|
+
agent: meta?.agent,
|
|
288
|
+
mcpServer: meta?.mcpServer,
|
|
289
|
+
hostname: os.hostname()
|
|
290
|
+
});
|
|
291
|
+
}
|
|
254
292
|
function tokenize(toolName) {
|
|
255
293
|
return toolName.toLowerCase().split(/[_.\-\s]+/).filter(Boolean);
|
|
256
294
|
}
|
|
@@ -344,16 +382,41 @@ async function analyzeShellCommand(command) {
|
|
|
344
382
|
}
|
|
345
383
|
return { actions, paths, allTokens };
|
|
346
384
|
}
|
|
385
|
+
function redactSecrets(text) {
|
|
386
|
+
if (!text) return text;
|
|
387
|
+
let redacted = text;
|
|
388
|
+
redacted = redacted.replace(
|
|
389
|
+
/(authorization:\s*(?:bearer|basic)\s+)[a-zA-Z0-9._\-\/\\=]+/gi,
|
|
390
|
+
"$1********"
|
|
391
|
+
);
|
|
392
|
+
redacted = redacted.replace(
|
|
393
|
+
/(api[_-]?key|secret|password|token)([:=]\s*['"]?)[a-zA-Z0-9._\-]{8,}/gi,
|
|
394
|
+
"$1$2********"
|
|
395
|
+
);
|
|
396
|
+
return redacted;
|
|
397
|
+
}
|
|
398
|
+
var DANGEROUS_WORDS = [
|
|
399
|
+
"drop",
|
|
400
|
+
"truncate",
|
|
401
|
+
"purge",
|
|
402
|
+
"format",
|
|
403
|
+
"destroy",
|
|
404
|
+
"terminate",
|
|
405
|
+
"revoke",
|
|
406
|
+
"docker",
|
|
407
|
+
"psql"
|
|
408
|
+
];
|
|
347
409
|
var DEFAULT_CONFIG = {
|
|
348
410
|
settings: {
|
|
349
411
|
mode: "standard",
|
|
350
412
|
autoStartDaemon: true,
|
|
351
|
-
enableUndo:
|
|
413
|
+
enableUndo: true,
|
|
414
|
+
// 🔥 ALWAYS TRUE BY DEFAULT for the safety net
|
|
352
415
|
enableHookLogDebug: false,
|
|
353
416
|
approvers: { native: true, browser: true, cloud: true, terminal: true }
|
|
354
417
|
},
|
|
355
418
|
policy: {
|
|
356
|
-
sandboxPaths: [],
|
|
419
|
+
sandboxPaths: ["/tmp/**", "**/sandbox/**", "**/test-results/**"],
|
|
357
420
|
dangerousWords: DANGEROUS_WORDS,
|
|
358
421
|
ignoredTools: [
|
|
359
422
|
"list_*",
|
|
@@ -361,12 +424,44 @@ var DEFAULT_CONFIG = {
|
|
|
361
424
|
"read_*",
|
|
362
425
|
"describe_*",
|
|
363
426
|
"read",
|
|
427
|
+
"glob",
|
|
364
428
|
"grep",
|
|
365
429
|
"ls",
|
|
366
|
-
"
|
|
430
|
+
"notebookread",
|
|
431
|
+
"notebookedit",
|
|
432
|
+
"webfetch",
|
|
433
|
+
"websearch",
|
|
434
|
+
"exitplanmode",
|
|
435
|
+
"askuserquestion",
|
|
436
|
+
"agent",
|
|
437
|
+
"task*",
|
|
438
|
+
"toolsearch",
|
|
439
|
+
"mcp__ide__*",
|
|
440
|
+
"getDiagnostics"
|
|
367
441
|
],
|
|
368
|
-
toolInspection: {
|
|
369
|
-
|
|
442
|
+
toolInspection: {
|
|
443
|
+
bash: "command",
|
|
444
|
+
shell: "command",
|
|
445
|
+
run_shell_command: "command",
|
|
446
|
+
"terminal.execute": "command",
|
|
447
|
+
"postgres:query": "sql"
|
|
448
|
+
},
|
|
449
|
+
rules: [
|
|
450
|
+
{
|
|
451
|
+
action: "rm",
|
|
452
|
+
allowPaths: [
|
|
453
|
+
"**/node_modules/**",
|
|
454
|
+
"dist/**",
|
|
455
|
+
"build/**",
|
|
456
|
+
".next/**",
|
|
457
|
+
"coverage/**",
|
|
458
|
+
".cache/**",
|
|
459
|
+
"tmp/**",
|
|
460
|
+
"temp/**",
|
|
461
|
+
".DS_Store"
|
|
462
|
+
]
|
|
463
|
+
}
|
|
464
|
+
]
|
|
370
465
|
},
|
|
371
466
|
environments: {}
|
|
372
467
|
};
|
|
@@ -404,20 +499,15 @@ async function evaluatePolicy(toolName, args, agent) {
|
|
|
404
499
|
}
|
|
405
500
|
const isManual = agent === "Terminal";
|
|
406
501
|
if (isManual) {
|
|
407
|
-
const
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
"
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
"revoke",
|
|
417
|
-
"docker"
|
|
418
|
-
];
|
|
419
|
-
const hasNuclear = allTokens.some((t) => NUCLEAR_COMMANDS.includes(t.toLowerCase()));
|
|
420
|
-
if (!hasNuclear) return { decision: "allow" };
|
|
502
|
+
const SYSTEM_DISASTER_COMMANDS = ["mkfs", "shred", "dd", "drop", "truncate", "purge"];
|
|
503
|
+
const hasSystemDisaster = allTokens.some(
|
|
504
|
+
(t) => SYSTEM_DISASTER_COMMANDS.includes(t.toLowerCase())
|
|
505
|
+
);
|
|
506
|
+
const isRootWipe = allTokens.includes("rm") && (allTokens.includes("/") || allTokens.includes("/*"));
|
|
507
|
+
if (hasSystemDisaster || isRootWipe) {
|
|
508
|
+
return { decision: "review", blockedByLabel: "Manual Nuclear Protection" };
|
|
509
|
+
}
|
|
510
|
+
return { decision: "allow" };
|
|
421
511
|
}
|
|
422
512
|
if (pathTokens.length > 0 && config.policy.sandboxPaths.length > 0) {
|
|
423
513
|
const allInSandbox = pathTokens.every((p) => matchesPattern(p, config.policy.sandboxPaths));
|
|
@@ -431,27 +521,39 @@ async function evaluatePolicy(toolName, args, agent) {
|
|
|
431
521
|
if (pathTokens.length > 0) {
|
|
432
522
|
const anyBlocked = pathTokens.some((p) => matchesPattern(p, rule.blockPaths || []));
|
|
433
523
|
if (anyBlocked)
|
|
434
|
-
return {
|
|
524
|
+
return {
|
|
525
|
+
decision: "review",
|
|
526
|
+
blockedByLabel: `Project/Global Config \u2014 rule "${rule.action}" (path blocked)`
|
|
527
|
+
};
|
|
435
528
|
const allAllowed = pathTokens.every((p) => matchesPattern(p, rule.allowPaths || []));
|
|
436
529
|
if (allAllowed) return { decision: "allow" };
|
|
437
530
|
}
|
|
438
|
-
return {
|
|
531
|
+
return {
|
|
532
|
+
decision: "review",
|
|
533
|
+
blockedByLabel: `Project/Global Config \u2014 rule "${rule.action}" (default block)`
|
|
534
|
+
};
|
|
439
535
|
}
|
|
440
536
|
}
|
|
537
|
+
let matchedDangerousWord;
|
|
441
538
|
const isDangerous = allTokens.some(
|
|
442
539
|
(token) => config.policy.dangerousWords.some((word) => {
|
|
443
540
|
const w = word.toLowerCase();
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
541
|
+
const hit = token === w || (() => {
|
|
542
|
+
try {
|
|
543
|
+
return new RegExp(`\\b${w}\\b`, "i").test(token);
|
|
544
|
+
} catch {
|
|
545
|
+
return false;
|
|
546
|
+
}
|
|
547
|
+
})();
|
|
548
|
+
if (hit && !matchedDangerousWord) matchedDangerousWord = word;
|
|
549
|
+
return hit;
|
|
450
550
|
})
|
|
451
551
|
);
|
|
452
552
|
if (isDangerous) {
|
|
453
|
-
|
|
454
|
-
|
|
553
|
+
return {
|
|
554
|
+
decision: "review",
|
|
555
|
+
blockedByLabel: `Project/Global Config \u2014 dangerous word: "${matchedDangerousWord}"`
|
|
556
|
+
};
|
|
455
557
|
}
|
|
456
558
|
if (config.settings.mode === "strict") {
|
|
457
559
|
const envConfig = getActiveEnvironment(config);
|
|
@@ -566,13 +668,16 @@ async function authorizeHeadless(toolName, args, allowTerminalFallback = false,
|
|
|
566
668
|
approvers.browser = false;
|
|
567
669
|
approvers.terminal = false;
|
|
568
670
|
}
|
|
671
|
+
if (config.settings.enableHookLogDebug && !isTestEnv2) {
|
|
672
|
+
appendHookDebug(toolName, args, meta);
|
|
673
|
+
}
|
|
569
674
|
const isManual = meta?.agent === "Terminal";
|
|
570
675
|
let explainableLabel = "Local Config";
|
|
571
676
|
if (config.settings.mode === "audit") {
|
|
572
677
|
if (!isIgnoredTool(toolName)) {
|
|
573
678
|
const policyResult = await evaluatePolicy(toolName, args, meta?.agent);
|
|
574
679
|
if (policyResult.decision === "review") {
|
|
575
|
-
|
|
680
|
+
appendLocalAudit(toolName, args, "allow", "audit-mode", meta);
|
|
576
681
|
sendDesktopNotification(
|
|
577
682
|
"Node9 Audit Mode",
|
|
578
683
|
`Would have blocked "${toolName}" (${policyResult.blockedByLabel || "Local Config"}) \u2014 running in audit mode`
|
|
@@ -584,20 +689,24 @@ async function authorizeHeadless(toolName, args, allowTerminalFallback = false,
|
|
|
584
689
|
if (!isIgnoredTool(toolName)) {
|
|
585
690
|
if (getActiveTrustSession(toolName)) {
|
|
586
691
|
if (creds?.apiKey) auditLocalAllow(toolName, args, "trust", creds, meta);
|
|
692
|
+
if (!isManual) appendLocalAudit(toolName, args, "allow", "trust", meta);
|
|
587
693
|
return { approved: true, checkedBy: "trust" };
|
|
588
694
|
}
|
|
589
695
|
const policyResult = await evaluatePolicy(toolName, args, meta?.agent);
|
|
590
696
|
if (policyResult.decision === "allow") {
|
|
591
697
|
if (creds?.apiKey) auditLocalAllow(toolName, args, "local-policy", creds, meta);
|
|
698
|
+
if (!isManual) appendLocalAudit(toolName, args, "allow", "local-policy", meta);
|
|
592
699
|
return { approved: true, checkedBy: "local-policy" };
|
|
593
700
|
}
|
|
594
701
|
explainableLabel = policyResult.blockedByLabel || "Local Config";
|
|
595
702
|
const persistent = getPersistentDecision(toolName);
|
|
596
703
|
if (persistent === "allow") {
|
|
597
704
|
if (creds?.apiKey) auditLocalAllow(toolName, args, "persistent", creds, meta);
|
|
705
|
+
if (!isManual) appendLocalAudit(toolName, args, "allow", "persistent", meta);
|
|
598
706
|
return { approved: true, checkedBy: "persistent" };
|
|
599
707
|
}
|
|
600
708
|
if (persistent === "deny") {
|
|
709
|
+
if (!isManual) appendLocalAudit(toolName, args, "deny", "persistent-deny", meta);
|
|
601
710
|
return {
|
|
602
711
|
approved: false,
|
|
603
712
|
reason: `This tool ("${toolName}") is explicitly listed in your 'Always Deny' list.`,
|
|
@@ -607,6 +716,7 @@ async function authorizeHeadless(toolName, args, allowTerminalFallback = false,
|
|
|
607
716
|
}
|
|
608
717
|
} else {
|
|
609
718
|
if (creds?.apiKey) auditLocalAllow(toolName, args, "ignoredTools", creds, meta);
|
|
719
|
+
if (!isManual) appendLocalAudit(toolName, args, "allow", "ignored", meta);
|
|
610
720
|
return { approved: true };
|
|
611
721
|
}
|
|
612
722
|
let cloudRequestId = null;
|
|
@@ -614,8 +724,7 @@ async function authorizeHeadless(toolName, args, allowTerminalFallback = false,
|
|
|
614
724
|
const cloudEnforced = approvers.cloud && !!creds?.apiKey;
|
|
615
725
|
if (cloudEnforced) {
|
|
616
726
|
try {
|
|
617
|
-
const
|
|
618
|
-
const initResult = await initNode9SaaS(toolName, args, creds, envConfig?.slackChannel, meta);
|
|
727
|
+
const initResult = await initNode9SaaS(toolName, args, creds, meta);
|
|
619
728
|
if (!initResult.pending) {
|
|
620
729
|
return {
|
|
621
730
|
approved: !!initResult.approved,
|
|
@@ -831,6 +940,15 @@ REASON: Action blocked because no approval channels are available. (Native/Brows
|
|
|
831
940
|
if (cloudRequestId && creds && finalResult.checkedBy !== "cloud") {
|
|
832
941
|
await resolveNode9SaaS(cloudRequestId, creds, finalResult.approved);
|
|
833
942
|
}
|
|
943
|
+
if (!isManual) {
|
|
944
|
+
appendLocalAudit(
|
|
945
|
+
toolName,
|
|
946
|
+
args,
|
|
947
|
+
finalResult.approved ? "allow" : "deny",
|
|
948
|
+
finalResult.checkedBy || finalResult.blockedBy || "unknown",
|
|
949
|
+
meta
|
|
950
|
+
);
|
|
951
|
+
}
|
|
834
952
|
return finalResult;
|
|
835
953
|
}
|
|
836
954
|
function getConfig() {
|
|
@@ -860,9 +978,10 @@ function getConfig() {
|
|
|
860
978
|
if (s.enableHookLogDebug !== void 0)
|
|
861
979
|
mergedSettings.enableHookLogDebug = s.enableHookLogDebug;
|
|
862
980
|
if (s.approvers) mergedSettings.approvers = { ...mergedSettings.approvers, ...s.approvers };
|
|
981
|
+
if (s.environment !== void 0) mergedSettings.environment = s.environment;
|
|
863
982
|
if (p.sandboxPaths) mergedPolicy.sandboxPaths.push(...p.sandboxPaths);
|
|
864
|
-
if (p.dangerousWords) mergedPolicy.dangerousWords = [...p.dangerousWords];
|
|
865
983
|
if (p.ignoredTools) mergedPolicy.ignoredTools.push(...p.ignoredTools);
|
|
984
|
+
if (p.dangerousWords) mergedPolicy.dangerousWords = [...p.dangerousWords];
|
|
866
985
|
if (p.toolInspection)
|
|
867
986
|
mergedPolicy.toolInspection = { ...mergedPolicy.toolInspection, ...p.toolInspection };
|
|
868
987
|
if (p.rules) mergedPolicy.rules.push(...p.rules);
|
|
@@ -889,7 +1008,7 @@ function tryLoadConfig(filePath) {
|
|
|
889
1008
|
}
|
|
890
1009
|
}
|
|
891
1010
|
function getActiveEnvironment(config) {
|
|
892
|
-
const env = process.env.NODE_ENV || "development";
|
|
1011
|
+
const env = config.settings.environment || process.env.NODE_ENV || "development";
|
|
893
1012
|
return config.environments[env] ?? null;
|
|
894
1013
|
}
|
|
895
1014
|
function getCredentials() {
|
|
@@ -949,7 +1068,7 @@ function auditLocalAllow(toolName, args, checkedBy, creds, meta) {
|
|
|
949
1068
|
}).catch(() => {
|
|
950
1069
|
});
|
|
951
1070
|
}
|
|
952
|
-
async function initNode9SaaS(toolName, args, creds,
|
|
1071
|
+
async function initNode9SaaS(toolName, args, creds, meta) {
|
|
953
1072
|
const controller = new AbortController();
|
|
954
1073
|
const timeout = setTimeout(() => controller.abort(), 1e4);
|
|
955
1074
|
try {
|
|
@@ -959,7 +1078,6 @@ async function initNode9SaaS(toolName, args, creds, slackChannel, meta) {
|
|
|
959
1078
|
body: JSON.stringify({
|
|
960
1079
|
toolName,
|
|
961
1080
|
args,
|
|
962
|
-
slackChannel,
|
|
963
1081
|
context: {
|
|
964
1082
|
agent: meta?.agent,
|
|
965
1083
|
mcpServer: meta?.mcpServer,
|