@agent-inspect/mcp-server 6.31.3 → 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,
@@ -7401,14 +7540,107 @@ function isCredentialSensitiveKey2(key, sensitiveKeys) {
7401
7540
  }
7402
7541
 
7403
7542
  // 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(
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(
7406
7545
  KEY_VALUE_SECRET_PATTERN_SOURCE,
7407
7546
  "i"
7408
7547
  );
7409
7548
  function valueContainsKeyValueSecret(value) {
7410
- KEY_VALUE_SECRET_PATTERN.lastIndex = 0;
7411
- 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
+ };
7412
7644
  }
7413
7645
 
7414
7646
  // packages/redact/src/index.ts
@@ -7477,7 +7709,9 @@ function findCompiledKeyRule(key, rules) {
7477
7709
  const exact = toKey2(key);
7478
7710
  const direct = rules.find((candidate) => candidate.key === exact);
7479
7711
  if (direct) return direct;
7480
- return rules.find((candidate) => isCredentialSensitiveKey2(key, [candidate.key]));
7712
+ return rules.find(
7713
+ (candidate) => isCredentialSensitiveKey2(key, [candidate.raw ?? candidate.key])
7714
+ );
7481
7715
  }
7482
7716
  function stableHash2(value) {
7483
7717
  const hash = crypto.createHash("sha256").update(value, "utf8").digest("hex");
@@ -7676,7 +7910,7 @@ function compileRules2(rules, extraKeys) {
7676
7910
  const out = /* @__PURE__ */ new Map();
7677
7911
  const set = (rule) => {
7678
7912
  const key = toKey2(rule.key);
7679
- out.set(key, { ...rule, key });
7913
+ out.set(key, { ...rule, key, raw: rule.raw ?? rule.key });
7680
7914
  };
7681
7915
  for (const key of DEFAULT_REDACT_KEYS2) {
7682
7916
  set({ key, strategy: "full" });
@@ -7755,6 +7989,7 @@ function createRedactionProfile(profile = "local") {
7755
7989
  }
7756
7990
  var Redactor2 = class {
7757
7991
  #rules;
7992
+ #sensitiveKeys;
7758
7993
  #detectors;
7759
7994
  #profile;
7760
7995
  #replacement;
@@ -7767,6 +8002,7 @@ var Redactor2 = class {
7767
8002
  ...resolved.extraKeys,
7768
8003
  ...options?.extraKeys ?? []
7769
8004
  ]);
8005
+ this.#sensitiveKeys = this.#rules.map((rule) => rule.raw ?? rule.key);
7770
8006
  this.#detectors = [
7771
8007
  ...builtInDetectorsForProfile(this.#profile),
7772
8008
  ...options?.detectors ?? []
@@ -7800,6 +8036,14 @@ var Redactor2 = class {
7800
8036
  #recordFinding(state, finding) {
7801
8037
  if (this.#collectFindings) state.findings.push(finding);
7802
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
+ }
7803
8047
  #redactValue(value, key, path16, depth, state) {
7804
8048
  if (depth > this.#maxDepth) {
7805
8049
  this.#recordFinding(
@@ -7818,6 +8062,43 @@ var Redactor2 = class {
7818
8062
  return applyRule(rule, value, this.#replacement);
7819
8063
  }
7820
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
+ }
7821
8102
  for (const detector of this.#detectors) {
7822
8103
  const detections = detector.detect({ path: path16, key, value });
7823
8104
  for (const detection of detections) {
@@ -8750,5 +9031,5 @@ async function runReadOnlyMcpServer(options = {}) {
8750
9031
  }
8751
9032
 
8752
9033
  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
9034
+ //# sourceMappingURL=chunk-OGX7RS23.mjs.map
9035
+ //# sourceMappingURL=chunk-OGX7RS23.mjs.map