@sema-agent/client-core 0.95.0 → 0.96.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 +180 -0
- package/README.md +49 -25
- package/dist/adapt/arms.js +7 -1
- package/dist/adapt/toolCards.d.ts +1 -0
- package/dist/adapt/toolCards.js +16 -3
- package/dist/adapt/wireShapes.js +2 -16
- package/dist/adapter/activeRunSelfHeal.d.ts +4 -0
- package/dist/adapter/activeRunSelfHeal.js +30 -4
- package/dist/adapter/downstream/eventToSdkMessage.js +11 -3
- package/dist/adapter/downstream/terminalToSdkResult.js +8 -9
- package/dist/adapter/runStream.js +30 -47
- package/dist/agentSession/engineAgentRegistryRead.js +5 -4
- package/dist/agentSession/engineSessionBackgroundStop.js +22 -15
- package/dist/agentsWireCaps.js +13 -2
- package/dist/callerCwdCapability.d.ts +0 -4
- package/dist/callerCwdCapability.js +0 -6
- package/dist/centerWiringCapability.d.ts +0 -4
- package/dist/centerWiringCapability.js +0 -6
- package/dist/classifierVerdictWire.js +4 -16
- package/dist/clientSlice.js +1 -1
- package/dist/cloudConfigWireCaps.d.ts +3 -3
- package/dist/cloudConfigWireCaps.js +65 -45
- package/dist/coreValuePorts.d.ts +11 -7
- package/dist/coreValuePorts.js +5 -8
- package/dist/credentialProvenance.d.ts +28 -0
- package/dist/credentialProvenance.js +76 -0
- package/dist/decideFailureNote.js +3 -1
- package/dist/decideReceipt.js +7 -10
- package/dist/declared/supportFloorDeclared.js +3 -3
- package/dist/displayUntrusted.d.ts +7 -3
- package/dist/displayUntrusted.js +356 -60
- package/dist/engineCapsCache.d.ts +6 -0
- package/dist/engineCapsCache.js +226 -32
- package/dist/engineDirectory.d.ts +79 -0
- package/dist/engineDirectory.js +186 -0
- package/dist/engineErrorCodes.d.ts +14 -1
- package/dist/engineErrorCodes.js +37 -15
- package/dist/engineNoticeCodes.d.ts +17 -0
- package/dist/engineNoticeCodes.js +57 -3
- package/dist/engineWordRenames.d.ts +8 -0
- package/dist/engineWordRenames.js +13 -0
- package/dist/fileHistoryCaptureCapability.d.ts +14 -0
- package/dist/fileHistoryCaptureCapability.js +70 -0
- package/dist/fleet/bgSubagentRow.d.ts +15 -0
- package/dist/fleet/bgSubagentRow.js +67 -0
- package/dist/fleet/fleetLedger.d.ts +1 -0
- package/dist/fleet/fleetLedger.js +103 -89
- package/dist/fleet/fleetProjection.d.ts +1 -0
- package/dist/fleet/fleetProjection.js +10 -3
- package/dist/fleetTaskDesc.js +4 -17
- package/dist/gateOutcome.d.ts +12 -1
- package/dist/gateOutcome.js +58 -1
- package/dist/gateVocabulary.d.ts +7 -2
- package/dist/gateVocabulary.js +5 -1
- package/dist/generated/engineAskFaceTables.js +1 -1
- package/dist/generated/engineFactTables.d.ts +5 -3
- package/dist/generated/engineFactTables.js +17 -4
- package/dist/generated/engineNoticeTables.js +12 -3
- package/dist/generated/settingsSchemaFns.d.ts +3 -0
- package/dist/generated/settingsSchemaFns.js +53 -0
- package/dist/generated/toolNameTables.d.ts +1 -0
- package/dist/generated/toolNameTables.js +1 -0
- package/dist/hitl/approvalEndingCause.d.ts +2 -0
- package/dist/hitl/approvalEndingCause.js +31 -21
- package/dist/hitl/askParkRowRouting.js +1 -1
- package/dist/hitl/automaticAnswerVerdict.d.ts +1 -1
- package/dist/hitl/automaticAnswerVerdict.js +13 -8
- package/dist/hitl/decisionConflict.js +0 -2
- package/dist/hitl/decisionRefusal.d.ts +8 -0
- package/dist/hitl/decisionRefusal.js +48 -14
- package/dist/hitl/frameRouter.d.ts +0 -1
- package/dist/hitl/frameRouter.js +6 -14
- package/dist/hitl/hitlBridge.d.ts +6 -2
- package/dist/hitl/hitlBridge.js +28 -7
- package/dist/hitl/localAllowRule.d.ts +1 -0
- package/dist/hitl/localAllowRule.js +2 -2
- package/dist/hitl/noAllowCause.d.ts +2 -1
- package/dist/hitl/noAllowCause.js +3 -2
- package/dist/hitl/parkResolver.d.ts +1 -0
- package/dist/hitl/parkResolver.js +8 -54
- package/dist/hitl/parkRowBirthWait.d.ts +1 -0
- package/dist/hitl/parkRowBirthWait.js +2 -2
- package/dist/hitl/planReviewWire.d.ts +1 -0
- package/dist/hitl/planReviewWire.js +98 -20
- package/dist/hitl/replayedApproval.d.ts +1 -0
- package/dist/hitl/replayedApproval.js +14 -0
- package/dist/hitl/sessionPolicyDeliverable.js +8 -6
- package/dist/hitl/standingRows.d.ts +18 -0
- package/dist/hitl/standingRows.js +127 -0
- package/dist/hitl/toolApprovalWire.d.ts +15 -3
- package/dist/hitl/toolApprovalWire.js +238 -25
- package/dist/hookMatcherVerdict.d.ts +1 -9
- package/dist/hookMatcherVerdict.js +21 -61
- package/dist/hooksWireCaps.js +2 -2
- package/dist/hostCallbackGuard.d.ts +1 -0
- package/dist/hostCallbackGuard.js +6 -0
- package/dist/index.d.ts +21 -9
- package/dist/index.js +17 -8
- package/dist/mcpProbeCapability.d.ts +0 -4
- package/dist/mcpProbeCapability.js +0 -6
- package/dist/memoryComplianceCapability.d.ts +0 -4
- package/dist/memoryComplianceCapability.js +0 -6
- package/dist/memoryOriginCapability.d.ts +0 -4
- package/dist/memoryOriginCapability.js +0 -6
- package/dist/memorySpecWire.js +2 -2
- package/dist/modelIdentityResolvability.d.ts +2 -2
- package/dist/modelIdentityResolvability.js +12 -3
- package/dist/noticePresentation.d.ts +68 -0
- package/dist/noticePresentation.js +247 -0
- package/dist/outputStyleSeatCapability.d.ts +0 -1
- package/dist/outputStyleSeatCapability.js +0 -3
- package/dist/ownKey.d.ts +2 -0
- package/dist/ownKey.js +18 -0
- package/dist/peerFrames.js +13 -9
- package/dist/permissionModesCapability.d.ts +0 -4
- package/dist/permissionModesCapability.js +0 -6
- package/dist/permissionRuleIssue.js +0 -3
- package/dist/permissionsDeclaration.d.ts +2 -0
- package/dist/permissionsDeclaration.js +56 -41
- package/dist/printPermissionOutcome.d.ts +12 -0
- package/dist/printPermissionOutcome.js +50 -0
- package/dist/printToolResultFrame.d.ts +1 -0
- package/dist/printToolResultFrame.js +5 -9
- package/dist/promptBlockedCopy.d.ts +5 -0
- package/dist/promptBlockedCopy.js +35 -0
- package/dist/protocolSegment.d.ts +9 -6
- package/dist/protocolSegment.js +31 -21
- package/dist/providerRejection.d.ts +4 -0
- package/dist/providerRejection.js +75 -14
- package/dist/replayBody.d.ts +12 -0
- package/dist/replayBody.js +25 -0
- package/dist/resumeRefusalCopy.js +5 -21
- package/dist/retryStatus.d.ts +9 -1
- package/dist/retryStatus.js +9 -2
- package/dist/rewindArchiveCapability.d.ts +3 -13
- package/dist/rewindArchiveCapability.js +7 -58
- package/dist/rewindArchiveReading.d.ts +15 -0
- package/dist/rewindArchiveReading.js +56 -0
- package/dist/ruleCompileVerdict.d.ts +9 -1
- package/dist/ruleCompileVerdict.js +29 -4
- package/dist/runTerminal.d.ts +15 -1
- package/dist/runTerminal.js +56 -8
- package/dist/sandboxWire.js +13 -0
- package/dist/sealedKeyCapability.d.ts +0 -4
- package/dist/sealedKeyCapability.js +0 -6
- package/dist/seam.js +4 -2
- package/dist/seatContract.d.ts +73 -11
- package/dist/seatContract.js +195 -14
- package/dist/sessionReadRedactionCapability.d.ts +0 -4
- package/dist/sessionReadRedactionCapability.js +0 -6
- package/dist/sessionSyncFileHistory.d.ts +4 -0
- package/dist/sessionSyncFileHistory.js +66 -0
- package/dist/sessionSyncFileHistoryCapability.d.ts +0 -1
- package/dist/sessionSyncFileHistoryCapability.js +0 -3
- package/dist/skillToolListsCapability.d.ts +0 -1
- package/dist/skillToolListsCapability.js +0 -3
- package/dist/steerPriorityCapability.d.ts +0 -1
- package/dist/steerPriorityCapability.js +0 -3
- package/dist/storePostureCapability.d.ts +0 -4
- package/dist/storePostureCapability.js +0 -6
- package/dist/subagent/engineSubagentSteer.js +7 -5
- package/dist/subagentContentStore.js +4 -15
- package/dist/supportFloor.d.ts +1 -0
- package/dist/supportFloor.js +13 -9
- package/dist/systemReminderTag.d.ts +1 -0
- package/dist/systemReminderTag.js +8 -0
- package/dist/toolHistoryMismatch.d.ts +0 -3
- package/dist/toolHistoryMismatch.js +3 -7
- package/dist/toolResult.d.ts +1 -1
- package/dist/toolResult.js +2 -1
- package/dist/toolResultContent.d.ts +2 -0
- package/dist/toolResultContent.js +63 -0
- package/dist/usageAnalytics.js +40 -16
- package/dist/userInvokedSkillsCapability.d.ts +0 -1
- package/dist/userInvokedSkillsCapability.js +0 -3
- package/dist/wireErrorTriage.d.ts +2 -0
- package/dist/wireErrorTriage.js +34 -5
- package/dist/wireFailureShape.d.ts +19 -2
- package/dist/wireFailureShape.js +55 -50
- package/docs/INTEGRATION-CLIENTS.md +1262 -17
- package/package.json +5 -5
package/dist/displayUntrusted.js
CHANGED
|
@@ -38,18 +38,12 @@ function scanForSecretLiteral(s) {
|
|
|
38
38
|
return true;
|
|
39
39
|
return s.split(/[^A-Za-z0-9_-]+/).some(tok => tok !== '' && SECRET_LITERAL_RE.test(tok));
|
|
40
40
|
}
|
|
41
|
-
function decodeAsciiEscapes(s) {
|
|
42
|
-
return s.replace(/%[0-9A-Fa-f]{2}/g, esc => {
|
|
43
|
-
const code = Number.parseInt(esc.slice(1), 16);
|
|
44
|
-
return code < 0x80 ? String.fromCharCode(code) : esc;
|
|
45
|
-
});
|
|
46
|
-
}
|
|
47
41
|
export function looksLikeKnownSecret(s) {
|
|
48
42
|
let cur = s;
|
|
49
43
|
for (let layer = 0; layer < 4; layer++) {
|
|
50
44
|
if (scanForSecretLiteral(cur))
|
|
51
45
|
return true;
|
|
52
|
-
const next =
|
|
46
|
+
const next = percentDecodeForMatching(cur).decoded;
|
|
53
47
|
if (next === cur)
|
|
54
48
|
break;
|
|
55
49
|
cur = next;
|
|
@@ -261,21 +255,23 @@ function isKnownSecretSegment(value) {
|
|
|
261
255
|
const core = valueCoreForJudging(value);
|
|
262
256
|
return core.length > 0 && looksLikeKnownSecret(core);
|
|
263
257
|
}
|
|
264
|
-
function queryCandidates(src, base, query, edits, soft) {
|
|
258
|
+
function queryCandidates(src, base, query, edits, soft, byShape = false) {
|
|
259
|
+
const relax = byShape === 'relaxed';
|
|
265
260
|
let pos = base;
|
|
266
261
|
for (const part of query.split('&')) {
|
|
267
262
|
if (part.length > 0) {
|
|
268
263
|
const eq = part.indexOf('=');
|
|
269
264
|
if (eq === -1) {
|
|
270
265
|
const at = pos;
|
|
271
|
-
if (!slotIsWholeMarker(part, soft && ((i) => soft(at + i))))
|
|
266
|
+
if (!slotIsWholeMarker(part, soft && ((i) => soft(at + i))) && (!byShape || queryPartCarriesCredential(null, part, relax)))
|
|
272
267
|
pushEdit(edits, src, pos, pos + part.length, REDACTED_QUERY);
|
|
273
268
|
}
|
|
274
269
|
else {
|
|
275
270
|
const value = part.slice(eq + 1);
|
|
276
271
|
const at = pos + eq + 1;
|
|
277
|
-
if (value.length > 0 && !slotIsWholeMarker(value, soft && ((i) => soft(at + i))))
|
|
272
|
+
if (value.length > 0 && !slotIsWholeMarker(value, soft && ((i) => soft(at + i))) && (!byShape || queryPartCarriesCredential(part.slice(0, eq), value, relax))) {
|
|
278
273
|
pushEdit(edits, src, pos + eq + 1, pos + part.length, REDACTED_QUERY);
|
|
274
|
+
}
|
|
279
275
|
}
|
|
280
276
|
}
|
|
281
277
|
pos += part.length + 1;
|
|
@@ -319,7 +315,7 @@ function pathCandidates(src, base, path, opaqueSegments, edits, soft) {
|
|
|
319
315
|
pos += segment.length + 1;
|
|
320
316
|
}
|
|
321
317
|
}
|
|
322
|
-
function urlTokenCandidatesAt(src, start, end, edits, soft) {
|
|
318
|
+
function urlTokenCandidatesAt(src, start, end, edits, soft, byShape = false) {
|
|
323
319
|
const token = src.slice(start, end);
|
|
324
320
|
const schemeEnd = token.indexOf('://') + '://'.length;
|
|
325
321
|
const scheme = token.slice(0, schemeEnd);
|
|
@@ -337,7 +333,7 @@ function urlTokenCandidatesAt(src, start, end, edits, soft) {
|
|
|
337
333
|
const path = question === -1 ? beforeHash : beforeHash.slice(0, question);
|
|
338
334
|
pathCandidates(src, start + tailStart, path, !/^file:/i.test(scheme), edits, soft);
|
|
339
335
|
if (question !== -1)
|
|
340
|
-
queryCandidates(src, start + tailStart + question + 1, beforeHash.slice(question + 1), edits, soft);
|
|
336
|
+
queryCandidates(src, start + tailStart + question + 1, beforeHash.slice(question + 1), edits, soft, byShape);
|
|
341
337
|
if (hash !== -1) {
|
|
342
338
|
const rawFragment = tail.slice(hash + 1);
|
|
343
339
|
const at = start + tailStart + hash + 1;
|
|
@@ -345,7 +341,7 @@ function urlTokenCandidatesAt(src, start, end, edits, soft) {
|
|
|
345
341
|
pushEdit(edits, src, start + tailStart + hash + 1, end, REDACTED_FRAGMENT);
|
|
346
342
|
}
|
|
347
343
|
}
|
|
348
|
-
function urlCandidates(text, out, regions, unitPos = () => false, soft) {
|
|
344
|
+
function urlCandidates(text, out, regions, unitPos = () => false, soft, byShape = false) {
|
|
349
345
|
let mark = text.indexOf('://');
|
|
350
346
|
while (mark !== -1) {
|
|
351
347
|
let start = mark;
|
|
@@ -410,7 +406,7 @@ function urlCandidates(text, out, regions, unitPos = () => false, soft) {
|
|
|
410
406
|
end = runStart;
|
|
411
407
|
end = trailingCloserEnd(text, start, mark + 3, end, unitPos);
|
|
412
408
|
const tokenEdits = [];
|
|
413
|
-
urlTokenCandidatesAt(text, start, end, tokenEdits, soft);
|
|
409
|
+
urlTokenCandidatesAt(text, start, end, tokenEdits, soft, byShape);
|
|
414
410
|
editsToCandidates(tokenEdits, 'url', out, end);
|
|
415
411
|
const token = text.slice(start, end);
|
|
416
412
|
const schemeEnd = token.indexOf('://') + 3;
|
|
@@ -813,8 +809,16 @@ const isNameBlank = (cp) => NAME_BLANK_RANGES.some(([a, b]) => cp >= a && cp <=
|
|
|
813
809
|
const isFoldedBlank = (c) => isNameBlank(c) && isRegexSpace(c);
|
|
814
810
|
const isUnicodeBlank = (c) => (c > 0x7f && isRegexSpace(c) && c !== 0xfeff) || isNameBlank(c);
|
|
815
811
|
const SECRET_SCHEME_WORD = /(?:\b|(?<=\\[A-Za-z"']))(?!(?<=(?<!\\)\\)(?:bearer|basic|token)\\t)(bearer|basic|token)(?:[\s:=\uFF1A\uFF1D\u115F\u1160\u3164\uFFA0\u2800]|\\t)+/gi;
|
|
812
|
+
const SCHEME_LEFT_STRICT = String.raw `(?:\b|(?<=\\[A-Za-z"']))`;
|
|
813
|
+
const SCHEME_WORD_OUTER_SOURCE = widenLeft(SECRET_SCHEME_WORD.source, SCHEME_LEFT_STRICT, String.raw `(?=bearer|basic)`);
|
|
816
814
|
const schemeWordIsInner = (word, tokenInner) => tokenInner || word.toLowerCase() !== 'token';
|
|
817
815
|
const SECRET_LABELLED_WORD = /(?:\b|(?<=_)|(?<=\\[A-Za-z"']))((?:access[-_ ]?|refresh[-_ ]?|id[-_ ]?|client[-_ ]?|api[-_ ]?|x[-_]api[-_ ]?|session[-_ ]?|auth[-_ ]?)?(?:token|key|secret|password|authorization|credential)s?)(?:\\*["'])?(?:[\s\u115F\u1160\u3164\uFFA0\u2800]|\\t)*[:=\uFF1A\uFF1D](?:[\s\u115F\u1160\u3164\uFFA0\u2800]|\\t)*/gi;
|
|
816
|
+
function widenLeft(source, strictLeft, extraAlt) {
|
|
817
|
+
return source.startsWith(strictLeft) ? `${strictLeft.slice(0, -1)}|${extraAlt})${source.slice(strictLeft.length)}` : source;
|
|
818
|
+
}
|
|
819
|
+
const LABEL_LEFT_STRICT = String.raw `(?:\b|(?<=_)|(?<=\\[A-Za-z"']))`;
|
|
820
|
+
const LABEL_LEFT_GLUED_ALT = String.raw `(?<=[A-Za-z0-9])(?:(?=(?:token|secret|password|authorization|credential)(?!s))|(?<!mon|tur|don|hoc|whis|joc|lac)(?=key(?!s)))`;
|
|
821
|
+
const LABEL_WORD_OUTER_SOURCE = widenLeft(SECRET_LABELLED_WORD.source, LABEL_LEFT_STRICT, LABEL_LEFT_GLUED_ALT);
|
|
818
822
|
const DIAGNOSTIC_VALUE_WORDS = new Set([
|
|
819
823
|
'missing', 'required', 'invalid', 'expired', 'unauthorized', 'unauthenticated', 'authentication',
|
|
820
824
|
'authorization', 'credential', 'credentials', 'unsupported', 'malformed', 'rejected', 'absent',
|
|
@@ -1266,6 +1270,35 @@ function percentDecodeForMatching(text) {
|
|
|
1266
1270
|
offsets.push(text.length);
|
|
1267
1271
|
return { decoded: chars.join(''), offsets };
|
|
1268
1272
|
}
|
|
1273
|
+
const PERCENT_ESCAPE_ASCII = /%[0-7][0-9A-Fa-f]/;
|
|
1274
|
+
const MAX_PERCENT_VIEW_LAYERS = 3;
|
|
1275
|
+
function percentLayers(text) {
|
|
1276
|
+
const out = [];
|
|
1277
|
+
if (!PERCENT_ESCAPE_ASCII.test(text))
|
|
1278
|
+
return out;
|
|
1279
|
+
let cur = text;
|
|
1280
|
+
let ps = null;
|
|
1281
|
+
let pe = null;
|
|
1282
|
+
for (let layer = 0; layer < MAX_PERCENT_VIEW_LAYERS; layer++) {
|
|
1283
|
+
const { decoded, offsets } = percentDecodeForMatching(cur);
|
|
1284
|
+
if (decoded === cur)
|
|
1285
|
+
break;
|
|
1286
|
+
const n = decoded.length;
|
|
1287
|
+
const nps = new Int32Array(n);
|
|
1288
|
+
const npe = new Int32Array(n);
|
|
1289
|
+
for (let k = 0; k < n; k++) {
|
|
1290
|
+
const a = offsets[k] ?? 0;
|
|
1291
|
+
const b = offsets[k + 1] ?? cur.length;
|
|
1292
|
+
nps[k] = ps === null ? a : (ps[a] ?? 0);
|
|
1293
|
+
npe[k] = pe === null ? b : (pe[b - 1] ?? 0);
|
|
1294
|
+
}
|
|
1295
|
+
out.push({ text: decoded, ps: nps, pe: npe });
|
|
1296
|
+
cur = decoded;
|
|
1297
|
+
ps = nps;
|
|
1298
|
+
pe = npe;
|
|
1299
|
+
}
|
|
1300
|
+
return out;
|
|
1301
|
+
}
|
|
1269
1302
|
function firstLabelCutInPath(path) {
|
|
1270
1303
|
let cut = -1;
|
|
1271
1304
|
for (const src of [SECRET_SCHEME_WORD, SECRET_LABELLED_WORD]) {
|
|
@@ -1520,7 +1553,7 @@ function quoteSwallowedBefore(S, at) {
|
|
|
1520
1553
|
return '';
|
|
1521
1554
|
return dq === -1 ? "'" : sq === -1 ? '"' : dq < sq ? '"' : "'";
|
|
1522
1555
|
}
|
|
1523
|
-
function labelEndsAt(text, end, atLeast = false) {
|
|
1556
|
+
function labelEndsAt(text, end, atLeast = false, outer = false) {
|
|
1524
1557
|
let k = end;
|
|
1525
1558
|
while (k > 0 && isRegexSpace(text.charCodeAt(k - 1)))
|
|
1526
1559
|
k--;
|
|
@@ -1539,7 +1572,7 @@ function labelEndsAt(text, end, atLeast = false) {
|
|
|
1539
1572
|
let w = k;
|
|
1540
1573
|
while (w > 0 && isIdentOrDash(text.charAt(w - 1)))
|
|
1541
1574
|
w--;
|
|
1542
|
-
const re = new RegExp(SECRET_LABELLED_WORD.source, 'iy');
|
|
1575
|
+
const re = new RegExp(outer ? LABEL_WORD_OUTER_SOURCE : SECRET_LABELLED_WORD.source, 'iy');
|
|
1543
1576
|
for (let s = w; s < k; s++) {
|
|
1544
1577
|
re.lastIndex = s;
|
|
1545
1578
|
const m = re.exec(text);
|
|
@@ -2097,10 +2130,12 @@ function blockSeqEntries(scan, rawFrom, rawLabel) {
|
|
|
2097
2130
|
}
|
|
2098
2131
|
return out;
|
|
2099
2132
|
}
|
|
2100
|
-
function labelCandidates(S, pass, regions, out, blocks) {
|
|
2133
|
+
function labelCandidates(S, pass, regions, out, blocks, outer = false) {
|
|
2101
2134
|
const text = S.text;
|
|
2102
2135
|
const unitAt = (k) => S.unitAt(k);
|
|
2103
|
-
const
|
|
2136
|
+
const strictSource = pass === 'label' ? SECRET_LABELLED_WORD.source : SECRET_SCHEME_WORD.source;
|
|
2137
|
+
const re = new RegExp(outer ? (pass === 'label' ? LABEL_WORD_OUTER_SOURCE : SCHEME_WORD_OUTER_SOURCE) : strictSource, 'gi');
|
|
2138
|
+
const strictAt = outer ? new RegExp(strictSource, 'iy') : null;
|
|
2104
2139
|
const schemePass = pass !== 'label';
|
|
2105
2140
|
const tokenPass = pass === 'token';
|
|
2106
2141
|
const rawUnitMap = blocks?.view.rawUnit ?? null;
|
|
@@ -2346,6 +2381,11 @@ function labelCandidates(S, pass, regions, out, blocks) {
|
|
|
2346
2381
|
re.lastIndex = 0;
|
|
2347
2382
|
hits: for (let hit = re.exec(text); hit !== null; hit = re.exec(text)) {
|
|
2348
2383
|
const label = hit[1] ?? '';
|
|
2384
|
+
if (strictAt !== null) {
|
|
2385
|
+
strictAt.lastIndex = hit.index;
|
|
2386
|
+
if (strictAt.test(text))
|
|
2387
|
+
continue;
|
|
2388
|
+
}
|
|
2349
2389
|
if (hit.index >= skipFrom && hit.index < skipTo)
|
|
2350
2390
|
continue;
|
|
2351
2391
|
if (schemePass && (label.toLowerCase() === 'token') !== tokenPass)
|
|
@@ -2665,6 +2705,19 @@ function isAuthScheme(value) {
|
|
|
2665
2705
|
const isKeyWordChar = (c) => (c >= 0x30 && c <= 0x39) || (c >= 0x41 && c <= 0x5a) || (c >= 0x61 && c <= 0x7a) || c === 0x5f || c === 0x2d;
|
|
2666
2706
|
const isAlnumCode = (c) => (c >= 0x30 && c <= 0x39) || (c >= 0x41 && c <= 0x5a) || (c >= 0x61 && c <= 0x7a);
|
|
2667
2707
|
const PROSE_LITERAL_MIN_RUN = 16;
|
|
2708
|
+
const PROSE_SHORT_LITERAL_MIN_TAIL = 6;
|
|
2709
|
+
function shortLiteralTail(word) {
|
|
2710
|
+
const lit = SECRET_LITERAL_RE.exec(word);
|
|
2711
|
+
if (lit === null)
|
|
2712
|
+
return false;
|
|
2713
|
+
const tail = word.slice(lit[0].length);
|
|
2714
|
+
if (tail.length < PROSE_SHORT_LITERAL_MIN_TAIL)
|
|
2715
|
+
return false;
|
|
2716
|
+
for (let k = 0; k < tail.length; k++)
|
|
2717
|
+
if (!isAlnumCode(tail.charCodeAt(k)))
|
|
2718
|
+
return false;
|
|
2719
|
+
return true;
|
|
2720
|
+
}
|
|
2668
2721
|
function longestAlnumRun(word) {
|
|
2669
2722
|
let best = 0;
|
|
2670
2723
|
let run = 0;
|
|
@@ -3009,7 +3062,7 @@ function authParamEdits(text) {
|
|
|
3009
3062
|
const LITERAL_PROBE_WINDOW = 64;
|
|
3010
3063
|
function gluedLiteralStart(word) {
|
|
3011
3064
|
const n = word.length;
|
|
3012
|
-
if (n <
|
|
3065
|
+
if (n < PROSE_SHORT_LITERAL_MIN_TAIL + 4)
|
|
3013
3066
|
return 0;
|
|
3014
3067
|
const runFrom = new Int32Array(n + 1);
|
|
3015
3068
|
let best = 0;
|
|
@@ -3028,9 +3081,10 @@ function gluedLiteralStart(word) {
|
|
|
3028
3081
|
const left = word.charCodeAt(k - 1);
|
|
3029
3082
|
if ((left !== 0x5f && left !== 0x2d) || !isAlnumCode(word.charCodeAt(k)))
|
|
3030
3083
|
continue;
|
|
3031
|
-
|
|
3084
|
+
const run = runFrom[k] ?? 0;
|
|
3085
|
+
if (run < PROSE_SHORT_LITERAL_MIN_TAIL)
|
|
3032
3086
|
return 0;
|
|
3033
|
-
if (SECRET_LITERAL_RE.test(word.slice(k, k + LITERAL_PROBE_WINDOW)))
|
|
3087
|
+
if (SECRET_LITERAL_RE.test(word.slice(k, k + LITERAL_PROBE_WINDOW)) && (run >= PROSE_LITERAL_MIN_RUN || shortLiteralTail(word.slice(k))))
|
|
3034
3088
|
return k;
|
|
3035
3089
|
}
|
|
3036
3090
|
return 0;
|
|
@@ -3090,8 +3144,27 @@ function isOpaqueWord(text, i, end) {
|
|
|
3090
3144
|
}
|
|
3091
3145
|
return segments > 0 && segmentChars / segments <= OPAQUE_MAX_AVG_SEGMENT;
|
|
3092
3146
|
}
|
|
3147
|
+
const FILE_URL_OPENERS = new Set(['(', '[', '<', "'", ',', ';']);
|
|
3148
|
+
function fileUrlPositions(text) {
|
|
3149
|
+
let marks = null;
|
|
3150
|
+
const re = /file:/gi;
|
|
3151
|
+
for (let hit = re.exec(text); hit !== null; hit = re.exec(text)) {
|
|
3152
|
+
const s = hit.index;
|
|
3153
|
+
if (s > 0 && !endsUrlFragmentAt(text, s - 1) && !FILE_URL_OPENERS.has(text.charAt(s - 1)))
|
|
3154
|
+
continue;
|
|
3155
|
+
let e = s + hit[0].length;
|
|
3156
|
+
while (e < text.length && !endsUrlFragmentAt(text, e))
|
|
3157
|
+
e++;
|
|
3158
|
+
marks ??= new Uint8Array(text.length);
|
|
3159
|
+
for (let k = s; k < e; k++)
|
|
3160
|
+
marks[k] = 1;
|
|
3161
|
+
re.lastIndex = e;
|
|
3162
|
+
}
|
|
3163
|
+
return marks;
|
|
3164
|
+
}
|
|
3093
3165
|
function proseLiteralEdits(text) {
|
|
3094
3166
|
const edits = [];
|
|
3167
|
+
let fileAt;
|
|
3095
3168
|
let i = 0;
|
|
3096
3169
|
while (i < text.length) {
|
|
3097
3170
|
const c = text.charCodeAt(i);
|
|
@@ -3102,6 +3175,7 @@ function proseLiteralEdits(text) {
|
|
|
3102
3175
|
let end = i;
|
|
3103
3176
|
while (end < text.length && isKeyWordChar(text.charCodeAt(end)))
|
|
3104
3177
|
end++;
|
|
3178
|
+
const escHead = i > 0 && text.charCodeAt(i - 1) === 0x25 && PERCENT_ESCAPE_ASCII.test(text.slice(i - 1, i + 2)) && i + 2 < end ? 2 : 0;
|
|
3105
3179
|
const word = text.slice(i, end);
|
|
3106
3180
|
if (word.startsWith('eyJ')) {
|
|
3107
3181
|
let segEnd = end;
|
|
@@ -3120,22 +3194,33 @@ function proseLiteralEdits(text) {
|
|
|
3120
3194
|
}
|
|
3121
3195
|
const prev = i > 0 ? text.charAt(i - 1) : '';
|
|
3122
3196
|
const hostSlot = prev === '/' && i > 1 && text.charAt(i - 2) === '/';
|
|
3123
|
-
const
|
|
3124
|
-
if (
|
|
3125
|
-
|
|
3126
|
-
|
|
3127
|
-
|
|
3197
|
+
const slashSegment = prev === '/' && !hostSlot;
|
|
3198
|
+
if (slashSegment && fileAt === undefined)
|
|
3199
|
+
fileAt = fileUrlPositions(text);
|
|
3200
|
+
const localPath = prev === '\\' || prev === '.' || (slashSegment && fileAt != null && fileAt[i] === 1);
|
|
3201
|
+
const pathish = slashSegment || localPath;
|
|
3202
|
+
if (!localPath && SECRET_LITERAL_RE.test(word) && (longestAlnumRun(word) >= PROSE_LITERAL_MIN_RUN || shortLiteralTail(word))) {
|
|
3203
|
+
pushEdit(edits, text, i + escHead, end, REDACTED_SECRET);
|
|
3204
|
+
}
|
|
3205
|
+
else if (!localPath) {
|
|
3128
3206
|
const glued = gluedLiteralStart(word);
|
|
3129
3207
|
if (glued > 0)
|
|
3130
|
-
pushEdit(edits, text, i + glued, end, REDACTED_SECRET);
|
|
3131
|
-
else if (!hostSlot && isOpaqueWord(text, i, end))
|
|
3132
|
-
pushEdit(edits, text, i, end, REDACTED_SECRET);
|
|
3208
|
+
pushEdit(edits, text, i + Math.max(glued, escHead), end, REDACTED_SECRET);
|
|
3209
|
+
else if (!pathish && !hostSlot && isOpaqueWord(text, i, end))
|
|
3210
|
+
pushEdit(edits, text, i + escHead, end, REDACTED_SECRET);
|
|
3133
3211
|
}
|
|
3134
3212
|
i = end;
|
|
3135
3213
|
}
|
|
3136
3214
|
return edits;
|
|
3137
3215
|
}
|
|
3216
|
+
const C0_ALL = '\\u0000-\\u001F';
|
|
3217
|
+
const C0_KEEP_LAYOUT = '\\u0000-\\u0008\\u000B-\\u001F';
|
|
3218
|
+
const DEL_C1 = '\\u007F-\\u009F';
|
|
3219
|
+
const LONE_SURROGATE = '\\uD800-\\uDFFF';
|
|
3220
|
+
const LINE_SEPARATORS = '\\u2028\\u2029';
|
|
3138
3221
|
const FORMAT_CHAR = /^\p{Cf}$/u;
|
|
3222
|
+
const FORMAT_CLASS = FORMAT_CHAR.source.slice(1, -1);
|
|
3223
|
+
const FACE_MARKED_CHAR = new RegExp(`^(?:[${C0_ALL}${DEL_C1}${LONE_SURROGATE}${LINE_SEPARATORS}]|${FORMAT_CLASS})$`, 'u');
|
|
3139
3224
|
const isStrippableControl = (c) => (c <= 0x1f && (c < 0x09 || c > 0x0d)) || (c >= 0x7f && c <= 0x9f);
|
|
3140
3225
|
class SequenceReader {
|
|
3141
3226
|
text;
|
|
@@ -3263,13 +3348,7 @@ function c1SequenceAfter(text, c1, at, seq) {
|
|
|
3263
3348
|
return null;
|
|
3264
3349
|
}
|
|
3265
3350
|
function isFaceMarkedCodePoint(cp) {
|
|
3266
|
-
|
|
3267
|
-
return true;
|
|
3268
|
-
if (cp >= 0xd800 && cp <= 0xdfff)
|
|
3269
|
-
return true;
|
|
3270
|
-
if (cp === 0x2028 || cp === 0x2029)
|
|
3271
|
-
return true;
|
|
3272
|
-
return cp >= 0xad && cp <= 0x10ffff && FORMAT_CHAR.test(String.fromCodePoint(cp));
|
|
3351
|
+
return cp >= 0 && cp <= 0x10ffff && FACE_MARKED_CHAR.test(String.fromCodePoint(cp));
|
|
3273
3352
|
}
|
|
3274
3353
|
function escapeTokenAt(text, at) {
|
|
3275
3354
|
const four = /^\\u([0-9A-Fa-f]{4})/.exec(text.slice(at, at + 6));
|
|
@@ -3585,8 +3664,39 @@ function screenSurfaces(text, o) {
|
|
|
3585
3664
|
if (joinedSpaced !== null)
|
|
3586
3665
|
out.push(joinedSpaced);
|
|
3587
3666
|
const contents = V.contents.map(([a, b]) => ({ sub: screen.slice(a, b), rawAt: (k) => M.ps[a + k] ?? 0, rawEndAt: (k) => M.pe[a + k] ?? 0 }));
|
|
3667
|
+
const decoded = percentSurfaces(text, screen, M, o);
|
|
3668
|
+
out.push(...decoded.surfaces);
|
|
3669
|
+
contents.push(...decoded.contents);
|
|
3588
3670
|
return { surfaces: out, rawSoft, rawPlain, contents };
|
|
3589
3671
|
}
|
|
3672
|
+
function percentSurfaces(raw, text, base, o) {
|
|
3673
|
+
const surfaces = [];
|
|
3674
|
+
const contents = [];
|
|
3675
|
+
for (const layer of percentLayers(text)) {
|
|
3676
|
+
const n = layer.text.length;
|
|
3677
|
+
const ps = new Int32Array(n);
|
|
3678
|
+
const pe = new Int32Array(n);
|
|
3679
|
+
const pdot = new Uint8Array(n);
|
|
3680
|
+
for (let k = 0; k < n; k++) {
|
|
3681
|
+
const a = layer.ps[k] ?? 0;
|
|
3682
|
+
const b = layer.pe[k] ?? 0;
|
|
3683
|
+
ps[k] = base === null ? a : (base.ps[a] ?? 0);
|
|
3684
|
+
pe[k] = base === null ? b : (base.pe[b - 1] ?? 0);
|
|
3685
|
+
if (base !== null && b - a === 1)
|
|
3686
|
+
pdot[k] = base.pdot[a] ?? 0;
|
|
3687
|
+
}
|
|
3688
|
+
const M = { ps, pe, pdot, rawDot: base === null ? new Uint8Array(raw.length) : base.rawDot };
|
|
3689
|
+
const VD = buildScanView(layer.text, o);
|
|
3690
|
+
const sep = separatedSurface(raw, VD, M);
|
|
3691
|
+
surfaces.push(base === null ? { ...sep, rawDot: null } : sep);
|
|
3692
|
+
const joined = joinedSurface(raw, VD, false, M);
|
|
3693
|
+
if (joined !== null)
|
|
3694
|
+
surfaces.push(base === null ? { ...joined, rawDot: null } : joined);
|
|
3695
|
+
for (const [a, b] of VD.contents)
|
|
3696
|
+
contents.push({ sub: layer.text.slice(a, b), rawAt: (k) => ps[a + k] ?? 0, rawEndAt: (k) => pe[a + k] ?? 0 });
|
|
3697
|
+
}
|
|
3698
|
+
return { surfaces, contents };
|
|
3699
|
+
}
|
|
3590
3700
|
function finalIsStructural(text, at) {
|
|
3591
3701
|
if (text.charCodeAt(at) !== 0x5c)
|
|
3592
3702
|
return false;
|
|
@@ -3855,6 +3965,9 @@ function collectCandidates(text, o, nested) {
|
|
|
3855
3965
|
surfaces.push(joinedSpaced);
|
|
3856
3966
|
const screen = screenSurfaces(text, o);
|
|
3857
3967
|
surfaces.push(...screen.surfaces);
|
|
3968
|
+
const decoded = percentSurfaces(text, text, null, o);
|
|
3969
|
+
surfaces.push(...decoded.surfaces);
|
|
3970
|
+
const decodedContents = decoded.contents;
|
|
3858
3971
|
const raw = [];
|
|
3859
3972
|
const blocks = { raw: text, view: V, out: raw, lines: [null, null], marks: null };
|
|
3860
3973
|
const regionSurface = screen.surfaces[0] ?? separated;
|
|
@@ -3864,7 +3977,7 @@ function collectCandidates(text, o, nested) {
|
|
|
3864
3977
|
const regions = [];
|
|
3865
3978
|
if (o.urls) {
|
|
3866
3979
|
const unitPos = (k) => S.dropped(k) || V.rawUnit[S.rawStart(k)] === 1;
|
|
3867
|
-
urlCandidates(S.text, cands, regions, unitPos, (k) => unitPos(k) || S.painted(k));
|
|
3980
|
+
urlCandidates(S.text, cands, regions, unitPos, (k) => unitPos(k) || S.painted(k), o.queryByShape ?? false);
|
|
3868
3981
|
userinfoCandidates(S, cands, o.words);
|
|
3869
3982
|
}
|
|
3870
3983
|
if (o.words) {
|
|
@@ -3873,6 +3986,8 @@ function collectCandidates(text, o, nested) {
|
|
|
3873
3986
|
labelCandidates(S, 'scheme', regions, cands, blocks);
|
|
3874
3987
|
labelCandidates(S, 'token', regions, cands, blocks);
|
|
3875
3988
|
labelCandidates(S, 'label', regions, cands, blocks);
|
|
3989
|
+
labelCandidates(S, 'scheme', regions, cands, blocks, true);
|
|
3990
|
+
labelCandidates(S, 'label', regions, cands, blocks, true);
|
|
3876
3991
|
editsToCandidates(authParamEdits(S.text), 'param', cands);
|
|
3877
3992
|
editsToCandidates(proseLiteralEdits(S.text), 'prose', cands);
|
|
3878
3993
|
}
|
|
@@ -3890,7 +4005,7 @@ function collectCandidates(text, o, nested) {
|
|
|
3890
4005
|
raw.push({ start: a + iv.start, end: a + iv.end, kind: iv.kind, cls: 'nested', tokenEnd: a + iv.end, pretrimmed: true });
|
|
3891
4006
|
}
|
|
3892
4007
|
}
|
|
3893
|
-
for (const c of screen.contents) {
|
|
4008
|
+
for (const c of [...screen.contents, ...decodedContents]) {
|
|
3894
4009
|
const inner = collectCandidates(c.sub, o, true);
|
|
3895
4010
|
for (const iv of resolveCandidates(c.sub, inner.cands, inner.rawUnit, inner.rawDot, o.words ? pemRegions(inner.pemBlocksSeen) : [], inner.rawSoft, inner.rawPlain)) {
|
|
3896
4011
|
const start = c.rawAt(iv.start);
|
|
@@ -3959,11 +4074,6 @@ export function washCredentials(text) {
|
|
|
3959
4074
|
return text;
|
|
3960
4075
|
return scanAndMask(text, WASH_OPTIONS, false).text;
|
|
3961
4076
|
}
|
|
3962
|
-
const C0_ALL = '\\u0000-\\u001F';
|
|
3963
|
-
const C0_KEEP_LAYOUT = '\\u0000-\\u0008\\u000B-\\u001F';
|
|
3964
|
-
const DEL_C1 = '\\u007F-\\u009F';
|
|
3965
|
-
const LONE_SURROGATE = '\\uD800-\\uDFFF';
|
|
3966
|
-
const LINE_SEPARATORS = '\\u2028\\u2029';
|
|
3967
4077
|
const NAME_BLANKS = NAME_BLANK_RANGES.map(([a, b]) => (a === b ? classEscape(a) : `${classEscape(a)}-${classEscape(b)}`)).join('');
|
|
3968
4078
|
function classEscape(cp) {
|
|
3969
4079
|
return `\\u${cp.toString(16).toUpperCase().padStart(4, '0')}`;
|
|
@@ -4068,9 +4178,7 @@ function faceHitKept(ch, at, text) {
|
|
|
4068
4178
|
const c = ch.charCodeAt(0);
|
|
4069
4179
|
return ch.length === 1 && (c === 0x200d || c === 0x200c) && joinerKept(text, at, c === 0x200d);
|
|
4070
4180
|
}
|
|
4071
|
-
const FORMAT_CLASS = '\\p{Cf}';
|
|
4072
4181
|
const RGI_TAG_FLAG = '\\u{1F3F4}\\u{E0067}\\u{E0062}(?:\\u{E0065}\\u{E006E}\\u{E0067}|\\u{E0073}\\u{E0063}\\u{E0074}|\\u{E0077}\\u{E006C}\\u{E0073})\\u{E007F}';
|
|
4073
|
-
const BIDI_REORDER = '\\u061C\\u200E\\u200F\\u2028\\u2029\\u202A-\\u202E\\u2066-\\u2069';
|
|
4074
4182
|
function unsafeClass(members, withFormatClass) {
|
|
4075
4183
|
if (members === '' && !withFormatClass)
|
|
4076
4184
|
return null;
|
|
@@ -4081,17 +4189,6 @@ function unsafeClass(members, withFormatClass) {
|
|
|
4081
4189
|
];
|
|
4082
4190
|
return new RegExp(parts.join('|'), 'gu');
|
|
4083
4191
|
}
|
|
4084
|
-
export const LEGACY_ESCAPE_FACE = Object.freeze({
|
|
4085
|
-
unsafe: unsafeClass(`${C0_ALL}${DEL_C1}${BIDI_REORDER}${LONE_SURROGATE}`, false),
|
|
4086
|
-
fold: false,
|
|
4087
|
-
mark: 'escape',
|
|
4088
|
-
});
|
|
4089
|
-
export const LEGACY_LABEL_FACE = Object.freeze({ ...LEGACY_ESCAPE_FACE, fold: true });
|
|
4090
|
-
export const LEGACY_NAME_FACE = Object.freeze({
|
|
4091
|
-
unsafe: unsafeClass(`${C0_ALL}\\u007F${LINE_SEPARATORS}`, false),
|
|
4092
|
-
fold: true,
|
|
4093
|
-
mark: 'space',
|
|
4094
|
-
});
|
|
4095
4192
|
function markOf(ch, mark) {
|
|
4096
4193
|
if (mark === 'dot')
|
|
4097
4194
|
return '.';
|
|
@@ -4269,8 +4366,11 @@ function publicFace(opts) {
|
|
|
4269
4366
|
const index = (opts.controls !== false ? 8 : 0) + (opts.bidi !== false ? 4 : 0) + (keep ? 2 : 0) + (blanks ? 1 : 0);
|
|
4270
4367
|
return { unsafe: PUBLIC_UNSAFE_BY_NETS[index] ?? null, fold, mark: opts.mark ?? 'escape', keepBlanks: blanks };
|
|
4271
4368
|
}
|
|
4369
|
+
export const ESCAPE_FACE = Object.freeze({ ...publicFace({ foldLines: false }) });
|
|
4370
|
+
export const LABEL_FACE = Object.freeze({ ...publicFace({}) });
|
|
4371
|
+
export const PEER_NAME_FACE = Object.freeze({ ...publicFace({ mark: 'space' }) });
|
|
4272
4372
|
const ESCAPE_TOKEN_AT = /^\\u(?:[0-9A-F]{4}|\{[0-9A-F]{1,6}\})/;
|
|
4273
|
-
function capDisplay(text, max, mark) {
|
|
4373
|
+
export function capDisplay(text, max, mark) {
|
|
4274
4374
|
if (typeof max !== 'number' || !Number.isFinite(max))
|
|
4275
4375
|
return text;
|
|
4276
4376
|
const limit = Math.max(0, Math.floor(max));
|
|
@@ -4309,6 +4409,14 @@ function runDisplaySteps(start, steps) {
|
|
|
4309
4409
|
}
|
|
4310
4410
|
return out;
|
|
4311
4411
|
}
|
|
4412
|
+
export function paintAndCap(text, face, max) {
|
|
4413
|
+
return runDisplaySteps(text, {
|
|
4414
|
+
wash: (s) => s,
|
|
4415
|
+
paint: (s) => applyDisplayFace(s, face),
|
|
4416
|
+
cap: (s) => capDisplay(s, max, face.mark),
|
|
4417
|
+
text: (s) => s,
|
|
4418
|
+
});
|
|
4419
|
+
}
|
|
4312
4420
|
function escapeSpacesFor(o, face) {
|
|
4313
4421
|
if (face.mark !== 'escape' || face.fold)
|
|
4314
4422
|
return null;
|
|
@@ -4327,9 +4435,10 @@ function escapeSpacesFor(o, face) {
|
|
|
4327
4435
|
}
|
|
4328
4436
|
return set.size === 0 ? null : set;
|
|
4329
4437
|
}
|
|
4330
|
-
function scanOptionsOf(o, face) {
|
|
4438
|
+
function scanOptionsOf(o, face, queryByShape = false) {
|
|
4331
4439
|
return {
|
|
4332
4440
|
urls: o.credentialUrls !== false,
|
|
4441
|
+
queryByShape,
|
|
4333
4442
|
words: o.credentialWords !== false,
|
|
4334
4443
|
blanks: face.keepBlanks === true,
|
|
4335
4444
|
fold: face.fold,
|
|
@@ -4344,11 +4453,11 @@ export function credentialWashPasses(text, opts = {}) {
|
|
|
4344
4453
|
scanAndMask(input, scan, false, stats);
|
|
4345
4454
|
return { passes: stats.passes, capped: stats.capped };
|
|
4346
4455
|
}
|
|
4347
|
-
function renderUntrusted(text, opts, ledger) {
|
|
4456
|
+
function renderUntrusted(text, opts, ledger, queryByShape = false) {
|
|
4348
4457
|
const input = typeof text === 'string' ? text : String(text ?? '');
|
|
4349
4458
|
const o = opts ?? {};
|
|
4350
4459
|
const face = publicFace(o);
|
|
4351
|
-
const scan = scanOptionsOf(o, face);
|
|
4460
|
+
const scan = scanOptionsOf(o, face, queryByShape);
|
|
4352
4461
|
const pairBackslashes = o.escapeBackslashes === true && face.mark !== 'dot' && face.mark !== 'space';
|
|
4353
4462
|
const book = ledger ?? (pairBackslashes ? { cps: [], lens: [] } : null);
|
|
4354
4463
|
if (book === null) {
|
|
@@ -4379,6 +4488,193 @@ export function displayUntrusted(text, opts = {}) {
|
|
|
4379
4488
|
const out = renderUntrusted(text, opts, null);
|
|
4380
4489
|
return typeof out === 'string' ? out : out.text;
|
|
4381
4490
|
}
|
|
4491
|
+
const NON_SECRET_FIELD_NAMES = new Set([
|
|
4492
|
+
'deployment', 'deploymentname', 'deploymentid', 'name', 'tenant', 'tenantname', 'tenantid', 'region', 'location', 'zone',
|
|
4493
|
+
'project', 'projectname', 'projectid', 'workspace', 'workspacename', 'workspaceid', 'resource', 'resourcename', 'resourcegroup',
|
|
4494
|
+
'model', 'modelname', 'modelid', 'apiversion', 'version', 'env', 'environment', 'stage', 'cluster', 'namespace', 'instance',
|
|
4495
|
+
'service', 'servicename', 'app', 'appname', 'application', 'team', 'site', 'org', 'organization',
|
|
4496
|
+
]);
|
|
4497
|
+
function isNonSecretFieldName(key) {
|
|
4498
|
+
const view = percentDecodeForMatching(key).decoded;
|
|
4499
|
+
return NON_SECRET_FIELD_NAMES.has(view.toLowerCase().replace(/[-_]/g, ''));
|
|
4500
|
+
}
|
|
4501
|
+
function isCredentialLabelName(name) {
|
|
4502
|
+
if (name.length === 0)
|
|
4503
|
+
return false;
|
|
4504
|
+
const decoded = percentDecodeForMatching(name).decoded;
|
|
4505
|
+
for (const view of decoded === name ? [name] : [name, decoded]) {
|
|
4506
|
+
const probe = `${view}=`;
|
|
4507
|
+
if (labelEndsAt(probe, probe.length, false, true))
|
|
4508
|
+
return true;
|
|
4509
|
+
}
|
|
4510
|
+
return false;
|
|
4511
|
+
}
|
|
4512
|
+
const WORD_CHAIN_MAX_SUFFIX_DIGITS = 4;
|
|
4513
|
+
const WORD_CHAIN_MAX_DIGIT_RUN = 8;
|
|
4514
|
+
function isReadableWordChain(value) {
|
|
4515
|
+
const core = valueCoreForJudging(value);
|
|
4516
|
+
if (core.length === 0 || !core.includes('-'))
|
|
4517
|
+
return false;
|
|
4518
|
+
const segments = core.split('-');
|
|
4519
|
+
if (segments.length < 2)
|
|
4520
|
+
return false;
|
|
4521
|
+
if (!segments.some((seg) => seg.length >= 3 && [...seg].every(isAlpha)))
|
|
4522
|
+
return false;
|
|
4523
|
+
if ([...core].every((ch) => ch === '-' || isHexDigit(ch)))
|
|
4524
|
+
return false;
|
|
4525
|
+
for (const seg of segments) {
|
|
4526
|
+
const letters = seg.replace(/[0-9]+$/, '').toLowerCase();
|
|
4527
|
+
if (/[bcdfghjklmnpqrstvwxz]{4,}/.test(letters))
|
|
4528
|
+
return false;
|
|
4529
|
+
if (letters.length >= 5 && !/[aeiouy]/.test(letters))
|
|
4530
|
+
return false;
|
|
4531
|
+
}
|
|
4532
|
+
for (const seg of segments) {
|
|
4533
|
+
if (seg.length === 0)
|
|
4534
|
+
return false;
|
|
4535
|
+
let i = 0;
|
|
4536
|
+
while (i < seg.length && isAlpha(seg.charAt(i)))
|
|
4537
|
+
i++;
|
|
4538
|
+
const letters = seg.slice(0, i);
|
|
4539
|
+
const digits = seg.slice(i);
|
|
4540
|
+
for (const ch of digits)
|
|
4541
|
+
if (ch < '0' || ch > '9')
|
|
4542
|
+
return false;
|
|
4543
|
+
if (letters.length === 0) {
|
|
4544
|
+
if (digits.length === 0 || digits.length > WORD_CHAIN_MAX_DIGIT_RUN)
|
|
4545
|
+
return false;
|
|
4546
|
+
continue;
|
|
4547
|
+
}
|
|
4548
|
+
const caseOk = letters === letters.toLowerCase() || letters === letters.toUpperCase() ||
|
|
4549
|
+
(letters.charAt(0) === letters.charAt(0).toUpperCase() && letters.slice(1) === letters.slice(1).toLowerCase());
|
|
4550
|
+
if (!caseOk)
|
|
4551
|
+
return false;
|
|
4552
|
+
if (digits.length > 0 && (letters.length < 2 || digits.length > WORD_CHAIN_MAX_SUFFIX_DIGITS))
|
|
4553
|
+
return false;
|
|
4554
|
+
}
|
|
4555
|
+
return true;
|
|
4556
|
+
}
|
|
4557
|
+
const OPAQUE_TOKEN_PUNCTUATION = new Set(['-', '_', '.', '~', '+', '/', '=']);
|
|
4558
|
+
const HIGH_ENTROPY_MIN_ALNUM = 20;
|
|
4559
|
+
function isHighEntropyQueryValue(value) {
|
|
4560
|
+
const { decoded } = percentDecodeForMatching(value);
|
|
4561
|
+
let alnum = 0;
|
|
4562
|
+
let mixed = 0;
|
|
4563
|
+
let run = 0;
|
|
4564
|
+
let runHasLetter = false;
|
|
4565
|
+
let runHasDigit = false;
|
|
4566
|
+
let anyLetter = false;
|
|
4567
|
+
let anyDigit = false;
|
|
4568
|
+
let allHex = true;
|
|
4569
|
+
const flush = () => {
|
|
4570
|
+
alnum += run;
|
|
4571
|
+
if (runHasLetter && runHasDigit)
|
|
4572
|
+
mixed += run;
|
|
4573
|
+
run = 0;
|
|
4574
|
+
runHasLetter = false;
|
|
4575
|
+
runHasDigit = false;
|
|
4576
|
+
};
|
|
4577
|
+
for (const ch of decoded) {
|
|
4578
|
+
if (isAlpha(ch)) {
|
|
4579
|
+
run += 1;
|
|
4580
|
+
runHasLetter = true;
|
|
4581
|
+
anyLetter = true;
|
|
4582
|
+
if (!isHexDigit(ch))
|
|
4583
|
+
allHex = false;
|
|
4584
|
+
continue;
|
|
4585
|
+
}
|
|
4586
|
+
if (ch >= '0' && ch <= '9') {
|
|
4587
|
+
run += 1;
|
|
4588
|
+
runHasDigit = true;
|
|
4589
|
+
anyDigit = true;
|
|
4590
|
+
continue;
|
|
4591
|
+
}
|
|
4592
|
+
if (!OPAQUE_TOKEN_PUNCTUATION.has(ch))
|
|
4593
|
+
return false;
|
|
4594
|
+
flush();
|
|
4595
|
+
}
|
|
4596
|
+
flush();
|
|
4597
|
+
if (alnum < HIGH_ENTROPY_MIN_ALNUM)
|
|
4598
|
+
return false;
|
|
4599
|
+
return mixed * 2 >= alnum || (allHex && anyLetter && anyDigit);
|
|
4600
|
+
}
|
|
4601
|
+
function isOpaqueSecretValue(value) {
|
|
4602
|
+
const core = valueCoreForJudging(value);
|
|
4603
|
+
return core.length > 0 && (looksLikeKnownSecret(core) || isHighEntropyQueryValue(core));
|
|
4604
|
+
}
|
|
4605
|
+
const VALUE_PIECE_SEPARATORS = new Set(['/', '\\', '?', '#', '&', '=', ';', ':', '@', ',', '+']);
|
|
4606
|
+
function hasOpaquePiece(text) {
|
|
4607
|
+
let start = 0;
|
|
4608
|
+
for (let i = 0; i <= text.length; i++) {
|
|
4609
|
+
const ch = text.charAt(i);
|
|
4610
|
+
if (i === text.length || ch <= ' ' || VALUE_PIECE_SEPARATORS.has(ch)) {
|
|
4611
|
+
if (i > start && isOpaqueSecretValue(text.slice(start, i)))
|
|
4612
|
+
return true;
|
|
4613
|
+
start = i + 1;
|
|
4614
|
+
}
|
|
4615
|
+
}
|
|
4616
|
+
return false;
|
|
4617
|
+
}
|
|
4618
|
+
function carriesCredentialStructure(value) {
|
|
4619
|
+
const netChanges = (v) => {
|
|
4620
|
+
if (washCredentials(v) !== v || hasOpaquePiece(v))
|
|
4621
|
+
return true;
|
|
4622
|
+
if (!v.includes('+'))
|
|
4623
|
+
return false;
|
|
4624
|
+
const spaced = v.split('+').join(' ');
|
|
4625
|
+
return washCredentials(spaced) !== spaced;
|
|
4626
|
+
};
|
|
4627
|
+
let view = value;
|
|
4628
|
+
for (let layer = 0; layer < MAX_PERCENT_VIEW_LAYERS; layer++) {
|
|
4629
|
+
if (netChanges(view))
|
|
4630
|
+
return true;
|
|
4631
|
+
const next = percentDecodeForMatching(view).decoded;
|
|
4632
|
+
if (next === view)
|
|
4633
|
+
return false;
|
|
4634
|
+
view = next;
|
|
4635
|
+
}
|
|
4636
|
+
if (netChanges(view))
|
|
4637
|
+
return true;
|
|
4638
|
+
return percentDecodeForMatching(view).decoded !== view;
|
|
4639
|
+
}
|
|
4640
|
+
function queryPartCarriesCredential(key, value, relax) {
|
|
4641
|
+
if (value.length === 0 || isMarker(value))
|
|
4642
|
+
return false;
|
|
4643
|
+
if (key !== null) {
|
|
4644
|
+
if (isCredentialLabelName(key))
|
|
4645
|
+
return true;
|
|
4646
|
+
if (relax && isNonSecretFieldName(key) && isReadableWordChain(value))
|
|
4647
|
+
return isKnownSecretSegment(value);
|
|
4648
|
+
}
|
|
4649
|
+
return isOpaqueSecretValue(value) || carriesCredentialStructure(value);
|
|
4650
|
+
}
|
|
4651
|
+
const UPLOAD_PROBE_PREFIX = 'https://q.invalid/?';
|
|
4652
|
+
const UPLOAD_SCAN_OPTIONS = Object.freeze({ urls: true, queryByShape: 'strict', words: true, blanks: false, fold: false, face: null, escapeSpaces: null });
|
|
4653
|
+
export function queryCarriesCredential(query) {
|
|
4654
|
+
if (typeof query !== 'string' || query.length === 0)
|
|
4655
|
+
return false;
|
|
4656
|
+
const probe = `${UPLOAD_PROBE_PREFIX}${query}`;
|
|
4657
|
+
return scanOnce(probe, UPLOAD_SCAN_OPTIONS, false).text !== probe;
|
|
4658
|
+
}
|
|
4659
|
+
export function displayConfiguredUrl(url, opts = {}) {
|
|
4660
|
+
const out = renderUntrusted(typeof url === 'string' ? configuredUrlScannable(url) : url, opts, null, 'relaxed');
|
|
4661
|
+
return typeof out === 'string' ? out : out.text;
|
|
4662
|
+
}
|
|
4663
|
+
function configuredUrlScannable(url) {
|
|
4664
|
+
if (/^[A-Za-z][A-Za-z0-9+.-]*:\/\//.test(url) || !/^[A-Za-z][A-Za-z0-9+.-]*:/.test(url))
|
|
4665
|
+
return url;
|
|
4666
|
+
let u;
|
|
4667
|
+
try {
|
|
4668
|
+
u = new URL(url);
|
|
4669
|
+
}
|
|
4670
|
+
catch {
|
|
4671
|
+
return url;
|
|
4672
|
+
}
|
|
4673
|
+
if (u.host === '')
|
|
4674
|
+
return url;
|
|
4675
|
+
const userinfo = u.username !== '' || u.password !== '' ? `${u.username}${u.password !== '' ? `:${u.password}` : ''}@` : '';
|
|
4676
|
+
return `${u.protocol}//${userinfo}${u.host}${u.pathname}${u.search}${u.hash}`;
|
|
4677
|
+
}
|
|
4382
4678
|
export function quoteUntrustedForSentence(text, max) {
|
|
4383
4679
|
const shown = displayUntrusted(text, { max, blanks: true, escapeBackslashes: true });
|
|
4384
4680
|
return shown.trim() === '' ? undefined : `"${shown.replace(/"/g, '\\"')}"`;
|