@sema-agent/server 7.135.0 → 7.136.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/MIGRATION.md +64 -2
- package/USAGE.md +62 -25
- package/deploy/sema-up/README.md +1 -1
- package/deploy/sema-up/chart/templates/NOTES.txt +1 -1
- package/deploy/sema-up/chart/values.yaml +1 -1
- package/dist/approval-card.d.ts +60 -4
- package/dist/approval-card.js +18 -1
- package/dist/approval-content-kind.d.ts +1 -0
- package/dist/approval-content-kind.js +1 -1
- package/dist/auth-bridge.d.ts +34 -5
- package/dist/auth-bridge.js +20 -3
- package/dist/boot/budget-tracing.d.ts +1 -2
- package/dist/boot/budget-tracing.js +2 -2
- package/dist/boot/org-memory.js +1 -1
- package/dist/boot/resolve-spec.js +10 -7
- package/dist/boot/runner-deps.js +4 -2
- package/dist/boot/stage-04-stores.d.ts +1 -1
- package/dist/boot/stage-05-execution-env.d.ts +1 -1
- package/dist/boot/stage-06-runners.d.ts +1 -1
- package/dist/boot/stage-07-capability-layer.d.ts +1 -1
- package/dist/boot/stage-08-reapers.d.ts +1 -1
- package/dist/boot/stage-09-leader.d.ts +1 -1
- package/dist/boot/stage-10-http-server.d.ts +1 -1
- package/dist/boot/stores.js +2 -3
- package/dist/boot/web-admin.js +10 -17
- package/dist/brain.js +12 -3
- package/dist/budget.d.ts +3 -3
- package/dist/budget.js +4 -0
- package/dist/capabilities/mcp-probe.d.ts +5 -4
- package/dist/capability-facts.d.ts +3 -7
- package/dist/capability-facts.js +3 -5
- package/dist/center-credential.d.ts +18 -4
- package/dist/center-credential.js +18 -3
- package/dist/config-catalog.js +12 -10
- package/dist/config-d-migration.js +1 -1
- package/dist/config-invariants.d.ts +1 -1
- package/dist/config-invariants.js +8 -0
- package/dist/config-lkg.d.ts +0 -38
- package/dist/config-lkg.js +1 -83
- package/dist/config-types.d.ts +12 -4
- package/dist/config.js +26 -8
- package/dist/deployment-shape.d.ts +1 -1
- package/dist/elicitation.d.ts +8 -7
- package/dist/elicitation.js +9 -8
- package/dist/file-history-disclosure.d.ts +6 -4
- package/dist/file-history-disclosure.js +22 -6
- package/dist/fleet-client.d.ts +32 -1
- package/dist/fleet-client.js +24 -4
- package/dist/fleet-lease.d.ts +7 -3
- package/dist/fleet-lease.js +31 -23
- package/dist/hooks/hook-llm.js +14 -8
- package/dist/hooks/hook-runner.d.ts +4 -4
- package/dist/hooks/hook-runner.js +2 -2
- package/dist/http/checkpoint-ctx.d.ts +28 -9
- package/dist/http/checkpoint-ctx.js +5 -1
- package/dist/http/dispatch.js +9 -9
- package/dist/http/resume-ctx-read.d.ts +2 -16
- package/dist/http/resume-ctx-read.js +2 -3
- package/dist/http/routes/approvals-assistant.d.ts +0 -2
- package/dist/http/routes/approvals-assistant.js +47 -12
- package/dist/http/routes/runs.js +2 -2
- package/dist/http/routes/session-sync.js +31 -9
- package/dist/http/routes/sessions.js +18 -14
- package/dist/http/submit-ctx.js +1 -1
- package/dist/http/wire-types.d.ts +5 -3
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/model-route-gap.d.ts +49 -4
- package/dist/model-route-gap.js +34 -1
- package/dist/object-store-buckets.d.ts +13 -0
- package/dist/object-store-buckets.js +3 -0
- package/dist/observability/fail-open.d.ts +6 -6
- package/dist/observability/fail-open.js +6 -6
- package/dist/observability/metrics.js +1 -1
- package/dist/observability/principal-context.d.ts +4 -1
- package/dist/observability/principal-context.js +6 -1
- package/dist/parked-decide.d.ts +4 -3
- package/dist/parked-decide.js +26 -7
- package/dist/peer-directory.d.ts +2 -1
- package/dist/peer-directory.js +5 -5
- package/dist/plugins/approval-ask-store-sql.d.ts +8 -0
- package/dist/plugins/approval-ask-store-sql.js +3 -0
- package/dist/plugins/blob-backend.js +2 -1
- package/dist/plugins/checkpoint-index-columns.d.ts +50 -1
- package/dist/plugins/checkpoint-index-columns.js +28 -0
- package/dist/plugins/checkpoint-store-sql.d.ts +27 -3
- package/dist/plugins/checkpoint-store-sql.js +58 -29
- package/dist/plugins/file-history-store-sql.d.ts +6 -0
- package/dist/plugins/file-history-store-sql.js +30 -23
- package/dist/plugins/local-checkpoint-store.js +4 -3
- package/dist/plugins/one-time-migrations.d.ts +21 -28
- package/dist/plugins/one-time-migrations.js +39 -123
- package/dist/plugins/remote-env-k8s.d.ts +18 -5
- package/dist/plugins/remote-env-k8s.js +24 -7
- package/dist/plugins/s3-answer.d.ts +15 -0
- package/dist/plugins/s3-answer.js +5 -0
- package/dist/plugins/snapshot-bucket-presence.d.ts +39 -4
- package/dist/plugins/snapshot-bucket-presence.js +35 -7
- package/dist/plugins/sql-errors.d.ts +39 -0
- package/dist/plugins/sql-errors.js +49 -1
- package/dist/plugins/store-fault.d.ts +4 -0
- package/dist/plugins/store-fault.js +8 -0
- package/dist/plugins/tidb-pool.js +1 -1
- package/dist/plugins/web-search.d.ts +1 -1
- package/dist/plugins/web-search.js +2 -2
- package/dist/request-declaration-refusal.d.ts +94 -0
- package/dist/request-declaration-refusal.js +78 -0
- package/dist/request-image-urls.d.ts +6 -0
- package/dist/request-image-urls.js +27 -0
- package/dist/request-key-closure.d.ts +5 -0
- package/dist/request-key-closure.js +4 -1
- package/dist/runtime-caps-resolver.js +1 -1
- package/dist/sandbox-pkg-source.d.ts +28 -1
- package/dist/sandbox-pkg-source.js +1 -2
- package/dist/sealed-key.d.ts +19 -0
- package/dist/sealed-key.js +47 -4
- package/dist/session-incarnation.d.ts +16 -5
- package/dist/session-incarnation.js +7 -4
- package/dist/session-row.d.ts +3 -17
- package/dist/session-row.js +4 -0
- package/dist/session-sync.d.ts +15 -3
- package/dist/session-sync.js +5 -2
- package/dist/task-a2a.d.ts +52 -81
- package/dist/task-a2a.js +219 -89
- package/dist/task-mcp.d.ts +6 -14
- package/dist/task-mcp.js +16 -51
- package/dist/tool-approval.d.ts +23 -10
- package/dist/tool-approval.js +51 -36
- package/dist/trace/core-keyset-guard.d.ts +2 -2
- package/dist/trace/exit-seat.js +37 -13
- package/dist/trace/redact.d.ts +28 -12
- package/dist/trace/redact.js +233 -94
- package/dist/trace/self-minted-wire.d.ts +267 -0
- package/dist/trace/self-minted-wire.js +79 -0
- package/dist/url-credential-refusal.d.ts +52 -0
- package/dist/url-credential-refusal.js +14 -0
- package/dist/url-refusal.d.ts +13 -4
- package/dist/url-refusal.js +8 -2
- package/dist/workspace-space.d.ts +27 -0
- package/dist/workspace-space.js +26 -0
- package/package.json +3 -2
- package/scripts/drain-check-local.mjs +2 -1
- package/scripts/drain-check.mysql.sql +3 -1
- package/scripts/drain-check.pg.sql +3 -1
- package/skills/schedule.md +1 -1
- package/wire-changes/server@7.104.0.json +134 -0
- package/wire-changes/server@7.105.0.json +53 -0
- package/wire-changes/server@7.106.0.json +475 -0
- package/wire-changes/server@7.107.0.json +64 -0
- package/wire-changes/server@7.108.0.json +153 -0
- package/wire-changes/server@7.109.0.json +46 -0
- package/wire-changes/server@7.110.0.json +90 -0
- package/wire-changes/server@7.111.0.json +86 -0
- package/wire-changes/server@7.112.0.json +60 -0
- package/wire-changes/server@7.113.0.json +26 -0
- package/wire-changes/server@7.114.0.json +26 -0
- package/wire-changes/server@7.115.0.json +97 -0
- package/wire-changes/server@7.116.0.json +218 -0
- package/wire-changes/server@7.116.1.json +26 -0
- package/wire-changes/server@7.117.0.json +65 -0
- package/wire-changes/server@7.118.0.json +129 -0
- package/wire-changes/server@7.119.0.json +65 -0
- package/wire-changes/server@7.120.0.json +89 -0
- package/wire-changes/server@7.121.0.json +219 -0
- package/wire-changes/server@7.122.0.json +54 -0
- package/wire-changes/server@7.123.0.json +26 -0
- package/wire-changes/server@7.124.0.json +336 -0
- package/wire-changes/server@7.124.1.json +26 -0
- package/wire-changes/server@7.125.0.json +111 -0
- package/wire-changes/server@7.126.0.json +285 -0
- package/wire-changes/server@7.127.0.json +77 -0
- package/wire-changes/server@7.127.1.json +26 -0
- package/wire-changes/server@7.127.2.json +53 -0
- package/wire-changes/server@7.128.0.json +26 -0
- package/wire-changes/server@7.129.0.json +106 -0
- package/wire-changes/server@7.129.1.json +26 -0
- package/wire-changes/server@7.130.0.json +26 -0
- package/wire-changes/server@7.131.0.json +118 -0
- package/wire-changes/server@7.132.0.json +226 -0
- package/wire-changes/server@7.133.0.json +1155 -0
- package/wire-changes/server@7.134.0.json +107 -0
- package/wire-changes/server@7.135.0.json +174 -0
- package/wire-changes/server@7.136.0.json +448 -0
- package/dist/url-credential.d.ts +0 -52
- package/dist/url-credential.js +0 -49
package/dist/trace/redact.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { displayUrlOf, stripFormatCharacters } from "@sema-agent/core";
|
|
2
|
-
import { CONTROL_AND_BIDI_CHARS, isHierarchicalUrl } from "@sema-agent/settings-schema";
|
|
2
|
+
import { CONTROL_AND_BIDI_CHARS, CREDENTIAL_PARAM_NAMES, isHierarchicalUrl } from "@sema-agent/settings-schema";
|
|
3
3
|
import { recordFailOpen } from "../observability/fail-open.js";
|
|
4
4
|
const normalizeForRedaction = stripFormatCharacters;
|
|
5
5
|
export function mapNormalizedToOriginal(text, normalized) {
|
|
@@ -836,15 +836,14 @@ const CREDENTIAL_NAME_ANCHORS = {
|
|
|
836
836
|
"x-amz-signature": "structure-only", "x-amz-security-token": "structure-only", "x-amz-credential": "structure-only",
|
|
837
837
|
sas: "structure-only",
|
|
838
838
|
};
|
|
839
|
-
const CREDENTIAL_NAME_WORDS = Object.keys(CREDENTIAL_NAME_ANCHORS);
|
|
840
839
|
const CREDENTIAL_NAME_ALT = (() => {
|
|
841
|
-
for (const w of
|
|
840
|
+
for (const w of CREDENTIAL_PARAM_NAMES) {
|
|
842
841
|
if (!/^[a-z][a-z0-9_-]*$/.test(w))
|
|
843
842
|
throw new Error(`redact: credential name ${JSON.stringify(w)} must be lowercase [a-z0-9_-] (the alternation is built without escaping)`);
|
|
844
843
|
}
|
|
845
|
-
return [...
|
|
844
|
+
return [...CREDENTIAL_PARAM_NAMES].sort((a, b) => b.length - a.length || (a < b ? -1 : 1)).join("|");
|
|
846
845
|
})();
|
|
847
|
-
const CREDENTIAL_NAME_MAX = Math.max(...
|
|
846
|
+
const CREDENTIAL_NAME_MAX = Math.max(...CREDENTIAL_PARAM_NAMES.map((w) => w.length));
|
|
848
847
|
const asciiLower = (s) => s.replace(/[A-Z]/g, (c) => c.toLowerCase());
|
|
849
848
|
function credentialNameAnchor(name) {
|
|
850
849
|
return CREDENTIAL_ANCHOR_OF.get(asciiLower(name));
|
|
@@ -1085,14 +1084,17 @@ function isPemFenceLabel(s, from, to) {
|
|
|
1085
1084
|
return false;
|
|
1086
1085
|
if (!s.startsWith(PEM_KEY_WORD, end - PEM_KEY_WORD.length))
|
|
1087
1086
|
return false;
|
|
1087
|
+
return !pemLabelDashRuleBroken(s, from, to);
|
|
1088
|
+
}
|
|
1089
|
+
function pemLabelDashRuleBroken(s, from, to) {
|
|
1088
1090
|
if (s.charCodeAt(from) === 45)
|
|
1089
|
-
return
|
|
1091
|
+
return true;
|
|
1090
1092
|
if (s.charCodeAt(from + 1) === 45)
|
|
1091
|
-
return
|
|
1093
|
+
return true;
|
|
1092
1094
|
for (let i = from + 1; i < to; i++)
|
|
1093
1095
|
if (s.charCodeAt(i) === 45 && s.charCodeAt(i + 1) === 45)
|
|
1094
|
-
return
|
|
1095
|
-
return
|
|
1096
|
+
return true;
|
|
1097
|
+
return false;
|
|
1096
1098
|
}
|
|
1097
1099
|
const PEM_FENCE_OPEN = "-----BEGIN";
|
|
1098
1100
|
const PEM_FENCE_CLOSE = "-----";
|
|
@@ -1137,7 +1139,7 @@ function unitCountWithin(scan, from, to, max) {
|
|
|
1137
1139
|
}
|
|
1138
1140
|
return n <= max;
|
|
1139
1141
|
}
|
|
1140
|
-
function collectSsh2FenceSpans(scan, text, span) {
|
|
1142
|
+
function collectSsh2FenceSpans(scan, text, span, isHead) {
|
|
1141
1143
|
let cachedFrom = 0;
|
|
1142
1144
|
let cachedAt;
|
|
1143
1145
|
const nextClose = (from) => {
|
|
@@ -1163,6 +1165,10 @@ function collectSsh2FenceSpans(scan, text, span) {
|
|
|
1163
1165
|
continue;
|
|
1164
1166
|
}
|
|
1165
1167
|
const bodyFrom = fence + SSH2_FENCE_OPEN.length;
|
|
1168
|
+
if (isHead && unitCountWithin(scan, bodyFrom, text.length, SSH2_BODY_MAX)) {
|
|
1169
|
+
span(fence, text.length);
|
|
1170
|
+
return;
|
|
1171
|
+
}
|
|
1166
1172
|
const close = nextClose(bodyFrom);
|
|
1167
1173
|
if (close < 0)
|
|
1168
1174
|
return;
|
|
@@ -1177,11 +1183,20 @@ function collectSsh2FenceSpans(scan, text, span) {
|
|
|
1177
1183
|
}
|
|
1178
1184
|
const PEM_END_OPEN = "-----END";
|
|
1179
1185
|
const PEM_FENCE_BODY_MAX = 8192;
|
|
1186
|
+
const PEM_LINE_INVALID = -1;
|
|
1187
|
+
const PEM_LINE_OPEN = -2;
|
|
1180
1188
|
function pemFenceLineEnd(text, labelFrom) {
|
|
1181
1189
|
const close = text.indexOf(PEM_FENCE_CLOSE, labelFrom);
|
|
1182
|
-
|
|
1190
|
+
if (close < 0) {
|
|
1191
|
+
let to = text.length;
|
|
1192
|
+
while (to > labelFrom && text.charCodeAt(to - 1) === 45)
|
|
1193
|
+
to -= 1;
|
|
1194
|
+
const stillPossible = text.length - to >= 2 ? isPemFenceLabel(text, labelFrom, to) : !pemLabelDashRuleBroken(text, labelFrom, to);
|
|
1195
|
+
return stillPossible ? PEM_LINE_OPEN : PEM_LINE_INVALID;
|
|
1196
|
+
}
|
|
1197
|
+
return isPemFenceLabel(text, labelFrom, close) ? close + PEM_FENCE_CLOSE.length : PEM_LINE_INVALID;
|
|
1183
1198
|
}
|
|
1184
|
-
function collectPemFenceSpans(text, span) {
|
|
1199
|
+
function collectPemFenceSpans(text, span, isHead) {
|
|
1185
1200
|
let endFrom = -1;
|
|
1186
1201
|
let endAt = -1;
|
|
1187
1202
|
let endAfter = -1;
|
|
@@ -1201,13 +1216,17 @@ function collectPemFenceSpans(text, span) {
|
|
|
1201
1216
|
if (fence < 0)
|
|
1202
1217
|
return;
|
|
1203
1218
|
const bodyFrom = pemFenceLineEnd(text, fence + PEM_FENCE_OPEN.length);
|
|
1204
|
-
if (bodyFrom
|
|
1219
|
+
if (bodyFrom === PEM_LINE_INVALID) {
|
|
1205
1220
|
from = fence + 1;
|
|
1206
1221
|
continue;
|
|
1207
1222
|
}
|
|
1208
|
-
|
|
1209
|
-
|
|
1223
|
+
if (bodyFrom !== PEM_LINE_OPEN)
|
|
1224
|
+
earliestEndFence(bodyFrom);
|
|
1225
|
+
if (bodyFrom === PEM_LINE_OPEN || endAt < 0) {
|
|
1226
|
+
if (isHead)
|
|
1227
|
+
span(fence, text.length);
|
|
1210
1228
|
return;
|
|
1229
|
+
}
|
|
1211
1230
|
if (endAt - bodyFrom > PEM_FENCE_BODY_MAX) {
|
|
1212
1231
|
from = fence + 1;
|
|
1213
1232
|
continue;
|
|
@@ -1248,9 +1267,19 @@ function collectPemFragmentSpans(scan, text, json, span) {
|
|
|
1248
1267
|
cur = next;
|
|
1249
1268
|
}
|
|
1250
1269
|
}
|
|
1251
|
-
function collectHeadlessPpkSpans(scan, text, span) {
|
|
1270
|
+
function collectHeadlessPpkSpans(scan, text, span, isHead) {
|
|
1252
1271
|
const KEY = "Private-Lines: ";
|
|
1253
1272
|
const n = text.length;
|
|
1273
|
+
const LINE_NONE = -1;
|
|
1274
|
+
const LINE_OPEN = -2;
|
|
1275
|
+
const openAt = (i) => {
|
|
1276
|
+
if (!isHead)
|
|
1277
|
+
return false;
|
|
1278
|
+
let k = i;
|
|
1279
|
+
while (k < n && opaqueUnitEnd(scan, k) < 0 && text.charCodeAt(k) === 92)
|
|
1280
|
+
k += 1;
|
|
1281
|
+
return k >= n;
|
|
1282
|
+
};
|
|
1254
1283
|
const isB64 = (c) => (c >= 48 && c <= 57) || (c >= 65 && c <= 90) || (c >= 97 && c <= 122) || c === 43 || c === 47 || c === 61;
|
|
1255
1284
|
const b64At = (i) => opaqueUnitEnd(scan, i) < 0 && isB64(text.charCodeAt(i));
|
|
1256
1285
|
const skipHs = (i) => { while (i < n && opaqueUnitEnd(scan, i) < 0 && (text.charCodeAt(i) === 32 || text.charCodeAt(i) === 9))
|
|
@@ -1289,7 +1318,9 @@ function collectHeadlessPpkSpans(scan, text, span) {
|
|
|
1289
1318
|
seen = true;
|
|
1290
1319
|
q = skipHs(q);
|
|
1291
1320
|
}
|
|
1292
|
-
|
|
1321
|
+
if (openAt(q))
|
|
1322
|
+
return LINE_OPEN;
|
|
1323
|
+
return seen ? q : LINE_NONE;
|
|
1293
1324
|
};
|
|
1294
1325
|
const escapedNewlineLen = (i) => {
|
|
1295
1326
|
if (opaqueUnitEnd(scan, i) >= 0)
|
|
@@ -1304,13 +1335,35 @@ function collectHeadlessPpkSpans(scan, text, span) {
|
|
|
1304
1335
|
};
|
|
1305
1336
|
const atLineEnd = (i) => {
|
|
1306
1337
|
const b = skipHs(i);
|
|
1307
|
-
if (b >= n)
|
|
1338
|
+
if (b >= n || openAt(b))
|
|
1308
1339
|
return true;
|
|
1309
1340
|
if (opaqueUnitEnd(scan, b) >= 0)
|
|
1310
1341
|
return false;
|
|
1311
1342
|
const c = text.charCodeAt(b);
|
|
1312
1343
|
return c === 10 || c === 13 || c === 34 || escapedNewlineLen(b) > 0;
|
|
1313
1344
|
};
|
|
1345
|
+
const bodyLineAt = (from) => {
|
|
1346
|
+
const q0 = skipNewlines(from);
|
|
1347
|
+
if (q0 < 0)
|
|
1348
|
+
return q0;
|
|
1349
|
+
let q = q0;
|
|
1350
|
+
const mk = opaqueUnitEnd(scan, q) < 0 ? text.charCodeAt(q) : -1;
|
|
1351
|
+
if (mk === 43 || mk === 45)
|
|
1352
|
+
q = skipHs(q + 1);
|
|
1353
|
+
let r = q;
|
|
1354
|
+
while (r < n && b64At(r))
|
|
1355
|
+
r += 1;
|
|
1356
|
+
if (r - q < 4) {
|
|
1357
|
+
if (isHead && r >= n)
|
|
1358
|
+
return LINE_OPEN;
|
|
1359
|
+
r = q0;
|
|
1360
|
+
while (r < n && b64At(r))
|
|
1361
|
+
r += 1;
|
|
1362
|
+
if (r - q0 < 4)
|
|
1363
|
+
return LINE_NONE;
|
|
1364
|
+
}
|
|
1365
|
+
return atLineEnd(r) ? r : LINE_NONE;
|
|
1366
|
+
};
|
|
1314
1367
|
let search = 0;
|
|
1315
1368
|
for (;;) {
|
|
1316
1369
|
const at = text.indexOf(KEY, search);
|
|
@@ -1324,36 +1377,21 @@ function collectHeadlessPpkSpans(scan, text, span) {
|
|
|
1324
1377
|
const d0 = p;
|
|
1325
1378
|
while (p < n && opaqueUnitEnd(scan, p) < 0 && text.charCodeAt(p) >= 48 && text.charCodeAt(p) <= 57)
|
|
1326
1379
|
p += 1;
|
|
1327
|
-
if (p === d0) {
|
|
1380
|
+
if (p === d0 && p < n) {
|
|
1328
1381
|
search = at + KEY.length;
|
|
1329
1382
|
continue;
|
|
1330
1383
|
}
|
|
1331
|
-
let end =
|
|
1332
|
-
let
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
if (q0 === -1)
|
|
1384
|
+
let end = -1;
|
|
1385
|
+
for (let from = p;;) {
|
|
1386
|
+
const r = bodyLineAt(from);
|
|
1387
|
+
if (r === LINE_NONE)
|
|
1336
1388
|
break;
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
if (mk === 43 || mk === 45)
|
|
1340
|
-
q = skipHs(q + 1);
|
|
1341
|
-
let r = q;
|
|
1342
|
-
while (r < n && b64At(r))
|
|
1343
|
-
r += 1;
|
|
1344
|
-
if (r - q < 4) {
|
|
1345
|
-
r = q0;
|
|
1346
|
-
while (r < n && b64At(r))
|
|
1347
|
-
r += 1;
|
|
1348
|
-
if (r - q0 < 4)
|
|
1349
|
-
break;
|
|
1350
|
-
}
|
|
1351
|
-
if (!atLineEnd(r))
|
|
1389
|
+
end = r === LINE_OPEN ? n : r;
|
|
1390
|
+
if (r === LINE_OPEN)
|
|
1352
1391
|
break;
|
|
1353
|
-
|
|
1354
|
-
end = r;
|
|
1392
|
+
from = r;
|
|
1355
1393
|
}
|
|
1356
|
-
if (
|
|
1394
|
+
if (end < 0) {
|
|
1357
1395
|
search = p;
|
|
1358
1396
|
continue;
|
|
1359
1397
|
}
|
|
@@ -1442,10 +1480,6 @@ const isRegexSpace = (c) => c === 32 || (c >= 9 && c <= 13) || c === 0xa0 || c =
|
|
|
1442
1480
|
const isLineTerminator = (c) => c === 10 || c === 13 || c === 0x2028 || c === 0x2029;
|
|
1443
1481
|
const isControlCode = (c) => c < 0x20 || c === 0x7f;
|
|
1444
1482
|
const TOKEN_UNIT_Y = new RegExp(REDACTION_TOKEN_RE_SRC, "y");
|
|
1445
|
-
export const CREDENTIAL_PARAM_NAMES = Object.freeze([...CREDENTIAL_NAME_WORDS]);
|
|
1446
|
-
export function isCredentialParamName(decodedName) {
|
|
1447
|
-
return credentialNameAnchor(decodedName) !== undefined;
|
|
1448
|
-
}
|
|
1449
1483
|
const CREDENTIAL_NAME_SCAN_MAX = CREDENTIAL_NAME_MAX * 3 + 1;
|
|
1450
1484
|
function decodeAsciiName(name) {
|
|
1451
1485
|
let out = "";
|
|
@@ -1757,10 +1791,12 @@ function scanUrlRun(scan, text, i, kind, json) {
|
|
|
1757
1791
|
noteEvidenceRead(scan, i);
|
|
1758
1792
|
return i;
|
|
1759
1793
|
}
|
|
1760
|
-
function matchUserinfoAt(scan, text, p, json) {
|
|
1794
|
+
function matchUserinfoAt(scan, text, p, json, isHead) {
|
|
1761
1795
|
const probeEnd = scanUrlRun(scan, text, p, "plain-user", json);
|
|
1762
1796
|
const keyId = probeEnd > p && text.charCodeAt(probeEnd) === 58 && credentialSpansCover(scan, p, probeEnd);
|
|
1763
|
-
scanUrlRun(scan, text, p, keyId ? "token-pass" : "plain-pass", json);
|
|
1797
|
+
const runEnd = scanUrlRun(scan, text, p, keyId ? "token-pass" : "plain-pass", json);
|
|
1798
|
+
if (isHead && runEnd >= text.length && runEnd > p)
|
|
1799
|
+
return runEnd;
|
|
1764
1800
|
const at = scan.lastAt;
|
|
1765
1801
|
if (at <= p)
|
|
1766
1802
|
return null;
|
|
@@ -1778,7 +1814,7 @@ function growUrlSegments(list) {
|
|
|
1778
1814
|
list.a = bigger;
|
|
1779
1815
|
return true;
|
|
1780
1816
|
}
|
|
1781
|
-
function collectUrlCredentialSpans(scan, text, json, span) {
|
|
1817
|
+
function collectUrlCredentialSpans(scan, text, json, span, isHead) {
|
|
1782
1818
|
const dirty = scan.dirty;
|
|
1783
1819
|
const hr = scan.headsRead;
|
|
1784
1820
|
const hw = scan.headsWrite;
|
|
@@ -1814,7 +1850,7 @@ function collectUrlCredentialSpans(scan, text, json, span) {
|
|
|
1814
1850
|
else {
|
|
1815
1851
|
scan.footLo = FOOT_NONE;
|
|
1816
1852
|
scan.footHi = -1;
|
|
1817
|
-
at = matchUserinfoAt(scan, text, start, json) ?? -1;
|
|
1853
|
+
at = matchUserinfoAt(scan, text, start, json, isHead) ?? -1;
|
|
1818
1854
|
lo = scan.footLo;
|
|
1819
1855
|
hi = scan.footHi;
|
|
1820
1856
|
urlSegmentRuns += 1;
|
|
@@ -1915,7 +1951,7 @@ function jsonSecretFieldValueQuoteAt(text, p) {
|
|
|
1915
1951
|
i += 1;
|
|
1916
1952
|
return text.charCodeAt(i) === CH_QUOTE ? i : -1;
|
|
1917
1953
|
}
|
|
1918
|
-
function jsonStringEndAt(text, q) {
|
|
1954
|
+
function jsonStringEndAt(text, q, isHead) {
|
|
1919
1955
|
const n = text.length;
|
|
1920
1956
|
for (let i = q + 1; i < n;) {
|
|
1921
1957
|
const c = text.charCodeAt(i);
|
|
@@ -1926,16 +1962,16 @@ function jsonStringEndAt(text, q) {
|
|
|
1926
1962
|
i += 2;
|
|
1927
1963
|
continue;
|
|
1928
1964
|
}
|
|
1929
|
-
return -1;
|
|
1965
|
+
return isHead && i + 1 >= n ? n : -1;
|
|
1930
1966
|
}
|
|
1931
1967
|
i += 1;
|
|
1932
1968
|
}
|
|
1933
|
-
return -1;
|
|
1969
|
+
return isHead ? n : -1;
|
|
1934
1970
|
}
|
|
1935
|
-
function collectJsonSecretFieldSpans(text, span) {
|
|
1971
|
+
function collectJsonSecretFieldSpans(text, span, isHead) {
|
|
1936
1972
|
for (let p = text.indexOf(JSON_FIELD_ANCHOR); p >= 0;) {
|
|
1937
1973
|
const q = jsonSecretFieldValueQuoteAt(text, p);
|
|
1938
|
-
const end = q < 0 ? -1 : jsonStringEndAt(text, q);
|
|
1974
|
+
const end = q < 0 ? -1 : jsonStringEndAt(text, q, isHead);
|
|
1939
1975
|
if (end < 0) {
|
|
1940
1976
|
p = text.indexOf(JSON_FIELD_ANCHOR, p + 1);
|
|
1941
1977
|
continue;
|
|
@@ -2088,7 +2124,7 @@ function anchorHeadAt(text, op, json) {
|
|
|
2088
2124
|
}
|
|
2089
2125
|
return { strength, quotedName: quote !== 0, spacedLeft, listSep };
|
|
2090
2126
|
}
|
|
2091
|
-
function quotedValueEnd(text, cs, quote, json) {
|
|
2127
|
+
function quotedValueEnd(text, cs, quote, json, isHead) {
|
|
2092
2128
|
const n = text.length;
|
|
2093
2129
|
let i = cs;
|
|
2094
2130
|
while (i < n) {
|
|
@@ -2097,18 +2133,18 @@ function quotedValueEnd(text, cs, quote, json) {
|
|
|
2097
2133
|
if (ch === quote)
|
|
2098
2134
|
return i;
|
|
2099
2135
|
if (ch === JSON_STRING_END)
|
|
2100
|
-
|
|
2136
|
+
return -1;
|
|
2101
2137
|
i += unitLenOf(unit);
|
|
2102
2138
|
if (ch === CH_BACKSLASH && quote !== JSON_STRING_END) {
|
|
2103
2139
|
if (i >= n)
|
|
2104
2140
|
break;
|
|
2105
2141
|
const escaped = readAnchorUnit(text, i, json);
|
|
2106
2142
|
if (unitChOf(escaped) === JSON_STRING_END)
|
|
2107
|
-
|
|
2143
|
+
return -1;
|
|
2108
2144
|
i += unitLenOf(escaped);
|
|
2109
2145
|
}
|
|
2110
2146
|
}
|
|
2111
|
-
return -1;
|
|
2147
|
+
return isHead ? n : -1;
|
|
2112
2148
|
}
|
|
2113
2149
|
function bareValueEnd(text, s, json, listSep) {
|
|
2114
2150
|
const n = text.length;
|
|
@@ -2153,7 +2189,7 @@ function bareValueEnd(text, s, json, listSep) {
|
|
|
2153
2189
|
}
|
|
2154
2190
|
return e;
|
|
2155
2191
|
}
|
|
2156
|
-
function literalValueAt(text, s, json, declaredIfBare, listSep) {
|
|
2192
|
+
function literalValueAt(text, s, json, declaredIfBare, listSep, isHead) {
|
|
2157
2193
|
const n = text.length;
|
|
2158
2194
|
if (s >= n)
|
|
2159
2195
|
return undefined;
|
|
@@ -2166,7 +2202,7 @@ function literalValueAt(text, s, json, declaredIfBare, listSep) {
|
|
|
2166
2202
|
return undefined;
|
|
2167
2203
|
if (!isAsciiAlnumCode(unitChOf(readAnchorUnit(text, vs, json))))
|
|
2168
2204
|
return undefined;
|
|
2169
|
-
const close = quotedValueEnd(text, vs, first, json);
|
|
2205
|
+
const close = quotedValueEnd(text, vs, first, json, isHead);
|
|
2170
2206
|
if (close >= 0)
|
|
2171
2207
|
return { start: vs, end: close, quoted: true };
|
|
2172
2208
|
}
|
|
@@ -2234,7 +2270,7 @@ function schemeValueAt(text, s, json) {
|
|
|
2234
2270
|
return undefined;
|
|
2235
2271
|
return { start: vs, end: e, quoted: false };
|
|
2236
2272
|
}
|
|
2237
|
-
function collectAnchoredValueSpans(text, json, span) {
|
|
2273
|
+
function collectAnchoredValueSpans(text, json, span, isHead) {
|
|
2238
2274
|
const n = text.length;
|
|
2239
2275
|
let resume = 0;
|
|
2240
2276
|
let nextEq = text.indexOf("=");
|
|
@@ -2275,10 +2311,10 @@ function collectAnchoredValueSpans(text, json, span) {
|
|
|
2275
2311
|
v = schemeValueAt(text, s, json);
|
|
2276
2312
|
break;
|
|
2277
2313
|
case "declarative":
|
|
2278
|
-
v = literalValueAt(text, s, json, head.quotedName || (!head.spacedLeft && !spacedRight), head.listSep);
|
|
2314
|
+
v = literalValueAt(text, s, json, head.quotedName || (!head.spacedLeft && !spacedRight), head.listSep, isHead);
|
|
2279
2315
|
break;
|
|
2280
2316
|
case "any-form":
|
|
2281
|
-
v = literalValueAt(text, s, json, true, head.listSep);
|
|
2317
|
+
v = literalValueAt(text, s, json, true, head.listSep, isHead);
|
|
2282
2318
|
break;
|
|
2283
2319
|
default: {
|
|
2284
2320
|
const unhandled = head.strength;
|
|
@@ -2294,7 +2330,7 @@ function collectAnchoredValueSpans(text, json, span) {
|
|
|
2294
2330
|
export function structuralScannerForTest(arm) {
|
|
2295
2331
|
const collect = (scan) => (text) => {
|
|
2296
2332
|
const out = [];
|
|
2297
|
-
scan(text, (start, end) => out.push([start, end]));
|
|
2333
|
+
scan(text, (start, end) => out.push([start, end]), false);
|
|
2298
2334
|
return out;
|
|
2299
2335
|
};
|
|
2300
2336
|
switch (arm) {
|
|
@@ -2307,7 +2343,7 @@ export function structuralScannerForTest(arm) {
|
|
|
2307
2343
|
let ppkScanBytes = 0;
|
|
2308
2344
|
export function ppkScanBytesForTest() { return ppkScanBytes; }
|
|
2309
2345
|
export function resetPpkScanBytesForTest() { ppkScanBytes = 0; }
|
|
2310
|
-
function collectPpkContainerSpans(scan, text, eat, span) {
|
|
2346
|
+
function collectPpkContainerSpans(scan, text, eat, span, isHead) {
|
|
2311
2347
|
const HEAD = "PuTTY-User-Key-File-";
|
|
2312
2348
|
const MAC = "Private-MAC: ";
|
|
2313
2349
|
const HASH = "Private-Hash: ";
|
|
@@ -2323,9 +2359,8 @@ function collectPpkContainerSpans(scan, text, eat, span) {
|
|
|
2323
2359
|
cache.set(needle, v);
|
|
2324
2360
|
return v;
|
|
2325
2361
|
};
|
|
2326
|
-
const
|
|
2362
|
+
const versionEnd = (at) => {
|
|
2327
2363
|
let p = at + HEAD.length;
|
|
2328
|
-
const start = p;
|
|
2329
2364
|
while (p < text.length && opaqueUnitEnd(scan, p) < 0) {
|
|
2330
2365
|
const c = text.charCodeAt(p);
|
|
2331
2366
|
if (c >= 48 && c <= 57)
|
|
@@ -2333,7 +2368,15 @@ function collectPpkContainerSpans(scan, text, eat, span) {
|
|
|
2333
2368
|
else
|
|
2334
2369
|
break;
|
|
2335
2370
|
}
|
|
2336
|
-
return p
|
|
2371
|
+
return p;
|
|
2372
|
+
};
|
|
2373
|
+
const isVersionedHead = (at) => {
|
|
2374
|
+
const p = versionEnd(at);
|
|
2375
|
+
return p > at + HEAD.length && text.startsWith(": ", p);
|
|
2376
|
+
};
|
|
2377
|
+
const isOpenHead = (at) => {
|
|
2378
|
+
const p = versionEnd(at);
|
|
2379
|
+
return p >= text.length || (p === text.length - 1 && p > at + HEAD.length && text.charCodeAt(p) === 58);
|
|
2337
2380
|
};
|
|
2338
2381
|
const nextVersionedHead = (from) => {
|
|
2339
2382
|
let p = find(HEAD, from);
|
|
@@ -2356,7 +2399,7 @@ function collectPpkContainerSpans(scan, text, eat, span) {
|
|
|
2356
2399
|
while (at !== Number.POSITIVE_INFINITY) {
|
|
2357
2400
|
let pos = at + HEAD.length + 1;
|
|
2358
2401
|
let end = text.length;
|
|
2359
|
-
let ateToEnd =
|
|
2402
|
+
let ateToEnd = !isHead;
|
|
2360
2403
|
for (;;) {
|
|
2361
2404
|
const mac = find(MAC, pos);
|
|
2362
2405
|
const hash = find(HASH, pos);
|
|
@@ -2389,8 +2432,9 @@ function collectPpkContainerSpans(scan, text, eat, span) {
|
|
|
2389
2432
|
}
|
|
2390
2433
|
}
|
|
2391
2434
|
}
|
|
2435
|
+
const undecided = isHead && limit === Number.POSITIVE_INFINITY;
|
|
2392
2436
|
ateToEnd = straggler < limit || !gapBlank;
|
|
2393
|
-
end = ateToEnd ? text.length : hexEnd;
|
|
2437
|
+
end = ateToEnd || undecided ? text.length : hexEnd;
|
|
2394
2438
|
break;
|
|
2395
2439
|
}
|
|
2396
2440
|
if (ateToEnd && eat !== undefined && eat.from < 0) {
|
|
@@ -2400,6 +2444,9 @@ function collectPpkContainerSpans(scan, text, eat, span) {
|
|
|
2400
2444
|
span(at, end);
|
|
2401
2445
|
at = end >= text.length ? Number.POSITIVE_INFINITY : nextVersionedHead(end);
|
|
2402
2446
|
}
|
|
2447
|
+
const last = isHead ? text.lastIndexOf(HEAD) : -1;
|
|
2448
|
+
if (last >= 0 && !inOpaqueUnit(scan, last) && isOpenHead(last))
|
|
2449
|
+
span(last, text.length);
|
|
2403
2450
|
}
|
|
2404
2451
|
const armPool = (() => {
|
|
2405
2452
|
const spanRepl = (token) => {
|
|
@@ -2444,7 +2491,7 @@ const armPool = (() => {
|
|
|
2444
2491
|
order,
|
|
2445
2492
|
phase: 1,
|
|
2446
2493
|
lexical,
|
|
2447
|
-
collect: (text, json, eat, out) => arm(text, json, eat, (start, end) => out.push({ start, end, repl, rank, order })),
|
|
2494
|
+
collect: (text, json, eat, out, isHead) => arm(text, json, eat, (start, end) => out.push({ start, end, repl, rank, order }), isHead),
|
|
2448
2495
|
retry: undefined,
|
|
2449
2496
|
headSrc: undefined,
|
|
2450
2497
|
headFirst: undefined,
|
|
@@ -2459,7 +2506,7 @@ const armPool = (() => {
|
|
|
2459
2506
|
order,
|
|
2460
2507
|
phase: 2,
|
|
2461
2508
|
lexical: false,
|
|
2462
|
-
collect: (scan, text, json, eat, out) => arm(scan, text, json, eat, (start, end) => out.push({ start, end, repl, rank, order })),
|
|
2509
|
+
collect: (scan, text, json, eat, out, isHead) => arm(scan, text, json, eat, (start, end) => out.push({ start, end, repl, rank, order }), isHead),
|
|
2463
2510
|
retry: undefined,
|
|
2464
2511
|
headSrc: undefined,
|
|
2465
2512
|
headFirst: undefined,
|
|
@@ -2467,22 +2514,22 @@ const armPool = (() => {
|
|
|
2467
2514
|
};
|
|
2468
2515
|
const structuralScannerMember = (row, order) => {
|
|
2469
2516
|
if (row === PEM_FENCE_PATTERN)
|
|
2470
|
-
return scannerMember(row[1], order, (text, _json, _eat, span) => collectPemFenceSpans(text, span));
|
|
2517
|
+
return scannerMember(row[1], order, (text, _json, _eat, span, isHead) => collectPemFenceSpans(text, span, isHead));
|
|
2471
2518
|
if (row === SSH2_FENCE_PATTERN)
|
|
2472
|
-
return forensicMember(row[1], order, (scan, text, _json, _eat, span) => collectSsh2FenceSpans(scan, text, span));
|
|
2519
|
+
return forensicMember(row[1], order, (scan, text, _json, _eat, span, isHead) => collectSsh2FenceSpans(scan, text, span, isHead));
|
|
2473
2520
|
if (row === URL_CREDENTIAL_PATTERN)
|
|
2474
|
-
return forensicMember(row[1], order, (scan, text, json, _eat, span) => collectUrlCredentialSpans(scan, text, json, span));
|
|
2521
|
+
return forensicMember(row[1], order, (scan, text, json, _eat, span, isHead) => collectUrlCredentialSpans(scan, text, json, span, isHead));
|
|
2475
2522
|
if (row === JSON_SECRET_FIELD_PATTERN)
|
|
2476
|
-
return scannerMember(row[1], order, (text, _json, _eat, span) => collectJsonSecretFieldSpans(text, span));
|
|
2523
|
+
return scannerMember(row[1], order, (text, _json, _eat, span, isHead) => collectJsonSecretFieldSpans(text, span, isHead));
|
|
2477
2524
|
throw new Error(`redact: structural row /${row[0].source}/ has no hand-written scanner — structural grammars are declared as regexes but never executed as one (S-102 / S-265)`);
|
|
2478
2525
|
};
|
|
2479
2526
|
const members = [];
|
|
2480
2527
|
for (const row of STRUCTURAL_SECRET_PATTERNS)
|
|
2481
2528
|
members.push(structuralScannerMember(row, members.length));
|
|
2482
|
-
members.push(forensicMember(PPK_CONTAINER_TOKEN, members.length, (scan, text, _json, eat, span) => collectPpkContainerSpans(scan, text, eat, span)));
|
|
2529
|
+
members.push(forensicMember(PPK_CONTAINER_TOKEN, members.length, (scan, text, _json, eat, span, isHead) => collectPpkContainerSpans(scan, text, eat, span, isHead)));
|
|
2483
2530
|
members.push(forensicMember(PEM_FRAGMENT_TOKEN, members.length, (scan, text, json, _eat, span) => collectPemFragmentSpans(scan, text, json, span)));
|
|
2484
|
-
members.push(forensicMember(HEADLESS_PPK_TOKEN, members.length, (scan, text, _json, _eat, span) => collectHeadlessPpkSpans(scan, text, span)));
|
|
2485
|
-
members.push(scannerMember(ANCHORED_VALUE_TOKEN, members.length, (text, json, _eat, span) => collectAnchoredValueSpans(text, json, span), ARM_RANK.keyword, true));
|
|
2531
|
+
members.push(forensicMember(HEADLESS_PPK_TOKEN, members.length, (scan, text, _json, _eat, span, isHead) => collectHeadlessPpkSpans(scan, text, span, isHead)));
|
|
2532
|
+
members.push(scannerMember(ANCHORED_VALUE_TOKEN, members.length, (text, json, _eat, span, isHead) => collectAnchoredValueSpans(text, json, span, isHead), ARM_RANK.keyword, true));
|
|
2486
2533
|
for (const row of KEYWORD_SECRET_PATTERNS)
|
|
2487
2534
|
members.push(regexMember(row, ARM_RANK.keyword, members.length));
|
|
2488
2535
|
for (const row of SHAPE_SECRET_PATTERNS)
|
|
@@ -2513,7 +2560,7 @@ const armPool = (() => {
|
|
|
2513
2560
|
const headFirstUnit = unionMembers(retryMembers.map((m) => m.headFirst)) ?? new Uint32Array(0x10000 >>> 5).fill(0xffffffff);
|
|
2514
2561
|
poolArmCount = members.length;
|
|
2515
2562
|
poolBoundaryArmCount = retryMembers.length;
|
|
2516
|
-
return (text, wantCount, json, eat, scope) => {
|
|
2563
|
+
return (text, wantCount, json, eat, scope, isHead) => {
|
|
2517
2564
|
readings += 1;
|
|
2518
2565
|
const lane = LANES[scope];
|
|
2519
2566
|
const spans = [];
|
|
@@ -2550,7 +2597,7 @@ const armPool = (() => {
|
|
|
2550
2597
|
}
|
|
2551
2598
|
};
|
|
2552
2599
|
for (const m of lane.phase1)
|
|
2553
|
-
m.collect(text, json, eat, spans);
|
|
2600
|
+
m.collect(text, json, eat, spans, isHead);
|
|
2554
2601
|
closeCoveragePositions(0);
|
|
2555
2602
|
if (lane.phase2.length > 0) {
|
|
2556
2603
|
const scan = buildForensicScan(credentialSpans(text), lane.phase2.length);
|
|
@@ -2567,7 +2614,7 @@ const armPool = (() => {
|
|
|
2567
2614
|
}
|
|
2568
2615
|
scan.footLo = FOOT_NONE;
|
|
2569
2616
|
scan.footHi = -1;
|
|
2570
|
-
lane.phase2[j].collect(scan, text, json, level === 0 ? eat : undefined, spans);
|
|
2617
|
+
lane.phase2[j].collect(scan, text, json, level === 0 ? eat : undefined, spans, isHead);
|
|
2571
2618
|
foot[2 * j] = scan.footLo;
|
|
2572
2619
|
foot[2 * j + 1] = scan.footHi;
|
|
2573
2620
|
forensicRuns += 1;
|
|
@@ -2613,8 +2660,8 @@ let onRedaction;
|
|
|
2613
2660
|
export function setRedactionObserver(fn) {
|
|
2614
2661
|
onRedaction = fn;
|
|
2615
2662
|
}
|
|
2616
|
-
function applyRules(text, wantCount, json, eat, scope) {
|
|
2617
|
-
return armPool(text, wantCount, json, eat, scope);
|
|
2663
|
+
function applyRules(text, wantCount, json, eat, scope, isHead) {
|
|
2664
|
+
return armPool(text, wantCount, json, eat, scope, isHead);
|
|
2618
2665
|
}
|
|
2619
2666
|
function reportHits(hits) {
|
|
2620
2667
|
if (onRedaction === undefined)
|
|
@@ -2639,7 +2686,99 @@ function invisibleClassesOf(layout) {
|
|
|
2639
2686
|
}
|
|
2640
2687
|
}
|
|
2641
2688
|
}
|
|
2642
|
-
function
|
|
2689
|
+
function trailingPartialEscapeStart(s) {
|
|
2690
|
+
const n = s.length;
|
|
2691
|
+
const runStart = (end) => {
|
|
2692
|
+
let k = end;
|
|
2693
|
+
while (k > 0 && s.charCodeAt(k - 1) === CH_BACKSLASH)
|
|
2694
|
+
k -= 1;
|
|
2695
|
+
return k;
|
|
2696
|
+
};
|
|
2697
|
+
const highSurrogateEscapeStartBefore = (end) => {
|
|
2698
|
+
let j = end;
|
|
2699
|
+
let value = -1;
|
|
2700
|
+
let bodyStart = -1;
|
|
2701
|
+
if (j >= 1 && s.charCodeAt(j - 1) === CH_RBRACE) {
|
|
2702
|
+
let k = j - 1;
|
|
2703
|
+
while (k > 0 && hexDigitValue(s.charCodeAt(k - 1)) >= 0)
|
|
2704
|
+
k -= 1;
|
|
2705
|
+
if (k >= 2 && s.charCodeAt(k - 1) === CH_LBRACE && s.charCodeAt(k - 2) === CH_u && j - 1 - k >= 1) {
|
|
2706
|
+
value = readHexRunValue(s, k, j - 1);
|
|
2707
|
+
bodyStart = k - 2;
|
|
2708
|
+
}
|
|
2709
|
+
}
|
|
2710
|
+
else if (j >= 5 && s.charCodeAt(j - 5) === CH_u) {
|
|
2711
|
+
value = readHexRun(s, j - 4, 4);
|
|
2712
|
+
bodyStart = j - 5;
|
|
2713
|
+
}
|
|
2714
|
+
if (value < 0xd800 || value > 0xdbff)
|
|
2715
|
+
return -1;
|
|
2716
|
+
const k = runStart(bodyStart);
|
|
2717
|
+
return k < bodyStart ? k : -1;
|
|
2718
|
+
};
|
|
2719
|
+
let j = n;
|
|
2720
|
+
while (j > 0 && hexDigitValue(s.charCodeAt(j - 1)) >= 0)
|
|
2721
|
+
j -= 1;
|
|
2722
|
+
const hexRun = n - j;
|
|
2723
|
+
let element = -1;
|
|
2724
|
+
if (j >= 2 && s.charCodeAt(j - 1) === CH_LBRACE && s.charCodeAt(j - 2) === CH_u)
|
|
2725
|
+
element = j - 2;
|
|
2726
|
+
else if (j >= 1 && s.charCodeAt(j - 1) === CH_u && hexRun <= 3)
|
|
2727
|
+
element = j - 1;
|
|
2728
|
+
else if (j >= 1 && s.charCodeAt(j - 1) === CH_x && hexRun <= 1)
|
|
2729
|
+
element = j - 1;
|
|
2730
|
+
else if (hexRun === 0 && n >= 1 && s.charCodeAt(n - 1) === CH_BACKSLASH)
|
|
2731
|
+
element = n;
|
|
2732
|
+
if (element >= 0) {
|
|
2733
|
+
const k = runStart(element);
|
|
2734
|
+
if (k >= element)
|
|
2735
|
+
return -1;
|
|
2736
|
+
const high = highSurrogateEscapeStartBefore(k);
|
|
2737
|
+
return high >= 0 ? high : k;
|
|
2738
|
+
}
|
|
2739
|
+
return highSurrogateEscapeStartBefore(n);
|
|
2740
|
+
}
|
|
2741
|
+
function readHexRunValue(s, from, to) {
|
|
2742
|
+
let v = 0;
|
|
2743
|
+
for (let k = from; k < to; k++) {
|
|
2744
|
+
const d = hexDigitValue(s.charCodeAt(k));
|
|
2745
|
+
if (d < 0)
|
|
2746
|
+
return -1;
|
|
2747
|
+
v = v * 16 + d;
|
|
2748
|
+
if (v > 0x10ffff)
|
|
2749
|
+
return -1;
|
|
2750
|
+
}
|
|
2751
|
+
return v;
|
|
2752
|
+
}
|
|
2753
|
+
function shortestReading(text, layout) {
|
|
2754
|
+
const raw = collectRawControlCuts(text, invisibleClassesOf(layout));
|
|
2755
|
+
const normalized = normalizeForRedaction(deleteInvisibleCharacters(text, raw, raw.present).text);
|
|
2756
|
+
const open = trailingPartialEscapeStart(normalized);
|
|
2757
|
+
const spelled = collectSpelledCuts(normalized);
|
|
2758
|
+
const length = deleteInvisibleCharacters(normalized, spelled, spelled.present).text.length;
|
|
2759
|
+
return open < 0 ? { visible: length, partial: false } : { visible: length - (normalized.length - open), partial: true };
|
|
2760
|
+
}
|
|
2761
|
+
export function readingHeadEnd(text, layout, units, from = 0) {
|
|
2762
|
+
const rest = text.length - from;
|
|
2763
|
+
let len = Math.min(rest, units);
|
|
2764
|
+
let first = true;
|
|
2765
|
+
let grow = 64;
|
|
2766
|
+
while (len < rest) {
|
|
2767
|
+
const { visible, partial } = shortestReading(text.slice(from, from + len), layout);
|
|
2768
|
+
if (visible >= units) {
|
|
2769
|
+
if (!partial)
|
|
2770
|
+
return from + len;
|
|
2771
|
+
len = Math.min(rest, len + grow);
|
|
2772
|
+
grow *= 2;
|
|
2773
|
+
continue;
|
|
2774
|
+
}
|
|
2775
|
+
const step = first && visible * 2 >= len ? Math.ceil((units - visible) / (visible / len)) + 1 : Math.max(units - visible, len);
|
|
2776
|
+
first = false;
|
|
2777
|
+
len = Math.min(rest, len + step);
|
|
2778
|
+
}
|
|
2779
|
+
return text.length;
|
|
2780
|
+
}
|
|
2781
|
+
function runRedaction(text, observe, json, wantHits, scope, layout, extent) {
|
|
2643
2782
|
const wantCount = wantHits || (onRedaction !== undefined && observe);
|
|
2644
2783
|
const eat = observe ? { from: -1, len: 0 } : undefined;
|
|
2645
2784
|
let spans = [];
|
|
@@ -2647,7 +2786,7 @@ function runRedaction(text, observe, json, wantHits, scope, layout) {
|
|
|
2647
2786
|
let readsSoFar = 0;
|
|
2648
2787
|
let unioned = false;
|
|
2649
2788
|
const read = (layers, view) => {
|
|
2650
|
-
const r = applyRules(view, wantCount, json, eat, scope);
|
|
2789
|
+
const r = applyRules(view, wantCount, json, eat, scope, extent === "head");
|
|
2651
2790
|
forgetEscapedSeparatorTails();
|
|
2652
2791
|
readsSoFar += 1;
|
|
2653
2792
|
if (readsSoFar === 1) {
|
|
@@ -2720,18 +2859,18 @@ function spansInOriginal(layers, kept) {
|
|
|
2720
2859
|
return out;
|
|
2721
2860
|
}
|
|
2722
2861
|
export function redactSecrets(text, opts) {
|
|
2723
|
-
return runRedaction(text, opts?.observe ?? true, opts?.json === true, false, "free-text", "multi-line").text;
|
|
2862
|
+
return runRedaction(text, opts?.observe ?? true, opts?.json === true, false, "free-text", "multi-line", "whole").text;
|
|
2724
2863
|
}
|
|
2725
2864
|
export function redactSecretsWithArms(text, opts) {
|
|
2726
|
-
const r = runRedaction(text, opts?.observe ?? true, opts?.json === true, true, "free-text", "multi-line");
|
|
2865
|
+
const r = runRedaction(text, opts?.observe ?? true, opts?.json === true, true, "free-text", "multi-line", "whole");
|
|
2727
2866
|
return { text: r.text, arms: new Set(r.hits.map((h) => h.arm)) };
|
|
2728
2867
|
}
|
|
2729
2868
|
export function redactionSpansForTest(text, opts) {
|
|
2730
|
-
const r = runRedaction(text, false, opts?.json === true, false, "free-text", "multi-line");
|
|
2869
|
+
const r = runRedaction(text, false, opts?.json === true, false, "free-text", "multi-line", "whole");
|
|
2731
2870
|
return { text: r.text, spans: r.spans.map((sp) => ({ start: sp.start, end: sp.end })) };
|
|
2732
2871
|
}
|
|
2733
|
-
export function proposeRedactionSpans(text, scope, layout) {
|
|
2734
|
-
return runRedaction(text, true, false, false, scope, layout).spans.map((sp) => ({ start: sp.start, end: sp.end, repl: sp.repl }));
|
|
2872
|
+
export function proposeRedactionSpans(text, scope, layout, extent) {
|
|
2873
|
+
return runRedaction(text, true, false, false, scope, layout, extent).spans.map((sp) => ({ start: sp.start, end: sp.end, repl: sp.repl }));
|
|
2735
2874
|
}
|
|
2736
2875
|
const REDACT_MAX_DEPTH = 64;
|
|
2737
2876
|
export function redactDeep(value) {
|
|
@@ -2833,7 +2972,7 @@ function unitCarriesCredential(unit) {
|
|
|
2833
2972
|
for (const v of armValueClassViews())
|
|
2834
2973
|
views.add(boundedUnitView(spelling, v.inClass, v.filler));
|
|
2835
2974
|
views.delete("");
|
|
2836
|
-
const hit = [...views].some((view) => runRedaction(view, true, false, false, "bounded-unit", "multi-line").text !== view);
|
|
2975
|
+
const hit = [...views].some((view) => runRedaction(view, true, false, false, "bounded-unit", "multi-line", "whole").text !== view);
|
|
2837
2976
|
return { hit, unreadable: decoded.residue };
|
|
2838
2977
|
}
|
|
2839
2978
|
function displaySafePathname(pathname) {
|
|
@@ -2863,7 +3002,7 @@ function shapeLawCredentialInQueryOrFragment(canonical) {
|
|
|
2863
3002
|
const boundary = canonical.indexOf("?");
|
|
2864
3003
|
if (boundary < 0)
|
|
2865
3004
|
return false;
|
|
2866
|
-
if (runRedaction(canonical, false, false, false, "free-text", "multi-line").spans.some((sp) => sp.end > boundary))
|
|
3005
|
+
if (runRedaction(canonical, false, false, false, "free-text", "multi-line", "whole").spans.some((sp) => sp.end > boundary))
|
|
2867
3006
|
return true;
|
|
2868
3007
|
for (const unit of canonical.slice(boundary + 1).split("&").flatMap((sg) => {
|
|
2869
3008
|
const eq = sg.indexOf("=");
|