@agent-inspect/mcp-server 6.31.2 → 6.31.4

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.
@@ -36,6 +36,80 @@ function extractCorrelationMetadata(record) {
36
36
  }
37
37
  return found ? out : void 0;
38
38
  }
39
+
40
+ // packages/core/src/safety/sensitive-key.ts
41
+ function keyHasExplicitSeparator(value) {
42
+ return /[_\-.]/.test(value);
43
+ }
44
+ function normalizeSensitiveKey(value) {
45
+ return value.replace(/([a-z0-9])([A-Z])/g, "$1_$2").toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, "");
46
+ }
47
+ var NON_CREDENTIAL_TOKEN_CONFIG_KEYS = new Set(
48
+ [
49
+ "tokens",
50
+ "max_tokens",
51
+ "min_tokens",
52
+ "ls_max_tokens",
53
+ "token_count",
54
+ "token_limit",
55
+ "token_budget",
56
+ "input_tokens",
57
+ "output_tokens",
58
+ "total_tokens",
59
+ "cached_tokens",
60
+ "prompt_tokens",
61
+ "completion_tokens"
62
+ ].map(normalizeSensitiveKey)
63
+ );
64
+ var DEFAULT_CREDENTIAL_SENSITIVE_KEYS = [
65
+ "authorization",
66
+ "cookie",
67
+ "token",
68
+ "access_token",
69
+ "accesstoken",
70
+ "auth_token",
71
+ "authtoken",
72
+ "refresh_token",
73
+ "refreshtoken",
74
+ "id_token",
75
+ "idtoken",
76
+ "bearer_token",
77
+ "bearertoken",
78
+ "api_token",
79
+ "apitoken",
80
+ "apikey",
81
+ "api_key",
82
+ "password",
83
+ "secret",
84
+ "email"
85
+ ];
86
+ function isTokenCredentialKey(normalized) {
87
+ if (NON_CREDENTIAL_TOKEN_CONFIG_KEYS.has(normalized)) return false;
88
+ if (normalized === "token") return true;
89
+ if (normalized.endsWith("tokens")) return false;
90
+ return normalized.endsWith("token");
91
+ }
92
+ function isCredentialSensitiveKey(key, sensitiveKeys = DEFAULT_CREDENTIAL_SENSITIVE_KEYS) {
93
+ if (!key) return false;
94
+ const normalized = normalizeSensitiveKey(key);
95
+ if (!normalized) return false;
96
+ if (NON_CREDENTIAL_TOKEN_CONFIG_KEYS.has(normalized)) return false;
97
+ const allowPrefixCompound = keyHasExplicitSeparator(key);
98
+ for (const sensitive of sensitiveKeys) {
99
+ const s = normalizeSensitiveKey(sensitive);
100
+ if (!s) continue;
101
+ if (s === "token") {
102
+ if (isTokenCredentialKey(normalized)) return true;
103
+ continue;
104
+ }
105
+ if (normalized === s) return true;
106
+ if (normalized.endsWith(`_${s}`)) return true;
107
+ if (allowPrefixCompound && normalized.startsWith(`${s}_`)) return true;
108
+ }
109
+ return false;
110
+ }
111
+
112
+ // packages/core/src/logs/redactor.ts
39
113
  var DEFAULT_REDACT_KEYS = [
40
114
  "authorization",
41
115
  "cookie",
@@ -59,7 +133,7 @@ function compileRules(rules, extraKeys) {
59
133
  const out = /* @__PURE__ */ new Map();
60
134
  const set = (r) => {
61
135
  const k = toKey(r.key);
62
- out.set(k, { ...r, key: k });
136
+ out.set(k, { ...r, key: k, raw: r.raw ?? r.key });
63
137
  };
64
138
  for (const k of DEFAULT_REDACT_KEYS) {
65
139
  set({ key: k, strategy: "full" });
@@ -88,9 +162,13 @@ var Redactor = class {
88
162
  constructor(options) {
89
163
  this.#rules = compileRules(options?.rules, options?.extraKeys);
90
164
  }
165
+ /** Field names this redactor treats as sensitive, for value-level helpers. */
166
+ get sensitiveKeys() {
167
+ return this.#rules.map((rule) => rule.raw ?? rule.key);
168
+ }
91
169
  redactValue(key, value) {
92
170
  const k = toKey(key);
93
- const rule = this.#rules.find((r) => r.key === k);
171
+ const rule = this.#rules.find((r) => r.key === k) ?? this.#rules.find((r) => isCredentialSensitiveKey(key, [r.raw ?? r.key]));
94
172
  if (!rule) {
95
173
  return this.#redactNested(value);
96
174
  }
@@ -129,6 +207,30 @@ var Redactor = class {
129
207
  }
130
208
  };
131
209
 
210
+ // packages/core/src/safety/credential-value-patterns.ts
211
+ var KEY_VALUE_SECRET_PATTERN = /\b(?:api[_-]?key|internal[_-]?token|access[_-]?token|auth[_-]?token|password|secret|token)=(?!\[(?:REDACTED(?::[^\]]*)?|HASH:[0-9a-f]{8})\](?![^\s"'\\&#?;/]))([^\s"'\\]{8,})/i;
212
+ var HIGH_CONFIDENCE_CREDENTIAL_PATTERNS = [
213
+ /^(?:basic|bearer|digest|apikey)\s+[a-z0-9._~+/=-]+$/i,
214
+ /\bbearer\s+[a-z0-9._~+/=-]{12,}\b/i,
215
+ /\b[a-z0-9_.-]+=[^;\s]+(?:;\s*[a-z0-9_.-]+=[^;\s]+)+/i,
216
+ /\beyJ[a-zA-Z0-9_-]{8,}\.[a-zA-Z0-9_-]{8,}\.[a-zA-Z0-9_-]{8,}\b/,
217
+ /\b(?:sk-(?:proj-)?[a-zA-Z0-9_-]{16,}|sk-ant-[a-zA-Z0-9_-]{16,}|AIza[0-9A-Za-z_-]{20,})\b/,
218
+ /\b(?:ghp|gho|ghu|ghs|ghr)_[A-Za-z0-9_]{20,}\b/,
219
+ /\b(?:AKIA|ASIA)[A-Z0-9]{16}\b/,
220
+ /-----BEGIN [A-Z ]*PRIVATE KEY-----[\s\S]*-----END [A-Z ]*PRIVATE KEY-----/,
221
+ KEY_VALUE_SECRET_PATTERN
222
+ ];
223
+ function stringContainsHighConfidenceCredential(value) {
224
+ for (const pattern of HIGH_CONFIDENCE_CREDENTIAL_PATTERNS) {
225
+ pattern.lastIndex = 0;
226
+ if (pattern.test(value)) {
227
+ pattern.lastIndex = 0;
228
+ return true;
229
+ }
230
+ }
231
+ return false;
232
+ }
233
+
132
234
  // packages/core/src/redaction-profiles.ts
133
235
  var SHARE_PROFILE_EXTRA_KEYS = [
134
236
  "userEmail",
@@ -2299,78 +2401,6 @@ function formatProgrammaticDiagnostic(code, detail) {
2299
2401
  return `${code}: ${summary} Remediation: ${spec.remediation}`;
2300
2402
  }
2301
2403
 
2302
- // packages/core/src/safety/sensitive-key.ts
2303
- function keyHasExplicitSeparator(value) {
2304
- return /[_\-.]/.test(value);
2305
- }
2306
- function normalizeSensitiveKey(value) {
2307
- return value.replace(/([a-z0-9])([A-Z])/g, "$1_$2").toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, "");
2308
- }
2309
- var NON_CREDENTIAL_TOKEN_CONFIG_KEYS = new Set(
2310
- [
2311
- "tokens",
2312
- "max_tokens",
2313
- "min_tokens",
2314
- "ls_max_tokens",
2315
- "token_count",
2316
- "token_limit",
2317
- "token_budget",
2318
- "input_tokens",
2319
- "output_tokens",
2320
- "total_tokens",
2321
- "cached_tokens",
2322
- "prompt_tokens",
2323
- "completion_tokens"
2324
- ].map(normalizeSensitiveKey)
2325
- );
2326
- var DEFAULT_CREDENTIAL_SENSITIVE_KEYS = [
2327
- "authorization",
2328
- "cookie",
2329
- "token",
2330
- "access_token",
2331
- "accesstoken",
2332
- "auth_token",
2333
- "authtoken",
2334
- "refresh_token",
2335
- "refreshtoken",
2336
- "id_token",
2337
- "idtoken",
2338
- "bearer_token",
2339
- "bearertoken",
2340
- "api_token",
2341
- "apitoken",
2342
- "apikey",
2343
- "api_key",
2344
- "password",
2345
- "secret",
2346
- "email"
2347
- ];
2348
- function isTokenCredentialKey(normalized) {
2349
- if (NON_CREDENTIAL_TOKEN_CONFIG_KEYS.has(normalized)) return false;
2350
- if (normalized === "token") return true;
2351
- if (normalized.endsWith("tokens")) return false;
2352
- return normalized.endsWith("token");
2353
- }
2354
- function isCredentialSensitiveKey(key, sensitiveKeys = DEFAULT_CREDENTIAL_SENSITIVE_KEYS) {
2355
- if (!key) return false;
2356
- const normalized = normalizeSensitiveKey(key);
2357
- if (!normalized) return false;
2358
- if (NON_CREDENTIAL_TOKEN_CONFIG_KEYS.has(normalized)) return false;
2359
- const allowPrefixCompound = keyHasExplicitSeparator(key);
2360
- for (const sensitive of sensitiveKeys) {
2361
- const s = normalizeSensitiveKey(sensitive);
2362
- if (!s) continue;
2363
- if (s === "token") {
2364
- if (isTokenCredentialKey(normalized)) return true;
2365
- continue;
2366
- }
2367
- if (normalized === s) return true;
2368
- if (normalized.endsWith(`_${s}`)) return true;
2369
- if (allowPrefixCompound && normalized.startsWith(`${s}_`)) return true;
2370
- }
2371
- return false;
2372
- }
2373
-
2374
2404
  // packages/core/src/checks/logical-events.ts
2375
2405
  function isRecord6(value) {
2376
2406
  return typeof value === "object" && value !== null && !Array.isArray(value);
@@ -3321,7 +3351,7 @@ var DEFAULT_SECRET_PATTERNS = [
3321
3351
  // Keep in sync with packages/redact/src/key-value-secret.ts (KEY_VALUE_SECRET_PATTERN_SOURCE).
3322
3352
  {
3323
3353
  id: "key-value-secret",
3324
- pattern: /\b(?:api[_-]?key|internal[_-]?token|access[_-]?token|auth[_-]?token|password|secret|token)=([^\s"'\\]{8,})/i
3354
+ pattern: /\b(?:api[_-]?key|internal[_-]?token|access[_-]?token|auth[_-]?token|password|secret|token)=(?!\[(?:REDACTED(?::[^\]]*)?|HASH:[0-9a-f]{8})\](?![^\s"'\\&#?;/]))([^\s"'\\]{8,})/i
3325
3355
  }
3326
3356
  ];
3327
3357
  function compareStrings(a, b) {
@@ -6662,6 +6692,99 @@ function openTrace(input, options = {}) {
6662
6692
  // packages/core/src/exporters/types.ts
6663
6693
  var EXPORT_PAYLOAD_VERSION = "0.1.2";
6664
6694
 
6695
+ // packages/core/src/safety/url-redaction.ts
6696
+ var HTTP_URL = /^https?:\/\/[^\s]+$/i;
6697
+ var UUID_SEGMENT = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
6698
+ var HEX_SEGMENT = /^[0-9a-f]{12,}$/i;
6699
+ var NUMERIC_SEGMENT = /^\d{5,}$/;
6700
+ var OPAQUE_SEGMENT = /^[A-Za-z0-9_-]{20,}$/;
6701
+ function looksLikeHttpUrl(value) {
6702
+ return HTTP_URL.test(value.trim());
6703
+ }
6704
+ function parseHttpUrl(value) {
6705
+ try {
6706
+ const url = new URL(value.trim());
6707
+ return url.protocol === "http:" || url.protocol === "https:" ? url : void 0;
6708
+ } catch {
6709
+ return void 0;
6710
+ }
6711
+ }
6712
+ function isIdentifierSegment(segment) {
6713
+ if (UUID_SEGMENT.test(segment)) return true;
6714
+ if (HEX_SEGMENT.test(segment)) return true;
6715
+ if (NUMERIC_SEGMENT.test(segment)) return true;
6716
+ return OPAQUE_SEGMENT.test(segment) && /\d/.test(segment) && /[A-Za-z]/.test(segment);
6717
+ }
6718
+ function decodeParam(value) {
6719
+ try {
6720
+ return decodeURIComponent(value.replace(/\+/g, " "));
6721
+ } catch {
6722
+ return value;
6723
+ }
6724
+ }
6725
+ function redactParams(raw, options, replacement) {
6726
+ let redacted = false;
6727
+ const parts = raw.split("&").map((part) => {
6728
+ if (part.length === 0) return part;
6729
+ const separator = part.indexOf("=");
6730
+ if (separator < 0) return part;
6731
+ const rawKey = part.slice(0, separator);
6732
+ const rawValue = part.slice(separator + 1);
6733
+ const key = decodeParam(rawKey);
6734
+ const value = decodeParam(rawValue);
6735
+ if (isCredentialSensitiveKey(key, options.sensitiveKeys) || options.isSensitiveValue(value)) {
6736
+ redacted = true;
6737
+ return `${rawKey}=${replacement}`;
6738
+ }
6739
+ return part;
6740
+ });
6741
+ return { value: parts.join("&"), redacted };
6742
+ }
6743
+ function redactUrlString(value, options) {
6744
+ const unchanged = {
6745
+ value,
6746
+ credentialRedacted: false,
6747
+ identifiersMasked: false
6748
+ };
6749
+ if (!looksLikeHttpUrl(value)) return unchanged;
6750
+ const url = parseHttpUrl(value);
6751
+ if (!url) return unchanged;
6752
+ const replacement = options.replacement ?? "[REDACTED]";
6753
+ const idPlaceholder = options.idPlaceholder ?? "[id]";
6754
+ let credentialRedacted = url.username.length > 0 || url.password.length > 0;
6755
+ let identifiersMasked = false;
6756
+ let pathname = url.pathname;
6757
+ if (options.maskPathIds === true) {
6758
+ pathname = url.pathname.split("/").map((segment) => {
6759
+ if (segment.length === 0 || !isIdentifierSegment(decodeParam(segment))) {
6760
+ return segment;
6761
+ }
6762
+ identifiersMasked = true;
6763
+ return idPlaceholder;
6764
+ }).join("/");
6765
+ }
6766
+ let search = "";
6767
+ if (url.search.length > 1) {
6768
+ const params = redactParams(url.search.slice(1), options, replacement);
6769
+ if (params.redacted) credentialRedacted = true;
6770
+ search = `?${params.value}`;
6771
+ } else if (url.search.length === 1) {
6772
+ search = "?";
6773
+ }
6774
+ let hash = url.hash;
6775
+ if (url.hash.includes("=")) {
6776
+ const params = redactParams(url.hash.slice(1), options, replacement);
6777
+ if (params.redacted) credentialRedacted = true;
6778
+ hash = `#${params.value}`;
6779
+ }
6780
+ if (!credentialRedacted && !identifiersMasked) return unchanged;
6781
+ return {
6782
+ value: `${url.protocol}//${url.host}${pathname}${search}${hash}`,
6783
+ credentialRedacted,
6784
+ identifiersMasked
6785
+ };
6786
+ }
6787
+
6665
6788
  // packages/core/src/exporters/redact-export.ts
6666
6789
  function isRecord11(value) {
6667
6790
  return typeof value === "object" && value !== null && !Array.isArray(value);
@@ -6679,21 +6802,34 @@ function deepClone(value) {
6679
6802
  }
6680
6803
  return out;
6681
6804
  }
6682
- function boundAttributeValues(record, maxMetadataValueLength, maxPreviewLength, seen, depth) {
6805
+ function boundAttributeValues(record, sensitiveKeys, maxMetadataValueLength, maxPreviewLength, seen, depth) {
6683
6806
  if (depth > 32) {
6684
6807
  return { truncated: true, reason: "maxDepth" };
6685
6808
  }
6686
6809
  const out = {};
6687
6810
  for (const [key, value] of Object.entries(record)) {
6688
- out[key] = boundValue(value, key, maxMetadataValueLength, maxPreviewLength, seen, depth);
6811
+ out[key] = boundValue(
6812
+ value,
6813
+ key,
6814
+ sensitiveKeys,
6815
+ maxMetadataValueLength,
6816
+ maxPreviewLength,
6817
+ seen,
6818
+ depth
6819
+ );
6689
6820
  }
6690
6821
  return out;
6691
6822
  }
6692
- function boundValue(value, key, maxMetadataValueLength, maxPreviewLength, seen, depth) {
6823
+ function boundValue(value, key, sensitiveKeys, maxMetadataValueLength, maxPreviewLength, seen, depth) {
6693
6824
  if (value === null || typeof value !== "object") {
6694
6825
  if (typeof value === "string") {
6826
+ const rewritten = redactUrlString(value, {
6827
+ sensitiveKeys,
6828
+ isSensitiveValue: stringContainsHighConfidenceCredential,
6829
+ maskPathIds: true
6830
+ }).value;
6695
6831
  return truncateStringForProfile(
6696
- value,
6832
+ stringContainsHighConfidenceCredential(rewritten) ? "[REDACTED]" : rewritten,
6697
6833
  key,
6698
6834
  maxMetadataValueLength,
6699
6835
  maxPreviewLength
@@ -6708,6 +6844,7 @@ function boundValue(value, key, maxMetadataValueLength, maxPreviewLength, seen,
6708
6844
  (item, index) => boundValue(
6709
6845
  item,
6710
6846
  String(index),
6847
+ sensitiveKeys,
6711
6848
  maxMetadataValueLength,
6712
6849
  maxPreviewLength,
6713
6850
  seen,
@@ -6717,6 +6854,7 @@ function boundValue(value, key, maxMetadataValueLength, maxPreviewLength, seen,
6717
6854
  }
6718
6855
  return boundAttributeValues(
6719
6856
  value,
6857
+ sensitiveKeys,
6720
6858
  maxMetadataValueLength,
6721
6859
  maxPreviewLength,
6722
6860
  seen,
@@ -6731,6 +6869,7 @@ function redactEventAttributes(attrs2, redactor, maxMetadataValueLength, maxPrev
6731
6869
  const seen = /* @__PURE__ */ new WeakSet();
6732
6870
  const bounded = boundAttributeValues(
6733
6871
  redacted,
6872
+ [...DEFAULT_REDACT_KEYS, ...redactor.sensitiveKeys],
6734
6873
  maxMetadataValueLength,
6735
6874
  maxPreviewLength,
6736
6875
  seen,
@@ -7184,6 +7323,11 @@ function exportOpenInference(tree, options) {
7184
7323
  function hexFrom2(seed, byteLen) {
7185
7324
  return crypto.createHash("sha256").update(seed, "utf8").digest("hex").slice(0, byteLen * 2);
7186
7325
  }
7326
+ function unixNano2(ms) {
7327
+ const whole = Math.trunc(ms);
7328
+ const fractionNs = Math.round((ms - whole) * 1e6);
7329
+ return BigInt(whole) * 1000000n + BigInt(fractionNs);
7330
+ }
7187
7331
  function stringAttr2(key, value) {
7188
7332
  return { key, value: { stringValue: value } };
7189
7333
  }
@@ -7217,10 +7361,10 @@ function exportOtlpJson(tree, options) {
7217
7361
  const ev = n.event;
7218
7362
  const spanId = hexFrom2(`${tree.runId}:${ev.eventId}`, 8);
7219
7363
  const parentSpanId = ev.parentId ? hexFrom2(`${tree.runId}:${ev.parentId}`, 8) : void 0;
7220
- const startNs = String(Math.round(ev.timestamp * 1e6));
7364
+ const startNs = String(unixNano2(ev.timestamp));
7221
7365
  let endNs;
7222
7366
  if (ev.durationMs !== void 0 && Number.isFinite(ev.durationMs)) {
7223
- endNs = String(Math.round(ev.timestamp * 1e6 + ev.durationMs * 1e6));
7367
+ endNs = String(unixNano2(ev.timestamp) + unixNano2(ev.durationMs));
7224
7368
  }
7225
7369
  const attrs2 = [
7226
7370
  stringAttr2("agent_inspect.kind", ev.kind),
@@ -7396,14 +7540,107 @@ function isCredentialSensitiveKey2(key, sensitiveKeys) {
7396
7540
  }
7397
7541
 
7398
7542
  // packages/redact/src/key-value-secret.ts
7399
- var KEY_VALUE_SECRET_PATTERN_SOURCE = String.raw`\b(?:api[_-]?key|internal[_-]?token|access[_-]?token|auth[_-]?token|password|secret|token)=([^\s"'\\]{8,})`;
7400
- var KEY_VALUE_SECRET_PATTERN = new RegExp(
7543
+ var KEY_VALUE_SECRET_PATTERN_SOURCE = String.raw`\b(?:api[_-]?key|internal[_-]?token|access[_-]?token|auth[_-]?token|password|secret|token)=(?!\[(?:REDACTED(?::[^\]]*)?|HASH:[0-9a-f]{8})\](?![^\s"'\\&#?;/]))([^\s"'\\]{8,})`;
7544
+ var KEY_VALUE_SECRET_PATTERN2 = new RegExp(
7401
7545
  KEY_VALUE_SECRET_PATTERN_SOURCE,
7402
7546
  "i"
7403
7547
  );
7404
7548
  function valueContainsKeyValueSecret(value) {
7405
- KEY_VALUE_SECRET_PATTERN.lastIndex = 0;
7406
- return KEY_VALUE_SECRET_PATTERN.test(value);
7549
+ KEY_VALUE_SECRET_PATTERN2.lastIndex = 0;
7550
+ return KEY_VALUE_SECRET_PATTERN2.test(value);
7551
+ }
7552
+
7553
+ // packages/redact/src/url-redaction.ts
7554
+ var HTTP_URL2 = /^https?:\/\/[^\s]+$/i;
7555
+ var UUID_SEGMENT2 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
7556
+ var HEX_SEGMENT2 = /^[0-9a-f]{12,}$/i;
7557
+ var NUMERIC_SEGMENT2 = /^\d{5,}$/;
7558
+ var OPAQUE_SEGMENT2 = /^[A-Za-z0-9_-]{20,}$/;
7559
+ function looksLikeHttpUrl2(value) {
7560
+ return HTTP_URL2.test(value.trim());
7561
+ }
7562
+ function parseHttpUrl2(value) {
7563
+ try {
7564
+ const url = new URL(value.trim());
7565
+ return url.protocol === "http:" || url.protocol === "https:" ? url : void 0;
7566
+ } catch {
7567
+ return void 0;
7568
+ }
7569
+ }
7570
+ function isIdentifierSegment2(segment) {
7571
+ if (UUID_SEGMENT2.test(segment)) return true;
7572
+ if (HEX_SEGMENT2.test(segment)) return true;
7573
+ if (NUMERIC_SEGMENT2.test(segment)) return true;
7574
+ return OPAQUE_SEGMENT2.test(segment) && /\d/.test(segment) && /[A-Za-z]/.test(segment);
7575
+ }
7576
+ function decodeParam2(value) {
7577
+ try {
7578
+ return decodeURIComponent(value.replace(/\+/g, " "));
7579
+ } catch {
7580
+ return value;
7581
+ }
7582
+ }
7583
+ function redactParams2(raw, options, replacement) {
7584
+ let redacted = false;
7585
+ const parts = raw.split("&").map((part) => {
7586
+ if (part.length === 0) return part;
7587
+ const separator = part.indexOf("=");
7588
+ if (separator < 0) return part;
7589
+ const rawKey = part.slice(0, separator);
7590
+ const rawValue = part.slice(separator + 1);
7591
+ const key = decodeParam2(rawKey);
7592
+ const value = decodeParam2(rawValue);
7593
+ if (isCredentialSensitiveKey2(key, options.sensitiveKeys) || options.isSensitiveValue(value)) {
7594
+ redacted = true;
7595
+ return `${rawKey}=${replacement}`;
7596
+ }
7597
+ return part;
7598
+ });
7599
+ return { value: parts.join("&"), redacted };
7600
+ }
7601
+ function redactUrlString2(value, options) {
7602
+ const unchanged = {
7603
+ value,
7604
+ credentialRedacted: false,
7605
+ identifiersMasked: false
7606
+ };
7607
+ if (!looksLikeHttpUrl2(value)) return unchanged;
7608
+ const url = parseHttpUrl2(value);
7609
+ if (!url) return unchanged;
7610
+ const replacement = options.replacement ?? "[REDACTED]";
7611
+ const idPlaceholder = options.idPlaceholder ?? "[id]";
7612
+ let credentialRedacted = url.username.length > 0 || url.password.length > 0;
7613
+ let identifiersMasked = false;
7614
+ let pathname = url.pathname;
7615
+ if (options.maskPathIds === true) {
7616
+ pathname = url.pathname.split("/").map((segment) => {
7617
+ if (segment.length === 0 || !isIdentifierSegment2(decodeParam2(segment))) {
7618
+ return segment;
7619
+ }
7620
+ identifiersMasked = true;
7621
+ return idPlaceholder;
7622
+ }).join("/");
7623
+ }
7624
+ let search = "";
7625
+ if (url.search.length > 1) {
7626
+ const params = redactParams2(url.search.slice(1), options, replacement);
7627
+ if (params.redacted) credentialRedacted = true;
7628
+ search = `?${params.value}`;
7629
+ } else if (url.search.length === 1) {
7630
+ search = "?";
7631
+ }
7632
+ let hash = url.hash;
7633
+ if (url.hash.includes("=")) {
7634
+ const params = redactParams2(url.hash.slice(1), options, replacement);
7635
+ if (params.redacted) credentialRedacted = true;
7636
+ hash = `#${params.value}`;
7637
+ }
7638
+ if (!credentialRedacted && !identifiersMasked) return unchanged;
7639
+ return {
7640
+ value: `${url.protocol}//${url.host}${pathname}${search}${hash}`,
7641
+ credentialRedacted,
7642
+ identifiersMasked
7643
+ };
7407
7644
  }
7408
7645
 
7409
7646
  // packages/redact/src/index.ts
@@ -7472,7 +7709,9 @@ function findCompiledKeyRule(key, rules) {
7472
7709
  const exact = toKey2(key);
7473
7710
  const direct = rules.find((candidate) => candidate.key === exact);
7474
7711
  if (direct) return direct;
7475
- return rules.find((candidate) => isCredentialSensitiveKey2(key, [candidate.key]));
7712
+ return rules.find(
7713
+ (candidate) => isCredentialSensitiveKey2(key, [candidate.raw ?? candidate.key])
7714
+ );
7476
7715
  }
7477
7716
  function stableHash2(value) {
7478
7717
  const hash = crypto.createHash("sha256").update(value, "utf8").digest("hex");
@@ -7671,7 +7910,7 @@ function compileRules2(rules, extraKeys) {
7671
7910
  const out = /* @__PURE__ */ new Map();
7672
7911
  const set = (rule) => {
7673
7912
  const key = toKey2(rule.key);
7674
- out.set(key, { ...rule, key });
7913
+ out.set(key, { ...rule, key, raw: rule.raw ?? rule.key });
7675
7914
  };
7676
7915
  for (const key of DEFAULT_REDACT_KEYS2) {
7677
7916
  set({ key, strategy: "full" });
@@ -7750,6 +7989,7 @@ function createRedactionProfile(profile = "local") {
7750
7989
  }
7751
7990
  var Redactor2 = class {
7752
7991
  #rules;
7992
+ #sensitiveKeys;
7753
7993
  #detectors;
7754
7994
  #profile;
7755
7995
  #replacement;
@@ -7762,6 +8002,7 @@ var Redactor2 = class {
7762
8002
  ...resolved.extraKeys,
7763
8003
  ...options?.extraKeys ?? []
7764
8004
  ]);
8005
+ this.#sensitiveKeys = this.#rules.map((rule) => rule.raw ?? rule.key);
7765
8006
  this.#detectors = [
7766
8007
  ...builtInDetectorsForProfile(this.#profile),
7767
8008
  ...options?.detectors ?? []
@@ -7795,6 +8036,14 @@ var Redactor2 = class {
7795
8036
  #recordFinding(state, finding) {
7796
8037
  if (this.#collectFindings) state.findings.push(finding);
7797
8038
  }
8039
+ #detect(value, key, path16) {
8040
+ for (const detector of this.#detectors) {
8041
+ for (const detection of detector.detect({ path: path16, key, value })) {
8042
+ if ((detection.action ?? "replace") !== "keep") return { detector, detection };
8043
+ }
8044
+ }
8045
+ return void 0;
8046
+ }
7798
8047
  #redactValue(value, key, path16, depth, state) {
7799
8048
  if (depth > this.#maxDepth) {
7800
8049
  this.#recordFinding(
@@ -7813,6 +8062,43 @@ var Redactor2 = class {
7813
8062
  return applyRule(rule, value, this.#replacement);
7814
8063
  }
7815
8064
  }
8065
+ if (typeof value === "string" && looksLikeHttpUrl2(value)) {
8066
+ const url = redactUrlString2(value, {
8067
+ sensitiveKeys: this.#sensitiveKeys,
8068
+ isSensitiveValue: (candidate) => this.#detect(candidate, key, path16) !== void 0,
8069
+ replacement: this.#replacement,
8070
+ maskPathIds: this.#profile !== "local"
8071
+ });
8072
+ if (url.value !== value) {
8073
+ this.#recordFinding(
8074
+ state,
8075
+ makeFinding(
8076
+ path16,
8077
+ url.credentialRedacted ? "value.urlCredential" : "value.urlIdentifier",
8078
+ "replace",
8079
+ "value",
8080
+ url.credentialRedacted ? "error" : "warning"
8081
+ )
8082
+ );
8083
+ const residual = this.#detect(
8084
+ url.value.split(this.#replacement).join(""),
8085
+ key,
8086
+ path16
8087
+ );
8088
+ if (residual === void 0) return url.value;
8089
+ this.#recordFinding(
8090
+ state,
8091
+ makeFinding(
8092
+ path16,
8093
+ residual.detector.id,
8094
+ "replace",
8095
+ residual.detection.matchKind ?? residual.detector.matchKind ?? "custom",
8096
+ residual.detection.severity ?? residual.detector.severity ?? "warning"
8097
+ )
8098
+ );
8099
+ return this.#replacement;
8100
+ }
8101
+ }
7816
8102
  for (const detector of this.#detectors) {
7817
8103
  const detections = detector.detect({ path: path16, key, value });
7818
8104
  for (const detection of detections) {
@@ -8745,5 +9031,5 @@ async function runReadOnlyMcpServer(options = {}) {
8745
9031
  }
8746
9032
 
8747
9033
  export { MCP_MAX_REQUEST_BYTES, MCP_PROTOCOL_VERSION, MCP_SERVER_INSTRUCTIONS, READ_ONLY_TOOLS, TRACE_DATA_UNTRUSTED_WARNING, callReadOnlyTool, createMcpServerContext, handleMcpProtocolLine, runReadOnlyMcpServer };
8748
- //# sourceMappingURL=chunk-YWFDV7U3.mjs.map
8749
- //# sourceMappingURL=chunk-YWFDV7U3.mjs.map
9034
+ //# sourceMappingURL=chunk-OGX7RS23.mjs.map
9035
+ //# sourceMappingURL=chunk-OGX7RS23.mjs.map