@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.mjs
CHANGED
|
@@ -102,7 +102,7 @@ function redactSecrets(text) {
|
|
|
102
102
|
if (!text) return text;
|
|
103
103
|
let redacted = text;
|
|
104
104
|
redacted = redacted.replace(
|
|
105
|
-
/(authorization:\s*(?:bearer|basic)\s+)[a-zA-Z0-9._
|
|
105
|
+
/(authorization:\s*(?:bearer|basic)\s+)[a-zA-Z0-9._\-\/=]+/gi,
|
|
106
106
|
"$1********"
|
|
107
107
|
);
|
|
108
108
|
redacted = redacted.replace(
|
|
@@ -163,7 +163,7 @@ function filePathFromArgs(args) {
|
|
|
163
163
|
return void 0;
|
|
164
164
|
}
|
|
165
165
|
function appendLocalAudit(toolName, args, decision, checkedBy, meta, auditHashArgsEnabled) {
|
|
166
|
-
const isDlpRow = checkedBy.toLowerCase().includes("dlp") || Boolean(meta?.dlpPattern);
|
|
166
|
+
const isDlpRow = checkedBy.toLowerCase().includes("dlp") || Boolean(meta?.dlpPattern) || /dlp|taint/i.test(String(meta?.ruleName ?? ""));
|
|
167
167
|
const preview2 = auditHashArgsEnabled && !isDlpRow ? buildArgsPreview(args) : void 0;
|
|
168
168
|
const argsField = auditHashArgsEnabled ? { argsHash: hashArgs(args), ...preview2 ? { argsPreview: preview2 } : {} } : { args: args ? JSON.parse(redactSecrets(JSON.stringify(args))) : {} };
|
|
169
169
|
const testRun = isTestCall(toolName, args) || process.env.NODE9_TESTING === "1" ? { testRun: true } : {};
|
|
@@ -336,8 +336,10 @@ var init_config_schema = __esm({
|
|
|
336
336
|
agentPolicy: z.enum(["require_approval", "block_on_rules"]).optional(),
|
|
337
337
|
// Where a `review` verdict's prompt is rendered: 'ask' = the agent's own
|
|
338
338
|
// inline approve/deny prompt (Claude Code / GitHub Copilot); 'approver' =
|
|
339
|
-
// node9's own approver (terminal/native/cloud). Unset →
|
|
340
|
-
//
|
|
339
|
+
// node9's own approver (terminal/native/cloud). Unset → default ASK for
|
|
340
|
+
// ask-capable agents (v2: cloud no longer disables inline — the outcome
|
|
341
|
+
// ships to the dashboard as audit; admins force routing via managed
|
|
342
|
+
// reviewChannel, which outranks the local --ask flag).
|
|
341
343
|
reviewChannel: z.enum(["ask", "approver"]).optional(),
|
|
342
344
|
// When true, agents may call WEAKENING node9 MCP tools (shield_disable,
|
|
343
345
|
// approver_set). Default (unset/false): those tools refuse over MCP — a human
|
|
@@ -375,7 +377,18 @@ var init_config_schema = __esm({
|
|
|
375
377
|
dlp: z.object({
|
|
376
378
|
enabled: z.boolean().optional(),
|
|
377
379
|
scanIgnoredTools: z.boolean().optional(),
|
|
378
|
-
pii: z.enum(["off", "block"]).optional()
|
|
380
|
+
pii: z.enum(["off", "block"]).optional(),
|
|
381
|
+
reviewAction: z.enum(["review", "block"]).optional()
|
|
382
|
+
}).optional(),
|
|
383
|
+
// Command-checks governance. Class-B keys (evalDynamic, pipeChainHigh)
|
|
384
|
+
// deliberately exclude 'off' — tighten-only.
|
|
385
|
+
commandChecks: z.object({
|
|
386
|
+
inlineExec: z.enum(["off", "review", "block"]).optional(),
|
|
387
|
+
rmAdvisory: z.enum(["off", "review", "block"]).optional(),
|
|
388
|
+
chmod: z.enum(["off", "review", "block"]).optional(),
|
|
389
|
+
sqlDdl: z.enum(["off", "review", "block"]).optional(),
|
|
390
|
+
evalDynamic: z.enum(["review", "block"]).optional(),
|
|
391
|
+
pipeChainHigh: z.enum(["review", "block"]).optional()
|
|
379
392
|
}).optional(),
|
|
380
393
|
egress: z.object({
|
|
381
394
|
enabled: z.boolean().optional(),
|
|
@@ -1520,6 +1533,45 @@ function evaluateSmartConditions(args, rule) {
|
|
|
1520
1533
|
});
|
|
1521
1534
|
return mode === "any" ? results.some((r) => r) : results.every((r) => r);
|
|
1522
1535
|
}
|
|
1536
|
+
function resolveCheck(v) {
|
|
1537
|
+
return v === "off" || v === "block" ? v : "review";
|
|
1538
|
+
}
|
|
1539
|
+
function resolveCheckTight(v) {
|
|
1540
|
+
return v === "block" ? "block" : "review";
|
|
1541
|
+
}
|
|
1542
|
+
function detectInlineExec(command) {
|
|
1543
|
+
const pipeFed = command.includes("|");
|
|
1544
|
+
const segments = command.split(/\|\||&&|;|\||\n|\$\(|`|\(/);
|
|
1545
|
+
for (const rawSeg of segments) {
|
|
1546
|
+
const tokens = rawSeg.trim().split(/\s+/).filter(Boolean);
|
|
1547
|
+
let i = 0;
|
|
1548
|
+
while (i < tokens.length && /^[A-Za-z_]\w*=/.test(tokens[i])) i++;
|
|
1549
|
+
if (i >= tokens.length) continue;
|
|
1550
|
+
const base = tokens[i].split("/").pop() ?? tokens[i];
|
|
1551
|
+
if (!INLINE_INTERP.test(base)) continue;
|
|
1552
|
+
const args = tokens.slice(i + 1);
|
|
1553
|
+
let hadRedirect = false;
|
|
1554
|
+
const positionals = [];
|
|
1555
|
+
for (let j = 0; j < args.length; j++) {
|
|
1556
|
+
const a = args[j];
|
|
1557
|
+
if (a === "-") return true;
|
|
1558
|
+
if (a.startsWith("<")) {
|
|
1559
|
+
hadRedirect = true;
|
|
1560
|
+
if (a === "<" || a === "<<") j++;
|
|
1561
|
+
continue;
|
|
1562
|
+
}
|
|
1563
|
+
if (a.startsWith("-")) {
|
|
1564
|
+
if (/^-(c|e|eval)$/i.test(a)) return true;
|
|
1565
|
+
continue;
|
|
1566
|
+
}
|
|
1567
|
+
positionals.push(a);
|
|
1568
|
+
}
|
|
1569
|
+
if (positionals.length === 0 && (hadRedirect || pipeFed) && !INLINE_SHELL.test(base)) {
|
|
1570
|
+
return true;
|
|
1571
|
+
}
|
|
1572
|
+
}
|
|
1573
|
+
return false;
|
|
1574
|
+
}
|
|
1523
1575
|
function resolvePinned(matches) {
|
|
1524
1576
|
if (matches.length === 0) return void 0;
|
|
1525
1577
|
const pinned = matches.filter((r) => r.pinned);
|
|
@@ -1546,7 +1598,7 @@ function isSqlTool(toolName, toolInspection) {
|
|
|
1546
1598
|
const fieldName = toolInspection[matchingPattern];
|
|
1547
1599
|
return fieldName === "sql" || fieldName === "query";
|
|
1548
1600
|
}
|
|
1549
|
-
function pipeChainVerdict(command, isTrustedHost2) {
|
|
1601
|
+
function pipeChainVerdict(command, isTrustedHost2, highAction = "review") {
|
|
1550
1602
|
const pipeAnalysis = analyzePipeChain(command);
|
|
1551
1603
|
if (!pipeAnalysis.isPipeline) return null;
|
|
1552
1604
|
if (pipeAnalysis.risk !== "critical" && pipeAnalysis.risk !== "high") return null;
|
|
@@ -1577,7 +1629,7 @@ function pipeChainVerdict(command, isTrustedHost2) {
|
|
|
1577
1629
|
};
|
|
1578
1630
|
}
|
|
1579
1631
|
return {
|
|
1580
|
-
decision:
|
|
1632
|
+
decision: highAction,
|
|
1581
1633
|
blockedByLabel: "Node9: Pipe-Chain Exfiltration (high)",
|
|
1582
1634
|
reason: `Sensitive file piped to network sink: ${pipeAnalysis.sourceFiles.join(", ")} \u2192 ${sinks.join(", ")}`,
|
|
1583
1635
|
tier: 3
|
|
@@ -1591,7 +1643,10 @@ async function evaluatePolicy(config, toolName, args, context = {}, hooks = {})
|
|
|
1591
1643
|
const dlpMatch = args !== void 0 ? scanArgs(args) : null;
|
|
1592
1644
|
if (dlpMatch) {
|
|
1593
1645
|
return {
|
|
1594
|
-
|
|
1646
|
+
// reviewAction:'block' (inline-ask v2): the admin upgraded
|
|
1647
|
+
// review-severity matches to a hard block — every evaluatePolicy
|
|
1648
|
+
// caller (orchestrator, explain, gateway) must agree with the gate.
|
|
1649
|
+
decision: dlpMatch.severity === "block" || config.policy.dlp.reviewAction === "block" ? "block" : "review",
|
|
1595
1650
|
blockedByLabel: `DLP: ${dlpMatch.patternName}`,
|
|
1596
1651
|
reason: `${dlpMatch.patternName} detected in ${dlpMatch.fieldPath}`
|
|
1597
1652
|
};
|
|
@@ -1600,7 +1655,11 @@ async function evaluatePolicy(config, toolName, args, context = {}, hooks = {})
|
|
|
1600
1655
|
if (wouldBeIgnored) return { decision: "allow" };
|
|
1601
1656
|
const bashCommand = agent !== "Terminal" && isBashTool(toolName) && args && typeof args === "object" ? typeof args.command === "string" ? args.command : null : null;
|
|
1602
1657
|
if (bashCommand !== null) {
|
|
1603
|
-
const pipeVerdict = pipeChainVerdict(
|
|
1658
|
+
const pipeVerdict = pipeChainVerdict(
|
|
1659
|
+
bashCommand,
|
|
1660
|
+
isTrustedHost2,
|
|
1661
|
+
resolveCheckTight(config.policy.commandChecks?.pipeChainHigh)
|
|
1662
|
+
);
|
|
1604
1663
|
if (pipeVerdict) return pipeVerdict;
|
|
1605
1664
|
const fsVerdict = analyzeFsOperation(bashCommand);
|
|
1606
1665
|
if (fsVerdict) {
|
|
@@ -1615,10 +1674,12 @@ async function evaluatePolicy(config, toolName, args, context = {}, hooks = {})
|
|
|
1615
1674
|
ruleDescription: fsVerdict.reason
|
|
1616
1675
|
};
|
|
1617
1676
|
}
|
|
1618
|
-
const
|
|
1677
|
+
const sqlAction = resolveCheck(config.policy.commandChecks?.sqlDdl);
|
|
1678
|
+
const sqlVerdict = sqlAction === "off" ? null : analyzeSqlDestructive(bashCommand);
|
|
1619
1679
|
if (sqlVerdict) {
|
|
1620
1680
|
return {
|
|
1621
|
-
|
|
1681
|
+
// analyzeSqlDestructive is typed review-only, so the knob maps 1:1.
|
|
1682
|
+
decision: sqlAction === "block" ? "block" : "review",
|
|
1622
1683
|
blockedByLabel: `Node9 (AST): ${sqlVerdict.ruleName}`,
|
|
1623
1684
|
reason: sqlVerdict.reason,
|
|
1624
1685
|
tier: 2,
|
|
@@ -1626,10 +1687,12 @@ async function evaluatePolicy(config, toolName, args, context = {}, hooks = {})
|
|
|
1626
1687
|
ruleDescription: sqlVerdict.description
|
|
1627
1688
|
};
|
|
1628
1689
|
}
|
|
1629
|
-
const
|
|
1690
|
+
const chmodAction = resolveCheck(config.policy.commandChecks?.chmod);
|
|
1691
|
+
const chmodVerdict = chmodAction === "off" ? null : analyzeChmod777(bashCommand);
|
|
1630
1692
|
if (chmodVerdict) {
|
|
1631
1693
|
return {
|
|
1632
|
-
|
|
1694
|
+
// analyzeChmod777 is typed review-only, so the knob maps 1:1.
|
|
1695
|
+
decision: chmodAction === "block" ? "block" : "review",
|
|
1633
1696
|
blockedByLabel: `project-jail (AST): ${chmodVerdict.ruleName}`,
|
|
1634
1697
|
reason: chmodVerdict.reason,
|
|
1635
1698
|
tier: 2,
|
|
@@ -1672,10 +1735,10 @@ async function evaluatePolicy(config, toolName, args, context = {}, hooks = {})
|
|
|
1672
1735
|
const analyzed = analyzeShellCommand(shellCommand);
|
|
1673
1736
|
allTokens = analyzed.allTokens;
|
|
1674
1737
|
pathTokens = analyzed.paths;
|
|
1675
|
-
const
|
|
1676
|
-
if (
|
|
1738
|
+
const inlineAction = resolveCheck(config.policy.commandChecks?.inlineExec);
|
|
1739
|
+
if (inlineAction !== "off" && detectInlineExec(shellCommand)) {
|
|
1677
1740
|
return {
|
|
1678
|
-
decision: "review",
|
|
1741
|
+
decision: inlineAction === "block" ? "block" : "review",
|
|
1679
1742
|
blockedByLabel: "Node9 Standard (Inline Execution)",
|
|
1680
1743
|
ruleDescription: "The AI is running code directly from the command line. Review the full script below before allowing it to execute.",
|
|
1681
1744
|
tier: 3
|
|
@@ -1693,14 +1756,21 @@ async function evaluatePolicy(config, toolName, args, context = {}, hooks = {})
|
|
|
1693
1756
|
}
|
|
1694
1757
|
if (evalVerdict === "review") {
|
|
1695
1758
|
return {
|
|
1696
|
-
|
|
1759
|
+
// Class B tighten-only: commandChecks.evalDynamic may upgrade to
|
|
1760
|
+
// block but can never turn this off (eval-remote above is Class A —
|
|
1761
|
+
// no knob at all).
|
|
1762
|
+
decision: resolveCheckTight(config.policy.commandChecks?.evalDynamic),
|
|
1697
1763
|
blockedByLabel: "Node9: Eval Dynamic Content",
|
|
1698
1764
|
reason: "eval of dynamic content (variable or subshell expansion) requires approval",
|
|
1699
1765
|
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.",
|
|
1700
1766
|
tier: 3
|
|
1701
1767
|
};
|
|
1702
1768
|
}
|
|
1703
|
-
const ptVerdict = pipeChainVerdict(
|
|
1769
|
+
const ptVerdict = pipeChainVerdict(
|
|
1770
|
+
shellCommand,
|
|
1771
|
+
isTrustedHost2,
|
|
1772
|
+
resolveCheckTight(config.policy.commandChecks?.pipeChainHigh)
|
|
1773
|
+
);
|
|
1704
1774
|
if (ptVerdict) return ptVerdict;
|
|
1705
1775
|
if (config.policy.egress?.enabled) {
|
|
1706
1776
|
const dests = extractShellDestinations(shellCommand);
|
|
@@ -2382,7 +2452,7 @@ function* stringValues(obj, depth = 0) {
|
|
|
2382
2452
|
}
|
|
2383
2453
|
for (const v of Object.values(obj)) yield* stringValues(v, depth + 1);
|
|
2384
2454
|
}
|
|
2385
|
-
var 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;
|
|
2455
|
+
var 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;
|
|
2386
2456
|
var init_dist = __esm({
|
|
2387
2457
|
"packages/policy-engine/dist/index.mjs"() {
|
|
2388
2458
|
"use strict";
|
|
@@ -3313,6 +3383,8 @@ var init_dist = __esm({
|
|
|
3313
3383
|
REGEX_CACHE_MAX = 500;
|
|
3314
3384
|
regexCache = /* @__PURE__ */ new Map();
|
|
3315
3385
|
FORBIDDEN_PATH_SEGMENTS = /* @__PURE__ */ new Set(["__proto__", "constructor", "prototype"]);
|
|
3386
|
+
INLINE_INTERP = /^(python[\d.]*|bash|sh|zsh|perl|ruby|node|php|lua)$/i;
|
|
3387
|
+
INLINE_SHELL = /^(bash|sh|zsh)$/i;
|
|
3316
3388
|
VERDICT_RANK = {
|
|
3317
3389
|
allow: 0,
|
|
3318
3390
|
review: 1,
|
|
@@ -4370,6 +4442,31 @@ function applyManagedDlp(local, managed, locked) {
|
|
|
4370
4442
|
locked.includes("dlpPii")
|
|
4371
4443
|
);
|
|
4372
4444
|
}
|
|
4445
|
+
if (typeof managed.reviewAction === "string") {
|
|
4446
|
+
next.reviewAction = resolveByOrder(
|
|
4447
|
+
DLP_REVIEW_ACTION_ORDER,
|
|
4448
|
+
local.reviewAction ?? "review",
|
|
4449
|
+
managed.reviewAction,
|
|
4450
|
+
locked.includes("dlpReviewAction")
|
|
4451
|
+
);
|
|
4452
|
+
}
|
|
4453
|
+
return next;
|
|
4454
|
+
}
|
|
4455
|
+
function applyManagedCommandChecks(local, managed, locked) {
|
|
4456
|
+
const next = { ...local };
|
|
4457
|
+
for (const key of COMMAND_CHECK_KEYS) {
|
|
4458
|
+
const m = managed[key];
|
|
4459
|
+
if (typeof m !== "string") continue;
|
|
4460
|
+
const lockKey = `commandChecks${key[0].toUpperCase()}${key.slice(1)}`;
|
|
4461
|
+
const resolved = resolveByOrder(
|
|
4462
|
+
COMMAND_CHECK_ORDER,
|
|
4463
|
+
local[key] ?? "review",
|
|
4464
|
+
m,
|
|
4465
|
+
locked.includes(lockKey)
|
|
4466
|
+
);
|
|
4467
|
+
if ((key === "evalDynamic" || key === "pipeChainHigh") && resolved === "off") continue;
|
|
4468
|
+
next[key] = resolved;
|
|
4469
|
+
}
|
|
4373
4470
|
return next;
|
|
4374
4471
|
}
|
|
4375
4472
|
function applyManagedApprovers(local, managed) {
|
|
@@ -4381,13 +4478,23 @@ function applyManagedApprovers(local, managed) {
|
|
|
4381
4478
|
terminal: typeof managed.terminal === "boolean" ? managed.terminal : local.terminal
|
|
4382
4479
|
};
|
|
4383
4480
|
}
|
|
4384
|
-
var MODE_ORDER, EGRESS_MODE_ORDER, DLP_PII_ORDER;
|
|
4481
|
+
var MODE_ORDER, EGRESS_MODE_ORDER, DLP_PII_ORDER, DLP_REVIEW_ACTION_ORDER, COMMAND_CHECK_ORDER, COMMAND_CHECK_KEYS;
|
|
4385
4482
|
var init_managed = __esm({
|
|
4386
4483
|
"src/config/managed.ts"() {
|
|
4387
4484
|
"use strict";
|
|
4388
4485
|
MODE_ORDER = ["observe", "audit", "standard", "strict"];
|
|
4389
4486
|
EGRESS_MODE_ORDER = ["off", "review", "block"];
|
|
4390
4487
|
DLP_PII_ORDER = ["off", "block"];
|
|
4488
|
+
DLP_REVIEW_ACTION_ORDER = ["review", "block"];
|
|
4489
|
+
COMMAND_CHECK_ORDER = ["off", "review", "block"];
|
|
4490
|
+
COMMAND_CHECK_KEYS = [
|
|
4491
|
+
"inlineExec",
|
|
4492
|
+
"rmAdvisory",
|
|
4493
|
+
"chmod",
|
|
4494
|
+
"sqlDdl",
|
|
4495
|
+
"evalDynamic",
|
|
4496
|
+
"pipeChainHigh"
|
|
4497
|
+
];
|
|
4391
4498
|
}
|
|
4392
4499
|
});
|
|
4393
4500
|
|
|
@@ -4765,6 +4872,22 @@ function getConfig(cwd) {
|
|
|
4765
4872
|
if (d.enabled !== void 0) mergedPolicy.dlp.enabled = d.enabled;
|
|
4766
4873
|
if (d.scanIgnoredTools !== void 0) mergedPolicy.dlp.scanIgnoredTools = d.scanIgnoredTools;
|
|
4767
4874
|
if (d.pii !== void 0) mergedPolicy.dlp.pii = d.pii;
|
|
4875
|
+
if (d.reviewAction !== void 0) mergedPolicy.dlp.reviewAction = d.reviewAction;
|
|
4876
|
+
}
|
|
4877
|
+
if (p.commandChecks && typeof p.commandChecks === "object") {
|
|
4878
|
+
const src = p.commandChecks;
|
|
4879
|
+
const cc2 = {
|
|
4880
|
+
...mergedPolicy.commandChecks
|
|
4881
|
+
};
|
|
4882
|
+
for (const k of ["inlineExec", "rmAdvisory", "chmod", "sqlDdl"]) {
|
|
4883
|
+
const v = src[k];
|
|
4884
|
+
if (v === "off" || v === "review" || v === "block") cc2[k] = v;
|
|
4885
|
+
}
|
|
4886
|
+
for (const k of ["evalDynamic", "pipeChainHigh"]) {
|
|
4887
|
+
const v = src[k];
|
|
4888
|
+
if (v === "review" || v === "block") cc2[k] = v;
|
|
4889
|
+
}
|
|
4890
|
+
if (Object.keys(cc2).length > 0) mergedPolicy.commandChecks = cc2;
|
|
4768
4891
|
}
|
|
4769
4892
|
if (p.egress) {
|
|
4770
4893
|
const e = p.egress;
|
|
@@ -4861,11 +4984,19 @@ function getConfig(cwd) {
|
|
|
4861
4984
|
mergedPolicy.dlp,
|
|
4862
4985
|
{
|
|
4863
4986
|
enabled: typeof mc.dlp.enabled === "boolean" ? mc.dlp.enabled : void 0,
|
|
4864
|
-
pii: typeof mc.dlp.pii === "string" ? mc.dlp.pii : void 0
|
|
4987
|
+
pii: typeof mc.dlp.pii === "string" ? mc.dlp.pii : void 0,
|
|
4988
|
+
reviewAction: mc.dlp.reviewAction === "review" || mc.dlp.reviewAction === "block" ? mc.dlp.reviewAction : void 0
|
|
4865
4989
|
},
|
|
4866
4990
|
locked
|
|
4867
4991
|
);
|
|
4868
4992
|
}
|
|
4993
|
+
if (mc.commandChecks && typeof mc.commandChecks === "object") {
|
|
4994
|
+
mergedPolicy.commandChecks = applyManagedCommandChecks(
|
|
4995
|
+
mergedPolicy.commandChecks ?? {},
|
|
4996
|
+
mc.commandChecks,
|
|
4997
|
+
locked
|
|
4998
|
+
);
|
|
4999
|
+
}
|
|
4869
5000
|
if (mc.approvers && typeof mc.approvers === "object") {
|
|
4870
5001
|
const bool = (v) => typeof v === "boolean" ? v : void 0;
|
|
4871
5002
|
mergedSettings.approvers = applyManagedApprovers(mergedSettings.approvers, {
|
|
@@ -4877,6 +5008,7 @@ function getConfig(cwd) {
|
|
|
4877
5008
|
}
|
|
4878
5009
|
if (mc.reviewChannel === "ask" || mc.reviewChannel === "approver") {
|
|
4879
5010
|
mergedSettings.reviewChannel = mc.reviewChannel;
|
|
5011
|
+
mergedSettings.reviewChannelManaged = true;
|
|
4880
5012
|
}
|
|
4881
5013
|
if (typeof mc.approvalTimeoutMs === "number" && mc.approvalTimeoutMs > 0) {
|
|
4882
5014
|
mergedSettings.approvalTimeoutMs = mc.approvalTimeoutMs;
|
|
@@ -4974,8 +5106,17 @@ function getConfig(cwd) {
|
|
|
4974
5106
|
}
|
|
4975
5107
|
}
|
|
4976
5108
|
const existingAdvisoryNames = new Set(mergedPolicy.smartRules.map((r) => r.name));
|
|
5109
|
+
const cc = mergedPolicy.commandChecks ?? {};
|
|
5110
|
+
const advisoryKnob = (name) => {
|
|
5111
|
+
if (name === "review-rm") return cc.rmAdvisory;
|
|
5112
|
+
if (name?.endsWith("-sql")) return cc.sqlDdl;
|
|
5113
|
+
return void 0;
|
|
5114
|
+
};
|
|
4977
5115
|
for (const rule of ADVISORY_SMART_RULES) {
|
|
4978
|
-
if (
|
|
5116
|
+
if (existingAdvisoryNames.has(rule.name)) continue;
|
|
5117
|
+
const knob = rule.verdict === "review" ? advisoryKnob(rule.name) : void 0;
|
|
5118
|
+
if (knob === "off") continue;
|
|
5119
|
+
mergedPolicy.smartRules.push(knob === "block" ? { ...rule, verdict: "block" } : rule);
|
|
4979
5120
|
}
|
|
4980
5121
|
const envMode = process.env.NODE9_MODE;
|
|
4981
5122
|
if (envMode && !modeCloudControlled && ["observe", "audit", "standard", "strict"].includes(envMode)) {
|
|
@@ -5621,13 +5762,14 @@ async function deriveExplainTrace(toolName, args) {
|
|
|
5621
5762
|
const filePathE = String(argsObjE.file_path ?? argsObjE.path ?? argsObjE.filename ?? "");
|
|
5622
5763
|
const dlpMatch = (filePathE ? scanFilePath(filePathE) : null) ?? (args !== void 0 ? scanArgs(args) : null);
|
|
5623
5764
|
if (dlpMatch) {
|
|
5765
|
+
const dlpBlocks = dlpMatch.severity === "block" || config.policy.dlp.reviewAction === "block";
|
|
5624
5766
|
steps.push({
|
|
5625
5767
|
name: "DLP Content Scanner",
|
|
5626
|
-
outcome:
|
|
5768
|
+
outcome: dlpBlocks ? "block" : "review",
|
|
5627
5769
|
detail: `\u{1F6A8} ${dlpMatch.patternName} detected in ${dlpMatch.fieldPath} \u2014 sample: ${dlpMatch.redactedSample}`,
|
|
5628
|
-
isFinal:
|
|
5770
|
+
isFinal: dlpBlocks
|
|
5629
5771
|
});
|
|
5630
|
-
if (
|
|
5772
|
+
if (dlpBlocks) {
|
|
5631
5773
|
return { tool: toolName, args, waterfall, steps, decision: "block" };
|
|
5632
5774
|
}
|
|
5633
5775
|
} else {
|
|
@@ -7122,7 +7264,7 @@ async function _authorizeHeadlessCore(toolName, args, metaArg, options) {
|
|
|
7122
7264
|
const dlpMatch = (filePath ? scanFilePath(filePath) : null) ?? scanArgs(args);
|
|
7123
7265
|
if (dlpMatch) {
|
|
7124
7266
|
const dlpReason = `\u{1F6A8} DATA LOSS PREVENTION: ${dlpMatch.patternName} detected in field "${dlpMatch.fieldPath}" (${dlpMatch.redactedSample})`;
|
|
7125
|
-
if (dlpMatch.severity === "block") {
|
|
7267
|
+
if (dlpMatch.severity === "block" || config.policy.dlp.reviewAction === "block") {
|
|
7126
7268
|
let dlpEid;
|
|
7127
7269
|
if (!isManual)
|
|
7128
7270
|
dlpEid = appendLocalAudit(
|
|
@@ -7460,12 +7602,14 @@ ${appPermReview}`
|
|
|
7460
7602
|
);
|
|
7461
7603
|
}
|
|
7462
7604
|
}
|
|
7463
|
-
|
|
7464
|
-
if (options?.deferReview && !hardBlockDowngraded && !taintWarning && !appPermReview && !cloudEnforcedForDefer) {
|
|
7605
|
+
if (options?.deferReview && !hardBlockDowngraded) {
|
|
7465
7606
|
return {
|
|
7466
7607
|
approved: false,
|
|
7467
7608
|
review: true,
|
|
7468
|
-
|
|
7609
|
+
// The prompt must say WHY: the taint sentence beats the bare label so
|
|
7610
|
+
// the dev sees the actual risk context inline. (No appPermReview term:
|
|
7611
|
+
// deferReview and serverKey never co-occur in production — see above.)
|
|
7612
|
+
reason: taintWarning || explainableLabel || "Node9 flagged this action for review.",
|
|
7469
7613
|
ruleDescription: policyRuleDescription,
|
|
7470
7614
|
blockedByLabel: explainableLabel
|
|
7471
7615
|
};
|
|
@@ -8412,7 +8556,7 @@ function printInlineAskNotice() {
|
|
|
8412
8556
|
);
|
|
8413
8557
|
console.log(
|
|
8414
8558
|
chalk.gray(
|
|
8415
|
-
' Prefer node9\u2019s own approver? Set "reviewChannel": "approver" in config, or add --no-ask to the hook.\n (Inline
|
|
8559
|
+
' 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.)'
|
|
8416
8560
|
)
|
|
8417
8561
|
);
|
|
8418
8562
|
}
|
|
@@ -18351,7 +18495,21 @@ function extractManagedConfig(body) {
|
|
|
18351
18495
|
const d = {};
|
|
18352
18496
|
if (typeof mc.dlp.enabled === "boolean") d.enabled = mc.dlp.enabled;
|
|
18353
18497
|
if (typeof mc.dlp.pii === "string") d.pii = mc.dlp.pii;
|
|
18354
|
-
if (
|
|
18498
|
+
if (mc.dlp.reviewAction === "review" || mc.dlp.reviewAction === "block")
|
|
18499
|
+
d.reviewAction = mc.dlp.reviewAction;
|
|
18500
|
+
if (d.enabled !== void 0 || d.pii !== void 0 || d.reviewAction !== void 0) out.dlp = d;
|
|
18501
|
+
}
|
|
18502
|
+
if (mc.commandChecks && typeof mc.commandChecks === "object") {
|
|
18503
|
+
const cc = {};
|
|
18504
|
+
for (const k of ["inlineExec", "rmAdvisory", "chmod", "sqlDdl"]) {
|
|
18505
|
+
const v = mc.commandChecks[k];
|
|
18506
|
+
if (v === "off" || v === "review" || v === "block") cc[k] = v;
|
|
18507
|
+
}
|
|
18508
|
+
for (const k of ["evalDynamic", "pipeChainHigh"]) {
|
|
18509
|
+
const v = mc.commandChecks[k];
|
|
18510
|
+
if (v === "review" || v === "block") cc[k] = v;
|
|
18511
|
+
}
|
|
18512
|
+
if (Object.keys(cc).length > 0) out.commandChecks = cc;
|
|
18355
18513
|
}
|
|
18356
18514
|
if (mc.approvers && typeof mc.approvers === "object") {
|
|
18357
18515
|
const a = {};
|
|
@@ -18415,7 +18573,7 @@ function extractManagedConfig(body) {
|
|
|
18415
18573
|
}
|
|
18416
18574
|
if (Object.keys(ap).length) out.appPermissions = ap;
|
|
18417
18575
|
}
|
|
18418
|
-
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;
|
|
18576
|
+
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;
|
|
18419
18577
|
}
|
|
18420
18578
|
function sweepStaleTmp(target) {
|
|
18421
18579
|
try {
|
|
@@ -19043,12 +19201,23 @@ function classifyDecision(a, b) {
|
|
|
19043
19201
|
function decisionTag(view) {
|
|
19044
19202
|
return `[${view.label}]`.padEnd(14);
|
|
19045
19203
|
}
|
|
19046
|
-
var HUMAN_SOURCES, TIMEOUT_SOURCES, has;
|
|
19204
|
+
var HUMAN_SOURCES, TIMEOUT_SOURCES, NON_DECISION_SOURCES, has;
|
|
19047
19205
|
var init_decision = __esm({
|
|
19048
19206
|
"src/audit/decision.ts"() {
|
|
19049
19207
|
"use strict";
|
|
19050
|
-
HUMAN_SOURCES = /* @__PURE__ */ new Set([
|
|
19208
|
+
HUMAN_SOURCES = /* @__PURE__ */ new Set([
|
|
19209
|
+
"daemon",
|
|
19210
|
+
"cloud",
|
|
19211
|
+
"local-decision",
|
|
19212
|
+
"inline-review-approved",
|
|
19213
|
+
"inline-review"
|
|
19214
|
+
]);
|
|
19051
19215
|
TIMEOUT_SOURCES = /* @__PURE__ */ new Set(["timeout"]);
|
|
19216
|
+
NON_DECISION_SOURCES = /* @__PURE__ */ new Set([
|
|
19217
|
+
"post-hook",
|
|
19218
|
+
"inline-review-approved",
|
|
19219
|
+
"response-dlp"
|
|
19220
|
+
]);
|
|
19052
19221
|
has = (s, needle) => s.includes(needle);
|
|
19053
19222
|
}
|
|
19054
19223
|
});
|
|
@@ -19631,7 +19800,7 @@ function buildDaemonReport(allEntries, period, now) {
|
|
|
19631
19800
|
else if (period === "30d") start.setDate(start.getDate() - 29);
|
|
19632
19801
|
else if (period === "month") start = new Date(now.getFullYear(), now.getMonth(), 1);
|
|
19633
19802
|
const entries = allEntries.filter((e) => {
|
|
19634
|
-
if (e.source === "
|
|
19803
|
+
if (typeof e.source === "string" && NON_DECISION_SOURCES.has(e.source)) return false;
|
|
19635
19804
|
return new Date(e.ts) >= start;
|
|
19636
19805
|
});
|
|
19637
19806
|
const isBlocked = (e) => classifyDecision(e).outcome === "deny";
|
|
@@ -47372,8 +47541,8 @@ INSTRUCTIONS:
|
|
|
47372
47541
|
- Do NOT retry this exact command or attempt to bypass the rule.${recovery}
|
|
47373
47542
|
- Inform the user which security rule was triggered and ask how to proceed.`;
|
|
47374
47543
|
}
|
|
47375
|
-
function buildReviewMessage(blockedByLabel, ruleDescription) {
|
|
47376
|
-
const why = ruleDescription || blockedByLabel || "this action needs your review";
|
|
47544
|
+
function buildReviewMessage(blockedByLabel, ruleDescription, reason) {
|
|
47545
|
+
const why = ruleDescription || (reason && reason !== blockedByLabel ? reason : void 0) || blockedByLabel || "this action needs your review";
|
|
47377
47546
|
return `Node9 flagged this for your review: ${why}. Approve to proceed, or deny to cancel.`;
|
|
47378
47547
|
}
|
|
47379
47548
|
|
|
@@ -48114,6 +48283,7 @@ import path46 from "path";
|
|
|
48114
48283
|
function storePath() {
|
|
48115
48284
|
return process.env.NODE9_PENDING_STORE || path46.join(os43.homedir(), ".node9", "pending-reviews.json");
|
|
48116
48285
|
}
|
|
48286
|
+
var TTL_TUID_MS = 72 * 60 * 60 * 1e3;
|
|
48117
48287
|
var TTL_MS2 = 6 * 60 * 60 * 1e3;
|
|
48118
48288
|
var MAX_ENTRIES = 500;
|
|
48119
48289
|
function reviewCorrelationKey(payload) {
|
|
@@ -48147,7 +48317,9 @@ function write(store) {
|
|
|
48147
48317
|
}
|
|
48148
48318
|
}
|
|
48149
48319
|
function prune(entries, now) {
|
|
48150
|
-
const fresh = entries.filter(
|
|
48320
|
+
const fresh = entries.filter(
|
|
48321
|
+
(e) => now - e.ts < (e.key.startsWith("tuid:") ? TTL_TUID_MS : TTL_MS2)
|
|
48322
|
+
);
|
|
48151
48323
|
return fresh.length > MAX_ENTRIES ? fresh.slice(fresh.length - MAX_ENTRIES) : fresh;
|
|
48152
48324
|
}
|
|
48153
48325
|
function recordPendingReview(entry) {
|
|
@@ -48162,19 +48334,30 @@ function recordPendingReview(entry) {
|
|
|
48162
48334
|
function resolvePendingReview(key, now = Date.now()) {
|
|
48163
48335
|
try {
|
|
48164
48336
|
const store = read();
|
|
48165
|
-
const
|
|
48337
|
+
const live = prune(store.entries, now);
|
|
48338
|
+
const idx = live.findIndex((e) => e.key === key);
|
|
48166
48339
|
if (idx === -1) {
|
|
48167
|
-
|
|
48168
|
-
if (pruned.length !== store.entries.length) write({ entries: pruned });
|
|
48340
|
+
if (live.length !== store.entries.length) write({ entries: live });
|
|
48169
48341
|
return null;
|
|
48170
48342
|
}
|
|
48171
|
-
const [match] =
|
|
48172
|
-
write({ entries:
|
|
48343
|
+
const [match] = live.splice(idx, 1);
|
|
48344
|
+
write({ entries: live });
|
|
48173
48345
|
return match;
|
|
48174
48346
|
} catch {
|
|
48175
48347
|
return null;
|
|
48176
48348
|
}
|
|
48177
48349
|
}
|
|
48350
|
+
function discardPendingReview(key, now = Date.now()) {
|
|
48351
|
+
try {
|
|
48352
|
+
const store = read();
|
|
48353
|
+
const kept = prune(
|
|
48354
|
+
store.entries.filter((e) => e.key !== key),
|
|
48355
|
+
now
|
|
48356
|
+
);
|
|
48357
|
+
if (kept.length !== store.entries.length) write({ entries: kept });
|
|
48358
|
+
} catch {
|
|
48359
|
+
}
|
|
48360
|
+
}
|
|
48178
48361
|
|
|
48179
48362
|
// src/cli/commands/check.ts
|
|
48180
48363
|
init_hook_payload();
|
|
@@ -48230,7 +48413,10 @@ function agentSupportsAsk(agent) {
|
|
|
48230
48413
|
}
|
|
48231
48414
|
function resolveAskMode(agent, opts, config) {
|
|
48232
48415
|
if (!agentSupportsAsk(agent)) return false;
|
|
48233
|
-
if (config.settings.
|
|
48416
|
+
if (config.settings.reviewChannelManaged === true) {
|
|
48417
|
+
if (config.settings.reviewChannel === "ask") return true;
|
|
48418
|
+
if (config.settings.reviewChannel === "approver") return false;
|
|
48419
|
+
}
|
|
48234
48420
|
if (opts.ask === true) return true;
|
|
48235
48421
|
if (opts.ask === false) return false;
|
|
48236
48422
|
if (config.settings.reviewChannel === "ask") return true;
|
|
@@ -48469,7 +48655,11 @@ RAW: ${raw}
|
|
|
48469
48655
|
process.exit(2);
|
|
48470
48656
|
};
|
|
48471
48657
|
const sendAsk = (result2) => {
|
|
48472
|
-
const msg = buildReviewMessage(
|
|
48658
|
+
const msg = buildReviewMessage(
|
|
48659
|
+
result2.blockedByLabel,
|
|
48660
|
+
result2.ruleDescription,
|
|
48661
|
+
result2.reason
|
|
48662
|
+
);
|
|
48473
48663
|
try {
|
|
48474
48664
|
const key = reviewCorrelationKey(payload);
|
|
48475
48665
|
if (key) {
|
|
@@ -48652,6 +48842,13 @@ RAW: ${raw}
|
|
|
48652
48842
|
cwd: safeCwdForAuth,
|
|
48653
48843
|
deferReview: askMode
|
|
48654
48844
|
});
|
|
48845
|
+
if (!result.review) {
|
|
48846
|
+
try {
|
|
48847
|
+
const key = reviewCorrelationKey(payload);
|
|
48848
|
+
if (key) discardPendingReview(key);
|
|
48849
|
+
} catch {
|
|
48850
|
+
}
|
|
48851
|
+
}
|
|
48655
48852
|
if (result.approved) {
|
|
48656
48853
|
if (result.checkedBy && process.env.NODE9_DEBUG === "1")
|
|
48657
48854
|
process.stderr.write(`\u2713 node9 [${result.checkedBy}]: "${toolName}" allowed
|
|
@@ -48739,6 +48936,7 @@ import path48 from "path";
|
|
|
48739
48936
|
import os45 from "os";
|
|
48740
48937
|
init_daemon();
|
|
48741
48938
|
init_dlp();
|
|
48939
|
+
init_hasher();
|
|
48742
48940
|
|
|
48743
48941
|
// src/utils/cp-mv-parser.ts
|
|
48744
48942
|
function parseCpMvOp(command) {
|
|
@@ -48800,6 +48998,20 @@ function atLeastConfidence(c, min) {
|
|
|
48800
48998
|
function sanitize3(value) {
|
|
48801
48999
|
return value.replace(/[\x00-\x1F\x7F]/g, "");
|
|
48802
49000
|
}
|
|
49001
|
+
function buildArgsField(rawInput) {
|
|
49002
|
+
let hashed = {};
|
|
49003
|
+
try {
|
|
49004
|
+
hashed = { argsHash: hashArgs(rawInput) };
|
|
49005
|
+
} catch {
|
|
49006
|
+
}
|
|
49007
|
+
try {
|
|
49008
|
+
const hit = scanArgs(rawInput);
|
|
49009
|
+
if (hit) return { ...hashed, dlpPattern: hit.patternName, dlpSample: hit.redactedSample };
|
|
49010
|
+
return { args: JSON.parse(redactSecrets(JSON.stringify(rawInput))) };
|
|
49011
|
+
} catch {
|
|
49012
|
+
return hashed;
|
|
49013
|
+
}
|
|
49014
|
+
}
|
|
48803
49015
|
function registerLogCommand(program2) {
|
|
48804
49016
|
program2.command("log", { hidden: true }).description("PostToolUse hook \u2014 records executed tool calls").argument("[data]", "JSON string of the tool call").option(
|
|
48805
49017
|
"--agent <name>",
|
|
@@ -48828,15 +49040,17 @@ function registerLogCommand(program2) {
|
|
|
48828
49040
|
})();
|
|
48829
49041
|
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;
|
|
48830
49042
|
let reviewApproved = false;
|
|
49043
|
+
let resolvedReview = null;
|
|
48831
49044
|
try {
|
|
48832
49045
|
const key = reviewCorrelationKey(payload);
|
|
48833
|
-
if (key
|
|
49046
|
+
if (key) resolvedReview = resolvePendingReview(key);
|
|
49047
|
+
if (resolvedReview) reviewApproved = true;
|
|
48834
49048
|
} catch {
|
|
48835
49049
|
}
|
|
48836
49050
|
const entry = {
|
|
48837
49051
|
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
48838
49052
|
tool,
|
|
48839
|
-
|
|
49053
|
+
...buildArgsField(rawInput),
|
|
48840
49054
|
decision: "allowed",
|
|
48841
49055
|
source: reviewApproved ? "inline-review-approved" : "post-hook"
|
|
48842
49056
|
};
|
|
@@ -48883,6 +49097,33 @@ function registerLogCommand(program2) {
|
|
|
48883
49097
|
const payloadCwd = typeof payload.cwd === "string" ? payload.cwd : Array.isArray(payload.workspacePaths) && typeof payload.workspacePaths[0] === "string" ? payload.workspacePaths[0] : void 0;
|
|
48884
49098
|
const safeCwd = typeof payloadCwd === "string" && path48.isAbsolute(payloadCwd) ? payloadCwd : void 0;
|
|
48885
49099
|
const config = getConfig(safeCwd);
|
|
49100
|
+
if (resolvedReview) {
|
|
49101
|
+
try {
|
|
49102
|
+
const reviewLabel = resolvedReview.label || "inline-review";
|
|
49103
|
+
const sensitiveReview = /dlp|taint/i.test(reviewLabel);
|
|
49104
|
+
appendLocalAudit(
|
|
49105
|
+
tool,
|
|
49106
|
+
rawInput,
|
|
49107
|
+
"allow",
|
|
49108
|
+
"inline-review",
|
|
49109
|
+
{
|
|
49110
|
+
agent,
|
|
49111
|
+
ruleName: reviewLabel,
|
|
49112
|
+
...rawToolName !== tool ? { agentToolName: rawToolName } : {},
|
|
49113
|
+
...typeof payloadSessionId === "string" ? { sessionId: payloadSessionId } : {},
|
|
49114
|
+
...safeCwd ? { workingDir: safeCwd } : {}
|
|
49115
|
+
},
|
|
49116
|
+
sensitiveReview || config.settings.auditHashArgs === true
|
|
49117
|
+
);
|
|
49118
|
+
} catch (err2) {
|
|
49119
|
+
appendToLog(HOOK_DEBUG_LOG, {
|
|
49120
|
+
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
49121
|
+
event: "inline-review-ship-row-fail",
|
|
49122
|
+
tool,
|
|
49123
|
+
error: err2 instanceof Error ? err2.message : String(err2)
|
|
49124
|
+
});
|
|
49125
|
+
}
|
|
49126
|
+
}
|
|
48886
49127
|
{
|
|
48887
49128
|
const toolOutput = payload.tool_response?.output;
|
|
48888
49129
|
const inj = config.policy.injectionScan;
|
|
@@ -50351,8 +50592,7 @@ function aggregateReportFromAudit(period, opts = {}) {
|
|
|
50351
50592
|
const priorEnd = new Date(start.getTime() - 1);
|
|
50352
50593
|
const priorStart = new Date(start.getTime() - periodMs);
|
|
50353
50594
|
const priorEntries = allEntries.filter((e) => {
|
|
50354
|
-
if (e.source === "
|
|
50355
|
-
if (e.source === "response-dlp") return false;
|
|
50595
|
+
if (typeof e.source === "string" && NON_DECISION_SOURCES.has(e.source)) return false;
|
|
50356
50596
|
if (typeof e.decision !== "string") return false;
|
|
50357
50597
|
const ts = new Date(e.ts);
|
|
50358
50598
|
return ts >= priorStart && ts <= priorEnd;
|
|
@@ -50363,8 +50603,7 @@ function aggregateReportFromAudit(period, opts = {}) {
|
|
|
50363
50603
|
const testTs = excludeTests ? buildTestTimestamps(allEntries) : /* @__PURE__ */ new Set();
|
|
50364
50604
|
let excludedTests = 0;
|
|
50365
50605
|
const entries = allEntries.filter((e) => {
|
|
50366
|
-
if (e.source === "
|
|
50367
|
-
if (e.source === "response-dlp") return false;
|
|
50606
|
+
if (typeof e.source === "string" && NON_DECISION_SOURCES.has(e.source)) return false;
|
|
50368
50607
|
if (typeof e.decision !== "string") return false;
|
|
50369
50608
|
const ts = new Date(e.ts);
|
|
50370
50609
|
if (ts < start || ts > end) return false;
|