@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.js
CHANGED
|
@@ -5,6 +5,9 @@ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
|
5
5
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
6
|
var __getProtoOf = Object.getPrototypeOf;
|
|
7
7
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __esm = (fn, res) => function __init() {
|
|
9
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
10
|
+
};
|
|
8
11
|
var __export = (target, all) => {
|
|
9
12
|
for (var name in all)
|
|
10
13
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
@@ -27,19 +30,52 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
27
30
|
));
|
|
28
31
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
32
|
|
|
30
|
-
// src/
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
33
|
+
// src/audit/hasher.ts
|
|
34
|
+
function canonicalise(value) {
|
|
35
|
+
return _canonicalise(value, /* @__PURE__ */ new WeakSet());
|
|
36
|
+
}
|
|
37
|
+
function _canonicalise(value, seen) {
|
|
38
|
+
if (value === null || typeof value !== "object") return value;
|
|
39
|
+
if (value instanceof Date) return value.toISOString();
|
|
40
|
+
if (value instanceof RegExp) return value.toString();
|
|
41
|
+
if (Buffer.isBuffer(value)) return value.toString("base64");
|
|
42
|
+
if (seen.has(value)) return "[Circular]";
|
|
43
|
+
seen.add(value);
|
|
44
|
+
let result;
|
|
45
|
+
if (Array.isArray(value)) {
|
|
46
|
+
result = value.map((v) => _canonicalise(v, seen));
|
|
47
|
+
} else {
|
|
48
|
+
const obj = value;
|
|
49
|
+
result = Object.fromEntries(
|
|
50
|
+
Object.keys(obj).sort().map((k) => [k, _canonicalise(obj[k], seen)])
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
seen.delete(value);
|
|
54
|
+
return result;
|
|
55
|
+
}
|
|
56
|
+
function hashArgs(args) {
|
|
57
|
+
const canonical = JSON.stringify(canonicalise(args) ?? null);
|
|
58
|
+
return (0, import_crypto.createHash)("sha256").update(canonical).digest("hex").slice(0, 32);
|
|
59
|
+
}
|
|
60
|
+
var import_crypto;
|
|
61
|
+
var init_hasher = __esm({
|
|
62
|
+
"src/audit/hasher.ts"() {
|
|
63
|
+
"use strict";
|
|
64
|
+
import_crypto = require("crypto");
|
|
65
|
+
}
|
|
34
66
|
});
|
|
35
|
-
module.exports = __toCommonJS(src_exports);
|
|
36
67
|
|
|
37
68
|
// src/audit/index.ts
|
|
38
|
-
var
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
69
|
+
var audit_exports = {};
|
|
70
|
+
__export(audit_exports, {
|
|
71
|
+
HOOK_DEBUG_LOG: () => HOOK_DEBUG_LOG,
|
|
72
|
+
LOCAL_AUDIT_LOG: () => LOCAL_AUDIT_LOG,
|
|
73
|
+
appendConfigAudit: () => appendConfigAudit,
|
|
74
|
+
appendHookDebug: () => appendHookDebug,
|
|
75
|
+
appendLocalAudit: () => appendLocalAudit,
|
|
76
|
+
appendToLog: () => appendToLog,
|
|
77
|
+
redactSecrets: () => redactSecrets
|
|
78
|
+
});
|
|
43
79
|
function redactSecrets(text) {
|
|
44
80
|
if (!text) return text;
|
|
45
81
|
let redacted = text;
|
|
@@ -61,24 +97,24 @@ function appendToLog(logPath, entry) {
|
|
|
61
97
|
} catch {
|
|
62
98
|
}
|
|
63
99
|
}
|
|
64
|
-
function appendHookDebug(toolName, args, meta) {
|
|
65
|
-
const
|
|
100
|
+
function appendHookDebug(toolName, args, meta, auditHashArgsEnabled) {
|
|
101
|
+
const argsField = auditHashArgsEnabled ? { argsHash: hashArgs(args) } : { args: args ? JSON.parse(redactSecrets(JSON.stringify(args))) : {} };
|
|
66
102
|
appendToLog(HOOK_DEBUG_LOG, {
|
|
67
103
|
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
68
104
|
tool: toolName,
|
|
69
|
-
|
|
105
|
+
...argsField,
|
|
70
106
|
agent: meta?.agent,
|
|
71
107
|
mcpServer: meta?.mcpServer,
|
|
72
108
|
hostname: import_os.default.hostname(),
|
|
73
109
|
cwd: process.cwd()
|
|
74
110
|
});
|
|
75
111
|
}
|
|
76
|
-
function appendLocalAudit(toolName, args, decision, checkedBy, meta) {
|
|
77
|
-
const
|
|
112
|
+
function appendLocalAudit(toolName, args, decision, checkedBy, meta, auditHashArgsEnabled) {
|
|
113
|
+
const argsField = auditHashArgsEnabled ? { argsHash: hashArgs(args) } : { args: args ? JSON.parse(redactSecrets(JSON.stringify(args))) : {} };
|
|
78
114
|
appendToLog(LOCAL_AUDIT_LOG, {
|
|
79
115
|
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
80
116
|
tool: toolName,
|
|
81
|
-
|
|
117
|
+
...argsField,
|
|
82
118
|
decision,
|
|
83
119
|
checkedBy,
|
|
84
120
|
agent: meta?.agent,
|
|
@@ -86,6 +122,35 @@ function appendLocalAudit(toolName, args, decision, checkedBy, meta) {
|
|
|
86
122
|
hostname: import_os.default.hostname()
|
|
87
123
|
});
|
|
88
124
|
}
|
|
125
|
+
function appendConfigAudit(entry) {
|
|
126
|
+
appendToLog(LOCAL_AUDIT_LOG, {
|
|
127
|
+
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
128
|
+
...entry,
|
|
129
|
+
hostname: import_os.default.hostname()
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
var import_fs, import_path, import_os, LOCAL_AUDIT_LOG, HOOK_DEBUG_LOG;
|
|
133
|
+
var init_audit = __esm({
|
|
134
|
+
"src/audit/index.ts"() {
|
|
135
|
+
"use strict";
|
|
136
|
+
import_fs = __toESM(require("fs"));
|
|
137
|
+
import_path = __toESM(require("path"));
|
|
138
|
+
import_os = __toESM(require("os"));
|
|
139
|
+
init_hasher();
|
|
140
|
+
LOCAL_AUDIT_LOG = import_path.default.join(import_os.default.homedir(), ".node9", "audit.log");
|
|
141
|
+
HOOK_DEBUG_LOG = import_path.default.join(import_os.default.homedir(), ".node9", "hook-debug.log");
|
|
142
|
+
}
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
// src/index.ts
|
|
146
|
+
var src_exports = {};
|
|
147
|
+
__export(src_exports, {
|
|
148
|
+
protect: () => protect
|
|
149
|
+
});
|
|
150
|
+
module.exports = __toCommonJS(src_exports);
|
|
151
|
+
|
|
152
|
+
// src/core.ts
|
|
153
|
+
init_audit();
|
|
89
154
|
|
|
90
155
|
// src/config/index.ts
|
|
91
156
|
var import_fs3 = __toESM(require("fs"));
|
|
@@ -154,7 +219,8 @@ var ConfigFileSchema = import_zod.z.object({
|
|
|
154
219
|
environment: import_zod.z.string().optional(),
|
|
155
220
|
slackEnabled: import_zod.z.boolean().optional(),
|
|
156
221
|
enableTrustSessions: import_zod.z.boolean().optional(),
|
|
157
|
-
allowGlobalPause: import_zod.z.boolean().optional()
|
|
222
|
+
allowGlobalPause: import_zod.z.boolean().optional(),
|
|
223
|
+
auditHashArgs: import_zod.z.boolean().optional()
|
|
158
224
|
}).optional(),
|
|
159
225
|
policy: import_zod.z.object({
|
|
160
226
|
sandboxPaths: import_zod.z.array(import_zod.z.string()).optional(),
|
|
@@ -450,6 +516,7 @@ var DEFAULT_CONFIG = {
|
|
|
450
516
|
approvalTimeoutMs: 12e4,
|
|
451
517
|
// 120-second auto-deny timeout
|
|
452
518
|
flightRecorder: true,
|
|
519
|
+
auditHashArgs: true,
|
|
453
520
|
approvers: { native: true, browser: true, cloud: false, terminal: true }
|
|
454
521
|
},
|
|
455
522
|
policy: {
|
|
@@ -2017,6 +2084,45 @@ async function notifyDaemonViewer(toolName, args, meta, riskMetadata) {
|
|
|
2017
2084
|
const { id, allowCount } = await res.json();
|
|
2018
2085
|
return { id, allowCount: allowCount ?? 1 };
|
|
2019
2086
|
}
|
|
2087
|
+
async function notifyTaint(filePath, source) {
|
|
2088
|
+
if (!isDaemonRunning()) return;
|
|
2089
|
+
const base = `http://${DAEMON_HOST}:${DAEMON_PORT}`;
|
|
2090
|
+
try {
|
|
2091
|
+
await fetch(`${base}/taint`, {
|
|
2092
|
+
method: "POST",
|
|
2093
|
+
headers: { "Content-Type": "application/json" },
|
|
2094
|
+
body: JSON.stringify({ path: filePath, source }),
|
|
2095
|
+
signal: AbortSignal.timeout(1e3)
|
|
2096
|
+
});
|
|
2097
|
+
} catch {
|
|
2098
|
+
}
|
|
2099
|
+
}
|
|
2100
|
+
async function checkTaint(paths) {
|
|
2101
|
+
if (paths.length === 0) return { tainted: false };
|
|
2102
|
+
if (!isDaemonRunning()) return { tainted: false, daemonUnavailable: true };
|
|
2103
|
+
const base = `http://${DAEMON_HOST}:${DAEMON_PORT}`;
|
|
2104
|
+
try {
|
|
2105
|
+
const res = await fetch(`${base}/taint/check`, {
|
|
2106
|
+
method: "POST",
|
|
2107
|
+
headers: { "Content-Type": "application/json" },
|
|
2108
|
+
body: JSON.stringify({ paths }),
|
|
2109
|
+
signal: AbortSignal.timeout(2e3)
|
|
2110
|
+
});
|
|
2111
|
+
return await res.json();
|
|
2112
|
+
} catch (err) {
|
|
2113
|
+
try {
|
|
2114
|
+
const { appendToLog: appendToLog2, HOOK_DEBUG_LOG: HOOK_DEBUG_LOG2 } = await Promise.resolve().then(() => (init_audit(), audit_exports));
|
|
2115
|
+
appendToLog2(HOOK_DEBUG_LOG2, {
|
|
2116
|
+
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2117
|
+
event: "checkTaint-error",
|
|
2118
|
+
error: String(err),
|
|
2119
|
+
paths
|
|
2120
|
+
});
|
|
2121
|
+
} catch {
|
|
2122
|
+
}
|
|
2123
|
+
return { tainted: false, daemonUnavailable: true };
|
|
2124
|
+
}
|
|
2125
|
+
}
|
|
2020
2126
|
async function resolveViaDaemon(id, decision, internalToken, source) {
|
|
2021
2127
|
const base = `http://${DAEMON_HOST}:${DAEMON_PORT}`;
|
|
2022
2128
|
await fetch(`${base}/resolve/${id}`, {
|
|
@@ -2031,7 +2137,7 @@ async function resolveViaDaemon(id, decision, internalToken, source) {
|
|
|
2031
2137
|
var import_net = __toESM(require("net"));
|
|
2032
2138
|
var import_path13 = __toESM(require("path"));
|
|
2033
2139
|
var import_os9 = __toESM(require("os"));
|
|
2034
|
-
var
|
|
2140
|
+
var import_crypto2 = require("crypto");
|
|
2035
2141
|
|
|
2036
2142
|
// src/ui/native.ts
|
|
2037
2143
|
var import_child_process2 = require("child_process");
|
|
@@ -2353,9 +2459,13 @@ end run`;
|
|
|
2353
2459
|
});
|
|
2354
2460
|
}
|
|
2355
2461
|
|
|
2462
|
+
// src/auth/orchestrator.ts
|
|
2463
|
+
init_audit();
|
|
2464
|
+
|
|
2356
2465
|
// src/auth/cloud.ts
|
|
2357
2466
|
var import_fs9 = __toESM(require("fs"));
|
|
2358
2467
|
var import_os8 = __toESM(require("os"));
|
|
2468
|
+
init_audit();
|
|
2359
2469
|
function auditLocalAllow(toolName, args, checkedBy, creds, meta) {
|
|
2360
2470
|
return fetch(`${creds.apiUrl}/audit`, {
|
|
2361
2471
|
method: "POST",
|
|
@@ -2464,6 +2574,51 @@ async function resolveNode9SaaS(requestId, creds, approved, decidedBy) {
|
|
|
2464
2574
|
}
|
|
2465
2575
|
|
|
2466
2576
|
// src/auth/orchestrator.ts
|
|
2577
|
+
var WRITE_TOOLS = /* @__PURE__ */ new Set([
|
|
2578
|
+
"write",
|
|
2579
|
+
"write_file",
|
|
2580
|
+
"create_file",
|
|
2581
|
+
"edit",
|
|
2582
|
+
"multiedit",
|
|
2583
|
+
"str_replace_based_edit_tool",
|
|
2584
|
+
"replace",
|
|
2585
|
+
"notebook_edit",
|
|
2586
|
+
"notebookedit"
|
|
2587
|
+
]);
|
|
2588
|
+
function isWriteTool(toolName) {
|
|
2589
|
+
const t = toolName.toLowerCase().replace(/[^a-z_]/g, "_");
|
|
2590
|
+
return WRITE_TOOLS.has(t);
|
|
2591
|
+
}
|
|
2592
|
+
function extractFilePaths(toolName, args) {
|
|
2593
|
+
const paths = [];
|
|
2594
|
+
if (!args || typeof args !== "object" || Array.isArray(args)) return paths;
|
|
2595
|
+
const a = args;
|
|
2596
|
+
for (const key of ["file_path", "path", "filename", "source", "src", "input"]) {
|
|
2597
|
+
if (typeof a[key] === "string" && a[key]) paths.push(a[key]);
|
|
2598
|
+
}
|
|
2599
|
+
const cmd = typeof a.command === "string" ? a.command : typeof a.cmd === "string" ? a.cmd : "";
|
|
2600
|
+
if (cmd) {
|
|
2601
|
+
for (const m of cmd.matchAll(/(?:-T|--upload-file|--data(?:-binary)?)\s+@?(\S+)/g)) {
|
|
2602
|
+
paths.push(m[1]);
|
|
2603
|
+
}
|
|
2604
|
+
for (const m of cmd.matchAll(/\b(?:scp|rsync)\s+(?:-\S+\s+)*(\S+)\s+\S+@/g)) {
|
|
2605
|
+
paths.push(m[1]);
|
|
2606
|
+
}
|
|
2607
|
+
for (const m of cmd.matchAll(/<\s*(\S+)/g)) {
|
|
2608
|
+
paths.push(m[1]);
|
|
2609
|
+
}
|
|
2610
|
+
}
|
|
2611
|
+
return paths.filter(Boolean);
|
|
2612
|
+
}
|
|
2613
|
+
function isNetworkTool(toolName, args) {
|
|
2614
|
+
const t = toolName.toLowerCase();
|
|
2615
|
+
if (t === "bash" || t === "shell" || t === "run_shell_command" || t === "terminal.execute") {
|
|
2616
|
+
const a = args;
|
|
2617
|
+
const cmd = typeof a?.command === "string" ? a.command : typeof a?.cmd === "string" ? a.cmd : "";
|
|
2618
|
+
return /\b(curl|wget|scp|rsync|nc|ncat|netcat|ssh)\b/.test(cmd);
|
|
2619
|
+
}
|
|
2620
|
+
return false;
|
|
2621
|
+
}
|
|
2467
2622
|
var ACTIVITY_SOCKET_PATH = process.platform === "win32" ? "\\\\.\\pipe\\node9-activity" : import_path13.default.join(import_os9.default.tmpdir(), "node9-activity.sock");
|
|
2468
2623
|
function notifyActivity(data) {
|
|
2469
2624
|
return new Promise((resolve) => {
|
|
@@ -2482,7 +2637,7 @@ function notifyActivity(data) {
|
|
|
2482
2637
|
}
|
|
2483
2638
|
async function authorizeHeadless(toolName, args, meta, options) {
|
|
2484
2639
|
if (!options?.calledFromDaemon) {
|
|
2485
|
-
const actId = (0,
|
|
2640
|
+
const actId = (0, import_crypto2.randomUUID)();
|
|
2486
2641
|
const actTs = Date.now();
|
|
2487
2642
|
await notifyActivity({ id: actId, ts: actTs, tool: toolName, args, status: "pending" });
|
|
2488
2643
|
const result = await _authorizeHeadlessCore(toolName, args, meta, {
|
|
@@ -2494,7 +2649,7 @@ async function authorizeHeadless(toolName, args, meta, options) {
|
|
|
2494
2649
|
id: actId,
|
|
2495
2650
|
tool: toolName,
|
|
2496
2651
|
ts: actTs,
|
|
2497
|
-
status: result.approved ? "allow" : result.blockedByLabel?.includes("DLP") ? "dlp" : "block",
|
|
2652
|
+
status: result.approved ? "allow" : result.blockedByLabel?.includes("DLP") ? "dlp" : result.blockedByLabel?.includes("Taint") ? "taint" : "block",
|
|
2498
2653
|
label: result.blockedByLabel
|
|
2499
2654
|
});
|
|
2500
2655
|
}
|
|
@@ -2508,6 +2663,7 @@ async function _authorizeHeadlessCore(toolName, args, meta, options) {
|
|
|
2508
2663
|
if (pauseState.paused) return { approved: true, checkedBy: "paused" };
|
|
2509
2664
|
const creds = getCredentials();
|
|
2510
2665
|
const config = getConfig(options?.cwd);
|
|
2666
|
+
const hashAuditArgs = config.settings.auditHashArgs === true;
|
|
2511
2667
|
const isTestEnv2 = !!(process.env.VITEST || process.env.NODE_ENV === "test" || process.env.CI || process.env.NODE9_TESTING === "1");
|
|
2512
2668
|
const approvers = {
|
|
2513
2669
|
...config.settings.approvers || { native: true, browser: true, cloud: true, terminal: true }
|
|
@@ -2518,13 +2674,26 @@ async function _authorizeHeadlessCore(toolName, args, meta, options) {
|
|
|
2518
2674
|
approvers.terminal = false;
|
|
2519
2675
|
}
|
|
2520
2676
|
if (config.settings.enableHookLogDebug && !isTestEnv2) {
|
|
2521
|
-
appendHookDebug(toolName, args, meta);
|
|
2677
|
+
appendHookDebug(toolName, args, meta, hashAuditArgs);
|
|
2522
2678
|
}
|
|
2523
2679
|
const isManual = meta?.agent === "Terminal";
|
|
2524
2680
|
let explainableLabel = "Local Config";
|
|
2525
2681
|
let policyMatchedField;
|
|
2526
2682
|
let policyMatchedWord;
|
|
2527
2683
|
let riskMetadata;
|
|
2684
|
+
let taintWarning = null;
|
|
2685
|
+
if (isNetworkTool(toolName, args)) {
|
|
2686
|
+
const filePaths = extractFilePaths(toolName, args);
|
|
2687
|
+
if (filePaths.length > 0) {
|
|
2688
|
+
const taintResult = await checkTaint(filePaths);
|
|
2689
|
+
if (taintResult.tainted && taintResult.record) {
|
|
2690
|
+
const { path: taintedPath, source: taintSource } = taintResult.record;
|
|
2691
|
+
taintWarning = `\u26A0\uFE0F ${taintedPath} was flagged by ${taintSource} \u2014 this file may contain sensitive data`;
|
|
2692
|
+
} else if (taintResult.daemonUnavailable) {
|
|
2693
|
+
taintWarning = `\u26A0\uFE0F Taint service unavailable \u2014 cannot verify if ${filePaths.join(", ")} is clean`;
|
|
2694
|
+
}
|
|
2695
|
+
}
|
|
2696
|
+
}
|
|
2528
2697
|
if (config.policy.dlp.enabled && (!isIgnoredTool(toolName) || config.policy.dlp.scanIgnoredTools)) {
|
|
2529
2698
|
const argsObj = args && typeof args === "object" && !Array.isArray(args) ? args : {};
|
|
2530
2699
|
const filePath = String(argsObj.file_path ?? argsObj.path ?? argsObj.filename ?? "");
|
|
@@ -2532,7 +2701,10 @@ async function _authorizeHeadlessCore(toolName, args, meta, options) {
|
|
|
2532
2701
|
if (dlpMatch) {
|
|
2533
2702
|
const dlpReason = `\u{1F6A8} DATA LOSS PREVENTION: ${dlpMatch.patternName} detected in field "${dlpMatch.fieldPath}" (${dlpMatch.redactedSample})`;
|
|
2534
2703
|
if (dlpMatch.severity === "block") {
|
|
2535
|
-
if (!isManual) appendLocalAudit(toolName, args, "deny", "dlp-block", meta);
|
|
2704
|
+
if (!isManual) appendLocalAudit(toolName, args, "deny", "dlp-block", meta, true);
|
|
2705
|
+
if (isWriteTool(toolName) && filePath) {
|
|
2706
|
+
await notifyTaint(filePath, `DLP:${dlpMatch.patternName}`);
|
|
2707
|
+
}
|
|
2536
2708
|
return {
|
|
2537
2709
|
approved: false,
|
|
2538
2710
|
reason: dlpReason,
|
|
@@ -2540,7 +2712,8 @@ async function _authorizeHeadlessCore(toolName, args, meta, options) {
|
|
|
2540
2712
|
blockedByLabel: "\u{1F6A8} Node9 DLP (Secret Detected)"
|
|
2541
2713
|
};
|
|
2542
2714
|
}
|
|
2543
|
-
if (!isManual)
|
|
2715
|
+
if (!isManual)
|
|
2716
|
+
appendLocalAudit(toolName, args, "allow", "dlp-review-flagged", meta, hashAuditArgs);
|
|
2544
2717
|
explainableLabel = "\u{1F6A8} Node9 DLP (Credential Review)";
|
|
2545
2718
|
}
|
|
2546
2719
|
}
|
|
@@ -2548,7 +2721,7 @@ async function _authorizeHeadlessCore(toolName, args, meta, options) {
|
|
|
2548
2721
|
if (!isIgnoredTool(toolName)) {
|
|
2549
2722
|
const policyResult = await evaluatePolicy(toolName, args, meta?.agent, options?.cwd);
|
|
2550
2723
|
if (policyResult.decision === "review") {
|
|
2551
|
-
appendLocalAudit(toolName, args, "allow", "audit-mode", meta);
|
|
2724
|
+
appendLocalAudit(toolName, args, "allow", "audit-mode", meta, hashAuditArgs);
|
|
2552
2725
|
if (approvers.cloud && creds?.apiKey) {
|
|
2553
2726
|
await auditLocalAllow(toolName, args, "audit-mode", creds, meta);
|
|
2554
2727
|
}
|
|
@@ -2556,22 +2729,23 @@ async function _authorizeHeadlessCore(toolName, args, meta, options) {
|
|
|
2556
2729
|
}
|
|
2557
2730
|
return { approved: true, checkedBy: "audit" };
|
|
2558
2731
|
}
|
|
2559
|
-
if (!isIgnoredTool(toolName)) {
|
|
2732
|
+
if (!taintWarning && !isIgnoredTool(toolName)) {
|
|
2560
2733
|
if (getActiveTrustSession(toolName)) {
|
|
2561
2734
|
if (approvers.cloud && creds?.apiKey)
|
|
2562
2735
|
await auditLocalAllow(toolName, args, "trust", creds, meta);
|
|
2563
|
-
if (!isManual) appendLocalAudit(toolName, args, "allow", "trust", meta);
|
|
2736
|
+
if (!isManual) appendLocalAudit(toolName, args, "allow", "trust", meta, hashAuditArgs);
|
|
2564
2737
|
return { approved: true, checkedBy: "trust" };
|
|
2565
2738
|
}
|
|
2566
2739
|
const policyResult = await evaluatePolicy(toolName, args, meta?.agent);
|
|
2567
2740
|
if (policyResult.decision === "allow") {
|
|
2568
2741
|
if (approvers.cloud && creds?.apiKey)
|
|
2569
2742
|
auditLocalAllow(toolName, args, "local-policy", creds, meta);
|
|
2570
|
-
if (!isManual) appendLocalAudit(toolName, args, "allow", "local-policy", meta);
|
|
2743
|
+
if (!isManual) appendLocalAudit(toolName, args, "allow", "local-policy", meta, hashAuditArgs);
|
|
2571
2744
|
return { approved: true, checkedBy: "local-policy" };
|
|
2572
2745
|
}
|
|
2573
2746
|
if (policyResult.decision === "block") {
|
|
2574
|
-
if (!isManual)
|
|
2747
|
+
if (!isManual)
|
|
2748
|
+
appendLocalAudit(toolName, args, "deny", "smart-rule-block", meta, hashAuditArgs);
|
|
2575
2749
|
return {
|
|
2576
2750
|
approved: false,
|
|
2577
2751
|
reason: policyResult.reason ?? "Action explicitly blocked by Smart Policy.",
|
|
@@ -2590,15 +2764,16 @@ async function _authorizeHeadlessCore(toolName, args, meta, options) {
|
|
|
2590
2764
|
policyMatchedWord,
|
|
2591
2765
|
policyResult.ruleName
|
|
2592
2766
|
);
|
|
2593
|
-
const persistent = getPersistentDecision(toolName);
|
|
2767
|
+
const persistent = policyResult.ruleName ? null : getPersistentDecision(toolName);
|
|
2594
2768
|
if (persistent === "allow") {
|
|
2595
2769
|
if (approvers.cloud && creds?.apiKey)
|
|
2596
2770
|
await auditLocalAllow(toolName, args, "persistent", creds, meta);
|
|
2597
|
-
if (!isManual) appendLocalAudit(toolName, args, "allow", "persistent", meta);
|
|
2771
|
+
if (!isManual) appendLocalAudit(toolName, args, "allow", "persistent", meta, hashAuditArgs);
|
|
2598
2772
|
return { approved: true, checkedBy: "persistent" };
|
|
2599
2773
|
}
|
|
2600
2774
|
if (persistent === "deny") {
|
|
2601
|
-
if (!isManual)
|
|
2775
|
+
if (!isManual)
|
|
2776
|
+
appendLocalAudit(toolName, args, "deny", "persistent-deny", meta, hashAuditArgs);
|
|
2602
2777
|
return {
|
|
2603
2778
|
approved: false,
|
|
2604
2779
|
reason: `This tool ("${toolName}") is explicitly listed in your 'Always Deny' list.`,
|
|
@@ -2606,10 +2781,21 @@ async function _authorizeHeadlessCore(toolName, args, meta, options) {
|
|
|
2606
2781
|
blockedByLabel: "Persistent User Rule"
|
|
2607
2782
|
};
|
|
2608
2783
|
}
|
|
2609
|
-
} else {
|
|
2610
|
-
if (!isManual) appendLocalAudit(toolName, args, "allow", "ignored", meta);
|
|
2784
|
+
} else if (!taintWarning) {
|
|
2785
|
+
if (!isManual) appendLocalAudit(toolName, args, "allow", "ignored", meta, hashAuditArgs);
|
|
2611
2786
|
return { approved: true };
|
|
2612
2787
|
}
|
|
2788
|
+
if (taintWarning) {
|
|
2789
|
+
explainableLabel = "\u{1F534} Node9 Taint (Exfiltration Prevention)";
|
|
2790
|
+
riskMetadata = computeRiskMetadata(
|
|
2791
|
+
args,
|
|
2792
|
+
7,
|
|
2793
|
+
explainableLabel,
|
|
2794
|
+
void 0,
|
|
2795
|
+
void 0,
|
|
2796
|
+
taintWarning
|
|
2797
|
+
);
|
|
2798
|
+
}
|
|
2613
2799
|
let cloudRequestId = null;
|
|
2614
2800
|
const cloudEnforced = approvers.cloud && !!creds?.apiKey;
|
|
2615
2801
|
if (cloudEnforced) {
|
|
@@ -2628,7 +2814,7 @@ async function _authorizeHeadlessCore(toolName, args, meta, options) {
|
|
|
2628
2814
|
};
|
|
2629
2815
|
}
|
|
2630
2816
|
cloudRequestId = initResult.requestId || null;
|
|
2631
|
-
explainableLabel = "Organization Policy (SaaS)";
|
|
2817
|
+
if (!taintWarning) explainableLabel = "Organization Policy (SaaS)";
|
|
2632
2818
|
} catch {
|
|
2633
2819
|
}
|
|
2634
2820
|
}
|
|
@@ -2815,7 +3001,8 @@ REASON: Action blocked because no approval channels are available. (Native/Brows
|
|
|
2815
3001
|
args,
|
|
2816
3002
|
finalResult.approved ? "allow" : "deny",
|
|
2817
3003
|
finalResult.checkedBy || finalResult.blockedBy || "unknown",
|
|
2818
|
-
meta
|
|
3004
|
+
meta,
|
|
3005
|
+
hashAuditArgs
|
|
2819
3006
|
);
|
|
2820
3007
|
}
|
|
2821
3008
|
return finalResult;
|