@mrciphersmith/keryx 0.3.17 → 0.3.19
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 +733 -320
- package/dist/core.js +491 -90
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -3913,6 +3913,19 @@ var init_provider_catalog_cache = __esm(() => {
|
|
|
3913
3913
|
|
|
3914
3914
|
// src/lib/oauth/open-url.ts
|
|
3915
3915
|
import { spawn } from "child_process";
|
|
3916
|
+
function isLoopbackHost(hostname) {
|
|
3917
|
+
return hostname === "localhost" || hostname === "127.0.0.1" || hostname === "::1" || hostname === "[::1]";
|
|
3918
|
+
}
|
|
3919
|
+
function isSafeVerificationUrl(url) {
|
|
3920
|
+
let parsed;
|
|
3921
|
+
try {
|
|
3922
|
+
parsed = new URL(url);
|
|
3923
|
+
} catch {
|
|
3924
|
+
return false;
|
|
3925
|
+
}
|
|
3926
|
+
const schemeOk = parsed.protocol === "https:" || parsed.protocol === "http:" && isLoopbackHost(parsed.hostname);
|
|
3927
|
+
return schemeOk && ALLOWED_URL_CHARS.test(url);
|
|
3928
|
+
}
|
|
3916
3929
|
function browserOpenPlan(url, platform = process.platform, env = process.env) {
|
|
3917
3930
|
if (url.length === 0) {
|
|
3918
3931
|
return;
|
|
@@ -3921,7 +3934,10 @@ function browserOpenPlan(url, platform = process.platform, env = process.env) {
|
|
|
3921
3934
|
return { cmd: "open", args: [url] };
|
|
3922
3935
|
}
|
|
3923
3936
|
if (platform === "win32") {
|
|
3924
|
-
|
|
3937
|
+
if (!isSafeVerificationUrl(url)) {
|
|
3938
|
+
return;
|
|
3939
|
+
}
|
|
3940
|
+
return { cmd: "rundll32", args: ["url.dll,FileProtocolHandler", url] };
|
|
3925
3941
|
}
|
|
3926
3942
|
if (typeof env.DISPLAY === "string" && env.DISPLAY.length > 0) {
|
|
3927
3943
|
return { cmd: "xdg-open", args: [url] };
|
|
@@ -3943,7 +3959,10 @@ function openVerificationUrl(url, deps = {}) {
|
|
|
3943
3959
|
child.unref();
|
|
3944
3960
|
} catch {}
|
|
3945
3961
|
}
|
|
3946
|
-
var
|
|
3962
|
+
var ALLOWED_URL_CHARS;
|
|
3963
|
+
var init_open_url = __esm(() => {
|
|
3964
|
+
ALLOWED_URL_CHARS = /^[A-Za-z0-9\-._~:/?#[\]@!$'()*+,;=%]+$/;
|
|
3965
|
+
});
|
|
3947
3966
|
|
|
3948
3967
|
// src/harness/policy/engine.ts
|
|
3949
3968
|
import path8 from "path";
|
|
@@ -4282,7 +4301,7 @@ function isPrivateEgressToken(token) {
|
|
|
4282
4301
|
function isPrivateEgressHost(host) {
|
|
4283
4302
|
return isPrivateEgressToken(host);
|
|
4284
4303
|
}
|
|
4285
|
-
function
|
|
4304
|
+
function isLoopbackHost2(host) {
|
|
4286
4305
|
if (typeof host !== "string" || host.length === 0)
|
|
4287
4306
|
return false;
|
|
4288
4307
|
const normalized = host.trim();
|
|
@@ -4415,7 +4434,7 @@ async function probeOllamaModels(deps, baseUrl) {
|
|
|
4415
4434
|
} catch {
|
|
4416
4435
|
host = baseUrl;
|
|
4417
4436
|
}
|
|
4418
|
-
if (isPrivateEgressHost(host) && !
|
|
4437
|
+
if (isPrivateEgressHost(host) && !isLoopbackHost2(host)) {
|
|
4419
4438
|
return;
|
|
4420
4439
|
}
|
|
4421
4440
|
let response;
|
|
@@ -9060,6 +9079,269 @@ var init_config = __esm(() => {
|
|
|
9060
9079
|
]);
|
|
9061
9080
|
});
|
|
9062
9081
|
|
|
9082
|
+
// src/security/detect/entropy.ts
|
|
9083
|
+
function isHexBlob(value) {
|
|
9084
|
+
return HEX_BLOB.test(value) && /[a-f]/i.test(value);
|
|
9085
|
+
}
|
|
9086
|
+
function isAllowShapedValue(value) {
|
|
9087
|
+
return FULL_GIT_SHA_RE.test(value) || UUID_RE.test(value) || INTEGRITY_RE.test(value);
|
|
9088
|
+
}
|
|
9089
|
+
function lastLineOf(before) {
|
|
9090
|
+
const newline = before.lastIndexOf(`
|
|
9091
|
+
`);
|
|
9092
|
+
return newline < 0 ? before : before.slice(newline + 1);
|
|
9093
|
+
}
|
|
9094
|
+
function isSingleTransitionTag(segment) {
|
|
9095
|
+
return /^[A-Za-z]+[0-9]+$/.test(segment) || /^[0-9]+[A-Za-z]+$/.test(segment);
|
|
9096
|
+
}
|
|
9097
|
+
function isWordSlug(value) {
|
|
9098
|
+
const segments = value.split(/[-_]/);
|
|
9099
|
+
if (segments.length < 3) {
|
|
9100
|
+
return false;
|
|
9101
|
+
}
|
|
9102
|
+
let words = 0;
|
|
9103
|
+
for (const segment of segments) {
|
|
9104
|
+
const isAlpha = /^[A-Za-z]+$/.test(segment);
|
|
9105
|
+
const isDigits = /^[0-9]+$/.test(segment);
|
|
9106
|
+
if (!isAlpha && !isDigits && !isSingleTransitionTag(segment)) {
|
|
9107
|
+
return false;
|
|
9108
|
+
}
|
|
9109
|
+
if (isAlpha && segment.length >= 3) {
|
|
9110
|
+
words += 1;
|
|
9111
|
+
}
|
|
9112
|
+
}
|
|
9113
|
+
return words >= 1;
|
|
9114
|
+
}
|
|
9115
|
+
function shannonEntropy(value) {
|
|
9116
|
+
const counts = new Map;
|
|
9117
|
+
for (const ch of value) {
|
|
9118
|
+
counts.set(ch, (counts.get(ch) ?? 0) + 1);
|
|
9119
|
+
}
|
|
9120
|
+
let entropy = 0;
|
|
9121
|
+
for (const count of counts.values()) {
|
|
9122
|
+
const p = count / value.length;
|
|
9123
|
+
entropy -= p * Math.log2(p);
|
|
9124
|
+
}
|
|
9125
|
+
return entropy;
|
|
9126
|
+
}
|
|
9127
|
+
function bareShapeQualifies(head) {
|
|
9128
|
+
if (!/[0-9]/.test(head) && !/[+=]/.test(head)) {
|
|
9129
|
+
return { qualifies: false, entropy: 0 };
|
|
9130
|
+
}
|
|
9131
|
+
if (isWordSlug(head)) {
|
|
9132
|
+
return { qualifies: false, entropy: 0 };
|
|
9133
|
+
}
|
|
9134
|
+
const entropy = shannonEntropy(head);
|
|
9135
|
+
const hexBlob = isHexBlob(head);
|
|
9136
|
+
if (entropy < 3.6 && !hexBlob) {
|
|
9137
|
+
return { qualifies: false, entropy };
|
|
9138
|
+
}
|
|
9139
|
+
return { qualifies: true, entropy };
|
|
9140
|
+
}
|
|
9141
|
+
function shapeQualifiesAsSecret(value) {
|
|
9142
|
+
if (isAllowShapedValue(value)) {
|
|
9143
|
+
return { qualifies: false, entropy: 0 };
|
|
9144
|
+
}
|
|
9145
|
+
return bareShapeQualifies(value);
|
|
9146
|
+
}
|
|
9147
|
+
function looksSecretShaped(value) {
|
|
9148
|
+
return shapeQualifiesAsSecret(value).qualifies;
|
|
9149
|
+
}
|
|
9150
|
+
function buildMatch(start, end, value, entropy) {
|
|
9151
|
+
const confidence = Math.max(0.4, Math.min(0.7, 0.4 + (entropy - 3.6) * 0.21));
|
|
9152
|
+
return {
|
|
9153
|
+
category: "secret",
|
|
9154
|
+
policyId: "secrets.high-entropy",
|
|
9155
|
+
severity: "medium",
|
|
9156
|
+
confidence: Number(confidence.toFixed(2)),
|
|
9157
|
+
start,
|
|
9158
|
+
end,
|
|
9159
|
+
value,
|
|
9160
|
+
mask: "entropy",
|
|
9161
|
+
remediation: "Verify this high-entropy value is not a live credential."
|
|
9162
|
+
};
|
|
9163
|
+
}
|
|
9164
|
+
function findUrlSpans(content) {
|
|
9165
|
+
const spans = [];
|
|
9166
|
+
URL_SPAN_RE.lastIndex = 0;
|
|
9167
|
+
let m;
|
|
9168
|
+
while ((m = URL_SPAN_RE.exec(content)) !== null) {
|
|
9169
|
+
let raw = m[0];
|
|
9170
|
+
while (raw.length > 0 && URL_TRAILING_PUNCTUATION_RE.test(raw)) {
|
|
9171
|
+
raw = raw.slice(0, -1);
|
|
9172
|
+
}
|
|
9173
|
+
if (raw.length > 0) {
|
|
9174
|
+
spans.push({ start: m.index, end: m.index + raw.length, raw });
|
|
9175
|
+
}
|
|
9176
|
+
if (m.index === URL_SPAN_RE.lastIndex) {
|
|
9177
|
+
URL_SPAN_RE.lastIndex += 1;
|
|
9178
|
+
}
|
|
9179
|
+
}
|
|
9180
|
+
return spans;
|
|
9181
|
+
}
|
|
9182
|
+
function urlComponentSpans(raw) {
|
|
9183
|
+
const spans = [];
|
|
9184
|
+
const schemeMatch = /^[a-zA-Z][a-zA-Z0-9+.-]*:\/\//.exec(raw);
|
|
9185
|
+
if (schemeMatch === null) {
|
|
9186
|
+
return spans;
|
|
9187
|
+
}
|
|
9188
|
+
let i = schemeMatch[0].length;
|
|
9189
|
+
while (i < raw.length && raw[i] !== "/" && raw[i] !== "?" && raw[i] !== "#") {
|
|
9190
|
+
i += 1;
|
|
9191
|
+
}
|
|
9192
|
+
let pathEnd = i;
|
|
9193
|
+
while (pathEnd < raw.length && raw[pathEnd] !== "?" && raw[pathEnd] !== "#") {
|
|
9194
|
+
pathEnd += 1;
|
|
9195
|
+
}
|
|
9196
|
+
let segStart = i;
|
|
9197
|
+
for (let j = i;j <= pathEnd; j += 1) {
|
|
9198
|
+
if (j === pathEnd || raw[j] === "/") {
|
|
9199
|
+
if (j > segStart) {
|
|
9200
|
+
spans.push({ start: segStart, end: j });
|
|
9201
|
+
}
|
|
9202
|
+
segStart = j + 1;
|
|
9203
|
+
}
|
|
9204
|
+
}
|
|
9205
|
+
let cursor = pathEnd;
|
|
9206
|
+
if (cursor < raw.length && raw[cursor] === "?") {
|
|
9207
|
+
let queryEnd = cursor + 1;
|
|
9208
|
+
while (queryEnd < raw.length && raw[queryEnd] !== "#") {
|
|
9209
|
+
queryEnd += 1;
|
|
9210
|
+
}
|
|
9211
|
+
let pairStart = cursor + 1;
|
|
9212
|
+
for (let j = cursor + 1;j <= queryEnd; j += 1) {
|
|
9213
|
+
if (j === queryEnd || raw[j] === "&") {
|
|
9214
|
+
const pair = raw.slice(pairStart, j);
|
|
9215
|
+
const eq = pair.indexOf("=");
|
|
9216
|
+
if (eq !== -1 && eq + 1 < pair.length) {
|
|
9217
|
+
spans.push({ start: pairStart + eq + 1, end: j });
|
|
9218
|
+
} else if (eq === -1 && pair.length > 0) {
|
|
9219
|
+
spans.push({ start: pairStart, end: j });
|
|
9220
|
+
}
|
|
9221
|
+
pairStart = j + 1;
|
|
9222
|
+
}
|
|
9223
|
+
}
|
|
9224
|
+
cursor = queryEnd;
|
|
9225
|
+
}
|
|
9226
|
+
if (cursor < raw.length && raw[cursor] === "#" && cursor + 1 < raw.length) {
|
|
9227
|
+
spans.push({ start: cursor + 1, end: raw.length });
|
|
9228
|
+
}
|
|
9229
|
+
return spans;
|
|
9230
|
+
}
|
|
9231
|
+
function secretShapedCandidatesIn(text) {
|
|
9232
|
+
const normalized = text.replace(/\+/g, " ");
|
|
9233
|
+
const out = [];
|
|
9234
|
+
TOKEN.lastIndex = 0;
|
|
9235
|
+
let m;
|
|
9236
|
+
while ((m = TOKEN.exec(normalized)) !== null) {
|
|
9237
|
+
const value = m[0];
|
|
9238
|
+
if (value.length < URL_COMPONENT_MIN_LENGTH) {
|
|
9239
|
+
continue;
|
|
9240
|
+
}
|
|
9241
|
+
const head = TOKEN_HEAD.exec(value)?.[0] ?? value;
|
|
9242
|
+
const { qualifies, entropy } = shapeQualifiesAsSecret(head);
|
|
9243
|
+
if (!qualifies) {
|
|
9244
|
+
continue;
|
|
9245
|
+
}
|
|
9246
|
+
const start = m.index;
|
|
9247
|
+
const end = start + value.length;
|
|
9248
|
+
out.push({ start, end, value: text.slice(start, end), entropy });
|
|
9249
|
+
}
|
|
9250
|
+
return out;
|
|
9251
|
+
}
|
|
9252
|
+
function looksSecretShapedIn(text) {
|
|
9253
|
+
return secretShapedCandidatesIn(text).length > 0;
|
|
9254
|
+
}
|
|
9255
|
+
function detectEntropy(content) {
|
|
9256
|
+
const matches = [];
|
|
9257
|
+
const urlSpans = findUrlSpans(content);
|
|
9258
|
+
for (const url of urlSpans) {
|
|
9259
|
+
for (const comp of urlComponentSpans(url.raw)) {
|
|
9260
|
+
const compText = url.raw.slice(comp.start, comp.end);
|
|
9261
|
+
for (const cand of secretShapedCandidatesIn(compText)) {
|
|
9262
|
+
const absStart = url.start + comp.start + cand.start;
|
|
9263
|
+
const absEnd = url.start + comp.start + cand.end;
|
|
9264
|
+
matches.push(buildMatch(absStart, absEnd, cand.value, cand.entropy));
|
|
9265
|
+
}
|
|
9266
|
+
}
|
|
9267
|
+
}
|
|
9268
|
+
LABEL_ASSIGNMENT_RE.lastIndex = 0;
|
|
9269
|
+
let am;
|
|
9270
|
+
while ((am = LABEL_ASSIGNMENT_RE.exec(content)) !== null) {
|
|
9271
|
+
const value = am[1];
|
|
9272
|
+
const end = am.index + am[0].length;
|
|
9273
|
+
const start = end - value.length;
|
|
9274
|
+
if (urlSpans.some((u) => start < u.end && end > u.start)) {
|
|
9275
|
+
continue;
|
|
9276
|
+
}
|
|
9277
|
+
const { qualifies, entropy } = bareShapeQualifies(value);
|
|
9278
|
+
if (qualifies) {
|
|
9279
|
+
matches.push(buildMatch(start, end, value, entropy));
|
|
9280
|
+
}
|
|
9281
|
+
}
|
|
9282
|
+
TOKEN.lastIndex = 0;
|
|
9283
|
+
let m;
|
|
9284
|
+
while ((m = TOKEN.exec(content)) !== null) {
|
|
9285
|
+
const value = m[0];
|
|
9286
|
+
const start = m.index;
|
|
9287
|
+
const end = start + value.length;
|
|
9288
|
+
if (urlSpans.some((u) => start < u.end && end > u.start)) {
|
|
9289
|
+
continue;
|
|
9290
|
+
}
|
|
9291
|
+
if (matches.some((existing) => start < existing.end && end > existing.start)) {
|
|
9292
|
+
continue;
|
|
9293
|
+
}
|
|
9294
|
+
const head = TOKEN_HEAD.exec(value)?.[0] ?? value;
|
|
9295
|
+
const { qualifies, entropy } = bareShapeQualifies(head);
|
|
9296
|
+
if (!qualifies) {
|
|
9297
|
+
continue;
|
|
9298
|
+
}
|
|
9299
|
+
const before = lastLineOf(content.slice(Math.max(0, start - LABEL_WINDOW), start));
|
|
9300
|
+
if (!SENSITIVE_LABEL.test(before)) {
|
|
9301
|
+
continue;
|
|
9302
|
+
}
|
|
9303
|
+
if (isAllowShapedValue(head) && !ADJACENT_LABEL.test(before)) {
|
|
9304
|
+
continue;
|
|
9305
|
+
}
|
|
9306
|
+
matches.push(buildMatch(start, end, value, entropy));
|
|
9307
|
+
}
|
|
9308
|
+
return matches.sort((a, b) => a.start - b.start);
|
|
9309
|
+
}
|
|
9310
|
+
var SENSITIVE_LABEL_WORDS = "key|secret|token|password|passwd|api|credential|auth|bearer", caseless = (words) => words.replace(/[a-z]/g, (ch) => `[${ch}${ch.toUpperCase()}]`), SENSITIVE_LABEL, ADJACENT_LABEL, LABEL_ASSIGNMENT_RE, TOKEN, TOKEN_HEAD, LABEL_WINDOW = 40, HEX_BLOB, FULL_GIT_SHA_RE, UUID_RE, INTEGRITY_RE, URL_SPAN_RE, URL_TRAILING_PUNCTUATION_RE, URL_COMPONENT_MIN_LENGTH = 20;
|
|
9311
|
+
var init_entropy = __esm(() => {
|
|
9312
|
+
SENSITIVE_LABEL = new RegExp(`(?:^|[^A-Za-z0-9])(${caseless(SENSITIVE_LABEL_WORDS)})(?:$|[^A-Za-z0-9]|(?=[A-Z]))`);
|
|
9313
|
+
ADJACENT_LABEL = new RegExp(`(?:^|[^A-Za-z0-9])(?:${caseless(SENSITIVE_LABEL_WORDS)})[A-Za-z0-9_-]*["']?\\s*[:=]?\\s*["']?$`);
|
|
9314
|
+
LABEL_ASSIGNMENT_RE = new RegExp(`(?:^|[^A-Za-z0-9])(?:${SENSITIVE_LABEL_WORDS})[A-Za-z0-9_-]*"?\\s*=\\s*"?([A-Za-z0-9+/_=-]{6,})`, "gi");
|
|
9315
|
+
TOKEN = /[A-Za-z0-9+/=_-]{20,}(?:\.[A-Za-z0-9+/=_-]{6,})*/g;
|
|
9316
|
+
TOKEN_HEAD = /^[A-Za-z0-9+/=_-]+/;
|
|
9317
|
+
HEX_BLOB = /^[0-9a-f]{24,}$/i;
|
|
9318
|
+
FULL_GIT_SHA_RE = /^[0-9a-f]{40}$/i;
|
|
9319
|
+
UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
9320
|
+
INTEGRITY_RE = /^(?:sha256|sha512)-[A-Za-z0-9+/]+=*$/;
|
|
9321
|
+
URL_SPAN_RE = /\bhttps?:\/\/[^\s<>"'\\]+/g;
|
|
9322
|
+
URL_TRAILING_PUNCTUATION_RE = /[.,;:!?)\]}]$/;
|
|
9323
|
+
});
|
|
9324
|
+
|
|
9325
|
+
// src/security/entropy-gate.ts
|
|
9326
|
+
function ensureLoading() {
|
|
9327
|
+
if (loadStarted) {
|
|
9328
|
+
return;
|
|
9329
|
+
}
|
|
9330
|
+
loadStarted = true;
|
|
9331
|
+
loadSecurityConfig(process.cwd()).then((config) => {
|
|
9332
|
+
cachedEnabled = config.backends.entropy.enabled;
|
|
9333
|
+
}).catch(() => {});
|
|
9334
|
+
}
|
|
9335
|
+
function isEntropyBackendEnabled() {
|
|
9336
|
+
ensureLoading();
|
|
9337
|
+
return cachedEnabled;
|
|
9338
|
+
}
|
|
9339
|
+
var cachedEnabled, loadStarted = false;
|
|
9340
|
+
var init_entropy_gate = __esm(() => {
|
|
9341
|
+
init_config();
|
|
9342
|
+
cachedEnabled = DEFAULT_SECURITY_CONFIG.backends.entropy.enabled;
|
|
9343
|
+
});
|
|
9344
|
+
|
|
9063
9345
|
// src/security/redact.ts
|
|
9064
9346
|
import path14 from "path";
|
|
9065
9347
|
import { createHmac, randomBytes } from "crypto";
|
|
@@ -9129,7 +9411,12 @@ function redactSensitiveText(text) {
|
|
|
9129
9411
|
if (text.length === 0) {
|
|
9130
9412
|
return text;
|
|
9131
9413
|
}
|
|
9132
|
-
const matches = [
|
|
9414
|
+
const matches = [
|
|
9415
|
+
...detectSecrets(text),
|
|
9416
|
+
...detectPii(text),
|
|
9417
|
+
...detectExfil(text),
|
|
9418
|
+
...isEntropyBackendEnabled() ? detectEntropy(text) : []
|
|
9419
|
+
];
|
|
9133
9420
|
if (matches.length === 0) {
|
|
9134
9421
|
return text;
|
|
9135
9422
|
}
|
|
@@ -9166,6 +9453,8 @@ var init_redact = __esm(() => {
|
|
|
9166
9453
|
init_secrets();
|
|
9167
9454
|
init_pii();
|
|
9168
9455
|
init_exfil();
|
|
9456
|
+
init_entropy();
|
|
9457
|
+
init_entropy_gate();
|
|
9169
9458
|
});
|
|
9170
9459
|
|
|
9171
9460
|
// src/security/output-validation.ts
|
|
@@ -9909,102 +10198,33 @@ var init_seam = __esm(() => {
|
|
|
9909
10198
|
init_warn_once();
|
|
9910
10199
|
});
|
|
9911
10200
|
|
|
9912
|
-
// src/security/detect/
|
|
9913
|
-
function
|
|
9914
|
-
|
|
9915
|
-
|
|
9916
|
-
|
|
9917
|
-
|
|
9918
|
-
function isWordSlug(value) {
|
|
9919
|
-
const segments = value.split(/[-_]/);
|
|
9920
|
-
if (segments.length < 3) {
|
|
9921
|
-
return false;
|
|
9922
|
-
}
|
|
9923
|
-
let words = 0;
|
|
9924
|
-
for (const segment of segments) {
|
|
9925
|
-
const isAlpha = /^[A-Za-z]+$/.test(segment);
|
|
9926
|
-
const isDigits = /^[0-9]+$/.test(segment);
|
|
9927
|
-
if (!isAlpha && !isDigits) {
|
|
9928
|
-
return false;
|
|
9929
|
-
}
|
|
9930
|
-
if (isAlpha && segment.length >= 3) {
|
|
9931
|
-
words += 1;
|
|
9932
|
-
}
|
|
9933
|
-
}
|
|
9934
|
-
return words >= 2;
|
|
9935
|
-
}
|
|
9936
|
-
function shannonEntropy(value) {
|
|
9937
|
-
const counts = new Map;
|
|
9938
|
-
for (const ch of value) {
|
|
9939
|
-
counts.set(ch, (counts.get(ch) ?? 0) + 1);
|
|
9940
|
-
}
|
|
9941
|
-
let entropy = 0;
|
|
9942
|
-
for (const count of counts.values()) {
|
|
9943
|
-
const p = count / value.length;
|
|
9944
|
-
entropy -= p * Math.log2(p);
|
|
9945
|
-
}
|
|
9946
|
-
return entropy;
|
|
9947
|
-
}
|
|
9948
|
-
function detectEntropy(content) {
|
|
9949
|
-
const matches = [];
|
|
9950
|
-
TOKEN.lastIndex = 0;
|
|
9951
|
-
let m;
|
|
9952
|
-
while ((m = TOKEN.exec(content)) !== null) {
|
|
9953
|
-
const value = m[0];
|
|
9954
|
-
const head = TOKEN_HEAD.exec(value)?.[0] ?? value;
|
|
9955
|
-
if (!/[0-9]/.test(head) && !/[+=]/.test(head)) {
|
|
9956
|
-
continue;
|
|
9957
|
-
}
|
|
9958
|
-
if (isWordSlug(head)) {
|
|
9959
|
-
continue;
|
|
9960
|
-
}
|
|
9961
|
-
const entropy = shannonEntropy(head);
|
|
9962
|
-
const hexBlob = HEX_BLOB.test(head);
|
|
9963
|
-
if (entropy < 3.6 && !hexBlob) {
|
|
9964
|
-
continue;
|
|
9965
|
-
}
|
|
9966
|
-
const before = lastLineOf(content.slice(Math.max(0, m.index - LABEL_WINDOW), m.index));
|
|
9967
|
-
if (!SENSITIVE_LABEL.test(before)) {
|
|
9968
|
-
continue;
|
|
9969
|
-
}
|
|
9970
|
-
const confidence = Math.max(0.4, Math.min(0.7, 0.4 + (entropy - 3.6) * 0.21));
|
|
9971
|
-
matches.push({
|
|
9972
|
-
category: "secret",
|
|
9973
|
-
policyId: "secrets.high-entropy",
|
|
9974
|
-
severity: "medium",
|
|
9975
|
-
confidence: Number(confidence.toFixed(2)),
|
|
9976
|
-
start: m.index,
|
|
9977
|
-
end: m.index + value.length,
|
|
9978
|
-
value,
|
|
9979
|
-
mask: "secret",
|
|
9980
|
-
remediation: "Verify this high-entropy value is not a live credential."
|
|
9981
|
-
});
|
|
10201
|
+
// src/security/detect/injection.ts
|
|
10202
|
+
function foldForMatching(content) {
|
|
10203
|
+
let folded = "";
|
|
10204
|
+
for (let i = 0;i < content.length; i += 1) {
|
|
10205
|
+
const ch = content[i];
|
|
10206
|
+
folded += CONFUSABLES[ch] ?? ch;
|
|
9982
10207
|
}
|
|
9983
|
-
|
|
10208
|
+
const nfkc = folded.normalize("NFKC");
|
|
10209
|
+
return nfkc.length === folded.length ? nfkc : folded;
|
|
9984
10210
|
}
|
|
9985
|
-
var SENSITIVE_LABEL, TOKEN, TOKEN_HEAD, LABEL_WINDOW = 40, HEX_BLOB;
|
|
9986
|
-
var init_entropy = __esm(() => {
|
|
9987
|
-
SENSITIVE_LABEL = /(key|secret|token|password|passwd|api|credential|auth)/i;
|
|
9988
|
-
TOKEN = /[A-Za-z0-9+/=_-]{20,}(?:\.[A-Za-z0-9+/=_-]{6,})*/g;
|
|
9989
|
-
TOKEN_HEAD = /^[A-Za-z0-9+/=_-]+/;
|
|
9990
|
-
HEX_BLOB = /^[0-9a-f]{24,}$/i;
|
|
9991
|
-
});
|
|
9992
|
-
|
|
9993
|
-
// src/security/detect/injection.ts
|
|
9994
10211
|
function detectInjection(content) {
|
|
9995
10212
|
const matches = [];
|
|
10213
|
+
const folded = foldForMatching(content);
|
|
9996
10214
|
for (const pattern of PATTERNS) {
|
|
9997
10215
|
pattern.regex.lastIndex = 0;
|
|
9998
10216
|
let m;
|
|
9999
|
-
while ((m = pattern.regex.exec(
|
|
10217
|
+
while ((m = pattern.regex.exec(folded)) !== null) {
|
|
10218
|
+
const start = Math.min(m.index, content.length);
|
|
10219
|
+
const end = Math.min(m.index + m[0].length, content.length);
|
|
10000
10220
|
matches.push({
|
|
10001
10221
|
category: "prompt-injection",
|
|
10002
10222
|
policyId: pattern.policyId,
|
|
10003
10223
|
severity: "low",
|
|
10004
10224
|
confidence: pattern.confidence,
|
|
10005
|
-
start
|
|
10006
|
-
end
|
|
10007
|
-
value:
|
|
10225
|
+
start,
|
|
10226
|
+
end,
|
|
10227
|
+
value: content.slice(start, end),
|
|
10008
10228
|
remediation: "Treat external content as data, not instruction; require human review."
|
|
10009
10229
|
});
|
|
10010
10230
|
if (m.index === pattern.regex.lastIndex) {
|
|
@@ -10014,27 +10234,69 @@ function detectInjection(content) {
|
|
|
10014
10234
|
}
|
|
10015
10235
|
return matches;
|
|
10016
10236
|
}
|
|
10017
|
-
var PATTERNS;
|
|
10237
|
+
var CONFUSABLES, PATTERNS;
|
|
10018
10238
|
var init_injection = __esm(() => {
|
|
10239
|
+
CONFUSABLES = {
|
|
10240
|
+
"\u0430": "a",
|
|
10241
|
+
"\u0410": "A",
|
|
10242
|
+
"\u0435": "e",
|
|
10243
|
+
"\u0415": "E",
|
|
10244
|
+
"\u043E": "o",
|
|
10245
|
+
"\u041E": "O",
|
|
10246
|
+
"\u0440": "p",
|
|
10247
|
+
"\u0420": "P",
|
|
10248
|
+
"\u0441": "c",
|
|
10249
|
+
"\u0421": "C",
|
|
10250
|
+
"\u0445": "x",
|
|
10251
|
+
"\u0425": "X",
|
|
10252
|
+
"\u0443": "y",
|
|
10253
|
+
"\u0423": "Y",
|
|
10254
|
+
"\u0455": "s",
|
|
10255
|
+
"\u0405": "S",
|
|
10256
|
+
"\u0456": "o",
|
|
10257
|
+
"\u0406": "O",
|
|
10258
|
+
"\u0458": "j",
|
|
10259
|
+
"\u0408": "J",
|
|
10260
|
+
"\u03B1": "a",
|
|
10261
|
+
"\u0391": "A",
|
|
10262
|
+
"\u03B2": "b",
|
|
10263
|
+
"\u0392": "B",
|
|
10264
|
+
"\u03B5": "e",
|
|
10265
|
+
"\u0395": "E",
|
|
10266
|
+
"\u03B9": "i",
|
|
10267
|
+
"\u0399": "I",
|
|
10268
|
+
"\u03BA": "k",
|
|
10269
|
+
"\u039A": "K",
|
|
10270
|
+
"\u03BF": "o",
|
|
10271
|
+
"\u039F": "O",
|
|
10272
|
+
"\u03C1": "p",
|
|
10273
|
+
"\u03A1": "P",
|
|
10274
|
+
"\u03C4": "t",
|
|
10275
|
+
"\u03A4": "T",
|
|
10276
|
+
"\u03C5": "u",
|
|
10277
|
+
"\u03A5": "Y",
|
|
10278
|
+
"\u03C7": "x",
|
|
10279
|
+
"\u03A7": "X"
|
|
10280
|
+
};
|
|
10019
10281
|
PATTERNS = [
|
|
10020
10282
|
{
|
|
10021
10283
|
policyId: "prompt-injection.ignore-instructions",
|
|
10022
|
-
regex: /\b(ignore|disregard|forget|override)\b[
|
|
10284
|
+
regex: /\b(ignore|disregard|forget|override)\b[^.]{0,40}\b(all\s+)?(previous|prior|above|earlier|system|developer)\b[^.]{0,20}\b(instructions?|prompts?|rules?|context)\b/gi,
|
|
10023
10285
|
confidence: 0.4
|
|
10024
10286
|
},
|
|
10025
10287
|
{
|
|
10026
10288
|
policyId: "prompt-injection.reveal-context",
|
|
10027
|
-
regex: /\b(reveal|show|print|expose|leak|dump|repeat)\b[
|
|
10289
|
+
regex: /\b(reveal|show|print|expose|leak|dump|repeat)\b[^.]{0,30}\b(your\s+)?(system\s+prompt|instructions?|memory|secrets?|hidden\s+context|api\s*keys?)\b/gi,
|
|
10028
10290
|
confidence: 0.45
|
|
10029
10291
|
},
|
|
10030
10292
|
{
|
|
10031
10293
|
policyId: "prompt-injection.role-override",
|
|
10032
|
-
regex: /\b(you\s+are\s+now|from\s+now\s+on|act\s+as|pretend\s+to\s+be)\b[
|
|
10294
|
+
regex: /\b(you\s+are\s+now|from\s+now\s+on|act\s+as|pretend\s+to\s+be)\b[^.]{0,40}\b(unrestricted|jailbroken|dan|admin|developer\s+mode|no\s+rules)\b/gi,
|
|
10033
10295
|
confidence: 0.4
|
|
10034
10296
|
},
|
|
10035
10297
|
{
|
|
10036
10298
|
policyId: "prompt-injection.priority-override",
|
|
10037
|
-
regex: /\b(treat|follow|obey)\b[
|
|
10299
|
+
regex: /\b(treat|follow|obey)\b[^.]{0,30}\b(the\s+)?(following|external|below)\b[^.]{0,20}\b(as\s+)?(instructions?|higher\s+priority|commands?)\b/gi,
|
|
10038
10300
|
confidence: 0.35
|
|
10039
10301
|
}
|
|
10040
10302
|
];
|
|
@@ -11204,6 +11466,140 @@ var init_read_source = __esm(() => {
|
|
|
11204
11466
|
init_contained_path();
|
|
11205
11467
|
});
|
|
11206
11468
|
|
|
11469
|
+
// src/security/credential-shape.ts
|
|
11470
|
+
function isDeniedForMcpChild(name, value) {
|
|
11471
|
+
if (value !== undefined && CREDENTIAL_IN_VALUE_RE.test(value))
|
|
11472
|
+
return true;
|
|
11473
|
+
const upper = name.toUpperCase();
|
|
11474
|
+
if (MCP_ENV_ALLOW_EXACT.includes(upper))
|
|
11475
|
+
return false;
|
|
11476
|
+
const conditional = VALUE_DEPENDENT[upper];
|
|
11477
|
+
if (conditional !== undefined) {
|
|
11478
|
+
return value === undefined ? true : conditional(value);
|
|
11479
|
+
}
|
|
11480
|
+
if (EXTERNAL_ENV_DENY.some((denied) => denied.toUpperCase() === upper))
|
|
11481
|
+
return true;
|
|
11482
|
+
if (MCP_ENV_DENY.includes(upper))
|
|
11483
|
+
return true;
|
|
11484
|
+
if (EXTERNAL_ENV_PREFIX_SWEEPS.some((prefix) => upper.startsWith(prefix.toUpperCase())))
|
|
11485
|
+
return true;
|
|
11486
|
+
if (MCP_ENV_PREFIX_SWEEPS.some((prefix) => upper.startsWith(prefix)))
|
|
11487
|
+
return true;
|
|
11488
|
+
return SECRET_SEGMENT_RE.test(upper) || SECRET_SUBSTRING_RE.test(upper) || GLUED_SECRET_RE.test(upper) || CONNECTION_STRING_RE.test(upper);
|
|
11489
|
+
}
|
|
11490
|
+
var EXTERNAL_ENV_DENY, EXTERNAL_ENV_PREFIX_SWEEPS, MCP_ENV_ALLOW_EXACT, MCP_ENV_DENY, MCP_ENV_PREFIX_SWEEPS, SECRET_SEGMENTS, SECRET_SEGMENT_RE, SECRET_SUBSTRING_RE, GLUED_SECRET_RE, CONNECTION_STRING_RE, CREDENTIAL_IN_VALUE_RE, VALUE_DEPENDENT;
|
|
11491
|
+
var init_credential_shape = __esm(() => {
|
|
11492
|
+
EXTERNAL_ENV_DENY = [
|
|
11493
|
+
"ANTHROPIC_API_KEY",
|
|
11494
|
+
"ANTHROPIC_AUTH_TOKEN",
|
|
11495
|
+
"ANTHROPIC_BASE_URL",
|
|
11496
|
+
"ANTHROPIC_MODEL",
|
|
11497
|
+
"CLAUDE_CONFIG_DIR",
|
|
11498
|
+
"CODEX_HOME",
|
|
11499
|
+
"CLAUDECODE"
|
|
11500
|
+
];
|
|
11501
|
+
EXTERNAL_ENV_PREFIX_SWEEPS = ["CLAUDE_CODE_", "KERYX_"];
|
|
11502
|
+
MCP_ENV_ALLOW_EXACT = ["PWD", "OLDPWD"];
|
|
11503
|
+
MCP_ENV_DENY = [
|
|
11504
|
+
"SSH_AUTH_SOCK",
|
|
11505
|
+
"SSH_AGENT_PID",
|
|
11506
|
+
"GPG_AGENT_INFO",
|
|
11507
|
+
"GNUPGHOME",
|
|
11508
|
+
"GNOME_KEYRING_CONTROL",
|
|
11509
|
+
"GNOME_KEYRING_PID",
|
|
11510
|
+
"KDE_FULL_SESSION",
|
|
11511
|
+
"GIT_ASKPASS",
|
|
11512
|
+
"SSH_ASKPASS",
|
|
11513
|
+
"SUDO_ASKPASS",
|
|
11514
|
+
"GIT_CREDENTIAL_HELPER",
|
|
11515
|
+
"GIT_SSH_COMMAND",
|
|
11516
|
+
"KUBECONFIG",
|
|
11517
|
+
"NETRC",
|
|
11518
|
+
"PGPASSFILE",
|
|
11519
|
+
"PGSERVICEFILE",
|
|
11520
|
+
"GOOGLE_APPLICATION_CREDENTIALS",
|
|
11521
|
+
"CLOUDSDK_CONFIG",
|
|
11522
|
+
"DOCKER_CONFIG",
|
|
11523
|
+
"AWS_SHARED_CREDENTIALS_FILE",
|
|
11524
|
+
"AWS_CONFIG_FILE",
|
|
11525
|
+
"BOTO_CONFIG",
|
|
11526
|
+
"AZURE_CONFIG_DIR",
|
|
11527
|
+
"CURLRC",
|
|
11528
|
+
"WGETRC",
|
|
11529
|
+
"NPMRC",
|
|
11530
|
+
"NPM_CONFIG_USERCONFIG",
|
|
11531
|
+
"PIP_CONFIG_FILE",
|
|
11532
|
+
"RCLONE_CONFIG",
|
|
11533
|
+
"MAVEN_SETTINGS",
|
|
11534
|
+
"GIT_CONFIG",
|
|
11535
|
+
"GIT_CONFIG_GLOBAL",
|
|
11536
|
+
"HGRCPATH",
|
|
11537
|
+
"ANSIBLE_VAULT_PASSWORD_FILE",
|
|
11538
|
+
"AZURE_CLIENT_CERTIFICATE_PATH",
|
|
11539
|
+
"ARM_CLIENT_CERTIFICATE_PATH",
|
|
11540
|
+
"VAULT_CLIENT_CERT",
|
|
11541
|
+
"VAULT_CLIENT_KEY",
|
|
11542
|
+
"SSL_CLIENT_CERT_PATH",
|
|
11543
|
+
"SSL_CLIENT_KEY_FILE",
|
|
11544
|
+
"IDENTITY_FILE",
|
|
11545
|
+
"SSH_IDENTITY_FILE",
|
|
11546
|
+
"GCP_SERVICE_ACCOUNT",
|
|
11547
|
+
"XAUTHORITY",
|
|
11548
|
+
"OPENAI_API_KEY",
|
|
11549
|
+
"GEMINI_API_KEY",
|
|
11550
|
+
"GOOGLE_API_KEY",
|
|
11551
|
+
"GROQ_API_KEY",
|
|
11552
|
+
"MISTRAL_API_KEY",
|
|
11553
|
+
"XAI_API_KEY",
|
|
11554
|
+
"DEEPSEEK_API_KEY",
|
|
11555
|
+
"OPENROUTER_API_KEY",
|
|
11556
|
+
"TOGETHER_API_KEY",
|
|
11557
|
+
"PERPLEXITY_API_KEY"
|
|
11558
|
+
];
|
|
11559
|
+
MCP_ENV_PREFIX_SWEEPS = [
|
|
11560
|
+
"OP_SESSION",
|
|
11561
|
+
"BW_SESSION"
|
|
11562
|
+
];
|
|
11563
|
+
SECRET_SEGMENTS = [
|
|
11564
|
+
"KEY",
|
|
11565
|
+
"KEYS",
|
|
11566
|
+
"TOKEN",
|
|
11567
|
+
"TOKENS",
|
|
11568
|
+
"SECRET",
|
|
11569
|
+
"SECRETS",
|
|
11570
|
+
"PASSWORD",
|
|
11571
|
+
"PASSWD",
|
|
11572
|
+
"PWD",
|
|
11573
|
+
"PASS",
|
|
11574
|
+
"PASSPHRASE",
|
|
11575
|
+
"PASSCODE",
|
|
11576
|
+
"PASSKEY",
|
|
11577
|
+
"CREDENTIAL",
|
|
11578
|
+
"CREDENTIALS",
|
|
11579
|
+
"CREDS",
|
|
11580
|
+
"AUTH",
|
|
11581
|
+
"AUTHORIZATION",
|
|
11582
|
+
"BEARER",
|
|
11583
|
+
"JWT",
|
|
11584
|
+
"COOKIE",
|
|
11585
|
+
"SIGNATURE",
|
|
11586
|
+
"APIKEY",
|
|
11587
|
+
"SECRETKEY",
|
|
11588
|
+
"ACCESSKEY",
|
|
11589
|
+
"AUTHTOKEN",
|
|
11590
|
+
"PAT",
|
|
11591
|
+
"WEBHOOK"
|
|
11592
|
+
];
|
|
11593
|
+
SECRET_SEGMENT_RE = new RegExp(`(^|_)(${SECRET_SEGMENTS.join("|")})($|_)`);
|
|
11594
|
+
SECRET_SUBSTRING_RE = /(PASSWORD|PASSWD|PASSPHRASE|PASSCODE|SSHPASS|SECRET|CREDENTIAL|APIKEY|KEYFILE|PRIVKEY)/;
|
|
11595
|
+
GLUED_SECRET_RE = /(PRIVATE|SECRET|ACCESS|REFRESH|SESSION|CLIENT|API|APP)(KEY|TOKEN|SECRET)($|_)|DB(PASS|PWD)($|_)/;
|
|
11596
|
+
CONNECTION_STRING_RE = /(^|_)(DSN|CONNECTION_STRING|CONNECTIONSTRING)($|_)/;
|
|
11597
|
+
CREDENTIAL_IN_VALUE_RE = /:\/\/[^/\s@]*(:[^/\s@]*)?@/;
|
|
11598
|
+
VALUE_DEPENDENT = {
|
|
11599
|
+
DOCKER_HOST: (value) => !value.startsWith("unix://") && !value.startsWith("fd://")
|
|
11600
|
+
};
|
|
11601
|
+
});
|
|
11602
|
+
|
|
11207
11603
|
// src/security/audit-harness/codex-toml.ts
|
|
11208
11604
|
function stripComment(line) {
|
|
11209
11605
|
let quoted = false;
|
|
@@ -17641,7 +18037,7 @@ var package_default;
|
|
|
17641
18037
|
var init_package = __esm(() => {
|
|
17642
18038
|
package_default = {
|
|
17643
18039
|
name: "@mrciphersmith/keryx",
|
|
17644
|
-
version: "0.3.
|
|
18040
|
+
version: "0.3.19",
|
|
17645
18041
|
description: "Version-controlled project context for AI coding agents: code graph, architecture wiki, project memory, relevant tests, quality signals, and task flows.",
|
|
17646
18042
|
private: false,
|
|
17647
18043
|
publishConfig: {
|
|
@@ -28787,6 +29183,11 @@ var init_service4 = __esm(() => {
|
|
|
28787
29183
|
init_path_scan();
|
|
28788
29184
|
init_read_source();
|
|
28789
29185
|
init_redact();
|
|
29186
|
+
init_secrets();
|
|
29187
|
+
init_entropy();
|
|
29188
|
+
init_incidents();
|
|
29189
|
+
init_entropy_gate();
|
|
29190
|
+
init_credential_shape();
|
|
28790
29191
|
init_audit_harness();
|
|
28791
29192
|
init_config();
|
|
28792
29193
|
init_impact_evidence();
|
|
@@ -28955,7 +29356,7 @@ function retryableFor2(kind, fallback) {
|
|
|
28955
29356
|
const concrete = defaultRetryable(kind);
|
|
28956
29357
|
return concrete === undefined ? fallback : concrete;
|
|
28957
29358
|
}
|
|
28958
|
-
function mergeUsage2(promptTokens, completionTokens, totalTokens) {
|
|
29359
|
+
function mergeUsage2(promptTokens, completionTokens, totalTokens, cacheReadTokens) {
|
|
28959
29360
|
const usage = { exact: true };
|
|
28960
29361
|
if (promptTokens !== undefined) {
|
|
28961
29362
|
usage.inputTokens = promptTokens;
|
|
@@ -28968,6 +29369,9 @@ function mergeUsage2(promptTokens, completionTokens, totalTokens) {
|
|
|
28968
29369
|
} else if (promptTokens !== undefined || completionTokens !== undefined) {
|
|
28969
29370
|
usage.totalTokens = (promptTokens ?? 0) + (completionTokens ?? 0);
|
|
28970
29371
|
}
|
|
29372
|
+
if (cacheReadTokens !== undefined) {
|
|
29373
|
+
usage.cacheReadTokens = cacheReadTokens;
|
|
29374
|
+
}
|
|
28971
29375
|
return usage;
|
|
28972
29376
|
}
|
|
28973
29377
|
function classifyHttpError2(status, headers, code) {
|
|
@@ -29141,7 +29545,7 @@ class OpenAiCompatEngine {
|
|
|
29141
29545
|
} catch {
|
|
29142
29546
|
host = baseUrl;
|
|
29143
29547
|
}
|
|
29144
|
-
const permitted = !isPrivateEgressHost(host) || grant.allowLoopback === true &&
|
|
29548
|
+
const permitted = !isPrivateEgressHost(host) || grant.allowLoopback === true && isLoopbackHost2(host) || grant.allowPrivateLan === true && isPrivateLanHost(host);
|
|
29145
29549
|
if (!permitted) {
|
|
29146
29550
|
yield errorEvent({
|
|
29147
29551
|
kind: "invalid_request",
|
|
@@ -29394,6 +29798,14 @@ ${message.content}` });
|
|
|
29394
29798
|
}
|
|
29395
29799
|
return "idx:0";
|
|
29396
29800
|
};
|
|
29801
|
+
const pendingToolCallStillAccumulating = () => {
|
|
29802
|
+
for (const acc of pendingTools.values()) {
|
|
29803
|
+
if (!acc.ended) {
|
|
29804
|
+
return acc.id;
|
|
29805
|
+
}
|
|
29806
|
+
}
|
|
29807
|
+
return;
|
|
29808
|
+
};
|
|
29397
29809
|
const flushPendingToolEnds = () => {
|
|
29398
29810
|
for (const acc of pendingTools.values()) {
|
|
29399
29811
|
if (acc.ended) {
|
|
@@ -29487,7 +29899,7 @@ ${message.content}` });
|
|
|
29487
29899
|
const usage = asRecord4(data.usage);
|
|
29488
29900
|
bodies.push({
|
|
29489
29901
|
kind: "usage_update",
|
|
29490
|
-
usage: mergeUsage2(asNumber3(usage.prompt_tokens), asNumber3(usage.completion_tokens), asNumber3(usage.total_tokens))
|
|
29902
|
+
usage: mergeUsage2(asNumber3(usage.prompt_tokens), asNumber3(usage.completion_tokens), asNumber3(usage.total_tokens), asNumber3(asRecord4(usage.prompt_tokens_details).cached_tokens))
|
|
29491
29903
|
});
|
|
29492
29904
|
}
|
|
29493
29905
|
const choice0 = asRecord4(asArray(data.choices)[0]);
|
|
@@ -29600,6 +30012,7 @@ ${message.content}` });
|
|
|
29600
30012
|
parser.push(trailing);
|
|
29601
30013
|
}
|
|
29602
30014
|
const torn = parser.flush();
|
|
30015
|
+
const pendingToolCallId = pendingToolCallStillAccumulating();
|
|
29603
30016
|
if (torn.length > 0) {
|
|
29604
30017
|
malformed = {
|
|
29605
30018
|
kind: "malformed",
|
|
@@ -29612,8 +30025,14 @@ ${message.content}` });
|
|
|
29612
30025
|
retryable: retryableFor2("malformed", false),
|
|
29613
30026
|
message: "empty response body"
|
|
29614
30027
|
};
|
|
30028
|
+
} else if (pendingToolCallId !== undefined) {
|
|
30029
|
+
malformed = {
|
|
30030
|
+
kind: "malformed",
|
|
30031
|
+
retryable: retryableFor2("malformed", false),
|
|
30032
|
+
message: `${this.label} SSE stream ended while a tool call was still accumulating`,
|
|
30033
|
+
detail: { pendingToolCallId }
|
|
30034
|
+
};
|
|
29615
30035
|
} else {
|
|
29616
|
-
flushPendingToolEnds();
|
|
29617
30036
|
if (sawStart && sawFinish) {
|
|
29618
30037
|
flushThinkTagParser();
|
|
29619
30038
|
flushFieldTagStripper();
|
|
@@ -30272,6 +30691,16 @@ class GeminiProvider {
|
|
|
30272
30691
|
break;
|
|
30273
30692
|
}
|
|
30274
30693
|
const chunk = asRecord5(parsed);
|
|
30694
|
+
const errorEnvelope = asRecord5(chunk.error);
|
|
30695
|
+
if (Object.keys(errorEnvelope).length > 0) {
|
|
30696
|
+
const classified = classifyGeminiError(asNumber4(errorEnvelope.code) ?? 0, asString4(errorEnvelope.status));
|
|
30697
|
+
const envelopeMessage = asString4(errorEnvelope.message);
|
|
30698
|
+
malformed = {
|
|
30699
|
+
...classified,
|
|
30700
|
+
message: redact(envelopeMessage !== undefined && envelopeMessage.length > 0 ? envelopeMessage : "Gemini stream carried an in-band error envelope")
|
|
30701
|
+
};
|
|
30702
|
+
break;
|
|
30703
|
+
}
|
|
30275
30704
|
if (!sawFirstChunk) {
|
|
30276
30705
|
sawFirstChunk = true;
|
|
30277
30706
|
bodies.push({ kind: "model_start" });
|
|
@@ -30295,7 +30724,7 @@ class GeminiProvider {
|
|
|
30295
30724
|
bodies.push({ kind: "text_delta", text });
|
|
30296
30725
|
}
|
|
30297
30726
|
} else if (callName !== undefined) {
|
|
30298
|
-
const callId = asString4(functionCall.id) ?? callName
|
|
30727
|
+
const callId = asString4(functionCall.id) ?? `${callName}#${functionCallIndexInRound}`;
|
|
30299
30728
|
const argsInput = JSON.stringify(asRecord5(functionCall.args));
|
|
30300
30729
|
bodies.push({ kind: "tool_call_start", toolCallId: callId, toolName: callName });
|
|
30301
30730
|
bodies.push({ kind: "tool_call_end", toolCallId: callId, input: argsInput });
|
|
@@ -30512,7 +30941,7 @@ function retryableFor4(kind, fallback) {
|
|
|
30512
30941
|
const concrete = defaultRetryable(kind);
|
|
30513
30942
|
return concrete === undefined ? fallback : concrete;
|
|
30514
30943
|
}
|
|
30515
|
-
function mergeUsage4(inputTokens, outputTokens, totalTokens) {
|
|
30944
|
+
function mergeUsage4(inputTokens, outputTokens, totalTokens, cacheReadTokens) {
|
|
30516
30945
|
const usage = { exact: true };
|
|
30517
30946
|
if (inputTokens !== undefined) {
|
|
30518
30947
|
usage.inputTokens = inputTokens;
|
|
@@ -30525,6 +30954,9 @@ function mergeUsage4(inputTokens, outputTokens, totalTokens) {
|
|
|
30525
30954
|
} else if (inputTokens !== undefined || outputTokens !== undefined) {
|
|
30526
30955
|
usage.totalTokens = (inputTokens ?? 0) + (outputTokens ?? 0);
|
|
30527
30956
|
}
|
|
30957
|
+
if (cacheReadTokens !== undefined) {
|
|
30958
|
+
usage.cacheReadTokens = cacheReadTokens;
|
|
30959
|
+
}
|
|
30528
30960
|
return usage;
|
|
30529
30961
|
}
|
|
30530
30962
|
function extractErrorFields(data) {
|
|
@@ -30942,9 +31374,10 @@ class OpenAiProvider {
|
|
|
30942
31374
|
const outputTokens = asNumber5(usage.output_tokens);
|
|
30943
31375
|
const totalTokens = asNumber5(usage.total_tokens);
|
|
30944
31376
|
const reasoningTokens = asNumber5(asRecord6(usage.output_tokens_details).reasoning_tokens);
|
|
31377
|
+
const cacheReadTokens = asNumber5(asRecord6(usage.input_tokens_details).cached_tokens);
|
|
30945
31378
|
const usageBody = {
|
|
30946
31379
|
kind: "usage_update",
|
|
30947
|
-
usage: mergeUsage4(inputTokens, outputTokens, totalTokens)
|
|
31380
|
+
usage: mergeUsage4(inputTokens, outputTokens, totalTokens, cacheReadTokens)
|
|
30948
31381
|
};
|
|
30949
31382
|
if (reasoningTokens !== undefined) {
|
|
30950
31383
|
usageBody.unknownExtensions = { "openai.reasoning_tokens": reasoningTokens };
|
|
@@ -58148,8 +58581,10 @@ async function runAgentTurn(io, deps, history, userLine, options = {}) {
|
|
|
58148
58581
|
|
|
58149
58582
|
${buildControlMarkerInstruction(controlNonce)}`
|
|
58150
58583
|
};
|
|
58584
|
+
const caughtToolErrors = [];
|
|
58151
58585
|
try {
|
|
58152
|
-
const
|
|
58586
|
+
const coreResult = await runAgentTurnCore(io, turnDeps, history, userLine, options, caughtToolErrors);
|
|
58587
|
+
const result = caughtToolErrors.length > 0 ? { ...coreResult, caughtToolErrors } : coreResult;
|
|
58153
58588
|
await fireStopHookBestEffort(io, deps, result);
|
|
58154
58589
|
return result;
|
|
58155
58590
|
} finally {
|
|
@@ -58231,7 +58666,7 @@ function computeReasoningDurationMs(startedAt, endedAt) {
|
|
|
58231
58666
|
const endMs = Date.parse(endedAt);
|
|
58232
58667
|
return !Number.isNaN(startMs) && !Number.isNaN(endMs) && endMs >= startMs ? endMs - startMs : undefined;
|
|
58233
58668
|
}
|
|
58234
|
-
async function runAgentTurnCore(io, deps, history, userLine, options = {}) {
|
|
58669
|
+
async function runAgentTurnCore(io, deps, history, userLine, options = {}, caughtToolErrors = []) {
|
|
58235
58670
|
const now = deps.now ?? (() => new Date().toISOString());
|
|
58236
58671
|
const controlNonce = deps.controlNonce ?? generateControlNonce();
|
|
58237
58672
|
const scrub = (text) => scrubControlNonce(text, controlNonce);
|
|
@@ -58821,7 +59256,7 @@ ${shown.join(`
|
|
|
58821
59256
|
}
|
|
58822
59257
|
const spawnConcurrencyCandidates = calls.filter((call) => call.name === "spawn_subagent" && reservationByCallId.get(call.id)?.ok === true);
|
|
58823
59258
|
const untrustedGateBlocksSpawns = untrustedContentSeen || batchContainsUntrustedWeb;
|
|
58824
|
-
const concurrentSpawnResults = spawnConcurrencyCandidates.length >= 2 && !untrustedGateBlocksSpawns && maxToolCalls === undefined ? await runConcurrentSpawnBatch(spawnConcurrencyCandidates, toolByName, io, deps, hasInvocationCapacity, reserveInvocation, signal) : undefined;
|
|
59259
|
+
const concurrentSpawnResults = spawnConcurrencyCandidates.length >= 2 && !untrustedGateBlocksSpawns && maxToolCalls === undefined ? await runConcurrentSpawnBatch(spawnConcurrencyCandidates, toolByName, io, deps, hasInvocationCapacity, reserveInvocation, signal, caughtToolErrors) : undefined;
|
|
58825
59260
|
const answeredToolIds = new Set;
|
|
58826
59261
|
let anchorsToAnnounce;
|
|
58827
59262
|
let repeatedFailureHint;
|
|
@@ -58859,53 +59294,18 @@ ${shown.join(`
|
|
|
58859
59294
|
}
|
|
58860
59295
|
const risk = toolByName.get(call.name)?.definition.risk;
|
|
58861
59296
|
const isPureReadTool = risk === "read" && !DURABLE_READ_TOOL_NAMES.has(call.name);
|
|
58862
|
-
|
|
58863
|
-
|
|
58864
|
-
|
|
58865
|
-
|
|
58866
|
-
|
|
58867
|
-
|
|
58868
|
-
|
|
58869
|
-
|
|
58870
|
-
|
|
58871
|
-
|
|
58872
|
-
|
|
58873
|
-
|
|
58874
|
-
io.onHistoryChange?.("tool");
|
|
58875
|
-
gateBlockedAny = true;
|
|
58876
|
-
continue;
|
|
58877
|
-
}
|
|
58878
|
-
const taintFingerprint = toolCallHash(call.name, call.input);
|
|
58879
|
-
let taintApproved;
|
|
58880
|
-
try {
|
|
58881
|
-
const response = await approver(call.name, call.input, {
|
|
58882
|
-
fingerprint: taintFingerprint,
|
|
58883
|
-
destructive: risk === "destructive",
|
|
58884
|
-
untrustedOrigin: true
|
|
58885
|
-
});
|
|
58886
|
-
taintApproved = isApprovalFor(response, taintFingerprint);
|
|
58887
|
-
} catch (err) {
|
|
58888
|
-
const result = {
|
|
58889
|
-
output: `${call.name} not executed: the approval prompt failed (${err instanceof Error ? err.message : String(err)})`,
|
|
58890
|
-
isError: true
|
|
58891
|
-
};
|
|
58892
|
-
io.onToolResult?.(call.name, result);
|
|
58893
|
-
history.push({ role: "tool", content: withBudgetWarning(result.output), provenance: "tool", toolCallId: call.id, ts: now() });
|
|
58894
|
-
io.onHistoryChange?.("tool");
|
|
58895
|
-
gateBlockedAny = true;
|
|
58896
|
-
continue;
|
|
58897
|
-
}
|
|
58898
|
-
if (!taintApproved) {
|
|
58899
|
-
const result = {
|
|
58900
|
-
output: "tool blocked: external web content cannot authorize this call, and the user did not authorize it either",
|
|
58901
|
-
isError: true
|
|
58902
|
-
};
|
|
58903
|
-
io.onToolResult?.(call.name, result);
|
|
58904
|
-
history.push({ role: "tool", content: withBudgetWarning(result.output), provenance: "tool", toolCallId: call.id, ts: now() });
|
|
58905
|
-
io.onHistoryChange?.("tool");
|
|
58906
|
-
gateBlockedAny = true;
|
|
58907
|
-
continue;
|
|
58908
|
-
}
|
|
59297
|
+
const untrustedOrigin = !isPureReadTool && untrustedContentSeen;
|
|
59298
|
+
if (untrustedOrigin && deps.unattended === true) {
|
|
59299
|
+
const result = {
|
|
59300
|
+
output: "tool blocked: external web content cannot authorize further tool calls in this turn",
|
|
59301
|
+
isError: true
|
|
59302
|
+
};
|
|
59303
|
+
io.onUnattendedDenial?.(call.name, "untrusted external content in this turn cannot authorize the call");
|
|
59304
|
+
io.onToolResult?.(call.name, result);
|
|
59305
|
+
history.push({ role: "tool", content: withBudgetWarning(result.output), provenance: "tool", toolCallId: call.id, ts: now() });
|
|
59306
|
+
io.onHistoryChange?.("tool");
|
|
59307
|
+
gateBlockedAny = true;
|
|
59308
|
+
continue;
|
|
58909
59309
|
}
|
|
58910
59310
|
io.onToolCall?.(call.name, call.input);
|
|
58911
59311
|
const reservation = reservationByCallId.get(call.id) ?? reserveToolAttempt(budget, call.name, call.input);
|
|
@@ -58921,7 +59321,19 @@ ${shown.join(`
|
|
|
58921
59321
|
if (toolByName.has(call.name)) {
|
|
58922
59322
|
turnExecutedToolCall = true;
|
|
58923
59323
|
}
|
|
58924
|
-
const
|
|
59324
|
+
const precomputedResult = concurrentSpawnResults?.get(call.id);
|
|
59325
|
+
let result;
|
|
59326
|
+
if (precomputedResult !== undefined) {
|
|
59327
|
+
result = precomputedResult;
|
|
59328
|
+
} else {
|
|
59329
|
+
try {
|
|
59330
|
+
result = await executeCall(call, toolByName, io.requestApproval, io.permissionMode, io.readOnly, io.onAutoApproved, hasInvocationCapacity, reserveInvocation, invocationBudget.maxCalls, signal, deps.busLeases, deps.hardDeny === undefined ? undefined : { check: deps.hardDeny, onDenied: io.onUnattendedDenial }, deps.hooks, untrustedOrigin);
|
|
59331
|
+
} catch (err) {
|
|
59332
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
59333
|
+
result = { output: `${call.name} failed: ${message}`, isError: true };
|
|
59334
|
+
caughtToolErrors.push({ toolName: call.name, message });
|
|
59335
|
+
}
|
|
59336
|
+
}
|
|
58925
59337
|
io.onToolResult?.(call.name, result);
|
|
58926
59338
|
const modelOutput = redactSensitiveText(result.output);
|
|
58927
59339
|
const untrusted = result.untrusted === true;
|
|
@@ -59371,7 +59783,7 @@ function isApprovalFor(response, fingerprint) {
|
|
|
59371
59783
|
}
|
|
59372
59784
|
return response.fingerprint === undefined || response.fingerprint === fingerprint;
|
|
59373
59785
|
}
|
|
59374
|
-
async function runConcurrentSpawnBatch(spawnCalls, toolByName, io, deps, hasInvocationCapacity, reserveInvocation, signal) {
|
|
59786
|
+
async function runConcurrentSpawnBatch(spawnCalls, toolByName, io, deps, hasInvocationCapacity, reserveInvocation, signal, caughtToolErrors) {
|
|
59375
59787
|
const maxConcurrency = deps.maxSubagentConcurrency ?? DEFAULT_MAX_SUBAGENT_CONCURRENCY;
|
|
59376
59788
|
const perTaskRuntimeMs = NOMINAL_CONCURRENT_SPAWN_RUNTIME_MS;
|
|
59377
59789
|
const tasks = spawnCalls.map((call) => ({
|
|
@@ -59398,6 +59810,7 @@ async function runConcurrentSpawnBatch(spawnCalls, toolByName, io, deps, hasInvo
|
|
|
59398
59810
|
output: `subagent call ${call.id} failed: sequential fallback error: ${message}`,
|
|
59399
59811
|
isError: true
|
|
59400
59812
|
});
|
|
59813
|
+
caughtToolErrors.push({ toolName: call.name, message });
|
|
59401
59814
|
}
|
|
59402
59815
|
}
|
|
59403
59816
|
return results;
|
|
@@ -59425,6 +59838,9 @@ async function runConcurrentSpawnBatch(spawnCalls, toolByName, io, deps, hasInvo
|
|
|
59425
59838
|
const results = new Map;
|
|
59426
59839
|
for (const call of spawnCalls) {
|
|
59427
59840
|
const settled = partialResults?.get(call.id);
|
|
59841
|
+
if (settled === undefined) {
|
|
59842
|
+
caughtToolErrors.push({ toolName: call.name, message });
|
|
59843
|
+
}
|
|
59428
59844
|
results.set(call.id, settled ?? {
|
|
59429
59845
|
output: `subagent call ${call.id} failed: concurrent wave error: ${message}`,
|
|
59430
59846
|
isError: true
|
|
@@ -59481,7 +59897,7 @@ function composeWithHook(toolName, rawDecision, hookResult, interactive) {
|
|
|
59481
59897
|
}
|
|
59482
59898
|
return { decision, hookTightened: true, hookAsked, ...denyMessage !== undefined ? { denyMessage } : {} };
|
|
59483
59899
|
}
|
|
59484
|
-
async function executeCall(call, toolByName, requestApproval, permissionMode, readOnly, onAutoApproved, hasInvocationCapacity, reserveInvocation, maxToolCalls, signal, busLeases, hardDeny, hooks) {
|
|
59900
|
+
async function executeCall(call, toolByName, requestApproval, permissionMode, readOnly, onAutoApproved, hasInvocationCapacity, reserveInvocation, maxToolCalls, signal, busLeases, hardDeny, hooks, untrustedOrigin = false) {
|
|
59485
59901
|
const tool = toolByName.get(call.name);
|
|
59486
59902
|
if (tool === undefined) {
|
|
59487
59903
|
return { output: `unknown tool: ${call.name}`, isError: true };
|
|
@@ -59507,6 +59923,7 @@ async function executeCall(call, toolByName, requestApproval, permissionMode, re
|
|
|
59507
59923
|
}
|
|
59508
59924
|
}
|
|
59509
59925
|
const mode = permissionMode?.() ?? DEFAULT_PERMISSION_MODE;
|
|
59926
|
+
const untrustedDenial = "tool blocked: external web content cannot authorize this call, and the user did not authorize it either";
|
|
59510
59927
|
const isReadOnly = readOnly?.() ?? false;
|
|
59511
59928
|
let hookResult;
|
|
59512
59929
|
if (hooks !== undefined) {
|
|
@@ -59547,7 +59964,7 @@ async function executeCall(call, toolByName, requestApproval, permissionMode, re
|
|
|
59547
59964
|
isError: true
|
|
59548
59965
|
};
|
|
59549
59966
|
}
|
|
59550
|
-
if (gated.decision === "auto") {
|
|
59967
|
+
if (gated.decision === "auto" && !untrustedOrigin) {
|
|
59551
59968
|
onAutoApproved?.(call.name, call.input, { destructive, credentials });
|
|
59552
59969
|
} else {
|
|
59553
59970
|
const fingerprint = toolCallHash(call.name, call.input);
|
|
@@ -59557,10 +59974,11 @@ async function executeCall(call, toolByName, requestApproval, permissionMode, re
|
|
|
59557
59974
|
...credentials ? { credentials } : {},
|
|
59558
59975
|
...publishLease ? { publishLease } : {},
|
|
59559
59976
|
...publishLease && publishLeaseHolder !== undefined ? { publishLeaseDetail: `held by @${publishLeaseHolder.name} \u2014 "${publishLeaseHolder.reason}"` } : {},
|
|
59560
|
-
...gated.hookAsked ? { hookAsk: true } : {}
|
|
59977
|
+
...gated.hookAsked ? { hookAsk: true } : {},
|
|
59978
|
+
...untrustedOrigin ? { untrustedOrigin: true } : {}
|
|
59561
59979
|
});
|
|
59562
59980
|
if (!isApprovalFor(response, fingerprint)) {
|
|
59563
|
-
return { output:
|
|
59981
|
+
return { output: untrustedOrigin ? untrustedDenial : "command not approved by the user; not executed", isError: true };
|
|
59564
59982
|
}
|
|
59565
59983
|
}
|
|
59566
59984
|
} else if (risk === "delegate") {
|
|
@@ -59579,17 +59997,18 @@ async function executeCall(call, toolByName, requestApproval, permissionMode, re
|
|
|
59579
59997
|
isError: true
|
|
59580
59998
|
};
|
|
59581
59999
|
}
|
|
59582
|
-
if (gated.decision === "auto") {
|
|
60000
|
+
if (gated.decision === "auto" && !untrustedOrigin) {
|
|
59583
60001
|
onAutoApproved?.(call.name, call.input, { destructive: false, credentials: false });
|
|
59584
60002
|
} else {
|
|
59585
60003
|
const fingerprint = toolCallHash(call.name, call.input);
|
|
59586
60004
|
const response = requestApproval === undefined ? false : await requestApproval(call.name, call.input, {
|
|
59587
60005
|
fingerprint,
|
|
59588
60006
|
destructive: false,
|
|
59589
|
-
...gated.hookAsked ? { hookAsk: true } : {}
|
|
60007
|
+
...gated.hookAsked ? { hookAsk: true } : {},
|
|
60008
|
+
...untrustedOrigin ? { untrustedOrigin: true } : {}
|
|
59590
60009
|
});
|
|
59591
60010
|
if (!isApprovalFor(response, fingerprint)) {
|
|
59592
|
-
return { output:
|
|
60011
|
+
return { output: untrustedOrigin ? untrustedDenial : "subagent spawn not approved by the user; not executed", isError: true };
|
|
59593
60012
|
}
|
|
59594
60013
|
}
|
|
59595
60014
|
} else if (risk === "write") {
|
|
@@ -59620,7 +60039,7 @@ async function executeCall(call, toolByName, requestApproval, permissionMode, re
|
|
|
59620
60039
|
isError: true
|
|
59621
60040
|
};
|
|
59622
60041
|
}
|
|
59623
|
-
if (gated.decision === "auto") {
|
|
60042
|
+
if (gated.decision === "auto" && !untrustedOrigin) {
|
|
59624
60043
|
onAutoApproved?.(call.name, call.input, { destructive, credentials });
|
|
59625
60044
|
} else {
|
|
59626
60045
|
const fingerprint = toolCallHash(call.name, call.input);
|
|
@@ -59629,13 +60048,14 @@ async function executeCall(call, toolByName, requestApproval, permissionMode, re
|
|
|
59629
60048
|
destructive,
|
|
59630
60049
|
...credentials ? { credentials } : {},
|
|
59631
60050
|
...card !== undefined ? { alwaysAsk: true, card } : {},
|
|
59632
|
-
...gated.hookAsked ? { hookAsk: true } : {}
|
|
60051
|
+
...gated.hookAsked ? { hookAsk: true } : {},
|
|
60052
|
+
...untrustedOrigin ? { untrustedOrigin: true } : {}
|
|
59633
60053
|
});
|
|
59634
60054
|
if (!isApprovalFor(response, fingerprint)) {
|
|
59635
60055
|
if (confirmationToken !== undefined)
|
|
59636
60056
|
tool.confirmationDeclined?.(confirmationToken);
|
|
59637
60057
|
return {
|
|
59638
|
-
output: card !== undefined ? `${call.name} not confirmed by the operator; nothing was written or installed` : `patch not approved by the user; not executed`,
|
|
60058
|
+
output: untrustedOrigin ? untrustedDenial : card !== undefined ? `${call.name} not confirmed by the operator; nothing was written or installed` : `patch not approved by the user; not executed`,
|
|
59639
60059
|
isError: true
|
|
59640
60060
|
};
|
|
59641
60061
|
}
|
|
@@ -59648,11 +60068,11 @@ async function executeCall(call, toolByName, requestApproval, permissionMode, re
|
|
|
59648
60068
|
if (gated.decision === "deny") {
|
|
59649
60069
|
return { output: gated.denyMessage ?? `tool "${call.name}" refused by a policy hook`, isError: true };
|
|
59650
60070
|
}
|
|
59651
|
-
if (gated.decision === "ask") {
|
|
60071
|
+
if (gated.decision === "ask" || untrustedOrigin) {
|
|
59652
60072
|
const fingerprint = toolCallHash(call.name, call.input);
|
|
59653
|
-
const response = requestApproval === undefined ? false : await requestApproval(call.name, call.input, { fingerprint, destructive: false, hookAsk: true });
|
|
60073
|
+
const response = requestApproval === undefined ? false : await requestApproval(call.name, call.input, { fingerprint, destructive: false, ...gated.hookAsked ? { hookAsk: true } : {}, ...untrustedOrigin ? { untrustedOrigin: true } : {} });
|
|
59654
60074
|
if (!isApprovalFor(response, fingerprint)) {
|
|
59655
|
-
return { output: `${call.name} not approved by the user; not executed`, isError: true };
|
|
60075
|
+
return { output: untrustedOrigin ? untrustedDenial : `${call.name} not approved by the user; not executed`, isError: true };
|
|
59656
60076
|
}
|
|
59657
60077
|
}
|
|
59658
60078
|
} else {
|
|
@@ -112108,6 +112528,7 @@ function resolveFqn(catalog, fqn) {
|
|
|
112108
112528
|
}
|
|
112109
112529
|
|
|
112110
112530
|
// src/mcp-servers/http-headers.ts
|
|
112531
|
+
init_service4();
|
|
112111
112532
|
var VAR_PATTERN2 = /\$\{([A-Za-z_][A-Za-z0-9_]*)(?::-[^}]*)?\}/;
|
|
112112
112533
|
var VAR_PATTERN_ALL = /\$\{([A-Za-z_][A-Za-z0-9_]*)(?::-([^}]*))?\}/g;
|
|
112113
112534
|
function unresolvedVariables(raw, env) {
|
|
@@ -112225,6 +112646,14 @@ function elideCredentialText(raw) {
|
|
|
112225
112646
|
const trimmed = query === -1 ? raw : `${raw.slice(0, query)}?\u2026`;
|
|
112226
112647
|
return trimmed.replace(/^([a-zA-Z][a-zA-Z0-9+.-]*:)?\/\/[^/@]*@/, (_m, scheme) => `${scheme ?? ""}//\u2026@`);
|
|
112227
112648
|
}
|
|
112649
|
+
var MASKED_SEGMENT_MIN_LENGTH = 16;
|
|
112650
|
+
function maskSecretShapedPathSegments(pathname) {
|
|
112651
|
+
return pathname.split("/").map((segment) => {
|
|
112652
|
+
if (segment.length < MASKED_SEGMENT_MIN_LENGTH)
|
|
112653
|
+
return segment;
|
|
112654
|
+
return detectSecrets(segment).length > 0 || looksSecretShaped(segment) ? "\u2026" : segment;
|
|
112655
|
+
}).join("/");
|
|
112656
|
+
}
|
|
112228
112657
|
function displayUrl(raw) {
|
|
112229
112658
|
if (raw === undefined)
|
|
112230
112659
|
return "";
|
|
@@ -112234,7 +112663,8 @@ function displayUrl(raw) {
|
|
|
112234
112663
|
const parsed = new URL(raw);
|
|
112235
112664
|
const query = parsed.search === "" ? "" : "?\u2026";
|
|
112236
112665
|
const auth = parsed.username === "" ? "" : "\u2026@";
|
|
112237
|
-
|
|
112666
|
+
const pathname = maskSecretShapedPathSegments(parsed.pathname);
|
|
112667
|
+
return `${parsed.protocol}//${auth}${parsed.host}${pathname}${query}`;
|
|
112238
112668
|
} catch {
|
|
112239
112669
|
return elideCredentialText(raw);
|
|
112240
112670
|
}
|
|
@@ -113645,139 +114075,10 @@ function fileForScope(scope, targets) {
|
|
|
113645
114075
|
return projectConfigFile(targets.projectRoot);
|
|
113646
114076
|
}
|
|
113647
114077
|
|
|
113648
|
-
// src/harness/external/env-deny.ts
|
|
113649
|
-
var EXTERNAL_ENV_DENY = [
|
|
113650
|
-
"ANTHROPIC_API_KEY",
|
|
113651
|
-
"ANTHROPIC_AUTH_TOKEN",
|
|
113652
|
-
"ANTHROPIC_BASE_URL",
|
|
113653
|
-
"ANTHROPIC_MODEL",
|
|
113654
|
-
"CLAUDE_CONFIG_DIR",
|
|
113655
|
-
"CODEX_HOME",
|
|
113656
|
-
"CLAUDECODE"
|
|
113657
|
-
];
|
|
113658
|
-
var EXTERNAL_ENV_PREFIX_SWEEPS = ["CLAUDE_CODE_", "KERYX_"];
|
|
113659
|
-
|
|
113660
114078
|
// src/mcp-servers/spawn-env.ts
|
|
114079
|
+
init_service4();
|
|
113661
114080
|
init_shell_config();
|
|
113662
|
-
|
|
113663
|
-
var MCP_ENV_DENY = [
|
|
113664
|
-
"SSH_AUTH_SOCK",
|
|
113665
|
-
"SSH_AGENT_PID",
|
|
113666
|
-
"GPG_AGENT_INFO",
|
|
113667
|
-
"GNUPGHOME",
|
|
113668
|
-
"GNOME_KEYRING_CONTROL",
|
|
113669
|
-
"GNOME_KEYRING_PID",
|
|
113670
|
-
"KDE_FULL_SESSION",
|
|
113671
|
-
"GIT_ASKPASS",
|
|
113672
|
-
"SSH_ASKPASS",
|
|
113673
|
-
"SUDO_ASKPASS",
|
|
113674
|
-
"GIT_CREDENTIAL_HELPER",
|
|
113675
|
-
"GIT_SSH_COMMAND",
|
|
113676
|
-
"KUBECONFIG",
|
|
113677
|
-
"NETRC",
|
|
113678
|
-
"PGPASSFILE",
|
|
113679
|
-
"PGSERVICEFILE",
|
|
113680
|
-
"GOOGLE_APPLICATION_CREDENTIALS",
|
|
113681
|
-
"CLOUDSDK_CONFIG",
|
|
113682
|
-
"DOCKER_CONFIG",
|
|
113683
|
-
"AWS_SHARED_CREDENTIALS_FILE",
|
|
113684
|
-
"AWS_CONFIG_FILE",
|
|
113685
|
-
"BOTO_CONFIG",
|
|
113686
|
-
"AZURE_CONFIG_DIR",
|
|
113687
|
-
"CURLRC",
|
|
113688
|
-
"WGETRC",
|
|
113689
|
-
"NPMRC",
|
|
113690
|
-
"NPM_CONFIG_USERCONFIG",
|
|
113691
|
-
"PIP_CONFIG_FILE",
|
|
113692
|
-
"RCLONE_CONFIG",
|
|
113693
|
-
"MAVEN_SETTINGS",
|
|
113694
|
-
"GIT_CONFIG",
|
|
113695
|
-
"GIT_CONFIG_GLOBAL",
|
|
113696
|
-
"HGRCPATH",
|
|
113697
|
-
"ANSIBLE_VAULT_PASSWORD_FILE",
|
|
113698
|
-
"AZURE_CLIENT_CERTIFICATE_PATH",
|
|
113699
|
-
"ARM_CLIENT_CERTIFICATE_PATH",
|
|
113700
|
-
"VAULT_CLIENT_CERT",
|
|
113701
|
-
"VAULT_CLIENT_KEY",
|
|
113702
|
-
"SSL_CLIENT_CERT_PATH",
|
|
113703
|
-
"SSL_CLIENT_KEY_FILE",
|
|
113704
|
-
"IDENTITY_FILE",
|
|
113705
|
-
"SSH_IDENTITY_FILE",
|
|
113706
|
-
"GCP_SERVICE_ACCOUNT",
|
|
113707
|
-
"XAUTHORITY",
|
|
113708
|
-
"OPENAI_API_KEY",
|
|
113709
|
-
"GEMINI_API_KEY",
|
|
113710
|
-
"GOOGLE_API_KEY",
|
|
113711
|
-
"GROQ_API_KEY",
|
|
113712
|
-
"MISTRAL_API_KEY",
|
|
113713
|
-
"XAI_API_KEY",
|
|
113714
|
-
"DEEPSEEK_API_KEY",
|
|
113715
|
-
"OPENROUTER_API_KEY",
|
|
113716
|
-
"TOGETHER_API_KEY",
|
|
113717
|
-
"PERPLEXITY_API_KEY"
|
|
113718
|
-
];
|
|
113719
|
-
var MCP_ENV_PREFIX_SWEEPS = [
|
|
113720
|
-
"OP_SESSION",
|
|
113721
|
-
"BW_SESSION"
|
|
113722
|
-
];
|
|
113723
|
-
var SECRET_SEGMENTS = [
|
|
113724
|
-
"KEY",
|
|
113725
|
-
"KEYS",
|
|
113726
|
-
"TOKEN",
|
|
113727
|
-
"TOKENS",
|
|
113728
|
-
"SECRET",
|
|
113729
|
-
"SECRETS",
|
|
113730
|
-
"PASSWORD",
|
|
113731
|
-
"PASSWD",
|
|
113732
|
-
"PWD",
|
|
113733
|
-
"PASS",
|
|
113734
|
-
"PASSPHRASE",
|
|
113735
|
-
"PASSCODE",
|
|
113736
|
-
"PASSKEY",
|
|
113737
|
-
"CREDENTIAL",
|
|
113738
|
-
"CREDENTIALS",
|
|
113739
|
-
"CREDS",
|
|
113740
|
-
"AUTH",
|
|
113741
|
-
"AUTHORIZATION",
|
|
113742
|
-
"BEARER",
|
|
113743
|
-
"JWT",
|
|
113744
|
-
"COOKIE",
|
|
113745
|
-
"SIGNATURE",
|
|
113746
|
-
"APIKEY",
|
|
113747
|
-
"SECRETKEY",
|
|
113748
|
-
"ACCESSKEY",
|
|
113749
|
-
"AUTHTOKEN",
|
|
113750
|
-
"PAT",
|
|
113751
|
-
"WEBHOOK"
|
|
113752
|
-
];
|
|
113753
|
-
var SECRET_SEGMENT_RE = new RegExp(`(^|_)(${SECRET_SEGMENTS.join("|")})($|_)`);
|
|
113754
|
-
var SECRET_SUBSTRING_RE = /(PASSWORD|PASSWD|PASSPHRASE|PASSCODE|SSHPASS|SECRET|CREDENTIAL|APIKEY|KEYFILE|PRIVKEY)/;
|
|
113755
|
-
var GLUED_SECRET_RE = /(PRIVATE|SECRET|ACCESS|REFRESH|SESSION|CLIENT|API|APP)(KEY|TOKEN|SECRET)|DB(PASS|PWD)/;
|
|
113756
|
-
var CONNECTION_STRING_RE = /(^|_)(DSN|CONNECTION_STRING|CONNECTIONSTRING)($|_)/;
|
|
113757
|
-
var CREDENTIAL_IN_VALUE_RE = /:\/\/[^/\s@]*(:[^/\s@]*)?@/;
|
|
113758
|
-
var VALUE_DEPENDENT = {
|
|
113759
|
-
DOCKER_HOST: (value) => !value.startsWith("unix://") && !value.startsWith("fd://")
|
|
113760
|
-
};
|
|
113761
|
-
function isDeniedForMcpChild(name, value) {
|
|
113762
|
-
if (value !== undefined && CREDENTIAL_IN_VALUE_RE.test(value))
|
|
113763
|
-
return true;
|
|
113764
|
-
const upper = name.toUpperCase();
|
|
113765
|
-
if (MCP_ENV_ALLOW_EXACT.includes(upper))
|
|
113766
|
-
return false;
|
|
113767
|
-
const conditional = VALUE_DEPENDENT[upper];
|
|
113768
|
-
if (conditional !== undefined) {
|
|
113769
|
-
return value === undefined ? true : conditional(value);
|
|
113770
|
-
}
|
|
113771
|
-
if (EXTERNAL_ENV_DENY.some((denied) => denied.toUpperCase() === upper))
|
|
113772
|
-
return true;
|
|
113773
|
-
if (MCP_ENV_DENY.includes(upper))
|
|
113774
|
-
return true;
|
|
113775
|
-
if (EXTERNAL_ENV_PREFIX_SWEEPS.some((prefix) => upper.startsWith(prefix.toUpperCase())))
|
|
113776
|
-
return true;
|
|
113777
|
-
if (MCP_ENV_PREFIX_SWEEPS.some((prefix) => upper.startsWith(prefix)))
|
|
113778
|
-
return true;
|
|
113779
|
-
return SECRET_SEGMENT_RE.test(upper) || SECRET_SUBSTRING_RE.test(upper) || GLUED_SECRET_RE.test(upper) || CONNECTION_STRING_RE.test(upper);
|
|
113780
|
-
}
|
|
114081
|
+
init_service4();
|
|
113781
114082
|
function buildMcpChildEnv(input) {
|
|
113782
114083
|
const saved = input.savedCredentialKeys ?? new Set([...savedCredentialEnvKeys(), ...declaredCredentialEnvKeys(input.configDir)]);
|
|
113783
114084
|
const env = {};
|
|
@@ -127701,7 +128002,7 @@ async function dispatchLocked(projectRoot, entry, flow, dispatch, deps, runId) {
|
|
|
127701
128002
|
});
|
|
127702
128003
|
const provider = guardUsage(built, () => meter.markUsageMissing());
|
|
127703
128004
|
try {
|
|
127704
|
-
const tools = buildUnattendedRoster(worktree, unattendedRunner(worktree, sandbox));
|
|
128005
|
+
const tools = [...buildUnattendedRoster(worktree, unattendedRunner(worktree, sandbox)), ...deps.extraTools ?? []];
|
|
127705
128006
|
const toolNames = tools.map((t) => t.definition.name);
|
|
127706
128007
|
const shellHooks = buildShellHookRuntime({
|
|
127707
128008
|
projectRoot: worktree,
|
|
@@ -127756,6 +128057,9 @@ async function dispatchLocked(projectRoot, entry, flow, dispatch, deps, runId) {
|
|
|
127756
128057
|
signal: controller.signal
|
|
127757
128058
|
});
|
|
127758
128059
|
finishReason = result.finishReason;
|
|
128060
|
+
if (result.caughtToolErrors !== undefined && result.caughtToolErrors.length > 0) {
|
|
128061
|
+
crashed = result.caughtToolErrors.map((e) => `${e.toolName}: ${e.message}`).join("; ");
|
|
128062
|
+
}
|
|
127759
128063
|
} catch (error) {
|
|
127760
128064
|
crashed = error instanceof Error ? error.message : String(error);
|
|
127761
128065
|
} finally {
|
|
@@ -128790,6 +129094,70 @@ function createSystemWebWorkerRunner() {
|
|
|
128790
129094
|
return new SystemWebWorkerRunner({ workspace: process.cwd(), home: homedir10() });
|
|
128791
129095
|
}
|
|
128792
129096
|
|
|
129097
|
+
// src/harness/web/outbound-secret.ts
|
|
129098
|
+
init_service4();
|
|
129099
|
+
function safeDecodeComponent(value) {
|
|
129100
|
+
try {
|
|
129101
|
+
return decodeURIComponent(value);
|
|
129102
|
+
} catch {
|
|
129103
|
+
return value;
|
|
129104
|
+
}
|
|
129105
|
+
}
|
|
129106
|
+
function outboundUrlComponents(text) {
|
|
129107
|
+
let url;
|
|
129108
|
+
try {
|
|
129109
|
+
url = new URL(text);
|
|
129110
|
+
} catch {
|
|
129111
|
+
return [];
|
|
129112
|
+
}
|
|
129113
|
+
const components = [];
|
|
129114
|
+
for (const segment of url.pathname.split("/")) {
|
|
129115
|
+
if (segment.length > 0) {
|
|
129116
|
+
components.push(safeDecodeComponent(segment));
|
|
129117
|
+
}
|
|
129118
|
+
}
|
|
129119
|
+
for (const [, value] of url.searchParams) {
|
|
129120
|
+
if (value.length > 0) {
|
|
129121
|
+
components.push(value);
|
|
129122
|
+
}
|
|
129123
|
+
}
|
|
129124
|
+
if (url.hash.length > 1) {
|
|
129125
|
+
components.push(safeDecodeComponent(url.hash.slice(1)));
|
|
129126
|
+
}
|
|
129127
|
+
return components;
|
|
129128
|
+
}
|
|
129129
|
+
function anyComponentLooksSecret(text) {
|
|
129130
|
+
const entropyGateOpen = isEntropyBackendEnabled();
|
|
129131
|
+
for (const component of outboundUrlComponents(text)) {
|
|
129132
|
+
if (detectSecrets(component).length > 0) {
|
|
129133
|
+
return true;
|
|
129134
|
+
}
|
|
129135
|
+
if (entropyGateOpen && looksSecretShapedIn(component)) {
|
|
129136
|
+
return true;
|
|
129137
|
+
}
|
|
129138
|
+
}
|
|
129139
|
+
return false;
|
|
129140
|
+
}
|
|
129141
|
+
function containsOutboundSecret(text) {
|
|
129142
|
+
if (detectSecrets(text).length > 0) {
|
|
129143
|
+
return true;
|
|
129144
|
+
}
|
|
129145
|
+
if (isEntropyBackendEnabled() && detectEntropy(text).length > 0) {
|
|
129146
|
+
return true;
|
|
129147
|
+
}
|
|
129148
|
+
return anyComponentLooksSecret(text);
|
|
129149
|
+
}
|
|
129150
|
+
async function recordOutboundSecretFinding(cwd, tool, subject) {
|
|
129151
|
+
try {
|
|
129152
|
+
await appendIncident(cwd, {
|
|
129153
|
+
at: new Date().toISOString(),
|
|
129154
|
+
type: "egress-blocked",
|
|
129155
|
+
message: "outbound secret-shaped content",
|
|
129156
|
+
details: { policyId: "egress.outbound-secret", tool, subject }
|
|
129157
|
+
});
|
|
129158
|
+
} catch {}
|
|
129159
|
+
}
|
|
129160
|
+
|
|
128793
129161
|
// src/harness/tool/builtin/web-fetch-tool.ts
|
|
128794
129162
|
function transportFor(deps) {
|
|
128795
129163
|
if (deps.transport !== undefined)
|
|
@@ -128813,6 +129181,12 @@ function webFetchTool(deps = {}) {
|
|
|
128813
129181
|
if (typeof input.url !== "string") {
|
|
128814
129182
|
return { output: "web_fetch: url must be an absolute HTTPS URL without credentials", isError: true };
|
|
128815
129183
|
}
|
|
129184
|
+
if (containsOutboundSecret(input.url)) {
|
|
129185
|
+
if (deps.cwd !== undefined) {
|
|
129186
|
+
await recordOutboundSecretFinding(deps.cwd, "web_fetch", "url");
|
|
129187
|
+
}
|
|
129188
|
+
return { output: "web_fetch: outbound secret-shaped content", isError: true };
|
|
129189
|
+
}
|
|
128816
129190
|
const result = await transport.fetchPage({ url: input.url, providerId: "web_fetch" });
|
|
128817
129191
|
return result.ok ? { output: result.value.text, isError: false, untrusted: true } : { output: `web_fetch: ${result.reason}`, isError: true };
|
|
128818
129192
|
}
|
|
@@ -128843,7 +129217,7 @@ ${result.canonicalUrl}`;
|
|
|
128843
129217
|
return lines.join(`
|
|
128844
129218
|
`).trim();
|
|
128845
129219
|
}
|
|
128846
|
-
function webSearchTool(service) {
|
|
129220
|
+
function webSearchTool(service, deps = {}) {
|
|
128847
129221
|
return {
|
|
128848
129222
|
definition: {
|
|
128849
129223
|
name: "web_search",
|
|
@@ -128855,7 +129229,14 @@ function webSearchTool(service) {
|
|
|
128855
129229
|
if (typeof input.query !== "string" || input.query.trim().length === 0) {
|
|
128856
129230
|
return { output: "web_search: query must be a non-empty string", isError: true };
|
|
128857
129231
|
}
|
|
128858
|
-
const
|
|
129232
|
+
const query = input.query.trim();
|
|
129233
|
+
if (containsOutboundSecret(query)) {
|
|
129234
|
+
if (deps.cwd !== undefined) {
|
|
129235
|
+
await recordOutboundSecretFinding(deps.cwd, "web_search", "query");
|
|
129236
|
+
}
|
|
129237
|
+
return { output: "web_search: outbound secret-shaped content", isError: true };
|
|
129238
|
+
}
|
|
129239
|
+
const response = await service.search(query);
|
|
128859
129240
|
if (!response.ok) {
|
|
128860
129241
|
return {
|
|
128861
129242
|
output: response.reason === "no-active-provider" ? "web_search: no active connected provider. Use /search-provider to configure one, test it, then use /search-connect to select it." : response.reason === "search-failed" ? `web_search: search failed${response.detail !== undefined && response.detail.length > 0 ? ` (${response.detail})` : ""}. Wait a few minutes before trying again, and do not switch providers yourself.` : "web_search: active provider is unavailable; reconnect it with /search-provider before retrying.",
|
|
@@ -129351,8 +129732,8 @@ function buildInteractiveAgentTools(input) {
|
|
|
129351
129732
|
const built = [
|
|
129352
129733
|
...builtinReadOnlyTools(input.cwd),
|
|
129353
129734
|
...metaprojectTools,
|
|
129354
|
-
webFetchTool(),
|
|
129355
|
-
webSearchTool(input.searchController),
|
|
129735
|
+
webFetchTool({ cwd: input.cwd }),
|
|
129736
|
+
webSearchTool(input.searchController, { cwd: input.cwd }),
|
|
129356
129737
|
shellExecTool(input.cwd, undefined, jobRegistry),
|
|
129357
129738
|
...jobRegistry !== undefined ? [
|
|
129358
129739
|
shellTaskOutputTool(jobRegistry, { observer: "main" }),
|
|
@@ -129825,12 +130206,13 @@ function evaluateShellApproval(input) {
|
|
|
129825
130206
|
const publishLease = input.meta?.publishLease === true;
|
|
129826
130207
|
const publishLeaseDetail = input.meta?.publishLeaseDetail;
|
|
129827
130208
|
const hookAsk = input.meta?.hookAsk === true;
|
|
130209
|
+
const untrustedOrigin = input.meta?.untrustedOrigin === true;
|
|
129828
130210
|
const audit = io.loadAudit();
|
|
129829
130211
|
for (const pattern of audit.permissions.allow) {
|
|
129830
130212
|
input.sessionAllow.add(pattern);
|
|
129831
130213
|
}
|
|
129832
130214
|
const tampered = io.fingerprint() !== input.fingerprintAtStart;
|
|
129833
|
-
const autoApprove = !destructive && !credentials && !sacReviewConfirmation && !publishLease && !hookAsk && isShellCommandAllowed(command, [...input.sessionAllow]);
|
|
130215
|
+
const autoApprove = !destructive && !credentials && !sacReviewConfirmation && !publishLease && !hookAsk && !untrustedOrigin && isShellCommandAllowed(command, [...input.sessionAllow]);
|
|
129834
130216
|
return {
|
|
129835
130217
|
command,
|
|
129836
130218
|
destructive,
|
|
@@ -129839,6 +130221,7 @@ function evaluateShellApproval(input) {
|
|
|
129839
130221
|
publishLease,
|
|
129840
130222
|
...publishLeaseDetail !== undefined ? { publishLeaseDetail } : {},
|
|
129841
130223
|
hookAsk,
|
|
130224
|
+
untrustedOrigin,
|
|
129842
130225
|
autoApprove,
|
|
129843
130226
|
rejected: audit.rejected,
|
|
129844
130227
|
tampered
|
|
@@ -129872,6 +130255,9 @@ function formatShellApprovalHints(evaled) {
|
|
|
129872
130255
|
if (evaled.hookAsk) {
|
|
129873
130256
|
lines.push("a policy hook asked about this call \u2014 will not be remembered");
|
|
129874
130257
|
}
|
|
130258
|
+
if (evaled.untrustedOrigin) {
|
|
130259
|
+
lines.push("follows untrusted external content \u2014 will not be remembered");
|
|
130260
|
+
}
|
|
129875
130261
|
if (evaled.publishLease) {
|
|
129876
130262
|
lines.push(evaled.publishLeaseDetail !== undefined ? `a git-publish lease applies \u2014 ${evaled.publishLeaseDetail} \u2014 will not be remembered` : "a peer's git-publish lease applies \u2014 will not be remembered");
|
|
129877
130263
|
}
|
|
@@ -131945,6 +132331,12 @@ function readRuntimeBlock(dispatch) {
|
|
|
131945
132331
|
return raw;
|
|
131946
132332
|
}
|
|
131947
132333
|
|
|
132334
|
+
// src/harness/external/env.ts
|
|
132335
|
+
init_service4();
|
|
132336
|
+
|
|
132337
|
+
// src/harness/external/env-deny.ts
|
|
132338
|
+
init_service4();
|
|
132339
|
+
|
|
131948
132340
|
// src/harness/external/env.ts
|
|
131949
132341
|
var EXTERNAL_RUNTIME_CREDENTIAL_ALLOW = {
|
|
131950
132342
|
"codex-cli": ["OPENAI_API_KEY"],
|
|
@@ -131952,17 +132344,18 @@ var EXTERNAL_RUNTIME_CREDENTIAL_ALLOW = {
|
|
|
131952
132344
|
};
|
|
131953
132345
|
var ENV_EXTERNAL_DEPTH = "KERYX_EXTERNAL_DEPTH";
|
|
131954
132346
|
function buildExternalChildEnv(input) {
|
|
131955
|
-
const denied = new Set(EXTERNAL_ENV_DENY);
|
|
132347
|
+
const denied = new Set(EXTERNAL_ENV_DENY.map((name) => name.toUpperCase()));
|
|
131956
132348
|
const allowedForThisRuntime = new Set((EXTERNAL_RUNTIME_CREDENTIAL_ALLOW[input.runtimeId] ?? []).map((name) => name.toUpperCase()));
|
|
131957
132349
|
const env = {};
|
|
131958
132350
|
for (const [key, value] of Object.entries(input.parent)) {
|
|
131959
132351
|
if (value === undefined)
|
|
131960
132352
|
continue;
|
|
131961
|
-
|
|
132353
|
+
const upper = key.toUpperCase();
|
|
132354
|
+
if (denied.has(upper))
|
|
131962
132355
|
continue;
|
|
131963
|
-
if (EXTERNAL_ENV_PREFIX_SWEEPS.some((prefix) =>
|
|
132356
|
+
if (EXTERNAL_ENV_PREFIX_SWEEPS.some((prefix) => upper.startsWith(prefix.toUpperCase())))
|
|
131964
132357
|
continue;
|
|
131965
|
-
if (!allowedForThisRuntime.has(
|
|
132358
|
+
if (!allowedForThisRuntime.has(upper) && isDeniedForMcpChild(key, value))
|
|
131966
132359
|
continue;
|
|
131967
132360
|
env[key] = value;
|
|
131968
132361
|
}
|
|
@@ -148035,7 +148428,7 @@ function ollamaShowAllowed(baseUrl) {
|
|
|
148035
148428
|
} catch {
|
|
148036
148429
|
return false;
|
|
148037
148430
|
}
|
|
148038
|
-
if (isPrivateEgressHost(host) && !
|
|
148431
|
+
if (isPrivateEgressHost(host) && !isLoopbackHost2(host)) {
|
|
148039
148432
|
return false;
|
|
148040
148433
|
}
|
|
148041
148434
|
return true;
|
|
@@ -153368,7 +153761,12 @@ async function runLocked(projectRoot, entry, action, deps, runId, now) {
|
|
|
153368
153761
|
const env = deps.grantedEnv ?? process.env;
|
|
153369
153762
|
const extraSecrets = await ghAuthToken(binaries.realpaths["gh"], grantedCwd, env);
|
|
153370
153763
|
const granted = action.grants.tools.map((id) => grantedToolSpec(id)).filter((spec) => spec !== undefined).map((spec) => grantedTool(spec, action, grantedCwd, env, grantedCalls, binaries.realpaths, extraSecrets, binaries.stats));
|
|
153371
|
-
const tools = [
|
|
153764
|
+
const tools = [
|
|
153765
|
+
...builtinReadOnlyTools(projectRoot),
|
|
153766
|
+
shellExecTool(workdir, sandboxedRunner(workdir, sandbox)),
|
|
153767
|
+
...granted,
|
|
153768
|
+
...deps.extraTools ?? []
|
|
153769
|
+
];
|
|
153372
153770
|
const offending = tools.filter((t) => UNATTENDED_EXCLUDED_TOOLS.includes(t.definition.name));
|
|
153373
153771
|
if (offending.length > 0)
|
|
153374
153772
|
throw new Error(`agent-task roster must not offer: ${offending.map((t) => t.definition.name).join(", ")}`);
|
|
@@ -153418,6 +153816,9 @@ async function runLocked(projectRoot, entry, action, deps, runId, now) {
|
|
|
153418
153816
|
};
|
|
153419
153817
|
const result = await runAgentTurn(io, agentDeps, history, buildAgentTaskPrompt(entry, action), { signal: controller.signal });
|
|
153420
153818
|
finishReason = result.finishReason;
|
|
153819
|
+
if (result.caughtToolErrors !== undefined && result.caughtToolErrors.length > 0) {
|
|
153820
|
+
crashed = result.caughtToolErrors.map((e) => `${e.toolName}: ${e.message}`).join("; ");
|
|
153821
|
+
}
|
|
153421
153822
|
} catch (error) {
|
|
153422
153823
|
crashed = error instanceof Error ? error.message : String(error);
|
|
153423
153824
|
} finally {
|
|
@@ -155507,14 +155908,19 @@ function attachUsageIo(io, chrome) {
|
|
|
155507
155908
|
}
|
|
155508
155909
|
});
|
|
155509
155910
|
}
|
|
155510
|
-
|
|
155911
|
+
var OPENAI_CACHED_INPUT_DISCOUNT = 0.5;
|
|
155912
|
+
function estimateTaskCostUsd(profile, providerId, inputTokens, outputTokens, cacheReadTokens) {
|
|
155511
155913
|
if (profile === undefined)
|
|
155512
155914
|
return;
|
|
155513
155915
|
const priceIn = profile.priceInputPerMillion.value;
|
|
155514
155916
|
const priceOut = profile.priceOutputPerMillion.value;
|
|
155515
155917
|
if (priceIn === "unknown" || priceOut === "unknown")
|
|
155516
155918
|
return;
|
|
155517
|
-
|
|
155919
|
+
const cachedInputDiscount = providerId === "openai" || providerId === "openai-codex" ? OPENAI_CACHED_INPUT_DISCOUNT : 1;
|
|
155920
|
+
const cacheRead = cacheReadTokens !== undefined && cacheReadTokens > 0 ? Math.min(cacheReadTokens, inputTokens) : 0;
|
|
155921
|
+
const fullPriceInputTokens = inputTokens - cacheRead;
|
|
155922
|
+
const inputCost = fullPriceInputTokens / 1e6 * priceIn + cacheRead / 1e6 * priceIn * cachedInputDiscount;
|
|
155923
|
+
return inputCost + outputTokens / 1e6 * priceOut;
|
|
155518
155924
|
}
|
|
155519
155925
|
async function recordTurnTaskCostBestEffort(input) {
|
|
155520
155926
|
if (input.inputTokens === 0 && input.outputTokens === 0)
|
|
@@ -155522,7 +155928,7 @@ async function recordTurnTaskCostBestEffort(input) {
|
|
|
155522
155928
|
try {
|
|
155523
155929
|
const profiles = loadModelProfiles(input.userConfigDir);
|
|
155524
155930
|
const profile = profiles[profileKey(input.providerId, input.modelId)];
|
|
155525
|
-
const costUsd = estimateTaskCostUsd(profile, input.inputTokens, input.outputTokens);
|
|
155931
|
+
const costUsd = estimateTaskCostUsd(profile, input.providerId, input.inputTokens, input.outputTokens, input.cacheReadTokens);
|
|
155526
155932
|
await appendTaskCostRecord({
|
|
155527
155933
|
providerId: input.providerId,
|
|
155528
155934
|
modelId: input.modelId,
|
|
@@ -155536,8 +155942,8 @@ async function recordTurnTaskCostBestEffort(input) {
|
|
|
155536
155942
|
}, input.userConfigDir);
|
|
155537
155943
|
} catch {}
|
|
155538
155944
|
}
|
|
155539
|
-
function shouldAutoApproveReadOnlySpawn(mode, hookAsk) {
|
|
155540
|
-
return mode === "read_only" && hookAsk !== true;
|
|
155945
|
+
function shouldAutoApproveReadOnlySpawn(mode, hookAsk, untrustedOrigin) {
|
|
155946
|
+
return mode === "read_only" && hookAsk !== true && untrustedOrigin !== true;
|
|
155541
155947
|
}
|
|
155542
155948
|
function isUserPromptApprovalCard(tool) {
|
|
155543
155949
|
return tool === "user_prompt";
|
|
@@ -157753,7 +158159,7 @@ async function launchTuiAgentShell(opts) {
|
|
|
157753
158159
|
}
|
|
157754
158160
|
}
|
|
157755
158161
|
} catch {}
|
|
157756
|
-
if (shouldAutoApproveReadOnlySpawn(mode, meta?.hookAsk)) {
|
|
158162
|
+
if (shouldAutoApproveReadOnlySpawn(mode, meta?.hookAsk, meta?.untrustedOrigin)) {
|
|
157757
158163
|
transcript.add(new otui.TextRenderable(r, {
|
|
157758
158164
|
id: `ap${uid++}`,
|
|
157759
158165
|
content: otui.t`${roleChunk(otui, "accent", "\u25C7 subagent auto-approved")} ${dimChunk(otui, `mode=read_only (no shell) \xB7 ${taskPreview}`)}`
|
|
@@ -157763,7 +158169,7 @@ async function launchTuiAgentShell(opts) {
|
|
|
157763
158169
|
chrome.hideMenu();
|
|
157764
158170
|
setMainAgent("blocked", "approval");
|
|
157765
158171
|
const id = await chrome.withOverlay(() => showComposerChoice(otui, r, chrome.dock, {
|
|
157766
|
-
title: mode === "read_only" ? "Spawn read-only subagent? (policy hook asked)" : "Spawn general subagent?",
|
|
158172
|
+
title: mode === "read_only" ? meta?.hookAsk === true ? "Spawn read-only subagent? (policy hook asked)" : "Spawn read-only subagent? (follows untrusted content)" : "Spawn general subagent?",
|
|
157767
158173
|
subtitle: taskPreview,
|
|
157768
158174
|
cancelId: "deny",
|
|
157769
158175
|
onOpen: () => chrome.blurComposer(),
|
|
@@ -157958,7 +158364,7 @@ async function launchTuiAgentShell(opts) {
|
|
|
157958
158364
|
const choice = await chrome.withOverlay(() => pickShellApproval(otui, r, chrome.dock, cmd, approvalContext, destructive, meta?.credentials === true, {
|
|
157959
158365
|
onOpen: () => chrome.blurComposer(),
|
|
157960
158366
|
signal: foregroundOperation.signal
|
|
157961
|
-
}, ev.publishLease, ev.publishLeaseDetail, ev.hookAsk));
|
|
158367
|
+
}, ev.publishLease, ev.publishLeaseDetail, ev.hookAsk || ev.untrustedOrigin));
|
|
157962
158368
|
input.focus();
|
|
157963
158369
|
if (choice === "deny") {
|
|
157964
158370
|
transcript.add(new otui.TextRenderable(r, {
|
|
@@ -160296,10 +160702,12 @@ Usage: /reasoning <${REASONING_EFFORT_LEVELS.join("|")}>
|
|
|
160296
160702
|
};
|
|
160297
160703
|
let turnInputTokens = 0;
|
|
160298
160704
|
let turnOutputTokens = 0;
|
|
160705
|
+
let turnCacheReadTokens = 0;
|
|
160299
160706
|
const prevOnUsageForCost = io.onUsage;
|
|
160300
160707
|
io.onUsage = (usage) => {
|
|
160301
160708
|
turnInputTokens += usage.inputTokens ?? 0;
|
|
160302
160709
|
turnOutputTokens += usage.outputTokens ?? 0;
|
|
160710
|
+
turnCacheReadTokens += usage.cacheReadTokens ?? 0;
|
|
160303
160711
|
prevOnUsageForCost?.(usage);
|
|
160304
160712
|
};
|
|
160305
160713
|
const suggestNextStep = async () => {
|
|
@@ -160399,6 +160807,7 @@ Usage: /reasoning <${REASONING_EFFORT_LEVELS.join("|")}>
|
|
|
160399
160807
|
modelId: deps.modelId,
|
|
160400
160808
|
inputTokens: turnInputTokens,
|
|
160401
160809
|
outputTokens: turnOutputTokens,
|
|
160810
|
+
...turnCacheReadTokens > 0 ? { cacheReadTokens: turnCacheReadTokens } : {},
|
|
160402
160811
|
success: !turnFailed,
|
|
160403
160812
|
...turnCategory !== undefined ? { category: turnCategory } : {}
|
|
160404
160813
|
});
|
|
@@ -162012,7 +162421,9 @@ async function runAgentRepl(lines, rich, deps, metaprojectPort, sessionOpts, ini
|
|
|
162012
162421
|
return settled;
|
|
162013
162422
|
}
|
|
162014
162423
|
const completion = new Promise((resolve) => {
|
|
162424
|
+
completionWaiters = [];
|
|
162015
162425
|
completionWaiters.push(() => resolve({ kind: "completion" }));
|
|
162426
|
+
rich.onCompletionWaitersSize?.(completionWaiters.length);
|
|
162016
162427
|
});
|
|
162017
162428
|
const winner = await Promise.race([pendingLine, completion]);
|
|
162018
162429
|
if (winner.kind !== "completion") {
|
|
@@ -162229,7 +162640,7 @@ ${indentBlock(style.dim(context), GUTTER)}`);
|
|
|
162229
162640
|
out(`${GUTTER}${style.yellow(hint)}
|
|
162230
162641
|
`);
|
|
162231
162642
|
}
|
|
162232
|
-
const rememberable = !evaled.destructive && !evaled.credentials && !evaled.sacReviewConfirmation && !evaled.publishLease && !evaled.hookAsk;
|
|
162643
|
+
const rememberable = !evaled.destructive && !evaled.credentials && !evaled.sacReviewConfirmation && !evaled.publishLease && !evaled.hookAsk && !evaled.untrustedOrigin;
|
|
162233
162644
|
const prompt = rememberable ? "[y/N/A=always] " : "[y/N] ";
|
|
162234
162645
|
out(`
|
|
162235
162646
|
${GUTTER}${style.yellow(`Run: ${evaled.command}`)} ${style.dim(prompt)}`);
|
|
@@ -162677,6 +163088,8 @@ ${GUTTER}${turnSeparator()}
|
|
|
162677
163088
|
continue;
|
|
162678
163089
|
}
|
|
162679
163090
|
await closeSlateSession(slateSession, mintTimestampAttemptId);
|
|
163091
|
+
lastToolOutput = undefined;
|
|
163092
|
+
lastToolName = undefined;
|
|
162680
163093
|
if (nextLive !== undefined) {
|
|
162681
163094
|
live = nextLive;
|
|
162682
163095
|
history = [];
|