@agent-inspect/mcp-server 6.7.5 → 6.9.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/index.cjs +273 -60
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +274 -61
- package/dist/index.mjs.map +1 -1
- package/package.json +3 -3
package/dist/index.mjs
CHANGED
|
@@ -4,7 +4,7 @@ import path from 'path';
|
|
|
4
4
|
import { fileURLToPath } from 'url';
|
|
5
5
|
import { AsyncLocalStorage } from 'async_hooks';
|
|
6
6
|
import crypto from 'crypto';
|
|
7
|
-
import { readdir, stat
|
|
7
|
+
import { readFile, readdir, stat } from 'fs/promises';
|
|
8
8
|
import os from 'os';
|
|
9
9
|
import 'nanoid';
|
|
10
10
|
import 'chalk';
|
|
@@ -1871,8 +1871,35 @@ var DEFAULT_RAW_CONTENT_KEYS = [
|
|
|
1871
1871
|
"toolinput",
|
|
1872
1872
|
"tool_input",
|
|
1873
1873
|
"tooloutput",
|
|
1874
|
-
"tool_output"
|
|
1874
|
+
"tool_output",
|
|
1875
|
+
// Framework / agent metadata that carries user or task text
|
|
1876
|
+
"currenttask",
|
|
1877
|
+
"current_task",
|
|
1878
|
+
"task",
|
|
1879
|
+
"userinput",
|
|
1880
|
+
"user_input",
|
|
1881
|
+
"requesttext",
|
|
1882
|
+
"request_text",
|
|
1883
|
+
"conversationtext",
|
|
1884
|
+
"conversation_text"
|
|
1875
1885
|
];
|
|
1886
|
+
var DEFAULT_SAFE_RAW_CONTENT_PATH_PREFIXES = ["tokenUsage", "usage"];
|
|
1887
|
+
var SAFE_USAGE_LEAF_KEYS = /* @__PURE__ */ new Set([
|
|
1888
|
+
"input",
|
|
1889
|
+
"output",
|
|
1890
|
+
"total",
|
|
1891
|
+
"cached",
|
|
1892
|
+
"input_tokens",
|
|
1893
|
+
"inputtokens",
|
|
1894
|
+
"output_tokens",
|
|
1895
|
+
"outputtokens",
|
|
1896
|
+
"total_tokens",
|
|
1897
|
+
"totaltokens",
|
|
1898
|
+
"prompt_tokens",
|
|
1899
|
+
"prompttokens",
|
|
1900
|
+
"completion_tokens",
|
|
1901
|
+
"completiontokens"
|
|
1902
|
+
]);
|
|
1876
1903
|
var DEFAULT_SECRET_PATTERNS = [
|
|
1877
1904
|
{ id: "bearer-token", pattern: /Bearer\s+[A-Za-z0-9._~+/-]{12,}=*/ },
|
|
1878
1905
|
{ id: "openai-key", pattern: /sk-[A-Za-z0-9_-]{16,}/ },
|
|
@@ -2050,7 +2077,11 @@ function normalizeFinding(rule, finding) {
|
|
|
2050
2077
|
message: finding.message,
|
|
2051
2078
|
...finding.expected !== void 0 ? { expected: finding.expected } : {},
|
|
2052
2079
|
...finding.actual !== void 0 ? { actual: finding.actual } : {},
|
|
2053
|
-
evidence: [...finding.evidence ?? []]
|
|
2080
|
+
evidence: [...finding.evidence ?? []],
|
|
2081
|
+
...finding.category !== void 0 ? { category: finding.category } : {},
|
|
2082
|
+
...finding.confidence !== void 0 ? { confidence: finding.confidence } : {},
|
|
2083
|
+
...finding.detector !== void 0 ? { detector: finding.detector } : {},
|
|
2084
|
+
...finding.action !== void 0 ? { action: finding.action } : {}
|
|
2054
2085
|
};
|
|
2055
2086
|
}
|
|
2056
2087
|
function summarize(findings, diagnostics) {
|
|
@@ -2081,15 +2112,19 @@ function eventEvidence(event, path14) {
|
|
|
2081
2112
|
function runEvidence(run) {
|
|
2082
2113
|
return run ? [{ runId: run.runId, name: run.name, status: run.status }] : [];
|
|
2083
2114
|
}
|
|
2084
|
-
function failFinding(ruleId, message, evidence, expected, actual) {
|
|
2115
|
+
function failFinding(ruleId, message, evidence, expected, actual, meta) {
|
|
2085
2116
|
return {
|
|
2086
2117
|
ruleId,
|
|
2087
|
-
severity: "error",
|
|
2088
|
-
status: "fail",
|
|
2118
|
+
severity: meta?.severity ?? "error",
|
|
2119
|
+
status: meta?.status ?? "fail",
|
|
2089
2120
|
message,
|
|
2090
2121
|
...expected !== void 0 ? { expected } : {},
|
|
2091
2122
|
...actual !== void 0 ? { actual } : {},
|
|
2092
|
-
evidence: [...evidence]
|
|
2123
|
+
evidence: [...evidence],
|
|
2124
|
+
...meta?.category !== void 0 ? { category: meta.category } : {},
|
|
2125
|
+
...meta?.confidence !== void 0 ? { confidence: meta.confidence } : {},
|
|
2126
|
+
...meta?.detector !== void 0 ? { detector: meta.detector } : {},
|
|
2127
|
+
...meta?.action !== void 0 ? { action: meta.action } : {}
|
|
2093
2128
|
};
|
|
2094
2129
|
}
|
|
2095
2130
|
function isRecord6(value) {
|
|
@@ -2170,6 +2205,19 @@ function isRawContentKey(key, forbiddenKeys) {
|
|
|
2170
2205
|
const normalized = normalizedKey(key);
|
|
2171
2206
|
return forbiddenKeys.some((forbidden) => normalized === normalizedKey(forbidden));
|
|
2172
2207
|
}
|
|
2208
|
+
function isSafeRawContentMetricPath(path14, key, safePathPrefixes) {
|
|
2209
|
+
const leaf = normalizedKey(key ?? lastPathSegment(path14));
|
|
2210
|
+
if (!SAFE_USAGE_LEAF_KEYS.has(leaf)) return false;
|
|
2211
|
+
const parts = path14.split(".").filter(Boolean);
|
|
2212
|
+
if (parts.length < 2) return false;
|
|
2213
|
+
const parent = parts[parts.length - 2] ?? "";
|
|
2214
|
+
const parentNorm = normalizedKey(parent);
|
|
2215
|
+
return safePathPrefixes.some((prefix) => parentNorm === normalizedKey(prefix));
|
|
2216
|
+
}
|
|
2217
|
+
function isRawContentPath(path14, key, forbiddenKeys, safePathPrefixes) {
|
|
2218
|
+
if (isSafeRawContentMetricPath(path14, key, safePathPrefixes)) return false;
|
|
2219
|
+
return isRawContentKey(key ?? lastPathSegment(path14), forbiddenKeys);
|
|
2220
|
+
}
|
|
2173
2221
|
function createRunStatusRule(options = {}) {
|
|
2174
2222
|
const expected = options.expected ?? "ok";
|
|
2175
2223
|
const allowIncomplete = options.allowIncomplete === true;
|
|
@@ -2228,7 +2276,13 @@ function createSafetyRedactionRule(options = {}) {
|
|
|
2228
2276
|
`Sensitive-looking field at ${entry.path} is not redacted.`,
|
|
2229
2277
|
[eventEvidence(event, entry.path)],
|
|
2230
2278
|
"redaction marker",
|
|
2231
|
-
{ path: entry.path, valueType: valueType(entry.value) }
|
|
2279
|
+
{ path: entry.path, valueType: valueType(entry.value) },
|
|
2280
|
+
{
|
|
2281
|
+
category: "credential",
|
|
2282
|
+
confidence: "high",
|
|
2283
|
+
detector: "safety.redaction",
|
|
2284
|
+
action: "redact"
|
|
2285
|
+
}
|
|
2232
2286
|
)
|
|
2233
2287
|
);
|
|
2234
2288
|
}
|
|
@@ -2239,6 +2293,7 @@ function createSafetyRedactionRule(options = {}) {
|
|
|
2239
2293
|
}
|
|
2240
2294
|
function createSafetyRawContentRule(options = {}) {
|
|
2241
2295
|
const forbiddenKeys = options.forbiddenKeys ?? DEFAULT_RAW_CONTENT_KEYS;
|
|
2296
|
+
const safePathPrefixes = options.safePathPrefixes ?? DEFAULT_SAFE_RAW_CONTENT_PATH_PREFIXES;
|
|
2242
2297
|
return {
|
|
2243
2298
|
id: "safety.rawPrompt",
|
|
2244
2299
|
category: "safety",
|
|
@@ -2248,14 +2303,20 @@ function createSafetyRawContentRule(options = {}) {
|
|
|
2248
2303
|
for (const event of context.events) {
|
|
2249
2304
|
for (const entry of eventValueEntries(event, { includeSummaries: options.includeSummaries })) {
|
|
2250
2305
|
const key = entry.key ?? lastPathSegment(entry.path);
|
|
2251
|
-
if (!
|
|
2306
|
+
if (!isRawContentPath(entry.path, key, forbiddenKeys, safePathPrefixes)) continue;
|
|
2252
2307
|
findings.push(
|
|
2253
2308
|
failFinding(
|
|
2254
2309
|
"safety.rawPrompt",
|
|
2255
2310
|
`Raw content-like field ${entry.path} is present.`,
|
|
2256
2311
|
[eventEvidence(event, entry.path)],
|
|
2257
2312
|
"metadata-only trace fields",
|
|
2258
|
-
{ path: entry.path, valueType: valueType(entry.value) }
|
|
2313
|
+
{ path: entry.path, valueType: valueType(entry.value) },
|
|
2314
|
+
{
|
|
2315
|
+
category: "raw-content",
|
|
2316
|
+
confidence: "high",
|
|
2317
|
+
detector: "safety.rawPrompt",
|
|
2318
|
+
action: "redact-or-omit"
|
|
2319
|
+
}
|
|
2259
2320
|
)
|
|
2260
2321
|
);
|
|
2261
2322
|
}
|
|
@@ -2287,7 +2348,13 @@ function createSafetySecretPatternRule(options = {}) {
|
|
|
2287
2348
|
`Secret-like pattern ${pattern.id} matched at ${entry.path}.`,
|
|
2288
2349
|
[eventEvidence(event, entry.path)],
|
|
2289
2350
|
"no secret-like strings",
|
|
2290
|
-
{ pattern: pattern.id, path: entry.path }
|
|
2351
|
+
{ pattern: pattern.id, path: entry.path },
|
|
2352
|
+
{
|
|
2353
|
+
category: "credential",
|
|
2354
|
+
confidence: "high",
|
|
2355
|
+
detector: pattern.id,
|
|
2356
|
+
action: "redact"
|
|
2357
|
+
}
|
|
2291
2358
|
)
|
|
2292
2359
|
);
|
|
2293
2360
|
break;
|
|
@@ -2314,7 +2381,13 @@ function createSafetyOversizedAttributeRule(options) {
|
|
|
2314
2381
|
`String at ${entry.path} exceeds ${options.maxStringLength} characters.`,
|
|
2315
2382
|
[eventEvidence(event, entry.path)],
|
|
2316
2383
|
{ maxStringLength: options.maxStringLength },
|
|
2317
|
-
{ path: entry.path, length: entry.value.length }
|
|
2384
|
+
{ path: entry.path, length: entry.value.length },
|
|
2385
|
+
{
|
|
2386
|
+
category: "size",
|
|
2387
|
+
confidence: "high",
|
|
2388
|
+
detector: "safety.oversizedAttribute",
|
|
2389
|
+
action: "truncate-or-omit"
|
|
2390
|
+
}
|
|
2318
2391
|
)
|
|
2319
2392
|
);
|
|
2320
2393
|
}
|
|
@@ -2325,7 +2398,13 @@ function createSafetyOversizedAttributeRule(options) {
|
|
|
2325
2398
|
`Array at ${entry.path} exceeds ${options.maxArrayLength} items.`,
|
|
2326
2399
|
[eventEvidence(event, entry.path)],
|
|
2327
2400
|
{ maxArrayLength: options.maxArrayLength },
|
|
2328
|
-
{ path: entry.path, length: entry.value.length }
|
|
2401
|
+
{ path: entry.path, length: entry.value.length },
|
|
2402
|
+
{
|
|
2403
|
+
category: "size",
|
|
2404
|
+
confidence: "high",
|
|
2405
|
+
detector: "safety.oversizedAttribute",
|
|
2406
|
+
action: "truncate-or-omit"
|
|
2407
|
+
}
|
|
2329
2408
|
)
|
|
2330
2409
|
);
|
|
2331
2410
|
}
|
|
@@ -2336,7 +2415,13 @@ function createSafetyOversizedAttributeRule(options) {
|
|
|
2336
2415
|
`Object at ${entry.path} exceeds ${options.maxObjectKeys} keys.`,
|
|
2337
2416
|
[eventEvidence(event, entry.path)],
|
|
2338
2417
|
{ maxObjectKeys: options.maxObjectKeys },
|
|
2339
|
-
{ path: entry.path, keys: Object.keys(entry.value).length }
|
|
2418
|
+
{ path: entry.path, keys: Object.keys(entry.value).length },
|
|
2419
|
+
{
|
|
2420
|
+
category: "size",
|
|
2421
|
+
confidence: "high",
|
|
2422
|
+
detector: "safety.oversizedAttribute",
|
|
2423
|
+
action: "truncate-or-omit"
|
|
2424
|
+
}
|
|
2340
2425
|
)
|
|
2341
2426
|
);
|
|
2342
2427
|
}
|
|
@@ -2349,7 +2434,13 @@ function createSafetyOversizedAttributeRule(options) {
|
|
|
2349
2434
|
`Value at ${entry.path} exceeds ${options.maxSerializedBytes} serialized bytes.`,
|
|
2350
2435
|
[eventEvidence(event, entry.path)],
|
|
2351
2436
|
{ maxSerializedBytes: options.maxSerializedBytes },
|
|
2352
|
-
{ path: entry.path, bytes }
|
|
2437
|
+
{ path: entry.path, bytes },
|
|
2438
|
+
{
|
|
2439
|
+
category: "size",
|
|
2440
|
+
confidence: "high",
|
|
2441
|
+
detector: "safety.oversizedAttribute",
|
|
2442
|
+
action: "truncate-or-omit"
|
|
2443
|
+
}
|
|
2353
2444
|
)
|
|
2354
2445
|
);
|
|
2355
2446
|
}
|
|
@@ -5623,43 +5714,6 @@ function exportRunTree(tree, options) {
|
|
|
5623
5714
|
}
|
|
5624
5715
|
}
|
|
5625
5716
|
}
|
|
5626
|
-
|
|
5627
|
-
// packages/mcp-server/src/assess-trace.ts
|
|
5628
|
-
var DEFAULT_MAX_STRING_LENGTH = 16384;
|
|
5629
|
-
var DEFAULT_MAX_ARRAY_LENGTH = 1e3;
|
|
5630
|
-
var DEFAULT_MAX_OBJECT_KEYS = 200;
|
|
5631
|
-
var DEFAULT_MAX_SERIALIZED_BYTES = 128 * 1024;
|
|
5632
|
-
function buildMcpSafetyRules() {
|
|
5633
|
-
return [
|
|
5634
|
-
createSafetyRawContentRule(),
|
|
5635
|
-
createSafetyRedactionRule(),
|
|
5636
|
-
createSafetySecretPatternRule(),
|
|
5637
|
-
createSafetyOversizedAttributeRule({
|
|
5638
|
-
maxStringLength: DEFAULT_MAX_STRING_LENGTH,
|
|
5639
|
-
maxArrayLength: DEFAULT_MAX_ARRAY_LENGTH,
|
|
5640
|
-
maxObjectKeys: DEFAULT_MAX_OBJECT_KEYS,
|
|
5641
|
-
maxSerializedBytes: DEFAULT_MAX_SERIALIZED_BYTES
|
|
5642
|
-
})
|
|
5643
|
-
];
|
|
5644
|
-
}
|
|
5645
|
-
function statusFrom(findings, hasErrors) {
|
|
5646
|
-
if (hasErrors) return "UNKNOWN";
|
|
5647
|
-
if (findings.some((item) => item.severity === "error")) return "UNSAFE";
|
|
5648
|
-
if (findings.some((item) => item.severity === "warning")) return "SAFE WITH WARNINGS";
|
|
5649
|
-
return "SAFE";
|
|
5650
|
-
}
|
|
5651
|
-
function assessTraceForMcp(read, runId) {
|
|
5652
|
-
const rules = buildMcpSafetyRules();
|
|
5653
|
-
const checkResult = runTraceChecks({ read }, { rules, runId });
|
|
5654
|
-
const hasErrors = checkResult.diagnostics.some((item) => item.severity === "error");
|
|
5655
|
-
const status = statusFrom(checkResult.findings, hasErrors);
|
|
5656
|
-
return {
|
|
5657
|
-
status,
|
|
5658
|
-
errors: checkResult.diagnostics.filter((item) => item.severity === "error").length + checkResult.findings.filter((item) => item.severity === "error").length,
|
|
5659
|
-
warnings: checkResult.diagnostics.filter((item) => item.severity === "warning").length + checkResult.findings.filter((item) => item.severity === "warning").length,
|
|
5660
|
-
findings: checkResult.findings.length
|
|
5661
|
-
};
|
|
5662
|
-
}
|
|
5663
5717
|
var DEFAULT_REDACT_KEYS2 = [
|
|
5664
5718
|
"authorization",
|
|
5665
5719
|
"cookie",
|
|
@@ -5690,7 +5744,12 @@ var SHARE_PROFILE_EXTRA_KEYS2 = [
|
|
|
5690
5744
|
"organizationId",
|
|
5691
5745
|
"traceId",
|
|
5692
5746
|
"spanId",
|
|
5693
|
-
"parentSpanId"
|
|
5747
|
+
"parentSpanId",
|
|
5748
|
+
"currentTask",
|
|
5749
|
+
"task",
|
|
5750
|
+
"userInput",
|
|
5751
|
+
"requestText",
|
|
5752
|
+
"conversationText"
|
|
5694
5753
|
];
|
|
5695
5754
|
var STRICT_PROFILE_EXTRA_KEYS2 = [
|
|
5696
5755
|
"prompt",
|
|
@@ -5758,6 +5817,61 @@ function passesLuhn(value) {
|
|
|
5758
5817
|
}
|
|
5759
5818
|
return sum % 10 === 0;
|
|
5760
5819
|
}
|
|
5820
|
+
var UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
5821
|
+
var EPOCH_MS_RE = /^1[0-9]{12}$/;
|
|
5822
|
+
var EMAIL_RE = /\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}\b/gi;
|
|
5823
|
+
function pathSuggestsNonCard(path14) {
|
|
5824
|
+
const normalized = path14.toLowerCase().replace(/[^a-z0-9._]/g, "");
|
|
5825
|
+
return /(^|\.)(tokenusage|usage)(\.|$)/.test(normalized) || /(startedat|endedat|durationms|timestamp|createdat|updatedat)(\.|$)/.test(normalized) || /(^|\.)(runid|traceid|spanid|eventid|sessionid|userid|parentid|requestid|correlationid)(\.|$)/.test(
|
|
5826
|
+
normalized
|
|
5827
|
+
) || /(^|\.)ts(\.|$)/.test(normalized);
|
|
5828
|
+
}
|
|
5829
|
+
function isUuidLike(value) {
|
|
5830
|
+
return UUID_RE.test(value.trim());
|
|
5831
|
+
}
|
|
5832
|
+
function isPlausibleCardCandidate(candidate, fullValue) {
|
|
5833
|
+
const digits = digitsOnly(candidate);
|
|
5834
|
+
if (digits.length < 13 || digits.length > 19) return false;
|
|
5835
|
+
if (!passesLuhn(candidate)) return false;
|
|
5836
|
+
const trimmed = fullValue.trim();
|
|
5837
|
+
if (isUuidLike(trimmed)) return false;
|
|
5838
|
+
if (EPOCH_MS_RE.test(digits) && digitsOnly(trimmed) === digits && /^\d+$/.test(trimmed)) {
|
|
5839
|
+
return false;
|
|
5840
|
+
}
|
|
5841
|
+
const start = fullValue.indexOf(candidate);
|
|
5842
|
+
if (start >= 0) {
|
|
5843
|
+
const before = fullValue[start - 1];
|
|
5844
|
+
const after = fullValue[start + candidate.length];
|
|
5845
|
+
if (before && /[A-Za-z0-9_]/.test(before)) return false;
|
|
5846
|
+
if (after && /[A-Za-z0-9_]/.test(after)) return false;
|
|
5847
|
+
}
|
|
5848
|
+
return true;
|
|
5849
|
+
}
|
|
5850
|
+
function looksLikePathOrPackageOrUrl(value) {
|
|
5851
|
+
const trimmed = value.trim();
|
|
5852
|
+
if (/^(?:[a-z][a-z0-9+.-]*:)?\/\//i.test(trimmed)) return true;
|
|
5853
|
+
if (/^webpack:/i.test(trimmed)) return true;
|
|
5854
|
+
if (/^@[A-Za-z0-9_.-]+\/[A-Za-z0-9_.@/-]+$/.test(trimmed)) return true;
|
|
5855
|
+
if (/^[A-Za-z]:[\\/]/.test(trimmed) || /^[/\\]/.test(trimmed)) return true;
|
|
5856
|
+
if (/[\\/]/.test(trimmed) && !/\s/.test(trimmed)) return true;
|
|
5857
|
+
return false;
|
|
5858
|
+
}
|
|
5859
|
+
function emailMatchIsPathAdjacent(value, index, length) {
|
|
5860
|
+
const before = value[index - 1];
|
|
5861
|
+
const after = value[index + length];
|
|
5862
|
+
return before === "/" || before === "\\" || after === "/" || after === "\\";
|
|
5863
|
+
}
|
|
5864
|
+
function valueContainsEmail(value) {
|
|
5865
|
+
if (looksLikePathOrPackageOrUrl(value)) return false;
|
|
5866
|
+
EMAIL_RE.lastIndex = 0;
|
|
5867
|
+
for (const match of value.matchAll(EMAIL_RE)) {
|
|
5868
|
+
const text = match[0] ?? "";
|
|
5869
|
+
const index = match.index ?? 0;
|
|
5870
|
+
if (emailMatchIsPathAdjacent(value, index, text.length)) continue;
|
|
5871
|
+
return true;
|
|
5872
|
+
}
|
|
5873
|
+
return false;
|
|
5874
|
+
}
|
|
5761
5875
|
var credentialDetectors = [
|
|
5762
5876
|
patternDetector({
|
|
5763
5877
|
id: "value.authorizationHeader",
|
|
@@ -5805,10 +5919,12 @@ var credentialDetectors = [
|
|
|
5805
5919
|
matchKind: "value",
|
|
5806
5920
|
detect(input) {
|
|
5807
5921
|
if (typeof input.value !== "string") return [];
|
|
5922
|
+
if (pathSuggestsNonCard(input.path)) return [];
|
|
5923
|
+
if (isUuidLike(input.value)) return [];
|
|
5808
5924
|
const candidatePattern = /(?:\d[ -]?){13,19}/g;
|
|
5809
5925
|
for (const match of input.value.matchAll(candidatePattern)) {
|
|
5810
5926
|
const candidate = match[0] ?? "";
|
|
5811
|
-
if (
|
|
5927
|
+
if (isPlausibleCardCandidate(candidate, input.value)) {
|
|
5812
5928
|
return [{ action: "replace", severity: "error", matchKind: "value" }];
|
|
5813
5929
|
}
|
|
5814
5930
|
}
|
|
@@ -5817,10 +5933,15 @@ var credentialDetectors = [
|
|
|
5817
5933
|
}
|
|
5818
5934
|
];
|
|
5819
5935
|
var identifierDetectors = [
|
|
5820
|
-
|
|
5936
|
+
{
|
|
5821
5937
|
id: "value.email",
|
|
5822
|
-
|
|
5823
|
-
|
|
5938
|
+
severity: "warning",
|
|
5939
|
+
matchKind: "value",
|
|
5940
|
+
detect(input) {
|
|
5941
|
+
if (typeof input.value !== "string") return [];
|
|
5942
|
+
return valueContainsEmail(input.value) ? [{ action: "replace", severity: "warning", matchKind: "value" }] : [];
|
|
5943
|
+
}
|
|
5944
|
+
},
|
|
5824
5945
|
patternDetector({
|
|
5825
5946
|
id: "value.phone",
|
|
5826
5947
|
pattern: /\b(?:\+\d{1,3}[\s.-]?)?(?:\(?\d{3}\)?[\s.-])\d{3}[\s.-]\d{4}\b/
|
|
@@ -6038,6 +6159,92 @@ function redact(value, options) {
|
|
|
6038
6159
|
return createRedactor(options).redact(value);
|
|
6039
6160
|
}
|
|
6040
6161
|
|
|
6162
|
+
// packages/mcp-server/src/assess-trace.ts
|
|
6163
|
+
var DEFAULT_MAX_STRING_LENGTH = 16384;
|
|
6164
|
+
var DEFAULT_MAX_ARRAY_LENGTH = 1e3;
|
|
6165
|
+
var DEFAULT_MAX_OBJECT_KEYS = 200;
|
|
6166
|
+
var DEFAULT_MAX_SERIALIZED_BYTES = 128 * 1024;
|
|
6167
|
+
function buildMcpSafetyRules() {
|
|
6168
|
+
return [
|
|
6169
|
+
createSafetyRawContentRule(),
|
|
6170
|
+
createSafetyRedactionRule(),
|
|
6171
|
+
createSafetySecretPatternRule(),
|
|
6172
|
+
createSafetyOversizedAttributeRule({
|
|
6173
|
+
maxStringLength: DEFAULT_MAX_STRING_LENGTH,
|
|
6174
|
+
maxArrayLength: DEFAULT_MAX_ARRAY_LENGTH,
|
|
6175
|
+
maxObjectKeys: DEFAULT_MAX_OBJECT_KEYS,
|
|
6176
|
+
maxSerializedBytes: DEFAULT_MAX_SERIALIZED_BYTES
|
|
6177
|
+
})
|
|
6178
|
+
];
|
|
6179
|
+
}
|
|
6180
|
+
function statusFrom(findings, hasErrors) {
|
|
6181
|
+
if (hasErrors) return "UNKNOWN";
|
|
6182
|
+
if (findings.some((item) => item.severity === "error")) return "UNSAFE";
|
|
6183
|
+
if (findings.some((item) => item.severity === "warning")) return "SAFE WITH WARNINGS";
|
|
6184
|
+
return "SAFE";
|
|
6185
|
+
}
|
|
6186
|
+
function redactJsonl(content, profile) {
|
|
6187
|
+
const trimmed = content.trim();
|
|
6188
|
+
if (trimmed.startsWith("{")) {
|
|
6189
|
+
try {
|
|
6190
|
+
const parsed = JSON.parse(trimmed);
|
|
6191
|
+
if (parsed !== null && typeof parsed === "object" && !Array.isArray(parsed) && ("schemaVersion" in parsed || "eventId" in parsed || "runId" in parsed)) {
|
|
6192
|
+
} else {
|
|
6193
|
+
const result = redact(parsed, { profile });
|
|
6194
|
+
return `${JSON.stringify(result.value)}
|
|
6195
|
+
`;
|
|
6196
|
+
}
|
|
6197
|
+
} catch {
|
|
6198
|
+
}
|
|
6199
|
+
}
|
|
6200
|
+
const lines = content.split(/\r?\n/);
|
|
6201
|
+
const out = [];
|
|
6202
|
+
for (const line of lines) {
|
|
6203
|
+
if (line.trim() === "") continue;
|
|
6204
|
+
const parsed = JSON.parse(line);
|
|
6205
|
+
const result = redact(parsed, { profile });
|
|
6206
|
+
out.push(JSON.stringify(result.value));
|
|
6207
|
+
}
|
|
6208
|
+
return out.length === 0 ? "" : `${out.join("\n")}
|
|
6209
|
+
`;
|
|
6210
|
+
}
|
|
6211
|
+
function assessTraceForMcp(read, runId) {
|
|
6212
|
+
const rules = buildMcpSafetyRules();
|
|
6213
|
+
const checkResult = runTraceChecks({ read }, { rules, runId });
|
|
6214
|
+
const hasErrors = checkResult.diagnostics.some((item) => item.severity === "error");
|
|
6215
|
+
const status = statusFrom(checkResult.findings, hasErrors);
|
|
6216
|
+
return {
|
|
6217
|
+
status,
|
|
6218
|
+
errors: checkResult.diagnostics.filter((item) => item.severity === "error").length + checkResult.findings.filter((item) => item.severity === "error").length,
|
|
6219
|
+
warnings: checkResult.diagnostics.filter((item) => item.severity === "warning").length + checkResult.findings.filter((item) => item.severity === "warning").length,
|
|
6220
|
+
findings: checkResult.findings.length
|
|
6221
|
+
};
|
|
6222
|
+
}
|
|
6223
|
+
async function assessTraceArtifactForMcp(options) {
|
|
6224
|
+
const source = assessTraceForMcp(options.read, options.runId);
|
|
6225
|
+
try {
|
|
6226
|
+
const raw = await readFile(options.filePath, "utf-8");
|
|
6227
|
+
const redacted = redactJsonl(raw, options.profile);
|
|
6228
|
+
const artifactRead = await openTrace(
|
|
6229
|
+
{ type: "string", content: redacted },
|
|
6230
|
+
{ format: "agent-inspect-jsonl" }
|
|
6231
|
+
);
|
|
6232
|
+
const artifact = assessTraceForMcp(artifactRead, options.runId);
|
|
6233
|
+
return {
|
|
6234
|
+
...artifact,
|
|
6235
|
+
sourceStatus: source.status
|
|
6236
|
+
};
|
|
6237
|
+
} catch {
|
|
6238
|
+
return {
|
|
6239
|
+
status: "UNKNOWN",
|
|
6240
|
+
errors: Math.max(1, source.errors),
|
|
6241
|
+
warnings: source.warnings,
|
|
6242
|
+
findings: source.findings,
|
|
6243
|
+
sourceStatus: source.status
|
|
6244
|
+
};
|
|
6245
|
+
}
|
|
6246
|
+
}
|
|
6247
|
+
|
|
6041
6248
|
// packages/mcp-server/src/prepare-result.ts
|
|
6042
6249
|
var DEFAULT_MCP_RESULT_MAX_BYTES = 512 * 1024;
|
|
6043
6250
|
function stableStringify(value) {
|
|
@@ -6396,16 +6603,21 @@ async function callReadOnlyTool(context, name, args = {}) {
|
|
|
6396
6603
|
}
|
|
6397
6604
|
case "create_share_safe_bundle": {
|
|
6398
6605
|
const runId = String(args.runId ?? "");
|
|
6399
|
-
const { read } = await openRunTrace(context, runId);
|
|
6606
|
+
const { meta, read } = await openRunTrace(context, runId);
|
|
6400
6607
|
const run = read.runs.find((item) => item.runId === runId) ?? read.runs[0];
|
|
6401
6608
|
if (!run) return errorResult2(`Run tree not found: ${runId}`);
|
|
6402
|
-
const
|
|
6609
|
+
const profile = redactionProfileForExport(context);
|
|
6610
|
+
const safety = await assessTraceArtifactForMcp({
|
|
6611
|
+
read,
|
|
6612
|
+
runId,
|
|
6613
|
+
filePath: meta.filePath,
|
|
6614
|
+
profile
|
|
6615
|
+
});
|
|
6403
6616
|
if (bundleFailsOnSafety(safety.status)) {
|
|
6404
6617
|
return errorResult2(
|
|
6405
|
-
`Share-safe bundle refused: safety status is ${safety.status}. Resolve findings before export.`
|
|
6618
|
+
`Share-safe bundle refused: artifact safety status is ${safety.status}. Resolve findings before export.`
|
|
6406
6619
|
);
|
|
6407
6620
|
}
|
|
6408
|
-
const profile = redactionProfileForExport(context);
|
|
6409
6621
|
const markdown = exportMarkdown(run, {
|
|
6410
6622
|
redacted: true});
|
|
6411
6623
|
const tree = exportRunTree(run, {
|
|
@@ -6423,6 +6635,7 @@ async function callReadOnlyTool(context, name, args = {}) {
|
|
|
6423
6635
|
{
|
|
6424
6636
|
runId,
|
|
6425
6637
|
status: safety.status,
|
|
6638
|
+
...safety.sourceStatus !== void 0 ? { sourceStatus: safety.sourceStatus } : {},
|
|
6426
6639
|
errors: safety.errors,
|
|
6427
6640
|
warnings: safety.warnings,
|
|
6428
6641
|
findings: safety.findings
|