@node9/proxy 1.65.1 → 1.67.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 +290 -51
- package/dist/cli.mjs +290 -51
- package/dist/dashboard.mjs +44 -21
- package/dist/index.js +167 -24
- package/dist/index.mjs +167 -24
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -94,7 +94,7 @@ function redactSecrets(text) {
|
|
|
94
94
|
if (!text) return text;
|
|
95
95
|
let redacted = text;
|
|
96
96
|
redacted = redacted.replace(
|
|
97
|
-
/(authorization:\s*(?:bearer|basic)\s+)[a-zA-Z0-9._
|
|
97
|
+
/(authorization:\s*(?:bearer|basic)\s+)[a-zA-Z0-9._\-\/=]+/gi,
|
|
98
98
|
"$1********"
|
|
99
99
|
);
|
|
100
100
|
redacted = redacted.replace(
|
|
@@ -155,7 +155,7 @@ function filePathFromArgs(args) {
|
|
|
155
155
|
return void 0;
|
|
156
156
|
}
|
|
157
157
|
function appendLocalAudit(toolName, args, decision, checkedBy, meta, auditHashArgsEnabled) {
|
|
158
|
-
const isDlpRow = checkedBy.toLowerCase().includes("dlp") || Boolean(meta?.dlpPattern);
|
|
158
|
+
const isDlpRow = checkedBy.toLowerCase().includes("dlp") || Boolean(meta?.dlpPattern) || /dlp|taint/i.test(String(meta?.ruleName ?? ""));
|
|
159
159
|
const preview2 = auditHashArgsEnabled && !isDlpRow ? buildArgsPreview(args) : void 0;
|
|
160
160
|
const argsField = auditHashArgsEnabled ? { argsHash: hashArgs(args), ...preview2 ? { argsPreview: preview2 } : {} } : { args: args ? JSON.parse(redactSecrets(JSON.stringify(args))) : {} };
|
|
161
161
|
const testRun = isTestCall(toolName, args) || process.env.NODE9_TESTING === "1" ? { testRun: true } : {};
|
|
@@ -332,8 +332,10 @@ var init_config_schema = __esm({
|
|
|
332
332
|
agentPolicy: import_zod.z.enum(["require_approval", "block_on_rules"]).optional(),
|
|
333
333
|
// Where a `review` verdict's prompt is rendered: 'ask' = the agent's own
|
|
334
334
|
// inline approve/deny prompt (Claude Code / GitHub Copilot); 'approver' =
|
|
335
|
-
// node9's own approver (terminal/native/cloud). Unset →
|
|
336
|
-
//
|
|
335
|
+
// node9's own approver (terminal/native/cloud). Unset → default ASK for
|
|
336
|
+
// ask-capable agents (v2: cloud no longer disables inline — the outcome
|
|
337
|
+
// ships to the dashboard as audit; admins force routing via managed
|
|
338
|
+
// reviewChannel, which outranks the local --ask flag).
|
|
337
339
|
reviewChannel: import_zod.z.enum(["ask", "approver"]).optional(),
|
|
338
340
|
// When true, agents may call WEAKENING node9 MCP tools (shield_disable,
|
|
339
341
|
// approver_set). Default (unset/false): those tools refuse over MCP — a human
|
|
@@ -371,7 +373,18 @@ var init_config_schema = __esm({
|
|
|
371
373
|
dlp: import_zod.z.object({
|
|
372
374
|
enabled: import_zod.z.boolean().optional(),
|
|
373
375
|
scanIgnoredTools: import_zod.z.boolean().optional(),
|
|
374
|
-
pii: import_zod.z.enum(["off", "block"]).optional()
|
|
376
|
+
pii: import_zod.z.enum(["off", "block"]).optional(),
|
|
377
|
+
reviewAction: import_zod.z.enum(["review", "block"]).optional()
|
|
378
|
+
}).optional(),
|
|
379
|
+
// Command-checks governance. Class-B keys (evalDynamic, pipeChainHigh)
|
|
380
|
+
// deliberately exclude 'off' — tighten-only.
|
|
381
|
+
commandChecks: import_zod.z.object({
|
|
382
|
+
inlineExec: import_zod.z.enum(["off", "review", "block"]).optional(),
|
|
383
|
+
rmAdvisory: import_zod.z.enum(["off", "review", "block"]).optional(),
|
|
384
|
+
chmod: import_zod.z.enum(["off", "review", "block"]).optional(),
|
|
385
|
+
sqlDdl: import_zod.z.enum(["off", "review", "block"]).optional(),
|
|
386
|
+
evalDynamic: import_zod.z.enum(["review", "block"]).optional(),
|
|
387
|
+
pipeChainHigh: import_zod.z.enum(["review", "block"]).optional()
|
|
375
388
|
}).optional(),
|
|
376
389
|
egress: import_zod.z.object({
|
|
377
390
|
enabled: import_zod.z.boolean().optional(),
|
|
@@ -1510,6 +1523,45 @@ function evaluateSmartConditions(args, rule) {
|
|
|
1510
1523
|
});
|
|
1511
1524
|
return mode === "any" ? results.some((r) => r) : results.every((r) => r);
|
|
1512
1525
|
}
|
|
1526
|
+
function resolveCheck(v) {
|
|
1527
|
+
return v === "off" || v === "block" ? v : "review";
|
|
1528
|
+
}
|
|
1529
|
+
function resolveCheckTight(v) {
|
|
1530
|
+
return v === "block" ? "block" : "review";
|
|
1531
|
+
}
|
|
1532
|
+
function detectInlineExec(command) {
|
|
1533
|
+
const pipeFed = command.includes("|");
|
|
1534
|
+
const segments = command.split(/\|\||&&|;|\||\n|\$\(|`|\(/);
|
|
1535
|
+
for (const rawSeg of segments) {
|
|
1536
|
+
const tokens = rawSeg.trim().split(/\s+/).filter(Boolean);
|
|
1537
|
+
let i = 0;
|
|
1538
|
+
while (i < tokens.length && /^[A-Za-z_]\w*=/.test(tokens[i])) i++;
|
|
1539
|
+
if (i >= tokens.length) continue;
|
|
1540
|
+
const base = tokens[i].split("/").pop() ?? tokens[i];
|
|
1541
|
+
if (!INLINE_INTERP.test(base)) continue;
|
|
1542
|
+
const args = tokens.slice(i + 1);
|
|
1543
|
+
let hadRedirect = false;
|
|
1544
|
+
const positionals = [];
|
|
1545
|
+
for (let j = 0; j < args.length; j++) {
|
|
1546
|
+
const a = args[j];
|
|
1547
|
+
if (a === "-") return true;
|
|
1548
|
+
if (a.startsWith("<")) {
|
|
1549
|
+
hadRedirect = true;
|
|
1550
|
+
if (a === "<" || a === "<<") j++;
|
|
1551
|
+
continue;
|
|
1552
|
+
}
|
|
1553
|
+
if (a.startsWith("-")) {
|
|
1554
|
+
if (/^-(c|e|eval)$/i.test(a)) return true;
|
|
1555
|
+
continue;
|
|
1556
|
+
}
|
|
1557
|
+
positionals.push(a);
|
|
1558
|
+
}
|
|
1559
|
+
if (positionals.length === 0 && (hadRedirect || pipeFed) && !INLINE_SHELL.test(base)) {
|
|
1560
|
+
return true;
|
|
1561
|
+
}
|
|
1562
|
+
}
|
|
1563
|
+
return false;
|
|
1564
|
+
}
|
|
1513
1565
|
function resolvePinned(matches) {
|
|
1514
1566
|
if (matches.length === 0) return void 0;
|
|
1515
1567
|
const pinned = matches.filter((r) => r.pinned);
|
|
@@ -1536,7 +1588,7 @@ function isSqlTool(toolName, toolInspection) {
|
|
|
1536
1588
|
const fieldName = toolInspection[matchingPattern];
|
|
1537
1589
|
return fieldName === "sql" || fieldName === "query";
|
|
1538
1590
|
}
|
|
1539
|
-
function pipeChainVerdict(command, isTrustedHost2) {
|
|
1591
|
+
function pipeChainVerdict(command, isTrustedHost2, highAction = "review") {
|
|
1540
1592
|
const pipeAnalysis = analyzePipeChain(command);
|
|
1541
1593
|
if (!pipeAnalysis.isPipeline) return null;
|
|
1542
1594
|
if (pipeAnalysis.risk !== "critical" && pipeAnalysis.risk !== "high") return null;
|
|
@@ -1567,7 +1619,7 @@ function pipeChainVerdict(command, isTrustedHost2) {
|
|
|
1567
1619
|
};
|
|
1568
1620
|
}
|
|
1569
1621
|
return {
|
|
1570
|
-
decision:
|
|
1622
|
+
decision: highAction,
|
|
1571
1623
|
blockedByLabel: "Node9: Pipe-Chain Exfiltration (high)",
|
|
1572
1624
|
reason: `Sensitive file piped to network sink: ${pipeAnalysis.sourceFiles.join(", ")} \u2192 ${sinks.join(", ")}`,
|
|
1573
1625
|
tier: 3
|
|
@@ -1581,7 +1633,10 @@ async function evaluatePolicy(config, toolName, args, context = {}, hooks = {})
|
|
|
1581
1633
|
const dlpMatch = args !== void 0 ? scanArgs(args) : null;
|
|
1582
1634
|
if (dlpMatch) {
|
|
1583
1635
|
return {
|
|
1584
|
-
|
|
1636
|
+
// reviewAction:'block' (inline-ask v2): the admin upgraded
|
|
1637
|
+
// review-severity matches to a hard block — every evaluatePolicy
|
|
1638
|
+
// caller (orchestrator, explain, gateway) must agree with the gate.
|
|
1639
|
+
decision: dlpMatch.severity === "block" || config.policy.dlp.reviewAction === "block" ? "block" : "review",
|
|
1585
1640
|
blockedByLabel: `DLP: ${dlpMatch.patternName}`,
|
|
1586
1641
|
reason: `${dlpMatch.patternName} detected in ${dlpMatch.fieldPath}`
|
|
1587
1642
|
};
|
|
@@ -1590,7 +1645,11 @@ async function evaluatePolicy(config, toolName, args, context = {}, hooks = {})
|
|
|
1590
1645
|
if (wouldBeIgnored) return { decision: "allow" };
|
|
1591
1646
|
const bashCommand = agent !== "Terminal" && isBashTool(toolName) && args && typeof args === "object" ? typeof args.command === "string" ? args.command : null : null;
|
|
1592
1647
|
if (bashCommand !== null) {
|
|
1593
|
-
const pipeVerdict = pipeChainVerdict(
|
|
1648
|
+
const pipeVerdict = pipeChainVerdict(
|
|
1649
|
+
bashCommand,
|
|
1650
|
+
isTrustedHost2,
|
|
1651
|
+
resolveCheckTight(config.policy.commandChecks?.pipeChainHigh)
|
|
1652
|
+
);
|
|
1594
1653
|
if (pipeVerdict) return pipeVerdict;
|
|
1595
1654
|
const fsVerdict = analyzeFsOperation(bashCommand);
|
|
1596
1655
|
if (fsVerdict) {
|
|
@@ -1605,10 +1664,12 @@ async function evaluatePolicy(config, toolName, args, context = {}, hooks = {})
|
|
|
1605
1664
|
ruleDescription: fsVerdict.reason
|
|
1606
1665
|
};
|
|
1607
1666
|
}
|
|
1608
|
-
const
|
|
1667
|
+
const sqlAction = resolveCheck(config.policy.commandChecks?.sqlDdl);
|
|
1668
|
+
const sqlVerdict = sqlAction === "off" ? null : analyzeSqlDestructive(bashCommand);
|
|
1609
1669
|
if (sqlVerdict) {
|
|
1610
1670
|
return {
|
|
1611
|
-
|
|
1671
|
+
// analyzeSqlDestructive is typed review-only, so the knob maps 1:1.
|
|
1672
|
+
decision: sqlAction === "block" ? "block" : "review",
|
|
1612
1673
|
blockedByLabel: `Node9 (AST): ${sqlVerdict.ruleName}`,
|
|
1613
1674
|
reason: sqlVerdict.reason,
|
|
1614
1675
|
tier: 2,
|
|
@@ -1616,10 +1677,12 @@ async function evaluatePolicy(config, toolName, args, context = {}, hooks = {})
|
|
|
1616
1677
|
ruleDescription: sqlVerdict.description
|
|
1617
1678
|
};
|
|
1618
1679
|
}
|
|
1619
|
-
const
|
|
1680
|
+
const chmodAction = resolveCheck(config.policy.commandChecks?.chmod);
|
|
1681
|
+
const chmodVerdict = chmodAction === "off" ? null : analyzeChmod777(bashCommand);
|
|
1620
1682
|
if (chmodVerdict) {
|
|
1621
1683
|
return {
|
|
1622
|
-
|
|
1684
|
+
// analyzeChmod777 is typed review-only, so the knob maps 1:1.
|
|
1685
|
+
decision: chmodAction === "block" ? "block" : "review",
|
|
1623
1686
|
blockedByLabel: `project-jail (AST): ${chmodVerdict.ruleName}`,
|
|
1624
1687
|
reason: chmodVerdict.reason,
|
|
1625
1688
|
tier: 2,
|
|
@@ -1662,10 +1725,10 @@ async function evaluatePolicy(config, toolName, args, context = {}, hooks = {})
|
|
|
1662
1725
|
const analyzed = analyzeShellCommand(shellCommand);
|
|
1663
1726
|
allTokens = analyzed.allTokens;
|
|
1664
1727
|
pathTokens = analyzed.paths;
|
|
1665
|
-
const
|
|
1666
|
-
if (
|
|
1728
|
+
const inlineAction = resolveCheck(config.policy.commandChecks?.inlineExec);
|
|
1729
|
+
if (inlineAction !== "off" && detectInlineExec(shellCommand)) {
|
|
1667
1730
|
return {
|
|
1668
|
-
decision: "review",
|
|
1731
|
+
decision: inlineAction === "block" ? "block" : "review",
|
|
1669
1732
|
blockedByLabel: "Node9 Standard (Inline Execution)",
|
|
1670
1733
|
ruleDescription: "The AI is running code directly from the command line. Review the full script below before allowing it to execute.",
|
|
1671
1734
|
tier: 3
|
|
@@ -1683,14 +1746,21 @@ async function evaluatePolicy(config, toolName, args, context = {}, hooks = {})
|
|
|
1683
1746
|
}
|
|
1684
1747
|
if (evalVerdict === "review") {
|
|
1685
1748
|
return {
|
|
1686
|
-
|
|
1749
|
+
// Class B tighten-only: commandChecks.evalDynamic may upgrade to
|
|
1750
|
+
// block but can never turn this off (eval-remote above is Class A —
|
|
1751
|
+
// no knob at all).
|
|
1752
|
+
decision: resolveCheckTight(config.policy.commandChecks?.evalDynamic),
|
|
1687
1753
|
blockedByLabel: "Node9: Eval Dynamic Content",
|
|
1688
1754
|
reason: "eval of dynamic content (variable or subshell expansion) requires approval",
|
|
1689
1755
|
ruleDescription: "The AI is running a command that includes a variable or subshell expansion. The actual command executed at runtime may differ from what is shown here.",
|
|
1690
1756
|
tier: 3
|
|
1691
1757
|
};
|
|
1692
1758
|
}
|
|
1693
|
-
const ptVerdict = pipeChainVerdict(
|
|
1759
|
+
const ptVerdict = pipeChainVerdict(
|
|
1760
|
+
shellCommand,
|
|
1761
|
+
isTrustedHost2,
|
|
1762
|
+
resolveCheckTight(config.policy.commandChecks?.pipeChainHigh)
|
|
1763
|
+
);
|
|
1694
1764
|
if (ptVerdict) return ptVerdict;
|
|
1695
1765
|
if (config.policy.egress?.enabled) {
|
|
1696
1766
|
const dests = extractShellDestinations(shellCommand);
|
|
@@ -2372,7 +2442,7 @@ function* stringValues(obj, depth = 0) {
|
|
|
2372
2442
|
}
|
|
2373
2443
|
for (const v of Object.values(obj)) yield* stringValues(v, depth + 1);
|
|
2374
2444
|
}
|
|
2375
|
-
var import_safe_regex2, import_mvdan_sh, import_picomatch, import_safe_regex22, import_safe_regex23, import_crypto3, MAX, UNTRUSTED_TOOLS, SIGNALS, ASSIGNMENT_CONTEXT_RE, DLP_STOPWORDS, DLP_PATTERNS, DLP_PATTERNS_GLOBAL, SENSITIVE_PATH_PATTERNS, MAX_DEPTH, MAX_STRING_BYTES, MAX_JSON_PARSE_BYTES, syntax, sharedParser, MESSAGE_FLAGS, SHELL_INTERPRETERS, DOWNLOAD_CMDS, NORMALIZE_CACHE_MAX, normalizeCache, AST_CACHE_MAX, astCache, PARSE_FAIL, FS_READ_TOOLS, FS_OP_PRESCREEN_RE, HOME_CACHE_ALLOWLIST, SENSITIVE_PATH_RULES, BASH_TOOL_NAMES, AST_FS_REGEX_RULES, SQL_DB_CLIS, SQL_DDL_RE, CHMOD_OPEN_PERM_TOKENS, COMMAND_WRAPPERS, NET_BINARIES, VALUE_FLAGS, FS_OP_CACHE_MAX, fsOpCache, stripDotSlash, REDIR_TRUNCATE_OPS, REDIR_HEREDOC_OPS, DEFAULT_EGRESS_ALLOWLIST, SOURCE_COMMANDS, SINK_COMMANDS, OBFUSCATORS, SENSITIVE_PATTERNS, FLAGS_WITH_VALUES, MAX_REGEX_LENGTH, REGEX_CACHE_MAX, regexCache, FORBIDDEN_PATH_SEGMENTS, VERDICT_RANK, SQL_DML_KEYWORDS, aws_default, bash_safe_default, docker_default, filesystem_default, github_default, k8s_default, mongodb_default, postgres_default, project_jail_default, redis_default, BUILTIN_SHIELDS, LOOP_MAX_RECORDS, FINDING_TO_SIGNAL, SCAN_SIGNAL_WEIGHTS, LOOP_THRESHOLD_FOR_WASTE, COST_PER_LOOP_ITER_USD, DESTRUCTIVE_OP_RE, SENSITIVE_PATH_RE, FILE_TOOLS, PII_EMAIL_RE, PII_SSN_RE, PII_PHONE_RE, PII_CC_RE, REALTIME_PII_PATTERNS, MAX_PII_SCAN_BYTES, LONG_OUTPUT_THRESHOLD_BYTES, CANONICAL_EXTRACTOR_VERSION, DEDUPE_PREVIEW_LEN, TERMINAL_ESCAPE_RE, ENGINE_VERSION;
|
|
2445
|
+
var import_safe_regex2, import_mvdan_sh, import_picomatch, import_safe_regex22, import_safe_regex23, import_crypto3, MAX, UNTRUSTED_TOOLS, SIGNALS, ASSIGNMENT_CONTEXT_RE, DLP_STOPWORDS, DLP_PATTERNS, DLP_PATTERNS_GLOBAL, SENSITIVE_PATH_PATTERNS, MAX_DEPTH, MAX_STRING_BYTES, MAX_JSON_PARSE_BYTES, syntax, sharedParser, MESSAGE_FLAGS, SHELL_INTERPRETERS, DOWNLOAD_CMDS, NORMALIZE_CACHE_MAX, normalizeCache, AST_CACHE_MAX, astCache, PARSE_FAIL, FS_READ_TOOLS, FS_OP_PRESCREEN_RE, HOME_CACHE_ALLOWLIST, SENSITIVE_PATH_RULES, BASH_TOOL_NAMES, AST_FS_REGEX_RULES, SQL_DB_CLIS, SQL_DDL_RE, CHMOD_OPEN_PERM_TOKENS, COMMAND_WRAPPERS, NET_BINARIES, VALUE_FLAGS, FS_OP_CACHE_MAX, fsOpCache, stripDotSlash, REDIR_TRUNCATE_OPS, REDIR_HEREDOC_OPS, DEFAULT_EGRESS_ALLOWLIST, SOURCE_COMMANDS, SINK_COMMANDS, OBFUSCATORS, SENSITIVE_PATTERNS, FLAGS_WITH_VALUES, MAX_REGEX_LENGTH, REGEX_CACHE_MAX, regexCache, FORBIDDEN_PATH_SEGMENTS, INLINE_INTERP, INLINE_SHELL, VERDICT_RANK, SQL_DML_KEYWORDS, aws_default, bash_safe_default, docker_default, filesystem_default, github_default, k8s_default, mongodb_default, postgres_default, project_jail_default, redis_default, BUILTIN_SHIELDS, LOOP_MAX_RECORDS, FINDING_TO_SIGNAL, SCAN_SIGNAL_WEIGHTS, LOOP_THRESHOLD_FOR_WASTE, COST_PER_LOOP_ITER_USD, DESTRUCTIVE_OP_RE, SENSITIVE_PATH_RE, FILE_TOOLS, PII_EMAIL_RE, PII_SSN_RE, PII_PHONE_RE, PII_CC_RE, REALTIME_PII_PATTERNS, MAX_PII_SCAN_BYTES, LONG_OUTPUT_THRESHOLD_BYTES, CANONICAL_EXTRACTOR_VERSION, DEDUPE_PREVIEW_LEN, TERMINAL_ESCAPE_RE, ENGINE_VERSION;
|
|
2376
2446
|
var init_dist = __esm({
|
|
2377
2447
|
"packages/policy-engine/dist/index.mjs"() {
|
|
2378
2448
|
"use strict";
|
|
@@ -3309,6 +3379,8 @@ var init_dist = __esm({
|
|
|
3309
3379
|
REGEX_CACHE_MAX = 500;
|
|
3310
3380
|
regexCache = /* @__PURE__ */ new Map();
|
|
3311
3381
|
FORBIDDEN_PATH_SEGMENTS = /* @__PURE__ */ new Set(["__proto__", "constructor", "prototype"]);
|
|
3382
|
+
INLINE_INTERP = /^(python[\d.]*|bash|sh|zsh|perl|ruby|node|php|lua)$/i;
|
|
3383
|
+
INLINE_SHELL = /^(bash|sh|zsh)$/i;
|
|
3312
3384
|
VERDICT_RANK = {
|
|
3313
3385
|
allow: 0,
|
|
3314
3386
|
review: 1,
|
|
@@ -4366,6 +4438,31 @@ function applyManagedDlp(local, managed, locked) {
|
|
|
4366
4438
|
locked.includes("dlpPii")
|
|
4367
4439
|
);
|
|
4368
4440
|
}
|
|
4441
|
+
if (typeof managed.reviewAction === "string") {
|
|
4442
|
+
next.reviewAction = resolveByOrder(
|
|
4443
|
+
DLP_REVIEW_ACTION_ORDER,
|
|
4444
|
+
local.reviewAction ?? "review",
|
|
4445
|
+
managed.reviewAction,
|
|
4446
|
+
locked.includes("dlpReviewAction")
|
|
4447
|
+
);
|
|
4448
|
+
}
|
|
4449
|
+
return next;
|
|
4450
|
+
}
|
|
4451
|
+
function applyManagedCommandChecks(local, managed, locked) {
|
|
4452
|
+
const next = { ...local };
|
|
4453
|
+
for (const key of COMMAND_CHECK_KEYS) {
|
|
4454
|
+
const m = managed[key];
|
|
4455
|
+
if (typeof m !== "string") continue;
|
|
4456
|
+
const lockKey = `commandChecks${key[0].toUpperCase()}${key.slice(1)}`;
|
|
4457
|
+
const resolved = resolveByOrder(
|
|
4458
|
+
COMMAND_CHECK_ORDER,
|
|
4459
|
+
local[key] ?? "review",
|
|
4460
|
+
m,
|
|
4461
|
+
locked.includes(lockKey)
|
|
4462
|
+
);
|
|
4463
|
+
if ((key === "evalDynamic" || key === "pipeChainHigh") && resolved === "off") continue;
|
|
4464
|
+
next[key] = resolved;
|
|
4465
|
+
}
|
|
4369
4466
|
return next;
|
|
4370
4467
|
}
|
|
4371
4468
|
function applyManagedApprovers(local, managed) {
|
|
@@ -4377,13 +4474,23 @@ function applyManagedApprovers(local, managed) {
|
|
|
4377
4474
|
terminal: typeof managed.terminal === "boolean" ? managed.terminal : local.terminal
|
|
4378
4475
|
};
|
|
4379
4476
|
}
|
|
4380
|
-
var MODE_ORDER, EGRESS_MODE_ORDER, DLP_PII_ORDER;
|
|
4477
|
+
var MODE_ORDER, EGRESS_MODE_ORDER, DLP_PII_ORDER, DLP_REVIEW_ACTION_ORDER, COMMAND_CHECK_ORDER, COMMAND_CHECK_KEYS;
|
|
4381
4478
|
var init_managed = __esm({
|
|
4382
4479
|
"src/config/managed.ts"() {
|
|
4383
4480
|
"use strict";
|
|
4384
4481
|
MODE_ORDER = ["observe", "audit", "standard", "strict"];
|
|
4385
4482
|
EGRESS_MODE_ORDER = ["off", "review", "block"];
|
|
4386
4483
|
DLP_PII_ORDER = ["off", "block"];
|
|
4484
|
+
DLP_REVIEW_ACTION_ORDER = ["review", "block"];
|
|
4485
|
+
COMMAND_CHECK_ORDER = ["off", "review", "block"];
|
|
4486
|
+
COMMAND_CHECK_KEYS = [
|
|
4487
|
+
"inlineExec",
|
|
4488
|
+
"rmAdvisory",
|
|
4489
|
+
"chmod",
|
|
4490
|
+
"sqlDdl",
|
|
4491
|
+
"evalDynamic",
|
|
4492
|
+
"pipeChainHigh"
|
|
4493
|
+
];
|
|
4387
4494
|
}
|
|
4388
4495
|
});
|
|
4389
4496
|
|
|
@@ -4758,6 +4865,22 @@ function getConfig(cwd) {
|
|
|
4758
4865
|
if (d.enabled !== void 0) mergedPolicy.dlp.enabled = d.enabled;
|
|
4759
4866
|
if (d.scanIgnoredTools !== void 0) mergedPolicy.dlp.scanIgnoredTools = d.scanIgnoredTools;
|
|
4760
4867
|
if (d.pii !== void 0) mergedPolicy.dlp.pii = d.pii;
|
|
4868
|
+
if (d.reviewAction !== void 0) mergedPolicy.dlp.reviewAction = d.reviewAction;
|
|
4869
|
+
}
|
|
4870
|
+
if (p.commandChecks && typeof p.commandChecks === "object") {
|
|
4871
|
+
const src = p.commandChecks;
|
|
4872
|
+
const cc2 = {
|
|
4873
|
+
...mergedPolicy.commandChecks
|
|
4874
|
+
};
|
|
4875
|
+
for (const k of ["inlineExec", "rmAdvisory", "chmod", "sqlDdl"]) {
|
|
4876
|
+
const v = src[k];
|
|
4877
|
+
if (v === "off" || v === "review" || v === "block") cc2[k] = v;
|
|
4878
|
+
}
|
|
4879
|
+
for (const k of ["evalDynamic", "pipeChainHigh"]) {
|
|
4880
|
+
const v = src[k];
|
|
4881
|
+
if (v === "review" || v === "block") cc2[k] = v;
|
|
4882
|
+
}
|
|
4883
|
+
if (Object.keys(cc2).length > 0) mergedPolicy.commandChecks = cc2;
|
|
4761
4884
|
}
|
|
4762
4885
|
if (p.egress) {
|
|
4763
4886
|
const e = p.egress;
|
|
@@ -4854,11 +4977,19 @@ function getConfig(cwd) {
|
|
|
4854
4977
|
mergedPolicy.dlp,
|
|
4855
4978
|
{
|
|
4856
4979
|
enabled: typeof mc.dlp.enabled === "boolean" ? mc.dlp.enabled : void 0,
|
|
4857
|
-
pii: typeof mc.dlp.pii === "string" ? mc.dlp.pii : void 0
|
|
4980
|
+
pii: typeof mc.dlp.pii === "string" ? mc.dlp.pii : void 0,
|
|
4981
|
+
reviewAction: mc.dlp.reviewAction === "review" || mc.dlp.reviewAction === "block" ? mc.dlp.reviewAction : void 0
|
|
4858
4982
|
},
|
|
4859
4983
|
locked
|
|
4860
4984
|
);
|
|
4861
4985
|
}
|
|
4986
|
+
if (mc.commandChecks && typeof mc.commandChecks === "object") {
|
|
4987
|
+
mergedPolicy.commandChecks = applyManagedCommandChecks(
|
|
4988
|
+
mergedPolicy.commandChecks ?? {},
|
|
4989
|
+
mc.commandChecks,
|
|
4990
|
+
locked
|
|
4991
|
+
);
|
|
4992
|
+
}
|
|
4862
4993
|
if (mc.approvers && typeof mc.approvers === "object") {
|
|
4863
4994
|
const bool = (v) => typeof v === "boolean" ? v : void 0;
|
|
4864
4995
|
mergedSettings.approvers = applyManagedApprovers(mergedSettings.approvers, {
|
|
@@ -4870,6 +5001,7 @@ function getConfig(cwd) {
|
|
|
4870
5001
|
}
|
|
4871
5002
|
if (mc.reviewChannel === "ask" || mc.reviewChannel === "approver") {
|
|
4872
5003
|
mergedSettings.reviewChannel = mc.reviewChannel;
|
|
5004
|
+
mergedSettings.reviewChannelManaged = true;
|
|
4873
5005
|
}
|
|
4874
5006
|
if (typeof mc.approvalTimeoutMs === "number" && mc.approvalTimeoutMs > 0) {
|
|
4875
5007
|
mergedSettings.approvalTimeoutMs = mc.approvalTimeoutMs;
|
|
@@ -4967,8 +5099,17 @@ function getConfig(cwd) {
|
|
|
4967
5099
|
}
|
|
4968
5100
|
}
|
|
4969
5101
|
const existingAdvisoryNames = new Set(mergedPolicy.smartRules.map((r) => r.name));
|
|
5102
|
+
const cc = mergedPolicy.commandChecks ?? {};
|
|
5103
|
+
const advisoryKnob = (name) => {
|
|
5104
|
+
if (name === "review-rm") return cc.rmAdvisory;
|
|
5105
|
+
if (name?.endsWith("-sql")) return cc.sqlDdl;
|
|
5106
|
+
return void 0;
|
|
5107
|
+
};
|
|
4970
5108
|
for (const rule of ADVISORY_SMART_RULES) {
|
|
4971
|
-
if (
|
|
5109
|
+
if (existingAdvisoryNames.has(rule.name)) continue;
|
|
5110
|
+
const knob = rule.verdict === "review" ? advisoryKnob(rule.name) : void 0;
|
|
5111
|
+
if (knob === "off") continue;
|
|
5112
|
+
mergedPolicy.smartRules.push(knob === "block" ? { ...rule, verdict: "block" } : rule);
|
|
4972
5113
|
}
|
|
4973
5114
|
const envMode = process.env.NODE9_MODE;
|
|
4974
5115
|
if (envMode && !modeCloudControlled && ["observe", "audit", "standard", "strict"].includes(envMode)) {
|
|
@@ -5614,13 +5755,14 @@ async function deriveExplainTrace(toolName, args) {
|
|
|
5614
5755
|
const filePathE = String(argsObjE.file_path ?? argsObjE.path ?? argsObjE.filename ?? "");
|
|
5615
5756
|
const dlpMatch = (filePathE ? scanFilePath(filePathE) : null) ?? (args !== void 0 ? scanArgs(args) : null);
|
|
5616
5757
|
if (dlpMatch) {
|
|
5758
|
+
const dlpBlocks = dlpMatch.severity === "block" || config.policy.dlp.reviewAction === "block";
|
|
5617
5759
|
steps.push({
|
|
5618
5760
|
name: "DLP Content Scanner",
|
|
5619
|
-
outcome:
|
|
5761
|
+
outcome: dlpBlocks ? "block" : "review",
|
|
5620
5762
|
detail: `\u{1F6A8} ${dlpMatch.patternName} detected in ${dlpMatch.fieldPath} \u2014 sample: ${dlpMatch.redactedSample}`,
|
|
5621
|
-
isFinal:
|
|
5763
|
+
isFinal: dlpBlocks
|
|
5622
5764
|
});
|
|
5623
|
-
if (
|
|
5765
|
+
if (dlpBlocks) {
|
|
5624
5766
|
return { tool: toolName, args, waterfall, steps, decision: "block" };
|
|
5625
5767
|
}
|
|
5626
5768
|
} else {
|
|
@@ -7119,7 +7261,7 @@ async function _authorizeHeadlessCore(toolName, args, metaArg, options) {
|
|
|
7119
7261
|
const dlpMatch = (filePath ? scanFilePath(filePath) : null) ?? scanArgs(args);
|
|
7120
7262
|
if (dlpMatch) {
|
|
7121
7263
|
const dlpReason = `\u{1F6A8} DATA LOSS PREVENTION: ${dlpMatch.patternName} detected in field "${dlpMatch.fieldPath}" (${dlpMatch.redactedSample})`;
|
|
7122
|
-
if (dlpMatch.severity === "block") {
|
|
7264
|
+
if (dlpMatch.severity === "block" || config.policy.dlp.reviewAction === "block") {
|
|
7123
7265
|
let dlpEid;
|
|
7124
7266
|
if (!isManual)
|
|
7125
7267
|
dlpEid = appendLocalAudit(
|
|
@@ -7457,12 +7599,14 @@ ${appPermReview}`
|
|
|
7457
7599
|
);
|
|
7458
7600
|
}
|
|
7459
7601
|
}
|
|
7460
|
-
|
|
7461
|
-
if (options?.deferReview && !hardBlockDowngraded && !taintWarning && !appPermReview && !cloudEnforcedForDefer) {
|
|
7602
|
+
if (options?.deferReview && !hardBlockDowngraded) {
|
|
7462
7603
|
return {
|
|
7463
7604
|
approved: false,
|
|
7464
7605
|
review: true,
|
|
7465
|
-
|
|
7606
|
+
// The prompt must say WHY: the taint sentence beats the bare label so
|
|
7607
|
+
// the dev sees the actual risk context inline. (No appPermReview term:
|
|
7608
|
+
// deferReview and serverKey never co-occur in production — see above.)
|
|
7609
|
+
reason: taintWarning || explainableLabel || "Node9 flagged this action for review.",
|
|
7466
7610
|
ruleDescription: policyRuleDescription,
|
|
7467
7611
|
blockedByLabel: explainableLabel
|
|
7468
7612
|
};
|
|
@@ -8404,7 +8548,7 @@ function printInlineAskNotice() {
|
|
|
8404
8548
|
);
|
|
8405
8549
|
console.log(
|
|
8406
8550
|
import_chalk.default.gray(
|
|
8407
|
-
' Prefer node9\u2019s own approver? Set "reviewChannel": "approver" in config, or add --no-ask to the hook.\n (Inline
|
|
8551
|
+
' Prefer node9\u2019s own approver? Set "reviewChannel": "approver" in config, or add --no-ask to the hook.\n (Inline is the default for every review; with a cloud workspace the outcome ships to the dashboard as audit.)'
|
|
8408
8552
|
)
|
|
8409
8553
|
);
|
|
8410
8554
|
}
|
|
@@ -18353,7 +18497,21 @@ function extractManagedConfig(body) {
|
|
|
18353
18497
|
const d = {};
|
|
18354
18498
|
if (typeof mc.dlp.enabled === "boolean") d.enabled = mc.dlp.enabled;
|
|
18355
18499
|
if (typeof mc.dlp.pii === "string") d.pii = mc.dlp.pii;
|
|
18356
|
-
if (
|
|
18500
|
+
if (mc.dlp.reviewAction === "review" || mc.dlp.reviewAction === "block")
|
|
18501
|
+
d.reviewAction = mc.dlp.reviewAction;
|
|
18502
|
+
if (d.enabled !== void 0 || d.pii !== void 0 || d.reviewAction !== void 0) out.dlp = d;
|
|
18503
|
+
}
|
|
18504
|
+
if (mc.commandChecks && typeof mc.commandChecks === "object") {
|
|
18505
|
+
const cc = {};
|
|
18506
|
+
for (const k of ["inlineExec", "rmAdvisory", "chmod", "sqlDdl"]) {
|
|
18507
|
+
const v = mc.commandChecks[k];
|
|
18508
|
+
if (v === "off" || v === "review" || v === "block") cc[k] = v;
|
|
18509
|
+
}
|
|
18510
|
+
for (const k of ["evalDynamic", "pipeChainHigh"]) {
|
|
18511
|
+
const v = mc.commandChecks[k];
|
|
18512
|
+
if (v === "review" || v === "block") cc[k] = v;
|
|
18513
|
+
}
|
|
18514
|
+
if (Object.keys(cc).length > 0) out.commandChecks = cc;
|
|
18357
18515
|
}
|
|
18358
18516
|
if (mc.approvers && typeof mc.approvers === "object") {
|
|
18359
18517
|
const a = {};
|
|
@@ -18417,7 +18575,7 @@ function extractManagedConfig(body) {
|
|
|
18417
18575
|
}
|
|
18418
18576
|
if (Object.keys(ap).length) out.appPermissions = ap;
|
|
18419
18577
|
}
|
|
18420
|
-
return out.mode !== void 0 || out.egress !== void 0 || out.dlp !== void 0 || out.approvers !== void 0 || out.reviewChannel !== void 0 || out.approvalTimeoutMs !== void 0 || out.injectionScan !== void 0 || out.loopDetection !== void 0 || out.skillPinning !== void 0 || out.jailPaths !== void 0 || out.trustedHosts !== void 0 || out.appPermissions !== void 0 ? out : void 0;
|
|
18578
|
+
return out.mode !== void 0 || out.egress !== void 0 || out.dlp !== void 0 || out.commandChecks !== void 0 || out.approvers !== void 0 || out.reviewChannel !== void 0 || out.approvalTimeoutMs !== void 0 || out.injectionScan !== void 0 || out.loopDetection !== void 0 || out.skillPinning !== void 0 || out.jailPaths !== void 0 || out.trustedHosts !== void 0 || out.appPermissions !== void 0 ? out : void 0;
|
|
18421
18579
|
}
|
|
18422
18580
|
function sweepStaleTmp(target) {
|
|
18423
18581
|
try {
|
|
@@ -19049,12 +19207,23 @@ function classifyDecision(a, b) {
|
|
|
19049
19207
|
function decisionTag(view) {
|
|
19050
19208
|
return `[${view.label}]`.padEnd(14);
|
|
19051
19209
|
}
|
|
19052
|
-
var HUMAN_SOURCES, TIMEOUT_SOURCES, has;
|
|
19210
|
+
var HUMAN_SOURCES, TIMEOUT_SOURCES, NON_DECISION_SOURCES, has;
|
|
19053
19211
|
var init_decision = __esm({
|
|
19054
19212
|
"src/audit/decision.ts"() {
|
|
19055
19213
|
"use strict";
|
|
19056
|
-
HUMAN_SOURCES = /* @__PURE__ */ new Set([
|
|
19214
|
+
HUMAN_SOURCES = /* @__PURE__ */ new Set([
|
|
19215
|
+
"daemon",
|
|
19216
|
+
"cloud",
|
|
19217
|
+
"local-decision",
|
|
19218
|
+
"inline-review-approved",
|
|
19219
|
+
"inline-review"
|
|
19220
|
+
]);
|
|
19057
19221
|
TIMEOUT_SOURCES = /* @__PURE__ */ new Set(["timeout"]);
|
|
19222
|
+
NON_DECISION_SOURCES = /* @__PURE__ */ new Set([
|
|
19223
|
+
"post-hook",
|
|
19224
|
+
"inline-review-approved",
|
|
19225
|
+
"response-dlp"
|
|
19226
|
+
]);
|
|
19058
19227
|
has = (s, needle) => s.includes(needle);
|
|
19059
19228
|
}
|
|
19060
19229
|
});
|
|
@@ -19630,7 +19799,7 @@ function buildDaemonReport(allEntries, period, now) {
|
|
|
19630
19799
|
else if (period === "30d") start.setDate(start.getDate() - 29);
|
|
19631
19800
|
else if (period === "month") start = new Date(now.getFullYear(), now.getMonth(), 1);
|
|
19632
19801
|
const entries = allEntries.filter((e) => {
|
|
19633
|
-
if (e.source === "
|
|
19802
|
+
if (typeof e.source === "string" && NON_DECISION_SOURCES.has(e.source)) return false;
|
|
19634
19803
|
return new Date(e.ts) >= start;
|
|
19635
19804
|
});
|
|
19636
19805
|
const isBlocked = (e) => classifyDecision(e).outcome === "deny";
|
|
@@ -47379,8 +47548,8 @@ INSTRUCTIONS:
|
|
|
47379
47548
|
- Do NOT retry this exact command or attempt to bypass the rule.${recovery}
|
|
47380
47549
|
- Inform the user which security rule was triggered and ask how to proceed.`;
|
|
47381
47550
|
}
|
|
47382
|
-
function buildReviewMessage(blockedByLabel, ruleDescription) {
|
|
47383
|
-
const why = ruleDescription || blockedByLabel || "this action needs your review";
|
|
47551
|
+
function buildReviewMessage(blockedByLabel, ruleDescription, reason) {
|
|
47552
|
+
const why = ruleDescription || (reason && reason !== blockedByLabel ? reason : void 0) || blockedByLabel || "this action needs your review";
|
|
47384
47553
|
return `Node9 flagged this for your review: ${why}. Approve to proceed, or deny to cancel.`;
|
|
47385
47554
|
}
|
|
47386
47555
|
|
|
@@ -48121,6 +48290,7 @@ init_hasher();
|
|
|
48121
48290
|
function storePath() {
|
|
48122
48291
|
return process.env.NODE9_PENDING_STORE || import_path45.default.join(import_os42.default.homedir(), ".node9", "pending-reviews.json");
|
|
48123
48292
|
}
|
|
48293
|
+
var TTL_TUID_MS = 72 * 60 * 60 * 1e3;
|
|
48124
48294
|
var TTL_MS2 = 6 * 60 * 60 * 1e3;
|
|
48125
48295
|
var MAX_ENTRIES = 500;
|
|
48126
48296
|
function reviewCorrelationKey(payload) {
|
|
@@ -48154,7 +48324,9 @@ function write(store) {
|
|
|
48154
48324
|
}
|
|
48155
48325
|
}
|
|
48156
48326
|
function prune(entries, now) {
|
|
48157
|
-
const fresh = entries.filter(
|
|
48327
|
+
const fresh = entries.filter(
|
|
48328
|
+
(e) => now - e.ts < (e.key.startsWith("tuid:") ? TTL_TUID_MS : TTL_MS2)
|
|
48329
|
+
);
|
|
48158
48330
|
return fresh.length > MAX_ENTRIES ? fresh.slice(fresh.length - MAX_ENTRIES) : fresh;
|
|
48159
48331
|
}
|
|
48160
48332
|
function recordPendingReview(entry) {
|
|
@@ -48169,19 +48341,30 @@ function recordPendingReview(entry) {
|
|
|
48169
48341
|
function resolvePendingReview(key, now = Date.now()) {
|
|
48170
48342
|
try {
|
|
48171
48343
|
const store = read();
|
|
48172
|
-
const
|
|
48344
|
+
const live = prune(store.entries, now);
|
|
48345
|
+
const idx = live.findIndex((e) => e.key === key);
|
|
48173
48346
|
if (idx === -1) {
|
|
48174
|
-
|
|
48175
|
-
if (pruned.length !== store.entries.length) write({ entries: pruned });
|
|
48347
|
+
if (live.length !== store.entries.length) write({ entries: live });
|
|
48176
48348
|
return null;
|
|
48177
48349
|
}
|
|
48178
|
-
const [match] =
|
|
48179
|
-
write({ entries:
|
|
48350
|
+
const [match] = live.splice(idx, 1);
|
|
48351
|
+
write({ entries: live });
|
|
48180
48352
|
return match;
|
|
48181
48353
|
} catch {
|
|
48182
48354
|
return null;
|
|
48183
48355
|
}
|
|
48184
48356
|
}
|
|
48357
|
+
function discardPendingReview(key, now = Date.now()) {
|
|
48358
|
+
try {
|
|
48359
|
+
const store = read();
|
|
48360
|
+
const kept = prune(
|
|
48361
|
+
store.entries.filter((e) => e.key !== key),
|
|
48362
|
+
now
|
|
48363
|
+
);
|
|
48364
|
+
if (kept.length !== store.entries.length) write({ entries: kept });
|
|
48365
|
+
} catch {
|
|
48366
|
+
}
|
|
48367
|
+
}
|
|
48185
48368
|
|
|
48186
48369
|
// src/cli/commands/check.ts
|
|
48187
48370
|
init_hook_payload();
|
|
@@ -48237,7 +48420,10 @@ function agentSupportsAsk(agent) {
|
|
|
48237
48420
|
}
|
|
48238
48421
|
function resolveAskMode(agent, opts, config) {
|
|
48239
48422
|
if (!agentSupportsAsk(agent)) return false;
|
|
48240
|
-
if (config.settings.
|
|
48423
|
+
if (config.settings.reviewChannelManaged === true) {
|
|
48424
|
+
if (config.settings.reviewChannel === "ask") return true;
|
|
48425
|
+
if (config.settings.reviewChannel === "approver") return false;
|
|
48426
|
+
}
|
|
48241
48427
|
if (opts.ask === true) return true;
|
|
48242
48428
|
if (opts.ask === false) return false;
|
|
48243
48429
|
if (config.settings.reviewChannel === "ask") return true;
|
|
@@ -48476,7 +48662,11 @@ RAW: ${raw}
|
|
|
48476
48662
|
process.exit(2);
|
|
48477
48663
|
};
|
|
48478
48664
|
const sendAsk = (result2) => {
|
|
48479
|
-
const msg = buildReviewMessage(
|
|
48665
|
+
const msg = buildReviewMessage(
|
|
48666
|
+
result2.blockedByLabel,
|
|
48667
|
+
result2.ruleDescription,
|
|
48668
|
+
result2.reason
|
|
48669
|
+
);
|
|
48480
48670
|
try {
|
|
48481
48671
|
const key = reviewCorrelationKey(payload);
|
|
48482
48672
|
if (key) {
|
|
@@ -48659,6 +48849,13 @@ RAW: ${raw}
|
|
|
48659
48849
|
cwd: safeCwdForAuth,
|
|
48660
48850
|
deferReview: askMode
|
|
48661
48851
|
});
|
|
48852
|
+
if (!result.review) {
|
|
48853
|
+
try {
|
|
48854
|
+
const key = reviewCorrelationKey(payload);
|
|
48855
|
+
if (key) discardPendingReview(key);
|
|
48856
|
+
} catch {
|
|
48857
|
+
}
|
|
48858
|
+
}
|
|
48662
48859
|
if (result.approved) {
|
|
48663
48860
|
if (result.checkedBy && process.env.NODE9_DEBUG === "1")
|
|
48664
48861
|
process.stderr.write(`\u2713 node9 [${result.checkedBy}]: "${toolName}" allowed
|
|
@@ -48746,6 +48943,7 @@ init_audit();
|
|
|
48746
48943
|
init_config();
|
|
48747
48944
|
init_daemon();
|
|
48748
48945
|
init_dlp();
|
|
48946
|
+
init_hasher();
|
|
48749
48947
|
|
|
48750
48948
|
// src/utils/cp-mv-parser.ts
|
|
48751
48949
|
function parseCpMvOp(command) {
|
|
@@ -48807,6 +49005,20 @@ function atLeastConfidence(c, min) {
|
|
|
48807
49005
|
function sanitize3(value) {
|
|
48808
49006
|
return value.replace(/[\x00-\x1F\x7F]/g, "");
|
|
48809
49007
|
}
|
|
49008
|
+
function buildArgsField(rawInput) {
|
|
49009
|
+
let hashed = {};
|
|
49010
|
+
try {
|
|
49011
|
+
hashed = { argsHash: hashArgs(rawInput) };
|
|
49012
|
+
} catch {
|
|
49013
|
+
}
|
|
49014
|
+
try {
|
|
49015
|
+
const hit = scanArgs(rawInput);
|
|
49016
|
+
if (hit) return { ...hashed, dlpPattern: hit.patternName, dlpSample: hit.redactedSample };
|
|
49017
|
+
return { args: JSON.parse(redactSecrets(JSON.stringify(rawInput))) };
|
|
49018
|
+
} catch {
|
|
49019
|
+
return hashed;
|
|
49020
|
+
}
|
|
49021
|
+
}
|
|
48810
49022
|
function registerLogCommand(program2) {
|
|
48811
49023
|
program2.command("log", { hidden: true }).description("PostToolUse hook \u2014 records executed tool calls").argument("[data]", "JSON string of the tool call").option(
|
|
48812
49024
|
"--agent <name>",
|
|
@@ -48835,15 +49047,17 @@ function registerLogCommand(program2) {
|
|
|
48835
49047
|
})();
|
|
48836
49048
|
const agent = agentOverride !== void 0 ? agentOverride : metaTag !== void 0 ? metaTag : payload.turn_id !== void 0 ? "Codex" : payload.toolCall !== void 0 || payload.conversationId !== void 0 ? "Antigravity" : payload.hook_event_name === "pre_tool_call" || payload.hook_event_name === "post_tool_call" ? "Hermes" : payload.hook_event_name === "PreToolUse" || payload.hook_event_name === "PostToolUse" || payload.tool_use_id !== void 0 || payload.permission_mode !== void 0 ? "Claude Code" : payload.hook_event_name === "BeforeTool" || payload.hook_event_name === "AfterTool" || payload.timestamp !== void 0 ? "Gemini CLI" : process.env.HERMES_SESSION_ID || process.env.HERMES_HOME || process.env.HERMES_INTERACTIVE ? "Hermes" : process.env.ANTIGRAVITY_CONVERSATION_ID ? "Antigravity" : void 0;
|
|
48837
49049
|
let reviewApproved = false;
|
|
49050
|
+
let resolvedReview = null;
|
|
48838
49051
|
try {
|
|
48839
49052
|
const key = reviewCorrelationKey(payload);
|
|
48840
|
-
if (key
|
|
49053
|
+
if (key) resolvedReview = resolvePendingReview(key);
|
|
49054
|
+
if (resolvedReview) reviewApproved = true;
|
|
48841
49055
|
} catch {
|
|
48842
49056
|
}
|
|
48843
49057
|
const entry = {
|
|
48844
49058
|
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
48845
49059
|
tool,
|
|
48846
|
-
|
|
49060
|
+
...buildArgsField(rawInput),
|
|
48847
49061
|
decision: "allowed",
|
|
48848
49062
|
source: reviewApproved ? "inline-review-approved" : "post-hook"
|
|
48849
49063
|
};
|
|
@@ -48890,6 +49104,33 @@ function registerLogCommand(program2) {
|
|
|
48890
49104
|
const payloadCwd = typeof payload.cwd === "string" ? payload.cwd : Array.isArray(payload.workspacePaths) && typeof payload.workspacePaths[0] === "string" ? payload.workspacePaths[0] : void 0;
|
|
48891
49105
|
const safeCwd = typeof payloadCwd === "string" && import_path47.default.isAbsolute(payloadCwd) ? payloadCwd : void 0;
|
|
48892
49106
|
const config = getConfig(safeCwd);
|
|
49107
|
+
if (resolvedReview) {
|
|
49108
|
+
try {
|
|
49109
|
+
const reviewLabel = resolvedReview.label || "inline-review";
|
|
49110
|
+
const sensitiveReview = /dlp|taint/i.test(reviewLabel);
|
|
49111
|
+
appendLocalAudit(
|
|
49112
|
+
tool,
|
|
49113
|
+
rawInput,
|
|
49114
|
+
"allow",
|
|
49115
|
+
"inline-review",
|
|
49116
|
+
{
|
|
49117
|
+
agent,
|
|
49118
|
+
ruleName: reviewLabel,
|
|
49119
|
+
...rawToolName !== tool ? { agentToolName: rawToolName } : {},
|
|
49120
|
+
...typeof payloadSessionId === "string" ? { sessionId: payloadSessionId } : {},
|
|
49121
|
+
...safeCwd ? { workingDir: safeCwd } : {}
|
|
49122
|
+
},
|
|
49123
|
+
sensitiveReview || config.settings.auditHashArgs === true
|
|
49124
|
+
);
|
|
49125
|
+
} catch (err2) {
|
|
49126
|
+
appendToLog(HOOK_DEBUG_LOG, {
|
|
49127
|
+
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
49128
|
+
event: "inline-review-ship-row-fail",
|
|
49129
|
+
tool,
|
|
49130
|
+
error: err2 instanceof Error ? err2.message : String(err2)
|
|
49131
|
+
});
|
|
49132
|
+
}
|
|
49133
|
+
}
|
|
48893
49134
|
{
|
|
48894
49135
|
const toolOutput = payload.tool_response?.output;
|
|
48895
49136
|
const inj = config.policy.injectionScan;
|
|
@@ -50358,8 +50599,7 @@ function aggregateReportFromAudit(period, opts = {}) {
|
|
|
50358
50599
|
const priorEnd = new Date(start.getTime() - 1);
|
|
50359
50600
|
const priorStart = new Date(start.getTime() - periodMs);
|
|
50360
50601
|
const priorEntries = allEntries.filter((e) => {
|
|
50361
|
-
if (e.source === "
|
|
50362
|
-
if (e.source === "response-dlp") return false;
|
|
50602
|
+
if (typeof e.source === "string" && NON_DECISION_SOURCES.has(e.source)) return false;
|
|
50363
50603
|
if (typeof e.decision !== "string") return false;
|
|
50364
50604
|
const ts = new Date(e.ts);
|
|
50365
50605
|
return ts >= priorStart && ts <= priorEnd;
|
|
@@ -50370,8 +50610,7 @@ function aggregateReportFromAudit(period, opts = {}) {
|
|
|
50370
50610
|
const testTs = excludeTests ? buildTestTimestamps(allEntries) : /* @__PURE__ */ new Set();
|
|
50371
50611
|
let excludedTests = 0;
|
|
50372
50612
|
const entries = allEntries.filter((e) => {
|
|
50373
|
-
if (e.source === "
|
|
50374
|
-
if (e.source === "response-dlp") return false;
|
|
50613
|
+
if (typeof e.source === "string" && NON_DECISION_SOURCES.has(e.source)) return false;
|
|
50375
50614
|
if (typeof e.decision !== "string") return false;
|
|
50376
50615
|
const ts = new Date(e.ts);
|
|
50377
50616
|
if (ts < start || ts > end) return false;
|