@veryfront/ext-content-mdx 0.1.1190 → 0.1.1192

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.
Files changed (66) hide show
  1. package/esm/deno.d.ts +6 -0
  2. package/esm/deno.js +13 -7
  3. package/esm/src/errors/error-registry/module.d.ts +4 -0
  4. package/esm/src/errors/error-registry/module.d.ts.map +1 -1
  5. package/esm/src/errors/error-registry/module.js +16 -0
  6. package/esm/src/errors/error-registry.d.ts +2 -0
  7. package/esm/src/errors/error-registry.d.ts.map +1 -1
  8. package/esm/src/errors/types.d.ts.map +1 -1
  9. package/esm/src/errors/types.js +5 -2
  10. package/esm/src/platform/compat/error-introspection.d.ts +12 -1
  11. package/esm/src/platform/compat/error-introspection.d.ts.map +1 -1
  12. package/esm/src/platform/compat/error-introspection.js +40 -5
  13. package/esm/src/platform/compat/path/basic-operations.d.ts.map +1 -1
  14. package/esm/src/platform/compat/path/basic-operations.js +6 -2
  15. package/esm/src/platform/compat/path/portable.d.ts.map +1 -1
  16. package/esm/src/platform/compat/path/portable.js +98 -54
  17. package/esm/src/platform/compat/path/resolution.d.ts.map +1 -1
  18. package/esm/src/platform/compat/path/resolution.js +14 -5
  19. package/esm/src/platform/compat/primordials/array.d.ts +8 -0
  20. package/esm/src/platform/compat/primordials/array.d.ts.map +1 -0
  21. package/esm/src/platform/compat/primordials/array.js +33 -0
  22. package/esm/src/transforms/import-rewriter/url-builder.d.ts +6 -0
  23. package/esm/src/transforms/import-rewriter/url-builder.d.ts.map +1 -1
  24. package/esm/src/transforms/import-rewriter/url-builder.js +96 -29
  25. package/esm/src/utils/base64url.d.ts.map +1 -1
  26. package/esm/src/utils/base64url.js +10 -4
  27. package/esm/src/utils/bundle-manifest.d.ts +19 -1
  28. package/esm/src/utils/bundle-manifest.d.ts.map +1 -1
  29. package/esm/src/utils/bundle-manifest.js +124 -27
  30. package/esm/src/utils/constants/limits.d.ts +1 -1
  31. package/esm/src/utils/constants/limits.d.ts.map +1 -1
  32. package/esm/src/utils/constants/limits.js +1 -1
  33. package/esm/src/utils/hash-utils.d.ts.map +1 -1
  34. package/esm/src/utils/hash-utils.js +32 -6
  35. package/esm/src/utils/id.d.ts +16 -0
  36. package/esm/src/utils/id.d.ts.map +1 -0
  37. package/esm/src/utils/id.js +69 -0
  38. package/esm/src/utils/import-lockfile.d.ts +52 -1
  39. package/esm/src/utils/import-lockfile.d.ts.map +1 -1
  40. package/esm/src/utils/import-lockfile.js +1042 -58
  41. package/esm/src/utils/index.d.ts +1 -1
  42. package/esm/src/utils/index.d.ts.map +1 -1
  43. package/esm/src/utils/index.js +1 -1
  44. package/esm/src/utils/logger/logger.d.ts +1 -0
  45. package/esm/src/utils/logger/logger.d.ts.map +1 -1
  46. package/esm/src/utils/logger/logger.js +359 -78
  47. package/esm/src/utils/logger/redact.d.ts +4 -2
  48. package/esm/src/utils/logger/redact.d.ts.map +1 -1
  49. package/esm/src/utils/logger/redact.js +300 -112
  50. package/esm/src/utils/logger/serialization.d.ts +3 -0
  51. package/esm/src/utils/logger/serialization.d.ts.map +1 -0
  52. package/esm/src/utils/logger/serialization.js +81 -0
  53. package/esm/src/utils/memoize.d.ts +1 -1
  54. package/esm/src/utils/memoize.d.ts.map +1 -1
  55. package/esm/src/utils/memoize.js +16 -5
  56. package/esm/src/utils/path-utils.d.ts.map +1 -1
  57. package/esm/src/utils/path-utils.js +4 -2
  58. package/esm/src/utils/sleep.d.ts +8 -0
  59. package/esm/src/utils/sleep.d.ts.map +1 -1
  60. package/esm/src/utils/sleep.js +11 -2
  61. package/esm/src/utils/timer.d.ts +10 -0
  62. package/esm/src/utils/timer.d.ts.map +1 -0
  63. package/esm/src/utils/timer.js +19 -0
  64. package/esm/src/utils/version-constant.d.ts +1 -1
  65. package/esm/src/utils/version-constant.js +1 -1
  66. package/package.json +2 -2
@@ -19,17 +19,85 @@
19
19
  /** Replacement value substituted for any sensitive field. */
20
20
  export const REDACTED = "[REDACTED]";
21
21
  const apply = Reflect.apply;
22
+ const arrayPop = Array.prototype.pop;
23
+ const arrayPush = Array.prototype.push;
22
24
  const NativeUint32Array = Uint32Array;
25
+ const arrayIsArray = Array.isArray;
26
+ const arrayPrototype = Array.prototype;
27
+ const bigIntToString = BigInt.prototype.toString;
28
+ const NativeMap = Map;
29
+ const mapDelete = Map.prototype.delete;
30
+ const mapGet = Map.prototype.get;
31
+ const mapKeys = Map.prototype.keys;
32
+ const mapSet = Map.prototype.set;
33
+ const objectDefineProperty = Object.defineProperty;
34
+ const objectGetOwnPropertyDescriptor = Object.getOwnPropertyDescriptor;
35
+ const objectGetPrototypeOf = Object.getPrototypeOf;
36
+ const objectHasOwn = Object.hasOwn;
37
+ const objectPrototype = Object.prototype;
38
+ const NativeSet = Set;
39
+ const nativeDecodeURIComponent = decodeURIComponent;
40
+ const NativeURL = URL;
41
+ const numberIsFinite = Number.isFinite;
42
+ const numberIsInteger = Number.isInteger;
23
43
  const regExpExec = RegExp.prototype.exec;
24
- const regExpReplace = RegExp.prototype[Symbol.replace];
44
+ const regExpGlobalGetter = objectGetOwnPropertyDescriptor(RegExp.prototype, "global").get;
45
+ const regExpUnicodeGetter = objectGetOwnPropertyDescriptor(RegExp.prototype, "unicode").get;
25
46
  const stringCharCodeAt = String.prototype.charCodeAt;
47
+ const stringIncludes = String.prototype.includes;
48
+ const stringIndexOf = String.prototype.indexOf;
26
49
  const stringSlice = String.prototype.slice;
50
+ const stringStartsWith = String.prototype.startsWith;
27
51
  const stringToLowerCase = String.prototype.toLowerCase;
52
+ const setAdd = Set.prototype.add;
53
+ const setDelete = Set.prototype.delete;
54
+ const setHas = Set.prototype.has;
55
+ const mapIteratorNext = objectGetPrototypeOf(new NativeMap().keys()).next;
56
+ const mapSizeGetter = objectGetOwnPropertyDescriptor(Map.prototype, "size").get;
57
+ const urlHostGetter = objectGetOwnPropertyDescriptor(NativeURL.prototype, "host").get;
58
+ const urlOriginGetter = objectGetOwnPropertyDescriptor(NativeURL.prototype, "origin").get;
59
+ const urlPasswordGetter = objectGetOwnPropertyDescriptor(NativeURL.prototype, "password").get;
60
+ const urlPathnameGetter = objectGetOwnPropertyDescriptor(NativeURL.prototype, "pathname").get;
61
+ const urlProtocolGetter = objectGetOwnPropertyDescriptor(NativeURL.prototype, "protocol").get;
62
+ const urlUsernameGetter = objectGetOwnPropertyDescriptor(NativeURL.prototype, "username").get;
28
63
  const NON_ALPHANUMERIC_PATTERN = /[^a-z0-9]/g;
64
+ const CAMEL_CASE_BOUNDARY_PATTERN = /([a-z0-9])([A-Z])/g;
65
+ const ACRONYM_BOUNDARY_PATTERN = /([A-Z])([A-Z][a-z])/g;
66
+ const PROVIDER_CREDENTIAL_PATTERN = /\b(?:sk-[A-Za-z0-9._-]{8,}|gh[po]_[A-Za-z0-9._-]{8,}|xox[baprs]-[A-Za-z0-9._-]{8,}|eyJ[A-Za-z0-9._-]{8,})\b/g;
67
+ function replaceWithCapturedExec(input, pattern, replacement) {
68
+ const global = apply(regExpGlobalGetter, pattern, []);
69
+ const unicode = apply(regExpUnicodeGetter, pattern, []);
70
+ let cursor = 0;
71
+ let matched = false;
72
+ let result = "";
73
+ pattern.lastIndex = 0;
74
+ try {
75
+ while (true) {
76
+ const match = apply(regExpExec, pattern, [input]);
77
+ if (match === null)
78
+ break;
79
+ const text = match[0];
80
+ const start = match.index;
81
+ result += sliceString(input, cursor, start);
82
+ result += typeof replacement === "string" ? replacement : replacement(match);
83
+ cursor = start + text.length;
84
+ matched = true;
85
+ if (!global)
86
+ break;
87
+ if (text.length === 0) {
88
+ pattern.lastIndex = advanceStringIndex(input, start, unicode);
89
+ }
90
+ }
91
+ }
92
+ finally {
93
+ pattern.lastIndex = 0;
94
+ }
95
+ return matched ? result + sliceString(input, cursor) : input;
96
+ }
29
97
  /** Strip all non-alphanumeric characters and lowercase, used for key normalization. */
30
98
  function normalizeToAlphanumeric(s) {
31
99
  const lowercase = apply(stringToLowerCase, s, []);
32
- return apply(regExpReplace, NON_ALPHANUMERIC_PATTERN, [lowercase, ""]);
100
+ return replaceWithCapturedExec(lowercase, NON_ALPHANUMERIC_PATTERN, "");
33
101
  }
34
102
  const FORWARD_SLASH_CODE_UNIT = 47;
35
103
  const BACKSLASH_CODE_UNIT = 92;
@@ -40,6 +108,16 @@ const ASCII_LOWERCASE_OFFSET = 32;
40
108
  function stringCodeUnitAt(value, index) {
41
109
  return apply(stringCharCodeAt, value, [index]);
42
110
  }
111
+ function advanceStringIndex(value, index, unicode) {
112
+ const next = index + 1;
113
+ if (!unicode || next >= value.length)
114
+ return next;
115
+ const first = stringCodeUnitAt(value, index);
116
+ if (first < 0xd800 || first > 0xdbff)
117
+ return next;
118
+ const second = stringCodeUnitAt(value, next);
119
+ return second >= 0xdc00 && second <= 0xdfff ? index + 2 : next;
120
+ }
43
121
  function sliceString(value, start, end) {
44
122
  return end === undefined
45
123
  ? apply(stringSlice, value, [start])
@@ -55,6 +133,22 @@ function isAsciiLetterCodeUnit(codeUnit) {
55
133
  : codeUnit;
56
134
  return lowercase >= 97 && lowercase <= 122;
57
135
  }
136
+ function splitIdentifierTokens(value) {
137
+ const tokens = [];
138
+ let tokenStart = 0;
139
+ for (let index = 0; index <= value.length; index++) {
140
+ const codeUnit = index === value.length ? -1 : stringCodeUnitAt(value, index);
141
+ const isIdentifierCodeUnit = (codeUnit >= 97 && codeUnit <= 122) ||
142
+ (codeUnit >= 48 && codeUnit <= 57);
143
+ if (isIdentifierCodeUnit)
144
+ continue;
145
+ if (index > tokenStart) {
146
+ tokens[tokens.length] = sliceString(value, tokenStart, index);
147
+ }
148
+ tokenStart = index + 1;
149
+ }
150
+ return tokens;
151
+ }
58
152
  function isWindowsPath(path) {
59
153
  if (path.length >= 2) {
60
154
  const first = stringCodeUnitAt(path, 0);
@@ -133,10 +227,9 @@ export function redactPathFromText(input, path, replacement) {
133
227
  * a lowercased, non-alphanumeric-stripped form of the key, so `API-Key`,
134
228
  * `api_key`, and `apiKey` all collapse to `apikey` and match.
135
229
  *
136
- * Deliberately omitted to avoid false positives that swamp real logs:
137
- * - bare `"auth"` (would mask `author`); `authorization`/`authToken` are still
138
- * covered via `authorization`/`token`.
139
- * - short tokens like `"dsn"`/`"sas"` (would mask `feedsNamespace`, etc.).
230
+ * Bare `"auth"` is matched separately as an exact normalized key so `author`
231
+ * remains visible. Short tokens like `"dsn"`/`"sas"` are deliberately omitted
232
+ * to avoid masking keys such as `feedsNamespace`.
140
233
  */
141
234
  const SENSITIVE_KEY_PATTERNS = [
142
235
  "password",
@@ -150,6 +243,7 @@ const SENSITIVE_KEY_PATTERNS = [
150
243
  "accesskey",
151
244
  "privatekey",
152
245
  "credential",
246
+ "authheader",
153
247
  "authorization",
154
248
  "cookie",
155
249
  "bearer",
@@ -168,13 +262,17 @@ const SENSITIVE_KEY_PATTERNS = [
168
262
  const SENSITIVE_KEY_CACHE_MAX_SIZE = 512;
169
263
  /** Avoid retaining attacker-controlled, oversized property names in the cache. */
170
264
  const SENSITIVE_KEY_CACHE_MAX_KEY_LENGTH = 128;
171
- const sensitiveKeyCache = new Map();
265
+ const sensitiveKeyCache = new NativeMap();
172
266
  /** Stop traversing past this depth to keep the pass cheap and stack-safe. */
173
267
  const MAX_DEPTH = 16;
174
268
  /** Bound every individual array/object before allocating a redacted copy. */
175
269
  const MAX_CONTAINER_ENTRIES = 1_024;
176
270
  /** Bound aggregate work across an entire redaction call, not per branch. */
177
271
  const MAX_TRAVERSAL_NODES = 4_096;
272
+ /** Bound hostile or cyclic prototype walks while looking for serializers. */
273
+ const MAX_SERIALIZATION_HOOK_PROTOTYPES = 64;
274
+ /** Bound the quadratic identifier-token scan used for free-text assignments. */
275
+ const MAX_ASSIGNMENT_KEY_LENGTH = 256;
178
276
  /**
179
277
  * Whether a context key names a credential and should have its value masked.
180
278
  *
@@ -186,19 +284,24 @@ const MAX_TRAVERSAL_NODES = 4_096;
186
284
  export function isSensitiveKey(key) {
187
285
  const cacheable = key.length <= SENSITIVE_KEY_CACHE_MAX_KEY_LENGTH;
188
286
  if (cacheable) {
189
- const cached = sensitiveKeyCache.get(key);
287
+ const cached = apply(mapGet, sensitiveKeyCache, [key]);
190
288
  if (cached !== undefined)
191
289
  return cached;
192
290
  }
193
291
  const normalized = normalizeToAlphanumeric(key);
194
- const sensitive = SENSITIVE_KEY_PATTERNS.some((pattern) => normalized.includes(pattern));
292
+ let sensitive = normalized === "auth";
293
+ for (let index = 0; !sensitive && index < SENSITIVE_KEY_PATTERNS.length; index++) {
294
+ sensitive = apply(stringIncludes, normalized, [SENSITIVE_KEY_PATTERNS[index]]);
295
+ }
195
296
  if (cacheable) {
196
- if (sensitiveKeyCache.size >= SENSITIVE_KEY_CACHE_MAX_SIZE) {
197
- const oldestKey = sensitiveKeyCache.keys().next().value;
297
+ const cacheSize = apply(mapSizeGetter, sensitiveKeyCache, []);
298
+ if (cacheSize >= SENSITIVE_KEY_CACHE_MAX_SIZE) {
299
+ const iterator = apply(mapKeys, sensitiveKeyCache, []);
300
+ const oldestKey = apply(mapIteratorNext, iterator, []).value;
198
301
  if (oldestKey !== undefined)
199
- sensitiveKeyCache.delete(oldestKey);
302
+ apply(mapDelete, sensitiveKeyCache, [oldestKey]);
200
303
  }
201
- sensitiveKeyCache.set(key, sensitive);
304
+ apply(mapSet, sensitiveKeyCache, [key, sensitive]);
202
305
  }
203
306
  return sensitive;
204
307
  }
@@ -209,12 +312,48 @@ export function isSensitiveKey(key) {
209
312
  */
210
313
  function classifyArray(value) {
211
314
  try {
212
- return Array.isArray(value);
315
+ return arrayIsArray(value);
213
316
  }
214
317
  catch {
215
318
  return null;
216
319
  }
217
320
  }
321
+ function hasSeenSerializationHookOwner(seenOwners, owner) {
322
+ for (let index = 0; index < seenOwners.length; index++) {
323
+ if (seenOwners[index] === owner)
324
+ return true;
325
+ }
326
+ return false;
327
+ }
328
+ /**
329
+ * Read a deliberate serialization hook without consulting hooks installed on
330
+ * the intrinsic Object or Array prototypes. Custom and platform prototypes
331
+ * such as Date and URL remain supported through data-property methods.
332
+ */
333
+ function readSerializationHook(value) {
334
+ let owner = value;
335
+ const seenOwners = [];
336
+ let prototypesVisited = 0;
337
+ while (owner !== null) {
338
+ if (owner === objectPrototype || owner === arrayPrototype)
339
+ return undefined;
340
+ if (prototypesVisited >= MAX_SERIALIZATION_HOOK_PROTOTYPES ||
341
+ hasSeenSerializationHookOwner(seenOwners, owner)) {
342
+ throw new TypeError("serialization hook prototype chain is cyclic or too deep");
343
+ }
344
+ prototypesVisited++;
345
+ apply(arrayPush, seenOwners, [owner]);
346
+ const descriptor = objectGetOwnPropertyDescriptor(owner, "toJSON");
347
+ if (descriptor !== undefined) {
348
+ if (!objectHasOwn(descriptor, "value")) {
349
+ throw new TypeError("serialization hooks must be data properties");
350
+ }
351
+ return descriptor.value;
352
+ }
353
+ owner = objectGetPrototypeOf(owner);
354
+ }
355
+ return undefined;
356
+ }
218
357
  function redactValue(value, depth, seen, mode, budget) {
219
358
  if (budget.remainingNodes <= 0) {
220
359
  budget.exhausted = true;
@@ -223,10 +362,11 @@ function redactValue(value, depth, seen, mode, budget) {
223
362
  budget.remainingNodes--;
224
363
  if (typeof value === "string")
225
364
  return sanitizeUrlCredentials(value);
226
- if (typeof value === "bigint")
227
- return mode === "serialization" ? value.toString() : value;
365
+ if (typeof value === "bigint") {
366
+ return mode === "serialization" ? apply(bigIntToString, value, []) : value;
367
+ }
228
368
  if (typeof value === "number") {
229
- return mode === "serialization" && !Number.isFinite(value) ? null : value;
369
+ return mode === "serialization" && !numberIsFinite(value) ? null : value;
230
370
  }
231
371
  if (typeof value === "boolean" || value === null)
232
372
  return value;
@@ -237,13 +377,13 @@ function redactValue(value, depth, seen, mode, budget) {
237
377
  if (arrayClassification === null)
238
378
  return REDACTED;
239
379
  if (arrayClassification) {
240
- if (depth >= MAX_DEPTH || seen.has(value))
380
+ if (depth >= MAX_DEPTH || apply(setHas, seen, [value]))
241
381
  return REDACTED;
242
- seen.add(value);
382
+ apply(setAdd, seen, [value]);
243
383
  try {
244
384
  const arrayValue = value;
245
385
  const length = arrayValue.length;
246
- if (!Number.isInteger(length) || length < 0 || length > MAX_CONTAINER_ENTRIES) {
386
+ if (!numberIsInteger(length) || length < 0 || length > MAX_CONTAINER_ENTRIES) {
247
387
  return REDACTED;
248
388
  }
249
389
  const redacted = mode === "compatible" ? new Array(length) : [];
@@ -257,7 +397,7 @@ function redactValue(value, depth, seen, mode, budget) {
257
397
  redacted[index] = item;
258
398
  }
259
399
  else {
260
- redacted.push(item);
400
+ apply(arrayPush, redacted, [item]);
261
401
  }
262
402
  }
263
403
  return redacted;
@@ -268,7 +408,7 @@ function redactValue(value, depth, seen, mode, budget) {
268
408
  return REDACTED;
269
409
  }
270
410
  finally {
271
- seen.delete(value);
411
+ apply(setDelete, seen, [value]);
272
412
  }
273
413
  }
274
414
  // Objects defining `toJSON` (Date, URL, custom serializers) are serialized
@@ -279,11 +419,11 @@ function redactValue(value, depth, seen, mode, budget) {
279
419
  // returns an object, array, or scalar, the serialization API redacts *that*
280
420
  // snapshot. The compatibility API keeps scalar serializers such as Date and
281
421
  // URL intact, preserving the established generic return contract.
282
- if (depth >= MAX_DEPTH || seen.has(value))
422
+ if (depth >= MAX_DEPTH || apply(setHas, seen, [value]))
283
423
  return REDACTED;
284
424
  let toJSON;
285
425
  try {
286
- toJSON = value.toJSON;
426
+ toJSON = readSerializationHook(value);
287
427
  }
288
428
  catch {
289
429
  // Accessors can throw before a serializer is callable. Never inspect the
@@ -291,7 +431,7 @@ function redactValue(value, depth, seen, mode, budget) {
291
431
  return REDACTED;
292
432
  }
293
433
  if (typeof toJSON === "function") {
294
- seen.add(value);
434
+ apply(setAdd, seen, [value]);
295
435
  try {
296
436
  const serialized = apply(toJSON, value, []);
297
437
  if (mode === "serialization") {
@@ -315,22 +455,22 @@ function redactValue(value, depth, seen, mode, budget) {
315
455
  return REDACTED;
316
456
  }
317
457
  finally {
318
- seen.delete(value);
458
+ apply(setDelete, seen, [value]);
319
459
  }
320
460
  }
321
- seen.add(value);
461
+ apply(setAdd, seen, [value]);
322
462
  try {
323
463
  const out = {};
324
464
  const record = value;
325
465
  let propertyCount = 0;
326
466
  for (const key in record) {
327
- if (!Object.hasOwn(record, key))
467
+ if (!objectHasOwn(record, key))
328
468
  continue;
329
469
  propertyCount++;
330
470
  if (propertyCount > MAX_CONTAINER_ENTRIES)
331
471
  return REDACTED;
332
472
  if (isSensitiveKey(key)) {
333
- Object.defineProperty(out, key, {
473
+ objectDefineProperty(out, key, {
334
474
  configurable: true,
335
475
  enumerable: true,
336
476
  value: REDACTED,
@@ -346,7 +486,7 @@ function redactValue(value, depth, seen, mode, budget) {
346
486
  const redactedChild = redactValue(child, depth + 1, seen, mode, budget);
347
487
  if (budget.exhausted)
348
488
  return REDACTED;
349
- Object.defineProperty(out, key, {
489
+ objectDefineProperty(out, key, {
350
490
  configurable: true,
351
491
  enumerable: true,
352
492
  value: redactedChild,
@@ -361,7 +501,7 @@ function redactValue(value, depth, seen, mode, budget) {
361
501
  return REDACTED;
362
502
  }
363
503
  finally {
364
- seen.delete(value);
504
+ apply(setDelete, seen, [value]);
365
505
  }
366
506
  }
367
507
  /**
@@ -375,7 +515,7 @@ function redactValue(value, depth, seen, mode, budget) {
375
515
  * functions, symbols, and custom `toJSON` implementations must be normalized.
376
516
  */
377
517
  export function redactSensitive(context) {
378
- return redactValue(context, 0, new Set(), "compatible", {
518
+ return redactValue(context, 0, new NativeSet(), "compatible", {
379
519
  remainingNodes: MAX_TRAVERSAL_NODES,
380
520
  exhausted: false,
381
521
  });
@@ -387,7 +527,7 @@ export function redactSensitive(context) {
387
527
  * closed. Objects with `toJSON` are snapshotted exactly once before redaction.
388
528
  */
389
529
  export function redactForSerialization(context) {
390
- return redactValue(context, 0, new Set(), "serialization", {
530
+ return redactValue(context, 0, new NativeSet(), "serialization", {
391
531
  remainingNodes: MAX_TRAVERSAL_NODES,
392
532
  exhausted: false,
393
533
  });
@@ -419,7 +559,12 @@ const SENSITIVE_URL_PARAMS = [
419
559
  "x-goog-credential",
420
560
  "x-goog-signature",
421
561
  ];
422
- const NORMALIZED_SENSITIVE_URL_PARAMS = new Set(SENSITIVE_URL_PARAMS.map(normalizeToAlphanumeric));
562
+ const NORMALIZED_SENSITIVE_URL_PARAMS = new NativeSet();
563
+ for (let index = 0; index < SENSITIVE_URL_PARAMS.length; index++) {
564
+ apply(setAdd, NORMALIZED_SENSITIVE_URL_PARAMS, [
565
+ normalizeToAlphanumeric(SENSITIVE_URL_PARAMS[index]),
566
+ ]);
567
+ }
423
568
  const URL_USERINFO_RE = /(\b[a-z][a-z0-9+.-]*:\/\/|\/\/)([^/?#\s]+)@/gi;
424
569
  const HORIZONTAL_WHITESPACE_URL_USERINFO_RE = /(\b[a-z][a-z0-9+.-]*:\/\/|\/\/)([a-z0-9._~!$&'()*+,;=%-]+):([^/?#@\r\n \t]+[ \t][^/?#@\r\n]*)@/gi;
425
570
  const MAX_URL_PARAMETER_DECODE_PASSES = 3;
@@ -435,7 +580,7 @@ function isHorizontalAssignmentBoundary(character) {
435
580
  function isAsciiLetter(character) {
436
581
  if (!character)
437
582
  return false;
438
- const code = character.charCodeAt(0);
583
+ const code = stringCodeUnitAt(character, 0);
439
584
  return (code >= 65 && code <= 90) || (code >= 97 && code <= 122);
440
585
  }
441
586
  function isAssignmentKeyStartCharacter(character) {
@@ -444,7 +589,7 @@ function isAssignmentKeyStartCharacter(character) {
444
589
  function isAssignmentKeyCharacter(character) {
445
590
  if (!character)
446
591
  return false;
447
- const code = character.charCodeAt(0);
592
+ const code = stringCodeUnitAt(character, 0);
448
593
  return (isAssignmentKeyStartCharacter(character) ||
449
594
  (code >= 48 && code <= 57) ||
450
595
  character === "." ||
@@ -495,7 +640,7 @@ function assignmentValueEndsAt(input, start) {
495
640
  function redactAssignmentValue(input, start) {
496
641
  let scanStart = start;
497
642
  let preserveValueQuote = true;
498
- if (input.startsWith(REDACTED, start)) {
643
+ if (apply(stringStartsWith, input, [REDACTED, start])) {
499
644
  const markerEnd = start + REDACTED.length;
500
645
  if (assignmentValueEndsAt(input, markerEnd)) {
501
646
  return {
@@ -525,6 +670,7 @@ function redactAssignmentValue(input, start) {
525
670
  if (character === quote) {
526
671
  if (quoteStart === scanStart && expectedClosings.length === 0) {
527
672
  wrapperQuoteClosed = true;
673
+ return { end: index + 1, replacement: replacement() };
528
674
  }
529
675
  quote = "";
530
676
  quoteStart = -1;
@@ -539,16 +685,16 @@ function redactAssignmentValue(input, start) {
539
685
  continue;
540
686
  }
541
687
  if (character === "{" || character === "[") {
542
- expectedClosings.push(character === "{" ? "}" : "]");
688
+ apply(arrayPush, expectedClosings, [character === "{" ? "}" : "]"]);
543
689
  index++;
544
690
  continue;
545
691
  }
546
692
  if (expectedClosings.length > 0 &&
547
693
  (character === "}" || character === "]")) {
548
- if (expectedClosings.at(-1) !== character) {
694
+ if (expectedClosings[expectedClosings.length - 1] !== character) {
549
695
  return { end: input.length, replacement: replacement() };
550
696
  }
551
- expectedClosings.pop();
697
+ apply(arrayPop, expectedClosings, []);
552
698
  index++;
553
699
  if (expectedClosings.length === 0 &&
554
700
  assignmentValueEndsAt(input, index)) {
@@ -574,7 +720,7 @@ function redactCredentialAssignments(input, prefixPattern, keyGroup, urlParamete
574
720
  let result = "";
575
721
  for (let match = apply(regExpExec, prefixPattern, [input]); match; match = apply(regExpExec, prefixPattern, [input])) {
576
722
  const key = match[keyGroup];
577
- if (!isSensitiveKey(key))
723
+ if (!isSensitiveAssignmentKey(key))
578
724
  continue;
579
725
  const valueStart = prefixPattern.lastIndex;
580
726
  const boundary = urlParameterBoundaryGroup === undefined
@@ -582,7 +728,7 @@ function redactCredentialAssignments(input, prefixPattern, keyGroup, urlParamete
582
728
  : match[urlParameterBoundaryGroup];
583
729
  const markerEnd = valueStart + REDACTED.length;
584
730
  if ((boundary === "?" || boundary === "&" || boundary === ";") &&
585
- input.startsWith(REDACTED, valueStart) &&
731
+ apply(stringStartsWith, input, [REDACTED, valueStart]) &&
586
732
  input[markerEnd] === "#") {
587
733
  // The URL-parameter pass already bounded this credential at the URI
588
734
  // fragment delimiter. Preserve that delimiter without treating an
@@ -598,15 +744,56 @@ function redactCredentialAssignments(input, prefixPattern, keyGroup, urlParamete
598
744
  }
599
745
  return cursor === 0 ? input : result + sliceString(input, cursor);
600
746
  }
747
+ /**
748
+ * Classify free-text assignment keys without applying the structured-key
749
+ * substring policy to ordinary words. Identifier and camel-case boundaries
750
+ * still recognize `refreshToken`, `client_secret`, and `x-api-key`, while
751
+ * words such as `mapping` and `considered` stay intact.
752
+ */
753
+ function isSensitiveAssignmentKey(key) {
754
+ // The token-range classifier below is quadratic in the number of identifier
755
+ // tokens. Oversized attacker-controlled keys fail closed before that work.
756
+ if (key.length > MAX_ASSIGNMENT_KEY_LENGTH)
757
+ return true;
758
+ const withAcronymBoundaries = replaceWithCapturedExec(key, ACRONYM_BOUNDARY_PATTERN, (match) => `${match[1]} ${match[2]}`);
759
+ const withBoundaries = replaceWithCapturedExec(withAcronymBoundaries, CAMEL_CASE_BOUNDARY_PATTERN, (match) => `${match[1]} ${match[2]}`);
760
+ const lowercase = apply(stringToLowerCase, withBoundaries, []);
761
+ const tokens = splitIdentifierTokens(lowercase);
762
+ for (let start = 0; start < tokens.length; start++) {
763
+ if (tokens[start].length === 0)
764
+ continue;
765
+ let candidate = "";
766
+ for (let end = start; end < tokens.length; end++) {
767
+ const token = tokens[end];
768
+ if (token.length === 0)
769
+ continue;
770
+ candidate += token;
771
+ for (let index = 0; index < SENSITIVE_KEY_PATTERNS.length; index++) {
772
+ if (candidate === SENSITIVE_KEY_PATTERNS[index])
773
+ return true;
774
+ }
775
+ }
776
+ }
777
+ return tokens.length === 1 && tokens[0] === "auth";
778
+ }
601
779
  function isStandaloneUrlAuthorityBeforeWhitespace(scheme, user, password) {
602
- const whitespaceIndex = password.search(/[ \t]/);
780
+ let whitespaceIndex = -1;
781
+ for (let index = 0; index < password.length; index++) {
782
+ const codeUnit = stringCodeUnitAt(password, index);
783
+ if (codeUnit === 0x20 || codeUnit === 0x09) {
784
+ whitespaceIndex = index;
785
+ break;
786
+ }
787
+ }
603
788
  if (whitespaceIndex < 0)
604
789
  return false;
605
790
  const authority = `${user}:${sliceString(password, 0, whitespaceIndex)}`;
606
791
  const candidate = scheme === "//" ? `https://${authority}` : `${scheme}${authority}`;
607
792
  try {
608
- const url = new URL(candidate);
609
- return url.username.length === 0 && url.password.length === 0;
793
+ const url = new NativeURL(candidate);
794
+ const username = apply(urlUsernameGetter, url, []);
795
+ const password = apply(urlPasswordGetter, url, []);
796
+ return username.length === 0 && password.length === 0;
610
797
  }
611
798
  catch {
612
799
  return false;
@@ -617,7 +804,7 @@ function decodeUrlParameterName(value) {
617
804
  for (let pass = 0; pass < MAX_URL_PARAMETER_DECODE_PASSES; pass++) {
618
805
  let next;
619
806
  try {
620
- next = decodeURIComponent(decoded);
807
+ next = nativeDecodeURIComponent(decoded);
621
808
  }
622
809
  catch {
623
810
  break;
@@ -634,8 +821,10 @@ function decodeUrlParameterName(value) {
634
821
  * {@link redactSensitive}, which is key-based, this scrubs secrets embedded in
635
822
  * the *value* itself:
636
823
  *
637
- * - URL userinfo: `http://user:pass@host` `http://user:[REDACTED]@host`
638
- * - sensitive query params: `?access_token=abc` `?access_token=[REDACTED]`
824
+ * - URL userinfo: `https://user:password@example.test/path` -> `https://user:[REDACTED]@example.test/path`
825
+ * - sensitive query params: `?access_token=abc` -> `?access_token=[REDACTED]`
826
+ * - credential assignments: `refreshToken=abc` -> `refreshToken=[REDACTED]`
827
+ * - common provider tokens: `Using token sk-...` -> `Using token [REDACTED]`
639
828
  *
640
829
  * It is intentionally tolerant: it operates on any string (a DSN, a Mongo URI,
641
830
  * an axios error message containing a URL) via regex rather than requiring a
@@ -646,68 +835,63 @@ export function sanitizeUrlCredentials(input) {
646
835
  if (typeof input !== "string" || input.length === 0)
647
836
  return input;
648
837
  // 1) userinfo: scheme://user:pass@ → mask the password (and any bare creds).
649
- let out = apply(regExpReplace, URL_USERINFO_RE, [
650
- input,
651
- (_match, scheme, userinfo) => {
652
- const colon = userinfo.indexOf(":");
653
- if (colon === -1) {
654
- // `scheme://token@host` — the whole userinfo is credential-like.
655
- return `${scheme}${REDACTED}@`;
656
- }
657
- const user = sliceString(userinfo, 0, colon);
658
- return `${scheme}${user}:${REDACTED}@`;
659
- },
660
- ]);
661
- out = apply(regExpReplace, HORIZONTAL_WHITESPACE_URL_USERINFO_RE, [
662
- out,
663
- (match, scheme, user, password) => {
664
- // Do not reinterpret a complete URL followed later by an email address
665
- // on the same line as malformed userinfo. Raw-horizontal-whitespace
666
- // recovery is limited to explicit `user:password` shapes whose prefix
667
- // cannot already be parsed as a standalone authority.
668
- if (isStandaloneUrlAuthorityBeforeWhitespace(scheme, user, password)) {
669
- return match;
670
- }
671
- return `${scheme}${user}:${REDACTED}@`;
672
- },
673
- ]);
838
+ let out = replaceWithCapturedExec(input, URL_USERINFO_RE, (match) => {
839
+ const scheme = match[1];
840
+ const userinfo = match[2];
841
+ const colon = apply(stringIndexOf, userinfo, [":"]);
842
+ if (colon === -1) {
843
+ // `scheme://token@host` — the whole userinfo is credential-like.
844
+ return `${scheme}${REDACTED}@`;
845
+ }
846
+ const user = sliceString(userinfo, 0, colon);
847
+ return `${scheme}${user}:${REDACTED}@`;
848
+ });
849
+ out = replaceWithCapturedExec(out, HORIZONTAL_WHITESPACE_URL_USERINFO_RE, (match) => {
850
+ const scheme = match[1];
851
+ const user = match[2];
852
+ const password = match[3];
853
+ // Do not reinterpret a complete URL followed later by an email address
854
+ // on the same line as malformed userinfo. Raw-horizontal-whitespace
855
+ // recovery is limited to explicit `user:password` shapes whose prefix
856
+ // cannot already be parsed as a standalone authority.
857
+ if (isStandaloneUrlAuthorityBeforeWhitespace(scheme, user, password)) {
858
+ return match[0];
859
+ }
860
+ return `${scheme}${user}:${REDACTED}@`;
861
+ });
674
862
  // 2) sensitive query/fragment params: `key=value` → `key=[REDACTED]`.
675
863
  // Match `?key=`, `#key=`, `&key=`, and `;key=` separators and stop at the
676
864
  // next delimiter. OAuth implicit-flow tokens commonly appear after `#`.
677
- out = apply(regExpReplace, /([?#&;])([-a-z0-9_.%\[\]]+)=([^&#;\s]*)/gi, [
678
- out,
679
- (match, sep, key, _val) => {
680
- const decodedKey = decodeUrlParameterName(key);
681
- const sensitive = NORMALIZED_SENSITIVE_URL_PARAMS.has(normalizeToAlphanumeric(decodedKey)) ||
682
- isSensitiveKey(decodedKey);
683
- return sensitive ? `${sep}${key}=${REDACTED}` : match;
684
- },
685
- ]);
865
+ out = replaceWithCapturedExec(out, /([?#&;])([-a-z0-9_.%\[\]]+)=([^&#;\s]*)/gi, (match) => {
866
+ const sep = match[1];
867
+ const key = match[2];
868
+ const decodedKey = decodeUrlParameterName(key);
869
+ const sensitive = apply(setHas, NORMALIZED_SENSITIVE_URL_PARAMS, [
870
+ normalizeToAlphanumeric(decodedKey),
871
+ ]) ||
872
+ isSensitiveKey(decodedKey);
873
+ return sensitive ? `${sep}${key}=${REDACTED}` : match[0];
874
+ });
686
875
  // 3) Cookie header values.
687
876
  // Cookie headers can carry multiple independent credentials separated by
688
877
  // semicolons (and Set-Cookie attributes can contain commas). Mask the entire
689
878
  // header line before the generic assignment scanner can stop at the first
690
879
  // delimiter and expose later values.
691
- out = apply(regExpReplace, /(^|[^a-z0-9_-])((?:set-cookie|cookie)\s*:\s*)[^\r\n]*/gi, [
692
- out,
693
- (_match, boundary, prefix) => `${boundary}${prefix}${REDACTED}`,
694
- ]);
880
+ out = replaceWithCapturedExec(out, /(^|[^a-z0-9_-])((?:set-cookie|cookie)\s*:\s*)[^\r\n]*/gi, (match) => `${match[1]}${match[2]}${REDACTED}`);
695
881
  // 4) Header-shaped authorization values and standalone auth schemes.
696
882
  // Authorization schemes are extensible (AWS SigV4, Digest, custom proxy
697
883
  // schemes, and others), so mask the complete line instead of trying to
698
884
  // enumerate schemes or parse their credential-bearing parameters.
699
- out = apply(regExpReplace, /\b(authorization\s*[:=]\s*)[^\r\n]*/gi, [
700
- out,
701
- (_match, prefix) => `${prefix}${REDACTED}`,
702
- ]);
703
- out = apply(regExpReplace, /\b(bearer|basic)(\s+)(?:"[^"\r\n]*"|'[^'\r\n]*'|[a-z0-9._~+/=-]+)/gi, [
704
- out,
705
- (_match, scheme, whitespace) => `${scheme}${whitespace}${REDACTED}`,
706
- ]);
707
- // 5) Credential assignments embedded in free-form messages/errors. Match
708
- // generic identifier-shaped keys and delegate classification to the same
709
- // deny-list used for structured context. This keeps JSON snippets, header
710
- // dumps, and ordinary `key=value` text from drifting to a weaker policy.
885
+ out = replaceWithCapturedExec(out, /\b(authorization\s*[:=]\s*)[^\r\n]*/gi, (match) => `${match[1]}${REDACTED}`);
886
+ out = replaceWithCapturedExec(out, /\b(bearer|basic)(\s+)(?:"[^"\r\n]*"|'[^'\r\n]*'|[a-z0-9._~+/=-]+)/gi, (match) => `${match[1]}${match[2]}${REDACTED}`);
887
+ // 5) Common provider token shapes can appear as bare values without an
888
+ // assignment delimiter, for example `Using token sk-...`.
889
+ out = replaceWithCapturedExec(out, PROVIDER_CREDENTIAL_PATTERN, REDACTED);
890
+ // 6) Credential assignments embedded in free-form messages/errors. Match
891
+ // generic identifier-shaped keys and apply the same credential vocabulary
892
+ // at identifier boundaries. This keeps JSON snippets, header dumps, and
893
+ // ordinary `key=value` text from drifting to a weaker policy without
894
+ // masking benign words that merely contain a short pattern.
711
895
  // Handle quoted JSON/object keys first and preserve their quoting so the
712
896
  // sanitized text remains intelligible and structurally valid.
713
897
  out = redactCredentialAssignments(out, /(["'])([_$a-z][a-z0-9_.$-]*)\1(\s*[:=]\s*)/gi, 2);
@@ -715,20 +899,20 @@ export function sanitizeUrlCredentials(input) {
715
899
  return out;
716
900
  }
717
901
  function firstUrlDelimiterIndex(input) {
718
- const queryIndex = input.indexOf("?");
719
- const hashIndex = input.indexOf("#");
902
+ const queryIndex = apply(stringIndexOf, input, ["?"]);
903
+ const hashIndex = apply(stringIndexOf, input, ["#"]);
720
904
  if (queryIndex === -1)
721
905
  return hashIndex;
722
906
  if (hashIndex === -1)
723
907
  return queryIndex;
724
- return Math.min(queryIndex, hashIndex);
908
+ return queryIndex < hashIndex ? queryIndex : hashIndex;
725
909
  }
726
910
  function sanitizeProtocolRelativeUrlForSpan(input) {
727
- if (!input.startsWith("//"))
911
+ if (!apply(stringStartsWith, input, ["//"]))
728
912
  return null;
729
913
  try {
730
- const url = new URL(`https:${input}`);
731
- return `//${url.host}${url.pathname}`;
914
+ const url = new NativeURL(`https:${input}`);
915
+ return `//${apply(urlHostGetter, url, [])}${apply(urlPathnameGetter, url, [])}`;
732
916
  }
733
917
  catch (_) {
734
918
  return null;
@@ -747,20 +931,24 @@ export function sanitizeUrlForSpan(input) {
747
931
  if (typeof input !== "string" || input.length === 0)
748
932
  return input;
749
933
  try {
750
- const url = new URL(input);
751
- if (url.protocol === "blob:") {
934
+ const url = new NativeURL(input);
935
+ const protocol = apply(urlProtocolGetter, url, []);
936
+ const pathname = apply(urlPathnameGetter, url, []);
937
+ const origin = apply(urlOriginGetter, url, []);
938
+ if (protocol === "blob:") {
752
939
  try {
753
- const embeddedUrl = new URL(url.pathname);
754
- return embeddedUrl.origin === "null" ? "blob:" : `blob:${embeddedUrl.origin}`;
940
+ const embeddedUrl = new NativeURL(pathname);
941
+ const embeddedOrigin = apply(urlOriginGetter, embeddedUrl, []);
942
+ return embeddedOrigin === "null" ? "blob:" : `blob:${embeddedOrigin}`;
755
943
  }
756
944
  catch (_) {
757
945
  return "blob:";
758
946
  }
759
947
  }
760
- if (url.origin !== "null")
761
- return `${url.origin}${url.pathname}`;
762
- if (/^[a-z][a-z0-9+.-]*:/i.test(input))
763
- return url.protocol;
948
+ if (origin !== "null")
949
+ return `${origin}${pathname}`;
950
+ if (apply(regExpExec, /^[a-z][a-z0-9+.-]*:/i, [input]) !== null)
951
+ return protocol;
764
952
  }
765
953
  catch (_) {
766
954
  // Relative or malformed URL-shaped strings are handled by the fallback.