@sema-agent/server 7.140.0 → 7.141.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 +290 -0
- package/README.md +1 -1
- package/README.zh-CN.md +1 -1
- package/USAGE.md +75 -13
- package/dist/approval-batch-census.d.ts +27 -0
- package/dist/approval-batch-census.js +27 -0
- package/dist/approval-card.d.ts +31 -16
- package/dist/approval-card.js +6 -3
- package/dist/approval.d.ts +15 -0
- package/dist/approval.js +10 -0
- package/dist/audit.d.ts +4 -2
- package/dist/audit.js +2 -2
- package/dist/boot/device-lane.d.ts +11 -0
- package/dist/boot/device-lane.js +16 -0
- package/dist/boot/engine-lease.d.ts +24 -3
- package/dist/boot/engine-lease.js +9 -2
- package/dist/boot/execution-env.js +8 -4
- package/dist/boot/memory-boundary.d.ts +2 -2
- package/dist/boot/memory-consolidation.d.ts +1 -1
- package/dist/boot/memory-consolidation.js +1 -1
- package/dist/boot/reapers.d.ts +2 -2
- package/dist/boot/resolve-spec.js +7 -4
- package/dist/boot/session-faces.d.ts +1 -1
- package/dist/boot/session-faces.js +4 -3
- package/dist/boot/shutdown.js +5 -0
- package/dist/boot/stage-05-execution-env.js +3 -1
- package/dist/boot/stage-06-runners.d.ts +0 -2
- package/dist/boot/stage-06-runners.js +1 -1
- package/dist/boot/stage-07-capability-layer.d.ts +0 -1
- package/dist/boot/stage-07-capability-layer.js +8 -10
- package/dist/boot/stage-08-reapers.d.ts +0 -1
- package/dist/boot/stage-09-leader.d.ts +0 -1
- package/dist/boot/stage-10-http-server.d.ts +0 -1
- package/dist/boot/stage-10-http-server.js +1 -0
- package/dist/boot/stores.js +10 -4
- package/dist/boot/web-admin.js +1 -0
- package/dist/capabilities/scenarios.d.ts +1 -1
- package/dist/capability-facts.d.ts +9 -0
- package/dist/capability-facts.js +9 -0
- package/dist/card-void.d.ts +38 -0
- package/dist/card-void.js +23 -0
- package/dist/config-catalog.js +11 -8
- package/dist/config-provider.d.ts +13 -13
- package/dist/config-provider.js +19 -33
- package/dist/config-types.d.ts +26 -8
- package/dist/config.js +55 -46
- package/dist/delegated-session-owner.d.ts +21 -19
- package/dist/delegated-session-owner.js +1 -1
- package/dist/device-store.d.ts +7 -0
- package/dist/device-store.js +3 -0
- package/dist/execution-config-root.d.ts +22 -0
- package/dist/execution-config-root.js +35 -0
- package/dist/execution-lane-caps.d.ts +49 -2
- package/dist/execution-lane-caps.js +90 -13
- package/dist/execution-placement.d.ts +3 -0
- package/dist/execution-placement.js +7 -0
- package/dist/fleet/drain-entry.d.ts +3 -0
- package/dist/hosted-posture.d.ts +8 -0
- package/dist/hosted-posture.js +4 -0
- package/dist/http/admission.js +3 -3
- package/dist/http/caller-trust.d.ts +3 -2
- package/dist/http/caller-trust.js +2 -1
- package/dist/http/dispatch.js +4 -0
- package/dist/http/live-frame.d.ts +24 -0
- package/dist/http/live-frame.js +16 -0
- package/dist/http/resume-legs.js +37 -20
- package/dist/http/route-table.js +2 -0
- package/dist/http/routes/approvals-assistant.js +1 -0
- package/dist/http/routes/capabilities.js +5 -4
- package/dist/http/routes/diagnostics.d.ts +0 -19
- package/dist/http/routes/diagnostics.js +16 -2
- package/dist/http/routes/memory-bundle.d.ts +2 -2
- package/dist/http/routes/memory-bundle.js +1 -1
- package/dist/http/routes/memory-compliance.js +1 -1
- package/dist/http/routes/memory-policy.js +2 -2
- package/dist/http/routes/run-permission-mode.d.ts +75 -0
- package/dist/http/routes/run-permission-mode.js +170 -0
- package/dist/http/routes/session-sync.d.ts +3 -6
- package/dist/http/routes/session-sync.js +7 -2
- package/dist/http/routes/sessions.js +5 -4
- package/dist/http/routes/tasks.js +63 -66
- package/dist/http/run-read-face.js +3 -3
- package/dist/http/send.d.ts +1 -1
- package/dist/http/send.js +2 -2
- package/dist/http/server.d.ts +9 -2
- package/dist/http/sse-log.d.ts +3 -8
- package/dist/http/stored-body-replay.d.ts +3 -3
- package/dist/http/stored-body-replay.js +3 -1
- package/dist/instruction-source-notice.d.ts +7 -0
- package/dist/instruction-source-notice.js +6 -6
- package/dist/mcp-assertion-header.d.ts +4 -0
- package/dist/mcp-assertion-header.js +5 -0
- package/dist/memory-posture.d.ts +1 -1
- package/dist/memory-scope.d.ts +4 -4
- package/dist/memory-scope.js +1 -1
- package/dist/memory-sync-client.d.ts +14 -2
- package/dist/memory-sync-client.js +80 -18
- package/dist/observability/fail-open.d.ts +30 -2
- package/dist/observability/fail-open.js +30 -2
- package/dist/observability/metrics.js +0 -1
- package/dist/observability/run-terminal.d.ts +6 -2
- package/dist/observability/run-terminal.js +25 -26
- package/dist/observability/secret-env-scrub.d.ts +20 -1
- package/dist/observability/secret-env-scrub.js +14 -0
- package/dist/orchestration/workflow-completion-inbox.d.ts +5 -2
- package/dist/orchestration/workflow-completion-inbox.js +13 -5
- package/dist/orchestration/workflow-notify-journal.d.ts +5 -3
- package/dist/orchestration/workflow-notify-journal.js +2 -1
- package/dist/permission-mode-admission.d.ts +68 -0
- package/dist/permission-mode-admission.js +23 -0
- package/dist/permission-rule-list.d.ts +55 -6
- package/dist/permission-rule-list.js +52 -5
- package/dist/plugins/caching-session-store.d.ts +6 -0
- package/dist/plugins/caching-session-store.js +3 -0
- package/dist/plugins/checkpoint-store-sql.d.ts +65 -17
- package/dist/plugins/checkpoint-store-sql.js +74 -3
- package/dist/plugins/device-store-sql.d.ts +5 -2
- package/dist/plugins/device-store-sql.js +4 -1
- package/dist/plugins/execution-placement-sql.d.ts +11 -1
- package/dist/plugins/execution-placement-sql.js +12 -1
- package/dist/plugins/file-run-store.d.ts +2 -2
- package/dist/plugins/file-run-store.js +1 -1
- package/dist/plugins/fork-routing-session-store.d.ts +10 -32
- package/dist/plugins/fork-routing-session-store.js +2 -61
- package/dist/plugins/k8s-exec-protocol.d.ts +4 -1
- package/dist/plugins/k8s-exec-protocol.js +6 -1
- package/dist/plugins/local-checkpoint-store.d.ts +10 -1
- package/dist/plugins/local-checkpoint-store.js +22 -0
- package/dist/plugins/local-session-store.d.ts +227 -35
- package/dist/plugins/local-session-store.js +518 -37
- package/dist/plugins/local-session-window.d.ts +17 -3
- package/dist/plugins/local-session-window.js +7 -13
- package/dist/plugins/memory-run-store.d.ts +2 -2
- package/dist/plugins/memory-run-store.js +1 -1
- package/dist/plugins/one-time-migrations.d.ts +42 -2
- package/dist/plugins/one-time-migrations.js +65 -3
- package/dist/plugins/pg-pool.js +3 -2
- package/dist/plugins/pg-session-storage.d.ts +2 -0
- package/dist/plugins/pg-session-storage.js +3 -2
- package/dist/plugins/remote-env-e2b.d.ts +24 -3
- package/dist/plugins/remote-env-e2b.js +131 -18
- package/dist/plugins/remote-env-k8s.d.ts +20 -1
- package/dist/plugins/remote-env-k8s.js +148 -25
- package/dist/plugins/remote-env-local-docker.d.ts +25 -11
- package/dist/plugins/remote-env-local-docker.js +87 -21
- package/dist/plugins/run-store-sql.d.ts +3 -3
- package/dist/plugins/run-store-sql.js +4 -3
- package/dist/plugins/sandbox-read-floor.d.ts +273 -0
- package/dist/plugins/sandbox-read-floor.js +608 -0
- package/dist/plugins/session-ownership.d.ts +2 -1
- package/dist/plugins/session-placement.d.ts +17 -4
- package/dist/plugins/session-placement.js +2 -1
- package/dist/plugins/session-store.d.ts +18 -20
- package/dist/plugins/session-store.js +4 -12
- package/dist/plugins/store-backend.d.ts +31 -0
- package/dist/plugins/store-backend.js +21 -4
- package/dist/plugins/store-contracts.d.ts +9 -2
- package/dist/plugins/tidb-pool.js +2 -1
- package/dist/plugins/tidb-session-store.d.ts +4 -1
- package/dist/plugins/tidb-session-store.js +3 -2
- package/dist/run-cancel-context.d.ts +34 -0
- package/dist/run-cancel-context.js +7 -0
- package/dist/runs.d.ts +39 -12
- package/dist/runs.js +68 -53
- package/dist/security.d.ts +2 -2
- package/dist/server-secret-env.d.ts +2 -1
- package/dist/server-secret-env.js +2 -4
- package/dist/session-error-wire.d.ts +22 -4
- package/dist/session-error-wire.js +16 -0
- package/dist/session-incarnation.d.ts +9 -3
- package/dist/session-sync-kernel.d.ts +42 -1
- package/dist/session-sync-kernel.js +17 -0
- package/dist/session-sync.js +16 -4
- package/dist/sql-engine-coordinates.d.ts +49 -0
- package/dist/sql-engine-coordinates.js +27 -0
- package/dist/standing-answer-grant.d.ts +31 -2
- package/dist/standing-answer-grant.js +14 -2
- package/dist/store-posture.d.ts +1 -1
- package/dist/task-a2a.d.ts +1 -1
- package/dist/task-mcp.d.ts +1 -1
- package/dist/task-settings.d.ts +52 -25
- package/dist/task-settings.js +91 -28
- package/dist/terminal.d.ts +77 -20
- package/dist/terminal.js +66 -20
- package/dist/trace/core-keyset-guard.d.ts +79 -2
- package/dist/trace/exit-seat.d.ts +7 -0
- package/dist/trace/exit-seat.js +1 -0
- package/dist/trace/model-segment.d.ts +2 -2
- package/dist/trace/redact.js +323 -90
- package/dist/trace/self-minted-wire.d.ts +168 -2
- package/dist/trace/self-minted-wire.js +63 -0
- package/dist/verifier-pause-card.d.ts +8 -0
- package/dist/verifier-pause-card.js +19 -0
- package/dist/web-admin-client/apply-effective.d.ts +5 -7
- package/dist/web-admin-client/apply-effective.js +2 -2
- package/dist/web-admin-client/candidate-gate.d.ts +1 -1
- package/dist/web-admin-client/read-warnings.d.ts +2 -2
- package/docs/DEVICE-EXECUTOR-PROTOCOL.md +3 -2
- package/package.json +2 -2
- package/wire-changes/server@7.141.0.json +367 -0
package/dist/trace/redact.js
CHANGED
|
@@ -888,6 +888,28 @@ const CREDENTIAL_NAME_ALT = (() => {
|
|
|
888
888
|
return [...CREDENTIAL_PARAM_NAMES].sort((a, b) => b.length - a.length || (a < b ? -1 : 1)).join("|");
|
|
889
889
|
})();
|
|
890
890
|
const CREDENTIAL_NAME_MAX = Math.max(...CREDENTIAL_PARAM_NAMES.map((w) => w.length));
|
|
891
|
+
const JSON_FIELD_QUOTE_FORMS = ['"', '\\"', '\\\\\\"'];
|
|
892
|
+
for (let d = 0; d < JSON_FIELD_QUOTE_FORMS.length; d++) {
|
|
893
|
+
if (JSON_FIELD_QUOTE_FORMS[d] !== "\\".repeat(2 ** d - 1) + '"')
|
|
894
|
+
throw new Error(`redact: JSON field quote form ${d} must be ${2 ** d - 1} backslashes + a quote`);
|
|
895
|
+
}
|
|
896
|
+
function buildJsonSecretFieldSource() {
|
|
897
|
+
const lt = String.raw `\n\r\u2028\u2029`;
|
|
898
|
+
const l = `[${lt}]`;
|
|
899
|
+
let q = '"';
|
|
900
|
+
let b = String.raw `\\`;
|
|
901
|
+
let o = `[^"\\\\${lt}]`;
|
|
902
|
+
const alts = [];
|
|
903
|
+
for (let d = 0; d < JSON_FIELD_QUOTE_FORMS.length; d++) {
|
|
904
|
+
if (d > 0) {
|
|
905
|
+
o = `(?:${o}|${b}${o})`;
|
|
906
|
+
q = b + q;
|
|
907
|
+
b = b + b;
|
|
908
|
+
}
|
|
909
|
+
alts.push(String.raw `(${q}(?:${CREDENTIAL_NAME_ALT})${q}\s*:\s*)${q}(?:${o}|${l}|${b}(?:${o}|${b}|${q}))*${q}`);
|
|
910
|
+
}
|
|
911
|
+
return alts.join("|");
|
|
912
|
+
}
|
|
891
913
|
const asciiLower = (s) => s.replace(/[A-Z]/g, (c) => c.toLowerCase());
|
|
892
914
|
function credentialNameAnchor(name) {
|
|
893
915
|
return CREDENTIAL_ANCHOR_OF.get(asciiLower(name));
|
|
@@ -897,7 +919,7 @@ const STRUCTURAL_SECRET_PATTERNS = [
|
|
|
897
919
|
[/-----BEGIN(?:[^-](?:[^-](?:[^-]|-(?!-))*)?)?PRIVATE KEY(?: BLOCK)?-----[\s\S]{0,8192}?-----END(?:[^-](?:[^-](?:[^-]|-(?!-))*)?)?PRIVATE KEY(?: BLOCK)?-----/g, "«redacted:private-key»"],
|
|
898
920
|
[/---- BEGIN SSH2 ENCRYPTED PRIVATE KEY ----[\s\S]{0,8192}?---- END SSH2 ENCRYPTED PRIVATE KEY ----/g, "«redacted:private-key»"],
|
|
899
921
|
[/:(?<=[A-Za-z][A-Za-z0-9+.-]*:)\/\//g, "$1«redacted»"],
|
|
900
|
-
[new RegExp(
|
|
922
|
+
[new RegExp(buildJsonSecretFieldSource(), "gi"), '$1"«redacted»"'],
|
|
901
923
|
];
|
|
902
924
|
const PPK_CONTAINER_TOKEN = "«redacted:private-key»";
|
|
903
925
|
const PEM_FRAGMENT_TOKEN = "«redacted:private-key:partial»";
|
|
@@ -1094,6 +1116,7 @@ function parsePemBody(scan, s, i, limit) {
|
|
|
1094
1116
|
function rightmostPemRunEnd(scan, s, from, to) {
|
|
1095
1117
|
let best = -1;
|
|
1096
1118
|
let runStart = -1;
|
|
1119
|
+
let plainBackslashTill = -1;
|
|
1097
1120
|
for (let p = from; p <= to; p++) {
|
|
1098
1121
|
if (p < to) {
|
|
1099
1122
|
const unit = opaqueUnitEnd(scan, p);
|
|
@@ -1106,6 +1129,20 @@ function rightmostPemRunEnd(scan, s, from, to) {
|
|
|
1106
1129
|
p = unit - 1;
|
|
1107
1130
|
continue;
|
|
1108
1131
|
}
|
|
1132
|
+
if (p >= plainBackslashTill && s.charCodeAt(p) === CH_BACKSLASH) {
|
|
1133
|
+
const j = pemEscapeLetterAt(s, p, to);
|
|
1134
|
+
if (j >= 0) {
|
|
1135
|
+
if (runStart >= 0 && p - runStart >= PEM_BODY_RUN_MIN)
|
|
1136
|
+
best = p;
|
|
1137
|
+
runStart = -1;
|
|
1138
|
+
p = j;
|
|
1139
|
+
continue;
|
|
1140
|
+
}
|
|
1141
|
+
let k = p + 1;
|
|
1142
|
+
while (k < to && s.charCodeAt(k) === CH_BACKSLASH)
|
|
1143
|
+
k += 1;
|
|
1144
|
+
plainBackslashTill = k;
|
|
1145
|
+
}
|
|
1109
1146
|
}
|
|
1110
1147
|
if (p < to && isBase64Code(s.charCodeAt(p))) {
|
|
1111
1148
|
if (runStart < 0)
|
|
@@ -1153,22 +1190,22 @@ function findPemFence(scan, text, from) {
|
|
|
1153
1190
|
if (close < 0)
|
|
1154
1191
|
return undefined;
|
|
1155
1192
|
from = labelFrom;
|
|
1156
|
-
if (inOpaqueUnit(scan, fence))
|
|
1157
|
-
continue;
|
|
1158
1193
|
if (!isPemFenceLabel(text, labelFrom, close))
|
|
1159
1194
|
continue;
|
|
1160
|
-
|
|
1195
|
+
const after = close + PEM_FENCE_CLOSE.length;
|
|
1196
|
+
if (anchorHeld(scan, fence, after))
|
|
1197
|
+
continue;
|
|
1198
|
+
return { fence, after };
|
|
1161
1199
|
}
|
|
1162
1200
|
}
|
|
1163
1201
|
const SSH2_FENCE_OPEN = "---- BEGIN SSH2 ENCRYPTED PRIVATE KEY ----";
|
|
1164
1202
|
const SSH2_FENCE_CLOSE = "---- END SSH2 ENCRYPTED PRIVATE KEY ----";
|
|
1165
1203
|
const SSH2_BODY_MAX = 8192;
|
|
1166
|
-
function unitCountWithin(scan, from, to, max) {
|
|
1204
|
+
function unitCountWithin(scan, from, to, max, t = scan.table) {
|
|
1167
1205
|
if (to - from <= max)
|
|
1168
1206
|
return true;
|
|
1169
1207
|
noteEvidenceRead(scan, from);
|
|
1170
1208
|
noteEvidenceRead(scan, to);
|
|
1171
|
-
const t = scan.table;
|
|
1172
1209
|
const lo = firstCredentialSpanFrom(t, from);
|
|
1173
1210
|
let a = lo;
|
|
1174
1211
|
let b = t.ends.length;
|
|
@@ -1200,7 +1237,7 @@ export function unitCountWithinForTest(table, from, to, max) {
|
|
|
1200
1237
|
const t = { starts: Int32Array.from(table, (x) => x[0]), ends: Int32Array.from(table, (x) => x[1]) };
|
|
1201
1238
|
return unitCountWithin(buildForensicScan(t, 1), from, to, max);
|
|
1202
1239
|
}
|
|
1203
|
-
function collectSsh2FenceSpans(scan, text, span, isHead) {
|
|
1240
|
+
function collectSsh2FenceSpans(scan, text, json, span, isHead) {
|
|
1204
1241
|
let cachedFrom = 0;
|
|
1205
1242
|
let cachedAt;
|
|
1206
1243
|
const nextClose = (from) => {
|
|
@@ -1217,29 +1254,78 @@ function collectSsh2FenceSpans(scan, text, span, isHead) {
|
|
|
1217
1254
|
cachedAt = p;
|
|
1218
1255
|
return p;
|
|
1219
1256
|
};
|
|
1257
|
+
let freeFrom = 0;
|
|
1258
|
+
let freeAt;
|
|
1259
|
+
const freeClose = (from) => {
|
|
1260
|
+
if (freeAt !== undefined && freeFrom <= from && (freeAt < 0 || freeAt >= from))
|
|
1261
|
+
return freeAt;
|
|
1262
|
+
let p = text.indexOf(SSH2_FENCE_CLOSE, from);
|
|
1263
|
+
ssh2ScanBytes += (p < 0 ? text.length : p) - from;
|
|
1264
|
+
while (p >= 0 && anchorHeld(scan, p, p + 1)) {
|
|
1265
|
+
const q = text.indexOf(SSH2_FENCE_CLOSE, p + 1);
|
|
1266
|
+
ssh2ScanBytes += (q < 0 ? text.length : q) - (p + 1);
|
|
1267
|
+
p = q;
|
|
1268
|
+
}
|
|
1269
|
+
freeFrom = from;
|
|
1270
|
+
freeAt = p;
|
|
1271
|
+
return p;
|
|
1272
|
+
};
|
|
1273
|
+
const truncatedEnd = createTruncatedBlockEnd(scan, text, json, freeClose);
|
|
1274
|
+
let openFrom = 0;
|
|
1275
|
+
let openAt;
|
|
1276
|
+
const nextFreeOpen = (from) => {
|
|
1277
|
+
if (openAt !== undefined && openFrom <= from && (openAt < 0 || openAt >= from))
|
|
1278
|
+
return openAt;
|
|
1279
|
+
let p = text.indexOf(SSH2_FENCE_OPEN, from);
|
|
1280
|
+
ssh2ScanBytes += (p < 0 ? text.length : p) - from;
|
|
1281
|
+
while (p >= 0 && anchorHeld(scan, p, p + SSH2_FENCE_OPEN.length)) {
|
|
1282
|
+
const q = text.indexOf(SSH2_FENCE_OPEN, p + 1);
|
|
1283
|
+
ssh2ScanBytes += (q < 0 ? text.length : q) - (p + 1);
|
|
1284
|
+
p = q;
|
|
1285
|
+
}
|
|
1286
|
+
openFrom = from;
|
|
1287
|
+
openAt = p;
|
|
1288
|
+
return p;
|
|
1289
|
+
};
|
|
1220
1290
|
for (let from = 0;;) {
|
|
1221
1291
|
const fence = text.indexOf(SSH2_FENCE_OPEN, from);
|
|
1222
1292
|
if (fence < 0)
|
|
1223
1293
|
return;
|
|
1224
|
-
|
|
1294
|
+
const bodyFrom = fence + SSH2_FENCE_OPEN.length;
|
|
1295
|
+
if (anchorHeld(scan, fence, bodyFrom)) {
|
|
1225
1296
|
from = fence + 1;
|
|
1226
1297
|
continue;
|
|
1227
1298
|
}
|
|
1228
|
-
const bodyFrom = fence + SSH2_FENCE_OPEN.length;
|
|
1229
1299
|
if (isHead && unitCountWithin(scan, bodyFrom, text.length, SSH2_BODY_MAX)) {
|
|
1230
1300
|
span(fence, text.length, true);
|
|
1231
1301
|
return;
|
|
1232
1302
|
}
|
|
1303
|
+
let resume = fence + 1;
|
|
1304
|
+
if (!isHead) {
|
|
1305
|
+
const open = nextFreeOpen(bodyFrom);
|
|
1306
|
+
const limit = open < 0 ? text.length : open;
|
|
1307
|
+
const t = truncatedEnd(bodyFrom, limit);
|
|
1308
|
+
const bodyEnd = t < 0 ? -1 : armorBodyEnd(scan, text, json, bodyFrom, limit, false);
|
|
1309
|
+
const end = bodyEnd < 0 ? -1 : Math.max(bodyEnd, t);
|
|
1310
|
+
if (end >= 0 && unitCountWithin(scan, bodyFrom, end, SSH2_BODY_MAX, othersOf(scan))) {
|
|
1311
|
+
span(fence, end);
|
|
1312
|
+
resume = end;
|
|
1313
|
+
}
|
|
1314
|
+
}
|
|
1233
1315
|
const close = nextClose(bodyFrom);
|
|
1234
|
-
if (close < 0)
|
|
1235
|
-
|
|
1316
|
+
if (close < 0) {
|
|
1317
|
+
if (isHead)
|
|
1318
|
+
return;
|
|
1319
|
+
from = resume;
|
|
1320
|
+
continue;
|
|
1321
|
+
}
|
|
1236
1322
|
if (!unitCountWithin(scan, bodyFrom, close, SSH2_BODY_MAX)) {
|
|
1237
|
-
from =
|
|
1323
|
+
from = resume;
|
|
1238
1324
|
continue;
|
|
1239
1325
|
}
|
|
1240
1326
|
const end = close + SSH2_FENCE_CLOSE.length;
|
|
1241
1327
|
span(fence, end);
|
|
1242
|
-
from = scanResumeAfterHit(fence, end, SSH2_FENCE_OPEN.length);
|
|
1328
|
+
from = Math.max(resume, scanResumeAfterHit(fence, end, SSH2_FENCE_OPEN.length));
|
|
1243
1329
|
}
|
|
1244
1330
|
}
|
|
1245
1331
|
const PEM_END_OPEN = "-----END";
|
|
@@ -1280,12 +1366,12 @@ function collectPemFenceSpans(scan, text, span, isHead) {
|
|
|
1280
1366
|
const fence = text.indexOf(PEM_FENCE_OPEN, from);
|
|
1281
1367
|
if (fence < 0)
|
|
1282
1368
|
return;
|
|
1283
|
-
|
|
1369
|
+
const bodyFrom = pemFenceLineEnd(text, fence + PEM_FENCE_OPEN.length);
|
|
1370
|
+
if (bodyFrom === PEM_LINE_INVALID) {
|
|
1284
1371
|
from = fence + 1;
|
|
1285
1372
|
continue;
|
|
1286
1373
|
}
|
|
1287
|
-
|
|
1288
|
-
if (bodyFrom === PEM_LINE_INVALID) {
|
|
1374
|
+
if (anchorHeld(scan, fence, bodyFrom === PEM_LINE_OPEN ? text.length : bodyFrom)) {
|
|
1289
1375
|
from = fence + 1;
|
|
1290
1376
|
continue;
|
|
1291
1377
|
}
|
|
@@ -1304,35 +1390,80 @@ function collectPemFenceSpans(scan, text, span, isHead) {
|
|
|
1304
1390
|
from = scanResumeAfterHit(fence, endAfter, PEM_FENCE_OPEN.length);
|
|
1305
1391
|
}
|
|
1306
1392
|
}
|
|
1307
|
-
function
|
|
1393
|
+
function truncationWindowEnd(text, from, json) {
|
|
1394
|
+
if (!json)
|
|
1395
|
+
return text.length;
|
|
1396
|
+
for (let i = from; i < text.length; i++) {
|
|
1397
|
+
const c = text.charCodeAt(i);
|
|
1398
|
+
if (c === CH_BACKSLASH) {
|
|
1399
|
+
i += 1;
|
|
1400
|
+
continue;
|
|
1401
|
+
}
|
|
1402
|
+
if (c === CH_QUOTE)
|
|
1403
|
+
return i;
|
|
1404
|
+
}
|
|
1405
|
+
return text.length;
|
|
1406
|
+
}
|
|
1407
|
+
function armorBodyEnd(scan, text, json, from, limit, allowBare) {
|
|
1408
|
+
let pos = from;
|
|
1409
|
+
let bare = allowBare;
|
|
1410
|
+
let hitLimit = false;
|
|
1411
|
+
let rightmost = -1;
|
|
1412
|
+
while (parsePemHeaderLine(scan, text, pos, limit, bare, json)) {
|
|
1413
|
+
const e = rightmostPemRunEnd(scan, text, scan.hdr.search, scan.hdr.contentEnd);
|
|
1414
|
+
if (e > rightmost)
|
|
1415
|
+
rightmost = e;
|
|
1416
|
+
pos = scan.hdr.next;
|
|
1417
|
+
bare = false;
|
|
1418
|
+
if (scan.hdr.hitLimit) {
|
|
1419
|
+
hitLimit = true;
|
|
1420
|
+
break;
|
|
1421
|
+
}
|
|
1422
|
+
if (scan.hdr.jsonEnd)
|
|
1423
|
+
break;
|
|
1424
|
+
}
|
|
1425
|
+
const bodyEnd = parsePemBody(scan, text, pos, limit);
|
|
1426
|
+
scan.hdr.hitLimit = hitLimit;
|
|
1427
|
+
return bodyEnd >= 0 ? bodyEnd : rightmost;
|
|
1428
|
+
}
|
|
1429
|
+
function createTruncatedBlockEnd(scan, text, json, closeAt) {
|
|
1430
|
+
let winFrom = -1;
|
|
1431
|
+
let winAt = -1;
|
|
1432
|
+
return (from, limit) => {
|
|
1433
|
+
if (!(winFrom >= 0 && from >= winFrom && from <= winAt)) {
|
|
1434
|
+
winFrom = from;
|
|
1435
|
+
winAt = truncationWindowEnd(text, from, json);
|
|
1436
|
+
}
|
|
1437
|
+
const close = closeAt(from);
|
|
1438
|
+
if (close >= 0 && close < winAt)
|
|
1439
|
+
return -1;
|
|
1440
|
+
return rightmostPemRunEnd(scan, text, from, Math.min(limit, winAt));
|
|
1441
|
+
};
|
|
1442
|
+
}
|
|
1443
|
+
function collectPemFragmentSpans(scan, text, json, span, isHead) {
|
|
1308
1444
|
let cur = findPemFence(scan, text, 0);
|
|
1309
1445
|
let inherited = false;
|
|
1446
|
+
let closeFrom = -1;
|
|
1447
|
+
let closeAt = -1;
|
|
1448
|
+
const freeClose = (from) => {
|
|
1449
|
+
if (closeFrom >= 0 && from >= closeFrom && (closeAt < 0 || closeAt >= from))
|
|
1450
|
+
return closeAt;
|
|
1451
|
+
let b = text.indexOf(PEM_END_OPEN, from);
|
|
1452
|
+
while (b >= 0 && (pemFenceLineEnd(text, b + PEM_END_OPEN.length) < 0 || anchorHeld(scan, b, b + 1)))
|
|
1453
|
+
b = text.indexOf(PEM_END_OPEN, b + 1);
|
|
1454
|
+
closeFrom = from;
|
|
1455
|
+
closeAt = b;
|
|
1456
|
+
return b;
|
|
1457
|
+
};
|
|
1458
|
+
const truncatedEnd = createTruncatedBlockEnd(scan, text, json, freeClose);
|
|
1310
1459
|
while (cur !== undefined) {
|
|
1311
1460
|
const next = findPemFence(scan, text, cur.after);
|
|
1312
1461
|
const limit = next === undefined ? text.length : next.fence;
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
const e = rightmostPemRunEnd(scan, text, scan.hdr.search, scan.hdr.contentEnd);
|
|
1319
|
-
if (e > rightmost)
|
|
1320
|
-
rightmost = e;
|
|
1321
|
-
pos = scan.hdr.next;
|
|
1322
|
-
allowBare = false;
|
|
1323
|
-
if (scan.hdr.hitLimit) {
|
|
1324
|
-
hitLimit = true;
|
|
1325
|
-
break;
|
|
1326
|
-
}
|
|
1327
|
-
if (scan.hdr.jsonEnd)
|
|
1328
|
-
break;
|
|
1329
|
-
}
|
|
1330
|
-
inherited = hitLimit;
|
|
1331
|
-
let bodyEnd = parsePemBody(scan, text, pos, limit);
|
|
1332
|
-
if (bodyEnd < 0)
|
|
1333
|
-
bodyEnd = rightmost;
|
|
1334
|
-
if (bodyEnd >= 0)
|
|
1335
|
-
span(cur.fence, bodyEnd);
|
|
1462
|
+
const bodyEnd = armorBodyEnd(scan, text, json, cur.after, limit, inherited);
|
|
1463
|
+
inherited = scan.hdr.hitLimit;
|
|
1464
|
+
const end = isHead || bodyEnd < 0 ? bodyEnd : Math.max(bodyEnd, truncatedEnd(cur.after, limit));
|
|
1465
|
+
if (end >= 0)
|
|
1466
|
+
span(cur.fence, end);
|
|
1336
1467
|
cur = next;
|
|
1337
1468
|
}
|
|
1338
1469
|
}
|
|
@@ -1438,10 +1569,6 @@ function collectHeadlessPpkSpans(scan, text, span, isHead) {
|
|
|
1438
1569
|
const at = text.indexOf(KEY, search);
|
|
1439
1570
|
if (at === -1)
|
|
1440
1571
|
break;
|
|
1441
|
-
if (inOpaqueUnit(scan, at)) {
|
|
1442
|
-
search = at + KEY.length;
|
|
1443
|
-
continue;
|
|
1444
|
-
}
|
|
1445
1572
|
let p = at + KEY.length;
|
|
1446
1573
|
const d0 = p;
|
|
1447
1574
|
while (p < n && opaqueUnitEnd(scan, p) < 0 && text.charCodeAt(p) >= 48 && text.charCodeAt(p) <= 57)
|
|
@@ -1450,6 +1577,10 @@ function collectHeadlessPpkSpans(scan, text, span, isHead) {
|
|
|
1450
1577
|
search = at + KEY.length;
|
|
1451
1578
|
continue;
|
|
1452
1579
|
}
|
|
1580
|
+
if (anchorHeld(scan, at, p)) {
|
|
1581
|
+
search = at + KEY.length;
|
|
1582
|
+
continue;
|
|
1583
|
+
}
|
|
1453
1584
|
let end = -1;
|
|
1454
1585
|
let open = false;
|
|
1455
1586
|
for (let from = p;;) {
|
|
@@ -1548,6 +1679,14 @@ const JSON_SECRET_FIELD_PATTERN = (() => {
|
|
|
1548
1679
|
throw new Error(`redact: expected exactly one JSON secret-field rule (replacement '$1"«redacted»"') in FULL_ARM_PATTERNS, found ${rows.length}`);
|
|
1549
1680
|
return rows[0];
|
|
1550
1681
|
})();
|
|
1682
|
+
const JSON_FIELD_VALUE_REPLS = (() => {
|
|
1683
|
+
const [plain] = JSON_FIELD_QUOTE_FORMS;
|
|
1684
|
+
const repl = JSON_SECRET_FIELD_PATTERN[1];
|
|
1685
|
+
if (!repl.startsWith("$1" + plain) || !repl.endsWith(plain) || repl.length < 2 + 2 * plain.length)
|
|
1686
|
+
throw new Error(`redact: the JSON secret-field replacement must be $1 + a quoted mark, got ${JSON.stringify(repl)}`);
|
|
1687
|
+
const mark = repl.slice(2 + plain.length, repl.length - plain.length);
|
|
1688
|
+
return JSON_FIELD_QUOTE_FORMS.map((f) => f + mark + f);
|
|
1689
|
+
})();
|
|
1551
1690
|
const isRegexSpace = (c) => c === 32 || (c >= 9 && c <= 13) || c === 0xa0 || c === 0x1680 || (c >= 0x2000 && c <= 0x200a) || c === 0x2028 || c === 0x2029 || c === 0x202f || c === 0x205f || c === 0x3000 || c === 0xfeff;
|
|
1552
1691
|
const isLineTerminator = (c) => c === 10 || c === 13 || c === 0x2028 || c === 0x2029;
|
|
1553
1692
|
const isControlCode = (c) => c < 0x20 || c === 0x7f;
|
|
@@ -1755,6 +1894,7 @@ const FOOT_NONE = 0x7fffffff;
|
|
|
1755
1894
|
function buildForensicScan(table, armCount) {
|
|
1756
1895
|
return {
|
|
1757
1896
|
table,
|
|
1897
|
+
others: undefined,
|
|
1758
1898
|
cursor: 0,
|
|
1759
1899
|
dirty: undefined,
|
|
1760
1900
|
footLo: FOOT_NONE,
|
|
@@ -1768,8 +1908,9 @@ function buildForensicScan(table, armCount) {
|
|
|
1768
1908
|
hdr: { search: 0, contentEnd: 0, next: 0, hitLimit: false, jsonEnd: false },
|
|
1769
1909
|
};
|
|
1770
1910
|
}
|
|
1771
|
-
function useEvidenceView(scan, table, dirty) {
|
|
1911
|
+
function useEvidenceView(scan, table, dirty, others) {
|
|
1772
1912
|
scan.table = table;
|
|
1913
|
+
scan.others = others;
|
|
1773
1914
|
scan.cursor = 0;
|
|
1774
1915
|
scan.dirty = dirty;
|
|
1775
1916
|
}
|
|
@@ -1803,6 +1944,22 @@ function inOpaqueUnit(scan, p) {
|
|
|
1803
1944
|
const k = firstCredentialSpanFrom(t, p) - 1;
|
|
1804
1945
|
return k >= 0 && st[k] < p && t.ends[k] > p;
|
|
1805
1946
|
}
|
|
1947
|
+
function othersOf(scan) {
|
|
1948
|
+
const t = scan.others;
|
|
1949
|
+
if (t === undefined)
|
|
1950
|
+
throw new Error("redact: a structural-anchor question was asked by a forensic arm that declares no credential family — it has no other-families evidence to read");
|
|
1951
|
+
return t;
|
|
1952
|
+
}
|
|
1953
|
+
function anchorHeld(scan, p, lineEnd) {
|
|
1954
|
+
const t = othersOf(scan);
|
|
1955
|
+
noteEvidenceRead(scan, p);
|
|
1956
|
+
noteEvidenceRead(scan, lineEnd - 1);
|
|
1957
|
+
const st = t.starts;
|
|
1958
|
+
if (st.length === 0)
|
|
1959
|
+
return false;
|
|
1960
|
+
const k = firstCredentialSpanFrom(t, p) - 1;
|
|
1961
|
+
return k >= 0 && st[k] < p && t.ends[k] >= lineEnd;
|
|
1962
|
+
}
|
|
1806
1963
|
function opaqueUnitEnd(scan, p) {
|
|
1807
1964
|
noteEvidenceRead(scan, p);
|
|
1808
1965
|
return opaqueUnitEndAt(scan, p);
|
|
@@ -2001,21 +2158,22 @@ function collectUrlCredentialSpans(scan, text, json, span, isHead) {
|
|
|
2001
2158
|
scan.footHi = armHi;
|
|
2002
2159
|
}
|
|
2003
2160
|
const JSON_FIELD_ANCHOR = '"';
|
|
2004
|
-
|
|
2161
|
+
const JSON_FIELD_BACK = Math.max(...JSON_FIELD_QUOTE_FORMS.map((f) => f.length));
|
|
2162
|
+
function jsonSecretFieldValueQuoteAt(text, p, depth) {
|
|
2163
|
+
const form = JSON_FIELD_QUOTE_FORMS[depth];
|
|
2005
2164
|
const n = text.length;
|
|
2006
|
-
|
|
2165
|
+
const nameFrom = p + form.length;
|
|
2166
|
+
let i = nameFrom;
|
|
2007
2167
|
for (; i < n; i++) {
|
|
2008
2168
|
const c = text.charCodeAt(i);
|
|
2009
|
-
if (c === CH_QUOTE)
|
|
2010
|
-
break;
|
|
2011
|
-
if (i - p > CREDENTIAL_NAME_MAX)
|
|
2012
|
-
return -1;
|
|
2013
2169
|
if (!(isAsciiAlnumCode(c) || c === CH_UNDERSCORE || c === CH_MINUS))
|
|
2170
|
+
break;
|
|
2171
|
+
if (i - nameFrom >= CREDENTIAL_NAME_MAX)
|
|
2014
2172
|
return -1;
|
|
2015
2173
|
}
|
|
2016
|
-
if (i
|
|
2174
|
+
if (i === nameFrom || !text.startsWith(form, i) || credentialNameAnchor(text.slice(nameFrom, i)) === undefined)
|
|
2017
2175
|
return -1;
|
|
2018
|
-
i +=
|
|
2176
|
+
i += form.length;
|
|
2019
2177
|
while (i < n && isRegexSpace(text.charCodeAt(i)))
|
|
2020
2178
|
i += 1;
|
|
2021
2179
|
if (text.charCodeAt(i) !== CH_COLON)
|
|
@@ -2023,38 +2181,99 @@ function jsonSecretFieldValueQuoteAt(text, p) {
|
|
|
2023
2181
|
i += 1;
|
|
2024
2182
|
while (i < n && isRegexSpace(text.charCodeAt(i)))
|
|
2025
2183
|
i += 1;
|
|
2026
|
-
return text.
|
|
2184
|
+
return text.startsWith(form, i) ? i : -1;
|
|
2027
2185
|
}
|
|
2028
2186
|
const STRING_OPEN = -2;
|
|
2029
|
-
|
|
2030
|
-
|
|
2031
|
-
|
|
2187
|
+
const U_END = 0;
|
|
2188
|
+
const U_QUOTE = 1;
|
|
2189
|
+
const U_BSL = 2;
|
|
2190
|
+
const U_ORD = 3;
|
|
2191
|
+
const U_LT = 4;
|
|
2192
|
+
const U_BREAK = 5;
|
|
2193
|
+
const packUnit8 = (kind, next) => next * 8 + kind;
|
|
2194
|
+
const unit8Kind = (u) => (u % 8);
|
|
2195
|
+
const unit8Next = (u) => (u - (u % 8)) / 8;
|
|
2196
|
+
function escapedUnitAt(text, i, depth) {
|
|
2197
|
+
if (i >= text.length)
|
|
2198
|
+
return packUnit8(U_END, i);
|
|
2199
|
+
if (depth === 0) {
|
|
2032
2200
|
const c = text.charCodeAt(i);
|
|
2033
|
-
|
|
2034
|
-
|
|
2035
|
-
|
|
2036
|
-
|
|
2037
|
-
|
|
2201
|
+
return packUnit8(c === CH_QUOTE ? U_QUOTE : c === CH_BACKSLASH ? U_BSL : isLineTerminator(c) ? U_LT : U_ORD, i + 1);
|
|
2202
|
+
}
|
|
2203
|
+
const u = escapedUnitAt(text, i, depth - 1);
|
|
2204
|
+
switch (unit8Kind(u)) {
|
|
2205
|
+
case U_QUOTE: return packUnit8(U_BREAK, unit8Next(u));
|
|
2206
|
+
case U_BSL: return escapePairUnit(text, unit8Next(u), depth - 1);
|
|
2207
|
+
case U_ORD:
|
|
2208
|
+
case U_LT:
|
|
2209
|
+
case U_BREAK:
|
|
2210
|
+
case U_END: return u;
|
|
2211
|
+
}
|
|
2212
|
+
}
|
|
2213
|
+
function escapePairUnit(text, i, depth) {
|
|
2214
|
+
const v = escapedUnitAt(text, i, depth);
|
|
2215
|
+
const next = unit8Next(v);
|
|
2216
|
+
switch (unit8Kind(v)) {
|
|
2217
|
+
case U_BSL: return packUnit8(U_BSL, next);
|
|
2218
|
+
case U_QUOTE: return packUnit8(U_QUOTE, next);
|
|
2219
|
+
case U_ORD: return packUnit8(U_ORD, next);
|
|
2220
|
+
case U_LT:
|
|
2221
|
+
case U_BREAK: return packUnit8(U_BREAK, next);
|
|
2222
|
+
case U_END: return packUnit8(U_END, next);
|
|
2223
|
+
}
|
|
2224
|
+
}
|
|
2225
|
+
function jsonStringEndAt(text, from, depth, isHead) {
|
|
2226
|
+
for (let i = from;;) {
|
|
2227
|
+
const u = escapedUnitAt(text, i, depth);
|
|
2228
|
+
switch (unit8Kind(u)) {
|
|
2229
|
+
case U_QUOTE:
|
|
2230
|
+
return unit8Next(u);
|
|
2231
|
+
case U_ORD:
|
|
2232
|
+
case U_LT:
|
|
2233
|
+
i = unit8Next(u);
|
|
2234
|
+
continue;
|
|
2235
|
+
case U_BSL: {
|
|
2236
|
+
const v = escapedUnitAt(text, unit8Next(u), depth);
|
|
2237
|
+
const vk = unit8Kind(v);
|
|
2238
|
+
if (vk === U_END)
|
|
2239
|
+
return isHead ? STRING_OPEN : -1;
|
|
2240
|
+
if (vk === U_LT || vk === U_BREAK)
|
|
2241
|
+
return -1;
|
|
2242
|
+
i = unit8Next(v);
|
|
2038
2243
|
continue;
|
|
2039
2244
|
}
|
|
2040
|
-
|
|
2245
|
+
case U_BREAK:
|
|
2246
|
+
return -1;
|
|
2247
|
+
case U_END:
|
|
2248
|
+
return isHead ? STRING_OPEN : -1;
|
|
2041
2249
|
}
|
|
2042
|
-
i += 1;
|
|
2043
2250
|
}
|
|
2044
|
-
return isHead ? STRING_OPEN : -1;
|
|
2045
2251
|
}
|
|
2046
2252
|
function collectJsonSecretFieldSpans(text, span, isHead) {
|
|
2253
|
+
let from = 0;
|
|
2047
2254
|
for (let p = text.indexOf(JSON_FIELD_ANCHOR); p >= 0;) {
|
|
2048
|
-
|
|
2049
|
-
|
|
2050
|
-
|
|
2051
|
-
|
|
2052
|
-
|
|
2255
|
+
let run = 0;
|
|
2256
|
+
while (run < JSON_FIELD_BACK - 1 && p - run - 1 >= from && text.charCodeAt(p - run - 1) === CH_BACKSLASH)
|
|
2257
|
+
run += 1;
|
|
2258
|
+
let next = -1;
|
|
2259
|
+
for (let d = JSON_FIELD_QUOTE_FORMS.length - 1; d >= 0 && next < 0; d--) {
|
|
2260
|
+
const form = JSON_FIELD_QUOTE_FORMS[d];
|
|
2261
|
+
const start = p - (form.length - 1);
|
|
2262
|
+
if (start < p - run)
|
|
2263
|
+
continue;
|
|
2264
|
+
const q = jsonSecretFieldValueQuoteAt(text, start, d);
|
|
2265
|
+
const found = q < 0 ? -1 : jsonStringEndAt(text, q + form.length, d, isHead);
|
|
2266
|
+
if (found === -1)
|
|
2267
|
+
continue;
|
|
2268
|
+
const open = found === STRING_OPEN;
|
|
2269
|
+
const end = open ? text.length : found;
|
|
2270
|
+
span(q, end, open, JSON_FIELD_VALUE_REPLS[d]);
|
|
2271
|
+
from = scanResumeAfterHit(start, end, JSON_FIELD_BACK);
|
|
2272
|
+
next = text.indexOf(JSON_FIELD_ANCHOR, from);
|
|
2273
|
+
if (next < 0)
|
|
2274
|
+
return;
|
|
2053
2275
|
}
|
|
2054
|
-
|
|
2055
|
-
const end = open ? text.length : found;
|
|
2056
|
-
span(q, end, open);
|
|
2057
|
-
p = text.indexOf(JSON_FIELD_ANCHOR, scanResumeAfterHit(p, end, JSON_FIELD_ANCHOR.length));
|
|
2276
|
+
p = next >= 0 ? next : text.indexOf(JSON_FIELD_ANCHOR, p + 1);
|
|
2058
2277
|
}
|
|
2059
2278
|
}
|
|
2060
2279
|
const STRONG_AUTH_SCHEMES = new Set(["basic", "bearer"]);
|
|
@@ -2443,7 +2662,7 @@ export function structuralScannerForTest(arm) {
|
|
|
2443
2662
|
case "json-secret-field":
|
|
2444
2663
|
return { declared: new RegExp(JSON_SECRET_FIELD_PATTERN[0].source, JSON_SECRET_FIELD_PATTERN[0].flags), spans: collect(collectJsonSecretFieldSpans) };
|
|
2445
2664
|
case "pem-fence":
|
|
2446
|
-
return { declared: new RegExp(PEM_FENCE_PATTERN[0].source, PEM_FENCE_PATTERN[0].flags), spans: collect((text, span, isHead) => collectPemFenceSpans(buildForensicScan(NO_CREDENTIAL_SPANS, 1), text, span, isHead)) };
|
|
2665
|
+
return { declared: new RegExp(PEM_FENCE_PATTERN[0].source, PEM_FENCE_PATTERN[0].flags), spans: collect((text, span, isHead) => collectPemFenceSpans({ ...buildForensicScan(NO_CREDENTIAL_SPANS, 1), others: NO_CREDENTIAL_SPANS }, text, span, isHead)) };
|
|
2447
2666
|
}
|
|
2448
2667
|
}
|
|
2449
2668
|
let ppkScanBytes = 0;
|
|
@@ -2476,9 +2695,9 @@ function collectPpkContainerSpans(scan, text, eat, span, isHead) {
|
|
|
2476
2695
|
}
|
|
2477
2696
|
return p;
|
|
2478
2697
|
};
|
|
2479
|
-
const
|
|
2698
|
+
const versionedHeadEnd = (at) => {
|
|
2480
2699
|
const p = versionEnd(at);
|
|
2481
|
-
return p > at + HEAD.length && text.startsWith(": ", p);
|
|
2700
|
+
return p > at + HEAD.length && text.startsWith(": ", p) ? p + 2 : -1;
|
|
2482
2701
|
};
|
|
2483
2702
|
const isOpenHead = (at) => {
|
|
2484
2703
|
const p = versionEnd(at);
|
|
@@ -2486,8 +2705,11 @@ function collectPpkContainerSpans(scan, text, eat, span, isHead) {
|
|
|
2486
2705
|
};
|
|
2487
2706
|
const nextVersionedHead = (from) => {
|
|
2488
2707
|
let p = find(HEAD, from);
|
|
2489
|
-
|
|
2490
|
-
|
|
2708
|
+
for (; p !== Number.POSITIVE_INFINITY; p = find(HEAD, p + HEAD.length)) {
|
|
2709
|
+
const lineEnd = versionedHeadEnd(p);
|
|
2710
|
+
if (lineEnd >= 0 && !anchorHeld(scan, p, lineEnd))
|
|
2711
|
+
break;
|
|
2712
|
+
}
|
|
2491
2713
|
return p;
|
|
2492
2714
|
};
|
|
2493
2715
|
const hexRunEnd = (from) => {
|
|
@@ -2555,7 +2777,7 @@ function collectPpkContainerSpans(scan, text, eat, span, isHead) {
|
|
|
2555
2777
|
at = end >= text.length ? Number.POSITIVE_INFINITY : nextVersionedHead(end);
|
|
2556
2778
|
}
|
|
2557
2779
|
const last = isHead ? text.lastIndexOf(HEAD) : -1;
|
|
2558
|
-
if (last >= 0 &&
|
|
2780
|
+
if (last >= 0 && isOpenHead(last) && !anchorHeld(scan, last, text.length))
|
|
2559
2781
|
span(last, text.length, true);
|
|
2560
2782
|
}
|
|
2561
2783
|
const armPool = (() => {
|
|
@@ -2611,7 +2833,7 @@ const armPool = (() => {
|
|
|
2611
2833
|
order,
|
|
2612
2834
|
phase: 1,
|
|
2613
2835
|
lexical,
|
|
2614
|
-
collect: (text, json, eat, out, isHead) => arm(text, json, eat, (start, end, undecided) => out.push(undecided === true ? { start, end, repl, rank, order, undecided } : { start, end, repl, rank, order }), isHead),
|
|
2836
|
+
collect: (text, json, eat, out, isHead) => arm(text, json, eat, (start, end, undecided, r = repl) => out.push(undecided === true ? { start, end, repl: r, rank, order, undecided } : { start, end, repl: r, rank, order }), isHead),
|
|
2615
2837
|
retry: undefined,
|
|
2616
2838
|
headSrc: undefined,
|
|
2617
2839
|
headFirst: undefined,
|
|
@@ -2629,7 +2851,7 @@ const armPool = (() => {
|
|
|
2629
2851
|
order,
|
|
2630
2852
|
phase: 2,
|
|
2631
2853
|
lexical: false,
|
|
2632
|
-
collect: (scan, text, json, eat, out, isHead) => arm(scan, text, json, eat, (start, end, undecided) => out.push(undecided === true ? { start, end, repl, rank, order, undecided } : { start, end, repl, rank, order }), isHead),
|
|
2854
|
+
collect: (scan, text, json, eat, out, isHead) => arm(scan, text, json, eat, (start, end, undecided, r = repl) => out.push(undecided === true ? { start, end, repl: r, rank, order, undecided } : { start, end, repl: r, rank, order }), isHead),
|
|
2633
2855
|
retry: undefined,
|
|
2634
2856
|
headSrc: undefined,
|
|
2635
2857
|
headFirst: undefined,
|
|
@@ -2641,7 +2863,7 @@ const armPool = (() => {
|
|
|
2641
2863
|
if (row === PEM_FENCE_PATTERN)
|
|
2642
2864
|
return forensicMember(row[1], order, (scan, text, _json, _eat, span, isHead) => collectPemFenceSpans(scan, text, span, isHead), "pem-armor", "other-families");
|
|
2643
2865
|
if (row === SSH2_FENCE_PATTERN)
|
|
2644
|
-
return forensicMember(row[1], order, (scan, text,
|
|
2866
|
+
return forensicMember(row[1], order, (scan, text, json, _eat, span, isHead) => collectSsh2FenceSpans(scan, text, json, span, isHead), "ssh2-armor");
|
|
2645
2867
|
if (row === URL_CREDENTIAL_PATTERN)
|
|
2646
2868
|
return forensicMember(row[1], order, (scan, text, json, _eat, span, isHead) => collectUrlCredentialSpans(scan, text, json, span, isHead));
|
|
2647
2869
|
if (row === JSON_SECRET_FIELD_PATTERN)
|
|
@@ -2651,9 +2873,9 @@ const armPool = (() => {
|
|
|
2651
2873
|
const members = [];
|
|
2652
2874
|
for (const row of STRUCTURAL_SECRET_PATTERNS)
|
|
2653
2875
|
members.push(structuralScannerMember(row, members.length));
|
|
2654
|
-
members.push(forensicMember(PPK_CONTAINER_TOKEN, members.length, (scan, text, _json, eat, span, isHead) => collectPpkContainerSpans(scan, text, eat, span, isHead)));
|
|
2655
|
-
members.push(forensicMember(PEM_FRAGMENT_TOKEN, members.length, (scan, text, json, _eat, span) => collectPemFragmentSpans(scan, text, json, span), "pem-armor"));
|
|
2656
|
-
members.push(forensicMember(HEADLESS_PPK_TOKEN, members.length, (scan, text, _json, _eat, span, isHead) => collectHeadlessPpkSpans(scan, text, span, isHead)));
|
|
2876
|
+
members.push(forensicMember(PPK_CONTAINER_TOKEN, members.length, (scan, text, _json, eat, span, isHead) => collectPpkContainerSpans(scan, text, eat, span, isHead), "putty-ppk"));
|
|
2877
|
+
members.push(forensicMember(PEM_FRAGMENT_TOKEN, members.length, (scan, text, json, _eat, span, isHead) => collectPemFragmentSpans(scan, text, json, span, isHead), "pem-armor"));
|
|
2878
|
+
members.push(forensicMember(HEADLESS_PPK_TOKEN, members.length, (scan, text, _json, _eat, span, isHead) => collectHeadlessPpkSpans(scan, text, span, isHead), "putty-ppk"));
|
|
2657
2879
|
members.push(scannerMember(ANCHORED_VALUE_TOKEN, members.length, (text, json, _eat, span, isHead) => collectAnchoredValueSpans(text, json, span, isHead), ARM_RANK.keyword, true));
|
|
2658
2880
|
for (const row of KEYWORD_SECRET_PATTERNS)
|
|
2659
2881
|
members.push(regexMember(row, ARM_RANK.keyword, members.length));
|
|
@@ -2683,8 +2905,15 @@ const armPool = (() => {
|
|
|
2683
2905
|
}
|
|
2684
2906
|
};
|
|
2685
2907
|
const lanePhase2 = phase2.filter(inScope);
|
|
2686
|
-
const viewKeys = [...new Set(lanePhase2.map(viewKeyOf))];
|
|
2687
|
-
return {
|
|
2908
|
+
const viewKeys = [...new Set([...lanePhase2.map(viewKeyOf), ...lanePhase2.flatMap((m) => (m.family === undefined ? [] : [m.family]))])];
|
|
2909
|
+
return {
|
|
2910
|
+
phase1: phase1.filter(inScope),
|
|
2911
|
+
phase2: lanePhase2,
|
|
2912
|
+
retry: retryMembers.filter(inScope),
|
|
2913
|
+
viewKeys,
|
|
2914
|
+
viewOf: lanePhase2.map((m) => viewKeys.indexOf(viewKeyOf(m))),
|
|
2915
|
+
othersOf: lanePhase2.map((m) => (m.family === undefined ? -1 : viewKeys.indexOf(m.family))),
|
|
2916
|
+
};
|
|
2688
2917
|
};
|
|
2689
2918
|
const LANES = {
|
|
2690
2919
|
"free-text": laneOf("free-text"),
|
|
@@ -2745,12 +2974,16 @@ const armPool = (() => {
|
|
|
2745
2974
|
const before = spans.length;
|
|
2746
2975
|
for (let j = 0; j < lane.phase2.length; j++) {
|
|
2747
2976
|
const view = views[lane.viewOf[j]];
|
|
2977
|
+
const oi = lane.othersOf[j];
|
|
2978
|
+
const others = oi < 0 ? undefined : views[oi];
|
|
2748
2979
|
const dirty = view.dirty;
|
|
2749
|
-
|
|
2980
|
+
const lo = foot[2 * j];
|
|
2981
|
+
const hi = foot[2 * j + 1];
|
|
2982
|
+
if (dirty !== undefined && footprintClean(dirty, lo, hi) && (others === undefined || (others.dirty !== undefined && footprintClean(others.dirty, lo, hi)))) {
|
|
2750
2983
|
forensicSkips += 1;
|
|
2751
2984
|
continue;
|
|
2752
2985
|
}
|
|
2753
|
-
useEvidenceView(scan, view.table, dirty);
|
|
2986
|
+
useEvidenceView(scan, view.table, dirty, others?.table);
|
|
2754
2987
|
scan.footLo = FOOT_NONE;
|
|
2755
2988
|
scan.footHi = -1;
|
|
2756
2989
|
lane.phase2[j].collect(scan, text, json, level === 0 ? eat : undefined, spans, isHead);
|