@node9/proxy 1.29.0 → 1.30.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.
- package/dist/cli.js +845 -650
- package/dist/cli.mjs +817 -623
- package/dist/dashboard.mjs +9 -2
- package/dist/index.js +64 -149
- package/dist/index.mjs +61 -146
- package/package.json +1 -1
package/dist/dashboard.mjs
CHANGED
|
@@ -2220,7 +2220,13 @@ var init_config_schema = __esm({
|
|
|
2220
2220
|
allowGlobalPause: z.boolean().optional(),
|
|
2221
2221
|
auditHashArgs: z.boolean().optional(),
|
|
2222
2222
|
agentPolicy: z.enum(["require_approval", "block_on_rules"]).optional(),
|
|
2223
|
-
cloudSyncIntervalHours: z.number().positive().optional()
|
|
2223
|
+
cloudSyncIntervalHours: z.number().positive().optional(),
|
|
2224
|
+
// Outbox shipper (audit.log → SaaS batch ingest). enabled defaults
|
|
2225
|
+
// to true; set false to fall back to local-only auditing.
|
|
2226
|
+
shipper: z.object({
|
|
2227
|
+
enabled: z.boolean().optional(),
|
|
2228
|
+
intervalSeconds: z.number().min(5).optional()
|
|
2229
|
+
}).optional()
|
|
2224
2230
|
}).optional(),
|
|
2225
2231
|
policy: z.object({
|
|
2226
2232
|
sandboxPaths: z.array(z.string()).optional(),
|
|
@@ -2355,7 +2361,8 @@ var init_config = __esm({
|
|
|
2355
2361
|
flightRecorder: true,
|
|
2356
2362
|
auditHashArgs: true,
|
|
2357
2363
|
approvers: { native: true, browser: false, cloud: false, terminal: true },
|
|
2358
|
-
cloudSyncIntervalHours: 5
|
|
2364
|
+
cloudSyncIntervalHours: 5,
|
|
2365
|
+
shipper: { enabled: true, intervalSeconds: 20 }
|
|
2359
2366
|
},
|
|
2360
2367
|
policy: {
|
|
2361
2368
|
sandboxPaths: ["/tmp/**", "**/sandbox/**", "**/test-results/**"],
|
package/dist/index.js
CHANGED
|
@@ -74,8 +74,13 @@ __export(audit_exports, {
|
|
|
74
74
|
appendHookDebug: () => appendHookDebug,
|
|
75
75
|
appendLocalAudit: () => appendLocalAudit,
|
|
76
76
|
appendToLog: () => appendToLog,
|
|
77
|
+
buildArgsPreview: () => buildArgsPreview,
|
|
78
|
+
generateEventId: () => generateEventId,
|
|
77
79
|
redactSecrets: () => redactSecrets
|
|
78
80
|
});
|
|
81
|
+
function generateEventId() {
|
|
82
|
+
return `${Date.now().toString(36)}-${import_crypto2.default.randomBytes(6).toString("hex")}`;
|
|
83
|
+
}
|
|
79
84
|
function isTestCall(toolName, args) {
|
|
80
85
|
if (toolName !== "Bash" && toolName !== "bash") return false;
|
|
81
86
|
const cmd = args?.command;
|
|
@@ -94,6 +99,17 @@ function redactSecrets(text) {
|
|
|
94
99
|
);
|
|
95
100
|
return redacted;
|
|
96
101
|
}
|
|
102
|
+
function buildArgsPreview(args) {
|
|
103
|
+
try {
|
|
104
|
+
const o = args && typeof args === "object" ? args : null;
|
|
105
|
+
const primary = o && (o.command ?? o.file_path ?? o.path ?? o.url ?? o.query);
|
|
106
|
+
const text = typeof primary === "string" ? primary : args ? JSON.stringify(args) : "";
|
|
107
|
+
if (!text) return void 0;
|
|
108
|
+
return redactSecrets(text).slice(0, 120);
|
|
109
|
+
} catch {
|
|
110
|
+
return void 0;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
97
113
|
function appendToLog(logPath, entry) {
|
|
98
114
|
try {
|
|
99
115
|
const dir = import_path.default.dirname(logPath);
|
|
@@ -116,11 +132,18 @@ function appendHookDebug(toolName, args, meta, auditHashArgsEnabled) {
|
|
|
116
132
|
});
|
|
117
133
|
}
|
|
118
134
|
function appendLocalAudit(toolName, args, decision, checkedBy, meta, auditHashArgsEnabled) {
|
|
119
|
-
const
|
|
135
|
+
const isDlpRow = checkedBy.toLowerCase().includes("dlp") || Boolean(meta?.dlpPattern);
|
|
136
|
+
const preview = auditHashArgsEnabled && !isDlpRow ? buildArgsPreview(args) : void 0;
|
|
137
|
+
const argsField = auditHashArgsEnabled ? { argsHash: hashArgs(args), ...preview ? { argsPreview: preview } : {} } : { args: args ? JSON.parse(redactSecrets(JSON.stringify(args))) : {} };
|
|
120
138
|
const testRun = isTestCall(toolName, args) || process.env.NODE9_TESTING === "1" ? { testRun: true } : {};
|
|
121
139
|
const ruleNameField = meta?.ruleName ? { ruleName: meta.ruleName } : {};
|
|
122
140
|
const agentToolNameField = meta?.agentToolName ? { agentToolName: meta.agentToolName } : {};
|
|
141
|
+
const dlpFields = meta?.dlpPattern ? { dlpPattern: meta.dlpPattern, dlpSample: meta.dlpSample } : {};
|
|
142
|
+
const cloudLinkField = meta?.cloudRequestId ? { cloudRequestId: meta.cloudRequestId } : {};
|
|
123
143
|
appendToLog(LOCAL_AUDIT_LOG, {
|
|
144
|
+
// eid first: the outbox shipper dedups on it, and a fixed leading field
|
|
145
|
+
// makes the JSONL easy to eyeball.
|
|
146
|
+
eid: generateEventId(),
|
|
124
147
|
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
125
148
|
tool: toolName,
|
|
126
149
|
...agentToolNameField,
|
|
@@ -128,6 +151,8 @@ function appendLocalAudit(toolName, args, decision, checkedBy, meta, auditHashAr
|
|
|
128
151
|
decision,
|
|
129
152
|
checkedBy,
|
|
130
153
|
...ruleNameField,
|
|
154
|
+
...dlpFields,
|
|
155
|
+
...cloudLinkField,
|
|
131
156
|
...testRun,
|
|
132
157
|
agent: meta?.agent,
|
|
133
158
|
mcpServer: meta?.mcpServer,
|
|
@@ -142,13 +167,14 @@ function appendConfigAudit(entry) {
|
|
|
142
167
|
hostname: import_os.default.hostname()
|
|
143
168
|
});
|
|
144
169
|
}
|
|
145
|
-
var import_fs, import_path, import_os, LOCAL_AUDIT_LOG, HOOK_DEBUG_LOG, TEST_COMMAND_RE;
|
|
170
|
+
var import_fs, import_path, import_os, import_crypto2, LOCAL_AUDIT_LOG, HOOK_DEBUG_LOG, TEST_COMMAND_RE;
|
|
146
171
|
var init_audit = __esm({
|
|
147
172
|
"src/audit/index.ts"() {
|
|
148
173
|
"use strict";
|
|
149
174
|
import_fs = __toESM(require("fs"));
|
|
150
175
|
import_path = __toESM(require("path"));
|
|
151
176
|
import_os = __toESM(require("os"));
|
|
177
|
+
import_crypto2 = __toESM(require("crypto"));
|
|
152
178
|
init_hasher();
|
|
153
179
|
LOCAL_AUDIT_LOG = import_path.default.join(import_os.default.homedir(), ".node9", "audit.log");
|
|
154
180
|
HOOK_DEBUG_LOG = import_path.default.join(import_os.default.homedir(), ".node9", "hook-debug.log");
|
|
@@ -246,7 +272,13 @@ var ConfigFileSchema = import_zod.z.object({
|
|
|
246
272
|
allowGlobalPause: import_zod.z.boolean().optional(),
|
|
247
273
|
auditHashArgs: import_zod.z.boolean().optional(),
|
|
248
274
|
agentPolicy: import_zod.z.enum(["require_approval", "block_on_rules"]).optional(),
|
|
249
|
-
cloudSyncIntervalHours: import_zod.z.number().positive().optional()
|
|
275
|
+
cloudSyncIntervalHours: import_zod.z.number().positive().optional(),
|
|
276
|
+
// Outbox shipper (audit.log → SaaS batch ingest). enabled defaults
|
|
277
|
+
// to true; set false to fall back to local-only auditing.
|
|
278
|
+
shipper: import_zod.z.object({
|
|
279
|
+
enabled: import_zod.z.boolean().optional(),
|
|
280
|
+
intervalSeconds: import_zod.z.number().min(5).optional()
|
|
281
|
+
}).optional()
|
|
250
282
|
}).optional(),
|
|
251
283
|
policy: import_zod.z.object({
|
|
252
284
|
sandboxPaths: import_zod.z.array(import_zod.z.string()).optional(),
|
|
@@ -314,7 +346,7 @@ var import_mvdan_sh = __toESM(require("mvdan-sh"), 1);
|
|
|
314
346
|
var import_picomatch = __toESM(require("picomatch"), 1);
|
|
315
347
|
var import_safe_regex22 = __toESM(require("safe-regex2"), 1);
|
|
316
348
|
var import_safe_regex23 = __toESM(require("safe-regex2"), 1);
|
|
317
|
-
var
|
|
349
|
+
var import_crypto3 = __toESM(require("crypto"), 1);
|
|
318
350
|
var ASSIGNMENT_CONTEXT_RE = /\b(?:password|passwd|secret|token|api[_-]?key|auth(?:_key|_token)?|credential|private[_-]?key|access[_-]?key|client[_-]?secret)\s*[=:]\s*/i;
|
|
319
351
|
function isAssignmentContext(text) {
|
|
320
352
|
return ASSIGNMENT_CONTEXT_RE.test(text);
|
|
@@ -2847,7 +2879,7 @@ assertBuiltinShieldRegexesAreSafe();
|
|
|
2847
2879
|
var LOOP_MAX_RECORDS = 500;
|
|
2848
2880
|
function computeArgsHash(args) {
|
|
2849
2881
|
const str = JSON.stringify(args ?? "");
|
|
2850
|
-
return
|
|
2882
|
+
return import_crypto3.default.createHash("sha256").update(str).digest("hex").slice(0, 16);
|
|
2851
2883
|
}
|
|
2852
2884
|
function evaluateLoopWindow(records, tool, args, threshold, windowMs, now) {
|
|
2853
2885
|
const hash = computeArgsHash(args);
|
|
@@ -2960,7 +2992,8 @@ var DEFAULT_CONFIG = {
|
|
|
2960
2992
|
flightRecorder: true,
|
|
2961
2993
|
auditHashArgs: true,
|
|
2962
2994
|
approvers: { native: true, browser: false, cloud: false, terminal: true },
|
|
2963
|
-
cloudSyncIntervalHours: 5
|
|
2995
|
+
cloudSyncIntervalHours: 5,
|
|
2996
|
+
shipper: { enabled: true, intervalSeconds: 20 }
|
|
2964
2997
|
},
|
|
2965
2998
|
policy: {
|
|
2966
2999
|
sandboxPaths: ["/tmp/**", "**/sandbox/**", "**/test-results/**"],
|
|
@@ -3255,7 +3288,8 @@ function getConfig(cwd) {
|
|
|
3255
3288
|
const projectConfig = tryLoadConfig(projectPath);
|
|
3256
3289
|
const mergedSettings = {
|
|
3257
3290
|
...DEFAULT_CONFIG.settings,
|
|
3258
|
-
approvers: { ...DEFAULT_CONFIG.settings.approvers }
|
|
3291
|
+
approvers: { ...DEFAULT_CONFIG.settings.approvers },
|
|
3292
|
+
shipper: { ...DEFAULT_CONFIG.settings.shipper }
|
|
3259
3293
|
};
|
|
3260
3294
|
const mergedPolicy = {
|
|
3261
3295
|
sandboxPaths: [...DEFAULT_CONFIG.policy.sandboxPaths],
|
|
@@ -3286,6 +3320,7 @@ function getConfig(cwd) {
|
|
|
3286
3320
|
if (s.enableHookLogDebug !== void 0)
|
|
3287
3321
|
mergedSettings.enableHookLogDebug = s.enableHookLogDebug;
|
|
3288
3322
|
if (s.approvers) mergedSettings.approvers = { ...mergedSettings.approvers, ...s.approvers };
|
|
3323
|
+
if (s.shipper) mergedSettings.shipper = { ...mergedSettings.shipper, ...s.shipper };
|
|
3289
3324
|
if (s.approvalTimeoutMs !== void 0) mergedSettings.approvalTimeoutMs = s.approvalTimeoutMs;
|
|
3290
3325
|
if (s.approvalTimeoutSeconds !== void 0 && s.approvalTimeoutMs === void 0)
|
|
3291
3326
|
mergedSettings.approvalTimeoutMs = s.approvalTimeoutSeconds * 1e3;
|
|
@@ -3948,7 +3983,7 @@ async function resolveViaDaemon(id, decision, internalToken, source) {
|
|
|
3948
3983
|
}
|
|
3949
3984
|
|
|
3950
3985
|
// src/auth/orchestrator.ts
|
|
3951
|
-
var
|
|
3986
|
+
var import_crypto4 = require("crypto");
|
|
3952
3987
|
|
|
3953
3988
|
// src/ui/native.ts
|
|
3954
3989
|
var import_child_process = require("child_process");
|
|
@@ -4281,91 +4316,6 @@ var import_fs9 = __toESM(require("fs"));
|
|
|
4281
4316
|
var import_os8 = __toESM(require("os"));
|
|
4282
4317
|
var import_path11 = __toESM(require("path"));
|
|
4283
4318
|
init_audit();
|
|
4284
|
-
var DLP_SAMPLE_MAX_LEN = 200;
|
|
4285
|
-
var DLP_PATTERN_MAX_LEN = 100;
|
|
4286
|
-
var KNOWN_CHECKED_BY = /* @__PURE__ */ new Set([
|
|
4287
|
-
"dlp-block",
|
|
4288
|
-
"observe-mode-dlp-would-block",
|
|
4289
|
-
"dlp-review-flagged",
|
|
4290
|
-
"loop-detected",
|
|
4291
|
-
"audit-mode",
|
|
4292
|
-
"local-policy",
|
|
4293
|
-
"smart-rule-block",
|
|
4294
|
-
// Smart-rule block was downgraded to review because the daemon was
|
|
4295
|
-
// running and we're not in CI. The block attempt is still recorded;
|
|
4296
|
-
// the user got a popup. Distinct from 'smart-rule-block' so the
|
|
4297
|
-
// dashboard can show "block rule overridden" separately from a hard
|
|
4298
|
-
// block that fired with no human in the loop.
|
|
4299
|
-
"smart-rule-block-override",
|
|
4300
|
-
"persistent",
|
|
4301
|
-
"trust",
|
|
4302
|
-
"observe-mode",
|
|
4303
|
-
"observe-mode-would-block"
|
|
4304
|
-
]);
|
|
4305
|
-
function validateApiUrl(raw) {
|
|
4306
|
-
let u;
|
|
4307
|
-
try {
|
|
4308
|
-
u = new URL(raw);
|
|
4309
|
-
} catch {
|
|
4310
|
-
return null;
|
|
4311
|
-
}
|
|
4312
|
-
if (u.username || u.password) return null;
|
|
4313
|
-
if (u.protocol === "https:") return u;
|
|
4314
|
-
if (u.protocol === "http:") {
|
|
4315
|
-
const h = u.hostname;
|
|
4316
|
-
if (h === "127.0.0.1" || h === "localhost" || h === "::1" || h === "[::1]") return u;
|
|
4317
|
-
}
|
|
4318
|
-
return null;
|
|
4319
|
-
}
|
|
4320
|
-
function auditLocalAllow(toolName, args, checkedBy, creds, meta, dlpInfo, containsSensitiveArgs = false, riskMetadata) {
|
|
4321
|
-
const validated = validateApiUrl(creds.apiUrl);
|
|
4322
|
-
if (!validated) {
|
|
4323
|
-
try {
|
|
4324
|
-
import_fs9.default.appendFileSync(
|
|
4325
|
-
HOOK_DEBUG_LOG,
|
|
4326
|
-
`[audit] refused to send: invalid apiUrl scheme/host (got "${String(creds.apiUrl).slice(0, 200)}")
|
|
4327
|
-
`
|
|
4328
|
-
);
|
|
4329
|
-
} catch {
|
|
4330
|
-
}
|
|
4331
|
-
return Promise.resolve();
|
|
4332
|
-
}
|
|
4333
|
-
const safeArgs = containsSensitiveArgs ? { tool: toolName, redacted: true } : args;
|
|
4334
|
-
const dlpSample = dlpInfo && typeof dlpInfo.redactedSample === "string" ? dlpInfo.redactedSample.slice(0, DLP_SAMPLE_MAX_LEN) : void 0;
|
|
4335
|
-
const dlpPattern = dlpInfo && typeof dlpInfo.pattern === "string" ? dlpInfo.pattern.slice(0, DLP_PATTERN_MAX_LEN) : void 0;
|
|
4336
|
-
const safeCheckedBy = KNOWN_CHECKED_BY.has(checkedBy) ? checkedBy : "unknown";
|
|
4337
|
-
const cleanedRiskMetadata = riskMetadata ? Object.fromEntries(
|
|
4338
|
-
Object.entries(riskMetadata).filter(([, v]) => typeof v === "string" && v.length > 0)
|
|
4339
|
-
) : void 0;
|
|
4340
|
-
const hasRiskMetadata = cleanedRiskMetadata && Object.keys(cleanedRiskMetadata).length > 0;
|
|
4341
|
-
return fetch(`${validated.toString().replace(/\/$/, "")}/audit`, {
|
|
4342
|
-
method: "POST",
|
|
4343
|
-
headers: { "Content-Type": "application/json", Authorization: `Bearer ${creds.apiKey}` },
|
|
4344
|
-
body: JSON.stringify({
|
|
4345
|
-
toolName,
|
|
4346
|
-
args: safeArgs,
|
|
4347
|
-
checkedBy: safeCheckedBy,
|
|
4348
|
-
...dlpInfo && { dlpPattern, dlpSample },
|
|
4349
|
-
...hasRiskMetadata && { riskMetadata: cleanedRiskMetadata },
|
|
4350
|
-
// session_id (Claude Code + Gemini CLI) groups all audit rows from one
|
|
4351
|
-
// agent run; transcript_path is the authoritative pointer to the
|
|
4352
|
-
// session log (survives Gemini resume drift). Both optional —
|
|
4353
|
-
// unsupported agents (MCP-mediated) leave them undefined.
|
|
4354
|
-
...meta?.sessionId && { runId: meta.sessionId },
|
|
4355
|
-
...meta?.transcriptPath && { transcriptPath: meta.transcriptPath },
|
|
4356
|
-
context: {
|
|
4357
|
-
agent: meta?.agent,
|
|
4358
|
-
mcpServer: meta?.mcpServer,
|
|
4359
|
-
hostname: import_os8.default.hostname(),
|
|
4360
|
-
cwd: process.cwd(),
|
|
4361
|
-
platform: import_os8.default.platform()
|
|
4362
|
-
}
|
|
4363
|
-
}),
|
|
4364
|
-
signal: AbortSignal.timeout(5e3)
|
|
4365
|
-
}).then(() => {
|
|
4366
|
-
}).catch(() => {
|
|
4367
|
-
});
|
|
4368
|
-
}
|
|
4369
4319
|
async function initNode9SaaS(toolName, args, creds, meta, riskMetadata, agentPolicy, forceReview) {
|
|
4370
4320
|
const controller = new AbortController();
|
|
4371
4321
|
const timeout = setTimeout(() => controller.abort(), 1e4);
|
|
@@ -4576,7 +4526,7 @@ function notifyActivity(data) {
|
|
|
4576
4526
|
}
|
|
4577
4527
|
async function authorizeHeadless(toolName, args, meta, options) {
|
|
4578
4528
|
if (!options?.calledFromDaemon) {
|
|
4579
|
-
const actId = (0,
|
|
4529
|
+
const actId = (0, import_crypto4.randomUUID)();
|
|
4580
4530
|
const actTs = Date.now();
|
|
4581
4531
|
const stripAnsi = (s) => s.replace(/\x1b(?:\[[0-9;?]*[a-zA-Z]|\][^\x07\x1b]*(?:\x07|\x1b\\)|[@-_])/g, "");
|
|
4582
4532
|
const sanitizedAgent = meta?.agent ? stripAnsi(meta.agent).slice(0, 80) : void 0;
|
|
@@ -4671,17 +4621,11 @@ async function _authorizeHeadlessCore(toolName, args, meta, options) {
|
|
|
4671
4621
|
args,
|
|
4672
4622
|
"deny",
|
|
4673
4623
|
isObserveMode ? "observe-mode-dlp-would-block" : "dlp-block",
|
|
4674
|
-
|
|
4675
|
-
|
|
4676
|
-
|
|
4677
|
-
|
|
4678
|
-
|
|
4679
|
-
toolName,
|
|
4680
|
-
args,
|
|
4681
|
-
isObserveMode ? "observe-mode-dlp-would-block" : "dlp-block",
|
|
4682
|
-
creds,
|
|
4683
|
-
meta,
|
|
4684
|
-
{ pattern: dlpMatch.patternName, redactedSample: dlpMatch.redactedSample },
|
|
4624
|
+
{
|
|
4625
|
+
...meta,
|
|
4626
|
+
dlpPattern: dlpMatch.patternName,
|
|
4627
|
+
dlpSample: dlpMatch.redactedSample
|
|
4628
|
+
},
|
|
4685
4629
|
true
|
|
4686
4630
|
);
|
|
4687
4631
|
if (isWriteTool(toolName) && filePath) {
|
|
@@ -4737,9 +4681,6 @@ async function _authorizeHeadlessCore(toolName, args, meta, options) {
|
|
|
4737
4681
|
const policyResult = await evaluatePolicy2(toolName, args, meta?.agent, options?.cwd);
|
|
4738
4682
|
if (policyResult.decision === "review") {
|
|
4739
4683
|
appendLocalAudit(toolName, args, "allow", "audit-mode", meta, hashAuditArgs);
|
|
4740
|
-
if (approvers.cloud && creds?.apiKey) {
|
|
4741
|
-
await auditLocalAllow(toolName, args, "audit-mode", creds, meta);
|
|
4742
|
-
}
|
|
4743
4684
|
}
|
|
4744
4685
|
}
|
|
4745
4686
|
return { approved: true, checkedBy: "audit" };
|
|
@@ -4752,8 +4693,6 @@ async function _authorizeHeadlessCore(toolName, args, meta, options) {
|
|
|
4752
4693
|
const reason = `It looks like you've called "${toolName}" ${loopResult.count} times with identical arguments in the last ${ld.windowSeconds}s. Are you stuck? Step back and reconsider your approach \u2014 what are you actually trying to accomplish, and is there a different way to get there?`;
|
|
4753
4694
|
if (!isManual)
|
|
4754
4695
|
appendLocalAudit(toolName, args, "deny", "loop-detected", meta, hashAuditArgs);
|
|
4755
|
-
if (approvers.cloud && creds?.apiKey)
|
|
4756
|
-
auditLocalAllow(toolName, args, "loop-detected", creds, meta, void 0, true);
|
|
4757
4696
|
return {
|
|
4758
4697
|
approved: false,
|
|
4759
4698
|
reason,
|
|
@@ -4772,15 +4711,15 @@ async function _authorizeHeadlessCore(toolName, args, meta, options) {
|
|
|
4772
4711
|
};
|
|
4773
4712
|
}
|
|
4774
4713
|
if (policyResult.decision === "allow") {
|
|
4775
|
-
if (
|
|
4776
|
-
|
|
4777
|
-
|
|
4778
|
-
|
|
4779
|
-
|
|
4780
|
-
|
|
4781
|
-
|
|
4782
|
-
|
|
4783
|
-
|
|
4714
|
+
if (!isManual)
|
|
4715
|
+
appendLocalAudit(
|
|
4716
|
+
toolName,
|
|
4717
|
+
args,
|
|
4718
|
+
"allow",
|
|
4719
|
+
"local-policy",
|
|
4720
|
+
{ ...meta, ruleName: policyResult.ruleName },
|
|
4721
|
+
hashAuditArgs
|
|
4722
|
+
);
|
|
4784
4723
|
return { approved: true, checkedBy: "local-policy" };
|
|
4785
4724
|
}
|
|
4786
4725
|
if (policyResult.decision === "block") {
|
|
@@ -4813,23 +4752,6 @@ async function _authorizeHeadlessCore(toolName, args, meta, options) {
|
|
|
4813
4752
|
{ ...meta, ruleName: policyResult.ruleName },
|
|
4814
4753
|
hashAuditArgs
|
|
4815
4754
|
);
|
|
4816
|
-
if (approvers.cloud && creds?.apiKey)
|
|
4817
|
-
auditLocalAllow(
|
|
4818
|
-
toolName,
|
|
4819
|
-
args,
|
|
4820
|
-
"smart-rule-block-override",
|
|
4821
|
-
creds,
|
|
4822
|
-
meta,
|
|
4823
|
-
void 0,
|
|
4824
|
-
false,
|
|
4825
|
-
{
|
|
4826
|
-
ruleName: policyResult.ruleName,
|
|
4827
|
-
ruleDescription: policyResult.ruleDescription,
|
|
4828
|
-
blockedByLabel: policyResult.blockedByLabel,
|
|
4829
|
-
matchedField: policyResult.matchedField,
|
|
4830
|
-
matchedWord: policyResult.matchedWord
|
|
4831
|
-
}
|
|
4832
|
-
);
|
|
4833
4755
|
const baseLabel = policyResult.blockedByLabel || "Smart Rule";
|
|
4834
4756
|
const OVERRIDE_PREFIX = "\u26A0\uFE0F Override block rule: ";
|
|
4835
4757
|
if (!baseLabel.startsWith(OVERRIDE_PREFIX)) {
|
|
@@ -4854,14 +4776,6 @@ async function _authorizeHeadlessCore(toolName, args, meta, options) {
|
|
|
4854
4776
|
{ ...meta, ruleName: policyResult.ruleName },
|
|
4855
4777
|
hashAuditArgs
|
|
4856
4778
|
);
|
|
4857
|
-
if (approvers.cloud && creds?.apiKey)
|
|
4858
|
-
auditLocalAllow(toolName, args, "smart-rule-block", creds, meta, void 0, false, {
|
|
4859
|
-
ruleName: policyResult.ruleName,
|
|
4860
|
-
ruleDescription: policyResult.ruleDescription,
|
|
4861
|
-
blockedByLabel: policyResult.blockedByLabel,
|
|
4862
|
-
matchedField: policyResult.matchedField,
|
|
4863
|
-
matchedWord: policyResult.matchedWord
|
|
4864
|
-
});
|
|
4865
4779
|
return {
|
|
4866
4780
|
approved: false,
|
|
4867
4781
|
reason: policyResult.reason ?? "Action explicitly blocked by Smart Policy.",
|
|
@@ -4890,8 +4804,6 @@ async function _authorizeHeadlessCore(toolName, args, meta, options) {
|
|
|
4890
4804
|
if (policyRuleDescription) riskMetadata.ruleDescription = policyRuleDescription.slice(0, 200);
|
|
4891
4805
|
const persistent = policyResult.ruleName ? null : getPersistentDecision(toolName);
|
|
4892
4806
|
if (persistent === "allow") {
|
|
4893
|
-
if (approvers.cloud && creds?.apiKey)
|
|
4894
|
-
await auditLocalAllow(toolName, args, "persistent", creds, meta);
|
|
4895
4807
|
if (!isManual) appendLocalAudit(toolName, args, "allow", "persistent", meta, hashAuditArgs);
|
|
4896
4808
|
return { approved: true, checkedBy: "persistent" };
|
|
4897
4809
|
}
|
|
@@ -4924,8 +4836,6 @@ async function _authorizeHeadlessCore(toolName, args, meta, options) {
|
|
|
4924
4836
|
}
|
|
4925
4837
|
}
|
|
4926
4838
|
if (!taintWarning && getActiveTrustSession(toolName, args)) {
|
|
4927
|
-
if (approvers.cloud && creds?.apiKey)
|
|
4928
|
-
await auditLocalAllow(toolName, args, "trust", creds, meta);
|
|
4929
4839
|
if (!isManual) appendLocalAudit(toolName, args, "allow", "trust", meta, hashAuditArgs);
|
|
4930
4840
|
return { approved: true, checkedBy: "trust" };
|
|
4931
4841
|
}
|
|
@@ -5170,7 +5080,12 @@ REASON: Action blocked because no approval channels are available. (Native/Brows
|
|
|
5170
5080
|
args,
|
|
5171
5081
|
finalResult.approved ? "allow" : "deny",
|
|
5172
5082
|
finalResult.checkedBy || finalResult.blockedBy || "unknown",
|
|
5173
|
-
|
|
5083
|
+
// cloudRequestId links this row to the BE-origin AuditLog row the
|
|
5084
|
+
// /intercept handshake created — the shipper hands it to the SaaS so
|
|
5085
|
+
// the BE enriches that row instead of inserting a duplicate. Matters
|
|
5086
|
+
// for EVERY racer outcome, not just cloud wins: a native-popup
|
|
5087
|
+
// decision on a cloud-pending request would otherwise count twice.
|
|
5088
|
+
cloudRequestId ? { ...meta, cloudRequestId } : meta,
|
|
5174
5089
|
hashAuditArgs
|
|
5175
5090
|
);
|
|
5176
5091
|
}
|