@node9/proxy 2.49.0 → 2.50.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-main.js +875 -465
- package/dist/dashboard.mjs +72 -27
- package/dist/hook.js +20 -13
- package/dist/index.js +7 -4
- package/dist/index.mjs +7 -4
- package/dist/scan-ink.mjs +4 -3
- package/package.json +2 -2
package/dist/dashboard.mjs
CHANGED
|
@@ -566,12 +566,13 @@ function packRows() {
|
|
|
566
566
|
if (!rule.name || SHIELD_RULES_FOLDED_INTO_PRODUCT[rule.name]) continue;
|
|
567
567
|
const id = packRowId(shield.name, rule.name);
|
|
568
568
|
if (!id) continue;
|
|
569
|
-
const owasp = packRowOwasp(id, shield.name);
|
|
570
|
-
if (!owasp) throw new Error(`pack row ${id} has no OWASP risk: add the shield to owasp.ts`);
|
|
571
569
|
rows.push({
|
|
572
570
|
id,
|
|
573
571
|
group: "commands",
|
|
574
|
-
|
|
572
|
+
// A shield missing from the table falls back to tool misuse instead of
|
|
573
|
+
// throwing here: this module loads on every hook call, and a mapping
|
|
574
|
+
// omission must fail a test (owasp.spec.ts), never the gate.
|
|
575
|
+
owasp: packRowOwasp(id, shield.name) ?? "ASI02",
|
|
575
576
|
title: rule.name.replace(SHIELD_RULE_NAME, "$3").replace(/-/g, " "),
|
|
576
577
|
catches: rule.description ?? rule.reason ?? "",
|
|
577
578
|
values: ALL,
|
|
@@ -25344,7 +25345,7 @@ var init_agent_permissions = __esm({
|
|
|
25344
25345
|
init_codex();
|
|
25345
25346
|
init_read_small_file();
|
|
25346
25347
|
CATEGORY = "Agent permissions";
|
|
25347
|
-
CLAUDE_NO_PROMPT_MODES = /* @__PURE__ */ new Set(["bypassPermissions"
|
|
25348
|
+
CLAUDE_NO_PROMPT_MODES = /* @__PURE__ */ new Set(["bypassPermissions"]);
|
|
25348
25349
|
MAX_FILE_BYTES2 = 2 * 1024 * 1024;
|
|
25349
25350
|
CI_SEVERITY = {
|
|
25350
25351
|
critical: "critical",
|
|
@@ -25380,6 +25381,15 @@ function checkOutsideContent(ctx) {
|
|
|
25380
25381
|
const roles = config.policy.untrustedContent?.roles ?? {};
|
|
25381
25382
|
const sources = outsideSources(home, roles);
|
|
25382
25383
|
if (sources.length === 0) return [];
|
|
25384
|
+
const byServer = /* @__PURE__ */ new Map();
|
|
25385
|
+
for (const src of sources) {
|
|
25386
|
+
const e = byServer.get(src.server);
|
|
25387
|
+
if (e) {
|
|
25388
|
+
if (!e.agents.includes(src.agent)) e.agents.push(src.agent);
|
|
25389
|
+
if (src.how === "role") e.how = "role";
|
|
25390
|
+
} else byServer.set(src.server, { agents: [src.agent], how: src.how });
|
|
25391
|
+
}
|
|
25392
|
+
const servers = byServer.size;
|
|
25383
25393
|
const egress = config.policy.egress;
|
|
25384
25394
|
const egressGated = !!egress?.enabled && (egress.mode === "block" || egress.mode === "review");
|
|
25385
25395
|
const hold = checkValue(
|
|
@@ -25396,11 +25406,14 @@ function checkOutsideContent(ctx) {
|
|
|
25396
25406
|
{
|
|
25397
25407
|
category: CATEGORY2,
|
|
25398
25408
|
severity: "high",
|
|
25399
|
-
title: `${
|
|
25409
|
+
title: `${servers} MCP server${servers === 1 ? "" : "s"} bring${servers === 1 ? "s" : ""} outside content in, and the way out is open`,
|
|
25400
25410
|
what: "An agent here reads pages, issues or messages written by others, and what it does next is not held.",
|
|
25401
25411
|
why: gaps.join("; ") + ".",
|
|
25402
25412
|
who: "One poisoned page or issue is enough: the agent reads it, then sends or deletes as it says.",
|
|
25403
|
-
detail: [
|
|
25413
|
+
detail: [
|
|
25414
|
+
...[...byServer].slice(0, 8).map(([name, e]) => `${name} (${e.agents.join(", ")}, by ${e.how})`),
|
|
25415
|
+
...gaps
|
|
25416
|
+
],
|
|
25404
25417
|
owner: "node9",
|
|
25405
25418
|
fix: 'Run `node9 egress watch` (or `node9 egress lock`), keep behavior.untrusted-content on, and mark each server\'s reading tools as "Brings outside content" on the Apps page so the hold applies to them.'
|
|
25406
25419
|
}
|
|
@@ -25550,14 +25563,14 @@ import fs24 from "fs";
|
|
|
25550
25563
|
import path27 from "path";
|
|
25551
25564
|
import os21 from "os";
|
|
25552
25565
|
import crypto3 from "crypto";
|
|
25553
|
-
function getPinsFilePath() {
|
|
25554
|
-
return path27.join(
|
|
25566
|
+
function getPinsFilePath(home = os21.homedir()) {
|
|
25567
|
+
return path27.join(home, ".node9", "skill-pins.json");
|
|
25555
25568
|
}
|
|
25556
25569
|
function getRootKey(absPath) {
|
|
25557
25570
|
return crypto3.createHash("sha256").update(absPath).digest("hex").slice(0, 16);
|
|
25558
25571
|
}
|
|
25559
|
-
function readSkillPinsSafe() {
|
|
25560
|
-
const filePath = getPinsFilePath();
|
|
25572
|
+
function readSkillPinsSafe(home) {
|
|
25573
|
+
const filePath = getPinsFilePath(home);
|
|
25561
25574
|
try {
|
|
25562
25575
|
const raw = fs24.readFileSync(filePath, "utf-8");
|
|
25563
25576
|
if (!raw.trim()) return { ok: false, reason: "corrupt", detail: "empty file" };
|
|
@@ -25678,7 +25691,7 @@ function checkSupplyChain(ctx) {
|
|
|
25678
25691
|
}
|
|
25679
25692
|
function pluginPinState(home) {
|
|
25680
25693
|
const roots = defaultSkillRoots(void 0, home);
|
|
25681
|
-
const pins = readSkillPinsSafe();
|
|
25694
|
+
const pins = readSkillPinsSafe(home);
|
|
25682
25695
|
const pinned = pins.ok ? pins.pins.roots : {};
|
|
25683
25696
|
return { roots, unpinned: roots.filter((r) => !pinned[getRootKey(r)]) };
|
|
25684
25697
|
}
|
|
@@ -25691,7 +25704,7 @@ function pluginsAndPackages(ctx, home) {
|
|
|
25691
25704
|
if (roots.length > 0 && tamper === "off") {
|
|
25692
25705
|
findings.push({
|
|
25693
25706
|
category: "Supply chain",
|
|
25694
|
-
severity: "
|
|
25707
|
+
severity: "advisory",
|
|
25695
25708
|
title: `${roots.length} installed plugin${roots.length === 1 ? "" : "s"} load${roots.length === 1 ? "s" : ""} unverified`,
|
|
25696
25709
|
what: "Plugins and skills the agent loads are code that runs with its rights, and no content hash is kept for them.",
|
|
25697
25710
|
why: "The check loading.skill-tamper is off, so node9 does not pin them.",
|
|
@@ -25703,7 +25716,7 @@ function pluginsAndPackages(ctx, home) {
|
|
|
25703
25716
|
} else if (unpinned.length > 0) {
|
|
25704
25717
|
findings.push({
|
|
25705
25718
|
category: "Supply chain",
|
|
25706
|
-
severity: "
|
|
25719
|
+
severity: "advisory",
|
|
25707
25720
|
title: `${unpinned.length} of ${roots.length} installed plugin${roots.length === 1 ? "" : "s"} ${unpinned.length === 1 ? "has" : "have"} no pin yet`,
|
|
25708
25721
|
what: "A plugin the agent can load has no recorded content hash yet.",
|
|
25709
25722
|
why: "node9 pins a plugin the first time it loads; these have not loaded since pinning was turned on.",
|
|
@@ -26148,7 +26161,8 @@ function checkCoverage(ctx) {
|
|
|
26148
26161
|
});
|
|
26149
26162
|
return findings;
|
|
26150
26163
|
}
|
|
26151
|
-
const
|
|
26164
|
+
const config = getConfig(ctx.cwd);
|
|
26165
|
+
const mode = config.settings.mode;
|
|
26152
26166
|
if (isRecordOnly(mode)) {
|
|
26153
26167
|
const level = levelOf(mode);
|
|
26154
26168
|
findings.push({
|
|
@@ -26163,7 +26177,6 @@ function checkCoverage(ctx) {
|
|
|
26163
26177
|
fix: "Set mode to `standard` (or `strict`) to enforce in-path."
|
|
26164
26178
|
});
|
|
26165
26179
|
}
|
|
26166
|
-
const config = getConfig(ctx.cwd);
|
|
26167
26180
|
if (config.settings.autoStartDaemon !== false) {
|
|
26168
26181
|
const state = autostartState();
|
|
26169
26182
|
if (state === "disabled" || state === "absent") {
|
|
@@ -26482,8 +26495,6 @@ async function annotateCoverage(findings, ctx) {
|
|
|
26482
26495
|
}
|
|
26483
26496
|
if (probe.kind === "egress") {
|
|
26484
26497
|
f.coverage = egressCoverage(env);
|
|
26485
|
-
if (f.coverage.state === "open" && config.policy.egress.enabled && config.policy.egress.mode !== "off" && isRecordOnly(mode))
|
|
26486
|
-
f.fix = auditModeFix(mode, "node9 egress is set");
|
|
26487
26498
|
continue;
|
|
26488
26499
|
}
|
|
26489
26500
|
if (probe.kind === "gateWired") {
|
|
@@ -30440,7 +30451,9 @@ async function _authorizeHeadlessCore(toolName, args, metaArg, options) {
|
|
|
30440
30451
|
systemAccessHandled: !isManual && fileAccess.handledPaths.has(filePath)
|
|
30441
30452
|
}) : null) ?? argumentScan(),
|
|
30442
30453
|
pii: () => detectArgsPii(args),
|
|
30443
|
-
packageCheck: () => packageCheck?.enabled && !isManual ? runPackageCheck(toolName, args, packageCheck, options?.cwd, {
|
|
30454
|
+
packageCheck: () => packageCheck?.enabled && !isManual ? runPackageCheck(toolName, args, packageCheck, options?.cwd, {
|
|
30455
|
+
offline: !!forecast && forecast.onlinePackages !== true
|
|
30456
|
+
}) : Promise.resolve(null)
|
|
30444
30457
|
});
|
|
30445
30458
|
const dlpMatch = gates.stop?.dlpMatch ?? gates.reviews.dlp ?? null;
|
|
30446
30459
|
if (dlpMatch) {
|
|
@@ -31463,7 +31476,8 @@ async function forecastDecision(toolName, args, meta, opts = {}) {
|
|
|
31463
31476
|
session,
|
|
31464
31477
|
config,
|
|
31465
31478
|
unknown: /* @__PURE__ */ new Set(),
|
|
31466
|
-
now: opts.at ?? Date.now()
|
|
31479
|
+
now: opts.at ?? Date.now(),
|
|
31480
|
+
...opts.onlinePackages && { onlinePackages: true }
|
|
31467
31481
|
};
|
|
31468
31482
|
const result = await runForecast(
|
|
31469
31483
|
run,
|
|
@@ -31558,6 +31572,20 @@ var init_forecast = __esm({
|
|
|
31558
31572
|
}
|
|
31559
31573
|
});
|
|
31560
31574
|
|
|
31575
|
+
// src/exposure/shell-fetch.ts
|
|
31576
|
+
function shellFetch(tool, rawInput) {
|
|
31577
|
+
if (tool !== "Bash" && tool !== "bash") return null;
|
|
31578
|
+
const cmd = rawInput && typeof rawInput === "object" && !Array.isArray(rawInput) ? rawInput.command : void 0;
|
|
31579
|
+
return typeof cmd === "string" && cmd.length > 0 ? classifyShellExposure(cmd) : null;
|
|
31580
|
+
}
|
|
31581
|
+
var init_shell_fetch = __esm({
|
|
31582
|
+
"src/exposure/shell-fetch.ts"() {
|
|
31583
|
+
"use strict";
|
|
31584
|
+
init_esm_shims();
|
|
31585
|
+
init_dist();
|
|
31586
|
+
}
|
|
31587
|
+
});
|
|
31588
|
+
|
|
31561
31589
|
// src/version.ts
|
|
31562
31590
|
import fs44 from "fs";
|
|
31563
31591
|
import path49 from "path";
|
|
@@ -31858,6 +31886,8 @@ var init_scan_judge = __esm({
|
|
|
31858
31886
|
init_forecast();
|
|
31859
31887
|
init_forecast_state();
|
|
31860
31888
|
init_roles();
|
|
31889
|
+
init_shell_fetch();
|
|
31890
|
+
init_dist();
|
|
31861
31891
|
init_scan_cache();
|
|
31862
31892
|
init_dlp();
|
|
31863
31893
|
init_scan();
|
|
@@ -31929,16 +31959,26 @@ var init_scan_judge = __esm({
|
|
|
31929
31959
|
this.session(sessionId).noteOutputSecret(patternName, clockOf(timestamp2));
|
|
31930
31960
|
}
|
|
31931
31961
|
/** A tool that brings outside content returned something. */
|
|
31932
|
-
|
|
31962
|
+
/**
|
|
31963
|
+
* A tool result reached the session. A source read marks it exposed, as the
|
|
31964
|
+
* PostToolUse hook does (log.ts): a source tool, or a shell command that
|
|
31965
|
+
* fetches outside content (`gh issue view`). `read.output` is the result's
|
|
31966
|
+
* text: the destinations it names are recorded as the hook records them.
|
|
31967
|
+
*/
|
|
31968
|
+
noteToolResult(sessionId, toolName, timestamp2, cwd, read2 = {}) {
|
|
31933
31969
|
const policy = this.configFor(cwd).policy.untrustedContent;
|
|
31934
31970
|
if (!policy?.enabled) return;
|
|
31935
|
-
|
|
31971
|
+
const shell = shellFetch(toolName, read2.input);
|
|
31972
|
+
if (!toolRole(toolName, policy).source && !shell?.fetches) return;
|
|
31936
31973
|
const mcp = /^mcp__([^_]+(?:_[^_]+)*)__/.exec(toolName);
|
|
31937
|
-
this.session(sessionId).noteOutsideContent(
|
|
31938
|
-
|
|
31939
|
-
|
|
31940
|
-
|
|
31941
|
-
|
|
31974
|
+
this.session(sessionId).noteOutsideContent(
|
|
31975
|
+
{
|
|
31976
|
+
tool: toolName,
|
|
31977
|
+
...mcp && { server: mcp[1] },
|
|
31978
|
+
at: clockOf(timestamp2)
|
|
31979
|
+
},
|
|
31980
|
+
read2.output ? extractContentDestinations(read2.output) : []
|
|
31981
|
+
);
|
|
31942
31982
|
}
|
|
31943
31983
|
async judge(call, result, dedup) {
|
|
31944
31984
|
const config = this.configFor(call.cwd);
|
|
@@ -32330,6 +32370,7 @@ async function processClaudeFile(file2, projPath, projLabel, judge, judgeFrom, r
|
|
|
32330
32370
|
const sessionCalls = [];
|
|
32331
32371
|
const toolUseFilePaths = /* @__PURE__ */ new Map();
|
|
32332
32372
|
const toolUseNames = /* @__PURE__ */ new Map();
|
|
32373
|
+
const toolUseInputs = /* @__PURE__ */ new Map();
|
|
32333
32374
|
let firstDlpTs = null;
|
|
32334
32375
|
let firstEditTs = null;
|
|
32335
32376
|
for (const line of raw.split("\n")) {
|
|
@@ -32400,7 +32441,10 @@ async function processClaudeFile(file2, projPath, projLabel, judge, judgeFrom, r
|
|
|
32400
32441
|
if (isNode9SelfOutput(resultText)) continue;
|
|
32401
32442
|
const resultTool = block.tool_use_id ? toolUseNames.get(block.tool_use_id) : void 0;
|
|
32402
32443
|
if (resultTool)
|
|
32403
|
-
judge.noteToolResult(sessionId, resultTool, entry.timestamp ?? "", entry.cwd
|
|
32444
|
+
judge.noteToolResult(sessionId, resultTool, entry.timestamp ?? "", entry.cwd, {
|
|
32445
|
+
input: block.tool_use_id ? toolUseInputs.get(block.tool_use_id) : void 0,
|
|
32446
|
+
output: resultText
|
|
32447
|
+
});
|
|
32404
32448
|
const decoysHere1 = recordCanaries(
|
|
32405
32449
|
{ text: resultText },
|
|
32406
32450
|
"tool-result",
|
|
@@ -32465,6 +32509,7 @@ async function processClaudeFile(file2, projPath, projLabel, judge, judgeFrom, r
|
|
|
32465
32509
|
toolUseFilePaths.set(block.id, input.file_path);
|
|
32466
32510
|
}
|
|
32467
32511
|
if (block.id) toolUseNames.set(block.id, toolName);
|
|
32512
|
+
if (block.id) toolUseInputs.set(block.id, input);
|
|
32468
32513
|
sessionCalls.push({ toolName, input, timestamp: entry.timestamp ?? "" });
|
|
32469
32514
|
if (toolNameLower === "bash" || toolNameLower === "execute_bash") {
|
|
32470
32515
|
result.bashCalls++;
|
package/dist/hook.js
CHANGED
|
@@ -475,12 +475,13 @@ function packRows() {
|
|
|
475
475
|
if (!rule.name || SHIELD_RULES_FOLDED_INTO_PRODUCT[rule.name]) continue;
|
|
476
476
|
const id = packRowId(shield.name, rule.name);
|
|
477
477
|
if (!id) continue;
|
|
478
|
-
const owasp = packRowOwasp(id, shield.name);
|
|
479
|
-
if (!owasp) throw new Error(`pack row ${id} has no OWASP risk: add the shield to owasp.ts`);
|
|
480
478
|
rows.push({
|
|
481
479
|
id,
|
|
482
480
|
group: "commands",
|
|
483
|
-
|
|
481
|
+
// A shield missing from the table falls back to tool misuse instead of
|
|
482
|
+
// throwing here: this module loads on every hook call, and a mapping
|
|
483
|
+
// omission must fail a test (owasp.spec.ts), never the gate.
|
|
484
|
+
owasp: packRowOwasp(id, shield.name) ?? "ASI02",
|
|
484
485
|
title: rule.name.replace(SHIELD_RULE_NAME, "$3").replace(/-/g, " "),
|
|
485
486
|
catches: rule.description ?? rule.reason ?? "",
|
|
486
487
|
values: ALL,
|
|
@@ -25813,7 +25814,9 @@ async function _authorizeHeadlessCore(toolName, args, metaArg, options) {
|
|
|
25813
25814
|
systemAccessHandled: !isManual && fileAccess.handledPaths.has(filePath)
|
|
25814
25815
|
}) : null) ?? argumentScan(),
|
|
25815
25816
|
pii: () => detectArgsPii(args),
|
|
25816
|
-
packageCheck: () => packageCheck?.enabled && !isManual ? runPackageCheck(toolName, args, packageCheck, options?.cwd, {
|
|
25817
|
+
packageCheck: () => packageCheck?.enabled && !isManual ? runPackageCheck(toolName, args, packageCheck, options?.cwd, {
|
|
25818
|
+
offline: !!forecast && forecast.onlinePackages !== true
|
|
25819
|
+
}) : Promise.resolve(null)
|
|
25817
25820
|
});
|
|
25818
25821
|
const dlpMatch = gates.stop?.dlpMatch ?? gates.reviews.dlp ?? null;
|
|
25819
25822
|
if (dlpMatch) {
|
|
@@ -26824,8 +26827,8 @@ var import_fs33 = __toESM(require("fs"));
|
|
|
26824
26827
|
var import_path35 = __toESM(require("path"));
|
|
26825
26828
|
var import_os24 = __toESM(require("os"));
|
|
26826
26829
|
var import_crypto11 = __toESM(require("crypto"));
|
|
26827
|
-
function getPinsFilePath() {
|
|
26828
|
-
return import_path35.default.join(
|
|
26830
|
+
function getPinsFilePath(home = import_os24.default.homedir()) {
|
|
26831
|
+
return import_path35.default.join(home, ".node9", "skill-pins.json");
|
|
26829
26832
|
}
|
|
26830
26833
|
var MAX_FILES = 5e3;
|
|
26831
26834
|
var MAX_TOTAL_BYTES = 50 * 1024 * 1024;
|
|
@@ -26897,8 +26900,8 @@ function hashSkillRoot(absPath) {
|
|
|
26897
26900
|
function getRootKey(absPath) {
|
|
26898
26901
|
return import_crypto11.default.createHash("sha256").update(absPath).digest("hex").slice(0, 16);
|
|
26899
26902
|
}
|
|
26900
|
-
function readSkillPinsSafe() {
|
|
26901
|
-
const filePath = getPinsFilePath();
|
|
26903
|
+
function readSkillPinsSafe(home) {
|
|
26904
|
+
const filePath = getPinsFilePath(home);
|
|
26902
26905
|
try {
|
|
26903
26906
|
const raw = import_fs33.default.readFileSync(filePath, "utf-8");
|
|
26904
26907
|
if (!raw.trim()) return { ok: false, reason: "corrupt", detail: "empty file" };
|
|
@@ -27885,6 +27888,15 @@ var import_os28 = __toESM(require("os"));
|
|
|
27885
27888
|
init_audit();
|
|
27886
27889
|
init_dist();
|
|
27887
27890
|
|
|
27891
|
+
// src/exposure/shell-fetch.ts
|
|
27892
|
+
init_cjs_shims();
|
|
27893
|
+
init_dist();
|
|
27894
|
+
function shellFetch(tool, rawInput) {
|
|
27895
|
+
if (tool !== "Bash" && tool !== "bash") return null;
|
|
27896
|
+
const cmd = rawInput && typeof rawInput === "object" && !Array.isArray(rawInput) ? rawInput.command : void 0;
|
|
27897
|
+
return typeof cmd === "string" && cmd.length > 0 ? classifyShellExposure(cmd) : null;
|
|
27898
|
+
}
|
|
27899
|
+
|
|
27888
27900
|
// src/exposure/source.ts
|
|
27889
27901
|
init_cjs_shims();
|
|
27890
27902
|
init_audit();
|
|
@@ -28098,11 +28110,6 @@ function buildArgsField(rawInput) {
|
|
|
28098
28110
|
return hashed;
|
|
28099
28111
|
}
|
|
28100
28112
|
}
|
|
28101
|
-
function shellFetch(tool, rawInput) {
|
|
28102
|
-
if (tool !== "Bash" && tool !== "bash") return null;
|
|
28103
|
-
const cmd = rawInput && typeof rawInput === "object" && !Array.isArray(rawInput) ? rawInput.command : void 0;
|
|
28104
|
-
return typeof cmd === "string" && cmd.length > 0 ? classifyShellExposure(cmd) : null;
|
|
28105
|
-
}
|
|
28106
28113
|
function registerLogCommand(program2) {
|
|
28107
28114
|
program2.command("log", { hidden: true }).description("PostToolUse hook \u2014 records executed tool calls").argument("[data]", "JSON string of the tool call").option(
|
|
28108
28115
|
"--agent <name>",
|
package/dist/index.js
CHANGED
|
@@ -529,12 +529,13 @@ function packRows() {
|
|
|
529
529
|
if (!rule.name || SHIELD_RULES_FOLDED_INTO_PRODUCT[rule.name]) continue;
|
|
530
530
|
const id = packRowId(shield.name, rule.name);
|
|
531
531
|
if (!id) continue;
|
|
532
|
-
const owasp = packRowOwasp(id, shield.name);
|
|
533
|
-
if (!owasp) throw new Error(`pack row ${id} has no OWASP risk: add the shield to owasp.ts`);
|
|
534
532
|
rows.push({
|
|
535
533
|
id,
|
|
536
534
|
group: "commands",
|
|
537
|
-
|
|
535
|
+
// A shield missing from the table falls back to tool misuse instead of
|
|
536
|
+
// throwing here: this module loads on every hook call, and a mapping
|
|
537
|
+
// omission must fail a test (owasp.spec.ts), never the gate.
|
|
538
|
+
owasp: packRowOwasp(id, shield.name) ?? "ASI02",
|
|
538
539
|
title: rule.name.replace(SHIELD_RULE_NAME, "$3").replace(/-/g, " "),
|
|
539
540
|
catches: rule.description ?? rule.reason ?? "",
|
|
540
541
|
values: ALL,
|
|
@@ -25293,7 +25294,9 @@ async function _authorizeHeadlessCore(toolName, args, metaArg, options) {
|
|
|
25293
25294
|
systemAccessHandled: !isManual && fileAccess.handledPaths.has(filePath)
|
|
25294
25295
|
}) : null) ?? argumentScan(),
|
|
25295
25296
|
pii: () => detectArgsPii(args),
|
|
25296
|
-
packageCheck: () => packageCheck?.enabled && !isManual ? runPackageCheck(toolName, args, packageCheck, options?.cwd, {
|
|
25297
|
+
packageCheck: () => packageCheck?.enabled && !isManual ? runPackageCheck(toolName, args, packageCheck, options?.cwd, {
|
|
25298
|
+
offline: !!forecast && forecast.onlinePackages !== true
|
|
25299
|
+
}) : Promise.resolve(null)
|
|
25297
25300
|
});
|
|
25298
25301
|
const dlpMatch = gates.stop?.dlpMatch ?? gates.reviews.dlp ?? null;
|
|
25299
25302
|
if (dlpMatch) {
|
package/dist/index.mjs
CHANGED
|
@@ -516,12 +516,13 @@ function packRows() {
|
|
|
516
516
|
if (!rule.name || SHIELD_RULES_FOLDED_INTO_PRODUCT[rule.name]) continue;
|
|
517
517
|
const id = packRowId(shield.name, rule.name);
|
|
518
518
|
if (!id) continue;
|
|
519
|
-
const owasp = packRowOwasp(id, shield.name);
|
|
520
|
-
if (!owasp) throw new Error(`pack row ${id} has no OWASP risk: add the shield to owasp.ts`);
|
|
521
519
|
rows.push({
|
|
522
520
|
id,
|
|
523
521
|
group: "commands",
|
|
524
|
-
|
|
522
|
+
// A shield missing from the table falls back to tool misuse instead of
|
|
523
|
+
// throwing here: this module loads on every hook call, and a mapping
|
|
524
|
+
// omission must fail a test (owasp.spec.ts), never the gate.
|
|
525
|
+
owasp: packRowOwasp(id, shield.name) ?? "ASI02",
|
|
525
526
|
title: rule.name.replace(SHIELD_RULE_NAME, "$3").replace(/-/g, " "),
|
|
526
527
|
catches: rule.description ?? rule.reason ?? "",
|
|
527
528
|
values: ALL,
|
|
@@ -25264,7 +25265,9 @@ async function _authorizeHeadlessCore(toolName, args, metaArg, options) {
|
|
|
25264
25265
|
systemAccessHandled: !isManual && fileAccess.handledPaths.has(filePath)
|
|
25265
25266
|
}) : null) ?? argumentScan(),
|
|
25266
25267
|
pii: () => detectArgsPii(args),
|
|
25267
|
-
packageCheck: () => packageCheck?.enabled && !isManual ? runPackageCheck(toolName, args, packageCheck, options?.cwd, {
|
|
25268
|
+
packageCheck: () => packageCheck?.enabled && !isManual ? runPackageCheck(toolName, args, packageCheck, options?.cwd, {
|
|
25269
|
+
offline: !!forecast && forecast.onlinePackages !== true
|
|
25270
|
+
}) : Promise.resolve(null)
|
|
25268
25271
|
});
|
|
25269
25272
|
const dlpMatch = gates.stop?.dlpMatch ?? gates.reviews.dlp ?? null;
|
|
25270
25273
|
if (dlpMatch) {
|
package/dist/scan-ink.mjs
CHANGED
|
@@ -3014,12 +3014,13 @@ function packRows() {
|
|
|
3014
3014
|
if (!rule.name || SHIELD_RULES_FOLDED_INTO_PRODUCT[rule.name]) continue;
|
|
3015
3015
|
const id = packRowId(shield.name, rule.name);
|
|
3016
3016
|
if (!id) continue;
|
|
3017
|
-
const owasp = packRowOwasp(id, shield.name);
|
|
3018
|
-
if (!owasp) throw new Error(`pack row ${id} has no OWASP risk: add the shield to owasp.ts`);
|
|
3019
3017
|
rows.push({
|
|
3020
3018
|
id,
|
|
3021
3019
|
group: "commands",
|
|
3022
|
-
|
|
3020
|
+
// A shield missing from the table falls back to tool misuse instead of
|
|
3021
|
+
// throwing here: this module loads on every hook call, and a mapping
|
|
3022
|
+
// omission must fail a test (owasp.spec.ts), never the gate.
|
|
3023
|
+
owasp: packRowOwasp(id, shield.name) ?? "ASI02",
|
|
3023
3024
|
title: rule.name.replace(SHIELD_RULE_NAME, "$3").replace(/-/g, " "),
|
|
3024
3025
|
catches: rule.description ?? rule.reason ?? "",
|
|
3025
3026
|
values: ALL,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@node9/proxy",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.50.0",
|
|
4
4
|
"description": "Access control for AI agents. Set what Claude Code, Codex, Gemini, Cursor and any MCP server are allowed to do, review risky actions before they run, and keep every action on the record.",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"module": "./dist/index.mjs",
|
|
@@ -110,7 +110,7 @@
|
|
|
110
110
|
"mvdan-sh"
|
|
111
111
|
],
|
|
112
112
|
"devDependencies": {
|
|
113
|
-
"@anthropic-ai/sdk": "^
|
|
113
|
+
"@anthropic-ai/sdk": "^1.3.0",
|
|
114
114
|
"@octokit/rest": "^22.0.1",
|
|
115
115
|
"@semantic-release/commit-analyzer": "^13.0.1",
|
|
116
116
|
"@semantic-release/git": "^10.0.1",
|