@sema-agent/server 7.139.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 +471 -0
- package/README.md +1 -1
- package/README.zh-CN.md +1 -1
- package/USAGE.md +94 -26
- package/dist/adoption/runner.js +1 -1
- package/dist/adoption/sql.js +1 -1
- package/dist/approval-batch-census.d.ts +27 -0
- package/dist/approval-batch-census.js +27 -0
- package/dist/approval-card.d.ts +37 -22
- 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/bench/s1/live-deps.js +3 -2
- package/dist/bench/s1/row.d.ts +5 -2
- package/dist/bench/s1/row.js +2 -22
- package/dist/boot/adoption.js +1 -1
- 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 +18 -12
- package/dist/boot/execution-lane.d.ts +8 -0
- package/dist/boot/execution-lane.js +6 -1
- package/dist/boot/memory-boundary.d.ts +16 -53
- package/dist/boot/memory-boundary.js +10 -20
- package/dist/boot/memory-consolidation.d.ts +16 -12
- package/dist/boot/memory-consolidation.js +24 -13
- package/dist/boot/reapers.d.ts +2 -2
- package/dist/boot/resolve-spec.d.ts +3 -5
- package/dist/boot/resolve-spec.js +9 -8
- package/dist/boot/runner-deps.d.ts +3 -3
- package/dist/boot/runner-deps.js +1 -0
- 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-03-store-backend.d.ts +2 -6
- package/dist/boot/stage-03-store-backend.js +2 -3
- package/dist/boot/stage-04-stores.d.ts +2 -6
- package/dist/boot/stage-04-stores.js +5 -5
- package/dist/boot/stage-05-execution-env.d.ts +2 -6
- package/dist/boot/stage-05-execution-env.js +3 -1
- package/dist/boot/stage-06-runners.d.ts +2 -8
- package/dist/boot/stage-06-runners.js +3 -3
- package/dist/boot/stage-07-capability-layer.d.ts +2 -7
- package/dist/boot/stage-07-capability-layer.js +8 -10
- package/dist/boot/stage-08-reapers.d.ts +2 -7
- package/dist/boot/stage-09-leader.d.ts +2 -7
- package/dist/boot/stage-10-http-server.d.ts +2 -7
- package/dist/boot/stage-10-http-server.js +6 -4
- package/dist/boot/stores.d.ts +3 -6
- package/dist/boot/stores.js +37 -44
- package/dist/boot/task-list-lane.d.ts +1 -1
- package/dist/boot/task-list-lane.js +1 -1
- package/dist/boot/web-admin.js +36 -9
- package/dist/capabilities/execution-lane.d.ts +2 -2
- 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 +12 -8
- package/dist/config-provider.d.ts +18 -17
- package/dist/config-provider.js +28 -36
- package/dist/config-types.d.ts +36 -9
- package/dist/config.js +56 -46
- package/dist/delegated-session-owner.d.ts +23 -20
- package/dist/delegated-session-owner.js +2 -2
- package/dist/deployment-shape.d.ts +9 -8
- package/dist/deployment-shape.js +4 -4
- 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 +145 -107
- package/dist/execution-lane-caps.js +144 -83
- 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/memory-compliance-failure-reply.d.ts +3 -1
- package/dist/http/memory-compliance-failure-reply.js +5 -0
- package/dist/http/memory-layer-reply.d.ts +30 -0
- package/dist/http/memory-layer-reply.js +24 -0
- package/dist/http/resume-legs.js +49 -35
- package/dist/http/retired-service-deps.d.ts +1 -1
- package/dist/http/retired-service-deps.js +1 -1
- package/dist/http/route-table.js +2 -0
- package/dist/http/routes/adoption.js +1 -1
- 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 +4 -1
- package/dist/http/routes/memory-compliance.js +9 -2
- package/dist/http/routes/memory-consolidation.js +12 -1
- package/dist/http/routes/memory-origin.js +14 -2
- package/dist/http/routes/memory-policy.js +7 -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 +33 -15
- package/dist/http/routes/tasks.js +67 -68
- 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 +14 -27
- package/dist/http/server.js +2 -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/identity-source.d.ts +1 -1
- package/dist/identity-source.js +1 -1
- package/dist/instruction-source-notice.d.ts +7 -0
- package/dist/instruction-source-notice.js +6 -6
- package/dist/lane-home-probe.d.ts +68 -42
- package/dist/lane-home-probe.js +53 -30
- package/dist/leader/wire.js +5 -4
- package/dist/mcp-assertion-header.d.ts +4 -0
- package/dist/mcp-assertion-header.js +5 -0
- package/dist/memory-bundle-engine.d.ts +16 -9
- package/dist/memory-bundle-engine.js +29 -6
- package/dist/memory-operator-faces.d.ts +85 -30
- package/dist/memory-operator-faces.js +136 -30
- package/dist/memory-posture.d.ts +1 -1
- package/dist/memory-project-layer.d.ts +3 -21
- package/dist/memory-project-layer.js +11 -8
- package/dist/memory-scope-stores.d.ts +73 -0
- package/dist/memory-scope-stores.js +86 -0
- package/dist/memory-scope.d.ts +33 -9
- package/dist/memory-scope.js +17 -5
- package/dist/memory-sync-client.d.ts +34 -9
- package/dist/memory-sync-client.js +108 -28
- 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/adoption-log-sql.js +4 -4
- package/dist/plugins/approval-ask-store-sql.js +14 -14
- package/dist/plugins/approval-nonce-store.js +4 -4
- package/dist/plugins/blob-backend.d.ts +4 -4
- package/dist/plugins/blob-backend.js +7 -7
- package/dist/plugins/breaker-state-sql.js +2 -2
- package/dist/plugins/caching-session-store.d.ts +6 -0
- package/dist/plugins/caching-session-store.js +3 -0
- package/dist/plugins/checkpoint-index-columns.js +2 -2
- package/dist/plugins/checkpoint-store-sql.d.ts +65 -17
- package/dist/plugins/checkpoint-store-sql.js +90 -19
- package/dist/plugins/device-store-sql.d.ts +5 -2
- package/dist/plugins/device-store-sql.js +23 -20
- package/dist/plugins/ensure-index.d.ts +1 -2
- package/dist/plugins/execution-placement-sql.d.ts +19 -9
- package/dist/plugins/execution-placement-sql.js +20 -9
- package/dist/plugins/file-history-store-sql.d.ts +2 -2
- package/dist/plugins/file-history-store-sql.js +4 -4
- 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/image-bake-store-sql.js +9 -9
- package/dist/plugins/image-index-sql.js +11 -11
- package/dist/plugins/k8s-exec-protocol.d.ts +4 -1
- package/dist/plugins/k8s-exec-protocol.js +6 -1
- package/dist/plugins/leader-run-store-sql.js +4 -4
- 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/mailbox-store-sql.d.ts +2 -1
- package/dist/plugins/mailbox-store-sql.js +4 -4
- package/dist/plugins/memory-engine-tidb.js +5 -5
- package/dist/plugins/memory-optout-grant-store-sql.js +1 -1
- package/dist/plugins/memory-run-store.d.ts +2 -2
- package/dist/plugins/memory-run-store.js +1 -1
- package/dist/plugins/memory-sync-store-tidb.js +3 -3
- package/dist/plugins/one-time-migrations.d.ts +52 -11
- package/dist/plugins/one-time-migrations.js +101 -39
- package/dist/plugins/org-delivery-quota-store.js +2 -2
- package/dist/plugins/outcome-ledger-sql.js +2 -2
- package/dist/plugins/permission-rule-store-sql.d.ts +4 -4
- package/dist/plugins/permission-rule-store-sql.js +16 -16
- 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-adb.d.ts +7 -24
- package/dist/plugins/remote-env-adb.js +22 -5
- package/dist/plugins/remote-env-device.d.ts +2 -28
- package/dist/plugins/remote-env-device.js +2 -1
- package/dist/plugins/remote-env-e2b.d.ts +40 -27
- package/dist/plugins/remote-env-e2b.js +180 -20
- package/dist/plugins/remote-env-file-error.d.ts +1 -1
- package/dist/plugins/remote-env-file-error.js +8 -2
- package/dist/plugins/remote-env-host.d.ts +19 -35
- package/dist/plugins/remote-env-host.js +26 -3
- package/dist/plugins/remote-env-k8s.d.ts +22 -14
- package/dist/plugins/remote-env-k8s.js +150 -26
- package/dist/plugins/remote-env-local-docker.d.ts +27 -24
- package/dist/plugins/remote-env-local-docker.js +91 -24
- package/dist/plugins/remote-env-ssh.d.ts +4 -24
- package/dist/plugins/remote-env-ssh.js +4 -3
- package/dist/plugins/resume-anchor-store-sql.js +2 -2
- package/dist/plugins/retention-lane-store-sql.d.ts +1 -1
- package/dist/plugins/retention-lane-store-sql.js +2 -2
- package/dist/plugins/retention-store-sql.d.ts +1 -1
- package/dist/plugins/retention-store-sql.js +6 -6
- package/dist/plugins/run-store-sql.d.ts +3 -3
- package/dist/plugins/run-store-sql.js +19 -18
- package/dist/plugins/sandbox-read-floor.d.ts +273 -0
- package/dist/plugins/sandbox-read-floor.js +608 -0
- package/dist/plugins/schema-consistency.d.ts +6 -6
- package/dist/plugins/schema-consistency.js +1 -1
- package/dist/plugins/schema-ensure-legs.d.ts +2 -3
- package/dist/plugins/schema-ensure-legs.js +2 -2
- package/dist/plugins/schema-live-columns.d.ts +1 -1
- package/dist/plugins/session-capture-record-store-sql.js +1 -1
- 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-rules-column.d.ts +1 -1
- package/dist/plugins/session-rules-column.js +1 -1
- package/dist/plugins/session-store.d.ts +18 -20
- package/dist/plugins/session-store.js +4 -12
- package/dist/plugins/session-workspace-cell.d.ts +1 -1
- package/dist/plugins/session-workspace-cell.js +1 -1
- package/dist/plugins/shared-memory-store-sql.js +2 -2
- package/dist/plugins/sql-driver.d.ts +10 -5
- package/dist/plugins/sql-driver.js +6 -6
- package/dist/plugins/sql-errors.js +1 -1
- package/dist/plugins/store-backend.d.ts +35 -6
- package/dist/plugins/store-backend.js +27 -10
- package/dist/plugins/store-contracts.d.ts +9 -2
- package/dist/plugins/task-list-store-sql.js +11 -11
- package/dist/plugins/tidb-pool.js +7 -6
- package/dist/plugins/tidb-session-storage.js +4 -4
- package/dist/plugins/tidb-session-store.d.ts +4 -1
- package/dist/plugins/tidb-session-store.js +16 -15
- package/dist/plugins/tool-result-store-sql.d.ts +2 -2
- package/dist/plugins/tool-result-store-sql.js +3 -3
- package/dist/plugins/user-input-ledger-sql.js +2 -2
- package/dist/plugins/workflow-journal-store-sql.js +5 -5
- package/dist/plugins/workflow-run-store-sql.js +19 -19
- package/dist/plugins/write-behind-counter.d.ts +2 -3
- package/dist/plugins/write-behind-counter.js +5 -5
- package/dist/resource-suspend.d.ts +14 -12
- package/dist/resource-suspend.js +1 -1
- package/dist/resource-window.d.ts +4 -3
- package/dist/run-cancel-context.d.ts +34 -0
- package/dist/run-cancel-context.js +7 -0
- package/dist/run-local.js +14 -19
- package/dist/runs.d.ts +47 -16
- package/dist/runs.js +112 -113
- 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 +10 -4
- package/dist/session-incarnation.js +1 -1
- package/dist/session-policy-wire.d.ts +1 -1
- 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/sql-engine-posture.d.ts +1 -1
- package/dist/sql-engine-posture.js +1 -1
- 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/{plugins → stores-sql}/background-agent-store-sql.d.ts +2 -2
- package/dist/{plugins → stores-sql}/background-agent-store-sql.js +8 -8
- package/dist/{plugins → stores-sql}/session-policy-store-sql.d.ts +7 -5
- package/dist/{plugins → stores-sql}/session-policy-store-sql.js +12 -12
- package/dist/{plugins → stores-sql}/usage-window-store-sql.d.ts +1 -1
- package/dist/{plugins → stores-sql}/usage-window-store-sql.js +3 -3
- package/dist/task-a2a.d.ts +1 -1
- package/dist/task-a2a.js +5 -6
- package/dist/task-mcp.d.ts +21 -8
- package/dist/task-mcp.js +17 -9
- package/dist/task-settings.d.ts +52 -25
- package/dist/task-settings.js +91 -28
- package/dist/terminal.d.ts +103 -19
- package/dist/terminal.js +72 -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.d.ts +3 -1
- package/dist/trace/redact.js +395 -110
- package/dist/trace/self-minted-wire.d.ts +226 -2
- package/dist/trace/self-minted-wire.js +79 -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 +11 -7
- package/dist/web-admin-client/candidate-gate.js +1 -1
- package/dist/web-admin-client/http-client.d.ts +5 -4
- package/dist/web-admin-client/http-client.js +2 -2
- package/dist/web-admin-client/read-warnings.d.ts +7 -17
- package/dist/web-admin-client/read-warnings.js +5 -9
- package/docs/DEVICE-EXECUTOR-PROTOCOL.md +3 -2
- package/package.json +2 -2
- package/wire-changes/server@7.140.0.json +184 -0
- 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,38 +1190,54 @@ 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
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
return false;
|
|
1181
|
-
const unit = opaqueUnitEnd(scan, p);
|
|
1182
|
-
p = unit > 0 && unit <= to ? unit : p + 1;
|
|
1183
|
-
n += 1;
|
|
1209
|
+
const lo = firstCredentialSpanFrom(t, from);
|
|
1210
|
+
let a = lo;
|
|
1211
|
+
let b = t.ends.length;
|
|
1212
|
+
while (a < b) {
|
|
1213
|
+
const mid = (a + b) >>> 1;
|
|
1214
|
+
if (t.ends[mid] <= to)
|
|
1215
|
+
a = mid + 1;
|
|
1216
|
+
else
|
|
1217
|
+
b = mid;
|
|
1184
1218
|
}
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1219
|
+
if (a === lo)
|
|
1220
|
+
return false;
|
|
1221
|
+
const saved = unitSavings(t);
|
|
1222
|
+
return to - from - (saved[a] - saved[lo]) <= max;
|
|
1223
|
+
}
|
|
1224
|
+
const UNIT_SAVINGS = new WeakMap();
|
|
1225
|
+
function unitSavings(t) {
|
|
1226
|
+
let saved = UNIT_SAVINGS.get(t);
|
|
1227
|
+
if (saved === undefined) {
|
|
1228
|
+
const n = t.starts.length;
|
|
1229
|
+
saved = new Float64Array(n + 1);
|
|
1230
|
+
for (let k = 0; k < n; k++)
|
|
1231
|
+
saved[k + 1] = saved[k] + (t.ends[k] - t.starts[k] - 1);
|
|
1232
|
+
UNIT_SAVINGS.set(t, saved);
|
|
1233
|
+
}
|
|
1234
|
+
return saved;
|
|
1235
|
+
}
|
|
1236
|
+
export function unitCountWithinForTest(table, from, to, max) {
|
|
1237
|
+
const t = { starts: Int32Array.from(table, (x) => x[0]), ends: Int32Array.from(table, (x) => x[1]) };
|
|
1238
|
+
return unitCountWithin(buildForensicScan(t, 1), from, to, max);
|
|
1239
|
+
}
|
|
1240
|
+
function collectSsh2FenceSpans(scan, text, json, span, isHead) {
|
|
1188
1241
|
let cachedFrom = 0;
|
|
1189
1242
|
let cachedAt;
|
|
1190
1243
|
const nextClose = (from) => {
|
|
@@ -1201,29 +1254,78 @@ function collectSsh2FenceSpans(scan, text, span, isHead) {
|
|
|
1201
1254
|
cachedAt = p;
|
|
1202
1255
|
return p;
|
|
1203
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
|
+
};
|
|
1204
1290
|
for (let from = 0;;) {
|
|
1205
1291
|
const fence = text.indexOf(SSH2_FENCE_OPEN, from);
|
|
1206
1292
|
if (fence < 0)
|
|
1207
1293
|
return;
|
|
1208
|
-
|
|
1294
|
+
const bodyFrom = fence + SSH2_FENCE_OPEN.length;
|
|
1295
|
+
if (anchorHeld(scan, fence, bodyFrom)) {
|
|
1209
1296
|
from = fence + 1;
|
|
1210
1297
|
continue;
|
|
1211
1298
|
}
|
|
1212
|
-
const bodyFrom = fence + SSH2_FENCE_OPEN.length;
|
|
1213
1299
|
if (isHead && unitCountWithin(scan, bodyFrom, text.length, SSH2_BODY_MAX)) {
|
|
1214
1300
|
span(fence, text.length, true);
|
|
1215
1301
|
return;
|
|
1216
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
|
+
}
|
|
1217
1315
|
const close = nextClose(bodyFrom);
|
|
1218
|
-
if (close < 0)
|
|
1219
|
-
|
|
1316
|
+
if (close < 0) {
|
|
1317
|
+
if (isHead)
|
|
1318
|
+
return;
|
|
1319
|
+
from = resume;
|
|
1320
|
+
continue;
|
|
1321
|
+
}
|
|
1220
1322
|
if (!unitCountWithin(scan, bodyFrom, close, SSH2_BODY_MAX)) {
|
|
1221
|
-
from =
|
|
1323
|
+
from = resume;
|
|
1222
1324
|
continue;
|
|
1223
1325
|
}
|
|
1224
1326
|
const end = close + SSH2_FENCE_CLOSE.length;
|
|
1225
1327
|
span(fence, end);
|
|
1226
|
-
from = scanResumeAfterHit(fence, end, SSH2_FENCE_OPEN.length);
|
|
1328
|
+
from = Math.max(resume, scanResumeAfterHit(fence, end, SSH2_FENCE_OPEN.length));
|
|
1227
1329
|
}
|
|
1228
1330
|
}
|
|
1229
1331
|
const PEM_END_OPEN = "-----END";
|
|
@@ -1241,16 +1343,20 @@ function pemFenceLineEnd(text, labelFrom) {
|
|
|
1241
1343
|
}
|
|
1242
1344
|
return isPemFenceLabel(text, labelFrom, close) ? close + PEM_FENCE_CLOSE.length : PEM_LINE_INVALID;
|
|
1243
1345
|
}
|
|
1244
|
-
function collectPemFenceSpans(text, span, isHead) {
|
|
1346
|
+
function collectPemFenceSpans(scan, text, span, isHead) {
|
|
1245
1347
|
let endFrom = -1;
|
|
1246
1348
|
let endAt = -1;
|
|
1247
1349
|
let endAfter = -1;
|
|
1350
|
+
const visibleEndLine = (b) => {
|
|
1351
|
+
const after = pemFenceLineEnd(text, b + PEM_END_OPEN.length);
|
|
1352
|
+
return after >= 0 && !inOpaqueUnit(scan, b) ? after : -1;
|
|
1353
|
+
};
|
|
1248
1354
|
const earliestEndFence = (from) => {
|
|
1249
1355
|
if (endFrom >= 0 && from >= endFrom && (endAt < 0 || endAt >= from))
|
|
1250
1356
|
return;
|
|
1251
1357
|
let b = text.indexOf(PEM_END_OPEN, from);
|
|
1252
1358
|
let after = -1;
|
|
1253
|
-
while (b >= 0 && (after =
|
|
1359
|
+
while (b >= 0 && (after = visibleEndLine(b)) < 0)
|
|
1254
1360
|
b = text.indexOf(PEM_END_OPEN, b + 1);
|
|
1255
1361
|
endFrom = from;
|
|
1256
1362
|
endAt = b;
|
|
@@ -1265,6 +1371,10 @@ function collectPemFenceSpans(text, span, isHead) {
|
|
|
1265
1371
|
from = fence + 1;
|
|
1266
1372
|
continue;
|
|
1267
1373
|
}
|
|
1374
|
+
if (anchorHeld(scan, fence, bodyFrom === PEM_LINE_OPEN ? text.length : bodyFrom)) {
|
|
1375
|
+
from = fence + 1;
|
|
1376
|
+
continue;
|
|
1377
|
+
}
|
|
1268
1378
|
if (bodyFrom !== PEM_LINE_OPEN)
|
|
1269
1379
|
earliestEndFence(bodyFrom);
|
|
1270
1380
|
if (bodyFrom === PEM_LINE_OPEN || endAt < 0) {
|
|
@@ -1272,7 +1382,7 @@ function collectPemFenceSpans(text, span, isHead) {
|
|
|
1272
1382
|
span(fence, text.length, true);
|
|
1273
1383
|
return;
|
|
1274
1384
|
}
|
|
1275
|
-
if (
|
|
1385
|
+
if (!unitCountWithin(scan, bodyFrom, endAt, PEM_FENCE_BODY_MAX)) {
|
|
1276
1386
|
from = fence + 1;
|
|
1277
1387
|
continue;
|
|
1278
1388
|
}
|
|
@@ -1280,35 +1390,80 @@ function collectPemFenceSpans(text, span, isHead) {
|
|
|
1280
1390
|
from = scanResumeAfterHit(fence, endAfter, PEM_FENCE_OPEN.length);
|
|
1281
1391
|
}
|
|
1282
1392
|
}
|
|
1283
|
-
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) {
|
|
1284
1444
|
let cur = findPemFence(scan, text, 0);
|
|
1285
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);
|
|
1286
1459
|
while (cur !== undefined) {
|
|
1287
1460
|
const next = findPemFence(scan, text, cur.after);
|
|
1288
1461
|
const limit = next === undefined ? text.length : next.fence;
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
const e = rightmostPemRunEnd(scan, text, scan.hdr.search, scan.hdr.contentEnd);
|
|
1295
|
-
if (e > rightmost)
|
|
1296
|
-
rightmost = e;
|
|
1297
|
-
pos = scan.hdr.next;
|
|
1298
|
-
allowBare = false;
|
|
1299
|
-
if (scan.hdr.hitLimit) {
|
|
1300
|
-
hitLimit = true;
|
|
1301
|
-
break;
|
|
1302
|
-
}
|
|
1303
|
-
if (scan.hdr.jsonEnd)
|
|
1304
|
-
break;
|
|
1305
|
-
}
|
|
1306
|
-
inherited = hitLimit;
|
|
1307
|
-
let bodyEnd = parsePemBody(scan, text, pos, limit);
|
|
1308
|
-
if (bodyEnd < 0)
|
|
1309
|
-
bodyEnd = rightmost;
|
|
1310
|
-
if (bodyEnd >= 0)
|
|
1311
|
-
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);
|
|
1312
1467
|
cur = next;
|
|
1313
1468
|
}
|
|
1314
1469
|
}
|
|
@@ -1414,10 +1569,6 @@ function collectHeadlessPpkSpans(scan, text, span, isHead) {
|
|
|
1414
1569
|
const at = text.indexOf(KEY, search);
|
|
1415
1570
|
if (at === -1)
|
|
1416
1571
|
break;
|
|
1417
|
-
if (inOpaqueUnit(scan, at)) {
|
|
1418
|
-
search = at + KEY.length;
|
|
1419
|
-
continue;
|
|
1420
|
-
}
|
|
1421
1572
|
let p = at + KEY.length;
|
|
1422
1573
|
const d0 = p;
|
|
1423
1574
|
while (p < n && opaqueUnitEnd(scan, p) < 0 && text.charCodeAt(p) >= 48 && text.charCodeAt(p) <= 57)
|
|
@@ -1426,6 +1577,10 @@ function collectHeadlessPpkSpans(scan, text, span, isHead) {
|
|
|
1426
1577
|
search = at + KEY.length;
|
|
1427
1578
|
continue;
|
|
1428
1579
|
}
|
|
1580
|
+
if (anchorHeld(scan, at, p)) {
|
|
1581
|
+
search = at + KEY.length;
|
|
1582
|
+
continue;
|
|
1583
|
+
}
|
|
1429
1584
|
let end = -1;
|
|
1430
1585
|
let open = false;
|
|
1431
1586
|
for (let from = p;;) {
|
|
@@ -1524,6 +1679,14 @@ const JSON_SECRET_FIELD_PATTERN = (() => {
|
|
|
1524
1679
|
throw new Error(`redact: expected exactly one JSON secret-field rule (replacement '$1"«redacted»"') in FULL_ARM_PATTERNS, found ${rows.length}`);
|
|
1525
1680
|
return rows[0];
|
|
1526
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
|
+
})();
|
|
1527
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;
|
|
1528
1691
|
const isLineTerminator = (c) => c === 10 || c === 13 || c === 0x2028 || c === 0x2029;
|
|
1529
1692
|
const isControlCode = (c) => c < 0x20 || c === 0x7f;
|
|
@@ -1610,7 +1773,7 @@ let forensicRescanMode = "incremental";
|
|
|
1610
1773
|
export function setForensicRescanModeForTest(mode) {
|
|
1611
1774
|
forensicRescanMode = mode;
|
|
1612
1775
|
}
|
|
1613
|
-
function foldCredentialSpans(prev, spans, from) {
|
|
1776
|
+
function foldCredentialSpans(prev, spans, from, admit) {
|
|
1614
1777
|
const cap = spans.length - from;
|
|
1615
1778
|
if (cap === 0)
|
|
1616
1779
|
return { next: prev, dirty: NO_CREDENTIAL_SPANS };
|
|
@@ -1619,6 +1782,8 @@ function foldCredentialSpans(prev, spans, from) {
|
|
|
1619
1782
|
let m = 0;
|
|
1620
1783
|
for (let i = from; i < spans.length; i++) {
|
|
1621
1784
|
const sp = spans[i];
|
|
1785
|
+
if (admit !== undefined && !admit(sp))
|
|
1786
|
+
continue;
|
|
1622
1787
|
const k = firstCredentialSpanFrom(prev, sp.start + 1) - 1;
|
|
1623
1788
|
if (k >= 0 && prev.ends[k] >= sp.end)
|
|
1624
1789
|
continue;
|
|
@@ -1729,6 +1894,7 @@ const FOOT_NONE = 0x7fffffff;
|
|
|
1729
1894
|
function buildForensicScan(table, armCount) {
|
|
1730
1895
|
return {
|
|
1731
1896
|
table,
|
|
1897
|
+
others: undefined,
|
|
1732
1898
|
cursor: 0,
|
|
1733
1899
|
dirty: undefined,
|
|
1734
1900
|
footLo: FOOT_NONE,
|
|
@@ -1742,8 +1908,9 @@ function buildForensicScan(table, armCount) {
|
|
|
1742
1908
|
hdr: { search: 0, contentEnd: 0, next: 0, hitLimit: false, jsonEnd: false },
|
|
1743
1909
|
};
|
|
1744
1910
|
}
|
|
1745
|
-
function
|
|
1911
|
+
function useEvidenceView(scan, table, dirty, others) {
|
|
1746
1912
|
scan.table = table;
|
|
1913
|
+
scan.others = others;
|
|
1747
1914
|
scan.cursor = 0;
|
|
1748
1915
|
scan.dirty = dirty;
|
|
1749
1916
|
}
|
|
@@ -1777,6 +1944,22 @@ function inOpaqueUnit(scan, p) {
|
|
|
1777
1944
|
const k = firstCredentialSpanFrom(t, p) - 1;
|
|
1778
1945
|
return k >= 0 && st[k] < p && t.ends[k] > p;
|
|
1779
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
|
+
}
|
|
1780
1963
|
function opaqueUnitEnd(scan, p) {
|
|
1781
1964
|
noteEvidenceRead(scan, p);
|
|
1782
1965
|
return opaqueUnitEndAt(scan, p);
|
|
@@ -1975,21 +2158,22 @@ function collectUrlCredentialSpans(scan, text, json, span, isHead) {
|
|
|
1975
2158
|
scan.footHi = armHi;
|
|
1976
2159
|
}
|
|
1977
2160
|
const JSON_FIELD_ANCHOR = '"';
|
|
1978
|
-
|
|
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];
|
|
1979
2164
|
const n = text.length;
|
|
1980
|
-
|
|
2165
|
+
const nameFrom = p + form.length;
|
|
2166
|
+
let i = nameFrom;
|
|
1981
2167
|
for (; i < n; i++) {
|
|
1982
2168
|
const c = text.charCodeAt(i);
|
|
1983
|
-
if (c === CH_QUOTE)
|
|
1984
|
-
break;
|
|
1985
|
-
if (i - p > CREDENTIAL_NAME_MAX)
|
|
1986
|
-
return -1;
|
|
1987
2169
|
if (!(isAsciiAlnumCode(c) || c === CH_UNDERSCORE || c === CH_MINUS))
|
|
2170
|
+
break;
|
|
2171
|
+
if (i - nameFrom >= CREDENTIAL_NAME_MAX)
|
|
1988
2172
|
return -1;
|
|
1989
2173
|
}
|
|
1990
|
-
if (i
|
|
2174
|
+
if (i === nameFrom || !text.startsWith(form, i) || credentialNameAnchor(text.slice(nameFrom, i)) === undefined)
|
|
1991
2175
|
return -1;
|
|
1992
|
-
i +=
|
|
2176
|
+
i += form.length;
|
|
1993
2177
|
while (i < n && isRegexSpace(text.charCodeAt(i)))
|
|
1994
2178
|
i += 1;
|
|
1995
2179
|
if (text.charCodeAt(i) !== CH_COLON)
|
|
@@ -1997,38 +2181,99 @@ function jsonSecretFieldValueQuoteAt(text, p) {
|
|
|
1997
2181
|
i += 1;
|
|
1998
2182
|
while (i < n && isRegexSpace(text.charCodeAt(i)))
|
|
1999
2183
|
i += 1;
|
|
2000
|
-
return text.
|
|
2184
|
+
return text.startsWith(form, i) ? i : -1;
|
|
2001
2185
|
}
|
|
2002
2186
|
const STRING_OPEN = -2;
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
|
|
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) {
|
|
2006
2200
|
const c = text.charCodeAt(i);
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
|
|
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);
|
|
2012
2243
|
continue;
|
|
2013
2244
|
}
|
|
2014
|
-
|
|
2245
|
+
case U_BREAK:
|
|
2246
|
+
return -1;
|
|
2247
|
+
case U_END:
|
|
2248
|
+
return isHead ? STRING_OPEN : -1;
|
|
2015
2249
|
}
|
|
2016
|
-
i += 1;
|
|
2017
2250
|
}
|
|
2018
|
-
return isHead ? STRING_OPEN : -1;
|
|
2019
2251
|
}
|
|
2020
2252
|
function collectJsonSecretFieldSpans(text, span, isHead) {
|
|
2253
|
+
let from = 0;
|
|
2021
2254
|
for (let p = text.indexOf(JSON_FIELD_ANCHOR); p >= 0;) {
|
|
2022
|
-
|
|
2023
|
-
|
|
2024
|
-
|
|
2025
|
-
|
|
2026
|
-
|
|
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;
|
|
2027
2275
|
}
|
|
2028
|
-
|
|
2029
|
-
const end = open ? text.length : found;
|
|
2030
|
-
span(q, end, open);
|
|
2031
|
-
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);
|
|
2032
2277
|
}
|
|
2033
2278
|
}
|
|
2034
2279
|
const STRONG_AUTH_SCHEMES = new Set(["basic", "bearer"]);
|
|
@@ -2417,7 +2662,7 @@ export function structuralScannerForTest(arm) {
|
|
|
2417
2662
|
case "json-secret-field":
|
|
2418
2663
|
return { declared: new RegExp(JSON_SECRET_FIELD_PATTERN[0].source, JSON_SECRET_FIELD_PATTERN[0].flags), spans: collect(collectJsonSecretFieldSpans) };
|
|
2419
2664
|
case "pem-fence":
|
|
2420
|
-
return { declared: new RegExp(PEM_FENCE_PATTERN[0].source, PEM_FENCE_PATTERN[0].flags), spans: collect(collectPemFenceSpans) };
|
|
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)) };
|
|
2421
2666
|
}
|
|
2422
2667
|
}
|
|
2423
2668
|
let ppkScanBytes = 0;
|
|
@@ -2450,9 +2695,9 @@ function collectPpkContainerSpans(scan, text, eat, span, isHead) {
|
|
|
2450
2695
|
}
|
|
2451
2696
|
return p;
|
|
2452
2697
|
};
|
|
2453
|
-
const
|
|
2698
|
+
const versionedHeadEnd = (at) => {
|
|
2454
2699
|
const p = versionEnd(at);
|
|
2455
|
-
return p > at + HEAD.length && text.startsWith(": ", p);
|
|
2700
|
+
return p > at + HEAD.length && text.startsWith(": ", p) ? p + 2 : -1;
|
|
2456
2701
|
};
|
|
2457
2702
|
const isOpenHead = (at) => {
|
|
2458
2703
|
const p = versionEnd(at);
|
|
@@ -2460,8 +2705,11 @@ function collectPpkContainerSpans(scan, text, eat, span, isHead) {
|
|
|
2460
2705
|
};
|
|
2461
2706
|
const nextVersionedHead = (from) => {
|
|
2462
2707
|
let p = find(HEAD, from);
|
|
2463
|
-
|
|
2464
|
-
|
|
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
|
+
}
|
|
2465
2713
|
return p;
|
|
2466
2714
|
};
|
|
2467
2715
|
const hexRunEnd = (from) => {
|
|
@@ -2529,7 +2777,7 @@ function collectPpkContainerSpans(scan, text, eat, span, isHead) {
|
|
|
2529
2777
|
at = end >= text.length ? Number.POSITIVE_INFINITY : nextVersionedHead(end);
|
|
2530
2778
|
}
|
|
2531
2779
|
const last = isHead ? text.lastIndexOf(HEAD) : -1;
|
|
2532
|
-
if (last >= 0 &&
|
|
2780
|
+
if (last >= 0 && isOpenHead(last) && !anchorHeld(scan, last, text.length))
|
|
2533
2781
|
span(last, text.length, true);
|
|
2534
2782
|
}
|
|
2535
2783
|
const armPool = (() => {
|
|
@@ -2574,6 +2822,7 @@ const armPool = (() => {
|
|
|
2574
2822
|
retry: boundary === undefined ? undefined : (text, at, out) => collectStickySpans(boundary, text, at, out),
|
|
2575
2823
|
headSrc: boundary === undefined ? undefined : headSrc,
|
|
2576
2824
|
headFirst: boundary === undefined ? undefined : headFirst,
|
|
2825
|
+
family: undefined,
|
|
2577
2826
|
};
|
|
2578
2827
|
};
|
|
2579
2828
|
const scannerMember = (token, order, arm, rank = ARM_RANK.structural, lexical = false) => {
|
|
@@ -2584,13 +2833,16 @@ const armPool = (() => {
|
|
|
2584
2833
|
order,
|
|
2585
2834
|
phase: 1,
|
|
2586
2835
|
lexical,
|
|
2587
|
-
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),
|
|
2588
2837
|
retry: undefined,
|
|
2589
2838
|
headSrc: undefined,
|
|
2590
2839
|
headFirst: undefined,
|
|
2840
|
+
family: undefined,
|
|
2591
2841
|
};
|
|
2592
2842
|
};
|
|
2593
|
-
const forensicMember = (token, order, arm) => {
|
|
2843
|
+
const forensicMember = (token, order, arm, family = undefined, view = "all") => {
|
|
2844
|
+
if (view === "other-families" && family === undefined)
|
|
2845
|
+
throw new Error(`redact: forensic arm ${token} reads the other-families evidence view but declares no credential family`);
|
|
2594
2846
|
const repl = spanRepl(token);
|
|
2595
2847
|
const rank = ARM_RANK.structural;
|
|
2596
2848
|
return {
|
|
@@ -2599,17 +2851,19 @@ const armPool = (() => {
|
|
|
2599
2851
|
order,
|
|
2600
2852
|
phase: 2,
|
|
2601
2853
|
lexical: false,
|
|
2602
|
-
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),
|
|
2603
2855
|
retry: undefined,
|
|
2604
2856
|
headSrc: undefined,
|
|
2605
2857
|
headFirst: undefined,
|
|
2858
|
+
family,
|
|
2859
|
+
view,
|
|
2606
2860
|
};
|
|
2607
2861
|
};
|
|
2608
2862
|
const structuralScannerMember = (row, order) => {
|
|
2609
2863
|
if (row === PEM_FENCE_PATTERN)
|
|
2610
|
-
return
|
|
2864
|
+
return forensicMember(row[1], order, (scan, text, _json, _eat, span, isHead) => collectPemFenceSpans(scan, text, span, isHead), "pem-armor", "other-families");
|
|
2611
2865
|
if (row === SSH2_FENCE_PATTERN)
|
|
2612
|
-
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");
|
|
2613
2867
|
if (row === URL_CREDENTIAL_PATTERN)
|
|
2614
2868
|
return forensicMember(row[1], order, (scan, text, json, _eat, span, isHead) => collectUrlCredentialSpans(scan, text, json, span, isHead));
|
|
2615
2869
|
if (row === JSON_SECRET_FIELD_PATTERN)
|
|
@@ -2619,9 +2873,9 @@ const armPool = (() => {
|
|
|
2619
2873
|
const members = [];
|
|
2620
2874
|
for (const row of STRUCTURAL_SECRET_PATTERNS)
|
|
2621
2875
|
members.push(structuralScannerMember(row, members.length));
|
|
2622
|
-
members.push(forensicMember(PPK_CONTAINER_TOKEN, members.length, (scan, text, _json, eat, span, isHead) => collectPpkContainerSpans(scan, text, eat, span, isHead)));
|
|
2623
|
-
members.push(forensicMember(PEM_FRAGMENT_TOKEN, members.length, (scan, text, json, _eat, span) => collectPemFragmentSpans(scan, text, json, span)));
|
|
2624
|
-
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"));
|
|
2625
2879
|
members.push(scannerMember(ANCHORED_VALUE_TOKEN, members.length, (text, json, _eat, span, isHead) => collectAnchoredValueSpans(text, json, span, isHead), ARM_RANK.keyword, true));
|
|
2626
2880
|
for (const row of KEYWORD_SECRET_PATTERNS)
|
|
2627
2881
|
members.push(regexMember(row, ARM_RANK.keyword, members.length));
|
|
@@ -2636,6 +2890,13 @@ const armPool = (() => {
|
|
|
2636
2890
|
const retryMembers = members.filter((m) => m.retry !== undefined);
|
|
2637
2891
|
const phase1 = members.filter((m) => m.phase === 1);
|
|
2638
2892
|
const phase2 = members.filter((m) => m.phase === 2);
|
|
2893
|
+
const familyOfOrder = members.map((m) => m.family);
|
|
2894
|
+
const viewKeyOf = (m) => {
|
|
2895
|
+
switch (m.view) {
|
|
2896
|
+
case "all": return undefined;
|
|
2897
|
+
case "other-families": return m.family;
|
|
2898
|
+
}
|
|
2899
|
+
};
|
|
2639
2900
|
const laneOf = (scope) => {
|
|
2640
2901
|
const inScope = (m) => {
|
|
2641
2902
|
switch (scope) {
|
|
@@ -2643,7 +2904,16 @@ const armPool = (() => {
|
|
|
2643
2904
|
case "bounded-unit": return !m.lexical;
|
|
2644
2905
|
}
|
|
2645
2906
|
};
|
|
2646
|
-
|
|
2907
|
+
const lanePhase2 = phase2.filter(inScope);
|
|
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
|
+
};
|
|
2647
2917
|
};
|
|
2648
2918
|
const LANES = {
|
|
2649
2919
|
"free-text": laneOf("free-text"),
|
|
@@ -2694,18 +2964,26 @@ const armPool = (() => {
|
|
|
2694
2964
|
m.collect(text, json, eat, spans, isHead);
|
|
2695
2965
|
closeCoveragePositions(0);
|
|
2696
2966
|
if (lane.phase2.length > 0) {
|
|
2697
|
-
const
|
|
2967
|
+
const tokens = credentialSpans(text);
|
|
2968
|
+
const scan = buildForensicScan(tokens, lane.phase2.length);
|
|
2698
2969
|
const reuse = forensicRescanMode === "incremental";
|
|
2699
2970
|
const foot = scan.armFoot;
|
|
2971
|
+
const views = lane.viewKeys.map((key) => ({ key, table: tokens, dirty: undefined }));
|
|
2700
2972
|
let folded = 0;
|
|
2701
2973
|
for (let level = 0;; level += 1) {
|
|
2702
2974
|
const before = spans.length;
|
|
2703
2975
|
for (let j = 0; j < lane.phase2.length; j++) {
|
|
2704
|
-
const
|
|
2705
|
-
|
|
2976
|
+
const view = views[lane.viewOf[j]];
|
|
2977
|
+
const oi = lane.othersOf[j];
|
|
2978
|
+
const others = oi < 0 ? undefined : views[oi];
|
|
2979
|
+
const dirty = view.dirty;
|
|
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)))) {
|
|
2706
2983
|
forensicSkips += 1;
|
|
2707
2984
|
continue;
|
|
2708
2985
|
}
|
|
2986
|
+
useEvidenceView(scan, view.table, dirty, others?.table);
|
|
2709
2987
|
scan.footLo = FOOT_NONE;
|
|
2710
2988
|
scan.footHi = -1;
|
|
2711
2989
|
lane.phase2[j].collect(scan, text, json, level === 0 ? eat : undefined, spans, isHead);
|
|
@@ -2715,11 +2993,18 @@ const armPool = (() => {
|
|
|
2715
2993
|
}
|
|
2716
2994
|
closeCoveragePositions(before);
|
|
2717
2995
|
forensicLevels += 1;
|
|
2718
|
-
|
|
2996
|
+
let grew = false;
|
|
2997
|
+
for (const view of views) {
|
|
2998
|
+
const key = view.key;
|
|
2999
|
+
const grown = foldCredentialSpans(view.table, spans, folded, key === undefined ? undefined : (sp) => familyOfOrder[sp.order] !== key);
|
|
3000
|
+
if (grown.dirty.starts.length > 0)
|
|
3001
|
+
grew = true;
|
|
3002
|
+
view.table = grown.next;
|
|
3003
|
+
view.dirty = reuse ? grown.dirty : undefined;
|
|
3004
|
+
}
|
|
2719
3005
|
folded = spans.length;
|
|
2720
|
-
if (
|
|
3006
|
+
if (!grew)
|
|
2721
3007
|
break;
|
|
2722
|
-
advanceEvidence(scan, grown.next, reuse ? grown.dirty : undefined);
|
|
2723
3008
|
}
|
|
2724
3009
|
}
|
|
2725
3010
|
poolSpanCount += spans.length;
|