@node9/proxy 2.32.0 → 2.33.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 +780 -452
- package/dist/cli.mjs +762 -434
- package/dist/dashboard.mjs +369 -29
- package/dist/index.js +583 -325
- package/dist/index.mjs +583 -325
- package/dist/scan-ink.mjs +202 -190
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -2158,32 +2158,109 @@ function evaluateSmartConditions(args, rule) {
|
|
|
2158
2158
|
function isBashTool(tool) {
|
|
2159
2159
|
return BASH_TOOL_NAMES.has(tool.toLowerCase());
|
|
2160
2160
|
}
|
|
2161
|
-
function
|
|
2161
|
+
function inspectionLayers(policy) {
|
|
2162
|
+
return { trusted: policy.toolInspection, project: policy.projectToolInspection };
|
|
2163
|
+
}
|
|
2164
|
+
function isShellEntry(value) {
|
|
2165
|
+
return typeof value === "object" && value !== null && value.kind === "shell" && typeof value.field === "string" && Object.keys(value).length === 2;
|
|
2166
|
+
}
|
|
2167
|
+
function isInspectionLayers(input) {
|
|
2168
|
+
const trusted = input.trusted;
|
|
2169
|
+
return typeof trusted === "object" && trusted !== null && !isShellEntry(trusted);
|
|
2170
|
+
}
|
|
2171
|
+
function layersOf(input) {
|
|
2172
|
+
return isInspectionLayers(input) ? { trusted: input.trusted, project: input.project ?? {} } : { trusted: input, project: {} };
|
|
2173
|
+
}
|
|
2174
|
+
function entryOf(tool, inspection) {
|
|
2162
2175
|
const pattern = Object.keys(inspection).find((p) => matchesPattern(tool, p));
|
|
2163
2176
|
return pattern === void 0 ? void 0 : inspection[pattern];
|
|
2164
2177
|
}
|
|
2178
|
+
function inspectionEntry(tool, inspection = {}) {
|
|
2179
|
+
const { trusted, project } = layersOf(inspection);
|
|
2180
|
+
return entryOf(tool, trusted) ?? entryOf(tool, project);
|
|
2181
|
+
}
|
|
2182
|
+
function executorName(tool) {
|
|
2183
|
+
const name = tool.toLowerCase();
|
|
2184
|
+
if (!name.startsWith("mcp__")) return name;
|
|
2185
|
+
const at = name.lastIndexOf("__");
|
|
2186
|
+
return at >= "mcp__".length ? name.slice(at + 2) : name;
|
|
2187
|
+
}
|
|
2188
|
+
function entryFields(entry) {
|
|
2189
|
+
if (typeof entry === "object") return [entry.field];
|
|
2190
|
+
return entry === "command" ? ["command"] : null;
|
|
2191
|
+
}
|
|
2165
2192
|
function shellFields(tool, inspection) {
|
|
2166
2193
|
if (isBashTool(tool)) return ["command"];
|
|
2167
|
-
const
|
|
2168
|
-
|
|
2169
|
-
if (
|
|
2170
|
-
const
|
|
2171
|
-
|
|
2194
|
+
const { trusted, project } = layersOf(inspection);
|
|
2195
|
+
const trustedEntry = entryOf(tool, trusted);
|
|
2196
|
+
if (trustedEntry !== void 0) return entryFields(trustedEntry);
|
|
2197
|
+
const builtin = MCP_EXECUTORS.has(executorName(tool)) ? BUILTIN_FIELDS : null;
|
|
2198
|
+
const projectEntry = entryOf(tool, project);
|
|
2199
|
+
if (projectEntry === void 0) return builtin;
|
|
2200
|
+
const added = entryFields(projectEntry);
|
|
2201
|
+
if (!builtin) return added;
|
|
2202
|
+
if (!added) return builtin;
|
|
2203
|
+
return [.../* @__PURE__ */ new Set([...builtin, ...added])];
|
|
2172
2204
|
}
|
|
2173
2205
|
function isShellShapedTool(tool, inspection = {}) {
|
|
2174
2206
|
return shellFields(tool, inspection) !== null;
|
|
2175
2207
|
}
|
|
2208
|
+
function shellWord(word) {
|
|
2209
|
+
if (BARE_WORD.test(word)) return word;
|
|
2210
|
+
return word === "" ? "''" : `'${word.replace(/'/g, `'\\''`)}'`;
|
|
2211
|
+
}
|
|
2212
|
+
function isStringArray(value) {
|
|
2213
|
+
return Array.isArray(value) && value.every((item) => typeof item === "string");
|
|
2214
|
+
}
|
|
2215
|
+
function commandLine(args, fieldPath, value, native) {
|
|
2216
|
+
const missing = { reason: "Shell command field is missing or is not a string" };
|
|
2217
|
+
const unreadable = { reason: "Shell command arguments could not be read" };
|
|
2218
|
+
if (typeof value !== "string" && (native || !Array.isArray(value))) return missing;
|
|
2219
|
+
const dot = fieldPath.lastIndexOf(".");
|
|
2220
|
+
const parent = dot === -1 ? args : getNestedValue(args, fieldPath.slice(0, dot));
|
|
2221
|
+
const siblings = ARGV_FIELDS.map((key) => ({
|
|
2222
|
+
key,
|
|
2223
|
+
value: parent && typeof parent === "object" ? parent[key] : void 0
|
|
2224
|
+
})).filter(({ value: value2 }) => value2 !== void 0 && value2 !== null);
|
|
2225
|
+
let argv = siblings[0];
|
|
2226
|
+
if (native) argv = siblings.find(({ value: v }) => isStringArray(v));
|
|
2227
|
+
else if (siblings.length === 2 && JSON.stringify(siblings[0].value) !== JSON.stringify(siblings[1].value))
|
|
2228
|
+
return { reason: "Shell command arguments disagree" };
|
|
2229
|
+
let command;
|
|
2230
|
+
let source = fieldPath;
|
|
2231
|
+
if (Array.isArray(value)) {
|
|
2232
|
+
if (argv || !value.length || !isStringArray(value)) return unreadable;
|
|
2233
|
+
command = value.map(shellWord).join(" ");
|
|
2234
|
+
} else if (!argv) {
|
|
2235
|
+
return { command: value, fieldPath };
|
|
2236
|
+
} else {
|
|
2237
|
+
if (!isStringArray(argv.value)) return unreadable;
|
|
2238
|
+
if (!argv.value.length) return { command: value, fieldPath };
|
|
2239
|
+
command = `${value} ${argv.value.map(shellWord).join(" ")}`;
|
|
2240
|
+
source = `${fieldPath}+${argv.key}`;
|
|
2241
|
+
}
|
|
2242
|
+
if (command.length > MAX_ARGV_CHARACTERS)
|
|
2243
|
+
return { reason: "Shell command arguments exceed the inspectable size" };
|
|
2244
|
+
return { command, fieldPath: source };
|
|
2245
|
+
}
|
|
2176
2246
|
function resolveShellInput(tool, args, inspection = {}) {
|
|
2177
2247
|
const fields = shellFields(tool, inspection);
|
|
2178
2248
|
if (!fields) return { kind: "not-shell" };
|
|
2179
2249
|
const values = fields.map((fieldPath) => ({ fieldPath, value: getNestedValue(args, fieldPath) })).filter(({ value }) => value !== void 0 && value !== null && value !== "");
|
|
2180
|
-
if (!values.length
|
|
2250
|
+
if (!values.length) {
|
|
2181
2251
|
return { kind: "uninspectable", reason: "Shell command field is missing or is not a string" };
|
|
2182
2252
|
}
|
|
2183
|
-
|
|
2253
|
+
const native = isBashTool(tool);
|
|
2254
|
+
const lines = values.map(({ fieldPath, value }) => commandLine(args, fieldPath, value, native));
|
|
2255
|
+
const failed = lines.find((line) => "reason" in line);
|
|
2256
|
+
if (failed) return { kind: "uninspectable", reason: failed.reason };
|
|
2257
|
+
const commands = lines.filter(
|
|
2258
|
+
(line) => "command" in line
|
|
2259
|
+
);
|
|
2260
|
+
if (new Set(commands.map(({ command }) => command)).size !== 1) {
|
|
2184
2261
|
return { kind: "uninspectable", reason: "Multiple shell command fields disagree" };
|
|
2185
2262
|
}
|
|
2186
|
-
return { kind: "shell",
|
|
2263
|
+
return { kind: "shell", ...commands[0] };
|
|
2187
2264
|
}
|
|
2188
2265
|
function shellRuleArgs(tool, args, ruleTool, inspection = {}) {
|
|
2189
2266
|
if (!ruleTool || !matchesPattern("bash", ruleTool) || matchesPattern(tool, ruleTool)) return args;
|
|
@@ -2924,20 +3001,25 @@ function evaluateProtectedFileAccess(accesses, context, settings) {
|
|
|
2924
3001
|
}
|
|
2925
3002
|
return best;
|
|
2926
3003
|
}
|
|
2927
|
-
function findMnemonic(text, budget = {
|
|
2928
|
-
const words = [...text.matchAll(/[a-zA-Z]+/g)];
|
|
3004
|
+
function findMnemonic(text, budget = { hashes: 0, reasons: /* @__PURE__ */ new Set() }) {
|
|
2929
3005
|
let run = [];
|
|
3006
|
+
let runIndex = [];
|
|
2930
3007
|
let previousEnd = 0;
|
|
2931
3008
|
const rejected = /* @__PURE__ */ new Set();
|
|
2932
3009
|
const inspect = () => {
|
|
3010
|
+
if (run.length < 12) return false;
|
|
3011
|
+
const ascending = new Array(run.length).fill(0);
|
|
3012
|
+
for (let i = 1; i < run.length; i++)
|
|
3013
|
+
ascending[i] = ascending[i - 1] + (runIndex[i] > runIndex[i - 1] ? 1 : 0);
|
|
2933
3014
|
for (let start = 0; start + 12 <= run.length; start++) {
|
|
2934
3015
|
for (const count of lengths) {
|
|
2935
3016
|
if (start + count > run.length) continue;
|
|
3017
|
+
if (ascending[start + count - 1] - ascending[start] === count - 1) continue;
|
|
2936
3018
|
const candidate = run.slice(start, start + count);
|
|
2937
3019
|
const sentence = candidate.join(" ");
|
|
2938
3020
|
if (rejected.has(sentence)) continue;
|
|
2939
|
-
if (++budget.
|
|
2940
|
-
budget.reasons.add("mnemonic-
|
|
3021
|
+
if (++budget.hashes > MAX_MNEMONIC_HASHES) {
|
|
3022
|
+
budget.reasons.add("mnemonic-budget");
|
|
2941
3023
|
return false;
|
|
2942
3024
|
}
|
|
2943
3025
|
if (BIP39_PUBLIC_VECTORS.has(hash(sentence).toString("hex"))) {
|
|
@@ -2955,13 +3037,18 @@ function findMnemonic(text, budget = { candidates: 0, reasons: /* @__PURE__ */ n
|
|
|
2955
3037
|
}
|
|
2956
3038
|
return false;
|
|
2957
3039
|
};
|
|
2958
|
-
for (const word of
|
|
2959
|
-
|
|
3040
|
+
for (const word of text.matchAll(/[a-zA-Z]+/g)) {
|
|
3041
|
+
const index = indices.get(word[0]);
|
|
3042
|
+
if (index === void 0 || run.length && !/^\s+$/.test(text.slice(previousEnd, word.index))) {
|
|
2960
3043
|
if (inspect()) return true;
|
|
2961
|
-
if (budget.reasons.has("mnemonic-
|
|
3044
|
+
if (budget.reasons.has("mnemonic-budget")) return false;
|
|
2962
3045
|
run = [];
|
|
3046
|
+
runIndex = [];
|
|
3047
|
+
}
|
|
3048
|
+
if (index !== void 0) {
|
|
3049
|
+
run.push(word[0]);
|
|
3050
|
+
runIndex.push(index);
|
|
2963
3051
|
}
|
|
2964
|
-
if (indices.has(word[0])) run.push(word[0]);
|
|
2965
3052
|
previousEnd = word.index + word[0].length;
|
|
2966
3053
|
}
|
|
2967
3054
|
return inspect();
|
|
@@ -3092,6 +3179,9 @@ function validateCask(raw) {
|
|
|
3092
3179
|
const second = b64urlIndex(s[tsAt + 5]);
|
|
3093
3180
|
return month <= 11 && day <= 30 && hour <= 23 && minute <= 59 && second <= 59;
|
|
3094
3181
|
}
|
|
3182
|
+
function isInvisibleChar(char) {
|
|
3183
|
+
return INVISIBLE_ONE.test(char);
|
|
3184
|
+
}
|
|
3095
3185
|
function stripInvisible(text) {
|
|
3096
3186
|
return text.replace(INVISIBLE_RE, "");
|
|
3097
3187
|
}
|
|
@@ -3278,6 +3368,45 @@ function matchArgumentText(text, fieldPath, budget) {
|
|
|
3278
3368
|
}
|
|
3279
3369
|
return best;
|
|
3280
3370
|
}
|
|
3371
|
+
function matchArgumentBatch(fragments, fieldPath, budget) {
|
|
3372
|
+
if (!fragments.length) return null;
|
|
3373
|
+
const text = fragments.join(BATCH_SEPARATOR);
|
|
3374
|
+
const lower2 = text.toLowerCase();
|
|
3375
|
+
let best = null;
|
|
3376
|
+
for (const pattern of DLP_PATTERNS) {
|
|
3377
|
+
if (pattern.keywords && !pattern.keywords.some((kw) => lower2.includes(kw.toLowerCase())))
|
|
3378
|
+
continue;
|
|
3379
|
+
const flags = pattern.regex.flags.replace(/[gmy]/g, "");
|
|
3380
|
+
if (!new RegExp(pattern.regex.source, flags + "m").test(text)) continue;
|
|
3381
|
+
for (const fragment of fragments) {
|
|
3382
|
+
if (firstAcceptedMatch(pattern, fragment) === null) continue;
|
|
3383
|
+
const severity = pattern.contextBoost && isAssignmentContext(fragment) ? "block" : pattern.severity;
|
|
3384
|
+
if (severity === "block")
|
|
3385
|
+
return {
|
|
3386
|
+
patternName: pattern.name,
|
|
3387
|
+
fieldPath,
|
|
3388
|
+
redactedSample: "[encoded secret]",
|
|
3389
|
+
severity
|
|
3390
|
+
};
|
|
3391
|
+
best ??= {
|
|
3392
|
+
patternName: pattern.name,
|
|
3393
|
+
fieldPath,
|
|
3394
|
+
redactedSample: "[encoded secret]",
|
|
3395
|
+
severity
|
|
3396
|
+
};
|
|
3397
|
+
if (!pattern.contextBoost) break;
|
|
3398
|
+
}
|
|
3399
|
+
}
|
|
3400
|
+
if (findMnemonic(text, budget)) {
|
|
3401
|
+
return {
|
|
3402
|
+
patternName: "BIP-39 Recovery Phrase",
|
|
3403
|
+
fieldPath,
|
|
3404
|
+
redactedSample: "[recovery phrase]",
|
|
3405
|
+
severity: "block"
|
|
3406
|
+
};
|
|
3407
|
+
}
|
|
3408
|
+
return best;
|
|
3409
|
+
}
|
|
3281
3410
|
function scanArgs(args, depth = 0, fieldPath = "args") {
|
|
3282
3411
|
if (depth > MAX_DEPTH2 || args === null || args === void 0) return null;
|
|
3283
3412
|
if (Array.isArray(args)) {
|
|
@@ -4149,6 +4278,9 @@ function ssrfDestinationFloor(toolName, args, opts = {}) {
|
|
|
4149
4278
|
return null;
|
|
4150
4279
|
}
|
|
4151
4280
|
}
|
|
4281
|
+
function newSecretViewBudget() {
|
|
4282
|
+
return { hashes: 0, nestedBytes: 0, joinedLabelBytes: 0, reasons: /* @__PURE__ */ new Set() };
|
|
4283
|
+
}
|
|
4152
4284
|
function textBytes(buf) {
|
|
4153
4285
|
if (buf.length < 16) return null;
|
|
4154
4286
|
const text = buf.toString("utf8");
|
|
@@ -4167,13 +4299,41 @@ function percentOnce(text) {
|
|
|
4167
4299
|
}
|
|
4168
4300
|
});
|
|
4169
4301
|
}
|
|
4302
|
+
function* wrappedBase64(text) {
|
|
4303
|
+
if (!text.includes("\n")) return;
|
|
4304
|
+
const word = /^[A-Za-z0-9+/_-]+={0,2}$/;
|
|
4305
|
+
let run = [];
|
|
4306
|
+
let width = 0;
|
|
4307
|
+
const flush = function* () {
|
|
4308
|
+
if (run.length >= 2) yield run.join("");
|
|
4309
|
+
run = [];
|
|
4310
|
+
width = 0;
|
|
4311
|
+
};
|
|
4312
|
+
for (const raw of text.split("\n")) {
|
|
4313
|
+
const line = raw.trim();
|
|
4314
|
+
const full = line.length >= 16 && word.test(line) && !line.includes("=");
|
|
4315
|
+
if (full && (!run.length || line.length === width)) {
|
|
4316
|
+
run.push(line);
|
|
4317
|
+
width = line.length;
|
|
4318
|
+
continue;
|
|
4319
|
+
}
|
|
4320
|
+
if (run.length && word.test(line) && line.length >= 2 && line.length <= width) {
|
|
4321
|
+
run.push(line);
|
|
4322
|
+
yield* flush();
|
|
4323
|
+
continue;
|
|
4324
|
+
}
|
|
4325
|
+
yield* flush();
|
|
4326
|
+
if (full) {
|
|
4327
|
+
run.push(line);
|
|
4328
|
+
width = line.length;
|
|
4329
|
+
}
|
|
4330
|
+
}
|
|
4331
|
+
yield* flush();
|
|
4332
|
+
}
|
|
4170
4333
|
function* encodedRegions(text, budget) {
|
|
4171
4334
|
yield [text, false];
|
|
4335
|
+
for (const block of wrappedBase64(text)) yield [block, false];
|
|
4172
4336
|
for (const match of text.matchAll(/(?<![a-z0-9+.-])[a-z][a-z0-9+.-]{0,30}:\/\/[^\s<>"']+/gi)) {
|
|
4173
|
-
if (++budget.candidates > SECRET_VIEW_LIMITS.maxCandidates) {
|
|
4174
|
-
budget.reasons.add("decode-candidates");
|
|
4175
|
-
return;
|
|
4176
|
-
}
|
|
4177
4337
|
try {
|
|
4178
4338
|
new URL(match[0]);
|
|
4179
4339
|
} catch {
|
|
@@ -4193,6 +4353,8 @@ function* encodedRegions(text, budget) {
|
|
|
4193
4353
|
];
|
|
4194
4354
|
for (const region of regions) if (region) yield [region, false];
|
|
4195
4355
|
const labels = host2.split(".");
|
|
4356
|
+
if (host2.length > SECRET_VIEW_LIMITS.maxHostLength || labels.length > SECRET_VIEW_LIMITS.maxLabels || !labels.every((l) => l.length >= 1 && l.length <= SECRET_VIEW_LIMITS.maxLabelLength))
|
|
4357
|
+
continue;
|
|
4196
4358
|
for (let start = 0; start < labels.length; start++) {
|
|
4197
4359
|
let joined = labels[start];
|
|
4198
4360
|
for (let end = start + 1; end < Math.min(labels.length, start + 8); end++) {
|
|
@@ -4200,11 +4362,13 @@ function* encodedRegions(text, budget) {
|
|
|
4200
4362
|
if (!run.every((s) => /^[a-f0-9]{2,}$/i.test(s)) && !run.every((s) => /^[A-Z2-7]{4,}$/i.test(s)))
|
|
4201
4363
|
break;
|
|
4202
4364
|
joined += labels[end];
|
|
4203
|
-
if (
|
|
4204
|
-
|
|
4365
|
+
if (joined.length < 16) continue;
|
|
4366
|
+
budget.joinedLabelBytes += joined.length;
|
|
4367
|
+
if (budget.joinedLabelBytes > SECRET_VIEW_LIMITS.maxJoinedLabelBytes) {
|
|
4368
|
+
budget.reasons.add("decode-labels");
|
|
4205
4369
|
return;
|
|
4206
4370
|
}
|
|
4207
|
-
|
|
4371
|
+
yield [joined, true];
|
|
4208
4372
|
}
|
|
4209
4373
|
}
|
|
4210
4374
|
}
|
|
@@ -4241,10 +4405,6 @@ function* decodedCandidates(text, budget) {
|
|
|
4241
4405
|
if (joined) yield ["dns-labels", region];
|
|
4242
4406
|
for (const [kind, re] of families) {
|
|
4243
4407
|
for (const match of region.matchAll(re)) {
|
|
4244
|
-
if (++budget.candidates > SECRET_VIEW_LIMITS.maxCandidates) {
|
|
4245
|
-
budget.reasons.add("decode-candidates");
|
|
4246
|
-
return;
|
|
4247
|
-
}
|
|
4248
4408
|
const raw = match[0];
|
|
4249
4409
|
let buf;
|
|
4250
4410
|
if (kind === "base64") {
|
|
@@ -4267,42 +4427,76 @@ function* decodedCandidates(text, budget) {
|
|
|
4267
4427
|
}
|
|
4268
4428
|
}
|
|
4269
4429
|
}
|
|
4270
|
-
function
|
|
4430
|
+
function secretViews(text, budget) {
|
|
4271
4431
|
const seen = /* @__PURE__ */ new Set([text]);
|
|
4272
|
-
const
|
|
4273
|
-
|
|
4274
|
-
|
|
4275
|
-
|
|
4276
|
-
for (const
|
|
4277
|
-
|
|
4278
|
-
|
|
4279
|
-
|
|
4280
|
-
|
|
4281
|
-
|
|
4282
|
-
|
|
4283
|
-
|
|
4284
|
-
|
|
4285
|
-
|
|
4432
|
+
const out = [];
|
|
4433
|
+
let frontier = [{ text, transforms: [] }];
|
|
4434
|
+
for (let depth = 1; depth <= SECRET_VIEW_LIMITS.maxDepth; depth++) {
|
|
4435
|
+
const next = [];
|
|
4436
|
+
for (const parent of frontier) {
|
|
4437
|
+
let perLeaf = 0;
|
|
4438
|
+
for (const [kind, decoded] of decodedCandidates(parent.text, budget)) {
|
|
4439
|
+
if (seen.has(decoded)) continue;
|
|
4440
|
+
seen.add(decoded);
|
|
4441
|
+
const normalizing = kind === "invisibles" || kind === "percent";
|
|
4442
|
+
if (depth >= 2 && !normalizing) {
|
|
4443
|
+
budget.nestedBytes += Buffer.byteLength(decoded);
|
|
4444
|
+
if (++perLeaf > SECRET_VIEW_LIMITS.maxNestedViewsPerLeaf || budget.nestedBytes > SECRET_VIEW_LIMITS.maxNestedBytes) {
|
|
4445
|
+
budget.reasons.add("decode-nested");
|
|
4446
|
+
return out;
|
|
4447
|
+
}
|
|
4448
|
+
}
|
|
4449
|
+
const view = { text: decoded, transforms: [...parent.transforms, kind] };
|
|
4450
|
+
out.push(view);
|
|
4451
|
+
next.push(view);
|
|
4286
4452
|
}
|
|
4287
|
-
seen.add(decoded);
|
|
4288
|
-
budget.views++;
|
|
4289
|
-
budget.bytes += bytes;
|
|
4290
|
-
queue.push({ text: decoded, transforms: [...view.transforms, kind] });
|
|
4291
4453
|
}
|
|
4292
|
-
|
|
4454
|
+
frontier = next;
|
|
4455
|
+
if (!frontier.length) return out;
|
|
4456
|
+
}
|
|
4457
|
+
for (const view of frontier) {
|
|
4458
|
+
if (!decodedCandidates(view.text, budget).next().done) {
|
|
4459
|
+
budget.reasons.add("decode-nested");
|
|
4460
|
+
break;
|
|
4461
|
+
}
|
|
4293
4462
|
}
|
|
4463
|
+
return out;
|
|
4464
|
+
}
|
|
4465
|
+
function batches(views) {
|
|
4466
|
+
const out = [];
|
|
4467
|
+
let current = [];
|
|
4468
|
+
let size = 0;
|
|
4469
|
+
for (const { text } of views) {
|
|
4470
|
+
if (current.length && size + text.length > SECRET_VIEW_LIMITS.batchCharacters) {
|
|
4471
|
+
out.push(current);
|
|
4472
|
+
current = [];
|
|
4473
|
+
size = 0;
|
|
4474
|
+
}
|
|
4475
|
+
current.push(text);
|
|
4476
|
+
size += text.length;
|
|
4477
|
+
}
|
|
4478
|
+
if (current.length) out.push(current);
|
|
4479
|
+
return out;
|
|
4294
4480
|
}
|
|
4295
4481
|
function inspectArgumentSecrets(args) {
|
|
4296
|
-
const budget =
|
|
4482
|
+
const budget = newSecretViewBudget();
|
|
4297
4483
|
const ancestors = /* @__PURE__ */ new Set();
|
|
4298
4484
|
let nodes = 0;
|
|
4299
4485
|
let sourceBytes = 0;
|
|
4300
4486
|
let tailBytes = 0;
|
|
4301
4487
|
let best = null;
|
|
4488
|
+
const blocked = () => best?.severity === "block";
|
|
4302
4489
|
const accept = (match) => {
|
|
4303
4490
|
if (match && (!best || best.severity !== "block" && match.severity === "block")) best = match;
|
|
4304
4491
|
};
|
|
4492
|
+
const decodeSegment = (segment, fieldPath) => {
|
|
4493
|
+
for (const batch of batches(secretViews(segment, budget))) {
|
|
4494
|
+
accept(matchArgumentBatch(batch, fieldPath, budget));
|
|
4495
|
+
if (blocked()) return;
|
|
4496
|
+
}
|
|
4497
|
+
};
|
|
4305
4498
|
const visit = (value, depth, fieldPath) => {
|
|
4499
|
+
if (blocked()) return;
|
|
4306
4500
|
if (++nodes > ARGUMENT_SCAN_LIMITS.maxNodes) {
|
|
4307
4501
|
budget.reasons.add("argument-nodes");
|
|
4308
4502
|
return;
|
|
@@ -4344,14 +4538,11 @@ function inspectArgumentSecrets(args) {
|
|
|
4344
4538
|
}
|
|
4345
4539
|
sourceBytes += Math.min(bytes, Math.max(0, remaining));
|
|
4346
4540
|
if (!text) return;
|
|
4347
|
-
|
|
4348
|
-
|
|
4349
|
-
if (hit)
|
|
4350
|
-
accept(view.transforms.length ? { ...hit, redactedSample: "[encoded secret]" } : hit);
|
|
4351
|
-
}
|
|
4541
|
+
accept(matchArgumentText(text, fieldPath, budget));
|
|
4542
|
+
let scannedEnd = text.length;
|
|
4352
4543
|
if (!truncated && value.length > window) {
|
|
4353
|
-
budget.reasons.add("decode-window");
|
|
4354
4544
|
for (let start = window - overlap; start < value.length; start += window - overlap) {
|
|
4545
|
+
if (blocked()) return;
|
|
4355
4546
|
const chunk = value.slice(start, start + window);
|
|
4356
4547
|
const chunkBytes = Buffer.byteLength(chunk);
|
|
4357
4548
|
if (tailBytes + chunkBytes > ARGUMENT_SCAN_LIMITS.maxTailBytes) {
|
|
@@ -4360,9 +4551,16 @@ function inspectArgumentSecrets(args) {
|
|
|
4360
4551
|
}
|
|
4361
4552
|
tailBytes += chunkBytes;
|
|
4362
4553
|
accept(matchArgumentText(chunk, fieldPath, budget));
|
|
4554
|
+
scannedEnd = start + chunk.length;
|
|
4363
4555
|
if (start + window >= value.length) break;
|
|
4364
4556
|
}
|
|
4365
4557
|
}
|
|
4558
|
+
for (let start = 0; start < scannedEnd && !blocked(); ) {
|
|
4559
|
+
let end = Math.min(start + window, scannedEnd);
|
|
4560
|
+
while (end < scannedEnd && runChar(value, end - 1) && runChar(value, end)) end++;
|
|
4561
|
+
decodeSegment(value.slice(start, end), fieldPath);
|
|
4562
|
+
start = end;
|
|
4563
|
+
}
|
|
4366
4564
|
const trimmed = text.trim();
|
|
4367
4565
|
if (value.length < ARGUMENT_SCAN_LIMITS.maxJsonCharacters && (trimmed.startsWith("{") || trimmed.startsWith("["))) {
|
|
4368
4566
|
try {
|
|
@@ -4381,7 +4579,7 @@ function inspectArgumentSecrets(args) {
|
|
|
4381
4579
|
function scanArgumentSecrets(args) {
|
|
4382
4580
|
const result = inspectArgumentSecrets(args);
|
|
4383
4581
|
if (result.match) return result.match;
|
|
4384
|
-
return result.
|
|
4582
|
+
return result.coverage === "incomplete" ? {
|
|
4385
4583
|
patternName: "Argument inspection incomplete",
|
|
4386
4584
|
fieldPath: "args",
|
|
4387
4585
|
redactedSample: "[uninspected content]",
|
|
@@ -4409,18 +4607,13 @@ function tokenize2(toolName) {
|
|
|
4409
4607
|
return toolName.toLowerCase().split(/[_.\-\s]+/).filter(Boolean);
|
|
4410
4608
|
}
|
|
4411
4609
|
function extractShellCommand(toolName, args, toolInspection) {
|
|
4412
|
-
const
|
|
4413
|
-
|
|
4414
|
-
if (!matchingPattern) return null;
|
|
4415
|
-
const fieldPath = toolInspection[matchingPattern];
|
|
4610
|
+
const fieldPath = inspectionEntry(toolName, toolInspection);
|
|
4611
|
+
if (fieldPath === void 0) return null;
|
|
4416
4612
|
const value = getNestedValue(args, typeof fieldPath === "string" ? fieldPath : fieldPath.field);
|
|
4417
4613
|
return typeof value === "string" ? value : null;
|
|
4418
4614
|
}
|
|
4419
4615
|
function isSqlTool(toolName, toolInspection) {
|
|
4420
|
-
const
|
|
4421
|
-
const matchingPattern = patterns.find((p) => matchesPattern(toolName, p));
|
|
4422
|
-
if (!matchingPattern) return false;
|
|
4423
|
-
const fieldName = toolInspection[matchingPattern];
|
|
4616
|
+
const fieldName = inspectionEntry(toolName, toolInspection);
|
|
4424
4617
|
return fieldName === "sql" || fieldName === "query";
|
|
4425
4618
|
}
|
|
4426
4619
|
function pipeChainVerdict(command, isTrustedHost2, highAction = "review") {
|
|
@@ -4487,9 +4680,10 @@ function egressPolicyVerdict(eg) {
|
|
|
4487
4680
|
}
|
|
4488
4681
|
async function evaluatePolicy(config, toolName, args, context = {}, hooks = {}) {
|
|
4489
4682
|
let protectedVerdict = null;
|
|
4683
|
+
const layers = inspectionLayers(config.policy);
|
|
4490
4684
|
if (context.agent !== "Terminal") {
|
|
4491
4685
|
try {
|
|
4492
|
-
const accesses = context.accesses ?? collectFileAccesses(toolName, args, context,
|
|
4686
|
+
const accesses = context.accesses ?? collectFileAccesses(toolName, args, context, layers);
|
|
4493
4687
|
protectedVerdict = evaluateProtectedFileAccess(accesses, context, catalogSettings(config));
|
|
4494
4688
|
} catch {
|
|
4495
4689
|
protectedVerdict = incompleteFileVerdict();
|
|
@@ -4524,7 +4718,7 @@ async function evaluatePolicy(config, toolName, args, context = {}, hooks = {})
|
|
|
4524
4718
|
hooks
|
|
4525
4719
|
);
|
|
4526
4720
|
if (result.decision === "block") return result;
|
|
4527
|
-
if (context.agent !== "Terminal" && resolveShellInput(toolName, args,
|
|
4721
|
+
if (context.agent !== "Terminal" && resolveShellInput(toolName, args, layers).kind === "uninspectable") {
|
|
4528
4722
|
return {
|
|
4529
4723
|
decision: "review",
|
|
4530
4724
|
ruleName: "shell-inspection-incomplete",
|
|
@@ -4540,13 +4734,14 @@ async function evaluatePolicyCore(config, toolName, args, context = {}, hooks =
|
|
|
4540
4734
|
const { agent, cwd, activeEnvironment } = context;
|
|
4541
4735
|
const { checkProvenance: checkProvenance2, isTrustedHost: isTrustedHost2 } = hooks;
|
|
4542
4736
|
const settingsView = catalogSettings(config);
|
|
4737
|
+
const layers = inspectionLayers(config.policy);
|
|
4543
4738
|
const cv = (id) => checkValue(settingsView, id);
|
|
4544
4739
|
let loggedFinding;
|
|
4545
4740
|
const noteLogged = (v) => {
|
|
4546
4741
|
if (v?.logged && !loggedFinding) loggedFinding = v;
|
|
4547
4742
|
};
|
|
4548
4743
|
const allowed = () => loggedFinding ?? { decision: "allow" };
|
|
4549
|
-
const shellInput = resolveShellInput(toolName, args,
|
|
4744
|
+
const shellInput = resolveShellInput(toolName, args, layers);
|
|
4550
4745
|
const wouldBeIgnored = matchesPattern(toolName, config.policy.ignoredTools);
|
|
4551
4746
|
{
|
|
4552
4747
|
const dest = ssrfDestinationFloor(toolName, args, {
|
|
@@ -4651,13 +4846,10 @@ async function evaluatePolicyCore(config, toolName, args, context = {}, hooks =
|
|
|
4651
4846
|
return { id, value: cv(id) };
|
|
4652
4847
|
};
|
|
4653
4848
|
const matches = config.policy.smartRules.filter((rule) => {
|
|
4654
|
-
if (!toolMatchesRule(toolName, rule.tool,
|
|
4849
|
+
if (!toolMatchesRule(toolName, rule.tool, layers)) return false;
|
|
4655
4850
|
if (astSuppressed(rule)) return false;
|
|
4656
4851
|
if (rmCleanupWaiver && rule.name === "review-rm" && rule.verdict === "review") return false;
|
|
4657
|
-
if (!evaluateSmartConditions(
|
|
4658
|
-
shellRuleArgs(toolName, args, rule.tool, config.policy.toolInspection),
|
|
4659
|
-
rule
|
|
4660
|
-
))
|
|
4852
|
+
if (!evaluateSmartConditions(shellRuleArgs(toolName, args, rule.tool, layers), rule))
|
|
4661
4853
|
return false;
|
|
4662
4854
|
const check = ruleCheck(rule);
|
|
4663
4855
|
if (check?.value === "off") return false;
|
|
@@ -4704,7 +4896,7 @@ async function evaluatePolicyCore(config, toolName, args, context = {}, hooks =
|
|
|
4704
4896
|
let allTokens = [];
|
|
4705
4897
|
let pathTokens = [];
|
|
4706
4898
|
if (pendingToolEgress) return pendingToolEgress;
|
|
4707
|
-
const shellCommand = shellInput.kind === "shell" ? shellInput.command : shellInput.kind === "not-shell" ? extractShellCommand(toolName, args,
|
|
4899
|
+
const shellCommand = shellInput.kind === "shell" ? shellInput.command : shellInput.kind === "not-shell" ? extractShellCommand(toolName, args, layers) : null;
|
|
4708
4900
|
if (shellCommand) {
|
|
4709
4901
|
const analyzed = analyzeShellCommand(shellCommand);
|
|
4710
4902
|
allTokens = analyzed.allTokens;
|
|
@@ -4781,7 +4973,7 @@ async function evaluatePolicyCore(config, toolName, args, context = {}, hooks =
|
|
|
4781
4973
|
if (["ssh", "scp", "rsync"].includes(firstToken)) {
|
|
4782
4974
|
const rawTokens = shellCommand.trim().split(/\s+/);
|
|
4783
4975
|
const sshHosts = extractAllSshHosts(rawTokens.slice(1));
|
|
4784
|
-
allTokens.
|
|
4976
|
+
allTokens = allTokens.concat(sshHosts);
|
|
4785
4977
|
}
|
|
4786
4978
|
if (firstToken && firstToken.startsWith("/") && checkProvenance2) {
|
|
4787
4979
|
const prov = checkProvenance2(firstToken, cwd);
|
|
@@ -4810,7 +5002,7 @@ async function evaluatePolicyCore(config, toolName, args, context = {}, hooks =
|
|
|
4810
5002
|
};
|
|
4811
5003
|
}
|
|
4812
5004
|
}
|
|
4813
|
-
if (isSqlTool(toolName,
|
|
5005
|
+
if (isSqlTool(toolName, layers)) {
|
|
4814
5006
|
allTokens = allTokens.filter((t) => !SQL_DML_KEYWORDS.has(t.toLowerCase()));
|
|
4815
5007
|
}
|
|
4816
5008
|
} else {
|
|
@@ -4818,7 +5010,7 @@ async function evaluatePolicyCore(config, toolName, args, context = {}, hooks =
|
|
|
4818
5010
|
if (args && typeof args === "object") {
|
|
4819
5011
|
const flattenedArgs = JSON.stringify(args).toLowerCase();
|
|
4820
5012
|
const extraTokens = flattenedArgs.split(/[^a-zA-Z0-9]+/).filter((t) => t.length > 1);
|
|
4821
|
-
allTokens.
|
|
5013
|
+
allTokens = allTokens.concat(extraTokens);
|
|
4822
5014
|
}
|
|
4823
5015
|
}
|
|
4824
5016
|
const isManual = agent === "Terminal";
|
|
@@ -5587,7 +5779,7 @@ function matchCanaryArgs(args, values) {
|
|
|
5587
5779
|
return null;
|
|
5588
5780
|
}
|
|
5589
5781
|
}
|
|
5590
|
-
var import_picomatch, import_safe_regex2, import_picomatch2, import_mvdan_sh, import_safe_regex22, import_mvdan_sh2, import_safe_regex23, import_crypto2, import_crypto3, import_mvdan_sh3, import_mvdan_sh4, import_crypto4, aws_default, bash_safe_default, docker_default, filesystem_default, github_default, k8s_default, mongodb_default, postgres_default, project_jail_default, redis_default, BUILTIN_SHIELDS, SSRF_MAX_HOST, METADATA_ADDRESSES, STRICT_TIERS, METADATA_HOSTNAMES, v4Octets, TIER_REASON, CHECK_TEXT, VERDICTS, CHECK_VERDICT_RANK, ALL, BUILTIN_DANGEROUS_WORDS, PRODUCT_CHECKS, SHIELD_RULE_NAME, SHIELD_RULES_FOLDED_INTO_PRODUCT, CHECKS, CHECK_BY_ID, LEGACY_TO_ID, PROJECT_JAIL_BLOCK_READ, OVERRIDE_PREFIX, LABEL_WRAPPERS, LEGACY_READERS, syntax, sharedParser, MESSAGE_FLAGS, SHELL_INTERPRETERS, DOWNLOAD_CMDS, NORMALIZE_CACHE_MAX, normalizeCache, AST_CACHE_MAX, astCache, PARSE_FAIL, defaultExpansionOps, VARIABLE_READERS, FS_READ_TOOLS, GREP_SHAPE, PATTERN_VERBS, PATTERN_VERB_NAMES, GREP_FILE_OPERANDS, READER_VALUE_LETTERS, FILE_OPERAND_FLAGS, SCP_VALUE_FLAGS, TAR_VALUE_LETTERS, ZIP_VALUE_LETTERS, RSYNC_VALUE_LETTERS, RSYNC_SKIP, CP_VALUE_LETTERS, INSTALL_VALUE_LETTERS, COPY_VERBS, TAR_MODE_WORD, COPY_VERB_HEADS, FS_OP_PRESCREEN_RE, HOME_CACHE_ALLOWLIST, SENSITIVE_PATH_RULES, AST_FS_REGEX_RULES, SQL_DB_CLIS, SQL_DDL_RE, CHMOD_OPEN_PERM_TOKENS, COMMAND_WRAPPERS, INLINE_INTERPRETER, RUNNER_WRAPPERS, _redirStdinOps, _listOps, WRAPPER_TAKES_TARGET, FIND_EXEC_FLAGS, FS_MUTATION_TOOLS, INTERP_LEADING_TARGET, NET_BINARIES, VALUE_FLAGS, HOME_VARIABLES, assignmentTable, currentStmtOffset, PAYLOAD_BUDGET, payloadBudget, seenPayloads, UNANALYSABLE_NESTING, ASSIGNMENT_HEADS, AND_OP, OR_OP, AND_OR_OPS, FS_OP_CACHE_MAX, fsOpCache, stripDotSlash, REDIR_TRUNCATE_OPS, REDIR_FILE_IN_OPS, REDIR_HEREDOC_OPS, FIND_OPTIONS, COPY_RULE_OF, isReaderWord, positionalAfter, NONE, UNKNOWN, MAX_REGEX_LENGTH, REGEX_CACHE_MAX, regexCache, FORBIDDEN_PATH_SEGMENTS, BASH_TOOL_NAMES, MCP_EXECUTORS, syntax2, MAX_ALTERNATIVES, MAX_DEPTH, MAX_PAYLOAD_DEPTH, unique, clone, base, quoteGlob, asWord, WRITES, READS, DUP_OUT, HERE_STRING, AND, OR, DEFAULT_OPS, SYSTEM_AUTH_FILES, SYSTEM_AUTH_DIRS, PASSWORD_HASH_FILES, STARTUP_NAMES, AGENT_INSTRUCTION_FILES, AGENT_INSTRUCTION_DIRS, LEGACY_SYSTEM_PATH_RE, RANK, BIP39_ENGLISH, BIP39_PUBLIC_VECTORS, indices, lengths, hash, IBAN_LENGTH, B58, B58_INDEX, XPRV_VERSIONS, B62, CRC32_TABLE, GITHUB_TOKEN_RE, B64URL, B64URL_INDEX, CASK_TWO_ZERO_SUFFIX, CASK_FOUR_ZERO_SUFFIX, INVISIBLE_RE, HOMOGLYPHS, HOMOGLYPH_RE, ASSIGNMENT_CONTEXT_RE, DLP_STOPWORDS, REFERENCE_VALUE_RE, IDENTIFIER_VALUE_RE, PATH_VALUE_RE, DOTTED_REFERENCE_RE, EMAIL_VALUE_RE, FIXTURE_NAME_RE, DERIVATIVE_NAME_RE, QUERY_CREDENTIAL_VALUE_RE, OTP_SECRET_RE, ASSIGNED_SECRET_VALUE_RE, ASSIGNED_SECRET_NAME_RE, NAME_KEYWORD_RE, ARN_CONTEXT_RE, DLP_PATTERNS, DLP_PATTERNS_GLOBAL, SENSITIVE_PATH_PATTERNS, SENSITIVE_PATH_REGEXES, MAX_DEPTH2, MAX_STRING_BYTES, MAX_JSON_PARSE_BYTES, syntax3, NPM_INSTALL_VERBS, NPM_EXEC_VERBS, PNPM_INSTALL_VERBS, YARN_INSTALL_VERBS, BUN_INSTALL_VERBS, DLX_VERBS, NON_REGISTRY_RE, NPM_NAME_RE, EXACT_SEMVER_RE, NPM_VALUE_FLAGS, PNPM_VALUE_FLAGS, YARN_VALUE_FLAGS, BUN_VALUE_FLAGS, PIP_VALUE_FLAGS, UV_VALUE_FLAGS, POETRY_VALUE_FLAGS, PIPENV_VALUE_FLAGS, PIPX_VALUE_FLAGS, PY_SPEC_RE, PY_EXACT_RE, MANAGER_HEADS, SHELLS, MAX_NESTING, SHELL_VALUE_FLAGS, TUNING_FIELDS, MAP_GOVERNED, KNOB_GOVERNED, CONFIGURABLE_CHECK_IDS, DEFAULT_EGRESS_ALLOWLIST, PRIVATE_HOST_SUFFIXES, SOURCE_COMMANDS, SINK_COMMANDS, OBFUSCATORS, SENSITIVE_PATTERNS, FLAGS_WITH_VALUES, DESTINATION_ARGS, SECRET_VIEW_LIMITS, ARGUMENT_SCAN_LIMITS, VERDICT_RANK, SQL_DML_KEYWORDS, MAX_DESTINATIONS, MAX_SCAN_CHARS, EMAIL_RE, URL_RE, IBAN_RE, RECIPIENT_KEYS, DESTRUCTIVE_VERB_RE, syntax4, EMPTY, CURL_SEND_FLAGS, WGET_SEND_FLAGS, IWR_SEND_FLAGS, WRITE_METHODS, GH_READ, GH_WRITE, GH_HOSTNAME_FLAGS, GH_REPO_VALUE_FLAGS, GH_API_VALUE_FLAGS, GIT_FETCH, PUBLISH, SHELLS2, OWN_HEADS, MAX_DEPTH3, CWD_CHANGERS, TERMINAL_ESCAPE_RE, LOOP_MAX_RECORDS, PII_EMAIL_RE, PII_SSN_RE, PII_PHONE_RE, PII_CC16_RE, PII_CC15_RE, PII_IBAN_RE, REALTIME_PII_PATTERNS, MAX_PII_SCAN_BYTES, CANARY_MIN_LENGTH, MAX_TEXT, MAX_DEPTH4, MAX_JSON_PARSE, URL_DEPTH, B64_DEPTH, MIN_SEGMENT, SEPARATORS, stripSeparators, looksText, CANARY_DECODERS, VIEWS, LONG_OUTPUT_THRESHOLD_BYTES;
|
|
5782
|
+
var import_picomatch, import_safe_regex2, import_picomatch2, import_mvdan_sh, import_safe_regex22, import_mvdan_sh2, import_safe_regex23, import_crypto2, import_crypto3, import_mvdan_sh3, import_mvdan_sh4, import_crypto4, aws_default, bash_safe_default, docker_default, filesystem_default, github_default, k8s_default, mongodb_default, postgres_default, project_jail_default, redis_default, BUILTIN_SHIELDS, SSRF_MAX_HOST, METADATA_ADDRESSES, STRICT_TIERS, METADATA_HOSTNAMES, v4Octets, TIER_REASON, CHECK_TEXT, VERDICTS, CHECK_VERDICT_RANK, ALL, BUILTIN_DANGEROUS_WORDS, PRODUCT_CHECKS, SHIELD_RULE_NAME, SHIELD_RULES_FOLDED_INTO_PRODUCT, CHECKS, CHECK_BY_ID, LEGACY_TO_ID, PROJECT_JAIL_BLOCK_READ, OVERRIDE_PREFIX, LABEL_WRAPPERS, LEGACY_READERS, syntax, sharedParser, MESSAGE_FLAGS, SHELL_INTERPRETERS, DOWNLOAD_CMDS, NORMALIZE_CACHE_MAX, normalizeCache, AST_CACHE_MAX, astCache, PARSE_FAIL, defaultExpansionOps, VARIABLE_READERS, FS_READ_TOOLS, GREP_SHAPE, PATTERN_VERBS, PATTERN_VERB_NAMES, GREP_FILE_OPERANDS, READER_VALUE_LETTERS, FILE_OPERAND_FLAGS, SCP_VALUE_FLAGS, TAR_VALUE_LETTERS, ZIP_VALUE_LETTERS, RSYNC_VALUE_LETTERS, RSYNC_SKIP, CP_VALUE_LETTERS, INSTALL_VALUE_LETTERS, COPY_VERBS, TAR_MODE_WORD, COPY_VERB_HEADS, FS_OP_PRESCREEN_RE, HOME_CACHE_ALLOWLIST, SENSITIVE_PATH_RULES, AST_FS_REGEX_RULES, SQL_DB_CLIS, SQL_DDL_RE, CHMOD_OPEN_PERM_TOKENS, COMMAND_WRAPPERS, INLINE_INTERPRETER, RUNNER_WRAPPERS, _redirStdinOps, _listOps, WRAPPER_TAKES_TARGET, FIND_EXEC_FLAGS, FS_MUTATION_TOOLS, INTERP_LEADING_TARGET, NET_BINARIES, VALUE_FLAGS, HOME_VARIABLES, assignmentTable, currentStmtOffset, PAYLOAD_BUDGET, payloadBudget, seenPayloads, UNANALYSABLE_NESTING, ASSIGNMENT_HEADS, AND_OP, OR_OP, AND_OR_OPS, FS_OP_CACHE_MAX, fsOpCache, stripDotSlash, REDIR_TRUNCATE_OPS, REDIR_FILE_IN_OPS, REDIR_HEREDOC_OPS, FIND_OPTIONS, COPY_RULE_OF, isReaderWord, positionalAfter, NONE, UNKNOWN, MAX_REGEX_LENGTH, REGEX_CACHE_MAX, regexCache, FORBIDDEN_PATH_SEGMENTS, BASH_TOOL_NAMES, MCP_EXECUTORS, BUILTIN_FIELDS, ARGV_FIELDS, BARE_WORD, MAX_ARGV_CHARACTERS, syntax2, MAX_ALTERNATIVES, MAX_DEPTH, MAX_PAYLOAD_DEPTH, unique, clone, base, quoteGlob, asWord, WRITES, READS, DUP_OUT, HERE_STRING, AND, OR, DEFAULT_OPS, SYSTEM_AUTH_FILES, SYSTEM_AUTH_DIRS, PASSWORD_HASH_FILES, STARTUP_NAMES, AGENT_INSTRUCTION_FILES, AGENT_INSTRUCTION_DIRS, LEGACY_SYSTEM_PATH_RE, RANK, BIP39_ENGLISH, BIP39_PUBLIC_VECTORS, indices, lengths, hash, MAX_MNEMONIC_HASHES, IBAN_LENGTH, B58, B58_INDEX, XPRV_VERSIONS, B62, CRC32_TABLE, GITHUB_TOKEN_RE, B64URL, B64URL_INDEX, CASK_TWO_ZERO_SUFFIX, CASK_FOUR_ZERO_SUFFIX, INVISIBLE_RE, INVISIBLE_ONE, HOMOGLYPHS, HOMOGLYPH_RE, ASSIGNMENT_CONTEXT_RE, DLP_STOPWORDS, REFERENCE_VALUE_RE, IDENTIFIER_VALUE_RE, PATH_VALUE_RE, DOTTED_REFERENCE_RE, EMAIL_VALUE_RE, FIXTURE_NAME_RE, DERIVATIVE_NAME_RE, QUERY_CREDENTIAL_VALUE_RE, OTP_SECRET_RE, ASSIGNED_SECRET_VALUE_RE, ASSIGNED_SECRET_NAME_RE, NAME_KEYWORD_RE, ARN_CONTEXT_RE, DLP_PATTERNS, DLP_PATTERNS_GLOBAL, SENSITIVE_PATH_PATTERNS, SENSITIVE_PATH_REGEXES, MAX_DEPTH2, MAX_STRING_BYTES, MAX_JSON_PARSE_BYTES, BATCH_SEPARATOR, syntax3, NPM_INSTALL_VERBS, NPM_EXEC_VERBS, PNPM_INSTALL_VERBS, YARN_INSTALL_VERBS, BUN_INSTALL_VERBS, DLX_VERBS, NON_REGISTRY_RE, NPM_NAME_RE, EXACT_SEMVER_RE, NPM_VALUE_FLAGS, PNPM_VALUE_FLAGS, YARN_VALUE_FLAGS, BUN_VALUE_FLAGS, PIP_VALUE_FLAGS, UV_VALUE_FLAGS, POETRY_VALUE_FLAGS, PIPENV_VALUE_FLAGS, PIPX_VALUE_FLAGS, PY_SPEC_RE, PY_EXACT_RE, MANAGER_HEADS, SHELLS, MAX_NESTING, SHELL_VALUE_FLAGS, TUNING_FIELDS, MAP_GOVERNED, KNOB_GOVERNED, CONFIGURABLE_CHECK_IDS, DEFAULT_EGRESS_ALLOWLIST, PRIVATE_HOST_SUFFIXES, SOURCE_COMMANDS, SINK_COMMANDS, OBFUSCATORS, SENSITIVE_PATTERNS, FLAGS_WITH_VALUES, DESTINATION_ARGS, SECRET_VIEW_LIMITS, ARGUMENT_SCAN_LIMITS, RUN_CHAR, runChar, VERDICT_RANK, SQL_DML_KEYWORDS, MAX_DESTINATIONS, MAX_SCAN_CHARS, EMAIL_RE, URL_RE, IBAN_RE, RECIPIENT_KEYS, DESTRUCTIVE_VERB_RE, syntax4, EMPTY, CURL_SEND_FLAGS, WGET_SEND_FLAGS, IWR_SEND_FLAGS, WRITE_METHODS, GH_READ, GH_WRITE, GH_HOSTNAME_FLAGS, GH_REPO_VALUE_FLAGS, GH_API_VALUE_FLAGS, GIT_FETCH, PUBLISH, SHELLS2, OWN_HEADS, MAX_DEPTH3, CWD_CHANGERS, TERMINAL_ESCAPE_RE, LOOP_MAX_RECORDS, PII_EMAIL_RE, PII_SSN_RE, PII_PHONE_RE, PII_CC16_RE, PII_CC15_RE, PII_IBAN_RE, REALTIME_PII_PATTERNS, MAX_PII_SCAN_BYTES, CANARY_MIN_LENGTH, MAX_TEXT, MAX_DEPTH4, MAX_JSON_PARSE, URL_DEPTH, B64_DEPTH, MIN_SEGMENT, SEPARATORS, stripSeparators, looksText, CANARY_DECODERS, VIEWS, LONG_OUTPUT_THRESHOLD_BYTES;
|
|
5591
5783
|
var init_dist = __esm({
|
|
5592
5784
|
"packages/policy-engine/dist/index.mjs"() {
|
|
5593
5785
|
"use strict";
|
|
@@ -7964,7 +8156,23 @@ var init_dist = __esm({
|
|
|
7964
8156
|
"shell",
|
|
7965
8157
|
"exec_command"
|
|
7966
8158
|
]);
|
|
7967
|
-
MCP_EXECUTORS = /* @__PURE__ */ new Set([
|
|
8159
|
+
MCP_EXECUTORS = /* @__PURE__ */ new Set([
|
|
8160
|
+
...BASH_TOOL_NAMES,
|
|
8161
|
+
"exec",
|
|
8162
|
+
"run_command",
|
|
8163
|
+
"terminal",
|
|
8164
|
+
"execute_command",
|
|
8165
|
+
"execute_shell",
|
|
8166
|
+
"execute_shell_command",
|
|
8167
|
+
"shell_execute",
|
|
8168
|
+
"run_shell",
|
|
8169
|
+
"run_terminal_command",
|
|
8170
|
+
"start_process"
|
|
8171
|
+
]);
|
|
8172
|
+
BUILTIN_FIELDS = ["command", "cmd", "script"];
|
|
8173
|
+
ARGV_FIELDS = ["args", "argv"];
|
|
8174
|
+
BARE_WORD = /^[A-Za-z0-9_./+:@%,-]+$/;
|
|
8175
|
+
MAX_ARGV_CHARACTERS = 262144;
|
|
7968
8176
|
({ syntax: syntax2 } = import_mvdan_sh2.default);
|
|
7969
8177
|
MAX_ALTERNATIVES = 64;
|
|
7970
8178
|
MAX_DEPTH = 64;
|
|
@@ -10090,6 +10298,7 @@ zoo`.split("\n");
|
|
|
10090
10298
|
indices = new Map(BIP39_ENGLISH.map((word, i) => [word, i]));
|
|
10091
10299
|
lengths = [24, 21, 18, 15, 12];
|
|
10092
10300
|
hash = (value) => (0, import_crypto2.createHash)("sha256").update(value).digest();
|
|
10301
|
+
MAX_MNEMONIC_HASHES = 25e3;
|
|
10093
10302
|
IBAN_LENGTH = {
|
|
10094
10303
|
AD: 24,
|
|
10095
10304
|
AE: 23,
|
|
@@ -10197,6 +10406,7 @@ zoo`.split("\n");
|
|
|
10197
10406
|
CASK_TWO_ZERO_SUFFIX = /* @__PURE__ */ new Set([..."AEIMQUYcgkosw048"]);
|
|
10198
10407
|
CASK_FOUR_ZERO_SUFFIX = /* @__PURE__ */ new Set([..."AQgw"]);
|
|
10199
10408
|
INVISIBLE_RE = /[͏----︀-️]|[\u{E0000}-\u{E007F}]/gu;
|
|
10409
|
+
INVISIBLE_ONE = new RegExp(`^(?:${INVISIBLE_RE.source.split("|")[0]})$`, "u");
|
|
10200
10410
|
HOMOGLYPHS = {
|
|
10201
10411
|
// Cyrillic lowercase
|
|
10202
10412
|
\u0430: "a",
|
|
@@ -10855,6 +11065,7 @@ zoo`.split("\n");
|
|
|
10855
11065
|
MAX_DEPTH2 = 5;
|
|
10856
11066
|
MAX_STRING_BYTES = 1e5;
|
|
10857
11067
|
MAX_JSON_PARSE_BYTES = 1e4;
|
|
11068
|
+
BATCH_SEPARATOR = "\n";
|
|
10858
11069
|
({ syntax: syntax3 } = import_mvdan_sh3.default);
|
|
10859
11070
|
NPM_INSTALL_VERBS = /* @__PURE__ */ new Set([
|
|
10860
11071
|
"install",
|
|
@@ -11324,16 +11535,24 @@ zoo`.split("\n");
|
|
|
11324
11535
|
["browser_batch", ["actions[].input.url"]]
|
|
11325
11536
|
]);
|
|
11326
11537
|
SECRET_VIEW_LIMITS = {
|
|
11538
|
+
/** A decoded text that itself decodes is followed this many layers; a deeper layer is reviewed. */
|
|
11327
11539
|
maxDepth: 4,
|
|
11328
|
-
|
|
11329
|
-
|
|
11330
|
-
|
|
11331
|
-
|
|
11540
|
+
/** Nested views per parent view, and nested text per call; past either the call is reviewed. */
|
|
11541
|
+
maxNestedViewsPerLeaf: 64,
|
|
11542
|
+
maxNestedBytes: 1048576,
|
|
11543
|
+
/** Joined hostname-label text per call; past it the call is reviewed. */
|
|
11544
|
+
maxJoinedLabelBytes: 1048576,
|
|
11545
|
+
/** Decoded fragments are matched in batches of about this many characters. */
|
|
11546
|
+
batchCharacters: 65536,
|
|
11547
|
+
/** Only a valid DNS name has its labels joined. */
|
|
11548
|
+
maxHostLength: 253,
|
|
11549
|
+
maxLabelLength: 63,
|
|
11550
|
+
maxLabels: 127
|
|
11332
11551
|
};
|
|
11333
11552
|
ARGUMENT_SCAN_LIMITS = {
|
|
11334
11553
|
maxDepth: 12,
|
|
11335
11554
|
maxNodes: 16384,
|
|
11336
|
-
/**
|
|
11555
|
+
/** Text is matched and decoded in windows of this size. */
|
|
11337
11556
|
maxStringCharacters: 1e5,
|
|
11338
11557
|
/** Windows overlap by this much so a token on a window edge is still seen whole. */
|
|
11339
11558
|
windowOverlap: 4096,
|
|
@@ -11343,6 +11562,11 @@ zoo`.split("\n");
|
|
|
11343
11562
|
maxTailBytes: 4194304,
|
|
11344
11563
|
maxJsonCharacters: 1e4
|
|
11345
11564
|
};
|
|
11565
|
+
RUN_CHAR = /[A-Za-z0-9+/=_%\\.-]/;
|
|
11566
|
+
runChar = (value, index) => {
|
|
11567
|
+
const char = value[index] ?? "";
|
|
11568
|
+
return RUN_CHAR.test(char) || char !== "" && isInvisibleChar(char);
|
|
11569
|
+
};
|
|
11346
11570
|
VERDICT_RANK = {
|
|
11347
11571
|
allow: 0,
|
|
11348
11572
|
review: 1,
|
|
@@ -11683,6 +11907,10 @@ function appendLocalAudit(toolName, args, decision, checkedBy, meta, auditHashAr
|
|
|
11683
11907
|
taintFromEid: meta.taintFromEid,
|
|
11684
11908
|
...meta.taintSource && { taintSource: meta.taintSource }
|
|
11685
11909
|
} : {};
|
|
11910
|
+
const exposureFields = {
|
|
11911
|
+
...meta?.exposure ? { exposure: meta.exposure } : {},
|
|
11912
|
+
...meta?.exposureFromEid ? { exposureFromEid: meta.exposureFromEid } : {}
|
|
11913
|
+
};
|
|
11686
11914
|
const eid = generateEventId();
|
|
11687
11915
|
appendToLog(LOCAL_AUDIT_LOG, {
|
|
11688
11916
|
// eid first: the outbox shipper dedups on it, and a fixed leading field
|
|
@@ -11705,6 +11933,7 @@ function appendLocalAudit(toolName, args, decision, checkedBy, meta, auditHashAr
|
|
|
11705
11933
|
...loopCountField,
|
|
11706
11934
|
...transcriptPathField,
|
|
11707
11935
|
...taintFields,
|
|
11936
|
+
...exposureFields,
|
|
11708
11937
|
...testRun,
|
|
11709
11938
|
agent: meta?.agent,
|
|
11710
11939
|
mcpServer: meta?.mcpServer,
|
|
@@ -11747,10 +11976,72 @@ module.exports = __toCommonJS(src_exports);
|
|
|
11747
11976
|
// src/core.ts
|
|
11748
11977
|
init_audit();
|
|
11749
11978
|
|
|
11979
|
+
// src/auth/trusted-recipients.ts
|
|
11980
|
+
var import_fs2 = __toESM(require("fs"));
|
|
11981
|
+
var import_path2 = __toESM(require("path"));
|
|
11982
|
+
var import_os2 = __toESM(require("os"));
|
|
11983
|
+
init_dist();
|
|
11984
|
+
function getTrustedRecipientsPath() {
|
|
11985
|
+
return import_path2.default.join(import_os2.default.homedir(), ".node9", "trusted-recipients.json");
|
|
11986
|
+
}
|
|
11987
|
+
function readTrustedRecipients() {
|
|
11988
|
+
try {
|
|
11989
|
+
const raw = import_fs2.default.readFileSync(getTrustedRecipientsPath(), "utf8");
|
|
11990
|
+
const parsed = JSON.parse(raw);
|
|
11991
|
+
return Array.isArray(parsed.entries) ? parsed.entries.filter((e) => e && typeof e.value === "string") : [];
|
|
11992
|
+
} catch {
|
|
11993
|
+
return [];
|
|
11994
|
+
}
|
|
11995
|
+
}
|
|
11996
|
+
function writeTrustedRecipients(entries) {
|
|
11997
|
+
const filePath = getTrustedRecipientsPath();
|
|
11998
|
+
import_fs2.default.mkdirSync(import_path2.default.dirname(filePath), { recursive: true });
|
|
11999
|
+
const tmp = filePath + ".node9-tmp";
|
|
12000
|
+
import_fs2.default.writeFileSync(tmp, JSON.stringify({ entries }, null, 2), { mode: 384 });
|
|
12001
|
+
import_fs2.default.renameSync(tmp, filePath);
|
|
12002
|
+
}
|
|
12003
|
+
function normalizeRecipient(raw) {
|
|
12004
|
+
const v = raw.trim();
|
|
12005
|
+
if (!v) return null;
|
|
12006
|
+
if (/^(email|host|iban):/i.test(v)) {
|
|
12007
|
+
const i = v.indexOf(":");
|
|
12008
|
+
const kind = v.slice(0, i).toLowerCase();
|
|
12009
|
+
const inner = normalizeRecipient(v.slice(i + 1));
|
|
12010
|
+
return inner && inner.startsWith(`${kind}:`) ? inner : null;
|
|
12011
|
+
}
|
|
12012
|
+
const found = extractArgDestinations({ to: v });
|
|
12013
|
+
return found.length === 1 ? found[0] : null;
|
|
12014
|
+
}
|
|
12015
|
+
function addTrustedRecipient(raw, addedBy = "user") {
|
|
12016
|
+
const value = normalizeRecipient(raw);
|
|
12017
|
+
if (!value) return null;
|
|
12018
|
+
const entries = readTrustedRecipients();
|
|
12019
|
+
if (!entries.some((e) => e.value === value)) {
|
|
12020
|
+
entries.push({ value, addedAt: Date.now(), addedBy });
|
|
12021
|
+
writeTrustedRecipients(entries);
|
|
12022
|
+
}
|
|
12023
|
+
return value;
|
|
12024
|
+
}
|
|
12025
|
+
var cache = null;
|
|
12026
|
+
function mtimeOf() {
|
|
12027
|
+
try {
|
|
12028
|
+
return import_fs2.default.statSync(getTrustedRecipientsPath()).mtimeMs;
|
|
12029
|
+
} catch {
|
|
12030
|
+
return 0;
|
|
12031
|
+
}
|
|
12032
|
+
}
|
|
12033
|
+
function isTrustedRecipient(value) {
|
|
12034
|
+
const mtime = mtimeOf();
|
|
12035
|
+
if (!cache || cache.mtime !== mtime) {
|
|
12036
|
+
cache = { mtime, values: new Set(readTrustedRecipients().map((e) => e.value)) };
|
|
12037
|
+
}
|
|
12038
|
+
return cache.values.has(value);
|
|
12039
|
+
}
|
|
12040
|
+
|
|
11750
12041
|
// src/config/index.ts
|
|
11751
|
-
var
|
|
11752
|
-
var
|
|
11753
|
-
var
|
|
12042
|
+
var import_fs5 = __toESM(require("fs"));
|
|
12043
|
+
var import_path5 = __toESM(require("path"));
|
|
12044
|
+
var import_os5 = __toESM(require("os"));
|
|
11754
12045
|
|
|
11755
12046
|
// src/config-schema.ts
|
|
11756
12047
|
var import_zod = require("zod");
|
|
@@ -12019,17 +12310,17 @@ function sanitizeConfig(raw) {
|
|
|
12019
12310
|
}
|
|
12020
12311
|
|
|
12021
12312
|
// src/shields.ts
|
|
12022
|
-
var
|
|
12023
|
-
var
|
|
12024
|
-
var
|
|
12313
|
+
var import_fs3 = __toESM(require("fs"));
|
|
12314
|
+
var import_path3 = __toESM(require("path"));
|
|
12315
|
+
var import_os3 = __toESM(require("os"));
|
|
12025
12316
|
init_dist();
|
|
12026
12317
|
init_dist();
|
|
12027
|
-
var USER_SHIELDS_DIR =
|
|
12318
|
+
var USER_SHIELDS_DIR = import_path3.default.join(import_os3.default.homedir(), ".node9", "shields");
|
|
12028
12319
|
function loadUserShields() {
|
|
12029
12320
|
const result = {};
|
|
12030
12321
|
let entries;
|
|
12031
12322
|
try {
|
|
12032
|
-
entries =
|
|
12323
|
+
entries = import_fs3.default.readdirSync(USER_SHIELDS_DIR).filter((f) => f.endsWith(".json"));
|
|
12033
12324
|
} catch (err) {
|
|
12034
12325
|
if (err.code !== "ENOENT") {
|
|
12035
12326
|
process.stderr.write(
|
|
@@ -12040,9 +12331,9 @@ function loadUserShields() {
|
|
|
12040
12331
|
return result;
|
|
12041
12332
|
}
|
|
12042
12333
|
for (const file2 of entries) {
|
|
12043
|
-
const filePath =
|
|
12334
|
+
const filePath = import_path3.default.join(USER_SHIELDS_DIR, file2);
|
|
12044
12335
|
try {
|
|
12045
|
-
const raw = JSON.parse(
|
|
12336
|
+
const raw = JSON.parse(import_fs3.default.readFileSync(filePath, "utf-8"));
|
|
12046
12337
|
const v = validateShieldDefinition(raw);
|
|
12047
12338
|
if ("error" in v) {
|
|
12048
12339
|
process.stderr.write(`[node9] ${v.error}: ${filePath}
|
|
@@ -12073,10 +12364,10 @@ function getShield(name) {
|
|
|
12073
12364
|
const resolved = resolveShieldName(name);
|
|
12074
12365
|
return resolved ? SHIELDS[resolved] : null;
|
|
12075
12366
|
}
|
|
12076
|
-
var SHIELDS_STATE_FILE =
|
|
12367
|
+
var SHIELDS_STATE_FILE = import_path3.default.join(import_os3.default.homedir(), ".node9", "shields.json");
|
|
12077
12368
|
function readShieldsFile() {
|
|
12078
12369
|
try {
|
|
12079
|
-
const raw =
|
|
12370
|
+
const raw = import_fs3.default.readFileSync(SHIELDS_STATE_FILE, "utf-8");
|
|
12080
12371
|
if (!raw.trim()) return { active: [] };
|
|
12081
12372
|
const parsed = JSON.parse(raw);
|
|
12082
12373
|
const active = Array.isArray(parsed.active) ? parsed.active.filter(
|
|
@@ -12288,15 +12579,15 @@ function pathRules(rawPath, verdict, reason) {
|
|
|
12288
12579
|
}
|
|
12289
12580
|
|
|
12290
12581
|
// src/auth/trusted-hosts.ts
|
|
12291
|
-
var
|
|
12292
|
-
var
|
|
12293
|
-
var
|
|
12582
|
+
var import_fs4 = __toESM(require("fs"));
|
|
12583
|
+
var import_path4 = __toESM(require("path"));
|
|
12584
|
+
var import_os4 = __toESM(require("os"));
|
|
12294
12585
|
function getTrustedHostsPath() {
|
|
12295
|
-
return
|
|
12586
|
+
return import_path4.default.join(import_os4.default.homedir(), ".node9", "trusted-hosts.json");
|
|
12296
12587
|
}
|
|
12297
12588
|
function readTrustedHosts() {
|
|
12298
12589
|
try {
|
|
12299
|
-
const raw =
|
|
12590
|
+
const raw = import_fs4.default.readFileSync(getTrustedHostsPath(), "utf8");
|
|
12300
12591
|
const parsed = JSON.parse(raw);
|
|
12301
12592
|
return Array.isArray(parsed.hosts) ? parsed.hosts : [];
|
|
12302
12593
|
} catch {
|
|
@@ -12307,7 +12598,7 @@ var _cache = null;
|
|
|
12307
12598
|
var CACHE_TTL_MS = 5e3;
|
|
12308
12599
|
function getFileMtime() {
|
|
12309
12600
|
try {
|
|
12310
|
-
return
|
|
12601
|
+
return import_fs4.default.statSync(getTrustedHostsPath()).mtimeMs;
|
|
12311
12602
|
} catch {
|
|
12312
12603
|
return 0;
|
|
12313
12604
|
}
|
|
@@ -12772,7 +13063,8 @@ var DEFAULT_CONFIG = {
|
|
|
12772
13063
|
spotlight: true,
|
|
12773
13064
|
afterExposure: "ask-once",
|
|
12774
13065
|
orgDomains: [],
|
|
12775
|
-
roles: {}
|
|
13066
|
+
roles: {},
|
|
13067
|
+
trustedRecipients: []
|
|
12776
13068
|
},
|
|
12777
13069
|
trustedHosts: [],
|
|
12778
13070
|
trustedHostsManaged: false,
|
|
@@ -12846,8 +13138,8 @@ function noteRejectedApiUrl(raw) {
|
|
|
12846
13138
|
if (rejectedApiUrlsSeen.has(key)) return;
|
|
12847
13139
|
rejectedApiUrlsSeen.add(key);
|
|
12848
13140
|
try {
|
|
12849
|
-
|
|
12850
|
-
|
|
13141
|
+
import_fs5.default.appendFileSync(
|
|
13142
|
+
import_path5.default.join(import_os5.default.homedir(), ".node9", "hook-debug.log"),
|
|
12851
13143
|
`[${(/* @__PURE__ */ new Date()).toISOString()}] REFUSED apiUrl, using the default instead: ${String(raw).slice(0, 200).replace(/[\r\n]+/g, " ")}
|
|
12852
13144
|
`
|
|
12853
13145
|
);
|
|
@@ -12864,9 +13156,9 @@ function getCredentials() {
|
|
|
12864
13156
|
};
|
|
12865
13157
|
}
|
|
12866
13158
|
try {
|
|
12867
|
-
const credPath =
|
|
12868
|
-
if (
|
|
12869
|
-
const creds = JSON.parse(
|
|
13159
|
+
const credPath = import_path5.default.join(import_os5.default.homedir(), ".node9", "credentials.json");
|
|
13160
|
+
if (import_fs5.default.existsSync(credPath)) {
|
|
13161
|
+
const creds = JSON.parse(import_fs5.default.readFileSync(credPath, "utf-8"));
|
|
12870
13162
|
const profileName = process.env.NODE9_PROFILE || "default";
|
|
12871
13163
|
const profile = creds[profileName];
|
|
12872
13164
|
if (typeof profile?.apiKey === "string" && profile.apiKey.length > 0) {
|
|
@@ -12898,7 +13190,7 @@ function readRulesCacheResilient(cacheFile) {
|
|
|
12898
13190
|
for (let attempt = 0; attempt < 3; attempt++) {
|
|
12899
13191
|
let content;
|
|
12900
13192
|
try {
|
|
12901
|
-
content =
|
|
13193
|
+
content = import_fs5.default.readFileSync(cacheFile, "utf-8");
|
|
12902
13194
|
existed = true;
|
|
12903
13195
|
} catch (err) {
|
|
12904
13196
|
if (err.code === "ENOENT") return {};
|
|
@@ -12913,10 +13205,10 @@ function readRulesCacheResilient(cacheFile) {
|
|
|
12913
13205
|
}
|
|
12914
13206
|
}
|
|
12915
13207
|
if (existed || sawReadError) {
|
|
12916
|
-
const backup =
|
|
13208
|
+
const backup = import_path5.default.join(import_path5.default.dirname(cacheFile), "rules-cache.last-good.json");
|
|
12917
13209
|
if (backup !== cacheFile) {
|
|
12918
13210
|
try {
|
|
12919
|
-
const raw = JSON.parse(
|
|
13211
|
+
const raw = JSON.parse(import_fs5.default.readFileSync(backup, "utf-8"));
|
|
12920
13212
|
logCacheReadIssue(cacheFile, "RULES_CACHE_CORRUPT_USED_BACKUP");
|
|
12921
13213
|
lastParsedRulesCache = raw;
|
|
12922
13214
|
return raw;
|
|
@@ -12939,8 +13231,8 @@ function logCacheReadIssue(cacheFile, kind) {
|
|
|
12939
13231
|
if (now - cacheReadLastLoggedAt < CACHE_LOG_REARM_MS) return;
|
|
12940
13232
|
cacheReadLastLoggedAt = now;
|
|
12941
13233
|
try {
|
|
12942
|
-
|
|
12943
|
-
|
|
13234
|
+
import_fs5.default.appendFileSync(
|
|
13235
|
+
import_path5.default.join(import_os5.default.homedir(), ".node9", "hook-debug.log"),
|
|
12944
13236
|
`[${(/* @__PURE__ */ new Date()).toISOString()}] ${kind} ${cacheFile}
|
|
12945
13237
|
`
|
|
12946
13238
|
);
|
|
@@ -12961,8 +13253,8 @@ function catalogSettingsFrom(settings, policy) {
|
|
|
12961
13253
|
}
|
|
12962
13254
|
function getConfig(cwd) {
|
|
12963
13255
|
if (!cwd && cachedConfig) return cachedConfig;
|
|
12964
|
-
const globalPath =
|
|
12965
|
-
const projectPath =
|
|
13256
|
+
const globalPath = import_path5.default.join(import_os5.default.homedir(), ".node9", "config.json");
|
|
13257
|
+
const projectPath = import_path5.default.join(cwd ?? process.cwd(), "node9.config.json");
|
|
12966
13258
|
const globalConfig = tryLoadConfig(globalPath);
|
|
12967
13259
|
const projectConfig = tryLoadConfig(projectPath, { project: true });
|
|
12968
13260
|
const mergedSettings = {
|
|
@@ -12975,6 +13267,7 @@ function getConfig(cwd) {
|
|
|
12975
13267
|
dangerousWords: [...DEFAULT_CONFIG.policy.dangerousWords],
|
|
12976
13268
|
ignoredTools: [...DEFAULT_CONFIG.policy.ignoredTools],
|
|
12977
13269
|
toolInspection: { ...DEFAULT_CONFIG.policy.toolInspection },
|
|
13270
|
+
projectToolInspection: {},
|
|
12978
13271
|
// Shipped rules carry `builtin`: the catalog's per-check value governs
|
|
12979
13272
|
// them and only them (a user rule that reuses a name keeps its verdict).
|
|
12980
13273
|
smartRules: DEFAULT_CONFIG.policy.smartRules.map((r) => ({ ...r, builtin: true })),
|
|
@@ -13001,7 +13294,8 @@ function getConfig(cwd) {
|
|
|
13001
13294
|
untrustedContent: {
|
|
13002
13295
|
...DEFAULT_CONFIG.policy.untrustedContent,
|
|
13003
13296
|
orgDomains: [...DEFAULT_CONFIG.policy.untrustedContent.orgDomains],
|
|
13004
|
-
roles: {}
|
|
13297
|
+
roles: {},
|
|
13298
|
+
trustedRecipients: []
|
|
13005
13299
|
},
|
|
13006
13300
|
// Left empty on purpose: the local file is read fresh at policy-eval time
|
|
13007
13301
|
// (getCachedHosts via isTrustedHost), NOT snapshotted into the frozen config
|
|
@@ -13047,8 +13341,14 @@ function getConfig(cwd) {
|
|
|
13047
13341
|
if (p.sandboxPaths) mergedPolicy.sandboxPaths.push(...p.sandboxPaths);
|
|
13048
13342
|
if (p.ignoredTools) mergedPolicy.ignoredTools.push(...p.ignoredTools);
|
|
13049
13343
|
if (p.dangerousWords) mergedPolicy.dangerousWords = [...p.dangerousWords];
|
|
13050
|
-
if (p.toolInspection)
|
|
13051
|
-
|
|
13344
|
+
if (p.toolInspection) {
|
|
13345
|
+
if (isProject)
|
|
13346
|
+
mergedPolicy.projectToolInspection = {
|
|
13347
|
+
...mergedPolicy.projectToolInspection,
|
|
13348
|
+
...p.toolInspection
|
|
13349
|
+
};
|
|
13350
|
+
else mergedPolicy.toolInspection = { ...mergedPolicy.toolInspection, ...p.toolInspection };
|
|
13351
|
+
}
|
|
13052
13352
|
if (p.smartRules) {
|
|
13053
13353
|
const defaultBlocks = mergedPolicy.smartRules.filter((r) => r.verdict === "block");
|
|
13054
13354
|
const defaultNonBlocks = mergedPolicy.smartRules.filter((r) => r.verdict !== "block");
|
|
@@ -13196,7 +13496,7 @@ function getConfig(cwd) {
|
|
|
13196
13496
|
let cloudMandatesEnforcement = false;
|
|
13197
13497
|
let cloudMandatesAppPerm = false;
|
|
13198
13498
|
{
|
|
13199
|
-
const cacheFile =
|
|
13499
|
+
const cacheFile = import_path5.default.join(import_os5.default.homedir(), ".node9", "rules-cache.json");
|
|
13200
13500
|
try {
|
|
13201
13501
|
const raw = readRulesCacheResilient(cacheFile);
|
|
13202
13502
|
if (Array.isArray(raw.rules) && raw.rules.length > 0) {
|
|
@@ -13589,10 +13889,10 @@ function getConfig(cwd) {
|
|
|
13589
13889
|
}
|
|
13590
13890
|
function tryLoadConfig(filePath, options = {}) {
|
|
13591
13891
|
v2ChecksByPath.delete(filePath);
|
|
13592
|
-
if (!
|
|
13892
|
+
if (!import_fs5.default.existsSync(filePath)) return null;
|
|
13593
13893
|
let raw;
|
|
13594
13894
|
try {
|
|
13595
|
-
raw = JSON.parse(
|
|
13895
|
+
raw = JSON.parse(import_fs5.default.readFileSync(filePath, "utf-8"));
|
|
13596
13896
|
} catch (err) {
|
|
13597
13897
|
const msg = err instanceof Error ? err.message : String(err);
|
|
13598
13898
|
process.stderr.write(
|
|
@@ -13667,6 +13967,13 @@ function applyUntrustedContent(cur, raw, replaceLists = false, isProject = false
|
|
|
13667
13967
|
if (replaceLists) cur.orgDomains = domains;
|
|
13668
13968
|
else for (const d of domains) if (!cur.orgDomains.includes(d)) cur.orgDomains.push(d);
|
|
13669
13969
|
}
|
|
13970
|
+
if (replaceLists && Array.isArray(uc.trustedRecipients)) {
|
|
13971
|
+
cur.trustedRecipients = [
|
|
13972
|
+
...new Set(
|
|
13973
|
+
uc.trustedRecipients.filter((v) => typeof v === "string").map((v) => normalizeRecipient(v)).filter((v) => v !== null)
|
|
13974
|
+
)
|
|
13975
|
+
].slice(0, 200);
|
|
13976
|
+
}
|
|
13670
13977
|
if (uc.roles && typeof uc.roles === "object" && !Array.isArray(uc.roles)) {
|
|
13671
13978
|
const next = replaceLists ? {} : cur.roles;
|
|
13672
13979
|
for (const [server, tools] of Object.entries(uc.roles)) {
|
|
@@ -13688,20 +13995,20 @@ function applyUntrustedContent(cur, raw, replaceLists = false, isProject = false
|
|
|
13688
13995
|
init_dist();
|
|
13689
13996
|
|
|
13690
13997
|
// src/dlp.ts
|
|
13691
|
-
var
|
|
13692
|
-
var
|
|
13998
|
+
var import_fs6 = __toESM(require("fs"));
|
|
13999
|
+
var import_path6 = __toESM(require("path"));
|
|
13693
14000
|
init_dist();
|
|
13694
14001
|
init_dist();
|
|
13695
14002
|
function scanFilePath(filePath, cwd = process.cwd(), options = {}) {
|
|
13696
14003
|
if (!filePath) return null;
|
|
13697
14004
|
let resolved;
|
|
13698
14005
|
try {
|
|
13699
|
-
const absolute =
|
|
13700
|
-
resolved =
|
|
14006
|
+
const absolute = import_path6.default.resolve(cwd, filePath);
|
|
14007
|
+
resolved = import_fs6.default.realpathSync.native(absolute);
|
|
13701
14008
|
} catch (err) {
|
|
13702
14009
|
const code = err.code;
|
|
13703
14010
|
if (code === "ENOENT" || code === "ENOTDIR") {
|
|
13704
|
-
resolved =
|
|
14011
|
+
resolved = import_path6.default.resolve(cwd, filePath);
|
|
13705
14012
|
} else {
|
|
13706
14013
|
return sensitivePathMatch(filePath);
|
|
13707
14014
|
}
|
|
@@ -13710,30 +14017,30 @@ function scanFilePath(filePath, cwd = process.cwd(), options = {}) {
|
|
|
13710
14017
|
}
|
|
13711
14018
|
|
|
13712
14019
|
// src/policy/file-access.ts
|
|
13713
|
-
var
|
|
13714
|
-
var
|
|
13715
|
-
var
|
|
14020
|
+
var import_fs7 = __toESM(require("fs"));
|
|
14021
|
+
var import_os6 = __toESM(require("os"));
|
|
14022
|
+
var import_path7 = __toESM(require("path"));
|
|
13716
14023
|
init_dist();
|
|
13717
14024
|
function absoluteSpelling(p, cwd, home) {
|
|
13718
14025
|
if (p === "~" || p.startsWith("~/")) p = home + p.slice(1);
|
|
13719
|
-
return
|
|
14026
|
+
return import_path7.default.isAbsolute(p) ? p : `${cwd}/${p}`;
|
|
13720
14027
|
}
|
|
13721
14028
|
function physical(p, followFinal, links = 0) {
|
|
13722
14029
|
if (links > 40) throw new Error("Too many symbolic links");
|
|
13723
14030
|
if (!followFinal) {
|
|
13724
|
-
const parent = physical(
|
|
13725
|
-
return { path: `${parent.path}/${
|
|
14031
|
+
const parent = physical(import_path7.default.dirname(p), true, links);
|
|
14032
|
+
return { path: `${parent.path}/${import_path7.default.basename(p)}`, missing: parent.missing };
|
|
13726
14033
|
}
|
|
13727
14034
|
try {
|
|
13728
|
-
return { path:
|
|
14035
|
+
return { path: import_fs7.default.realpathSync.native(p), missing: false };
|
|
13729
14036
|
} catch (error) {
|
|
13730
14037
|
const code = error.code;
|
|
13731
14038
|
if (!["ENOENT", "ENOTDIR"].includes(code ?? "")) throw error;
|
|
13732
14039
|
try {
|
|
13733
|
-
if (
|
|
13734
|
-
const target =
|
|
14040
|
+
if (import_fs7.default.lstatSync(p).isSymbolicLink()) {
|
|
14041
|
+
const target = import_fs7.default.readlinkSync(p);
|
|
13735
14042
|
return physical(
|
|
13736
|
-
|
|
14043
|
+
import_path7.default.isAbsolute(target) ? target : `${import_path7.default.dirname(p)}/${target}`,
|
|
13737
14044
|
true,
|
|
13738
14045
|
links + 1
|
|
13739
14046
|
);
|
|
@@ -13742,13 +14049,13 @@ function physical(p, followFinal, links = 0) {
|
|
|
13742
14049
|
if (!["ENOENT", "ENOTDIR"].includes(linkError.code ?? ""))
|
|
13743
14050
|
throw linkError;
|
|
13744
14051
|
}
|
|
13745
|
-
const parent =
|
|
14052
|
+
const parent = import_path7.default.dirname(p);
|
|
13746
14053
|
if (parent === p) return { path: p, missing: true };
|
|
13747
14054
|
const resolved = physical(parent, true, links);
|
|
13748
|
-
return { path: `${resolved.path}/${
|
|
14055
|
+
return { path: `${resolved.path}/${import_path7.default.basename(p)}`, missing: true };
|
|
13749
14056
|
}
|
|
13750
14057
|
}
|
|
13751
|
-
function prepareFileAccesses(tool, args, cwd = process.cwd(), inspection = {}, home =
|
|
14058
|
+
function prepareFileAccesses(tool, args, cwd = process.cwd(), inspection = {}, home = import_os6.default.homedir()) {
|
|
13752
14059
|
const context = { cwd, homeDirs: [home, "/root"] };
|
|
13753
14060
|
const accesses = collectFileAccesses(tool, args, context, inspection);
|
|
13754
14061
|
const handledPaths = /* @__PURE__ */ new Set();
|
|
@@ -13771,14 +14078,14 @@ function prepareFileAccesses(tool, args, cwd = process.cwd(), inspection = {}, h
|
|
|
13771
14078
|
const raw = absoluteSpelling(access.path, access.cwd ?? cwd, home);
|
|
13772
14079
|
if (access.directoryTarget) {
|
|
13773
14080
|
try {
|
|
13774
|
-
if (!
|
|
14081
|
+
if (!import_fs7.default.statSync(absoluteSpelling(access.directoryTarget, access.cwd ?? cwd, home)).isDirectory())
|
|
13775
14082
|
continue;
|
|
13776
14083
|
} catch {
|
|
13777
14084
|
}
|
|
13778
14085
|
}
|
|
13779
14086
|
if (access.operation === "create") {
|
|
13780
14087
|
try {
|
|
13781
|
-
|
|
14088
|
+
import_fs7.default.statSync(raw);
|
|
13782
14089
|
continue;
|
|
13783
14090
|
} catch {
|
|
13784
14091
|
}
|
|
@@ -13827,27 +14134,27 @@ function prepareFileAccesses(tool, args, cwd = process.cwd(), inspection = {}, h
|
|
|
13827
14134
|
}
|
|
13828
14135
|
|
|
13829
14136
|
// src/utils/provenance.ts
|
|
13830
|
-
var
|
|
13831
|
-
var
|
|
13832
|
-
var
|
|
14137
|
+
var import_fs8 = __toESM(require("fs"));
|
|
14138
|
+
var import_path8 = __toESM(require("path"));
|
|
14139
|
+
var import_os7 = __toESM(require("os"));
|
|
13833
14140
|
var SYSTEM_PREFIXES = ["/usr/bin", "/usr/sbin", "/bin", "/sbin"];
|
|
13834
14141
|
var MANAGED_PREFIXES = ["/usr/local/bin", "/opt/homebrew", "/home/linuxbrew", "/nix/store"];
|
|
13835
14142
|
var USER_PREFIXES = [
|
|
13836
|
-
|
|
13837
|
-
|
|
13838
|
-
|
|
13839
|
-
|
|
13840
|
-
|
|
14143
|
+
import_path8.default.join(import_os7.default.homedir(), "bin"),
|
|
14144
|
+
import_path8.default.join(import_os7.default.homedir(), ".local", "bin"),
|
|
14145
|
+
import_path8.default.join(import_os7.default.homedir(), ".cargo", "bin"),
|
|
14146
|
+
import_path8.default.join(import_os7.default.homedir(), ".npm-global", "bin"),
|
|
14147
|
+
import_path8.default.join(import_os7.default.homedir(), ".volta", "bin")
|
|
13841
14148
|
];
|
|
13842
14149
|
var SUSPECT_PREFIXES = ["/tmp", "/var/tmp", "/dev/shm"];
|
|
13843
14150
|
function findInPath(cmd) {
|
|
13844
|
-
if (
|
|
14151
|
+
if (import_path8.default.posix.isAbsolute(cmd)) return cmd;
|
|
13845
14152
|
const pathEnv = process.env.PATH ?? "";
|
|
13846
|
-
for (const dir2 of pathEnv.split(
|
|
14153
|
+
for (const dir2 of pathEnv.split(import_path8.default.delimiter)) {
|
|
13847
14154
|
if (!dir2) continue;
|
|
13848
|
-
const full =
|
|
14155
|
+
const full = import_path8.default.join(dir2, cmd);
|
|
13849
14156
|
try {
|
|
13850
|
-
|
|
14157
|
+
import_fs8.default.accessSync(full, import_fs8.default.constants.X_OK);
|
|
13851
14158
|
return full;
|
|
13852
14159
|
} catch {
|
|
13853
14160
|
}
|
|
@@ -13858,7 +14165,7 @@ function _classifyPath(resolved, cwd) {
|
|
|
13858
14165
|
if (cwd && resolved.startsWith(cwd + "/")) {
|
|
13859
14166
|
return { trustLevel: "user", reason: "binary in project directory" };
|
|
13860
14167
|
}
|
|
13861
|
-
const osTmp =
|
|
14168
|
+
const osTmp = import_os7.default.tmpdir();
|
|
13862
14169
|
const allSuspect = osTmp ? [...SUSPECT_PREFIXES, osTmp] : SUSPECT_PREFIXES;
|
|
13863
14170
|
if (allSuspect.some((p) => resolved === p || resolved.startsWith(p + "/"))) {
|
|
13864
14171
|
return { trustLevel: "suspect", reason: `binary in temp directory: ${resolved}` };
|
|
@@ -13876,7 +14183,7 @@ function _classifyPath(resolved, cwd) {
|
|
|
13876
14183
|
}
|
|
13877
14184
|
function checkProvenance(cmd, cwd) {
|
|
13878
14185
|
const bare = cmd.startsWith("./") ? cmd.slice(2) : cmd;
|
|
13879
|
-
if (
|
|
14186
|
+
if (import_path8.default.posix.isAbsolute(bare)) {
|
|
13880
14187
|
const early = _classifyPath(bare, cwd);
|
|
13881
14188
|
if (early.trustLevel === "suspect") {
|
|
13882
14189
|
return { resolvedPath: bare, ...early };
|
|
@@ -13892,7 +14199,7 @@ function checkProvenance(cmd, cwd) {
|
|
|
13892
14199
|
reason: "binary not found in PATH"
|
|
13893
14200
|
};
|
|
13894
14201
|
}
|
|
13895
|
-
resolved =
|
|
14202
|
+
resolved = import_fs8.default.realpathSync(found);
|
|
13896
14203
|
} catch {
|
|
13897
14204
|
return {
|
|
13898
14205
|
resolvedPath: cmd,
|
|
@@ -13901,7 +14208,7 @@ function checkProvenance(cmd, cwd) {
|
|
|
13901
14208
|
};
|
|
13902
14209
|
}
|
|
13903
14210
|
try {
|
|
13904
|
-
const stat =
|
|
14211
|
+
const stat = import_fs8.default.statSync(resolved);
|
|
13905
14212
|
if (stat.mode & 2) {
|
|
13906
14213
|
return {
|
|
13907
14214
|
resolvedPath: resolved,
|
|
@@ -13926,7 +14233,7 @@ init_dist();
|
|
|
13926
14233
|
init_dist();
|
|
13927
14234
|
async function evaluatePolicy2(toolName, args, agent, cwd, opts) {
|
|
13928
14235
|
const config = opts?.config ?? getConfig(cwd);
|
|
13929
|
-
const prepared = opts?.fileAccess ?? prepareFileAccesses(toolName, args, cwd, config.policy
|
|
14236
|
+
const prepared = opts?.fileAccess ?? prepareFileAccesses(toolName, args, cwd, inspectionLayers(config.policy));
|
|
13930
14237
|
const activeEnvironment = getActiveEnvironment(config) ?? void 0;
|
|
13931
14238
|
return evaluatePolicy(
|
|
13932
14239
|
config,
|
|
@@ -13955,32 +14262,32 @@ function isIgnoredTool2(toolName) {
|
|
|
13955
14262
|
}
|
|
13956
14263
|
|
|
13957
14264
|
// src/auth/state.ts
|
|
13958
|
-
var
|
|
13959
|
-
var
|
|
13960
|
-
var
|
|
14265
|
+
var import_fs10 = __toESM(require("fs"));
|
|
14266
|
+
var import_path10 = __toESM(require("path"));
|
|
14267
|
+
var import_os8 = __toESM(require("os"));
|
|
13961
14268
|
|
|
13962
14269
|
// src/utils/atomic-write.ts
|
|
13963
|
-
var
|
|
13964
|
-
var
|
|
14270
|
+
var import_fs9 = __toESM(require("fs"));
|
|
14271
|
+
var import_path9 = __toESM(require("path"));
|
|
13965
14272
|
var import_crypto6 = require("crypto");
|
|
13966
14273
|
function atomicWriteSync(filePath, data, options) {
|
|
13967
|
-
const dir2 =
|
|
13968
|
-
if (!
|
|
14274
|
+
const dir2 = import_path9.default.dirname(filePath);
|
|
14275
|
+
if (!import_fs9.default.existsSync(dir2)) import_fs9.default.mkdirSync(dir2, { recursive: true });
|
|
13969
14276
|
const tmpPath = `${filePath}.${(0, import_crypto6.randomUUID)()}.tmp`;
|
|
13970
14277
|
try {
|
|
13971
|
-
|
|
14278
|
+
import_fs9.default.writeFileSync(tmpPath, data, options);
|
|
13972
14279
|
} catch (err) {
|
|
13973
14280
|
try {
|
|
13974
|
-
|
|
14281
|
+
import_fs9.default.unlinkSync(tmpPath);
|
|
13975
14282
|
} catch {
|
|
13976
14283
|
}
|
|
13977
14284
|
throw err;
|
|
13978
14285
|
}
|
|
13979
14286
|
try {
|
|
13980
|
-
|
|
14287
|
+
import_fs9.default.renameSync(tmpPath, filePath);
|
|
13981
14288
|
} catch (err) {
|
|
13982
14289
|
try {
|
|
13983
|
-
|
|
14290
|
+
import_fs9.default.unlinkSync(tmpPath);
|
|
13984
14291
|
} catch {
|
|
13985
14292
|
}
|
|
13986
14293
|
throw err;
|
|
@@ -13988,8 +14295,8 @@ function atomicWriteSync(filePath, data, options) {
|
|
|
13988
14295
|
}
|
|
13989
14296
|
|
|
13990
14297
|
// src/auth/state.ts
|
|
13991
|
-
var PAUSED_FILE =
|
|
13992
|
-
var TRUST_FILE =
|
|
14298
|
+
var PAUSED_FILE = import_path10.default.join(import_os8.default.homedir(), ".node9", "PAUSED");
|
|
14299
|
+
var TRUST_FILE = import_path10.default.join(import_os8.default.homedir(), ".node9", "trust.json");
|
|
13993
14300
|
function extractCommandPattern(toolName, args) {
|
|
13994
14301
|
const lower2 = toolName.toLowerCase();
|
|
13995
14302
|
if (lower2 !== "bash" && lower2 !== "execute_bash" && lower2 !== "shell") return void 0;
|
|
@@ -14001,11 +14308,11 @@ function extractCommandPattern(toolName, args) {
|
|
|
14001
14308
|
}
|
|
14002
14309
|
function checkPause() {
|
|
14003
14310
|
try {
|
|
14004
|
-
if (!
|
|
14005
|
-
const state = JSON.parse(
|
|
14311
|
+
if (!import_fs10.default.existsSync(PAUSED_FILE)) return { paused: false };
|
|
14312
|
+
const state = JSON.parse(import_fs10.default.readFileSync(PAUSED_FILE, "utf-8"));
|
|
14006
14313
|
if (state.expiry > 0 && Date.now() >= state.expiry) {
|
|
14007
14314
|
try {
|
|
14008
|
-
|
|
14315
|
+
import_fs10.default.unlinkSync(PAUSED_FILE);
|
|
14009
14316
|
} catch {
|
|
14010
14317
|
}
|
|
14011
14318
|
return { paused: false };
|
|
@@ -14017,8 +14324,8 @@ function checkPause() {
|
|
|
14017
14324
|
}
|
|
14018
14325
|
function getActiveTrustSession(toolName, args) {
|
|
14019
14326
|
try {
|
|
14020
|
-
if (!
|
|
14021
|
-
const trust = JSON.parse(
|
|
14327
|
+
if (!import_fs10.default.existsSync(TRUST_FILE)) return false;
|
|
14328
|
+
const trust = JSON.parse(import_fs10.default.readFileSync(TRUST_FILE, "utf-8"));
|
|
14022
14329
|
const now = Date.now();
|
|
14023
14330
|
const active = trust.entries.filter((e) => e.expiry > now);
|
|
14024
14331
|
if (active.length !== trust.entries.length) {
|
|
@@ -14041,8 +14348,8 @@ function writeTrustSession(toolName, durationMs, args) {
|
|
|
14041
14348
|
try {
|
|
14042
14349
|
let trust = { entries: [] };
|
|
14043
14350
|
try {
|
|
14044
|
-
if (
|
|
14045
|
-
trust = JSON.parse(
|
|
14351
|
+
if (import_fs10.default.existsSync(TRUST_FILE)) {
|
|
14352
|
+
trust = JSON.parse(import_fs10.default.readFileSync(TRUST_FILE, "utf-8"));
|
|
14046
14353
|
}
|
|
14047
14354
|
} catch {
|
|
14048
14355
|
}
|
|
@@ -14064,9 +14371,9 @@ function writeTrustSession(toolName, durationMs, args) {
|
|
|
14064
14371
|
}
|
|
14065
14372
|
function getPersistentDecision(toolName) {
|
|
14066
14373
|
try {
|
|
14067
|
-
const file2 =
|
|
14068
|
-
if (!
|
|
14069
|
-
const decisions = JSON.parse(
|
|
14374
|
+
const file2 = import_path10.default.join(import_os8.default.homedir(), ".node9", "decisions.json");
|
|
14375
|
+
if (!import_fs10.default.existsSync(file2)) return null;
|
|
14376
|
+
const decisions = JSON.parse(import_fs10.default.readFileSync(file2, "utf-8"));
|
|
14070
14377
|
const d = decisions[toolName];
|
|
14071
14378
|
if (d === "allow" || d === "deny") return d;
|
|
14072
14379
|
} catch {
|
|
@@ -14075,11 +14382,11 @@ function getPersistentDecision(toolName) {
|
|
|
14075
14382
|
}
|
|
14076
14383
|
|
|
14077
14384
|
// src/auth/daemon.ts
|
|
14078
|
-
var
|
|
14385
|
+
var import_fs11 = __toESM(require("fs"));
|
|
14079
14386
|
var import_net = __toESM(require("net"));
|
|
14080
|
-
var
|
|
14081
|
-
var
|
|
14082
|
-
var ACTIVITY_SOCKET_PATH = process.platform === "win32" ? "\\\\.\\pipe\\node9-activity" :
|
|
14387
|
+
var import_path11 = __toESM(require("path"));
|
|
14388
|
+
var import_os9 = __toESM(require("os"));
|
|
14389
|
+
var ACTIVITY_SOCKET_PATH = process.platform === "win32" ? "\\\\.\\pipe\\node9-activity" : import_path11.default.join(import_os9.default.tmpdir(), "node9-activity.sock");
|
|
14083
14390
|
function notifyActivitySocket(data) {
|
|
14084
14391
|
return new Promise((resolve) => {
|
|
14085
14392
|
try {
|
|
@@ -14121,9 +14428,9 @@ var DAEMON_PORT = resolveDaemonPort();
|
|
|
14121
14428
|
var DAEMON_HOST = "127.0.0.1";
|
|
14122
14429
|
function getInternalToken() {
|
|
14123
14430
|
try {
|
|
14124
|
-
const pidFile =
|
|
14125
|
-
if (!
|
|
14126
|
-
const data = JSON.parse(
|
|
14431
|
+
const pidFile = import_path11.default.join(import_os9.default.homedir(), ".node9", "daemon.pid");
|
|
14432
|
+
if (!import_fs11.default.existsSync(pidFile)) return null;
|
|
14433
|
+
const data = JSON.parse(import_fs11.default.readFileSync(pidFile, "utf-8"));
|
|
14127
14434
|
process.kill(data.pid, 0);
|
|
14128
14435
|
return data.internalToken ?? null;
|
|
14129
14436
|
} catch {
|
|
@@ -14131,12 +14438,12 @@ function getInternalToken() {
|
|
|
14131
14438
|
}
|
|
14132
14439
|
}
|
|
14133
14440
|
function isDaemonRunning() {
|
|
14134
|
-
const pidFile =
|
|
14135
|
-
if (
|
|
14441
|
+
const pidFile = import_path11.default.join(import_os9.default.homedir(), ".node9", "daemon.pid");
|
|
14442
|
+
if (import_fs11.default.existsSync(pidFile)) {
|
|
14136
14443
|
let pid;
|
|
14137
14444
|
let port;
|
|
14138
14445
|
try {
|
|
14139
|
-
const data = JSON.parse(
|
|
14446
|
+
const data = JSON.parse(import_fs11.default.readFileSync(pidFile, "utf-8"));
|
|
14140
14447
|
pid = data.pid;
|
|
14141
14448
|
port = data.port;
|
|
14142
14449
|
} catch {
|
|
@@ -14154,7 +14461,7 @@ function isDaemonRunning() {
|
|
|
14154
14461
|
} catch (err) {
|
|
14155
14462
|
if (err instanceof Error && "code" in err && err.code === "ESRCH") {
|
|
14156
14463
|
try {
|
|
14157
|
-
|
|
14464
|
+
import_fs11.default.unlinkSync(pidFile);
|
|
14158
14465
|
} catch {
|
|
14159
14466
|
}
|
|
14160
14467
|
}
|
|
@@ -14362,10 +14669,10 @@ var import_os18 = __toESM(require("os"));
|
|
|
14362
14669
|
|
|
14363
14670
|
// src/ui/native.ts
|
|
14364
14671
|
var import_child_process = require("child_process");
|
|
14365
|
-
var
|
|
14672
|
+
var import_path13 = __toESM(require("path"));
|
|
14366
14673
|
|
|
14367
14674
|
// src/context-sniper.ts
|
|
14368
|
-
var
|
|
14675
|
+
var import_path12 = __toESM(require("path"));
|
|
14369
14676
|
function smartTruncate(str, maxLen = 500) {
|
|
14370
14677
|
if (str.length <= maxLen) return str;
|
|
14371
14678
|
const edge = Math.floor(maxLen / 2) - 3;
|
|
@@ -14433,7 +14740,7 @@ function computeRiskMetadata(args, tier, blockedByLabel, matchedField, matchedWo
|
|
|
14433
14740
|
intent = "EDIT";
|
|
14434
14741
|
if (obj.file_path) {
|
|
14435
14742
|
editFilePath = String(obj.file_path);
|
|
14436
|
-
editFileName =
|
|
14743
|
+
editFileName = import_path12.default.basename(editFilePath);
|
|
14437
14744
|
}
|
|
14438
14745
|
const result = extractContext(String(obj.new_string), matchedWord);
|
|
14439
14746
|
contextSnippet = result.snippet;
|
|
@@ -14501,7 +14808,7 @@ function formatArgs(args, matchedField, matchedWord) {
|
|
|
14501
14808
|
if (typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
14502
14809
|
const obj = parsed;
|
|
14503
14810
|
if (obj.old_string !== void 0 && obj.new_string !== void 0) {
|
|
14504
|
-
const file2 = obj.file_path ?
|
|
14811
|
+
const file2 = obj.file_path ? import_path13.default.basename(String(obj.file_path)) : "file";
|
|
14505
14812
|
const oldPreview = smartTruncate(String(obj.old_string), 120);
|
|
14506
14813
|
const newPreview = extractContext(String(obj.new_string), matchedWord).snippet;
|
|
14507
14814
|
return {
|
|
@@ -14704,24 +15011,24 @@ end run`;
|
|
|
14704
15011
|
}
|
|
14705
15012
|
|
|
14706
15013
|
// src/canary/registry.ts
|
|
14707
|
-
var
|
|
14708
|
-
var
|
|
14709
|
-
var
|
|
15014
|
+
var import_fs13 = __toESM(require("fs"));
|
|
15015
|
+
var import_os11 = __toESM(require("os"));
|
|
15016
|
+
var import_path15 = __toESM(require("path"));
|
|
14710
15017
|
init_dist();
|
|
14711
15018
|
|
|
14712
15019
|
// src/shields/jail.ts
|
|
14713
|
-
var
|
|
14714
|
-
var
|
|
14715
|
-
var
|
|
15020
|
+
var import_fs12 = __toESM(require("fs"));
|
|
15021
|
+
var import_os10 = __toESM(require("os"));
|
|
15022
|
+
var import_path14 = __toESM(require("path"));
|
|
14716
15023
|
init_dist();
|
|
14717
15024
|
var USER_JAIL_SHIELD = "user-jail";
|
|
14718
15025
|
function jailStorePath() {
|
|
14719
|
-
return
|
|
15026
|
+
return import_path14.default.join(import_os10.default.homedir(), ".node9", "jail-paths.json");
|
|
14720
15027
|
}
|
|
14721
15028
|
function readJailPaths() {
|
|
14722
15029
|
let text;
|
|
14723
15030
|
try {
|
|
14724
|
-
text =
|
|
15031
|
+
text = import_fs12.default.readFileSync(jailStorePath(), "utf8");
|
|
14725
15032
|
} catch (err) {
|
|
14726
15033
|
if (err.code === "ENOENT") return [];
|
|
14727
15034
|
throw err;
|
|
@@ -14750,7 +15057,7 @@ function findJailedPathIn(candidate, paths) {
|
|
|
14750
15057
|
|
|
14751
15058
|
// src/canary/registry.ts
|
|
14752
15059
|
function canaryStorePath() {
|
|
14753
|
-
return
|
|
15060
|
+
return import_path15.default.join(import_os11.default.homedir(), ".node9", "canaries.json");
|
|
14754
15061
|
}
|
|
14755
15062
|
function isRecord(x) {
|
|
14756
15063
|
if (!x || typeof x !== "object") return false;
|
|
@@ -14761,7 +15068,7 @@ function loadCanaries(opts) {
|
|
|
14761
15068
|
const p = canaryStorePath();
|
|
14762
15069
|
let raw;
|
|
14763
15070
|
try {
|
|
14764
|
-
raw =
|
|
15071
|
+
raw = import_fs13.default.readFileSync(p, "utf-8");
|
|
14765
15072
|
} catch (e) {
|
|
14766
15073
|
if (e.code === "ENOENT") return [];
|
|
14767
15074
|
throw e;
|
|
@@ -14790,26 +15097,26 @@ init_audit();
|
|
|
14790
15097
|
init_dist();
|
|
14791
15098
|
|
|
14792
15099
|
// src/exposure/known-hosts.ts
|
|
14793
|
-
var
|
|
14794
|
-
var
|
|
15100
|
+
var import_fs15 = __toESM(require("fs"));
|
|
15101
|
+
var import_path17 = __toESM(require("path"));
|
|
14795
15102
|
|
|
14796
15103
|
// src/utils/git-remotes.ts
|
|
14797
|
-
var
|
|
14798
|
-
var
|
|
15104
|
+
var import_fs14 = __toESM(require("fs"));
|
|
15105
|
+
var import_path16 = __toESM(require("path"));
|
|
14799
15106
|
init_dist();
|
|
14800
15107
|
function readSmallFile(p, max) {
|
|
14801
15108
|
let fd;
|
|
14802
15109
|
try {
|
|
14803
|
-
fd =
|
|
14804
|
-
const st =
|
|
15110
|
+
fd = import_fs14.default.openSync(p, import_fs14.default.constants.O_RDONLY | (import_fs14.default.constants.O_NOFOLLOW ?? 0));
|
|
15111
|
+
const st = import_fs14.default.fstatSync(fd);
|
|
14805
15112
|
if (!st.isFile() || st.size > max) return null;
|
|
14806
15113
|
const buf = Buffer.alloc(st.size);
|
|
14807
|
-
const n =
|
|
15114
|
+
const n = import_fs14.default.readSync(fd, buf, 0, st.size, 0);
|
|
14808
15115
|
return buf.subarray(0, n).toString("utf8");
|
|
14809
15116
|
} catch {
|
|
14810
15117
|
return null;
|
|
14811
15118
|
} finally {
|
|
14812
|
-
if (fd !== void 0)
|
|
15119
|
+
if (fd !== void 0) import_fs14.default.closeSync(fd);
|
|
14813
15120
|
}
|
|
14814
15121
|
}
|
|
14815
15122
|
function gitRemoteHost(url) {
|
|
@@ -14828,35 +15135,35 @@ function gitRemoteHost(url) {
|
|
|
14828
15135
|
return null;
|
|
14829
15136
|
}
|
|
14830
15137
|
function findGitConfig(dir2, walkUp = true) {
|
|
14831
|
-
let cur =
|
|
15138
|
+
let cur = import_path16.default.resolve(dir2);
|
|
14832
15139
|
for (let i = 0; i < (walkUp ? 64 : 1); i++) {
|
|
14833
|
-
const dotGit =
|
|
14834
|
-
const direct = readSmallFile(
|
|
15140
|
+
const dotGit = import_path16.default.join(cur, ".git");
|
|
15141
|
+
const direct = readSmallFile(import_path16.default.join(dotGit, "config"), 1e6);
|
|
14835
15142
|
if (direct !== null) return direct;
|
|
14836
15143
|
const pointer = readSmallFile(dotGit, 4096);
|
|
14837
15144
|
const m = pointer && /^gitdir:\s*(.+?)\s*$/m.exec(pointer);
|
|
14838
15145
|
if (m) {
|
|
14839
|
-
let gitDir =
|
|
14840
|
-
const common = readSmallFile(
|
|
14841
|
-
if (common !== null) gitDir =
|
|
14842
|
-
return readSmallFile(
|
|
15146
|
+
let gitDir = import_path16.default.resolve(cur, m[1]);
|
|
15147
|
+
const common = readSmallFile(import_path16.default.join(gitDir, "commondir"), 4096);
|
|
15148
|
+
if (common !== null) gitDir = import_path16.default.resolve(gitDir, common.trim());
|
|
15149
|
+
return readSmallFile(import_path16.default.join(gitDir, "config"), 1e6);
|
|
14843
15150
|
}
|
|
14844
|
-
const parent =
|
|
15151
|
+
const parent = import_path16.default.dirname(cur);
|
|
14845
15152
|
if (parent === cur) return null;
|
|
14846
15153
|
cur = parent;
|
|
14847
15154
|
}
|
|
14848
15155
|
return null;
|
|
14849
15156
|
}
|
|
14850
|
-
var
|
|
15157
|
+
var cache2 = /* @__PURE__ */ new Map();
|
|
14851
15158
|
function readGitRemotes(dir2, opts = {}) {
|
|
14852
15159
|
if (!dir2) return [];
|
|
14853
15160
|
const walkUp = opts.walkUp !== false;
|
|
14854
|
-
const key = `${walkUp ? "up" : "at"}:${
|
|
14855
|
-
const hit =
|
|
15161
|
+
const key = `${walkUp ? "up" : "at"}:${import_path16.default.resolve(dir2)}`;
|
|
15162
|
+
const hit = cache2.get(key);
|
|
14856
15163
|
if (hit) return hit;
|
|
14857
15164
|
const out = [];
|
|
14858
15165
|
try {
|
|
14859
|
-
const cfg = findGitConfig(
|
|
15166
|
+
const cfg = findGitConfig(import_path16.default.resolve(dir2), walkUp);
|
|
14860
15167
|
if (cfg !== null) {
|
|
14861
15168
|
let current = null;
|
|
14862
15169
|
for (const line of cfg.split(/\r?\n/)) {
|
|
@@ -14878,7 +15185,7 @@ function readGitRemotes(dir2, opts = {}) {
|
|
|
14878
15185
|
}
|
|
14879
15186
|
} catch {
|
|
14880
15187
|
}
|
|
14881
|
-
|
|
15188
|
+
cache2.set(key, out);
|
|
14882
15189
|
return out;
|
|
14883
15190
|
}
|
|
14884
15191
|
|
|
@@ -14890,17 +15197,17 @@ var ECOSYSTEMS = [
|
|
|
14890
15197
|
["Cargo.toml", "host:crates.io"],
|
|
14891
15198
|
["Gemfile", "host:rubygems.org"]
|
|
14892
15199
|
];
|
|
14893
|
-
var
|
|
15200
|
+
var cache3 = /* @__PURE__ */ new Map();
|
|
14894
15201
|
function manifestRoot(dir2) {
|
|
14895
|
-
let cur =
|
|
15202
|
+
let cur = import_path17.default.resolve(dir2);
|
|
14896
15203
|
for (let i = 0; i < 64; i++) {
|
|
14897
15204
|
for (const [m] of ECOSYSTEMS) {
|
|
14898
15205
|
try {
|
|
14899
|
-
if (
|
|
15206
|
+
if (import_fs15.default.existsSync(import_path17.default.join(cur, m))) return cur;
|
|
14900
15207
|
} catch {
|
|
14901
15208
|
}
|
|
14902
15209
|
}
|
|
14903
|
-
const parent =
|
|
15210
|
+
const parent = import_path17.default.dirname(cur);
|
|
14904
15211
|
if (parent === cur) return null;
|
|
14905
15212
|
cur = parent;
|
|
14906
15213
|
}
|
|
@@ -14909,8 +15216,8 @@ function manifestRoot(dir2) {
|
|
|
14909
15216
|
function knownProjectHosts(dir2) {
|
|
14910
15217
|
const empty = { hosts: /* @__PURE__ */ new Set(), repos: /* @__PURE__ */ new Set(), remotes: /* @__PURE__ */ new Map() };
|
|
14911
15218
|
if (!dir2) return empty;
|
|
14912
|
-
const key =
|
|
14913
|
-
const hit =
|
|
15219
|
+
const key = import_path17.default.resolve(dir2);
|
|
15220
|
+
const hit = cache3.get(key);
|
|
14914
15221
|
if (hit) return hit;
|
|
14915
15222
|
const out = { hosts: /* @__PURE__ */ new Set(), repos: /* @__PURE__ */ new Set(), remotes: /* @__PURE__ */ new Map() };
|
|
14916
15223
|
try {
|
|
@@ -14922,24 +15229,24 @@ function knownProjectHosts(dir2) {
|
|
|
14922
15229
|
const root = manifestRoot(key);
|
|
14923
15230
|
if (root !== null) {
|
|
14924
15231
|
for (const [m, registry] of ECOSYSTEMS) {
|
|
14925
|
-
if (
|
|
15232
|
+
if (import_fs15.default.existsSync(import_path17.default.join(root, m))) out.hosts.add(registry);
|
|
14926
15233
|
}
|
|
14927
15234
|
try {
|
|
14928
|
-
if (
|
|
15235
|
+
if (import_fs15.default.readdirSync(root).some((f) => f.endsWith(".gemspec")))
|
|
14929
15236
|
out.hosts.add("host:rubygems.org");
|
|
14930
15237
|
} catch {
|
|
14931
15238
|
}
|
|
14932
15239
|
}
|
|
14933
15240
|
} catch {
|
|
14934
15241
|
}
|
|
14935
|
-
|
|
15242
|
+
cache3.set(key, out);
|
|
14936
15243
|
return out;
|
|
14937
15244
|
}
|
|
14938
15245
|
|
|
14939
15246
|
// src/exposure/writes.ts
|
|
14940
|
-
var
|
|
14941
|
-
var
|
|
14942
|
-
var
|
|
15247
|
+
var import_fs16 = __toESM(require("fs"));
|
|
15248
|
+
var import_os12 = __toESM(require("os"));
|
|
15249
|
+
var import_path18 = __toESM(require("path"));
|
|
14943
15250
|
init_dist();
|
|
14944
15251
|
var WRITE_OPS = /* @__PURE__ */ new Set(["write", "create", "remove"]);
|
|
14945
15252
|
var LIFECYCLE_RE = /"(scripts|preinstall|install|postinstall|prepare|prepublish|prepublishOnly|prepack|postpack|publish|postpublish)"\s*:/;
|
|
@@ -14969,7 +15276,7 @@ function packageScriptsTouched(args, absPath, shellCommand) {
|
|
|
14969
15276
|
if (typeof a.content === "string") {
|
|
14970
15277
|
try {
|
|
14971
15278
|
const next = JSON.parse(a.content);
|
|
14972
|
-
const prev = JSON.parse(
|
|
15279
|
+
const prev = JSON.parse(import_fs16.default.readFileSync(absPath, "utf8"));
|
|
14973
15280
|
return JSON.stringify(next.scripts ?? null) !== JSON.stringify(prev.scripts ?? null);
|
|
14974
15281
|
} catch {
|
|
14975
15282
|
return LIFECYCLE_RE.test(a.content);
|
|
@@ -14979,8 +15286,8 @@ function packageScriptsTouched(args, absPath, shellCommand) {
|
|
|
14979
15286
|
}
|
|
14980
15287
|
function judgeWritesAfterExposure(opts) {
|
|
14981
15288
|
const holds = [];
|
|
14982
|
-
const home = opts.home ??
|
|
14983
|
-
const tmp = normalizeFilePath(
|
|
15289
|
+
const home = opts.home ?? import_os12.default.homedir();
|
|
15290
|
+
const tmp = normalizeFilePath(import_os12.default.tmpdir());
|
|
14984
15291
|
const root = opts.cwd ? normalizeFilePath(opts.cwd, "", home) : null;
|
|
14985
15292
|
const seen = /* @__PURE__ */ new Set();
|
|
14986
15293
|
for (const access of opts.accesses) {
|
|
@@ -14990,7 +15297,7 @@ function judgeWritesAfterExposure(opts) {
|
|
|
14990
15297
|
seen.add(p);
|
|
14991
15298
|
if (p === "/dev" || p.startsWith("/dev/")) continue;
|
|
14992
15299
|
if (root !== null && isInside(root, p)) {
|
|
14993
|
-
if (isAgentInstructionPath(p, root) && (
|
|
15300
|
+
if (isAgentInstructionPath(p, root) && (import_path18.default.basename(p) !== "package.json" || packageScriptsTouched(opts.args, p, opts.shellCommand)))
|
|
14994
15301
|
holds.push({ path: p, why: "agent-instruction" });
|
|
14995
15302
|
continue;
|
|
14996
15303
|
}
|
|
@@ -15012,25 +15319,25 @@ function judgeWritesAfterExposure(opts) {
|
|
|
15012
15319
|
|
|
15013
15320
|
// src/exposure/marker.ts
|
|
15014
15321
|
var import_crypto7 = __toESM(require("crypto"));
|
|
15015
|
-
var
|
|
15016
|
-
var
|
|
15017
|
-
var
|
|
15322
|
+
var import_fs17 = __toESM(require("fs"));
|
|
15323
|
+
var import_os13 = __toESM(require("os"));
|
|
15324
|
+
var import_path19 = __toESM(require("path"));
|
|
15018
15325
|
|
|
15019
15326
|
// src/daemon/exposure-store.ts
|
|
15020
15327
|
var SESSION_EXPOSURE_TTL_MS = 4 * 60 * 60 * 1e3;
|
|
15021
15328
|
|
|
15022
15329
|
// src/exposure/marker.ts
|
|
15023
15330
|
function dir() {
|
|
15024
|
-
return
|
|
15331
|
+
return import_path19.default.join(import_os13.default.homedir(), ".node9", "exposure-sessions");
|
|
15025
15332
|
}
|
|
15026
15333
|
function file(sessionId) {
|
|
15027
15334
|
const id = import_crypto7.default.createHash("sha256").update(sessionId).digest("hex").slice(0, 32);
|
|
15028
|
-
return
|
|
15335
|
+
return import_path19.default.join(dir(), id);
|
|
15029
15336
|
}
|
|
15030
15337
|
function maybeSessionExposed(sessionId) {
|
|
15031
15338
|
if (!sessionId) return false;
|
|
15032
15339
|
try {
|
|
15033
|
-
const st =
|
|
15340
|
+
const st = import_fs17.default.statSync(file(sessionId));
|
|
15034
15341
|
return Date.now() - st.mtimeMs < SESSION_EXPOSURE_TTL_MS;
|
|
15035
15342
|
} catch (e) {
|
|
15036
15343
|
return e.code !== "ENOENT";
|
|
@@ -15038,20 +15345,20 @@ function maybeSessionExposed(sessionId) {
|
|
|
15038
15345
|
}
|
|
15039
15346
|
|
|
15040
15347
|
// src/exposure/roles.ts
|
|
15041
|
-
var
|
|
15348
|
+
var import_fs19 = __toESM(require("fs"));
|
|
15042
15349
|
|
|
15043
15350
|
// src/daemon/mcp-tools.ts
|
|
15044
|
-
var
|
|
15045
|
-
var
|
|
15046
|
-
var
|
|
15351
|
+
var import_fs18 = __toESM(require("fs"));
|
|
15352
|
+
var import_path20 = __toESM(require("path"));
|
|
15353
|
+
var import_os14 = __toESM(require("os"));
|
|
15047
15354
|
function getMcpToolsFile() {
|
|
15048
|
-
return
|
|
15355
|
+
return import_path20.default.join(import_os14.default.homedir(), ".node9", "mcp-tools.json");
|
|
15049
15356
|
}
|
|
15050
15357
|
function readMcpToolsConfig() {
|
|
15051
15358
|
try {
|
|
15052
15359
|
const file2 = getMcpToolsFile();
|
|
15053
|
-
if (!
|
|
15054
|
-
const raw =
|
|
15360
|
+
if (!import_fs18.default.existsSync(file2)) return {};
|
|
15361
|
+
const raw = import_fs18.default.readFileSync(file2, "utf-8");
|
|
15055
15362
|
return JSON.parse(raw);
|
|
15056
15363
|
} catch {
|
|
15057
15364
|
return {};
|
|
@@ -15068,7 +15375,7 @@ var inventoryCache = null;
|
|
|
15068
15375
|
var inventoryStamp = null;
|
|
15069
15376
|
function inventoryFileStamp() {
|
|
15070
15377
|
try {
|
|
15071
|
-
const st =
|
|
15378
|
+
const st = import_fs19.default.statSync(getMcpToolsFile());
|
|
15072
15379
|
return `${st.mtimeMs}:${st.size}`;
|
|
15073
15380
|
} catch {
|
|
15074
15381
|
return "";
|
|
@@ -15147,68 +15454,6 @@ function sourceWord(toolName) {
|
|
|
15147
15454
|
return "tool result";
|
|
15148
15455
|
}
|
|
15149
15456
|
|
|
15150
|
-
// src/auth/trusted-recipients.ts
|
|
15151
|
-
var import_fs19 = __toESM(require("fs"));
|
|
15152
|
-
var import_path20 = __toESM(require("path"));
|
|
15153
|
-
var import_os14 = __toESM(require("os"));
|
|
15154
|
-
init_dist();
|
|
15155
|
-
function getTrustedRecipientsPath() {
|
|
15156
|
-
return import_path20.default.join(import_os14.default.homedir(), ".node9", "trusted-recipients.json");
|
|
15157
|
-
}
|
|
15158
|
-
function readTrustedRecipients() {
|
|
15159
|
-
try {
|
|
15160
|
-
const raw = import_fs19.default.readFileSync(getTrustedRecipientsPath(), "utf8");
|
|
15161
|
-
const parsed = JSON.parse(raw);
|
|
15162
|
-
return Array.isArray(parsed.entries) ? parsed.entries.filter((e) => e && typeof e.value === "string") : [];
|
|
15163
|
-
} catch {
|
|
15164
|
-
return [];
|
|
15165
|
-
}
|
|
15166
|
-
}
|
|
15167
|
-
function writeTrustedRecipients(entries) {
|
|
15168
|
-
const filePath = getTrustedRecipientsPath();
|
|
15169
|
-
import_fs19.default.mkdirSync(import_path20.default.dirname(filePath), { recursive: true });
|
|
15170
|
-
const tmp = filePath + ".node9-tmp";
|
|
15171
|
-
import_fs19.default.writeFileSync(tmp, JSON.stringify({ entries }, null, 2), { mode: 384 });
|
|
15172
|
-
import_fs19.default.renameSync(tmp, filePath);
|
|
15173
|
-
}
|
|
15174
|
-
function normalizeRecipient(raw) {
|
|
15175
|
-
const v = raw.trim();
|
|
15176
|
-
if (!v) return null;
|
|
15177
|
-
if (/^(email|host|iban):/i.test(v)) {
|
|
15178
|
-
const i = v.indexOf(":");
|
|
15179
|
-
const kind = v.slice(0, i).toLowerCase();
|
|
15180
|
-
const inner = normalizeRecipient(v.slice(i + 1));
|
|
15181
|
-
return inner && inner.startsWith(`${kind}:`) ? inner : null;
|
|
15182
|
-
}
|
|
15183
|
-
const found = extractArgDestinations({ to: v });
|
|
15184
|
-
return found.length === 1 ? found[0] : null;
|
|
15185
|
-
}
|
|
15186
|
-
function addTrustedRecipient(raw, addedBy = "user") {
|
|
15187
|
-
const value = normalizeRecipient(raw);
|
|
15188
|
-
if (!value) return null;
|
|
15189
|
-
const entries = readTrustedRecipients();
|
|
15190
|
-
if (!entries.some((e) => e.value === value)) {
|
|
15191
|
-
entries.push({ value, addedAt: Date.now(), addedBy });
|
|
15192
|
-
writeTrustedRecipients(entries);
|
|
15193
|
-
}
|
|
15194
|
-
return value;
|
|
15195
|
-
}
|
|
15196
|
-
var cache3 = null;
|
|
15197
|
-
function mtimeOf() {
|
|
15198
|
-
try {
|
|
15199
|
-
return import_fs19.default.statSync(getTrustedRecipientsPath()).mtimeMs;
|
|
15200
|
-
} catch {
|
|
15201
|
-
return 0;
|
|
15202
|
-
}
|
|
15203
|
-
}
|
|
15204
|
-
function isTrustedRecipient(value) {
|
|
15205
|
-
const mtime = mtimeOf();
|
|
15206
|
-
if (!cache3 || cache3.mtime !== mtime) {
|
|
15207
|
-
cache3 = { mtime, values: new Set(readTrustedRecipients().map((e) => e.value)) };
|
|
15208
|
-
}
|
|
15209
|
-
return cache3.values.has(value);
|
|
15210
|
-
}
|
|
15211
|
-
|
|
15212
15457
|
// src/auth/cloud.ts
|
|
15213
15458
|
var import_fs21 = __toESM(require("fs"));
|
|
15214
15459
|
var import_os15 = __toESM(require("os"));
|
|
@@ -15303,11 +15548,15 @@ async function initNode9SaaS(toolName, args, creds, meta, riskMetadata, agentPol
|
|
|
15303
15548
|
cwd: process.cwd(),
|
|
15304
15549
|
platform: import_os15.default.platform()
|
|
15305
15550
|
},
|
|
15306
|
-
//
|
|
15307
|
-
//
|
|
15551
|
+
// The SaaS validates riskMetadata strictly and would drop it whole on
|
|
15552
|
+
// an unknown key, so only fields its schema declares go on the wire.
|
|
15553
|
+
// exposureDestinations ships since the dashboard's 2b contract
|
|
15554
|
+
// (fw#318): "Approve and trust" needs them.
|
|
15308
15555
|
...riskMetadata && {
|
|
15309
15556
|
riskMetadata: Object.fromEntries(
|
|
15310
|
-
Object.entries(riskMetadata).filter(
|
|
15557
|
+
Object.entries(riskMetadata).filter(
|
|
15558
|
+
([k, v]) => !Array.isArray(v) || k === "exposureDestinations"
|
|
15559
|
+
)
|
|
15311
15560
|
)
|
|
15312
15561
|
},
|
|
15313
15562
|
...ciContext && { ciContext },
|
|
@@ -16030,7 +16279,7 @@ async function _authorizeHeadlessCore(toolName, args, metaArg, options) {
|
|
|
16030
16279
|
let fileAccess;
|
|
16031
16280
|
let fileFinding;
|
|
16032
16281
|
try {
|
|
16033
|
-
fileAccess = prepareFileAccesses(toolName, args, options?.cwd, config.policy
|
|
16282
|
+
fileAccess = prepareFileAccesses(toolName, args, options?.cwd, inspectionLayers(config.policy));
|
|
16034
16283
|
fileFinding = isManual ? null : evaluateProtectedFileAccess(
|
|
16035
16284
|
fileAccess.accesses,
|
|
16036
16285
|
fileAccess,
|
|
@@ -16049,7 +16298,7 @@ async function _authorizeHeadlessCore(toolName, args, metaArg, options) {
|
|
|
16049
16298
|
fileAccess = { cwd: options?.cwd, homeDirs: [], accesses: [], handledPaths: /* @__PURE__ */ new Set() };
|
|
16050
16299
|
fileFinding = isManual ? null : incompleteFileVerdict();
|
|
16051
16300
|
}
|
|
16052
|
-
const shellInput = resolveShellInput(toolName, args, config.policy
|
|
16301
|
+
const shellInput = resolveShellInput(toolName, args, inspectionLayers(config.policy));
|
|
16053
16302
|
const networkShell = shellInput.kind === "shell" && NETWORK_COMMAND_RE.test(shellInput.command);
|
|
16054
16303
|
const shellReview = !isManual && shellInput.kind === "uninspectable";
|
|
16055
16304
|
if (shellReview) {
|
|
@@ -16159,6 +16408,7 @@ async function _authorizeHeadlessCore(toolName, args, metaArg, options) {
|
|
|
16159
16408
|
let exposureReview = null;
|
|
16160
16409
|
let exposureKeys = [];
|
|
16161
16410
|
let exposureDestinations = [];
|
|
16411
|
+
let exposureFromEid;
|
|
16162
16412
|
const ucPolicy = config.policy.untrustedContent;
|
|
16163
16413
|
if (ucPolicy?.enabled && !taintWarning && meta?.sessionId && !isManual) {
|
|
16164
16414
|
const role = toolRole(toolName, ucPolicy);
|
|
@@ -16166,7 +16416,7 @@ async function _authorizeHeadlessCore(toolName, args, metaArg, options) {
|
|
|
16166
16416
|
const shellExp = shellInput.kind === "shell" ? classifyShellExposure(shellInput.command) : null;
|
|
16167
16417
|
const bareName = splitMcpToolName(toolName)?.bare;
|
|
16168
16418
|
const mcpFileWrite = bareName !== void 0 && fileToolOperation(bareName, args) === "write";
|
|
16169
|
-
const writes = (mcpFileWrite ? prepareFileAccesses(bareName, args, options?.cwd, config.policy
|
|
16419
|
+
const writes = (mcpFileWrite ? prepareFileAccesses(bareName, args, options?.cwd, inspectionLayers(config.policy)).accesses : fileAccess.accesses).filter((a) => ["write", "create", "remove"].includes(a.operation));
|
|
16170
16420
|
const sendsSomething = acts && !mcpFileWrite || !!shellExp?.sends;
|
|
16171
16421
|
const mayBeExposed = daemonSaysExposed ?? (maybeSessionExposed(meta.sessionId) || writes.length > 0 && judgeWritesAfterExposure({
|
|
16172
16422
|
accesses: writes,
|
|
@@ -16210,7 +16460,7 @@ async function _authorizeHeadlessCore(toolName, args, metaArg, options) {
|
|
|
16210
16460
|
const isKnown = (d) => {
|
|
16211
16461
|
const domain = destinationDomain(d);
|
|
16212
16462
|
const value = destinationValue(d);
|
|
16213
|
-
return domain !== null && ucPolicy.orgDomains.includes(domain) || mentioned.has(d) || isTrustedRecipient(d) || destinationKind(d) === "host" && (isLoopbackHost(value) || isTrustedHost(value) || config.policy.trustedHostsManaged && matchesTrustedHost(value, config.policy.trustedHosts) || config.policy.egress.allow.some((pat) => hostMatches(value, pat)) || writeProjectHosts.hosts.has(d));
|
|
16463
|
+
return domain !== null && ucPolicy.orgDomains.includes(domain) || mentioned.has(d) || isTrustedRecipient(d) || ucPolicy.trustedRecipients.includes(d) || destinationKind(d) === "host" && (isLoopbackHost(value) || isTrustedHost(value) || config.policy.trustedHostsManaged && matchesTrustedHost(value, config.policy.trustedHosts) || config.policy.egress.allow.some((pat) => hostMatches(value, pat)) || writeProjectHosts.hosts.has(d));
|
|
16214
16464
|
};
|
|
16215
16465
|
const approvedOnce = (key) => ucPolicy.afterExposure === "ask-once" && approved.has(key);
|
|
16216
16466
|
const repos = (shellExp?.sends ? shellExp.repos : []).filter(
|
|
@@ -16274,6 +16524,8 @@ async function _authorizeHeadlessCore(toolName, args, metaArg, options) {
|
|
|
16274
16524
|
);
|
|
16275
16525
|
}
|
|
16276
16526
|
exposureReview = `\u{1F6E1}\uFE0F Node9: this session read ${read}. This call ${parts.join("; and ")}.`;
|
|
16527
|
+
exposureFromEid = last?.eid;
|
|
16528
|
+
if (exposureFromEid) meta = { ...meta, exposureFromEid };
|
|
16277
16529
|
exposureKeys = [
|
|
16278
16530
|
...held.filter((h) => h.dest || h.why === "unresolved" || h.why === "repository").map(
|
|
16279
16531
|
(h) => h.why === "repository" ? `${toolName}|repo:${h.repo}` : `${toolName}|${h.dest || "unresolved"}`
|
|
@@ -16287,6 +16539,11 @@ async function _authorizeHeadlessCore(toolName, args, metaArg, options) {
|
|
|
16287
16539
|
exposureReview += ` To trust ${exposureDestinations.length === 1 ? "this address" : "these addresses"} for good, the user runs: ` + exposureDestinations.map((d) => `node9 trust recipient add ${destinationValue(d)}`).join("; ") + ".";
|
|
16288
16540
|
}
|
|
16289
16541
|
} else if (held.length > 0 || writeHolds.length > 0 || logged.length > 0) {
|
|
16542
|
+
meta = {
|
|
16543
|
+
...meta,
|
|
16544
|
+
exposure: { kind: "logged", destinations: logged.length },
|
|
16545
|
+
...last?.eid ? { exposureFromEid: last.eid } : {}
|
|
16546
|
+
};
|
|
16290
16547
|
appendToLog(HOOK_DEBUG_LOG, {
|
|
16291
16548
|
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
16292
16549
|
event: "exposure-logged",
|
|
@@ -16840,6 +17097,7 @@ ${packageReview}`;
|
|
|
16840
17097
|
// deferReview and serverKey never co-occur in production — see above.)
|
|
16841
17098
|
reason: taintWarning || packageReview || exposureReview || explainableLabel || "Node9 flagged this action for review.",
|
|
16842
17099
|
...exposureReview && exposureKeys.length > 0 ? { exposureKeys } : {},
|
|
17100
|
+
...exposureReview && exposureFromEid ? { exposureFromEid } : {},
|
|
16843
17101
|
ruleDescription: policyRuleDescription,
|
|
16844
17102
|
blockedByLabel: explainableLabel
|
|
16845
17103
|
};
|