@agent-inspect/mcp-server 6.31.3 → 6.31.5

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,124 @@ 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 CONNECTION_PROTOCOLS = /* @__PURE__ */ new Set([
6698
+ "postgres:",
6699
+ "postgresql:",
6700
+ "mysql:",
6701
+ "mariadb:",
6702
+ "mongodb:",
6703
+ "mongodb+srv:",
6704
+ "redis:",
6705
+ "rediss:",
6706
+ "amqp:",
6707
+ "amqps:"
6708
+ ]);
6709
+ var CONNECTION_URI = /^(?:postgres|postgresql|mysql|mariadb|mongodb(?:\+srv)?|rediss?|amqps?):\/\/[^\s]+$/i;
6710
+ var UUID_SEGMENT = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
6711
+ var HEX_SEGMENT = /^[0-9a-f]{12,}$/i;
6712
+ var NUMERIC_SEGMENT = /^\d{5,}$/;
6713
+ var OPAQUE_SEGMENT = /^[A-Za-z0-9_-]{20,}$/;
6714
+ function looksLikeHttpUrl(value) {
6715
+ return HTTP_URL.test(value.trim());
6716
+ }
6717
+ function looksLikeConnectionUri(value) {
6718
+ return CONNECTION_URI.test(value.trim());
6719
+ }
6720
+ function looksLikeUrlForRedaction(value) {
6721
+ return looksLikeHttpUrl(value) || looksLikeConnectionUri(value);
6722
+ }
6723
+ function parseRedactableUrl(value) {
6724
+ try {
6725
+ const url = new URL(value.trim());
6726
+ if (url.protocol === "http:" || url.protocol === "https:") return url;
6727
+ if (CONNECTION_PROTOCOLS.has(url.protocol)) return url;
6728
+ return void 0;
6729
+ } catch {
6730
+ return void 0;
6731
+ }
6732
+ }
6733
+ function isHttpProtocol(protocol) {
6734
+ return protocol === "http:" || protocol === "https:";
6735
+ }
6736
+ function isIdentifierSegment(segment) {
6737
+ if (UUID_SEGMENT.test(segment)) return true;
6738
+ if (HEX_SEGMENT.test(segment)) return true;
6739
+ if (NUMERIC_SEGMENT.test(segment)) return true;
6740
+ return OPAQUE_SEGMENT.test(segment) && /\d/.test(segment) && /[A-Za-z]/.test(segment);
6741
+ }
6742
+ function decodeParam(value) {
6743
+ try {
6744
+ return decodeURIComponent(value.replace(/\+/g, " "));
6745
+ } catch {
6746
+ return value;
6747
+ }
6748
+ }
6749
+ function redactParams(raw, options, replacement) {
6750
+ let redacted = false;
6751
+ const parts = raw.split("&").map((part) => {
6752
+ if (part.length === 0) return part;
6753
+ const separator = part.indexOf("=");
6754
+ if (separator < 0) return part;
6755
+ const rawKey = part.slice(0, separator);
6756
+ const rawValue = part.slice(separator + 1);
6757
+ const key = decodeParam(rawKey);
6758
+ const value = decodeParam(rawValue);
6759
+ if (isCredentialSensitiveKey(key, options.sensitiveKeys) || options.isSensitiveValue(value)) {
6760
+ redacted = true;
6761
+ return `${rawKey}=${replacement}`;
6762
+ }
6763
+ return part;
6764
+ });
6765
+ return { value: parts.join("&"), redacted };
6766
+ }
6767
+ function redactUrlString(value, options) {
6768
+ const unchanged = {
6769
+ value,
6770
+ credentialRedacted: false,
6771
+ identifiersMasked: false
6772
+ };
6773
+ if (!looksLikeUrlForRedaction(value)) return unchanged;
6774
+ const url = parseRedactableUrl(value);
6775
+ if (!url) return unchanged;
6776
+ const replacement = options.replacement ?? "[REDACTED]";
6777
+ const idPlaceholder = options.idPlaceholder ?? "[id]";
6778
+ const http = isHttpProtocol(url.protocol);
6779
+ let credentialRedacted = url.username.length > 0 || url.password.length > 0;
6780
+ let identifiersMasked = false;
6781
+ let pathname = url.pathname;
6782
+ if (http && options.maskPathIds === true) {
6783
+ pathname = url.pathname.split("/").map((segment) => {
6784
+ if (segment.length === 0 || !isIdentifierSegment(decodeParam(segment))) {
6785
+ return segment;
6786
+ }
6787
+ identifiersMasked = true;
6788
+ return idPlaceholder;
6789
+ }).join("/");
6790
+ }
6791
+ let search = "";
6792
+ if (url.search.length > 1) {
6793
+ const params = redactParams(url.search.slice(1), options, replacement);
6794
+ if (params.redacted) credentialRedacted = true;
6795
+ search = `?${params.value}`;
6796
+ } else if (url.search.length === 1) {
6797
+ search = "?";
6798
+ }
6799
+ let hash = url.hash;
6800
+ if (url.hash.includes("=")) {
6801
+ const params = redactParams(url.hash.slice(1), options, replacement);
6802
+ if (params.redacted) credentialRedacted = true;
6803
+ hash = `#${params.value}`;
6804
+ }
6805
+ if (!credentialRedacted && !identifiersMasked) return unchanged;
6806
+ return {
6807
+ value: `${url.protocol}//${url.host}${pathname}${search}${hash}`,
6808
+ credentialRedacted,
6809
+ identifiersMasked
6810
+ };
6811
+ }
6812
+
6665
6813
  // packages/core/src/exporters/redact-export.ts
6666
6814
  function isRecord11(value) {
6667
6815
  return typeof value === "object" && value !== null && !Array.isArray(value);
@@ -6679,21 +6827,34 @@ function deepClone(value) {
6679
6827
  }
6680
6828
  return out;
6681
6829
  }
6682
- function boundAttributeValues(record, maxMetadataValueLength, maxPreviewLength, seen, depth) {
6830
+ function boundAttributeValues(record, sensitiveKeys, maxMetadataValueLength, maxPreviewLength, seen, depth) {
6683
6831
  if (depth > 32) {
6684
6832
  return { truncated: true, reason: "maxDepth" };
6685
6833
  }
6686
6834
  const out = {};
6687
6835
  for (const [key, value] of Object.entries(record)) {
6688
- out[key] = boundValue(value, key, maxMetadataValueLength, maxPreviewLength, seen, depth);
6836
+ out[key] = boundValue(
6837
+ value,
6838
+ key,
6839
+ sensitiveKeys,
6840
+ maxMetadataValueLength,
6841
+ maxPreviewLength,
6842
+ seen,
6843
+ depth
6844
+ );
6689
6845
  }
6690
6846
  return out;
6691
6847
  }
6692
- function boundValue(value, key, maxMetadataValueLength, maxPreviewLength, seen, depth) {
6848
+ function boundValue(value, key, sensitiveKeys, maxMetadataValueLength, maxPreviewLength, seen, depth) {
6693
6849
  if (value === null || typeof value !== "object") {
6694
6850
  if (typeof value === "string") {
6851
+ const rewritten = redactUrlString(value, {
6852
+ sensitiveKeys,
6853
+ isSensitiveValue: stringContainsHighConfidenceCredential,
6854
+ maskPathIds: true
6855
+ }).value;
6695
6856
  return truncateStringForProfile(
6696
- value,
6857
+ stringContainsHighConfidenceCredential(rewritten) ? "[REDACTED]" : rewritten,
6697
6858
  key,
6698
6859
  maxMetadataValueLength,
6699
6860
  maxPreviewLength
@@ -6708,6 +6869,7 @@ function boundValue(value, key, maxMetadataValueLength, maxPreviewLength, seen,
6708
6869
  (item, index) => boundValue(
6709
6870
  item,
6710
6871
  String(index),
6872
+ sensitiveKeys,
6711
6873
  maxMetadataValueLength,
6712
6874
  maxPreviewLength,
6713
6875
  seen,
@@ -6717,6 +6879,7 @@ function boundValue(value, key, maxMetadataValueLength, maxPreviewLength, seen,
6717
6879
  }
6718
6880
  return boundAttributeValues(
6719
6881
  value,
6882
+ sensitiveKeys,
6720
6883
  maxMetadataValueLength,
6721
6884
  maxPreviewLength,
6722
6885
  seen,
@@ -6731,6 +6894,7 @@ function redactEventAttributes(attrs2, redactor, maxMetadataValueLength, maxPrev
6731
6894
  const seen = /* @__PURE__ */ new WeakSet();
6732
6895
  const bounded = boundAttributeValues(
6733
6896
  redacted,
6897
+ [...DEFAULT_REDACT_KEYS, ...redactor.sensitiveKeys],
6734
6898
  maxMetadataValueLength,
6735
6899
  maxPreviewLength,
6736
6900
  seen,
@@ -7401,14 +7565,132 @@ function isCredentialSensitiveKey2(key, sensitiveKeys) {
7401
7565
  }
7402
7566
 
7403
7567
  // packages/redact/src/key-value-secret.ts
7404
- var KEY_VALUE_SECRET_PATTERN_SOURCE = String.raw`\b(?:api[_-]?key|internal[_-]?token|access[_-]?token|auth[_-]?token|password|secret|token)=([^\s"'\\]{8,})`;
7405
- var KEY_VALUE_SECRET_PATTERN = new RegExp(
7568
+ 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,})`;
7569
+ var KEY_VALUE_SECRET_PATTERN2 = new RegExp(
7406
7570
  KEY_VALUE_SECRET_PATTERN_SOURCE,
7407
7571
  "i"
7408
7572
  );
7409
7573
  function valueContainsKeyValueSecret(value) {
7410
- KEY_VALUE_SECRET_PATTERN.lastIndex = 0;
7411
- return KEY_VALUE_SECRET_PATTERN.test(value);
7574
+ KEY_VALUE_SECRET_PATTERN2.lastIndex = 0;
7575
+ return KEY_VALUE_SECRET_PATTERN2.test(value);
7576
+ }
7577
+
7578
+ // packages/redact/src/url-redaction.ts
7579
+ var HTTP_URL2 = /^https?:\/\/[^\s]+$/i;
7580
+ var CONNECTION_PROTOCOLS2 = /* @__PURE__ */ new Set([
7581
+ "postgres:",
7582
+ "postgresql:",
7583
+ "mysql:",
7584
+ "mariadb:",
7585
+ "mongodb:",
7586
+ "mongodb+srv:",
7587
+ "redis:",
7588
+ "rediss:",
7589
+ "amqp:",
7590
+ "amqps:"
7591
+ ]);
7592
+ var CONNECTION_URI2 = /^(?:postgres|postgresql|mysql|mariadb|mongodb(?:\+srv)?|rediss?|amqps?):\/\/[^\s]+$/i;
7593
+ var UUID_SEGMENT2 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
7594
+ var HEX_SEGMENT2 = /^[0-9a-f]{12,}$/i;
7595
+ var NUMERIC_SEGMENT2 = /^\d{5,}$/;
7596
+ var OPAQUE_SEGMENT2 = /^[A-Za-z0-9_-]{20,}$/;
7597
+ function looksLikeHttpUrl2(value) {
7598
+ return HTTP_URL2.test(value.trim());
7599
+ }
7600
+ function looksLikeConnectionUri2(value) {
7601
+ return CONNECTION_URI2.test(value.trim());
7602
+ }
7603
+ function looksLikeUrlForRedaction2(value) {
7604
+ return looksLikeHttpUrl2(value) || looksLikeConnectionUri2(value);
7605
+ }
7606
+ function parseRedactableUrl2(value) {
7607
+ try {
7608
+ const url = new URL(value.trim());
7609
+ if (url.protocol === "http:" || url.protocol === "https:") return url;
7610
+ if (CONNECTION_PROTOCOLS2.has(url.protocol)) return url;
7611
+ return void 0;
7612
+ } catch {
7613
+ return void 0;
7614
+ }
7615
+ }
7616
+ function isHttpProtocol2(protocol) {
7617
+ return protocol === "http:" || protocol === "https:";
7618
+ }
7619
+ function isIdentifierSegment2(segment) {
7620
+ if (UUID_SEGMENT2.test(segment)) return true;
7621
+ if (HEX_SEGMENT2.test(segment)) return true;
7622
+ if (NUMERIC_SEGMENT2.test(segment)) return true;
7623
+ return OPAQUE_SEGMENT2.test(segment) && /\d/.test(segment) && /[A-Za-z]/.test(segment);
7624
+ }
7625
+ function decodeParam2(value) {
7626
+ try {
7627
+ return decodeURIComponent(value.replace(/\+/g, " "));
7628
+ } catch {
7629
+ return value;
7630
+ }
7631
+ }
7632
+ function redactParams2(raw, options, replacement) {
7633
+ let redacted = false;
7634
+ const parts = raw.split("&").map((part) => {
7635
+ if (part.length === 0) return part;
7636
+ const separator = part.indexOf("=");
7637
+ if (separator < 0) return part;
7638
+ const rawKey = part.slice(0, separator);
7639
+ const rawValue = part.slice(separator + 1);
7640
+ const key = decodeParam2(rawKey);
7641
+ const value = decodeParam2(rawValue);
7642
+ if (isCredentialSensitiveKey2(key, options.sensitiveKeys) || options.isSensitiveValue(value)) {
7643
+ redacted = true;
7644
+ return `${rawKey}=${replacement}`;
7645
+ }
7646
+ return part;
7647
+ });
7648
+ return { value: parts.join("&"), redacted };
7649
+ }
7650
+ function redactUrlString2(value, options) {
7651
+ const unchanged = {
7652
+ value,
7653
+ credentialRedacted: false,
7654
+ identifiersMasked: false
7655
+ };
7656
+ if (!looksLikeUrlForRedaction2(value)) return unchanged;
7657
+ const url = parseRedactableUrl2(value);
7658
+ if (!url) return unchanged;
7659
+ const replacement = options.replacement ?? "[REDACTED]";
7660
+ const idPlaceholder = options.idPlaceholder ?? "[id]";
7661
+ const http = isHttpProtocol2(url.protocol);
7662
+ let credentialRedacted = url.username.length > 0 || url.password.length > 0;
7663
+ let identifiersMasked = false;
7664
+ let pathname = url.pathname;
7665
+ if (http && options.maskPathIds === true) {
7666
+ pathname = url.pathname.split("/").map((segment) => {
7667
+ if (segment.length === 0 || !isIdentifierSegment2(decodeParam2(segment))) {
7668
+ return segment;
7669
+ }
7670
+ identifiersMasked = true;
7671
+ return idPlaceholder;
7672
+ }).join("/");
7673
+ }
7674
+ let search = "";
7675
+ if (url.search.length > 1) {
7676
+ const params = redactParams2(url.search.slice(1), options, replacement);
7677
+ if (params.redacted) credentialRedacted = true;
7678
+ search = `?${params.value}`;
7679
+ } else if (url.search.length === 1) {
7680
+ search = "?";
7681
+ }
7682
+ let hash = url.hash;
7683
+ if (url.hash.includes("=")) {
7684
+ const params = redactParams2(url.hash.slice(1), options, replacement);
7685
+ if (params.redacted) credentialRedacted = true;
7686
+ hash = `#${params.value}`;
7687
+ }
7688
+ if (!credentialRedacted && !identifiersMasked) return unchanged;
7689
+ return {
7690
+ value: `${url.protocol}//${url.host}${pathname}${search}${hash}`,
7691
+ credentialRedacted,
7692
+ identifiersMasked
7693
+ };
7412
7694
  }
7413
7695
 
7414
7696
  // packages/redact/src/index.ts
@@ -7477,7 +7759,9 @@ function findCompiledKeyRule(key, rules) {
7477
7759
  const exact = toKey2(key);
7478
7760
  const direct = rules.find((candidate) => candidate.key === exact);
7479
7761
  if (direct) return direct;
7480
- return rules.find((candidate) => isCredentialSensitiveKey2(key, [candidate.key]));
7762
+ return rules.find(
7763
+ (candidate) => isCredentialSensitiveKey2(key, [candidate.raw ?? candidate.key])
7764
+ );
7481
7765
  }
7482
7766
  function stableHash2(value) {
7483
7767
  const hash = crypto.createHash("sha256").update(value, "utf8").digest("hex");
@@ -7676,7 +7960,7 @@ function compileRules2(rules, extraKeys) {
7676
7960
  const out = /* @__PURE__ */ new Map();
7677
7961
  const set = (rule) => {
7678
7962
  const key = toKey2(rule.key);
7679
- out.set(key, { ...rule, key });
7963
+ out.set(key, { ...rule, key, raw: rule.raw ?? rule.key });
7680
7964
  };
7681
7965
  for (const key of DEFAULT_REDACT_KEYS2) {
7682
7966
  set({ key, strategy: "full" });
@@ -7755,6 +8039,7 @@ function createRedactionProfile(profile = "local") {
7755
8039
  }
7756
8040
  var Redactor2 = class {
7757
8041
  #rules;
8042
+ #sensitiveKeys;
7758
8043
  #detectors;
7759
8044
  #profile;
7760
8045
  #replacement;
@@ -7767,6 +8052,7 @@ var Redactor2 = class {
7767
8052
  ...resolved.extraKeys,
7768
8053
  ...options?.extraKeys ?? []
7769
8054
  ]);
8055
+ this.#sensitiveKeys = this.#rules.map((rule) => rule.raw ?? rule.key);
7770
8056
  this.#detectors = [
7771
8057
  ...builtInDetectorsForProfile(this.#profile),
7772
8058
  ...options?.detectors ?? []
@@ -7800,6 +8086,14 @@ var Redactor2 = class {
7800
8086
  #recordFinding(state, finding) {
7801
8087
  if (this.#collectFindings) state.findings.push(finding);
7802
8088
  }
8089
+ #detect(value, key, path16) {
8090
+ for (const detector of this.#detectors) {
8091
+ for (const detection of detector.detect({ path: path16, key, value })) {
8092
+ if ((detection.action ?? "replace") !== "keep") return { detector, detection };
8093
+ }
8094
+ }
8095
+ return void 0;
8096
+ }
7803
8097
  #redactValue(value, key, path16, depth, state) {
7804
8098
  if (depth > this.#maxDepth) {
7805
8099
  this.#recordFinding(
@@ -7818,6 +8112,43 @@ var Redactor2 = class {
7818
8112
  return applyRule(rule, value, this.#replacement);
7819
8113
  }
7820
8114
  }
8115
+ if (typeof value === "string" && looksLikeUrlForRedaction2(value)) {
8116
+ const url = redactUrlString2(value, {
8117
+ sensitiveKeys: this.#sensitiveKeys,
8118
+ isSensitiveValue: (candidate) => this.#detect(candidate, key, path16) !== void 0,
8119
+ replacement: this.#replacement,
8120
+ maskPathIds: this.#profile !== "local"
8121
+ });
8122
+ if (url.value !== value) {
8123
+ this.#recordFinding(
8124
+ state,
8125
+ makeFinding(
8126
+ path16,
8127
+ url.credentialRedacted ? "value.urlCredential" : "value.urlIdentifier",
8128
+ "replace",
8129
+ "value",
8130
+ url.credentialRedacted ? "error" : "warning"
8131
+ )
8132
+ );
8133
+ const residual = this.#detect(
8134
+ url.value.split(this.#replacement).join(""),
8135
+ key,
8136
+ path16
8137
+ );
8138
+ if (residual === void 0) return url.value;
8139
+ this.#recordFinding(
8140
+ state,
8141
+ makeFinding(
8142
+ path16,
8143
+ residual.detector.id,
8144
+ "replace",
8145
+ residual.detection.matchKind ?? residual.detector.matchKind ?? "custom",
8146
+ residual.detection.severity ?? residual.detector.severity ?? "warning"
8147
+ )
8148
+ );
8149
+ return this.#replacement;
8150
+ }
8151
+ }
7821
8152
  for (const detector of this.#detectors) {
7822
8153
  const detections = detector.detect({ path: path16, key, value });
7823
8154
  for (const detection of detections) {
@@ -8750,5 +9081,5 @@ async function runReadOnlyMcpServer(options = {}) {
8750
9081
  }
8751
9082
 
8752
9083
  export { MCP_MAX_REQUEST_BYTES, MCP_PROTOCOL_VERSION, MCP_SERVER_INSTRUCTIONS, READ_ONLY_TOOLS, TRACE_DATA_UNTRUSTED_WARNING, callReadOnlyTool, createMcpServerContext, handleMcpProtocolLine, runReadOnlyMcpServer };
8753
- //# sourceMappingURL=chunk-P2HSJJKS.mjs.map
8754
- //# sourceMappingURL=chunk-P2HSJJKS.mjs.map
9084
+ //# sourceMappingURL=chunk-FUNPQPC3.mjs.map
9085
+ //# sourceMappingURL=chunk-FUNPQPC3.mjs.map