@node9/proxy 2.47.1 → 2.48.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 +928 -666
- package/dist/dashboard.mjs +1 -0
- package/dist/hook.js +376 -123
- package/dist/index.js +17 -5
- package/dist/index.mjs +17 -5
- package/package.json +1 -1
- package/dist/cli-main.d.mts +0 -1
- package/dist/cli-main.d.ts +0 -1
- package/dist/cli-main.mjs +0 -90543
package/dist/hook.js
CHANGED
|
@@ -18474,17 +18474,17 @@ var init_audit = __esm({
|
|
|
18474
18474
|
// src/hook-entry.ts
|
|
18475
18475
|
init_cjs_shims();
|
|
18476
18476
|
var import_commander = require("commander");
|
|
18477
|
-
var
|
|
18478
|
-
var
|
|
18479
|
-
var
|
|
18477
|
+
var import_fs37 = __toESM(require("fs"));
|
|
18478
|
+
var import_os29 = __toESM(require("os"));
|
|
18479
|
+
var import_path39 = __toESM(require("path"));
|
|
18480
18480
|
|
|
18481
18481
|
// src/cli/commands/check.ts
|
|
18482
18482
|
init_cjs_shims();
|
|
18483
18483
|
var import_chalk2 = __toESM(require("chalk"));
|
|
18484
|
-
var
|
|
18484
|
+
var import_fs35 = __toESM(require("fs"));
|
|
18485
18485
|
var import_child_process3 = require("child_process");
|
|
18486
|
-
var
|
|
18487
|
-
var
|
|
18486
|
+
var import_path37 = __toESM(require("path"));
|
|
18487
|
+
var import_os27 = __toESM(require("os"));
|
|
18488
18488
|
|
|
18489
18489
|
// src/auth/orchestrator.ts
|
|
18490
18490
|
init_cjs_shims();
|
|
@@ -19662,19 +19662,30 @@ function recordFromLegacyWait(body, now = Date.now()) {
|
|
|
19662
19662
|
|
|
19663
19663
|
// src/auth/daemon.ts
|
|
19664
19664
|
var ACTIVITY_SOCKET_PATH = process.platform === "win32" ? "\\\\.\\pipe\\node9-activity" : import_path11.default.join(import_os8.default.tmpdir(), "node9-activity.sock");
|
|
19665
|
-
|
|
19665
|
+
var ACTIVITY_SOCKET_TIMEOUT_MS = 500;
|
|
19666
|
+
function notifyActivitySocket(data, opts = {}) {
|
|
19666
19667
|
if (forecastRun()) return Promise.resolve(false);
|
|
19667
19668
|
return new Promise((resolve) => {
|
|
19669
|
+
let settled = false;
|
|
19670
|
+
let sock = null;
|
|
19671
|
+
const finish = (ok) => {
|
|
19672
|
+
if (settled) return;
|
|
19673
|
+
settled = true;
|
|
19674
|
+
clearTimeout(timer);
|
|
19675
|
+
if (!ok) sock?.destroy();
|
|
19676
|
+
resolve(ok);
|
|
19677
|
+
};
|
|
19678
|
+
const timer = setTimeout(() => finish(false), opts.timeoutMs ?? ACTIVITY_SOCKET_TIMEOUT_MS);
|
|
19668
19679
|
try {
|
|
19669
19680
|
const payload = JSON.stringify(data);
|
|
19670
|
-
|
|
19681
|
+
sock = import_net.default.createConnection(opts.socketPath ?? ACTIVITY_SOCKET_PATH);
|
|
19671
19682
|
sock.on("connect", () => {
|
|
19672
|
-
sock.on("close", () =>
|
|
19683
|
+
sock.on("close", () => finish(true));
|
|
19673
19684
|
sock.end(payload);
|
|
19674
19685
|
});
|
|
19675
|
-
sock.on("error", () =>
|
|
19686
|
+
sock.on("error", () => finish(false));
|
|
19676
19687
|
} catch {
|
|
19677
|
-
|
|
19688
|
+
finish(false);
|
|
19678
19689
|
}
|
|
19679
19690
|
});
|
|
19680
19691
|
}
|
|
@@ -20934,8 +20945,8 @@ var ConfigFileSchema = import_zod.z.object({
|
|
|
20934
20945
|
}).strict();
|
|
20935
20946
|
function formatIssues(issues) {
|
|
20936
20947
|
const lines = issues.map((issue) => {
|
|
20937
|
-
const
|
|
20938
|
-
return ` \u2022 ${
|
|
20948
|
+
const path40 = issue.path.length > 0 ? issue.path.map(String).join(".") : "root";
|
|
20949
|
+
return ` \u2022 ${path40}: ${issue.message}`;
|
|
20939
20950
|
});
|
|
20940
20951
|
return `Invalid config:
|
|
20941
20952
|
${lines.join("\n")}`;
|
|
@@ -20948,9 +20959,9 @@ function prunePaths(root, paths) {
|
|
|
20948
20959
|
const yi = y[y.length - 1];
|
|
20949
20960
|
return typeof xi === "number" && typeof yi === "number" ? yi - xi : 0;
|
|
20950
20961
|
});
|
|
20951
|
-
for (const
|
|
20962
|
+
for (const path40 of ordered) {
|
|
20952
20963
|
let cur = root;
|
|
20953
|
-
for (const key of
|
|
20964
|
+
for (const key of path40.slice(0, -1)) {
|
|
20954
20965
|
if (cur === null || typeof cur !== "object") {
|
|
20955
20966
|
cur = void 0;
|
|
20956
20967
|
break;
|
|
@@ -20958,7 +20969,7 @@ function prunePaths(root, paths) {
|
|
|
20958
20969
|
cur = cur[key];
|
|
20959
20970
|
}
|
|
20960
20971
|
if (cur === null || typeof cur !== "object") continue;
|
|
20961
|
-
const last =
|
|
20972
|
+
const last = path40[path40.length - 1];
|
|
20962
20973
|
if (Array.isArray(cur)) {
|
|
20963
20974
|
const i = Number(last);
|
|
20964
20975
|
if (Number.isInteger(i) && i >= 0 && i < cur.length) {
|
|
@@ -20994,7 +21005,7 @@ function sanitizeConfig(raw) {
|
|
|
20994
21005
|
return issue.keys.map((k) => [...at, k]);
|
|
20995
21006
|
}
|
|
20996
21007
|
return [at.slice(0, -level || void 0)];
|
|
20997
|
-
}).filter((
|
|
21008
|
+
}).filter((path40) => path40.length > 0);
|
|
20998
21009
|
if (paths.length === 0 || !prunePaths(working, paths)) break;
|
|
20999
21010
|
}
|
|
21000
21011
|
if (ConfigFileSchema.safeParse(working).success) break;
|
|
@@ -22215,13 +22226,13 @@ function getConfig(cwd) {
|
|
|
22215
22226
|
}
|
|
22216
22227
|
if (Array.isArray(mc.jailPaths)) {
|
|
22217
22228
|
for (const jp of mc.jailPaths) {
|
|
22218
|
-
const
|
|
22219
|
-
if (!
|
|
22229
|
+
const path40 = typeof jp?.path === "string" ? jp.path.trim() : "";
|
|
22230
|
+
if (!path40) continue;
|
|
22220
22231
|
const verdict = jp?.verdict === "review" ? "review" : "block";
|
|
22221
|
-
for (const r of pathRules(
|
|
22232
|
+
for (const r of pathRules(path40, verdict, "org-managed jail")) {
|
|
22222
22233
|
mergedPolicy.smartRules.push({ ...r, name: `org:${r.name}` });
|
|
22223
22234
|
}
|
|
22224
|
-
mergedPolicy.managedJailPaths.push({ path:
|
|
22235
|
+
mergedPolicy.managedJailPaths.push({ path: path40, verdict });
|
|
22225
22236
|
}
|
|
22226
22237
|
}
|
|
22227
22238
|
if (Array.isArray(mc.trustedHosts)) {
|
|
@@ -24148,6 +24159,79 @@ function recordAndCheck(tool, args, threshold = 3, windowMs = 12e4) {
|
|
|
24148
24159
|
|
|
24149
24160
|
// src/policy/negotiation.ts
|
|
24150
24161
|
init_cjs_shims();
|
|
24162
|
+
init_dist();
|
|
24163
|
+
var USER_LINE_ICON = "\u{1F6E1}\uFE0F";
|
|
24164
|
+
var QUOTE_INSTRUCTION = "Start your reply with the line above, exactly as written, on its own line.";
|
|
24165
|
+
var USER_LINE_PART_MAX = 160;
|
|
24166
|
+
var ONE_LINE_INPUT_MAX = 1e3;
|
|
24167
|
+
var PICTOGRAPHS = /[\p{Extended_Pictographic}\u200D\uFE0F]/gu;
|
|
24168
|
+
function oneLine(text) {
|
|
24169
|
+
return stripTerminalEscapes(text.slice(0, ONE_LINE_INPUT_MAX)).replace(/\s*\u2014\s*/g, ", ").replace(/\s+/g, " ").trim();
|
|
24170
|
+
}
|
|
24171
|
+
function bounded(text, max = USER_LINE_PART_MAX) {
|
|
24172
|
+
return text.length > max ? `${text.slice(0, max - 3).trimEnd()}...` : text;
|
|
24173
|
+
}
|
|
24174
|
+
function userLinePart(text) {
|
|
24175
|
+
return bounded(oneLine(text).replace(/[.\s]+$/, ""));
|
|
24176
|
+
}
|
|
24177
|
+
function userLineValue(text, max = 80) {
|
|
24178
|
+
const v = bounded(oneLine(text).replace(/`/g, "'"), max);
|
|
24179
|
+
return v ? `\`${v}\`` : "";
|
|
24180
|
+
}
|
|
24181
|
+
function userLineOf(message) {
|
|
24182
|
+
const first = message.split("\n", 1)[0];
|
|
24183
|
+
return first.startsWith(USER_LINE_ICON) ? first : void 0;
|
|
24184
|
+
}
|
|
24185
|
+
function withUserLine(line, body) {
|
|
24186
|
+
return `${USER_LINE_ICON} ${line}
|
|
24187
|
+
${QUOTE_INSTRUCTION}
|
|
24188
|
+
${body}`;
|
|
24189
|
+
}
|
|
24190
|
+
function blockedLine(context, fallback) {
|
|
24191
|
+
return `Node9 blocked: ${ruleWhat(context?.ruleDescription) || fallback}`;
|
|
24192
|
+
}
|
|
24193
|
+
function firstSentence(text) {
|
|
24194
|
+
const plain = oneLine(text.replace(PICTOGRAPHS, ""));
|
|
24195
|
+
return plain.split(/(?<=[.!?])\s+(?=[A-Z])/)[0] ?? plain;
|
|
24196
|
+
}
|
|
24197
|
+
function withoutLeadingVerdict(text) {
|
|
24198
|
+
return text.replace(/^node9\b\s*:?\s*/i, "").replace(/^blocked\b\s*:?\s*/i, "");
|
|
24199
|
+
}
|
|
24200
|
+
var IS_BLOCKED_BY = /^(.+?) (?:is|are) blocked by (.+?)\.?$/i;
|
|
24201
|
+
var IS_BLOCKED = /^(.+?) (?:is|are) blocked\.?$/i;
|
|
24202
|
+
var COMMA_BLOCKED_BY = /^(.+?), blocked by (.+?)\.?$/i;
|
|
24203
|
+
function ruleWhat(description) {
|
|
24204
|
+
if (!description) return "";
|
|
24205
|
+
const s = firstSentence(description).replace(/^node9\s*:\s*/i, "").replace(/^blocked\s*:\s*/i, "");
|
|
24206
|
+
if (!/[\p{L}\p{N}]/u.test(s)) return "";
|
|
24207
|
+
const by = s.match(IS_BLOCKED_BY) ?? s.match(COMMA_BLOCKED_BY);
|
|
24208
|
+
if (by) return userLinePart(`${by[1]} (${by[2]})`);
|
|
24209
|
+
const bare = s.match(IS_BLOCKED);
|
|
24210
|
+
return userLinePart(bare ? bare[1] : s);
|
|
24211
|
+
}
|
|
24212
|
+
function reasonSentence(reason) {
|
|
24213
|
+
if (!reason) return "";
|
|
24214
|
+
const s = withoutLeadingVerdict(firstSentence(reason));
|
|
24215
|
+
if (/^("[^"]*")?\.?$/.test(s)) return "";
|
|
24216
|
+
return userLinePart(s);
|
|
24217
|
+
}
|
|
24218
|
+
function maliciousPackageLine(reason) {
|
|
24219
|
+
if (!reason) return "";
|
|
24220
|
+
const names = [...reason.matchAll(/"([^"]+)"/g)].map((m) => {
|
|
24221
|
+
const spec = m[1];
|
|
24222
|
+
const at = spec.lastIndexOf("@");
|
|
24223
|
+
return at > 0 ? spec.slice(0, at) : spec;
|
|
24224
|
+
});
|
|
24225
|
+
if (names.length === 0) return "";
|
|
24226
|
+
const advisories = [...reason.matchAll(/\b((?:MAL|GHSA|CVE|OSV|PYSEC|RUSTSEC)-[\w-]+)/g)].map(
|
|
24227
|
+
(m) => m[1]
|
|
24228
|
+
);
|
|
24229
|
+
const unique2 = [...new Set(names)];
|
|
24230
|
+
const suffix = advisories.length ? ` (${[...new Set(advisories)].join(", ")})` : "";
|
|
24231
|
+
return userLinePart(
|
|
24232
|
+
unique2.length === 1 ? `${unique2[0]} is a known malicious package${suffix}` : `${unique2.join(", ")} are known malicious packages${suffix}`
|
|
24233
|
+
);
|
|
24234
|
+
}
|
|
24151
24235
|
function buildNegotiationMessage(blockedByLabel, isHumanDecision2, humanReason, recoveryCommand, context) {
|
|
24152
24236
|
if (isHumanDecision2 && context?.redirect && humanReason) {
|
|
24153
24237
|
return `NODE9: The user stopped this action and gave you an instruction.
|
|
@@ -24157,88 +24241,138 @@ INSTRUCTIONS:
|
|
|
24157
24241
|
- Retry the original action only if the instruction says you may.`;
|
|
24158
24242
|
}
|
|
24159
24243
|
if (isHumanDecision2) {
|
|
24160
|
-
return
|
|
24244
|
+
return withUserLine(
|
|
24245
|
+
"Node9: you blocked this action",
|
|
24246
|
+
`NODE9: The human user rejected this action.
|
|
24161
24247
|
REASON: ${humanReason || "No specific reason provided."}
|
|
24162
24248
|
INSTRUCTIONS:
|
|
24163
24249
|
- Do NOT retry this exact command.
|
|
24164
24250
|
- Acknowledge the block to the user and ask if there is an alternative approach.
|
|
24165
|
-
- If you believe this action is critical, explain your reasoning and ask them to run "node9 pause 15m" to proceed
|
|
24251
|
+
- If you believe this action is critical, explain your reasoning and ask them to run "node9 pause 15m" to proceed.`
|
|
24252
|
+
);
|
|
24166
24253
|
}
|
|
24167
24254
|
if (context?.kind === "protected-path") {
|
|
24168
24255
|
const subject = context.path ? `${context.path} is a protected file` : "This file is protected";
|
|
24169
|
-
|
|
24256
|
+
const shown = context.path ? userLineValue(context.path) : "";
|
|
24257
|
+
const line2 = shown ? `Node9 blocked access to ${shown} (protected file)` : "Node9 blocked access to a protected file";
|
|
24258
|
+
return withUserLine(
|
|
24259
|
+
line2,
|
|
24260
|
+
`NODE9: Blocked. ${subject}, and node9 stopped this call to it. The call did not run, so nothing was exposed.
|
|
24170
24261
|
INSTRUCTIONS:
|
|
24171
24262
|
- Do NOT retry, and do NOT reach the file another way (cat, grep, a copy, a script, a redirect).
|
|
24172
24263
|
- If this step needs that file, ask the user to provide what you need or to do the step themselves.
|
|
24173
|
-
- Tell the user node9 protected this file, then ask how to proceed
|
|
24264
|
+
- Tell the user node9 protected this file, then ask how to proceed.`
|
|
24265
|
+
);
|
|
24174
24266
|
}
|
|
24175
24267
|
const label2 = blockedByLabel.toLowerCase();
|
|
24176
24268
|
if (label2.includes("dlp") || label2.includes("secret detected") || label2.includes("credential review")) {
|
|
24177
|
-
return
|
|
24269
|
+
return withUserLine(
|
|
24270
|
+
"Node9 blocked this command: it contains a secret",
|
|
24271
|
+
`NODE9 SECURITY ALERT: A sensitive credential (API key, token, or private key) was found in your tool call arguments.
|
|
24178
24272
|
CRITICAL INSTRUCTION: Do NOT retry this action.
|
|
24179
24273
|
REQUIRED ACTIONS:
|
|
24180
24274
|
1. Remove the hardcoded credential from your command or code.
|
|
24181
24275
|
2. Use an environment variable or a dedicated secrets manager instead.
|
|
24182
24276
|
3. Treat the leaked credential as compromised and rotate it immediately.
|
|
24183
24277
|
Do NOT attempt to bypass this check or pass the credential through another tool.
|
|
24184
|
-
- Tell the user node9 blocked this and why, then ask how to proceed
|
|
24278
|
+
- Tell the user node9 blocked this and why, then ask how to proceed.`
|
|
24279
|
+
);
|
|
24185
24280
|
}
|
|
24186
24281
|
if (label2.includes("package check")) {
|
|
24187
|
-
|
|
24282
|
+
const pkg = maliciousPackageLine(humanReason);
|
|
24283
|
+
return withUserLine(
|
|
24284
|
+
pkg ? `Node9 blocked: ${pkg}` : "Node9 blocked: this package is known to be malicious",
|
|
24285
|
+
`NODE9: Blocked \u2014 ${humanReason || "a package in this command is known to be malicious."}
|
|
24188
24286
|
INSTRUCTIONS:
|
|
24189
24287
|
- Do NOT install this package, and do NOT fetch it another way (a different package manager, a tarball URL, a git URL, a mirror).
|
|
24190
24288
|
- If a dependency is needed, suggest a well-known alternative and let the user choose.
|
|
24191
|
-
- Tell the user node9 blocked this install and name the advisory, then ask how to proceed
|
|
24289
|
+
- Tell the user node9 blocked this install and name the advisory, then ask how to proceed.`
|
|
24290
|
+
);
|
|
24192
24291
|
}
|
|
24193
24292
|
if (label2.includes("sql safety") && label2.includes("delete without where")) {
|
|
24194
|
-
return
|
|
24293
|
+
return withUserLine(
|
|
24294
|
+
blockedLine(context, "DELETE without WHERE would wipe the whole table"),
|
|
24295
|
+
`NODE9: Blocked \u2014 DELETE without WHERE clause would wipe the entire table.
|
|
24195
24296
|
INSTRUCTION: Add a WHERE clause to scope the deletion (e.g. WHERE id = <value>).
|
|
24196
24297
|
Do NOT retry without a WHERE clause.
|
|
24197
|
-
- Tell the user node9 blocked this and why, then ask how to proceed
|
|
24298
|
+
- Tell the user node9 blocked this and why, then ask how to proceed.`
|
|
24299
|
+
);
|
|
24198
24300
|
}
|
|
24199
24301
|
if (label2.includes("sql safety") && label2.includes("update without where")) {
|
|
24200
|
-
return
|
|
24302
|
+
return withUserLine(
|
|
24303
|
+
blockedLine(context, "UPDATE without WHERE would change every row"),
|
|
24304
|
+
`NODE9: Blocked \u2014 UPDATE without WHERE clause would update every row.
|
|
24201
24305
|
INSTRUCTION: Add a WHERE clause to scope the update (e.g. WHERE id = <value>).
|
|
24202
24306
|
Do NOT retry without a WHERE clause.
|
|
24203
|
-
- Tell the user node9 blocked this and why, then ask how to proceed
|
|
24307
|
+
- Tell the user node9 blocked this and why, then ask how to proceed.`
|
|
24308
|
+
);
|
|
24204
24309
|
}
|
|
24205
24310
|
if (label2.includes("dangerous word")) {
|
|
24206
24311
|
const match = blockedByLabel.match(/dangerous word: "([^"]+)"/i);
|
|
24207
24312
|
const word = match?.[1] ?? "a dangerous keyword";
|
|
24208
|
-
return
|
|
24313
|
+
return withUserLine(
|
|
24314
|
+
blockedLine(context, `the command contains ${userLineValue(word)}`),
|
|
24315
|
+
`NODE9: Blocked \u2014 command contains forbidden keyword "${word}".
|
|
24209
24316
|
INSTRUCTION: Do NOT use "${word}". Use a non-destructive alternative.
|
|
24210
24317
|
Do NOT attempt to bypass this with shell tricks or aliases \u2014 it will be blocked again.
|
|
24211
|
-
- Tell the user node9 blocked this and why, then ask how to proceed
|
|
24318
|
+
- Tell the user node9 blocked this and why, then ask how to proceed.`
|
|
24319
|
+
);
|
|
24212
24320
|
}
|
|
24213
24321
|
if (label2.includes("path blocked") || label2.includes("sandbox")) {
|
|
24214
|
-
return
|
|
24322
|
+
return withUserLine(
|
|
24323
|
+
blockedLine(context, "this path is outside the allowed sandbox"),
|
|
24324
|
+
`NODE9: Blocked \u2014 operation targets a path outside the allowed sandbox.
|
|
24215
24325
|
INSTRUCTION: Move your output to an allowed directory such as /tmp/ or the project directory.
|
|
24216
24326
|
Do NOT retry on the same path.
|
|
24217
|
-
- Tell the user node9 blocked this and why, then ask how to proceed
|
|
24327
|
+
- Tell the user node9 blocked this and why, then ask how to proceed.`
|
|
24328
|
+
);
|
|
24218
24329
|
}
|
|
24219
24330
|
if (label2.includes("inline execution")) {
|
|
24220
|
-
return
|
|
24331
|
+
return withUserLine(
|
|
24332
|
+
blockedLine(context, "inline code execution is not allowed"),
|
|
24333
|
+
`NODE9: Blocked \u2014 inline code execution (e.g. bash -c "...") is not allowed.
|
|
24221
24334
|
INSTRUCTION: Use individual tool calls instead of embedding code in a shell string.
|
|
24222
|
-
- Tell the user node9 blocked this and why, then ask how to proceed
|
|
24335
|
+
- Tell the user node9 blocked this and why, then ask how to proceed.`
|
|
24336
|
+
);
|
|
24223
24337
|
}
|
|
24224
24338
|
if (label2.includes("strict mode")) {
|
|
24225
|
-
return
|
|
24226
|
-
|
|
24339
|
+
return withUserLine(
|
|
24340
|
+
"Node9: the Block all level is on, so every action needs your approval",
|
|
24341
|
+
`NODE9: Blocked \u2014 the Block all level is on. All tool calls require explicit human approval.
|
|
24342
|
+
INSTRUCTION: Inform the user this action is pending approval. Wait for them to approve via the dashboard or run "node9 pause".`
|
|
24343
|
+
);
|
|
24227
24344
|
}
|
|
24228
24345
|
if (label2.includes("rule") && label2.includes("default block")) {
|
|
24229
24346
|
const match = blockedByLabel.match(/rule "([^"]+)"/i);
|
|
24230
|
-
const
|
|
24231
|
-
return
|
|
24232
|
-
|
|
24347
|
+
const rule2 = match?.[1] ?? "a policy rule";
|
|
24348
|
+
return withUserLine(
|
|
24349
|
+
blockedLine(context, `${userLineValue(rule2)} is not allowed by policy`),
|
|
24350
|
+
`NODE9: Blocked \u2014 action "${rule2}" is forbidden by security policy.
|
|
24351
|
+
INSTRUCTION: Do NOT use "${rule2}". Find a read-only or non-destructive alternative.
|
|
24233
24352
|
Do NOT attempt to bypass this rule.
|
|
24234
|
-
- Tell the user node9 blocked this and why, then ask how to proceed
|
|
24353
|
+
- Tell the user node9 blocked this and why, then ask how to proceed.`
|
|
24354
|
+
);
|
|
24355
|
+
}
|
|
24356
|
+
const timedOut = label2.includes("approval timeout");
|
|
24357
|
+
const rule = ruleWhat(context?.ruleDescription);
|
|
24358
|
+
let line;
|
|
24359
|
+
if (timedOut) {
|
|
24360
|
+
line = rule ? `Node9 blocked: no one approved this in time (${rule.replace(/ \(([^()]+)\)$/, ", $1")})` : "Node9 blocked: no one approved this in time";
|
|
24361
|
+
} else if (rule) {
|
|
24362
|
+
line = `Node9 blocked: ${rule}`;
|
|
24363
|
+
} else {
|
|
24364
|
+
const why = reasonSentence(humanReason);
|
|
24365
|
+
line = why ? `Node9 blocked: ${why}` : `Node9 blocked this action (${userLinePart(agentFacingLabel(blockedByLabel))})`;
|
|
24235
24366
|
}
|
|
24236
24367
|
const recovery = recoveryCommand ? `
|
|
24237
24368
|
REQUIRED ACTION: Run \`${recoveryCommand}\` first, then retry your original command.` : "\n- Pivot to a non-destructive or read-only alternative.";
|
|
24238
|
-
return
|
|
24369
|
+
return withUserLine(
|
|
24370
|
+
line,
|
|
24371
|
+
`NODE9: Action blocked by security policy [${agentFacingLabel(blockedByLabel)}].
|
|
24239
24372
|
INSTRUCTIONS:
|
|
24240
24373
|
- Do NOT retry this exact command or attempt to bypass the rule.${recovery}
|
|
24241
|
-
- Inform the user which security rule was triggered and ask how to proceed
|
|
24374
|
+
- Inform the user which security rule was triggered and ask how to proceed.`
|
|
24375
|
+
);
|
|
24242
24376
|
}
|
|
24243
24377
|
function buildReviewMessage(blockedByLabel, ruleDescription, reason) {
|
|
24244
24378
|
const why = ruleDescription || (reason && reason !== blockedByLabel ? reason : void 0) || blockedByLabel || "this action needs your review";
|
|
@@ -24253,7 +24387,7 @@ function isHumanDecision(label2, blockedBy, decidedBy) {
|
|
|
24253
24387
|
return l.includes("user") || l.includes("daemon") || l.includes("decision");
|
|
24254
24388
|
}
|
|
24255
24389
|
function agentFacingLabel(label2) {
|
|
24256
|
-
const plain = label2.replace(
|
|
24390
|
+
const plain = label2.replace(PICTOGRAPHS, "").replace(/\bNode9\b/g, "node9").replace(/\s+/g, " ").trim();
|
|
24257
24391
|
return plain || "Security Policy";
|
|
24258
24392
|
}
|
|
24259
24393
|
var BLOCKED_BY = " is blocked by ";
|
|
@@ -26297,6 +26431,109 @@ function canonicalToolInput(rawToolName, input) {
|
|
|
26297
26431
|
return canonical;
|
|
26298
26432
|
}
|
|
26299
26433
|
|
|
26434
|
+
// src/utils/own-dir.ts
|
|
26435
|
+
init_cjs_shims();
|
|
26436
|
+
var import_fs33 = __toESM(require("fs"));
|
|
26437
|
+
var import_path35 = __toESM(require("path"));
|
|
26438
|
+
var SESSION_FLAG_SUFFIX = ".node9-flag";
|
|
26439
|
+
var SESSION_FLAG_NAME = /^[A-Za-z0-9_-]{1,128}\.node9-flag$/;
|
|
26440
|
+
function isSessionFlagName(name) {
|
|
26441
|
+
return SESSION_FLAG_NAME.test(name);
|
|
26442
|
+
}
|
|
26443
|
+
function ownRealDir(st) {
|
|
26444
|
+
if (st.isSymbolicLink() || !st.isDirectory()) return false;
|
|
26445
|
+
return typeof process.getuid !== "function" || st.uid === process.getuid();
|
|
26446
|
+
}
|
|
26447
|
+
function ensureOwnDir(dir2) {
|
|
26448
|
+
try {
|
|
26449
|
+
import_fs33.default.mkdirSync(dir2, { recursive: true, mode: 448 });
|
|
26450
|
+
const st = import_fs33.default.lstatSync(dir2);
|
|
26451
|
+
return ownRealDir(st) ? { dev: st.dev, ino: st.ino } : null;
|
|
26452
|
+
} catch {
|
|
26453
|
+
return null;
|
|
26454
|
+
}
|
|
26455
|
+
}
|
|
26456
|
+
function isSameOwnDir(dir2, seen) {
|
|
26457
|
+
try {
|
|
26458
|
+
const st = import_fs33.default.lstatSync(dir2);
|
|
26459
|
+
return ownRealDir(st) && st.dev === seen.dev && st.ino === seen.ino;
|
|
26460
|
+
} catch {
|
|
26461
|
+
return false;
|
|
26462
|
+
}
|
|
26463
|
+
}
|
|
26464
|
+
function removeOldFlag(dir2, seen, name, cutoffMs) {
|
|
26465
|
+
if (!isSessionFlagName(name)) return;
|
|
26466
|
+
const p = import_path35.default.join(dir2, name);
|
|
26467
|
+
try {
|
|
26468
|
+
const st = import_fs33.default.lstatSync(p);
|
|
26469
|
+
if (!st.isFile() || st.mtimeMs >= cutoffMs) return;
|
|
26470
|
+
if (!isSameOwnDir(dir2, seen)) return;
|
|
26471
|
+
import_fs33.default.unlinkSync(p);
|
|
26472
|
+
} catch {
|
|
26473
|
+
}
|
|
26474
|
+
}
|
|
26475
|
+
|
|
26476
|
+
// src/notices/session-notice.ts
|
|
26477
|
+
init_cjs_shims();
|
|
26478
|
+
var import_fs34 = __toESM(require("fs"));
|
|
26479
|
+
var import_os26 = __toESM(require("os"));
|
|
26480
|
+
var import_path36 = __toESM(require("path"));
|
|
26481
|
+
init_dist();
|
|
26482
|
+
var ICON = "\u{1F6E1}\uFE0F";
|
|
26483
|
+
var LEVEL_NAMES = {
|
|
26484
|
+
audit: "Audit",
|
|
26485
|
+
soft: "Soft",
|
|
26486
|
+
standard: "Standard",
|
|
26487
|
+
strict: "Strict",
|
|
26488
|
+
"block-all": "Block all"
|
|
26489
|
+
};
|
|
26490
|
+
var SESSION_NOTICE_INSTRUCTION = "Start your next reply to the user with the line above, exactly as written, on its own line.";
|
|
26491
|
+
var FLAG_TTL_MS = 7 * 24 * 60 * 60 * 1e3;
|
|
26492
|
+
var SAFE_SESSION_ID = /^[A-Za-z0-9_-]{1,128}$/;
|
|
26493
|
+
function sessionNoticeLine(mode) {
|
|
26494
|
+
const level = levelOf(mode);
|
|
26495
|
+
if (level === "audit") {
|
|
26496
|
+
return `${ICON} Node9 is in Audit mode: actions are logged, nothing is blocked.`;
|
|
26497
|
+
}
|
|
26498
|
+
return `${ICON} Node9 is on (${LEVEL_NAMES[level]}). Every action in this session is checked.`;
|
|
26499
|
+
}
|
|
26500
|
+
function sessionNoticeOutput(mode) {
|
|
26501
|
+
const line = sessionNoticeLine(mode);
|
|
26502
|
+
return JSON.stringify({
|
|
26503
|
+
hookSpecificOutput: {
|
|
26504
|
+
hookEventName: "PreToolUse",
|
|
26505
|
+
additionalContext: `${line}
|
|
26506
|
+
${SESSION_NOTICE_INSTRUCTION}`
|
|
26507
|
+
},
|
|
26508
|
+
systemMessage: line
|
|
26509
|
+
});
|
|
26510
|
+
}
|
|
26511
|
+
function sessionNoticeEnabled(env = process.env) {
|
|
26512
|
+
if (env.NODE9_SESSION_NOTICE === "1") return true;
|
|
26513
|
+
return env.NODE9_TESTING !== "1";
|
|
26514
|
+
}
|
|
26515
|
+
function claimSessionNotice(sessionId, home = import_os26.default.homedir()) {
|
|
26516
|
+
if (!sessionId || !SAFE_SESSION_ID.test(sessionId)) return false;
|
|
26517
|
+
const dir2 = import_path36.default.join(home, ".node9", "notice-sessions");
|
|
26518
|
+
const seen = ensureOwnDir(dir2);
|
|
26519
|
+
if (!seen) return false;
|
|
26520
|
+
try {
|
|
26521
|
+
if (!isSameOwnDir(dir2, seen)) return false;
|
|
26522
|
+
import_fs34.default.closeSync(import_fs34.default.openSync(import_path36.default.join(dir2, sessionId + SESSION_FLAG_SUFFIX), "wx"));
|
|
26523
|
+
} catch {
|
|
26524
|
+
return false;
|
|
26525
|
+
}
|
|
26526
|
+
removeOldFlags(dir2, seen);
|
|
26527
|
+
return true;
|
|
26528
|
+
}
|
|
26529
|
+
function removeOldFlags(dir2, seen) {
|
|
26530
|
+
const cutoff = Date.now() - FLAG_TTL_MS;
|
|
26531
|
+
try {
|
|
26532
|
+
for (const name of import_fs34.default.readdirSync(dir2)) removeOldFlag(dir2, seen, name, cutoff);
|
|
26533
|
+
} catch {
|
|
26534
|
+
}
|
|
26535
|
+
}
|
|
26536
|
+
|
|
26300
26537
|
// src/cli/commands/check.ts
|
|
26301
26538
|
function detectAiAgent(payload) {
|
|
26302
26539
|
const meta = payload.meta;
|
|
@@ -26375,9 +26612,9 @@ function registerCheckCommand(program2) {
|
|
|
26375
26612
|
} catch (err) {
|
|
26376
26613
|
const tempConfig = getConfig();
|
|
26377
26614
|
if (process.env.NODE9_DEBUG === "1" || tempConfig.settings.enableHookLogDebug) {
|
|
26378
|
-
const logPath =
|
|
26615
|
+
const logPath = import_path37.default.join(import_os27.default.homedir(), ".node9", "hook-debug.log");
|
|
26379
26616
|
const errMsg = err instanceof Error ? err.message : String(err);
|
|
26380
|
-
|
|
26617
|
+
import_fs35.default.appendFileSync(
|
|
26381
26618
|
logPath,
|
|
26382
26619
|
`[${(/* @__PURE__ */ new Date()).toISOString()}] JSON_PARSE_ERROR: ${errMsg}
|
|
26383
26620
|
RAW: ${raw}
|
|
@@ -26390,14 +26627,14 @@ RAW: ${raw}
|
|
|
26390
26627
|
const prompt = typeof payload.prompt === "string" ? payload.prompt : "";
|
|
26391
26628
|
if (process.env.NODE9_DEBUG === "1") {
|
|
26392
26629
|
try {
|
|
26393
|
-
const logPath =
|
|
26394
|
-
if (!
|
|
26395
|
-
|
|
26630
|
+
const logPath = import_path37.default.join(import_os27.default.homedir(), ".node9", "hook-debug.log");
|
|
26631
|
+
if (!import_fs35.default.existsSync(import_path37.default.dirname(logPath)))
|
|
26632
|
+
import_fs35.default.mkdirSync(import_path37.default.dirname(logPath), { recursive: true });
|
|
26396
26633
|
const sanitized = JSON.stringify({
|
|
26397
26634
|
...payload,
|
|
26398
26635
|
prompt: `<redacted, ${prompt.length} bytes>`
|
|
26399
26636
|
});
|
|
26400
|
-
|
|
26637
|
+
import_fs35.default.appendFileSync(logPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] STDIN: ${sanitized}
|
|
26401
26638
|
`);
|
|
26402
26639
|
} catch {
|
|
26403
26640
|
}
|
|
@@ -26415,7 +26652,7 @@ RAW: ${raw}
|
|
|
26415
26652
|
if (!dlpMatch && !canaryHit) {
|
|
26416
26653
|
try {
|
|
26417
26654
|
const sid = typeof payload.session_id === "string" ? payload.session_id : typeof payload.conversationId === "string" ? payload.conversationId : "";
|
|
26418
|
-
const promptCwd = typeof payload.cwd === "string" &&
|
|
26655
|
+
const promptCwd = typeof payload.cwd === "string" && import_path37.default.isAbsolute(payload.cwd) ? payload.cwd : void 0;
|
|
26419
26656
|
if (sid && getConfig(promptCwd).policy.untrustedContent?.enabled) {
|
|
26420
26657
|
await notifyUserMentions(sid, extractContentDestinations(prompt));
|
|
26421
26658
|
}
|
|
@@ -26456,8 +26693,8 @@ RAW: ${raw}
|
|
|
26456
26693
|
);
|
|
26457
26694
|
const reason = `\u{1F6A8} Node9 DLP: ${shownPattern} detected in prompt (${shownSample}). Prompt was not submitted \u2014 remove the credential and try again.`;
|
|
26458
26695
|
try {
|
|
26459
|
-
const ttyFd =
|
|
26460
|
-
|
|
26696
|
+
const ttyFd = import_fs35.default.openSync("/dev/tty", "w");
|
|
26697
|
+
import_fs35.default.writeSync(
|
|
26461
26698
|
ttyFd,
|
|
26462
26699
|
import_chalk2.default.bgRed.white.bold(`
|
|
26463
26700
|
\u{1F6A8} NODE9 DLP \u2014 PROMPT BLOCKED
|
|
@@ -26467,7 +26704,7 @@ RAW: ${raw}
|
|
|
26467
26704
|
|
|
26468
26705
|
`)
|
|
26469
26706
|
);
|
|
26470
|
-
|
|
26707
|
+
import_fs35.default.closeSync(ttyFd);
|
|
26471
26708
|
} catch {
|
|
26472
26709
|
}
|
|
26473
26710
|
const isCodex = agent2 === "Codex";
|
|
@@ -26486,17 +26723,17 @@ RAW: ${raw}
|
|
|
26486
26723
|
process.exit(2);
|
|
26487
26724
|
}
|
|
26488
26725
|
const payloadCwd = typeof payload.cwd === "string" ? payload.cwd : Array.isArray(payload.workspacePaths) && typeof payload.workspacePaths[0] === "string" ? payload.workspacePaths[0] : void 0;
|
|
26489
|
-
const safeCwdForConfig = typeof payloadCwd === "string" &&
|
|
26726
|
+
const safeCwdForConfig = typeof payloadCwd === "string" && import_path37.default.isAbsolute(payloadCwd) ? payloadCwd : void 0;
|
|
26490
26727
|
const config = getConfig(safeCwdForConfig);
|
|
26491
26728
|
const daemonDown = !isDaemonRunning();
|
|
26492
26729
|
if (config.settings.autoStartDaemon && daemonDown && !process.env.NODE9_NO_AUTO_DAEMON) {
|
|
26493
26730
|
try {
|
|
26494
26731
|
const scriptPath = process.argv[1];
|
|
26495
|
-
if (typeof scriptPath !== "string" || !
|
|
26732
|
+
if (typeof scriptPath !== "string" || !import_path37.default.isAbsolute(scriptPath))
|
|
26496
26733
|
throw new Error("node9: argv[1] is not an absolute path");
|
|
26497
|
-
const resolvedScript =
|
|
26498
|
-
const packageDist =
|
|
26499
|
-
if (!resolvedScript.startsWith(packageDist +
|
|
26734
|
+
const resolvedScript = import_fs35.default.realpathSync(scriptPath);
|
|
26735
|
+
const packageDist = import_fs35.default.realpathSync(import_path37.default.resolve(__dirname, "../.."));
|
|
26736
|
+
if (!resolvedScript.startsWith(packageDist + import_path37.default.sep) && resolvedScript !== packageDist)
|
|
26500
26737
|
throw new Error(
|
|
26501
26738
|
`node9: daemon spawn aborted \u2014 argv[1] (${resolvedScript}) is outside package dist (${packageDist})`
|
|
26502
26739
|
);
|
|
@@ -26526,17 +26763,17 @@ RAW: ${raw}
|
|
|
26526
26763
|
} finally {
|
|
26527
26764
|
if (startupFd !== void 0) {
|
|
26528
26765
|
try {
|
|
26529
|
-
|
|
26766
|
+
import_fs35.default.closeSync(startupFd);
|
|
26530
26767
|
} catch {
|
|
26531
26768
|
}
|
|
26532
26769
|
}
|
|
26533
26770
|
}
|
|
26534
26771
|
} catch (spawnErr) {
|
|
26535
|
-
const logPath =
|
|
26772
|
+
const logPath = import_path37.default.join(import_os27.default.homedir(), ".node9", "hook-debug.log");
|
|
26536
26773
|
const msg = spawnErr instanceof Error ? spawnErr.message : String(spawnErr);
|
|
26537
26774
|
recordStartupState("failed", "spawn-aborted", msg);
|
|
26538
26775
|
try {
|
|
26539
|
-
|
|
26776
|
+
import_fs35.default.appendFileSync(
|
|
26540
26777
|
logPath,
|
|
26541
26778
|
`[${(/* @__PURE__ */ new Date()).toISOString()}] daemon-autostart-failed: ${msg}
|
|
26542
26779
|
`
|
|
@@ -26550,10 +26787,10 @@ RAW: ${raw}
|
|
|
26550
26787
|
);
|
|
26551
26788
|
}
|
|
26552
26789
|
if (process.env.NODE9_DEBUG === "1" || config.settings.enableHookLogDebug) {
|
|
26553
|
-
const logPath =
|
|
26554
|
-
if (!
|
|
26555
|
-
|
|
26556
|
-
|
|
26790
|
+
const logPath = import_path37.default.join(import_os27.default.homedir(), ".node9", "hook-debug.log");
|
|
26791
|
+
if (!import_fs35.default.existsSync(import_path37.default.dirname(logPath)))
|
|
26792
|
+
import_fs35.default.mkdirSync(import_path37.default.dirname(logPath), { recursive: true });
|
|
26793
|
+
import_fs35.default.appendFileSync(
|
|
26557
26794
|
logPath,
|
|
26558
26795
|
`[${(/* @__PURE__ */ new Date()).toISOString()}] STDIN: ${JSON.stringify(raw)}
|
|
26559
26796
|
`
|
|
@@ -26574,8 +26811,8 @@ RAW: ${raw}
|
|
|
26574
26811
|
);
|
|
26575
26812
|
let ttyFd = null;
|
|
26576
26813
|
try {
|
|
26577
|
-
ttyFd =
|
|
26578
|
-
const writeTty = (line) =>
|
|
26814
|
+
ttyFd = import_fs35.default.openSync("/dev/tty", "w");
|
|
26815
|
+
const writeTty = (line) => import_fs35.default.writeSync(ttyFd, line + "\n");
|
|
26579
26816
|
if (result2?.blockKind === "protected-path") {
|
|
26580
26817
|
writeTty(
|
|
26581
26818
|
import_chalk2.default.red.bold(
|
|
@@ -26602,7 +26839,7 @@ RAW: ${raw}
|
|
|
26602
26839
|
} finally {
|
|
26603
26840
|
if (ttyFd !== null)
|
|
26604
26841
|
try {
|
|
26605
|
-
|
|
26842
|
+
import_fs35.default.closeSync(ttyFd);
|
|
26606
26843
|
} catch {
|
|
26607
26844
|
}
|
|
26608
26845
|
}
|
|
@@ -26611,7 +26848,12 @@ RAW: ${raw}
|
|
|
26611
26848
|
humanDecision,
|
|
26612
26849
|
msg,
|
|
26613
26850
|
result2?.recoveryCommand,
|
|
26614
|
-
{
|
|
26851
|
+
{
|
|
26852
|
+
kind: result2?.blockKind,
|
|
26853
|
+
path: result2?.blockedPath,
|
|
26854
|
+
redirect: !!result2?.redirect,
|
|
26855
|
+
ruleDescription: result2?.ruleDescription
|
|
26856
|
+
}
|
|
26615
26857
|
);
|
|
26616
26858
|
if (agent === "Antigravity") {
|
|
26617
26859
|
process.stdout.write(
|
|
@@ -26628,11 +26870,13 @@ RAW: ${raw}
|
|
|
26628
26870
|
);
|
|
26629
26871
|
process.exit(0);
|
|
26630
26872
|
}
|
|
26873
|
+
const userLine = userLineOf(aiFeedbackMessage);
|
|
26631
26874
|
process.stdout.write(
|
|
26632
26875
|
JSON.stringify({
|
|
26633
26876
|
decision: "block",
|
|
26634
26877
|
reason: aiFeedbackMessage,
|
|
26635
26878
|
// This is the core instruction
|
|
26879
|
+
...agent === "Claude Code" && userLine ? { systemMessage: userLine } : {},
|
|
26636
26880
|
hookSpecificOutput: {
|
|
26637
26881
|
hookEventName: "PreToolUse",
|
|
26638
26882
|
permissionDecision: "deny",
|
|
@@ -26666,8 +26910,8 @@ RAW: ${raw}
|
|
|
26666
26910
|
} catch {
|
|
26667
26911
|
}
|
|
26668
26912
|
try {
|
|
26669
|
-
const ttyFd =
|
|
26670
|
-
|
|
26913
|
+
const ttyFd = import_fs35.default.openSync("/dev/tty", "w");
|
|
26914
|
+
import_fs35.default.writeSync(
|
|
26671
26915
|
ttyFd,
|
|
26672
26916
|
import_chalk2.default.yellow(
|
|
26673
26917
|
`
|
|
@@ -26675,7 +26919,7 @@ RAW: ${raw}
|
|
|
26675
26919
|
`
|
|
26676
26920
|
)
|
|
26677
26921
|
);
|
|
26678
|
-
|
|
26922
|
+
import_fs35.default.closeSync(ttyFd);
|
|
26679
26923
|
} catch {
|
|
26680
26924
|
}
|
|
26681
26925
|
if (agent === "GitHub Copilot") {
|
|
@@ -26707,17 +26951,20 @@ RAW: ${raw}
|
|
|
26707
26951
|
const safeSessionId = /^[A-Za-z0-9_\-]{1,128}$/.test(rawSessionId) ? rawSessionId : "";
|
|
26708
26952
|
if (skillPinCfg.enabled && safeSessionId) {
|
|
26709
26953
|
try {
|
|
26710
|
-
const sessionsDir =
|
|
26711
|
-
const flagPath =
|
|
26954
|
+
const sessionsDir = import_path37.default.join(import_os27.default.homedir(), ".node9", "skill-sessions");
|
|
26955
|
+
const flagPath = import_path37.default.join(sessionsDir, `${safeSessionId}${SESSION_FLAG_SUFFIX}`);
|
|
26956
|
+
const memo = ensureOwnDir(sessionsDir);
|
|
26712
26957
|
let flag = null;
|
|
26713
26958
|
try {
|
|
26714
|
-
|
|
26959
|
+
const st = memo ? import_fs35.default.lstatSync(flagPath) : null;
|
|
26960
|
+
if (st && st.isFile() && st.nlink === 1)
|
|
26961
|
+
flag = JSON.parse(import_fs35.default.readFileSync(flagPath, "utf-8"));
|
|
26715
26962
|
} catch {
|
|
26716
26963
|
}
|
|
26717
26964
|
const writeFlag = (data2) => {
|
|
26965
|
+
if (!memo || !isSameOwnDir(sessionsDir, memo)) return;
|
|
26718
26966
|
try {
|
|
26719
|
-
|
|
26720
|
-
import_fs33.default.writeFileSync(
|
|
26967
|
+
atomicWriteSync(
|
|
26721
26968
|
flagPath,
|
|
26722
26969
|
JSON.stringify({ ...data2, timestamp: (/* @__PURE__ */ new Date()).toISOString() }, null, 2),
|
|
26723
26970
|
{ mode: 384 }
|
|
@@ -26728,8 +26975,8 @@ RAW: ${raw}
|
|
|
26728
26975
|
const sendSkillWarn = (detail, recoveryCmd) => {
|
|
26729
26976
|
let ttyFd = null;
|
|
26730
26977
|
try {
|
|
26731
|
-
ttyFd =
|
|
26732
|
-
const w = (line) =>
|
|
26978
|
+
ttyFd = import_fs35.default.openSync("/dev/tty", "w");
|
|
26979
|
+
const w = (line) => import_fs35.default.writeSync(ttyFd, line + "\n");
|
|
26733
26980
|
w(import_chalk2.default.yellow(`
|
|
26734
26981
|
\u26A0\uFE0F Node9: installed skill drift detected`));
|
|
26735
26982
|
w(import_chalk2.default.gray(` ${detail}`));
|
|
@@ -26744,7 +26991,7 @@ RAW: ${raw}
|
|
|
26744
26991
|
} finally {
|
|
26745
26992
|
if (ttyFd !== null)
|
|
26746
26993
|
try {
|
|
26747
|
-
|
|
26994
|
+
import_fs35.default.closeSync(ttyFd);
|
|
26748
26995
|
} catch {
|
|
26749
26996
|
}
|
|
26750
26997
|
}
|
|
@@ -26760,7 +27007,7 @@ RAW: ${raw}
|
|
|
26760
27007
|
return;
|
|
26761
27008
|
}
|
|
26762
27009
|
if (!flag || flag.state !== "verified" && flag.state !== "warned") {
|
|
26763
|
-
const absoluteCwd = typeof payloadCwd === "string" &&
|
|
27010
|
+
const absoluteCwd = typeof payloadCwd === "string" && import_path37.default.isAbsolute(payloadCwd) ? payloadCwd : void 0;
|
|
26764
27011
|
const extraRoots = skillPinCfg.roots;
|
|
26765
27012
|
const resolvedExtra = extraRoots.map((r) => resolveUserSkillRoot(r, absoluteCwd)).filter((r) => typeof r === "string");
|
|
26766
27013
|
const roots = [...defaultSkillRoots(absoluteCwd), ...resolvedExtra];
|
|
@@ -26799,31 +27046,27 @@ RAW: ${raw}
|
|
|
26799
27046
|
} else {
|
|
26800
27047
|
writeFlag({ state: "verified" });
|
|
26801
27048
|
}
|
|
26802
|
-
|
|
26803
|
-
|
|
26804
|
-
|
|
26805
|
-
const
|
|
26806
|
-
|
|
26807
|
-
|
|
26808
|
-
} catch {
|
|
26809
|
-
}
|
|
27049
|
+
if (memo)
|
|
27050
|
+
try {
|
|
27051
|
+
const cutoff = Date.now() - 7 * 24 * 60 * 60 * 1e3;
|
|
27052
|
+
for (const name of import_fs35.default.readdirSync(sessionsDir))
|
|
27053
|
+
removeOldFlag(sessionsDir, memo, name, cutoff);
|
|
27054
|
+
} catch {
|
|
26810
27055
|
}
|
|
26811
|
-
} catch {
|
|
26812
|
-
}
|
|
26813
27056
|
}
|
|
26814
27057
|
} catch (err) {
|
|
26815
27058
|
if (process.env.NODE9_DEBUG === "1") {
|
|
26816
27059
|
try {
|
|
26817
|
-
const dbg =
|
|
27060
|
+
const dbg = import_path37.default.join(import_os27.default.homedir(), ".node9", "hook-debug.log");
|
|
26818
27061
|
const msg = err instanceof Error ? err.message : String(err);
|
|
26819
|
-
|
|
27062
|
+
import_fs35.default.appendFileSync(dbg, `[${(/* @__PURE__ */ new Date()).toISOString()}] SKILL_PIN_ERROR: ${msg}
|
|
26820
27063
|
`);
|
|
26821
27064
|
} catch {
|
|
26822
27065
|
}
|
|
26823
27066
|
}
|
|
26824
27067
|
}
|
|
26825
27068
|
}
|
|
26826
|
-
const safeCwdForAuth = typeof payloadCwd === "string" &&
|
|
27069
|
+
const safeCwdForAuth = typeof payloadCwd === "string" && import_path37.default.isAbsolute(payloadCwd) ? payloadCwd : void 0;
|
|
26827
27070
|
const askMode = resolveAskMode(agent, opts, config);
|
|
26828
27071
|
const result = await authorizeHeadless(toolName, toolInput, meta, {
|
|
26829
27072
|
cwd: safeCwdForAuth,
|
|
@@ -26836,11 +27079,21 @@ RAW: ${raw}
|
|
|
26836
27079
|
} catch {
|
|
26837
27080
|
}
|
|
26838
27081
|
}
|
|
27082
|
+
const isSubagentCall = typeof payload.agent_id === "string" && payload.agent_id !== "";
|
|
27083
|
+
const allowExit = (checkedBy) => {
|
|
27084
|
+
if (checkedBy !== "paused" && agent === "Claude Code" && !isSubagentCall && sessionNoticeEnabled() && claimSessionNotice(safeSessionId)) {
|
|
27085
|
+
try {
|
|
27086
|
+
import_fs35.default.writeSync(1, sessionNoticeOutput(config.settings.mode) + "\n");
|
|
27087
|
+
} catch {
|
|
27088
|
+
}
|
|
27089
|
+
}
|
|
27090
|
+
process.exit(0);
|
|
27091
|
+
};
|
|
26839
27092
|
if (result.approved) {
|
|
26840
27093
|
if (result.checkedBy && process.env.NODE9_DEBUG === "1")
|
|
26841
27094
|
process.stderr.write(`\u2713 node9 [${result.checkedBy}]: "${toolName}" allowed
|
|
26842
27095
|
`);
|
|
26843
|
-
|
|
27096
|
+
allowExit(result.checkedBy);
|
|
26844
27097
|
}
|
|
26845
27098
|
if (result.review) {
|
|
26846
27099
|
sendAsk(result);
|
|
@@ -26848,12 +27101,12 @@ RAW: ${raw}
|
|
|
26848
27101
|
}
|
|
26849
27102
|
if (result.noApprovalMechanism && !isDaemonRunning() && !process.env.NODE9_NO_AUTO_DAEMON && !process.stdout.isTTY && config.settings.autoStartDaemon) {
|
|
26850
27103
|
try {
|
|
26851
|
-
const tty =
|
|
26852
|
-
|
|
27104
|
+
const tty = import_fs35.default.openSync("/dev/tty", "w");
|
|
27105
|
+
import_fs35.default.writeSync(
|
|
26853
27106
|
tty,
|
|
26854
27107
|
import_chalk2.default.cyan("\n\u{1F6E1}\uFE0F Node9: Starting approval daemon automatically...\n")
|
|
26855
27108
|
);
|
|
26856
|
-
|
|
27109
|
+
import_fs35.default.closeSync(tty);
|
|
26857
27110
|
} catch {
|
|
26858
27111
|
}
|
|
26859
27112
|
const daemonReady = await autoStartDaemonAndWait();
|
|
@@ -26865,7 +27118,7 @@ RAW: ${raw}
|
|
|
26865
27118
|
if (retry.checkedBy && process.env.NODE9_DEBUG === "1")
|
|
26866
27119
|
process.stderr.write(`\u2713 node9 [${retry.checkedBy}]: "${toolName}" allowed
|
|
26867
27120
|
`);
|
|
26868
|
-
|
|
27121
|
+
allowExit(retry.checkedBy);
|
|
26869
27122
|
}
|
|
26870
27123
|
sendBlock(retry.reason ?? `Node9 blocked "${toolName}".`, {
|
|
26871
27124
|
...retry,
|
|
@@ -26880,9 +27133,9 @@ RAW: ${raw}
|
|
|
26880
27133
|
});
|
|
26881
27134
|
} catch (err) {
|
|
26882
27135
|
if (process.env.NODE9_DEBUG === "1") {
|
|
26883
|
-
const logPath =
|
|
27136
|
+
const logPath = import_path37.default.join(import_os27.default.homedir(), ".node9", "hook-debug.log");
|
|
26884
27137
|
const errMsg = err instanceof Error ? err.message : String(err);
|
|
26885
|
-
|
|
27138
|
+
import_fs35.default.appendFileSync(logPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] ERROR: ${errMsg}
|
|
26886
27139
|
`);
|
|
26887
27140
|
}
|
|
26888
27141
|
process.exit(0);
|
|
@@ -26917,9 +27170,9 @@ RAW: ${raw}
|
|
|
26917
27170
|
|
|
26918
27171
|
// src/cli/commands/log.ts
|
|
26919
27172
|
init_cjs_shims();
|
|
26920
|
-
var
|
|
26921
|
-
var
|
|
26922
|
-
var
|
|
27173
|
+
var import_fs36 = __toESM(require("fs"));
|
|
27174
|
+
var import_path38 = __toESM(require("path"));
|
|
27175
|
+
var import_os28 = __toESM(require("os"));
|
|
26923
27176
|
init_audit();
|
|
26924
27177
|
init_dist();
|
|
26925
27178
|
|
|
@@ -27187,12 +27440,12 @@ function registerLogCommand(program2) {
|
|
|
27187
27440
|
if (rawToolName !== tool) entry.agentToolName = rawToolName;
|
|
27188
27441
|
const payloadSessionId = payload.session_id ?? payload.conversationId;
|
|
27189
27442
|
if (payloadSessionId) entry.sessionId = payloadSessionId;
|
|
27190
|
-
const logPath =
|
|
27191
|
-
if (!
|
|
27192
|
-
|
|
27193
|
-
|
|
27443
|
+
const logPath = import_path38.default.join(import_os28.default.homedir(), ".node9", "audit.log");
|
|
27444
|
+
if (!import_fs36.default.existsSync(import_path38.default.dirname(logPath)))
|
|
27445
|
+
import_fs36.default.mkdirSync(import_path38.default.dirname(logPath), { recursive: true });
|
|
27446
|
+
import_fs36.default.appendFileSync(logPath, JSON.stringify(entry) + "\n");
|
|
27194
27447
|
const payloadCwd = typeof payload.cwd === "string" ? payload.cwd : Array.isArray(payload.workspacePaths) && typeof payload.workspacePaths[0] === "string" ? payload.workspacePaths[0] : void 0;
|
|
27195
|
-
const safeCwd = typeof payloadCwd === "string" &&
|
|
27448
|
+
const safeCwd = typeof payloadCwd === "string" && import_path38.default.isAbsolute(payloadCwd) ? payloadCwd : void 0;
|
|
27196
27449
|
if ((tool === "Bash" || tool === "bash") && isDaemonRunning()) {
|
|
27197
27450
|
const command = typeof rawInput === "object" && rawInput !== null && "command" in rawInput && typeof rawInput.command === "string" ? rawInput.command : null;
|
|
27198
27451
|
if (command) {
|
|
@@ -27413,9 +27666,9 @@ function registerLogCommand(program2) {
|
|
|
27413
27666
|
const msg = err instanceof Error ? err.message : String(err);
|
|
27414
27667
|
process.stderr.write(`[Node9] audit log error: ${msg}
|
|
27415
27668
|
`);
|
|
27416
|
-
const debugPath =
|
|
27669
|
+
const debugPath = import_path38.default.join(import_os28.default.homedir(), ".node9", "hook-debug.log");
|
|
27417
27670
|
try {
|
|
27418
|
-
|
|
27671
|
+
import_fs36.default.appendFileSync(debugPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] LOG_ERROR: ${msg}
|
|
27419
27672
|
`);
|
|
27420
27673
|
} catch {
|
|
27421
27674
|
}
|
|
@@ -27438,9 +27691,9 @@ function registerLogCommand(program2) {
|
|
|
27438
27691
|
});
|
|
27439
27692
|
}
|
|
27440
27693
|
function absolutePathFor(spelled, cwd) {
|
|
27441
|
-
const expanded = spelled === "~" || spelled.startsWith("~/") ?
|
|
27442
|
-
if (
|
|
27443
|
-
return cwd ?
|
|
27694
|
+
const expanded = spelled === "~" || spelled.startsWith("~/") ? import_path38.default.join(import_os28.default.homedir(), spelled.slice(1)) : spelled;
|
|
27695
|
+
if (import_path38.default.isAbsolute(expanded)) return expanded;
|
|
27696
|
+
return cwd ? import_path38.default.resolve(cwd, expanded) : import_path38.default.resolve(expanded);
|
|
27444
27697
|
}
|
|
27445
27698
|
async function carryTaintAcrossWrites(command, cwd) {
|
|
27446
27699
|
let pairs;
|
|
@@ -27459,13 +27712,13 @@ async function carryTaintAcrossWrites(command, cwd) {
|
|
|
27459
27712
|
continue;
|
|
27460
27713
|
}
|
|
27461
27714
|
const sensitive = scanFilePath(src, cwd);
|
|
27462
|
-
if (sensitive) await notifyTaint(dst, `copy:${sensitive.patternName}:${
|
|
27715
|
+
if (sensitive) await notifyTaint(dst, `copy:${sensitive.patternName}:${import_path38.default.basename(src)}`);
|
|
27463
27716
|
}
|
|
27464
27717
|
}
|
|
27465
27718
|
|
|
27466
27719
|
// src/hook-entry.ts
|
|
27467
27720
|
var { version } = JSON.parse(
|
|
27468
|
-
|
|
27721
|
+
import_fs37.default.readFileSync(import_path39.default.join(__dirname, "../package.json"), "utf-8")
|
|
27469
27722
|
);
|
|
27470
27723
|
var program = new import_commander.Command();
|
|
27471
27724
|
program.name("node9").description("The Sudo Command for AI Agents").version(version);
|
|
@@ -27474,9 +27727,9 @@ registerLogCommand(program);
|
|
|
27474
27727
|
process.on("unhandledRejection", (reason) => {
|
|
27475
27728
|
if (process.argv[2] === "check") {
|
|
27476
27729
|
if (process.env.NODE9_DEBUG === "1" || getConfig().settings.enableHookLogDebug) {
|
|
27477
|
-
const logPath =
|
|
27730
|
+
const logPath = import_path39.default.join(import_os29.default.homedir(), ".node9", "hook-debug.log");
|
|
27478
27731
|
const msg = reason instanceof Error ? reason.message : String(reason);
|
|
27479
|
-
|
|
27732
|
+
import_fs37.default.appendFileSync(logPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] UNHANDLED: ${msg}
|
|
27480
27733
|
`);
|
|
27481
27734
|
}
|
|
27482
27735
|
process.exit(0);
|