@sema-agent/client-core 0.101.0 → 0.102.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +57 -0
- package/README.md +12 -7
- package/dist/adapt/toolCards.d.ts +2 -1
- package/dist/adapt/toolCards.js +20 -6
- package/dist/adapt.d.ts +1 -1
- package/dist/adapt.js +1 -1
- package/dist/adapter/downstream/eventToSdkMessage.js +6 -68
- package/dist/adapter/downstream/wiringManifestView.d.ts +1 -0
- package/dist/adapter/downstream/wiringManifestView.js +138 -0
- package/dist/compensations.js +1 -1
- package/dist/displayUntrusted.d.ts +8 -4
- package/dist/displayUntrusted.js +769 -96
- package/dist/engineRowRef.js +1 -0
- package/dist/fleet/fleetProjection.js +1 -1
- package/dist/i18n/messages.en.d.ts +38 -0
- package/dist/i18n/messages.en.js +38 -0
- package/dist/i18n/messages.zh.js +40 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +5 -0
- package/dist/interactiveHalt.d.ts +3 -1
- package/dist/interactiveHalt.js +2 -0
- package/dist/interactiveInterrupt.d.ts +2 -0
- package/dist/interactiveInterrupt.js +15 -0
- package/dist/modsFace.d.ts +8 -0
- package/dist/modsFace.js +59 -0
- package/dist/ownKey.d.ts +1 -0
- package/dist/ownKey.js +15 -0
- package/dist/printToolResultFrame.js +1 -1
- package/dist/request/printNotification.js +1 -1
- package/dist/request/taskRequest.d.ts +21 -0
- package/dist/request/taskRequest.js +123 -7
- package/dist/seam.js +1 -1
- package/dist/seatContract.d.ts +22 -6
- package/dist/seatContract.js +35 -6
- package/dist/sessionBackground.d.ts +165 -0
- package/dist/sessionBackground.js +781 -0
- package/dist/sessionBackgroundHandoff.d.ts +54 -0
- package/dist/sessionBackgroundHandoff.js +321 -0
- package/dist/sessionMap.js +4 -1
- package/dist/settingsCredentialKeyPaths.js +2 -17
- package/dist/thinkingLatch.d.ts +69 -0
- package/dist/thinkingLatch.js +255 -0
- package/dist/thinkingLatchWords.d.ts +7 -0
- package/dist/thinkingLatchWords.js +4 -0
- package/dist/toolResult.js +2 -24
- package/docs/INTEGRATION-CLIENTS.md +309 -10
- package/package.json +1 -1
- package/dist/argvFlagValue.d.ts +0 -8
- package/dist/argvFlagValue.js +0 -37
package/dist/displayUntrusted.js
CHANGED
|
@@ -11,7 +11,7 @@ function isMarker(v) {
|
|
|
11
11
|
return core === RETIRED_MARKER || MARKER_RE.test(core);
|
|
12
12
|
}
|
|
13
13
|
const VIEW_SEPARATOR = '\ufeff';
|
|
14
|
-
function pushEdit(edits, src, start, end, text) {
|
|
14
|
+
function pushEdit(edits, src, start, end, text, phases) {
|
|
15
15
|
let a = start;
|
|
16
16
|
let b = end;
|
|
17
17
|
while (a < b && src[a] === VIEW_SEPARATOR)
|
|
@@ -22,7 +22,7 @@ function pushEdit(edits, src, start, end, text) {
|
|
|
22
22
|
a = b = start;
|
|
23
23
|
if (src.slice(a, b) === text)
|
|
24
24
|
return;
|
|
25
|
-
edits.push({ start: a, end: b, text });
|
|
25
|
+
edits.push(phases === undefined ? { start: a, end: b, text } : { start: a, end: b, text, phases });
|
|
26
26
|
}
|
|
27
27
|
const UNIT_NONE = 0;
|
|
28
28
|
const UNIT_SEQUENCE = 1;
|
|
@@ -220,7 +220,7 @@ const isHostChar = (c) => (c >= 0x30 && c <= 0x39) || (c >= 0x41 && c <= 0x5a) |
|
|
|
220
220
|
const isIpv6Char = (c) => (c >= 0x30 && c <= 0x3a) || (c >= 0x41 && c <= 0x46) || (c >= 0x61 && c <= 0x66) || c === 0x2e;
|
|
221
221
|
const endsSchemelessHost = (c) => Number.isNaN(c) || c === 0x2f || c === 0x3f || c === 0x23 || c === 0x22 || c === 0x3c || c === 0x3e || c === 0x60 ||
|
|
222
222
|
c === 0x7c || c === 0x5c || c === 0x5e || c === 0x7b || c === 0x7d || isRegexSpace(c);
|
|
223
|
-
function schemelessHostEnd(text, from) {
|
|
223
|
+
function schemelessHostEnd(text, from, wide = false) {
|
|
224
224
|
let k = from;
|
|
225
225
|
if (text.charCodeAt(k) === 0x5b) {
|
|
226
226
|
k++;
|
|
@@ -238,14 +238,25 @@ function schemelessHostEnd(text, from) {
|
|
|
238
238
|
}
|
|
239
239
|
if (text.charCodeAt(k) === 0x3a) {
|
|
240
240
|
let d = k + 1;
|
|
241
|
-
|
|
242
|
-
d
|
|
243
|
-
|
|
241
|
+
if (wide) {
|
|
242
|
+
while (d < text.length && isSchemelessPortChar(text.charCodeAt(d)))
|
|
243
|
+
d++;
|
|
244
244
|
k = d;
|
|
245
|
+
}
|
|
246
|
+
else {
|
|
247
|
+
while (d < text.length && text.charCodeAt(d) >= 0x30 && text.charCodeAt(d) <= 0x39)
|
|
248
|
+
d++;
|
|
249
|
+
if (d > k + 1)
|
|
250
|
+
k = d;
|
|
251
|
+
}
|
|
245
252
|
}
|
|
246
253
|
const c = text.charCodeAt(k);
|
|
247
|
-
return endsSchemelessHost(c) || c === 0x27 ? k : -1;
|
|
254
|
+
return endsSchemelessHost(c) || c === 0x27 || (wide && endsSchemelessHostInProse(c)) ? k : -1;
|
|
248
255
|
}
|
|
256
|
+
const isSchemelessPortChar = (c) => (c >= 0x30 && c <= 0x39) || (c >= 0x41 && c <= 0x5a) || (c >= 0x61 && c <= 0x7a) || c === 0x2e || c === 0x5f || c === 0x7e || c === 0x25 || c === 0x2d;
|
|
257
|
+
const endsSchemelessHostInProse = (c) => c === 0x29 || c === 0x2c || c === 0x3b || c === 0x21 || c === 0x5d;
|
|
258
|
+
const SCHEMELESS_USERINFO_WIDE_SPAN = 64 + 1 + 256;
|
|
259
|
+
const isUserinfoWideDelimiter = (ch) => ch === '?' || ch === '#' || ch === '@';
|
|
249
260
|
const MAX_PORT = 65535;
|
|
250
261
|
function authorityIsHostPort(text, from, to) {
|
|
251
262
|
if (to <= from || schemelessHostEnd(text.slice(0, to), from) !== to)
|
|
@@ -282,32 +293,32 @@ function queryCandidates(src, base, query, edits, soft, byShape = false) {
|
|
|
282
293
|
const eq = part.indexOf('=');
|
|
283
294
|
if (eq === -1) {
|
|
284
295
|
const at = pos;
|
|
285
|
-
if (!slotIsWholeMarker(part, soft && ((i) => soft(at + i)))
|
|
286
|
-
|
|
296
|
+
if (!slotIsWholeMarker(part, soft && ((i) => soft(at + i)))) {
|
|
297
|
+
const phases = byShape ? queryPartPhases(null, part, relax) : phaseRange(0, scanUpto);
|
|
298
|
+
if (phases !== 0)
|
|
299
|
+
pushEdit(edits, src, pos, pos + part.length, REDACTED_QUERY, phasesOrAll(phases));
|
|
300
|
+
}
|
|
287
301
|
}
|
|
288
302
|
else {
|
|
289
303
|
const value = part.slice(eq + 1);
|
|
290
304
|
const at = pos + eq + 1;
|
|
291
|
-
if (value.length > 0 && !slotIsWholeMarker(value, soft && ((i) => soft(at + i)))
|
|
292
|
-
|
|
305
|
+
if (value.length > 0 && !slotIsWholeMarker(value, soft && ((i) => soft(at + i)))) {
|
|
306
|
+
const phases = byShape ? queryPartPhases(part.slice(0, eq), value, relax) : phaseRange(0, scanUpto);
|
|
307
|
+
if (phases !== 0)
|
|
308
|
+
pushEdit(edits, src, pos + eq + 1, pos + part.length, REDACTED_QUERY, phasesOrAll(phases));
|
|
293
309
|
}
|
|
294
310
|
}
|
|
295
311
|
}
|
|
296
312
|
pos += part.length + 1;
|
|
297
313
|
}
|
|
298
314
|
}
|
|
299
|
-
function pathCandidates(src, base, path, opaqueSegments, edits, soft) {
|
|
315
|
+
function pathCandidates(src, base, path, opaqueSegments, edits, soft, whole = false) {
|
|
300
316
|
let pos = base;
|
|
301
317
|
for (const segment of path.split('/')) {
|
|
302
318
|
const semi = segment.indexOf(';');
|
|
303
319
|
const head = semi === -1 ? segment : segment.slice(0, semi);
|
|
304
320
|
if (opaqueSegments) {
|
|
305
|
-
|
|
306
|
-
let b = head.length;
|
|
307
|
-
while (a < b && !isKeyWordChar(head.charCodeAt(a)) && !(head.charCodeAt(a) === 0x25 && hasNonAsciiPercentEscape(head.slice(a, a + 3))))
|
|
308
|
-
a++;
|
|
309
|
-
while (b > a && !isKeyWordChar(head.charCodeAt(b - 1)))
|
|
310
|
-
b--;
|
|
321
|
+
const [a, b] = markerAtomicCut(head, ...segmentWordCut(head));
|
|
311
322
|
if (isKnownSecretSegment(head)) {
|
|
312
323
|
if (b > a)
|
|
313
324
|
pushEdit(edits, src, pos + a, pos + b, REDACTED_SECRET);
|
|
@@ -317,6 +328,10 @@ function pathCandidates(src, base, path, opaqueSegments, edits, soft) {
|
|
|
317
328
|
if (glued > 0)
|
|
318
329
|
pushEdit(edits, src, pos + a + glued, pos + b, REDACTED_SECRET);
|
|
319
330
|
}
|
|
331
|
+
if (whole && b > a) {
|
|
332
|
+
const at = pos;
|
|
333
|
+
emitRow('path-slot', (phases) => pathSlotEdits(src, at, head, a, b, phases, edits, soft));
|
|
334
|
+
}
|
|
320
335
|
}
|
|
321
336
|
if (semi !== -1) {
|
|
322
337
|
let ppos = pos + semi + 1;
|
|
@@ -334,9 +349,76 @@ function pathCandidates(src, base, path, opaqueSegments, edits, soft) {
|
|
|
334
349
|
pos += segment.length + 1;
|
|
335
350
|
}
|
|
336
351
|
}
|
|
337
|
-
function
|
|
352
|
+
function segmentWordCut(head) {
|
|
353
|
+
let a = 0;
|
|
354
|
+
let b = head.length;
|
|
355
|
+
while (a < b && !isKeyWordChar(head.charCodeAt(a)) && !(head.charCodeAt(a) === 0x25 && hasNonAsciiPercentEscape(head.slice(a, a + 3))))
|
|
356
|
+
a++;
|
|
357
|
+
while (b > a && !isKeyWordChar(head.charCodeAt(b - 1)))
|
|
358
|
+
b--;
|
|
359
|
+
return [a, b];
|
|
360
|
+
}
|
|
361
|
+
function markerAtomicCut(head, a, b) {
|
|
362
|
+
if (!head.includes(REDACTION_MARKER_PREFIX))
|
|
363
|
+
return [a, b];
|
|
364
|
+
let s = a;
|
|
365
|
+
let e = b;
|
|
366
|
+
const re = new RegExp(MARKER_ANYWHERE.source, MARKER_ANYWHERE.flags);
|
|
367
|
+
for (let m = re.exec(head); m !== null; m = re.exec(head)) {
|
|
368
|
+
const ms = m.index;
|
|
369
|
+
const me = ms + m[0].length;
|
|
370
|
+
if (ms < s && s < me)
|
|
371
|
+
s = ms;
|
|
372
|
+
if (ms < e && e < me)
|
|
373
|
+
e = me;
|
|
374
|
+
}
|
|
375
|
+
return [s, e];
|
|
376
|
+
}
|
|
377
|
+
function pathSlotEdits(src, pos, head, a, b, rowPhases, edits, soft) {
|
|
378
|
+
const softFrom = (at) => (soft === undefined ? undefined : (i) => soft(pos + at + i));
|
|
379
|
+
const eq = head.indexOf('=', a);
|
|
380
|
+
let named = 0;
|
|
381
|
+
if (eq > a && eq < b) {
|
|
382
|
+
const name = head.slice(a, eq);
|
|
383
|
+
if (!slotIsWholeMarker(name, softFrom(a))) {
|
|
384
|
+
named = queryPartPhases(null, name, false) & rowPhases;
|
|
385
|
+
if (named !== 0)
|
|
386
|
+
pushEdit(edits, src, pos + a, pos + b, REDACTED_SECRET, named);
|
|
387
|
+
if (named === rowPhases)
|
|
388
|
+
return;
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
const [s, e] = markerAtomicCut(head, eq !== -1 && eq < b ? eq + 1 : a, b);
|
|
392
|
+
if (s >= e)
|
|
393
|
+
return;
|
|
394
|
+
const slot = head.slice(s, e);
|
|
395
|
+
if (slotIsWholeMarker(slot, softFrom(s)))
|
|
396
|
+
return;
|
|
397
|
+
const valued = queryPartPhases(null, slot, false) & rowPhases & ~named;
|
|
398
|
+
if (valued !== 0)
|
|
399
|
+
pushEdit(edits, src, pos + s, pos + e, REDACTED_SECRET, valued);
|
|
400
|
+
}
|
|
401
|
+
const anchoredFormPattern = (form) => new RegExp(`(?:https?:\/\/)?${form.host.split('.').join('\\.')}${form.trailingDot ? '\\.?' : ''}\/(?:${form.pathKinds.join('|')})\/((?:[${form.tailClass}]|${MARKER_ANYWHERE.source})+)`, 'gi');
|
|
402
|
+
function anchoredFormCandidates(text, out) {
|
|
403
|
+
for (const { id, form, probe, pattern } of ANCHORED_FORM_ROWS) {
|
|
404
|
+
if (!probe.test(text))
|
|
405
|
+
continue;
|
|
406
|
+
emitRow(id, (phases) => {
|
|
407
|
+
const re = new RegExp(pattern.source, pattern.flags);
|
|
408
|
+
for (let m = re.exec(text); m !== null; m = re.exec(text)) {
|
|
409
|
+
const tail = m[1] ?? '';
|
|
410
|
+
const end = m.index + m[0].length;
|
|
411
|
+
if (tail.length > 0 && (tail.includes(REDACTION_MARKER_PREFIX) || tail.length >= form.minTailLength))
|
|
412
|
+
out.push({ a: end - tail.length, b: end, kind: 'secret', cls: 'url', te: -1, phases });
|
|
413
|
+
if (m[0].length === 0)
|
|
414
|
+
re.lastIndex++;
|
|
415
|
+
}
|
|
416
|
+
});
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
function addressCandidates(src, from, end, opaqueSegments, edits, regions, soft, byShape = false, whole = false, hostRegion = whole, pathOnly = false) {
|
|
338
420
|
const rest = src.slice(from, end);
|
|
339
|
-
const authorityEnd = rest.search(/[/?#]/);
|
|
421
|
+
const authorityEnd = pathOnly ? 0 : rest.search(/[/?#]/);
|
|
340
422
|
const authority = authorityEnd === -1 ? rest : rest.slice(0, authorityEnd);
|
|
341
423
|
const tailStart = from + authority.length;
|
|
342
424
|
const tail = src.slice(tailStart, end);
|
|
@@ -347,7 +429,7 @@ function addressCandidates(src, from, end, opaqueSegments, edits, regions, soft,
|
|
|
347
429
|
const beforeHash = hash === -1 ? tail : tail.slice(0, hash);
|
|
348
430
|
const question = beforeHash.indexOf('?');
|
|
349
431
|
const path = question === -1 ? beforeHash : beforeHash.slice(0, question);
|
|
350
|
-
pathCandidates(src, tailStart, path, opaqueSegments, edits, soft);
|
|
432
|
+
pathCandidates(src, tailStart, path, opaqueSegments, edits, soft, whole);
|
|
351
433
|
if (whole) {
|
|
352
434
|
const cut = pathCutAfterFirstLabel(path, true);
|
|
353
435
|
if (cut !== -1 && fragmentTerminatorFrom(path, cut))
|
|
@@ -376,7 +458,8 @@ function relativeAddressCandidates(text, out, regions, unitPos, soft, hostRegion
|
|
|
376
458
|
while (p0 < text.length && (isWhitespaceOrControl(text.charAt(p0)) || text.charAt(p0) === VIEW_SEPARATOR))
|
|
377
459
|
p0++;
|
|
378
460
|
const c0 = text.charAt(p0);
|
|
379
|
-
|
|
461
|
+
const pathOnly = c0 !== '/' && c0 !== '?' && c0 !== '#' && isRelativeRequestAt(text, p0);
|
|
462
|
+
if (c0 !== '/' && c0 !== '?' && c0 !== '#' && !pathOnly)
|
|
380
463
|
return false;
|
|
381
464
|
let end = text.length;
|
|
382
465
|
while (end > p0 && (text.charAt(end - 1) === VIEW_SEPARATOR || isWhitespaceOrControl(text.charAt(end - 1)) || isUnicodeBlank(text.charCodeAt(end - 1))))
|
|
@@ -384,10 +467,19 @@ function relativeAddressCandidates(text, out, regions, unitPos, soft, hostRegion
|
|
|
384
467
|
end = trailingCloserEnd(text, p0, p0 + 1, end, unitPos);
|
|
385
468
|
const from = text.startsWith('//', p0) ? p0 + 2 : p0;
|
|
386
469
|
const tokenEdits = [];
|
|
387
|
-
addressCandidates(text, from, end, true, tokenEdits, regions, soft, false, true, hostRegion);
|
|
470
|
+
addressCandidates(text, from, end, true, tokenEdits, regions, soft, false, true, hostRegion, pathOnly);
|
|
388
471
|
editsToCandidates(tokenEdits, 'url', out, end);
|
|
389
472
|
return true;
|
|
390
473
|
}
|
|
474
|
+
function isRelativeRequestAt(text, p0) {
|
|
475
|
+
const rest = text.slice(p0);
|
|
476
|
+
if (/^[A-Za-z][A-Za-z0-9+.-]*:/.test(rest))
|
|
477
|
+
return false;
|
|
478
|
+
const q = rest.search(/[?#]/);
|
|
479
|
+
if (q <= 0)
|
|
480
|
+
return false;
|
|
481
|
+
return !/[\s=\ufeff]/.test(rest.slice(0, q)) && rest.indexOf('=', q + 1) !== -1;
|
|
482
|
+
}
|
|
391
483
|
function urlCandidates(text, out, regions, unitPos = () => false, soft, byShape = false, whole = false, hostRegion = whole) {
|
|
392
484
|
if (whole && relativeAddressCandidates(text, out, regions, unitPos, soft, hostRegion))
|
|
393
485
|
return;
|
|
@@ -457,6 +549,8 @@ function urlCandidates(text, out, regions, unitPos = () => false, soft, byShape
|
|
|
457
549
|
end = runStart;
|
|
458
550
|
end = trailingCloserEnd(text, start, mark + 3, end, unitPos);
|
|
459
551
|
const tokenEdits = [];
|
|
552
|
+
if (userinfoBoundaryAmbiguous(text.slice(start, end)))
|
|
553
|
+
pushEdit(tokenEdits, text, start, end, REDACTED_SECRET);
|
|
460
554
|
addressCandidates(text, mark + 3, end, !/^file:/i.test(text.slice(start, mark + 3)), tokenEdits, regions, soft, byShape, whole, hostRegion);
|
|
461
555
|
editsToCandidates(tokenEdits, 'url', out, end);
|
|
462
556
|
mark = text.indexOf('://', end > mark + 3 ? end : mark + 3);
|
|
@@ -635,13 +729,7 @@ function userinfoCandidates(S, out, words = true) {
|
|
|
635
729
|
return nextBareStop(from) >= at;
|
|
636
730
|
};
|
|
637
731
|
let floor = 0;
|
|
638
|
-
|
|
639
|
-
let hostEnd = schemelessHostEnd(text, at + 1);
|
|
640
|
-
const hidden = hostEnd === -1;
|
|
641
|
-
if (hidden)
|
|
642
|
-
hostEnd = hiddenHostEnd(text, at + 1, unitAt);
|
|
643
|
-
if (hostEnd === -1)
|
|
644
|
-
continue;
|
|
732
|
+
const readAt = (at, hostEnd, hidden, runStartIn) => {
|
|
645
733
|
let shaped = null;
|
|
646
734
|
const hostShaped = () => {
|
|
647
735
|
if (shaped === null) {
|
|
@@ -673,9 +761,7 @@ function userinfoCandidates(S, out, words = true) {
|
|
|
673
761
|
return hostShaped() ? 'bare' : null;
|
|
674
762
|
};
|
|
675
763
|
const refused = [];
|
|
676
|
-
let runStart =
|
|
677
|
-
while (runStart > floor && (!breaksUserinfo(text.charCodeAt(runStart - 1)) || (hidden && unitAt(runStart - 1) !== UNIT_NONE)))
|
|
678
|
-
runStart--;
|
|
764
|
+
let runStart = runStartIn;
|
|
679
765
|
if (text.charCodeAt(runStart) === 0x3a && runStart > floor && text[runStart - 1] === VIEW_SEPARATOR) {
|
|
680
766
|
while (runStart > floor && text[runStart - 1] === VIEW_SEPARATOR)
|
|
681
767
|
runStart--;
|
|
@@ -723,7 +809,7 @@ function userinfoCandidates(S, out, words = true) {
|
|
|
723
809
|
continue;
|
|
724
810
|
}
|
|
725
811
|
}
|
|
726
|
-
if (USERNAME_DELIMITERS.has(ch)) {
|
|
812
|
+
if (USERNAME_DELIMITERS.has(ch) || isUserinfoWideDelimiter(ch)) {
|
|
727
813
|
lo = colon + 1;
|
|
728
814
|
continue;
|
|
729
815
|
}
|
|
@@ -791,10 +877,50 @@ function userinfoCandidates(S, out, words = true) {
|
|
|
791
877
|
}
|
|
792
878
|
}
|
|
793
879
|
}
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
880
|
+
return found;
|
|
881
|
+
};
|
|
882
|
+
const walkFrom = (at, hidden) => {
|
|
883
|
+
let r = at;
|
|
884
|
+
while (r > floor && (!breaksUserinfo(text.charCodeAt(r - 1)) || (hidden && unitAt(r - 1) !== UNIT_NONE)))
|
|
885
|
+
r--;
|
|
886
|
+
return r;
|
|
887
|
+
};
|
|
888
|
+
const widen = (at, r) => {
|
|
889
|
+
const wideFloor = Math.max(floor, at - SCHEMELESS_USERINFO_WIDE_SPAN);
|
|
890
|
+
let w = r;
|
|
891
|
+
while (w > wideFloor && isUserinfoWideDelimiter(text.charAt(w - 1))) {
|
|
892
|
+
w--;
|
|
893
|
+
while (w > wideFloor && !breaksUserinfo(text.charCodeAt(w - 1)))
|
|
894
|
+
w--;
|
|
895
|
+
}
|
|
896
|
+
return w;
|
|
897
|
+
};
|
|
898
|
+
for (let at = text.indexOf('@'); at !== -1; at = text.indexOf('@', at + 1)) {
|
|
899
|
+
let hostEnd = schemelessHostEnd(text, at + 1);
|
|
900
|
+
const hidden = hostEnd === -1;
|
|
901
|
+
if (hidden)
|
|
902
|
+
hostEnd = hiddenHostEnd(text, at + 1, unitAt);
|
|
903
|
+
const cursor = markerFrom;
|
|
904
|
+
const runStart = hostEnd === -1 ? -1 : walkFrom(at, hidden);
|
|
905
|
+
const found = runStart === -1 ? -1 : readAt(at, hostEnd, hidden, runStart);
|
|
906
|
+
if (found !== -1)
|
|
907
|
+
out.push({ a: found, b: at, kind: 'userinfo', cls: hidden ? 'userinfo-hidden' : 'structural', te: hostEnd });
|
|
908
|
+
const wideEnd = schemelessHostEnd(text, at + 1, true);
|
|
909
|
+
const wHidden = wideEnd === -1 ? hidden : false;
|
|
910
|
+
const wHostEnd = wideEnd === -1 ? hostEnd : wideEnd;
|
|
911
|
+
if (wHostEnd !== -1) {
|
|
912
|
+
const wRunStart = widen(at, wHostEnd === hostEnd && wHidden === hidden && runStart !== -1 ? runStart : walkFrom(at, wHidden));
|
|
913
|
+
if (wHostEnd !== hostEnd || wHidden !== hidden || wRunStart !== runStart) {
|
|
914
|
+
const after = markerFrom;
|
|
915
|
+
markerFrom = cursor;
|
|
916
|
+
const wFound = readAt(at, wHostEnd, wHidden, wRunStart);
|
|
917
|
+
markerFrom = Math.max(after, markerFrom);
|
|
918
|
+
if (wFound !== -1 && (wFound !== found || wHostEnd !== hostEnd))
|
|
919
|
+
out.push({ a: wFound, b: at, kind: 'userinfo', cls: wHidden ? 'userinfo-hidden' : 'structural', te: wHostEnd });
|
|
920
|
+
}
|
|
921
|
+
}
|
|
922
|
+
if (found !== -1)
|
|
923
|
+
floor = hostEnd;
|
|
798
924
|
}
|
|
799
925
|
}
|
|
800
926
|
function labelWashedBefore(text, a, floor) {
|
|
@@ -4040,7 +4166,7 @@ function editsToCandidates(edits, cls, out, te = -1) {
|
|
|
4040
4166
|
const kind = KIND_BY_TEXT.get(e.text);
|
|
4041
4167
|
if (kind === undefined || e.end <= e.start)
|
|
4042
4168
|
continue;
|
|
4043
|
-
out.push({ a: e.start, b: e.end, kind, cls, te });
|
|
4169
|
+
out.push(e.phases === undefined ? { a: e.start, b: e.end, kind, cls, te } : { a: e.start, b: e.end, kind, cls, te, phases: e.phases });
|
|
4044
4170
|
}
|
|
4045
4171
|
}
|
|
4046
4172
|
function pemRegions(recognized) {
|
|
@@ -4258,7 +4384,8 @@ function toRaw(S, c) {
|
|
|
4258
4384
|
if (a >= b)
|
|
4259
4385
|
return null;
|
|
4260
4386
|
const te = c.te > b ? S.rawEnd(c.te - 1) : S.rawEnd(b - 1);
|
|
4261
|
-
|
|
4387
|
+
const r = { start: S.rawStart(a), end: S.rawEnd(b - 1), kind: c.kind, cls: c.cls, tokenEnd: te };
|
|
4388
|
+
return c.phases === undefined ? r : { ...r, phases: c.phases };
|
|
4262
4389
|
}
|
|
4263
4390
|
function runLabelPasses(S, regions, cands, blocks) {
|
|
4264
4391
|
labelCandidates(S, 'scheme', regions, cands, blocks);
|
|
@@ -4307,6 +4434,7 @@ function collectCandidates(text, o, nested) {
|
|
|
4307
4434
|
if (r[2] === 'host')
|
|
4308
4435
|
regions.push(r);
|
|
4309
4436
|
}
|
|
4437
|
+
anchoredFormCandidates(S.text, cands);
|
|
4310
4438
|
if (o.words) {
|
|
4311
4439
|
editsToCandidates(pemEdits(S, (blk) => { if (S === regionSurface)
|
|
4312
4440
|
pemBlocksSeen.push(pemBlockToRaw(S, blk)); }), 'pem', cands);
|
|
@@ -4327,26 +4455,100 @@ function collectCandidates(text, o, nested) {
|
|
|
4327
4455
|
for (const [a, b] of V.contents) {
|
|
4328
4456
|
const sub = text.slice(a, b);
|
|
4329
4457
|
const inner = collectCandidates(sub, o, true);
|
|
4330
|
-
for (const iv of
|
|
4331
|
-
|
|
4458
|
+
for (const [iv, phases] of resolveEachPhase(sub, inner, o)) {
|
|
4459
|
+
const c = { start: a + iv.start, end: a + iv.end, kind: iv.kind, cls: 'nested', tokenEnd: a + iv.end, pretrimmed: true };
|
|
4460
|
+
raw.push(phases === undefined ? c : { ...c, phases });
|
|
4332
4461
|
}
|
|
4333
4462
|
}
|
|
4334
4463
|
for (const c of [...screen.contents, ...decodedContents]) {
|
|
4335
4464
|
const inner = collectCandidates(c.sub, o, true);
|
|
4336
|
-
for (const iv of
|
|
4465
|
+
for (const [iv, phases] of resolveEachPhase(c.sub, inner, o)) {
|
|
4337
4466
|
const start = c.rawAt(iv.start);
|
|
4338
4467
|
const end = c.rawEndAt(iv.end - 1);
|
|
4339
|
-
if (end
|
|
4340
|
-
|
|
4468
|
+
if (end <= start)
|
|
4469
|
+
continue;
|
|
4470
|
+
const r = { start, end, kind: iv.kind, cls: 'nested', tokenEnd: end, pretrimmed: true };
|
|
4471
|
+
raw.push(phases === undefined ? r : { ...r, phases });
|
|
4341
4472
|
}
|
|
4342
4473
|
}
|
|
4343
4474
|
}
|
|
4344
4475
|
return { cands: raw, rawUnit: V.rawUnit, rawDot: screen.surfaces[0]?.rawDot ?? null, rawSoft: screen.rawSoft, rawPlain: screen.rawPlain, pemBlocksSeen };
|
|
4345
4476
|
}
|
|
4346
|
-
|
|
4347
|
-
|
|
4348
|
-
|
|
4349
|
-
|
|
4477
|
+
const RULES_BY_VERSION = {
|
|
4478
|
+
'0.102.0': [
|
|
4479
|
+
{ id: 'path-slot', ticket: 'CC-849', kind: 'generator' },
|
|
4480
|
+
{
|
|
4481
|
+
id: 'anchored-form:slack-webhook-url',
|
|
4482
|
+
ticket: 'CC-849',
|
|
4483
|
+
kind: 'generator',
|
|
4484
|
+
form: { host: 'hooks.slack.com', trailingDot: true, pathKinds: ['services', 'workflows', 'triggers'], tailClass: 'A-Za-z0-9+/_-', minTailLength: 40 },
|
|
4485
|
+
},
|
|
4486
|
+
],
|
|
4487
|
+
};
|
|
4488
|
+
const RULE_ROWS_ARE_GENERATORS = true;
|
|
4489
|
+
void RULE_ROWS_ARE_GENERATORS;
|
|
4490
|
+
function rowPhaseTable() {
|
|
4491
|
+
const m = new Map();
|
|
4492
|
+
const keys = Object.keys(RULES_BY_VERSION);
|
|
4493
|
+
keys.forEach((k, j) => {
|
|
4494
|
+
for (const row of RULES_BY_VERSION[k])
|
|
4495
|
+
m.set(row.id, j + 1);
|
|
4496
|
+
});
|
|
4497
|
+
return m;
|
|
4498
|
+
}
|
|
4499
|
+
const ROW_PHASE = rowPhaseTable();
|
|
4500
|
+
const LAST_PHASE = Object.keys(RULES_BY_VERSION).length;
|
|
4501
|
+
const ANCHORED_FORM_ROWS = Object.values(RULES_BY_VERSION)
|
|
4502
|
+
.flatMap((rows) => rows.filter((r) => 'form' in r))
|
|
4503
|
+
.map((r) => ({ id: r.id, form: r.form, probe: new RegExp(r.form.host.split('.').join('\\.'), 'i'), pattern: anchoredFormPattern(r.form) }));
|
|
4504
|
+
let scanUpto = LAST_PHASE;
|
|
4505
|
+
const phaseRange = (from, to) => (to < from ? 0 : ((1 << (to + 1)) - 1) & ~((1 << from) - 1));
|
|
4506
|
+
const phasesOrAll = (mask) => (mask === phaseRange(0, scanUpto) ? undefined : mask);
|
|
4507
|
+
const inPhase = (phases, k) => phases === undefined || ((phases >> k) & 1) === 1;
|
|
4508
|
+
function emitRow(rowId, emit) {
|
|
4509
|
+
const p = ROW_PHASE.get(rowId) ?? Number.POSITIVE_INFINITY;
|
|
4510
|
+
if (p > scanUpto)
|
|
4511
|
+
return;
|
|
4512
|
+
emit(phaseRange(p, scanUpto));
|
|
4513
|
+
}
|
|
4514
|
+
function resolveEachPhase(text, c, o) {
|
|
4515
|
+
const pems = o.words ? pemRegions(c.pemBlocksSeen) : [];
|
|
4516
|
+
const run = (cs) => resolveCandidates(text, cs, c.rawUnit, c.rawDot, pems, c.rawSoft, c.rawPlain);
|
|
4517
|
+
if (!c.cands.some((x) => x.phases !== undefined))
|
|
4518
|
+
return run(c.cands).map((iv) => [iv, undefined]);
|
|
4519
|
+
const per = [];
|
|
4520
|
+
for (let k = 0; k <= scanUpto; k++)
|
|
4521
|
+
per.push(run(c.cands.filter((x) => inPhase(x.phases, k))));
|
|
4522
|
+
const key = (ivs) => ivs.map((iv) => `${iv.start}:${iv.end}:${iv.kind}`).join(',');
|
|
4523
|
+
const k0 = key(per[0] ?? []);
|
|
4524
|
+
if (per.every((ivs) => key(ivs) === k0))
|
|
4525
|
+
return (per[0] ?? []).map((iv) => [iv, undefined]);
|
|
4526
|
+
const out = [];
|
|
4527
|
+
per.forEach((ivs, k) => {
|
|
4528
|
+
for (const iv of ivs)
|
|
4529
|
+
out.push([iv, 1 << k]);
|
|
4530
|
+
});
|
|
4531
|
+
return out;
|
|
4532
|
+
}
|
|
4533
|
+
function scanPhases(text, o, trace, upto) {
|
|
4534
|
+
const saved = scanUpto;
|
|
4535
|
+
scanUpto = upto;
|
|
4536
|
+
try {
|
|
4537
|
+
const c = collectCandidates(text, o, false);
|
|
4538
|
+
const pems = o.words ? pemRegions(c.pemBlocksSeen) : [];
|
|
4539
|
+
const run = (cs) => materialize(text, resolveCandidates(text, cs, c.rawUnit, c.rawDot, pems, c.rawSoft, c.rawPlain), trace);
|
|
4540
|
+
if (!c.cands.some((x) => x.phases !== undefined)) {
|
|
4541
|
+
const one = run(c.cands);
|
|
4542
|
+
return Array.from({ length: upto + 1 }, () => one);
|
|
4543
|
+
}
|
|
4544
|
+
const outs = [];
|
|
4545
|
+
for (let k = 0; k <= upto; k++)
|
|
4546
|
+
outs.push(run(c.cands.filter((x) => inPhase(x.phases, k))));
|
|
4547
|
+
return outs;
|
|
4548
|
+
}
|
|
4549
|
+
finally {
|
|
4550
|
+
scanUpto = saved;
|
|
4551
|
+
}
|
|
4350
4552
|
}
|
|
4351
4553
|
const MAX_WASH_REWRITES = 8;
|
|
4352
4554
|
function composeOrigin(first, second) {
|
|
@@ -4361,36 +4563,76 @@ function composeOrigin(first, second) {
|
|
|
4361
4563
|
}
|
|
4362
4564
|
return out;
|
|
4363
4565
|
}
|
|
4364
|
-
function scanAndMask(text, o, trace, stats) {
|
|
4566
|
+
function scanAndMask(text, o, trace, stats, phaseOut) {
|
|
4365
4567
|
if (!o.urls && !o.words) {
|
|
4366
4568
|
if (stats !== undefined) {
|
|
4367
4569
|
stats.passes = 0;
|
|
4368
4570
|
stats.capped = false;
|
|
4571
|
+
stats.collections = 0;
|
|
4369
4572
|
}
|
|
4573
|
+
phaseOut?.push({ text });
|
|
4370
4574
|
return { text };
|
|
4371
4575
|
}
|
|
4372
|
-
|
|
4373
|
-
let
|
|
4576
|
+
const upto = scanUpto;
|
|
4577
|
+
let memoText = null;
|
|
4578
|
+
let memo = [];
|
|
4579
|
+
let collections = 0;
|
|
4580
|
+
let passes = 0;
|
|
4581
|
+
const op = (t, k) => {
|
|
4582
|
+
if (t !== memoText) {
|
|
4583
|
+
memo = scanPhases(t, o, trace, upto);
|
|
4584
|
+
memoText = t;
|
|
4585
|
+
collections++;
|
|
4586
|
+
}
|
|
4587
|
+
passes++;
|
|
4588
|
+
return memo[k] ?? { text: t };
|
|
4589
|
+
};
|
|
4590
|
+
const advance = (cur, next) => {
|
|
4591
|
+
const origin = trace ? composeOrigin(cur.origin, next.origin) : undefined;
|
|
4592
|
+
return origin === undefined ? { text: next.text } : { text: next.text, origin };
|
|
4593
|
+
};
|
|
4594
|
+
let cur = op(text, 0);
|
|
4374
4595
|
let rewrites = cur.text !== text ? 1 : 0;
|
|
4375
4596
|
let changed = rewrites > 0;
|
|
4376
4597
|
while (changed && rewrites < MAX_WASH_REWRITES) {
|
|
4377
|
-
const next =
|
|
4378
|
-
passes++;
|
|
4598
|
+
const next = op(cur.text, 0);
|
|
4379
4599
|
changed = next.text !== cur.text;
|
|
4380
4600
|
if (!changed)
|
|
4381
4601
|
break;
|
|
4382
4602
|
rewrites++;
|
|
4383
|
-
|
|
4384
|
-
|
|
4603
|
+
cur = advance(cur, next);
|
|
4604
|
+
}
|
|
4605
|
+
const stable = [!changed];
|
|
4606
|
+
phaseOut?.push(cur);
|
|
4607
|
+
for (let j = 1; j <= upto; j++) {
|
|
4608
|
+
stable.push(false);
|
|
4609
|
+
let rw = 0;
|
|
4610
|
+
let k = j;
|
|
4611
|
+
while (rw < MAX_WASH_REWRITES && !stable.every(Boolean)) {
|
|
4612
|
+
if (stable[k] === true) {
|
|
4613
|
+
k = (k + j) % (j + 1);
|
|
4614
|
+
continue;
|
|
4615
|
+
}
|
|
4616
|
+
const next = op(cur.text, k);
|
|
4617
|
+
if (next.text === cur.text)
|
|
4618
|
+
stable[k] = true;
|
|
4619
|
+
else {
|
|
4620
|
+
rw++;
|
|
4621
|
+
cur = advance(cur, next);
|
|
4622
|
+
stable.fill(false);
|
|
4623
|
+
}
|
|
4624
|
+
k = (k + j) % (j + 1);
|
|
4625
|
+
}
|
|
4626
|
+
phaseOut?.push(cur);
|
|
4385
4627
|
}
|
|
4386
4628
|
if (stats !== undefined) {
|
|
4387
4629
|
let capped = false;
|
|
4388
|
-
|
|
4389
|
-
|
|
4390
|
-
|
|
4391
|
-
}
|
|
4630
|
+
for (let k = 0; k < stable.length; k++)
|
|
4631
|
+
if (stable[k] !== true && op(cur.text, k).text !== cur.text)
|
|
4632
|
+
capped = true;
|
|
4392
4633
|
stats.passes = passes;
|
|
4393
4634
|
stats.capped = capped;
|
|
4635
|
+
stats.collections = collections;
|
|
4394
4636
|
}
|
|
4395
4637
|
return cur;
|
|
4396
4638
|
}
|
|
@@ -4789,14 +5031,28 @@ function scanOptionsOf(o, face, queryByShape = false, wholeAddress = false) {
|
|
|
4789
5031
|
};
|
|
4790
5032
|
}
|
|
4791
5033
|
export function credentialWashPasses(text, opts = {}) {
|
|
4792
|
-
const input =
|
|
4793
|
-
const stats = { passes: 0, capped: false };
|
|
5034
|
+
const input = displayInputText(text);
|
|
5035
|
+
const stats = { passes: 0, capped: false, collections: 0 };
|
|
4794
5036
|
const scan = opts === null ? WASH_OPTIONS : scanOptionsOf(opts, publicFace(opts));
|
|
4795
5037
|
scanAndMask(input, scan, false, stats);
|
|
4796
|
-
return { passes: stats.passes, capped: stats.capped };
|
|
5038
|
+
return { collections: stats.collections, passes: stats.passes, capped: stats.capped };
|
|
5039
|
+
}
|
|
5040
|
+
function displayInputText(text) {
|
|
5041
|
+
switch (typeof text) {
|
|
5042
|
+
case 'string':
|
|
5043
|
+
return text;
|
|
5044
|
+
case 'number':
|
|
5045
|
+
case 'bigint':
|
|
5046
|
+
case 'boolean':
|
|
5047
|
+
return String(text);
|
|
5048
|
+
case 'undefined':
|
|
5049
|
+
return '';
|
|
5050
|
+
default:
|
|
5051
|
+
return text === null ? '' : REDACTED_SECRET;
|
|
5052
|
+
}
|
|
4797
5053
|
}
|
|
4798
5054
|
function renderUntrusted(text, opts, ledger, queryByShape = false, wholeAddress = false) {
|
|
4799
|
-
const input =
|
|
5055
|
+
const input = displayInputText(text);
|
|
4800
5056
|
const o = opts ?? {};
|
|
4801
5057
|
const face = publicFace(o);
|
|
4802
5058
|
const scan = scanOptionsOf(o, face, queryByShape, wholeAddress);
|
|
@@ -4995,51 +5251,74 @@ function hasOpaquePiece(text) {
|
|
|
4995
5251
|
}
|
|
4996
5252
|
return false;
|
|
4997
5253
|
}
|
|
4998
|
-
function
|
|
4999
|
-
const
|
|
5000
|
-
|
|
5001
|
-
|
|
5002
|
-
if (
|
|
5003
|
-
|
|
5004
|
-
|
|
5005
|
-
|
|
5254
|
+
function carriesCredentialStructurePhases(value) {
|
|
5255
|
+
const all = phaseRange(0, scanUpto);
|
|
5256
|
+
let hit = 0;
|
|
5257
|
+
const net = (v) => {
|
|
5258
|
+
if (hasOpaquePiece(v)) {
|
|
5259
|
+
hit = all;
|
|
5260
|
+
return;
|
|
5261
|
+
}
|
|
5262
|
+
hit |= washChangesPhases(v);
|
|
5263
|
+
if (hit === all || !v.includes('+'))
|
|
5264
|
+
return;
|
|
5265
|
+
hit |= washChangesPhases(v.split('+').join(' '));
|
|
5006
5266
|
};
|
|
5007
5267
|
let view = value;
|
|
5008
5268
|
for (let layer = 0; layer < MAX_PERCENT_VIEW_LAYERS; layer++) {
|
|
5009
|
-
|
|
5010
|
-
|
|
5269
|
+
net(view);
|
|
5270
|
+
if (hit === all)
|
|
5271
|
+
return all;
|
|
5011
5272
|
const next = percentDecodeForMatching(view).decoded;
|
|
5012
5273
|
if (next === view)
|
|
5013
|
-
return
|
|
5274
|
+
return hit;
|
|
5014
5275
|
view = next;
|
|
5015
5276
|
}
|
|
5016
|
-
|
|
5017
|
-
|
|
5018
|
-
|
|
5277
|
+
net(view);
|
|
5278
|
+
if (hit === all)
|
|
5279
|
+
return all;
|
|
5280
|
+
return percentDecodeForMatching(view).decoded !== view ? all : hit;
|
|
5281
|
+
}
|
|
5282
|
+
function washChangesPhases(v) {
|
|
5283
|
+
const ends = [];
|
|
5284
|
+
scanAndMask(v, WASH_OPTIONS, false, undefined, ends);
|
|
5285
|
+
let mask = 0;
|
|
5286
|
+
ends.forEach((t, k) => {
|
|
5287
|
+
if (t.text !== v)
|
|
5288
|
+
mask |= 1 << k;
|
|
5289
|
+
});
|
|
5290
|
+
return mask;
|
|
5019
5291
|
}
|
|
5020
|
-
function
|
|
5292
|
+
function queryPartPhases(key, value, relax) {
|
|
5293
|
+
const all = phaseRange(0, scanUpto);
|
|
5021
5294
|
if (value.length === 0 || isMarker(value))
|
|
5022
|
-
return
|
|
5295
|
+
return 0;
|
|
5023
5296
|
if (key !== null) {
|
|
5024
5297
|
if (isCredentialLabelName(key))
|
|
5025
|
-
return
|
|
5298
|
+
return all;
|
|
5026
5299
|
if (relax && isNonSecretFieldName(key) && isReadableWordChain(value))
|
|
5027
|
-
return isKnownSecretSegment(value);
|
|
5300
|
+
return isKnownSecretSegment(value) ? all : 0;
|
|
5028
5301
|
}
|
|
5029
|
-
if (isOpaqueSecretValue(value)
|
|
5030
|
-
return
|
|
5302
|
+
if (isOpaqueSecretValue(value))
|
|
5303
|
+
return all;
|
|
5304
|
+
let hit = carriesCredentialStructurePhases(value);
|
|
5305
|
+
if (hit === all)
|
|
5306
|
+
return all;
|
|
5031
5307
|
let from = 0;
|
|
5032
5308
|
for (let i = 0; i <= value.length; i++) {
|
|
5033
5309
|
if (i < value.length && !endsUrlFragmentAt(value, i))
|
|
5034
5310
|
continue;
|
|
5035
5311
|
if (i > from && (from > 0 || i < value.length)) {
|
|
5036
5312
|
const chunk = value.slice(from, i);
|
|
5037
|
-
if (isOpaqueSecretValue(chunk)
|
|
5038
|
-
return
|
|
5313
|
+
if (isOpaqueSecretValue(chunk))
|
|
5314
|
+
return all;
|
|
5315
|
+
hit |= carriesCredentialStructurePhases(chunk);
|
|
5316
|
+
if (hit === all)
|
|
5317
|
+
return all;
|
|
5039
5318
|
}
|
|
5040
5319
|
from = i + 1;
|
|
5041
5320
|
}
|
|
5042
|
-
return
|
|
5321
|
+
return hit;
|
|
5043
5322
|
}
|
|
5044
5323
|
const UPLOAD_PROBE_PREFIX = 'https://q.invalid/?';
|
|
5045
5324
|
const UPLOAD_SCAN_OPTIONS = Object.freeze({ urls: true, queryByShape: 'strict', wholeAddress: true, words: true, blanks: false, fold: false, face: null, escapeSpaces: null });
|
|
@@ -5047,27 +5326,421 @@ export function queryCarriesCredential(query) {
|
|
|
5047
5326
|
if (typeof query !== 'string' || query.length === 0)
|
|
5048
5327
|
return false;
|
|
5049
5328
|
const probe = `${UPLOAD_PROBE_PREFIX}${query}`;
|
|
5050
|
-
return
|
|
5329
|
+
return scanAndMask(probe, UPLOAD_SCAN_OPTIONS, false).text !== probe;
|
|
5051
5330
|
}
|
|
5052
5331
|
export function displayConfiguredUrl(url, opts = {}) {
|
|
5053
|
-
const
|
|
5332
|
+
const reading = typeof url === 'string' ? configuredAddressReading(url) : null;
|
|
5333
|
+
const out = renderUntrusted(reading === null ? url : reading.withheld ? REDACTED_SECRET : reading.text, opts, null, 'relaxed', true);
|
|
5054
5334
|
return typeof out === 'string' ? out : out.text;
|
|
5055
5335
|
}
|
|
5056
|
-
function
|
|
5057
|
-
|
|
5058
|
-
|
|
5059
|
-
|
|
5336
|
+
function parserViewOf(url) {
|
|
5337
|
+
return url.replace(/[\t\n\r]/g, '').replace(/^[\u0000-\u0020]+/, '');
|
|
5338
|
+
}
|
|
5339
|
+
const AMBIGUOUS_USERINFO_BOUNDARY = /^(?:[A-Za-z][A-Za-z0-9+.-]*:\/\/|(?:https?|wss?|ftp):[\\/]*)[^/\\?#@]*[?#][^@]*@/i;
|
|
5340
|
+
function userinfoBoundaryAmbiguous(view) {
|
|
5341
|
+
return AMBIGUOUS_USERINFO_BOUNDARY.test(view);
|
|
5342
|
+
}
|
|
5343
|
+
function authorityHasBackslash(view) {
|
|
5344
|
+
const scheme = /^(?:https?|wss?|ftp|file):\/\//i.exec(view);
|
|
5345
|
+
if (scheme === null)
|
|
5346
|
+
return false;
|
|
5347
|
+
const from = scheme[0].length;
|
|
5348
|
+
let end = from;
|
|
5349
|
+
while (end < view.length && !'/?#'.includes(view.charAt(end)))
|
|
5350
|
+
end++;
|
|
5351
|
+
const marks = markerSpans(view);
|
|
5352
|
+
for (let s = 0; s < marks.length; s += 2)
|
|
5353
|
+
if ((marks[s] ?? end) >= from && (marks[s] ?? end) < end)
|
|
5354
|
+
return false;
|
|
5355
|
+
let backslash = false;
|
|
5356
|
+
for (let k = from; k < end; k++) {
|
|
5357
|
+
if (view.charAt(k) !== '\\')
|
|
5358
|
+
continue;
|
|
5359
|
+
if (outletAtomEnd(view, k) > k)
|
|
5360
|
+
return false;
|
|
5361
|
+
backslash = true;
|
|
5362
|
+
}
|
|
5363
|
+
return backslash;
|
|
5364
|
+
}
|
|
5365
|
+
function outletAtomEnd(text, at) {
|
|
5366
|
+
return escapeTokenAt(text, at)?.end ?? (nameBlankTokenAt(text, at) !== -1 ? at + 6 : -1);
|
|
5367
|
+
}
|
|
5368
|
+
function rebuiltAddress(url) {
|
|
5060
5369
|
let u;
|
|
5061
5370
|
try {
|
|
5062
5371
|
u = new URL(url);
|
|
5063
5372
|
}
|
|
5064
5373
|
catch {
|
|
5065
|
-
return
|
|
5374
|
+
return null;
|
|
5066
5375
|
}
|
|
5067
5376
|
if (u.host === '')
|
|
5068
|
-
return
|
|
5377
|
+
return null;
|
|
5069
5378
|
const userinfo = u.username !== '' || u.password !== '' ? `${u.username}${u.password !== '' ? `:${u.password}` : ''}@` : '';
|
|
5070
|
-
|
|
5379
|
+
const text = `${u.protocol}//${userinfo}${u.host}${u.pathname}${u.search}${u.hash}`;
|
|
5380
|
+
const fwdA = new Int32Array(url.length).fill(-2);
|
|
5381
|
+
const fwdB = new Int32Array(url.length).fill(-2);
|
|
5382
|
+
const backA = new Int32Array(text.length).fill(-2);
|
|
5383
|
+
const backB = new Int32Array(text.length).fill(-2);
|
|
5384
|
+
const parts = new Int8Array(url.length);
|
|
5385
|
+
const out = { text, toRebuilt: [fwdA, fwdB], toOriginal: [backA, backB], parts };
|
|
5386
|
+
const encoder = new TextEncoder();
|
|
5387
|
+
const at = [];
|
|
5388
|
+
for (let i = 0; i < url.length; i++) {
|
|
5389
|
+
const c = url.charCodeAt(i);
|
|
5390
|
+
if (c === 0x09 || c === 0x0a || c === 0x0d)
|
|
5391
|
+
fwdA[i] = -1;
|
|
5392
|
+
else
|
|
5393
|
+
at.push(i);
|
|
5394
|
+
}
|
|
5395
|
+
let a = 0;
|
|
5396
|
+
let b = at.length;
|
|
5397
|
+
while (a < b && url.charCodeAt(at[a] ?? 0) <= 0x20)
|
|
5398
|
+
fwdA[at[a++] ?? 0] = -1;
|
|
5399
|
+
while (b > a && url.charCodeAt(at[b - 1] ?? 0) <= 0x20)
|
|
5400
|
+
fwdA[at[--b] ?? 0] = -1;
|
|
5401
|
+
const ch = (k) => url.charAt(at[k] ?? -1);
|
|
5402
|
+
let e = a;
|
|
5403
|
+
while (e < b && ch(e) !== ':')
|
|
5404
|
+
e++;
|
|
5405
|
+
const scheme = at.slice(a, e).map((i) => url.charAt(i)).join('');
|
|
5406
|
+
if (e === b || !/^(?:https?|wss?|ftp)$/i.test(scheme))
|
|
5407
|
+
return out;
|
|
5408
|
+
const align = (from, to, rFrom, rTo, mode, part) => {
|
|
5409
|
+
const pairs = [];
|
|
5410
|
+
let r = rFrom;
|
|
5411
|
+
for (let k = from; k < to;) {
|
|
5412
|
+
const i = at[k] ?? -1;
|
|
5413
|
+
const cp = url.codePointAt(i) ?? 0;
|
|
5414
|
+
const wide = cp > 0xffff && k + 1 < to && at[k + 1] === i + 1;
|
|
5415
|
+
const c = wide ? String.fromCodePoint(cp) : url.charAt(i);
|
|
5416
|
+
const step = c.length;
|
|
5417
|
+
let len = -1;
|
|
5418
|
+
if (text.startsWith(c, r))
|
|
5419
|
+
len = c.length;
|
|
5420
|
+
else if (mode === 'fold' && c.length === 1 && /[A-Z]/.test(c) && text.charAt(r) === c.toLowerCase())
|
|
5421
|
+
len = 1;
|
|
5422
|
+
else if (mode === 'path' && c === '\\' && text.charAt(r) === '/')
|
|
5423
|
+
len = 1;
|
|
5424
|
+
else if (mode === 'encoded' || mode === 'path') {
|
|
5425
|
+
const enc = [...encoder.encode(c)].map((x) => `%${x.toString(16).toUpperCase().padStart(2, '0')}`).join('');
|
|
5426
|
+
if (text.startsWith(enc, r))
|
|
5427
|
+
len = enc.length;
|
|
5428
|
+
}
|
|
5429
|
+
if (len < 0 || r + len > rTo)
|
|
5430
|
+
return;
|
|
5431
|
+
pairs.push(i, step, r, r + len);
|
|
5432
|
+
r += len;
|
|
5433
|
+
k += step;
|
|
5434
|
+
}
|
|
5435
|
+
if (r !== rTo)
|
|
5436
|
+
return;
|
|
5437
|
+
for (let p = 0; p < pairs.length; p += 4) {
|
|
5438
|
+
const i = pairs[p] ?? -1;
|
|
5439
|
+
const n = pairs[p + 1] ?? 1;
|
|
5440
|
+
const s = pairs[p + 2] ?? 0;
|
|
5441
|
+
const t = pairs[p + 3] ?? 0;
|
|
5442
|
+
for (let j = i; j < i + n; j++) {
|
|
5443
|
+
fwdA[j] = s;
|
|
5444
|
+
fwdB[j] = t;
|
|
5445
|
+
parts[j] = part;
|
|
5446
|
+
}
|
|
5447
|
+
backA.fill(i, s, t);
|
|
5448
|
+
backB.fill(i + n, s, t);
|
|
5449
|
+
}
|
|
5450
|
+
};
|
|
5451
|
+
const drop = (from, to) => {
|
|
5452
|
+
for (let k = from; k < to; k++)
|
|
5453
|
+
fwdA[at[k] ?? -1] = -1;
|
|
5454
|
+
};
|
|
5455
|
+
const insert = (rFrom, rTo) => {
|
|
5456
|
+
backA.fill(-1, rFrom, rTo);
|
|
5457
|
+
};
|
|
5458
|
+
align(a, e + 1, 0, u.protocol.length, 'fold', 1);
|
|
5459
|
+
let k = e + 1;
|
|
5460
|
+
while (k < b && (ch(k) === '/' || ch(k) === '\\'))
|
|
5461
|
+
k++;
|
|
5462
|
+
drop(e + 1, k);
|
|
5463
|
+
insert(u.protocol.length, u.protocol.length + 2);
|
|
5464
|
+
let r = u.protocol.length + 2;
|
|
5465
|
+
let end = k;
|
|
5466
|
+
while (end < b && !'/\\?#'.includes(ch(end)))
|
|
5467
|
+
end++;
|
|
5468
|
+
let lastAt = -1;
|
|
5469
|
+
for (let j = k; j < end; j++)
|
|
5470
|
+
if (ch(j) === '@')
|
|
5471
|
+
lastAt = j;
|
|
5472
|
+
if (lastAt >= 0) {
|
|
5473
|
+
let colon = lastAt;
|
|
5474
|
+
for (let j = k; j < lastAt; j++)
|
|
5475
|
+
if (ch(j) === ':') {
|
|
5476
|
+
colon = j;
|
|
5477
|
+
break;
|
|
5478
|
+
}
|
|
5479
|
+
align(k, colon, r, r + u.username.length, 'encoded', 2);
|
|
5480
|
+
r += u.username.length;
|
|
5481
|
+
if (colon < lastAt) {
|
|
5482
|
+
if (u.password !== '') {
|
|
5483
|
+
align(colon, colon + 1, r, r + 1, 'exact', 2);
|
|
5484
|
+
r += 1;
|
|
5485
|
+
}
|
|
5486
|
+
else
|
|
5487
|
+
drop(colon, colon + 1);
|
|
5488
|
+
align(colon + 1, lastAt, r, r + u.password.length, 'encoded', 2);
|
|
5489
|
+
r += u.password.length;
|
|
5490
|
+
}
|
|
5491
|
+
if (userinfo !== '') {
|
|
5492
|
+
align(lastAt, lastAt + 1, r, r + 1, 'exact', 3);
|
|
5493
|
+
r += 1;
|
|
5494
|
+
}
|
|
5495
|
+
else
|
|
5496
|
+
drop(lastAt, lastAt + 1);
|
|
5497
|
+
k = lastAt + 1;
|
|
5498
|
+
}
|
|
5499
|
+
let portColon = end;
|
|
5500
|
+
let bracket = false;
|
|
5501
|
+
for (let j = k; j < end; j++) {
|
|
5502
|
+
const c = ch(j);
|
|
5503
|
+
if (c === '[')
|
|
5504
|
+
bracket = true;
|
|
5505
|
+
else if (c === ']')
|
|
5506
|
+
bracket = false;
|
|
5507
|
+
else if (c === ':' && !bracket) {
|
|
5508
|
+
portColon = j;
|
|
5509
|
+
break;
|
|
5510
|
+
}
|
|
5511
|
+
}
|
|
5512
|
+
align(k, portColon, r, r + u.hostname.length, 'fold', 4);
|
|
5513
|
+
r += u.hostname.length;
|
|
5514
|
+
if (portColon < end) {
|
|
5515
|
+
if (u.port !== '') {
|
|
5516
|
+
align(portColon, end, r, r + 1 + u.port.length, 'exact', 5);
|
|
5517
|
+
r += 1 + u.port.length;
|
|
5518
|
+
}
|
|
5519
|
+
else
|
|
5520
|
+
drop(portColon, end);
|
|
5521
|
+
}
|
|
5522
|
+
let q = end;
|
|
5523
|
+
if (q < b && (ch(q) === '/' || ch(q) === '\\'))
|
|
5524
|
+
while (q < b && ch(q) !== '?' && ch(q) !== '#')
|
|
5525
|
+
q++;
|
|
5526
|
+
if (q === end)
|
|
5527
|
+
insert(r, r + u.pathname.length);
|
|
5528
|
+
else
|
|
5529
|
+
align(end, q, r, r + u.pathname.length, 'path', 6);
|
|
5530
|
+
r += u.pathname.length;
|
|
5531
|
+
let h = q;
|
|
5532
|
+
if (h < b && ch(h) === '?')
|
|
5533
|
+
while (h < b && ch(h) !== '#')
|
|
5534
|
+
h++;
|
|
5535
|
+
if (h - q === 1)
|
|
5536
|
+
drop(q, h);
|
|
5537
|
+
else if (h > q)
|
|
5538
|
+
align(q, h, r, r + u.search.length, 'encoded', 7);
|
|
5539
|
+
r += u.search.length;
|
|
5540
|
+
if (b - h === 1)
|
|
5541
|
+
drop(h, b);
|
|
5542
|
+
else if (b > h)
|
|
5543
|
+
align(h, b, r, r + u.hash.length, 'encoded', 8);
|
|
5544
|
+
return out;
|
|
5545
|
+
}
|
|
5546
|
+
function restoreAtoms(url, c) {
|
|
5547
|
+
const atoms = [];
|
|
5548
|
+
const marks = markerSpans(url);
|
|
5549
|
+
for (let s = 0; s + 1 < marks.length; s += 2)
|
|
5550
|
+
atoms.push([marks[s] ?? 0, marks[s + 1] ?? 0]);
|
|
5551
|
+
for (let i = url.indexOf('\\'); i >= 0; i = url.indexOf('\\', i + 1)) {
|
|
5552
|
+
const end = outletAtomEnd(url, i);
|
|
5553
|
+
if (end > i)
|
|
5554
|
+
atoms.push([i, end]);
|
|
5555
|
+
}
|
|
5556
|
+
atoms.sort((x, y) => x[0] - y[0]);
|
|
5557
|
+
const [fwdA, fwdB] = c.toRebuilt;
|
|
5558
|
+
const [backA, backB] = c.toOriginal;
|
|
5559
|
+
const swaps = [];
|
|
5560
|
+
for (const [from, to] of atoms) {
|
|
5561
|
+
let ok = (fwdA[from] ?? -1) >= 0;
|
|
5562
|
+
for (let i = from; ok && i + 1 < to; i++)
|
|
5563
|
+
ok = (fwdA[i + 1] ?? -1) >= 0 && fwdB[i] === fwdA[i + 1];
|
|
5564
|
+
if (ok)
|
|
5565
|
+
swaps.push(fwdA[from] ?? 0, fwdB[to - 1] ?? 0, from, to);
|
|
5566
|
+
}
|
|
5567
|
+
if (swaps.length === 0)
|
|
5568
|
+
return c;
|
|
5569
|
+
const old = c.text;
|
|
5570
|
+
const shift = new Int32Array(old.length + 1);
|
|
5571
|
+
let text = '';
|
|
5572
|
+
const nA = [];
|
|
5573
|
+
const nB = [];
|
|
5574
|
+
let w = 0;
|
|
5575
|
+
for (let k = 0; k <= old.length;) {
|
|
5576
|
+
if (w < swaps.length && k === swaps[w]) {
|
|
5577
|
+
const rTo = swaps[w + 1] ?? k;
|
|
5578
|
+
const from = swaps[w + 2] ?? 0;
|
|
5579
|
+
const to = swaps[w + 3] ?? 0;
|
|
5580
|
+
shift[k] = text.length;
|
|
5581
|
+
for (let j = k + 1; j < rTo; j++)
|
|
5582
|
+
shift[j] = -1;
|
|
5583
|
+
for (let i = from; i < to; i++) {
|
|
5584
|
+
fwdA[i] = text.length;
|
|
5585
|
+
fwdB[i] = text.length + 1;
|
|
5586
|
+
nA.push(i);
|
|
5587
|
+
nB.push(i + 1);
|
|
5588
|
+
text += url.charAt(i);
|
|
5589
|
+
}
|
|
5590
|
+
w += 4;
|
|
5591
|
+
k = rTo;
|
|
5592
|
+
continue;
|
|
5593
|
+
}
|
|
5594
|
+
shift[k] = text.length;
|
|
5595
|
+
if (k < old.length) {
|
|
5596
|
+
nA.push(backA[k] ?? -2);
|
|
5597
|
+
nB.push(backB[k] ?? -2);
|
|
5598
|
+
text += old.charAt(k);
|
|
5599
|
+
}
|
|
5600
|
+
k++;
|
|
5601
|
+
}
|
|
5602
|
+
const inSwap = new Uint8Array(url.length);
|
|
5603
|
+
for (let s = 0; s < swaps.length; s += 4)
|
|
5604
|
+
inSwap.fill(1, swaps[s + 2], swaps[s + 3]);
|
|
5605
|
+
for (let i = 0; i < url.length; i++) {
|
|
5606
|
+
if (inSwap[i] === 1 || (fwdA[i] ?? -2) < 0)
|
|
5607
|
+
continue;
|
|
5608
|
+
fwdA[i] = shift[fwdA[i] ?? 0] ?? -2;
|
|
5609
|
+
fwdB[i] = shift[fwdB[i] ?? 0] ?? -2;
|
|
5610
|
+
}
|
|
5611
|
+
return { text, toRebuilt: [fwdA, fwdB], toOriginal: [Int32Array.from(nA), Int32Array.from(nB)], parts: c.parts };
|
|
5612
|
+
}
|
|
5613
|
+
function hiddenUnits(text, queryByShape) {
|
|
5614
|
+
const r = scanAndMask(text, scanOptionsOf({}, publicFace({}), queryByShape, true), true);
|
|
5615
|
+
const hidden = new Uint8Array(text.length);
|
|
5616
|
+
if (r.text !== text) {
|
|
5617
|
+
if (r.origin === undefined)
|
|
5618
|
+
return null;
|
|
5619
|
+
hidden.fill(1);
|
|
5620
|
+
for (const k of r.origin)
|
|
5621
|
+
if (k >= 0)
|
|
5622
|
+
hidden[k] = 0;
|
|
5623
|
+
}
|
|
5624
|
+
return hidden;
|
|
5625
|
+
}
|
|
5626
|
+
function coversHidden(from, to, image) {
|
|
5627
|
+
for (let i = 0; i < from.length; i++) {
|
|
5628
|
+
if (from[i] !== 1)
|
|
5629
|
+
continue;
|
|
5630
|
+
const s = image[0][i] ?? -2;
|
|
5631
|
+
if (s === -1)
|
|
5632
|
+
continue;
|
|
5633
|
+
if (s === -2)
|
|
5634
|
+
return false;
|
|
5635
|
+
for (let k = s; k < (image[1][i] ?? s); k++)
|
|
5636
|
+
if (to[k] !== 1)
|
|
5637
|
+
return false;
|
|
5638
|
+
}
|
|
5639
|
+
return true;
|
|
5640
|
+
}
|
|
5641
|
+
const CONFIGURED_ADDRESS_WITHHELD = Object.freeze({ withheld: true });
|
|
5642
|
+
function rebuiltKeepsRuns(url, rebuilt, hidden) {
|
|
5643
|
+
const [fwdA, fwdB] = rebuilt.toRebuilt;
|
|
5644
|
+
for (let i = 0; i < url.length;) {
|
|
5645
|
+
if (hidden[i] !== 1) {
|
|
5646
|
+
i++;
|
|
5647
|
+
continue;
|
|
5648
|
+
}
|
|
5649
|
+
let j = i;
|
|
5650
|
+
while (j < url.length && hidden[j] === 1)
|
|
5651
|
+
j++;
|
|
5652
|
+
const s = fwdA[i] ?? -2;
|
|
5653
|
+
const t = fwdB[j - 1] ?? -2;
|
|
5654
|
+
if (s < 0 || t - s !== j - i || rebuilt.text.slice(s, t) !== url.slice(i, j))
|
|
5655
|
+
return false;
|
|
5656
|
+
for (let k = i; k < j; k++)
|
|
5657
|
+
if (rebuilt.parts[k] !== rebuilt.parts[i])
|
|
5658
|
+
return false;
|
|
5659
|
+
i = j;
|
|
5660
|
+
}
|
|
5661
|
+
return true;
|
|
5662
|
+
}
|
|
5663
|
+
function readingOf(url, rebuilt, n1) {
|
|
5664
|
+
if (!n1) {
|
|
5665
|
+
const hidden = hiddenUnits(url, 'strict');
|
|
5666
|
+
return hidden !== null && rebuiltKeepsRuns(url, rebuilt, hidden) ? { withheld: false, text: rebuilt.text } : CONFIGURED_ADDRESS_WITHHELD;
|
|
5667
|
+
}
|
|
5668
|
+
let original = true;
|
|
5669
|
+
let viaRebuilt = true;
|
|
5670
|
+
for (const mode of ['relaxed', 'strict']) {
|
|
5671
|
+
const hiddenOriginal = hiddenUnits(url, mode);
|
|
5672
|
+
const hiddenRebuilt = hiddenUnits(rebuilt.text, mode);
|
|
5673
|
+
if (hiddenOriginal === null || hiddenRebuilt === null)
|
|
5674
|
+
return CONFIGURED_ADDRESS_WITHHELD;
|
|
5675
|
+
original &&= coversHidden(hiddenRebuilt, hiddenOriginal, rebuilt.toOriginal);
|
|
5676
|
+
viaRebuilt &&= coversHidden(hiddenOriginal, hiddenRebuilt, rebuilt.toRebuilt);
|
|
5677
|
+
}
|
|
5678
|
+
return original ? { withheld: false, text: url } : viaRebuilt ? { withheld: false, text: rebuilt.text } : CONFIGURED_ADDRESS_WITHHELD;
|
|
5679
|
+
}
|
|
5680
|
+
function configuredAddressReading(url) {
|
|
5681
|
+
const view = parserViewOf(url);
|
|
5682
|
+
if (userinfoBoundaryAmbiguous(view))
|
|
5683
|
+
return CONFIGURED_ADDRESS_WITHHELD;
|
|
5684
|
+
if (!/^[A-Za-z][A-Za-z0-9+.-]*:/.test(view))
|
|
5685
|
+
return { withheld: false, text: url };
|
|
5686
|
+
if (/^[A-Za-z][A-Za-z0-9+.-]*:\/\//.test(view)) {
|
|
5687
|
+
const n1 = authorityHasBackslash(view) ? rebuiltAddress(url) : null;
|
|
5688
|
+
return n1 === null ? { withheld: false, text: url } : readingOf(url, restoreAtoms(url, n1), true);
|
|
5689
|
+
}
|
|
5690
|
+
const rebuilt = rebuiltAddress(url);
|
|
5691
|
+
if (rebuilt !== null)
|
|
5692
|
+
return readingOf(url, rebuilt, false);
|
|
5693
|
+
if (/^(?:https?|wss?|ftp):/i.test(view) && /[@?#]/.test(view) && !schemelessUserinfoLeads(view))
|
|
5694
|
+
return CONFIGURED_ADDRESS_WITHHELD;
|
|
5695
|
+
return { withheld: false, text: url };
|
|
5696
|
+
}
|
|
5697
|
+
function schemelessUserinfoLeads(view) {
|
|
5698
|
+
const V = buildScanView(view, WASH_OPTIONS);
|
|
5699
|
+
const S = separatedSurface(view, V);
|
|
5700
|
+
const out = [];
|
|
5701
|
+
userinfoCandidates(S, out, true);
|
|
5702
|
+
return out.some((c) => c.kind === 'userinfo' && S.rawStart(c.a) === 0);
|
|
5703
|
+
}
|
|
5704
|
+
export function configuredValueCarriesCredential(value) {
|
|
5705
|
+
if (typeof value !== 'string' || value.length === 0)
|
|
5706
|
+
return false;
|
|
5707
|
+
const reading = configuredAddressReading(value);
|
|
5708
|
+
if (reading.withheld)
|
|
5709
|
+
return true;
|
|
5710
|
+
const text = reading.text;
|
|
5711
|
+
const r = scanAndMask(text, scanOptionsOf({}, publicFace({}), 'strict', true), true);
|
|
5712
|
+
const origin = r.origin;
|
|
5713
|
+
if (r.text === text || origin === undefined)
|
|
5714
|
+
return false;
|
|
5715
|
+
let prevVisible = -1;
|
|
5716
|
+
for (let k = 0; k < r.text.length;) {
|
|
5717
|
+
const o = origin[k] ?? -1;
|
|
5718
|
+
if (o >= 0) {
|
|
5719
|
+
prevVisible = o;
|
|
5720
|
+
k++;
|
|
5721
|
+
continue;
|
|
5722
|
+
}
|
|
5723
|
+
let e = k;
|
|
5724
|
+
while (e < r.text.length && (origin[e] ?? -1) < 0)
|
|
5725
|
+
e++;
|
|
5726
|
+
const hiddenStart = prevVisible + 1;
|
|
5727
|
+
const hiddenEnd = e < r.text.length ? (origin[e] ?? text.length) : text.length;
|
|
5728
|
+
const shown = r.text.slice(k, e);
|
|
5729
|
+
const re = new RegExp(MARKER_ANYWHERE.source, MARKER_ANYWHERE.flags);
|
|
5730
|
+
for (let m = re.exec(shown); m !== null; m = re.exec(shown)) {
|
|
5731
|
+
const kind = KIND_BY_TEXT.get(m[0]);
|
|
5732
|
+
if (kind === 'userinfo' || kind === 'query' || kind === 'secret')
|
|
5733
|
+
return true;
|
|
5734
|
+
if (kind === 'fragment') {
|
|
5735
|
+
if (hiddenStart === 0 || text[hiddenStart - 1] !== '#')
|
|
5736
|
+
return true;
|
|
5737
|
+
if (queryCarriesCredential(text.slice(hiddenStart, hiddenEnd)))
|
|
5738
|
+
return true;
|
|
5739
|
+
}
|
|
5740
|
+
}
|
|
5741
|
+
k = e;
|
|
5742
|
+
}
|
|
5743
|
+
return false;
|
|
5071
5744
|
}
|
|
5072
5745
|
export function quoteUntrustedForSentence(text, max) {
|
|
5073
5746
|
const shown = displayUntrusted(text, { max, blanks: true, escapeBackslashes: true });
|