@node9/proxy 1.5.0 → 1.5.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 +616 -246
- package/dist/cli.mjs +596 -227
- package/dist/index.js +222 -35
- package/dist/index.mjs +218 -25
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -1,9 +1,61 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
3
|
+
var __esm = (fn, res) => function __init() {
|
|
4
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
5
|
+
};
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
// src/audit/hasher.ts
|
|
12
|
+
import { createHash } from "crypto";
|
|
13
|
+
function canonicalise(value) {
|
|
14
|
+
return _canonicalise(value, /* @__PURE__ */ new WeakSet());
|
|
15
|
+
}
|
|
16
|
+
function _canonicalise(value, seen) {
|
|
17
|
+
if (value === null || typeof value !== "object") return value;
|
|
18
|
+
if (value instanceof Date) return value.toISOString();
|
|
19
|
+
if (value instanceof RegExp) return value.toString();
|
|
20
|
+
if (Buffer.isBuffer(value)) return value.toString("base64");
|
|
21
|
+
if (seen.has(value)) return "[Circular]";
|
|
22
|
+
seen.add(value);
|
|
23
|
+
let result;
|
|
24
|
+
if (Array.isArray(value)) {
|
|
25
|
+
result = value.map((v) => _canonicalise(v, seen));
|
|
26
|
+
} else {
|
|
27
|
+
const obj = value;
|
|
28
|
+
result = Object.fromEntries(
|
|
29
|
+
Object.keys(obj).sort().map((k) => [k, _canonicalise(obj[k], seen)])
|
|
30
|
+
);
|
|
31
|
+
}
|
|
32
|
+
seen.delete(value);
|
|
33
|
+
return result;
|
|
34
|
+
}
|
|
35
|
+
function hashArgs(args) {
|
|
36
|
+
const canonical = JSON.stringify(canonicalise(args) ?? null);
|
|
37
|
+
return createHash("sha256").update(canonical).digest("hex").slice(0, 32);
|
|
38
|
+
}
|
|
39
|
+
var init_hasher = __esm({
|
|
40
|
+
"src/audit/hasher.ts"() {
|
|
41
|
+
"use strict";
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
|
|
1
45
|
// src/audit/index.ts
|
|
46
|
+
var audit_exports = {};
|
|
47
|
+
__export(audit_exports, {
|
|
48
|
+
HOOK_DEBUG_LOG: () => HOOK_DEBUG_LOG,
|
|
49
|
+
LOCAL_AUDIT_LOG: () => LOCAL_AUDIT_LOG,
|
|
50
|
+
appendConfigAudit: () => appendConfigAudit,
|
|
51
|
+
appendHookDebug: () => appendHookDebug,
|
|
52
|
+
appendLocalAudit: () => appendLocalAudit,
|
|
53
|
+
appendToLog: () => appendToLog,
|
|
54
|
+
redactSecrets: () => redactSecrets
|
|
55
|
+
});
|
|
2
56
|
import fs from "fs";
|
|
3
57
|
import path from "path";
|
|
4
58
|
import os from "os";
|
|
5
|
-
var LOCAL_AUDIT_LOG = path.join(os.homedir(), ".node9", "audit.log");
|
|
6
|
-
var HOOK_DEBUG_LOG = path.join(os.homedir(), ".node9", "hook-debug.log");
|
|
7
59
|
function redactSecrets(text) {
|
|
8
60
|
if (!text) return text;
|
|
9
61
|
let redacted = text;
|
|
@@ -25,24 +77,24 @@ function appendToLog(logPath, entry) {
|
|
|
25
77
|
} catch {
|
|
26
78
|
}
|
|
27
79
|
}
|
|
28
|
-
function appendHookDebug(toolName, args, meta) {
|
|
29
|
-
const
|
|
80
|
+
function appendHookDebug(toolName, args, meta, auditHashArgsEnabled) {
|
|
81
|
+
const argsField = auditHashArgsEnabled ? { argsHash: hashArgs(args) } : { args: args ? JSON.parse(redactSecrets(JSON.stringify(args))) : {} };
|
|
30
82
|
appendToLog(HOOK_DEBUG_LOG, {
|
|
31
83
|
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
32
84
|
tool: toolName,
|
|
33
|
-
|
|
85
|
+
...argsField,
|
|
34
86
|
agent: meta?.agent,
|
|
35
87
|
mcpServer: meta?.mcpServer,
|
|
36
88
|
hostname: os.hostname(),
|
|
37
89
|
cwd: process.cwd()
|
|
38
90
|
});
|
|
39
91
|
}
|
|
40
|
-
function appendLocalAudit(toolName, args, decision, checkedBy, meta) {
|
|
41
|
-
const
|
|
92
|
+
function appendLocalAudit(toolName, args, decision, checkedBy, meta, auditHashArgsEnabled) {
|
|
93
|
+
const argsField = auditHashArgsEnabled ? { argsHash: hashArgs(args) } : { args: args ? JSON.parse(redactSecrets(JSON.stringify(args))) : {} };
|
|
42
94
|
appendToLog(LOCAL_AUDIT_LOG, {
|
|
43
95
|
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
44
96
|
tool: toolName,
|
|
45
|
-
|
|
97
|
+
...argsField,
|
|
46
98
|
decision,
|
|
47
99
|
checkedBy,
|
|
48
100
|
agent: meta?.agent,
|
|
@@ -50,6 +102,25 @@ function appendLocalAudit(toolName, args, decision, checkedBy, meta) {
|
|
|
50
102
|
hostname: os.hostname()
|
|
51
103
|
});
|
|
52
104
|
}
|
|
105
|
+
function appendConfigAudit(entry) {
|
|
106
|
+
appendToLog(LOCAL_AUDIT_LOG, {
|
|
107
|
+
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
108
|
+
...entry,
|
|
109
|
+
hostname: os.hostname()
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
var LOCAL_AUDIT_LOG, HOOK_DEBUG_LOG;
|
|
113
|
+
var init_audit = __esm({
|
|
114
|
+
"src/audit/index.ts"() {
|
|
115
|
+
"use strict";
|
|
116
|
+
init_hasher();
|
|
117
|
+
LOCAL_AUDIT_LOG = path.join(os.homedir(), ".node9", "audit.log");
|
|
118
|
+
HOOK_DEBUG_LOG = path.join(os.homedir(), ".node9", "hook-debug.log");
|
|
119
|
+
}
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
// src/core.ts
|
|
123
|
+
init_audit();
|
|
53
124
|
|
|
54
125
|
// src/config/index.ts
|
|
55
126
|
import fs3 from "fs";
|
|
@@ -118,7 +189,8 @@ var ConfigFileSchema = z.object({
|
|
|
118
189
|
environment: z.string().optional(),
|
|
119
190
|
slackEnabled: z.boolean().optional(),
|
|
120
191
|
enableTrustSessions: z.boolean().optional(),
|
|
121
|
-
allowGlobalPause: z.boolean().optional()
|
|
192
|
+
allowGlobalPause: z.boolean().optional(),
|
|
193
|
+
auditHashArgs: z.boolean().optional()
|
|
122
194
|
}).optional(),
|
|
123
195
|
policy: z.object({
|
|
124
196
|
sandboxPaths: z.array(z.string()).optional(),
|
|
@@ -414,6 +486,7 @@ var DEFAULT_CONFIG = {
|
|
|
414
486
|
approvalTimeoutMs: 12e4,
|
|
415
487
|
// 120-second auto-deny timeout
|
|
416
488
|
flightRecorder: true,
|
|
489
|
+
auditHashArgs: true,
|
|
417
490
|
approvers: { native: true, browser: true, cloud: false, terminal: true }
|
|
418
491
|
},
|
|
419
492
|
policy: {
|
|
@@ -1981,6 +2054,45 @@ async function notifyDaemonViewer(toolName, args, meta, riskMetadata) {
|
|
|
1981
2054
|
const { id, allowCount } = await res.json();
|
|
1982
2055
|
return { id, allowCount: allowCount ?? 1 };
|
|
1983
2056
|
}
|
|
2057
|
+
async function notifyTaint(filePath, source) {
|
|
2058
|
+
if (!isDaemonRunning()) return;
|
|
2059
|
+
const base = `http://${DAEMON_HOST}:${DAEMON_PORT}`;
|
|
2060
|
+
try {
|
|
2061
|
+
await fetch(`${base}/taint`, {
|
|
2062
|
+
method: "POST",
|
|
2063
|
+
headers: { "Content-Type": "application/json" },
|
|
2064
|
+
body: JSON.stringify({ path: filePath, source }),
|
|
2065
|
+
signal: AbortSignal.timeout(1e3)
|
|
2066
|
+
});
|
|
2067
|
+
} catch {
|
|
2068
|
+
}
|
|
2069
|
+
}
|
|
2070
|
+
async function checkTaint(paths) {
|
|
2071
|
+
if (paths.length === 0) return { tainted: false };
|
|
2072
|
+
if (!isDaemonRunning()) return { tainted: false, daemonUnavailable: true };
|
|
2073
|
+
const base = `http://${DAEMON_HOST}:${DAEMON_PORT}`;
|
|
2074
|
+
try {
|
|
2075
|
+
const res = await fetch(`${base}/taint/check`, {
|
|
2076
|
+
method: "POST",
|
|
2077
|
+
headers: { "Content-Type": "application/json" },
|
|
2078
|
+
body: JSON.stringify({ paths }),
|
|
2079
|
+
signal: AbortSignal.timeout(2e3)
|
|
2080
|
+
});
|
|
2081
|
+
return await res.json();
|
|
2082
|
+
} catch (err) {
|
|
2083
|
+
try {
|
|
2084
|
+
const { appendToLog: appendToLog2, HOOK_DEBUG_LOG: HOOK_DEBUG_LOG2 } = await Promise.resolve().then(() => (init_audit(), audit_exports));
|
|
2085
|
+
appendToLog2(HOOK_DEBUG_LOG2, {
|
|
2086
|
+
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2087
|
+
event: "checkTaint-error",
|
|
2088
|
+
error: String(err),
|
|
2089
|
+
paths
|
|
2090
|
+
});
|
|
2091
|
+
} catch {
|
|
2092
|
+
}
|
|
2093
|
+
return { tainted: false, daemonUnavailable: true };
|
|
2094
|
+
}
|
|
2095
|
+
}
|
|
1984
2096
|
async function resolveViaDaemon(id, decision, internalToken, source) {
|
|
1985
2097
|
const base = `http://${DAEMON_HOST}:${DAEMON_PORT}`;
|
|
1986
2098
|
await fetch(`${base}/resolve/${id}`, {
|
|
@@ -2317,7 +2429,11 @@ end run`;
|
|
|
2317
2429
|
});
|
|
2318
2430
|
}
|
|
2319
2431
|
|
|
2432
|
+
// src/auth/orchestrator.ts
|
|
2433
|
+
init_audit();
|
|
2434
|
+
|
|
2320
2435
|
// src/auth/cloud.ts
|
|
2436
|
+
init_audit();
|
|
2321
2437
|
import fs9 from "fs";
|
|
2322
2438
|
import os8 from "os";
|
|
2323
2439
|
function auditLocalAllow(toolName, args, checkedBy, creds, meta) {
|
|
@@ -2428,6 +2544,51 @@ async function resolveNode9SaaS(requestId, creds, approved, decidedBy) {
|
|
|
2428
2544
|
}
|
|
2429
2545
|
|
|
2430
2546
|
// src/auth/orchestrator.ts
|
|
2547
|
+
var WRITE_TOOLS = /* @__PURE__ */ new Set([
|
|
2548
|
+
"write",
|
|
2549
|
+
"write_file",
|
|
2550
|
+
"create_file",
|
|
2551
|
+
"edit",
|
|
2552
|
+
"multiedit",
|
|
2553
|
+
"str_replace_based_edit_tool",
|
|
2554
|
+
"replace",
|
|
2555
|
+
"notebook_edit",
|
|
2556
|
+
"notebookedit"
|
|
2557
|
+
]);
|
|
2558
|
+
function isWriteTool(toolName) {
|
|
2559
|
+
const t = toolName.toLowerCase().replace(/[^a-z_]/g, "_");
|
|
2560
|
+
return WRITE_TOOLS.has(t);
|
|
2561
|
+
}
|
|
2562
|
+
function extractFilePaths(toolName, args) {
|
|
2563
|
+
const paths = [];
|
|
2564
|
+
if (!args || typeof args !== "object" || Array.isArray(args)) return paths;
|
|
2565
|
+
const a = args;
|
|
2566
|
+
for (const key of ["file_path", "path", "filename", "source", "src", "input"]) {
|
|
2567
|
+
if (typeof a[key] === "string" && a[key]) paths.push(a[key]);
|
|
2568
|
+
}
|
|
2569
|
+
const cmd = typeof a.command === "string" ? a.command : typeof a.cmd === "string" ? a.cmd : "";
|
|
2570
|
+
if (cmd) {
|
|
2571
|
+
for (const m of cmd.matchAll(/(?:-T|--upload-file|--data(?:-binary)?)\s+@?(\S+)/g)) {
|
|
2572
|
+
paths.push(m[1]);
|
|
2573
|
+
}
|
|
2574
|
+
for (const m of cmd.matchAll(/\b(?:scp|rsync)\s+(?:-\S+\s+)*(\S+)\s+\S+@/g)) {
|
|
2575
|
+
paths.push(m[1]);
|
|
2576
|
+
}
|
|
2577
|
+
for (const m of cmd.matchAll(/<\s*(\S+)/g)) {
|
|
2578
|
+
paths.push(m[1]);
|
|
2579
|
+
}
|
|
2580
|
+
}
|
|
2581
|
+
return paths.filter(Boolean);
|
|
2582
|
+
}
|
|
2583
|
+
function isNetworkTool(toolName, args) {
|
|
2584
|
+
const t = toolName.toLowerCase();
|
|
2585
|
+
if (t === "bash" || t === "shell" || t === "run_shell_command" || t === "terminal.execute") {
|
|
2586
|
+
const a = args;
|
|
2587
|
+
const cmd = typeof a?.command === "string" ? a.command : typeof a?.cmd === "string" ? a.cmd : "";
|
|
2588
|
+
return /\b(curl|wget|scp|rsync|nc|ncat|netcat|ssh)\b/.test(cmd);
|
|
2589
|
+
}
|
|
2590
|
+
return false;
|
|
2591
|
+
}
|
|
2431
2592
|
var ACTIVITY_SOCKET_PATH = process.platform === "win32" ? "\\\\.\\pipe\\node9-activity" : path13.join(os9.tmpdir(), "node9-activity.sock");
|
|
2432
2593
|
function notifyActivity(data) {
|
|
2433
2594
|
return new Promise((resolve) => {
|
|
@@ -2458,7 +2619,7 @@ async function authorizeHeadless(toolName, args, meta, options) {
|
|
|
2458
2619
|
id: actId,
|
|
2459
2620
|
tool: toolName,
|
|
2460
2621
|
ts: actTs,
|
|
2461
|
-
status: result.approved ? "allow" : result.blockedByLabel?.includes("DLP") ? "dlp" : "block",
|
|
2622
|
+
status: result.approved ? "allow" : result.blockedByLabel?.includes("DLP") ? "dlp" : result.blockedByLabel?.includes("Taint") ? "taint" : "block",
|
|
2462
2623
|
label: result.blockedByLabel
|
|
2463
2624
|
});
|
|
2464
2625
|
}
|
|
@@ -2472,6 +2633,7 @@ async function _authorizeHeadlessCore(toolName, args, meta, options) {
|
|
|
2472
2633
|
if (pauseState.paused) return { approved: true, checkedBy: "paused" };
|
|
2473
2634
|
const creds = getCredentials();
|
|
2474
2635
|
const config = getConfig(options?.cwd);
|
|
2636
|
+
const hashAuditArgs = config.settings.auditHashArgs === true;
|
|
2475
2637
|
const isTestEnv2 = !!(process.env.VITEST || process.env.NODE_ENV === "test" || process.env.CI || process.env.NODE9_TESTING === "1");
|
|
2476
2638
|
const approvers = {
|
|
2477
2639
|
...config.settings.approvers || { native: true, browser: true, cloud: true, terminal: true }
|
|
@@ -2482,13 +2644,26 @@ async function _authorizeHeadlessCore(toolName, args, meta, options) {
|
|
|
2482
2644
|
approvers.terminal = false;
|
|
2483
2645
|
}
|
|
2484
2646
|
if (config.settings.enableHookLogDebug && !isTestEnv2) {
|
|
2485
|
-
appendHookDebug(toolName, args, meta);
|
|
2647
|
+
appendHookDebug(toolName, args, meta, hashAuditArgs);
|
|
2486
2648
|
}
|
|
2487
2649
|
const isManual = meta?.agent === "Terminal";
|
|
2488
2650
|
let explainableLabel = "Local Config";
|
|
2489
2651
|
let policyMatchedField;
|
|
2490
2652
|
let policyMatchedWord;
|
|
2491
2653
|
let riskMetadata;
|
|
2654
|
+
let taintWarning = null;
|
|
2655
|
+
if (isNetworkTool(toolName, args)) {
|
|
2656
|
+
const filePaths = extractFilePaths(toolName, args);
|
|
2657
|
+
if (filePaths.length > 0) {
|
|
2658
|
+
const taintResult = await checkTaint(filePaths);
|
|
2659
|
+
if (taintResult.tainted && taintResult.record) {
|
|
2660
|
+
const { path: taintedPath, source: taintSource } = taintResult.record;
|
|
2661
|
+
taintWarning = `\u26A0\uFE0F ${taintedPath} was flagged by ${taintSource} \u2014 this file may contain sensitive data`;
|
|
2662
|
+
} else if (taintResult.daemonUnavailable) {
|
|
2663
|
+
taintWarning = `\u26A0\uFE0F Taint service unavailable \u2014 cannot verify if ${filePaths.join(", ")} is clean`;
|
|
2664
|
+
}
|
|
2665
|
+
}
|
|
2666
|
+
}
|
|
2492
2667
|
if (config.policy.dlp.enabled && (!isIgnoredTool(toolName) || config.policy.dlp.scanIgnoredTools)) {
|
|
2493
2668
|
const argsObj = args && typeof args === "object" && !Array.isArray(args) ? args : {};
|
|
2494
2669
|
const filePath = String(argsObj.file_path ?? argsObj.path ?? argsObj.filename ?? "");
|
|
@@ -2496,7 +2671,10 @@ async function _authorizeHeadlessCore(toolName, args, meta, options) {
|
|
|
2496
2671
|
if (dlpMatch) {
|
|
2497
2672
|
const dlpReason = `\u{1F6A8} DATA LOSS PREVENTION: ${dlpMatch.patternName} detected in field "${dlpMatch.fieldPath}" (${dlpMatch.redactedSample})`;
|
|
2498
2673
|
if (dlpMatch.severity === "block") {
|
|
2499
|
-
if (!isManual) appendLocalAudit(toolName, args, "deny", "dlp-block", meta);
|
|
2674
|
+
if (!isManual) appendLocalAudit(toolName, args, "deny", "dlp-block", meta, true);
|
|
2675
|
+
if (isWriteTool(toolName) && filePath) {
|
|
2676
|
+
await notifyTaint(filePath, `DLP:${dlpMatch.patternName}`);
|
|
2677
|
+
}
|
|
2500
2678
|
return {
|
|
2501
2679
|
approved: false,
|
|
2502
2680
|
reason: dlpReason,
|
|
@@ -2504,7 +2682,8 @@ async function _authorizeHeadlessCore(toolName, args, meta, options) {
|
|
|
2504
2682
|
blockedByLabel: "\u{1F6A8} Node9 DLP (Secret Detected)"
|
|
2505
2683
|
};
|
|
2506
2684
|
}
|
|
2507
|
-
if (!isManual)
|
|
2685
|
+
if (!isManual)
|
|
2686
|
+
appendLocalAudit(toolName, args, "allow", "dlp-review-flagged", meta, hashAuditArgs);
|
|
2508
2687
|
explainableLabel = "\u{1F6A8} Node9 DLP (Credential Review)";
|
|
2509
2688
|
}
|
|
2510
2689
|
}
|
|
@@ -2512,7 +2691,7 @@ async function _authorizeHeadlessCore(toolName, args, meta, options) {
|
|
|
2512
2691
|
if (!isIgnoredTool(toolName)) {
|
|
2513
2692
|
const policyResult = await evaluatePolicy(toolName, args, meta?.agent, options?.cwd);
|
|
2514
2693
|
if (policyResult.decision === "review") {
|
|
2515
|
-
appendLocalAudit(toolName, args, "allow", "audit-mode", meta);
|
|
2694
|
+
appendLocalAudit(toolName, args, "allow", "audit-mode", meta, hashAuditArgs);
|
|
2516
2695
|
if (approvers.cloud && creds?.apiKey) {
|
|
2517
2696
|
await auditLocalAllow(toolName, args, "audit-mode", creds, meta);
|
|
2518
2697
|
}
|
|
@@ -2520,22 +2699,23 @@ async function _authorizeHeadlessCore(toolName, args, meta, options) {
|
|
|
2520
2699
|
}
|
|
2521
2700
|
return { approved: true, checkedBy: "audit" };
|
|
2522
2701
|
}
|
|
2523
|
-
if (!isIgnoredTool(toolName)) {
|
|
2702
|
+
if (!taintWarning && !isIgnoredTool(toolName)) {
|
|
2524
2703
|
if (getActiveTrustSession(toolName)) {
|
|
2525
2704
|
if (approvers.cloud && creds?.apiKey)
|
|
2526
2705
|
await auditLocalAllow(toolName, args, "trust", creds, meta);
|
|
2527
|
-
if (!isManual) appendLocalAudit(toolName, args, "allow", "trust", meta);
|
|
2706
|
+
if (!isManual) appendLocalAudit(toolName, args, "allow", "trust", meta, hashAuditArgs);
|
|
2528
2707
|
return { approved: true, checkedBy: "trust" };
|
|
2529
2708
|
}
|
|
2530
2709
|
const policyResult = await evaluatePolicy(toolName, args, meta?.agent);
|
|
2531
2710
|
if (policyResult.decision === "allow") {
|
|
2532
2711
|
if (approvers.cloud && creds?.apiKey)
|
|
2533
2712
|
auditLocalAllow(toolName, args, "local-policy", creds, meta);
|
|
2534
|
-
if (!isManual) appendLocalAudit(toolName, args, "allow", "local-policy", meta);
|
|
2713
|
+
if (!isManual) appendLocalAudit(toolName, args, "allow", "local-policy", meta, hashAuditArgs);
|
|
2535
2714
|
return { approved: true, checkedBy: "local-policy" };
|
|
2536
2715
|
}
|
|
2537
2716
|
if (policyResult.decision === "block") {
|
|
2538
|
-
if (!isManual)
|
|
2717
|
+
if (!isManual)
|
|
2718
|
+
appendLocalAudit(toolName, args, "deny", "smart-rule-block", meta, hashAuditArgs);
|
|
2539
2719
|
return {
|
|
2540
2720
|
approved: false,
|
|
2541
2721
|
reason: policyResult.reason ?? "Action explicitly blocked by Smart Policy.",
|
|
@@ -2554,15 +2734,16 @@ async function _authorizeHeadlessCore(toolName, args, meta, options) {
|
|
|
2554
2734
|
policyMatchedWord,
|
|
2555
2735
|
policyResult.ruleName
|
|
2556
2736
|
);
|
|
2557
|
-
const persistent = getPersistentDecision(toolName);
|
|
2737
|
+
const persistent = policyResult.ruleName ? null : getPersistentDecision(toolName);
|
|
2558
2738
|
if (persistent === "allow") {
|
|
2559
2739
|
if (approvers.cloud && creds?.apiKey)
|
|
2560
2740
|
await auditLocalAllow(toolName, args, "persistent", creds, meta);
|
|
2561
|
-
if (!isManual) appendLocalAudit(toolName, args, "allow", "persistent", meta);
|
|
2741
|
+
if (!isManual) appendLocalAudit(toolName, args, "allow", "persistent", meta, hashAuditArgs);
|
|
2562
2742
|
return { approved: true, checkedBy: "persistent" };
|
|
2563
2743
|
}
|
|
2564
2744
|
if (persistent === "deny") {
|
|
2565
|
-
if (!isManual)
|
|
2745
|
+
if (!isManual)
|
|
2746
|
+
appendLocalAudit(toolName, args, "deny", "persistent-deny", meta, hashAuditArgs);
|
|
2566
2747
|
return {
|
|
2567
2748
|
approved: false,
|
|
2568
2749
|
reason: `This tool ("${toolName}") is explicitly listed in your 'Always Deny' list.`,
|
|
@@ -2570,10 +2751,21 @@ async function _authorizeHeadlessCore(toolName, args, meta, options) {
|
|
|
2570
2751
|
blockedByLabel: "Persistent User Rule"
|
|
2571
2752
|
};
|
|
2572
2753
|
}
|
|
2573
|
-
} else {
|
|
2574
|
-
if (!isManual) appendLocalAudit(toolName, args, "allow", "ignored", meta);
|
|
2754
|
+
} else if (!taintWarning) {
|
|
2755
|
+
if (!isManual) appendLocalAudit(toolName, args, "allow", "ignored", meta, hashAuditArgs);
|
|
2575
2756
|
return { approved: true };
|
|
2576
2757
|
}
|
|
2758
|
+
if (taintWarning) {
|
|
2759
|
+
explainableLabel = "\u{1F534} Node9 Taint (Exfiltration Prevention)";
|
|
2760
|
+
riskMetadata = computeRiskMetadata(
|
|
2761
|
+
args,
|
|
2762
|
+
7,
|
|
2763
|
+
explainableLabel,
|
|
2764
|
+
void 0,
|
|
2765
|
+
void 0,
|
|
2766
|
+
taintWarning
|
|
2767
|
+
);
|
|
2768
|
+
}
|
|
2577
2769
|
let cloudRequestId = null;
|
|
2578
2770
|
const cloudEnforced = approvers.cloud && !!creds?.apiKey;
|
|
2579
2771
|
if (cloudEnforced) {
|
|
@@ -2592,7 +2784,7 @@ async function _authorizeHeadlessCore(toolName, args, meta, options) {
|
|
|
2592
2784
|
};
|
|
2593
2785
|
}
|
|
2594
2786
|
cloudRequestId = initResult.requestId || null;
|
|
2595
|
-
explainableLabel = "Organization Policy (SaaS)";
|
|
2787
|
+
if (!taintWarning) explainableLabel = "Organization Policy (SaaS)";
|
|
2596
2788
|
} catch {
|
|
2597
2789
|
}
|
|
2598
2790
|
}
|
|
@@ -2779,7 +2971,8 @@ REASON: Action blocked because no approval channels are available. (Native/Brows
|
|
|
2779
2971
|
args,
|
|
2780
2972
|
finalResult.approved ? "allow" : "deny",
|
|
2781
2973
|
finalResult.checkedBy || finalResult.blockedBy || "unknown",
|
|
2782
|
-
meta
|
|
2974
|
+
meta,
|
|
2975
|
+
hashAuditArgs
|
|
2783
2976
|
);
|
|
2784
2977
|
}
|
|
2785
2978
|
return finalResult;
|