@sema-agent/server 7.135.0 → 7.137.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 +237 -2
- package/USAGE.md +146 -41
- package/deploy/sema-up/README.md +15 -1
- package/deploy/sema-up/chart/templates/NOTES.txt +1 -1
- package/deploy/sema-up/chart/templates/sandbox-rbac.yaml +7 -1
- package/deploy/sema-up/chart/values.yaml +1 -1
- package/deploy/sema-up/stack/sema-stack-cluster.yaml +7 -1
- package/dist/approval-card.d.ts +60 -4
- package/dist/approval-card.js +18 -1
- package/dist/approval-content-kind.d.ts +7 -0
- package/dist/approval-content-kind.js +8 -1
- package/dist/approval-gate-currency.d.ts +48 -6
- package/dist/approval-gate-currency.js +16 -9
- package/dist/approval-hmac.d.ts +13 -1
- package/dist/approval-hmac.js +1 -1
- package/dist/approval.js +3 -3
- package/dist/auth-bridge.d.ts +34 -5
- package/dist/auth-bridge.js +22 -5
- package/dist/boot/budget-tracing.d.ts +1 -2
- package/dist/boot/budget-tracing.js +2 -2
- package/dist/boot/execution-env.d.ts +24 -1
- package/dist/boot/execution-env.js +69 -6
- package/dist/boot/org-memory.js +1 -1
- package/dist/boot/reapers.d.ts +3 -0
- package/dist/boot/reapers.js +15 -1
- package/dist/boot/resolve-spec.d.ts +4 -0
- package/dist/boot/resolve-spec.js +24 -22
- package/dist/boot/runner-deps.js +4 -2
- package/dist/boot/stage-03-store-backend.d.ts +1 -1
- package/dist/boot/stage-04-stores.d.ts +2 -2
- package/dist/boot/stage-05-execution-env.d.ts +4 -2
- package/dist/boot/stage-05-execution-env.js +11 -2
- package/dist/boot/stage-06-runners.d.ts +4 -2
- package/dist/boot/stage-06-runners.js +5 -1
- package/dist/boot/stage-07-capability-layer.d.ts +5 -2
- package/dist/boot/stage-07-capability-layer.js +1 -1
- package/dist/boot/stage-08-reapers.d.ts +5 -2
- package/dist/boot/stage-08-reapers.js +2 -1
- package/dist/boot/stage-09-leader.d.ts +5 -2
- package/dist/boot/stage-10-http-server.d.ts +5 -2
- package/dist/boot/stage-10-http-server.js +3 -2
- package/dist/boot/stores.d.ts +1 -1
- package/dist/boot/stores.js +2 -3
- package/dist/boot/web-admin.js +10 -17
- package/dist/brain.js +12 -3
- package/dist/broker/broker.d.ts +89 -0
- package/dist/broker/broker.js +287 -0
- package/dist/broker/constants.d.ts +39 -0
- package/dist/broker/constants.js +15 -0
- package/dist/broker/http1.d.ts +111 -0
- package/dist/broker/http1.js +256 -0
- package/dist/broker/lease-book.d.ts +87 -0
- package/dist/broker/lease-book.js +142 -0
- package/dist/broker/lease-file.d.ts +60 -0
- package/dist/broker/lease-file.js +127 -0
- package/dist/broker/main.d.ts +2 -0
- package/dist/broker/main.js +20 -0
- package/dist/budget.d.ts +3 -3
- package/dist/budget.js +4 -0
- package/dist/capabilities/composite-members.d.ts +2 -1
- package/dist/capabilities/composite-members.js +2 -1
- package/dist/capabilities/engine-capabilities.js +1 -0
- package/dist/capabilities/mcp-probe.d.ts +5 -4
- package/dist/capabilities/scenarios.d.ts +23 -8
- package/dist/capabilities/scenarios.js +13 -8
- package/dist/capabilities/skills.d.ts +28 -5
- package/dist/capabilities/skills.js +8 -0
- package/dist/capability-facts.d.ts +3 -7
- package/dist/capability-facts.js +3 -5
- package/dist/center-credential-renewer.js +5 -1
- package/dist/center-credential.d.ts +18 -4
- package/dist/center-credential.js +18 -3
- package/dist/checkpoint-ctx-seal-m28.d.ts +3 -10
- package/dist/checkpoint-ctx-seal-m28.js +1 -1
- package/dist/checkpoint-identity.d.ts +3 -3
- package/dist/config-catalog.js +21 -16
- package/dist/config-d-migration.js +1 -1
- package/dist/config-invariants.d.ts +1 -1
- package/dist/config-invariants.js +8 -0
- package/dist/config-lkg.d.ts +0 -38
- package/dist/config-lkg.js +1 -83
- package/dist/config-types.d.ts +21 -4
- package/dist/config.d.ts +1 -1
- package/dist/config.js +60 -16
- package/dist/deployment-governance.d.ts +2 -2
- package/dist/deployment-governance.js +7 -9
- package/dist/deployment-shape.d.ts +1 -1
- package/dist/device-ws-protocol.d.ts +1 -0
- package/dist/device-ws-protocol.js +2 -1
- package/dist/elicitation.d.ts +8 -7
- package/dist/elicitation.js +9 -8
- package/dist/file-history-disclosure.d.ts +8 -4
- package/dist/file-history-disclosure.js +24 -6
- package/dist/fleet-client.d.ts +32 -1
- package/dist/fleet-client.js +24 -4
- package/dist/fleet-lease.d.ts +7 -3
- package/dist/fleet-lease.js +31 -23
- package/dist/hooks/classic-stations.d.ts +11 -54
- package/dist/hooks/hook-llm.js +14 -8
- package/dist/hooks/hook-runner.d.ts +4 -4
- package/dist/hooks/hook-runner.js +2 -2
- package/dist/http/admission.js +10 -7
- package/dist/http/approval-sweeps.js +4 -3
- package/dist/http/caller-trust.d.ts +2 -1
- package/dist/http/caller-trust.js +1 -3
- package/dist/http/checkpoint-ctx.d.ts +76 -16
- package/dist/http/checkpoint-ctx.js +45 -1
- package/dist/http/dispatch.js +24 -19
- package/dist/http/durable-run-leg.d.ts +5 -1
- package/dist/http/durable-run-leg.js +1 -1
- package/dist/http/health-self-report.d.ts +2 -2
- package/dist/http/mcp-remote-text.d.ts +5 -0
- package/dist/http/mcp-remote-text.js +7 -0
- package/dist/http/principal-gate.d.ts +15 -10
- package/dist/http/principal-gate.js +48 -9
- package/dist/http/resume-ctx-read.d.ts +2 -16
- package/dist/http/resume-ctx-read.js +2 -3
- package/dist/http/resume-legs.d.ts +2 -20
- package/dist/http/resume-legs.js +52 -38
- package/dist/http/route-ctx.d.ts +8 -2
- package/dist/http/routes/approvals-assistant.d.ts +0 -2
- package/dist/http/routes/approvals-assistant.js +65 -16
- package/dist/http/routes/capabilities.js +11 -1
- package/dist/http/routes/mcp-reconnect.d.ts +1 -1
- package/dist/http/routes/mcp-reconnect.js +5 -7
- package/dist/http/routes/runs.js +4 -3
- package/dist/http/routes/session-sync.js +70 -12
- package/dist/http/routes/sessions.js +20 -15
- package/dist/http/routes/tasks.js +8 -6
- package/dist/http/server.d.ts +4 -0
- package/dist/http/stored-body-replay.d.ts +3 -3
- package/dist/http/stored-body-replay.js +6 -1
- package/dist/http/submit-ctx.js +1 -1
- package/dist/http/wire-types.d.ts +57 -30
- package/dist/identity-source.d.ts +65 -0
- package/dist/identity-source.js +45 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/memory-store-refusal.d.ts +3 -3
- package/dist/model-entry-refusal.d.ts +2 -0
- package/dist/model-entry-refusal.js +11 -0
- package/dist/model-route-gap.d.ts +49 -4
- package/dist/model-route-gap.js +34 -1
- package/dist/model-select.d.ts +6 -17
- package/dist/model-select.js +0 -3
- package/dist/namespaced-rule-name.d.ts +1 -1
- package/dist/object-store-buckets.d.ts +13 -0
- package/dist/object-store-buckets.js +3 -0
- package/dist/observability/fail-open.d.ts +19 -7
- package/dist/observability/fail-open.js +19 -7
- package/dist/observability/metrics.js +2 -2
- package/dist/observability/principal-context.d.ts +4 -1
- package/dist/observability/principal-context.js +6 -1
- package/dist/observability/run-terminal.js +1 -0
- package/dist/operator-ask.d.ts +2 -2
- package/dist/org-delivery-gate.js +1 -1
- package/dist/org-memory-admission.d.ts +2 -2
- package/dist/org-memory-admission.js +39 -2
- package/dist/parked-decide.d.ts +4 -3
- package/dist/parked-decide.js +29 -9
- package/dist/peer-directory.d.ts +2 -1
- package/dist/peer-directory.js +7 -5
- package/dist/permission-rule-list.d.ts +2 -2
- package/dist/plugins/approval-ask-store-sql.d.ts +8 -0
- package/dist/plugins/approval-ask-store-sql.js +3 -0
- package/dist/plugins/blob-backend.js +2 -1
- package/dist/plugins/checkpoint-index-columns.d.ts +50 -1
- package/dist/plugins/checkpoint-index-columns.js +28 -0
- package/dist/plugins/checkpoint-store-sql.d.ts +46 -12
- package/dist/plugins/checkpoint-store-sql.js +64 -35
- package/dist/plugins/file-history-store-sql.d.ts +6 -0
- package/dist/plugins/file-history-store-sql.js +37 -37
- package/dist/plugins/file-run-store.js +8 -2
- package/dist/plugins/k8s-exec-protocol.d.ts +69 -13
- package/dist/plugins/k8s-exec-protocol.js +116 -28
- package/dist/plugins/k8s-pod-delete-failure.d.ts +25 -0
- package/dist/plugins/k8s-pod-delete-failure.js +17 -0
- package/dist/plugins/k8s-pod-reaper.d.ts +112 -0
- package/dist/plugins/k8s-pod-reaper.js +122 -0
- package/dist/plugins/local-checkpoint-store.d.ts +3 -6
- package/dist/plugins/local-checkpoint-store.js +7 -6
- package/dist/plugins/memory-run-store.js +2 -0
- package/dist/plugins/one-time-migrations.d.ts +49 -37
- package/dist/plugins/one-time-migrations.js +87 -128
- package/dist/plugins/permission-rule-store-file.js +2 -2
- package/dist/plugins/permission-rule-store-sql.d.ts +17 -17
- package/dist/plugins/permission-rule-store-sql.js +16 -17
- package/dist/plugins/pg-pool.js +4 -0
- package/dist/plugins/pg-session-storage.js +2 -2
- package/dist/plugins/remote-env-adb.d.ts +3 -0
- package/dist/plugins/remote-env-adb.js +1 -0
- package/dist/plugins/remote-env-device.d.ts +3 -0
- package/dist/plugins/remote-env-device.js +4 -0
- package/dist/plugins/remote-env-e2b.d.ts +3 -0
- package/dist/plugins/remote-env-e2b.js +1 -0
- package/dist/plugins/remote-env-file-error.d.ts +8 -1
- package/dist/plugins/remote-env-file-error.js +7 -1
- package/dist/plugins/remote-env-host.d.ts +7 -1
- package/dist/plugins/remote-env-host.js +7 -1
- package/dist/plugins/remote-env-k8s.d.ts +154 -14
- package/dist/plugins/remote-env-k8s.js +300 -21
- package/dist/plugins/remote-env-local-docker.d.ts +3 -0
- package/dist/plugins/remote-env-local-docker.js +1 -0
- package/dist/plugins/remote-env-ssh.d.ts +3 -0
- package/dist/plugins/remote-env-ssh.js +1 -0
- package/dist/plugins/run-store-sql.js +10 -4
- package/dist/plugins/s3-answer.d.ts +15 -0
- package/dist/plugins/s3-answer.js +5 -0
- package/dist/plugins/session-ownership.d.ts +17 -3
- package/dist/plugins/session-ownership.js +19 -5
- package/dist/plugins/snapshot-bucket-presence.d.ts +39 -4
- package/dist/plugins/snapshot-bucket-presence.js +35 -7
- package/dist/plugins/sql-errors.d.ts +60 -0
- package/dist/plugins/sql-errors.js +64 -1
- package/dist/plugins/store-contracts.d.ts +10 -1
- package/dist/plugins/store-fault.d.ts +4 -0
- package/dist/plugins/store-fault.js +10 -0
- package/dist/plugins/tidb-pool.js +8 -1
- package/dist/plugins/tidb-session-store.js +2 -2
- package/dist/plugins/web-search.d.ts +1 -1
- package/dist/plugins/web-search.js +2 -2
- package/dist/request-declaration-refusal.d.ts +94 -0
- package/dist/request-declaration-refusal.js +78 -0
- package/dist/request-image-urls.d.ts +6 -0
- package/dist/request-image-urls.js +27 -0
- package/dist/request-key-closure.d.ts +5 -0
- package/dist/request-key-closure.js +5 -2
- package/dist/request-value-closure.d.ts +31 -2
- package/dist/request-value-closure.js +7 -1
- package/dist/rule-entry-list.d.ts +3 -2
- package/dist/rule-entry-list.js +2 -2
- package/dist/rules-consent.js +7 -4
- package/dist/run-local.d.ts +2 -2
- package/dist/runs.js +2 -1
- package/dist/runtime-caps-resolver.js +1 -1
- package/dist/runtime-governance.d.ts +5 -5
- package/dist/sandbox-git-lease.d.ts +49 -0
- package/dist/sandbox-git-lease.js +60 -0
- package/dist/sandbox-git-plane.d.ts +84 -0
- package/dist/sandbox-git-plane.js +105 -0
- package/dist/sandbox-pkg-source.d.ts +28 -1
- package/dist/sandbox-pkg-source.js +1 -2
- package/dist/sealed-key.d.ts +19 -0
- package/dist/sealed-key.js +47 -4
- package/dist/security.d.ts +16 -4
- package/dist/security.js +8 -3
- package/dist/server-secret-env.d.ts +6 -0
- package/dist/server-secret-env.js +14 -0
- package/dist/session-error-wire.d.ts +14 -6
- package/dist/session-error-wire.js +2 -1
- package/dist/session-incarnation.d.ts +16 -5
- package/dist/session-incarnation.js +7 -4
- package/dist/session-policy-wire.d.ts +1 -1
- package/dist/session-policy-wire.js +2 -2
- package/dist/session-row.d.ts +3 -17
- package/dist/session-row.js +4 -0
- package/dist/session-sync.d.ts +15 -3
- package/dist/session-sync.js +5 -2
- package/dist/spec-fields.d.ts +43 -13
- package/dist/spec-fields.js +33 -12
- package/dist/stored-request-prompt-profile-m30.d.ts +49 -0
- package/dist/stored-request-prompt-profile-m30.js +25 -0
- package/dist/task-a2a.d.ts +52 -81
- package/dist/task-a2a.js +219 -89
- package/dist/task-mcp.d.ts +7 -15
- package/dist/task-mcp.js +16 -51
- package/dist/task-settings.d.ts +2 -2
- package/dist/tool-approval.d.ts +35 -12
- package/dist/tool-approval.js +59 -39
- package/dist/tool-rule-names.d.ts +5 -0
- package/dist/trace/core-keyset-guard.d.ts +5 -5
- package/dist/trace/engine-notice-wire.d.ts +1 -1
- package/dist/trace/engine-notice-wire.js +2 -0
- package/dist/trace/exit-seat.js +37 -13
- package/dist/trace/ledger-sink.d.ts +4 -5
- package/dist/trace/ledger-sink.js +3 -7
- package/dist/trace/project.d.ts +37 -3
- package/dist/trace/project.js +20 -6
- package/dist/trace/redact.d.ts +40 -12
- package/dist/trace/redact.js +332 -128
- package/dist/trace/self-minted-wire.d.ts +641 -0
- package/dist/trace/self-minted-wire.js +201 -0
- package/dist/trace/wire-projection-faces.d.ts +5 -3
- package/dist/trace/wire-projection-faces.js +7 -3
- package/dist/url-credential-refusal.d.ts +52 -0
- package/dist/url-credential-refusal.js +14 -0
- package/dist/url-refusal.d.ts +13 -4
- package/dist/url-refusal.js +8 -2
- package/dist/web-admin-client/apply-effective.js +0 -1
- package/dist/web-admin-client/center-request.d.ts +20 -2
- package/dist/web-admin-client/center-request.js +40 -5
- package/dist/web-admin-client/http-client.js +6 -6
- package/dist/web-admin-client/types.d.ts +5 -5
- package/dist/workspace-space.d.ts +27 -0
- package/dist/workspace-space.js +26 -0
- package/docs/DEVICE-EXECUTOR-PROTOCOL.md +11 -5
- package/package.json +4 -3
- package/scripts/drain-check-local.mjs +2 -1
- package/scripts/drain-check.mysql.sql +3 -1
- package/scripts/drain-check.pg.sql +3 -1
- package/skills/schedule.md +1 -1
- package/wire-changes/server@7.104.0.json +134 -0
- package/wire-changes/server@7.105.0.json +53 -0
- package/wire-changes/server@7.106.0.json +475 -0
- package/wire-changes/server@7.107.0.json +64 -0
- package/wire-changes/server@7.108.0.json +153 -0
- package/wire-changes/server@7.109.0.json +46 -0
- package/wire-changes/server@7.110.0.json +90 -0
- package/wire-changes/server@7.111.0.json +86 -0
- package/wire-changes/server@7.112.0.json +60 -0
- package/wire-changes/server@7.113.0.json +26 -0
- package/wire-changes/server@7.114.0.json +26 -0
- package/wire-changes/server@7.115.0.json +97 -0
- package/wire-changes/server@7.116.0.json +218 -0
- package/wire-changes/server@7.116.1.json +26 -0
- package/wire-changes/server@7.117.0.json +65 -0
- package/wire-changes/server@7.118.0.json +129 -0
- package/wire-changes/server@7.119.0.json +65 -0
- package/wire-changes/server@7.120.0.json +89 -0
- package/wire-changes/server@7.121.0.json +219 -0
- package/wire-changes/server@7.122.0.json +54 -0
- package/wire-changes/server@7.123.0.json +26 -0
- package/wire-changes/server@7.124.0.json +336 -0
- package/wire-changes/server@7.124.1.json +26 -0
- package/wire-changes/server@7.125.0.json +111 -0
- package/wire-changes/server@7.126.0.json +285 -0
- package/wire-changes/server@7.127.0.json +77 -0
- package/wire-changes/server@7.127.1.json +26 -0
- package/wire-changes/server@7.127.2.json +53 -0
- package/wire-changes/server@7.128.0.json +26 -0
- package/wire-changes/server@7.129.0.json +106 -0
- package/wire-changes/server@7.129.1.json +26 -0
- package/wire-changes/server@7.130.0.json +26 -0
- package/wire-changes/server@7.131.0.json +118 -0
- package/wire-changes/server@7.132.0.json +226 -0
- package/wire-changes/server@7.133.0.json +1155 -0
- package/wire-changes/server@7.134.0.json +107 -0
- package/wire-changes/server@7.135.0.json +174 -0
- package/wire-changes/server@7.136.0.json +448 -0
- package/wire-changes/server@7.137.0.json +825 -0
- package/dist/url-credential.d.ts +0 -52
- package/dist/url-credential.js +0 -49
package/dist/trace/redact.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { displayUrlOf, stripFormatCharacters } from "@sema-agent/core";
|
|
2
|
-
import { CONTROL_AND_BIDI_CHARS, isHierarchicalUrl } from "@sema-agent/settings-schema";
|
|
2
|
+
import { CONTROL_AND_BIDI_CHARS, CREDENTIAL_PARAM_NAMES, isHierarchicalUrl } from "@sema-agent/settings-schema";
|
|
3
3
|
import { recordFailOpen } from "../observability/fail-open.js";
|
|
4
4
|
const normalizeForRedaction = stripFormatCharacters;
|
|
5
5
|
export function mapNormalizedToOriginal(text, normalized) {
|
|
@@ -819,13 +819,57 @@ if (/[\]^-]/.test(SCHEME_DELIMITER_CHARS))
|
|
|
819
819
|
const SCHEME_DELIMITER_CODES = new Set([...SCHEME_DELIMITER_CHARS].map((c) => c.charCodeAt(0)));
|
|
820
820
|
const isSchemeDelimiterCode = (c) => SCHEME_DELIMITER_CODES.has(c);
|
|
821
821
|
const SCHEME_SEPARATOR_CLASS = `[\\s${SCHEME_DELIMITER_CHARS.replace(/\\/g, "\\\\")}]`;
|
|
822
|
-
|
|
822
|
+
function expandKeywordForms(src) {
|
|
823
|
+
let forms = [""];
|
|
824
|
+
for (let i = 0; i < src.length;) {
|
|
825
|
+
let alts;
|
|
826
|
+
const pair = /^\[([A-Za-z])([A-Za-z])\]/.exec(src.slice(i));
|
|
827
|
+
if (pair !== null) {
|
|
828
|
+
if (pair[1].toLowerCase() !== pair[2].toLowerCase())
|
|
829
|
+
throw new Error(`redact: keyword source ${src} holds a class that is not a case pair`);
|
|
830
|
+
alts = [pair[1], pair[2]];
|
|
831
|
+
i += 4;
|
|
832
|
+
}
|
|
833
|
+
else if (/[A-Za-z0-9]/.test(src.charAt(i))) {
|
|
834
|
+
alts = [src.charAt(i)];
|
|
835
|
+
i += 1;
|
|
836
|
+
}
|
|
837
|
+
else {
|
|
838
|
+
throw new Error(`redact: keyword source ${src} holds an atom the head reading cannot enumerate (only letters, digits and [Xx] case pairs)`);
|
|
839
|
+
}
|
|
840
|
+
forms = forms.flatMap((f) => alts.map((a) => f + a));
|
|
841
|
+
}
|
|
842
|
+
const whole = new RegExp(`^(?:${src})$`);
|
|
843
|
+
for (const f of forms)
|
|
844
|
+
if (!whole.test(f))
|
|
845
|
+
throw new Error(`redact: keyword source ${src} does not match its own enumerated form ${f}`);
|
|
846
|
+
return forms;
|
|
847
|
+
}
|
|
848
|
+
const LEXICAL_KEYWORD_ARMS = new Map();
|
|
823
849
|
const keywordArm = (keywordSrc, valueClass, min, tailSrc = "", separatorClass = "\\s") => {
|
|
824
850
|
const re = new RegExp(`(?:${keywordSrc}${separatorClass}+${atLeast(valueClass, min)}${tailSrc}` +
|
|
825
851
|
`|(?<!${WORD_SEGMENT_CLASS})${keywordSrc}(?:${twoStrongAhead(valueClass)}|${noConnectorAhead(valueClass)})${atLeast(valueClass, min)}${tailSrc})`, "g");
|
|
826
|
-
|
|
852
|
+
const separator = new RegExp(`^${separatorClass}$`);
|
|
853
|
+
const value = new RegExp(`^${valueClass}$`);
|
|
854
|
+
LEXICAL_KEYWORD_ARMS.set(re, { forms: expandKeywordForms(keywordSrc), min, isSeparator: (u) => separator.test(u), isValue: (u) => value.test(u) });
|
|
827
855
|
return re;
|
|
828
856
|
};
|
|
857
|
+
function undecidedKeywordStart(text, t) {
|
|
858
|
+
const n = text.length;
|
|
859
|
+
let valueRun = 0;
|
|
860
|
+
while (valueRun < t.min && valueRun < n && t.isValue(text.charAt(n - 1 - valueRun)))
|
|
861
|
+
valueRun += 1;
|
|
862
|
+
if (valueRun >= t.min)
|
|
863
|
+
return -1;
|
|
864
|
+
let sepStart = n - valueRun;
|
|
865
|
+
while (sepStart > 0 && t.isSeparator(text.charAt(sepStart - 1)))
|
|
866
|
+
sepStart -= 1;
|
|
867
|
+
for (const f of t.forms) {
|
|
868
|
+
if (sepStart >= f.length && text.startsWith(f, sepStart - f.length))
|
|
869
|
+
return sepStart - f.length;
|
|
870
|
+
}
|
|
871
|
+
return -1;
|
|
872
|
+
}
|
|
829
873
|
const CREDENTIAL_NAME_ANCHORS = {
|
|
830
874
|
api_key: "structure-only", apikey: "structure-only", "api-key": "structure-only", key: "structure-only",
|
|
831
875
|
token: "structure-only", access_token: "structure-only", refresh_token: "structure-only", id_token: "structure-only",
|
|
@@ -836,15 +880,14 @@ const CREDENTIAL_NAME_ANCHORS = {
|
|
|
836
880
|
"x-amz-signature": "structure-only", "x-amz-security-token": "structure-only", "x-amz-credential": "structure-only",
|
|
837
881
|
sas: "structure-only",
|
|
838
882
|
};
|
|
839
|
-
const CREDENTIAL_NAME_WORDS = Object.keys(CREDENTIAL_NAME_ANCHORS);
|
|
840
883
|
const CREDENTIAL_NAME_ALT = (() => {
|
|
841
|
-
for (const w of
|
|
884
|
+
for (const w of CREDENTIAL_PARAM_NAMES) {
|
|
842
885
|
if (!/^[a-z][a-z0-9_-]*$/.test(w))
|
|
843
886
|
throw new Error(`redact: credential name ${JSON.stringify(w)} must be lowercase [a-z0-9_-] (the alternation is built without escaping)`);
|
|
844
887
|
}
|
|
845
|
-
return [...
|
|
888
|
+
return [...CREDENTIAL_PARAM_NAMES].sort((a, b) => b.length - a.length || (a < b ? -1 : 1)).join("|");
|
|
846
889
|
})();
|
|
847
|
-
const CREDENTIAL_NAME_MAX = Math.max(...
|
|
890
|
+
const CREDENTIAL_NAME_MAX = Math.max(...CREDENTIAL_PARAM_NAMES.map((w) => w.length));
|
|
848
891
|
const asciiLower = (s) => s.replace(/[A-Z]/g, (c) => c.toLowerCase());
|
|
849
892
|
function credentialNameAnchor(name) {
|
|
850
893
|
return CREDENTIAL_ANCHOR_OF.get(asciiLower(name));
|
|
@@ -1085,14 +1128,17 @@ function isPemFenceLabel(s, from, to) {
|
|
|
1085
1128
|
return false;
|
|
1086
1129
|
if (!s.startsWith(PEM_KEY_WORD, end - PEM_KEY_WORD.length))
|
|
1087
1130
|
return false;
|
|
1131
|
+
return !pemLabelDashRuleBroken(s, from, to);
|
|
1132
|
+
}
|
|
1133
|
+
function pemLabelDashRuleBroken(s, from, to) {
|
|
1088
1134
|
if (s.charCodeAt(from) === 45)
|
|
1089
|
-
return
|
|
1135
|
+
return true;
|
|
1090
1136
|
if (s.charCodeAt(from + 1) === 45)
|
|
1091
|
-
return
|
|
1137
|
+
return true;
|
|
1092
1138
|
for (let i = from + 1; i < to; i++)
|
|
1093
1139
|
if (s.charCodeAt(i) === 45 && s.charCodeAt(i + 1) === 45)
|
|
1094
|
-
return
|
|
1095
|
-
return
|
|
1140
|
+
return true;
|
|
1141
|
+
return false;
|
|
1096
1142
|
}
|
|
1097
1143
|
const PEM_FENCE_OPEN = "-----BEGIN";
|
|
1098
1144
|
const PEM_FENCE_CLOSE = "-----";
|
|
@@ -1137,7 +1183,7 @@ function unitCountWithin(scan, from, to, max) {
|
|
|
1137
1183
|
}
|
|
1138
1184
|
return n <= max;
|
|
1139
1185
|
}
|
|
1140
|
-
function collectSsh2FenceSpans(scan, text, span) {
|
|
1186
|
+
function collectSsh2FenceSpans(scan, text, span, isHead) {
|
|
1141
1187
|
let cachedFrom = 0;
|
|
1142
1188
|
let cachedAt;
|
|
1143
1189
|
const nextClose = (from) => {
|
|
@@ -1163,6 +1209,10 @@ function collectSsh2FenceSpans(scan, text, span) {
|
|
|
1163
1209
|
continue;
|
|
1164
1210
|
}
|
|
1165
1211
|
const bodyFrom = fence + SSH2_FENCE_OPEN.length;
|
|
1212
|
+
if (isHead && unitCountWithin(scan, bodyFrom, text.length, SSH2_BODY_MAX)) {
|
|
1213
|
+
span(fence, text.length, true);
|
|
1214
|
+
return;
|
|
1215
|
+
}
|
|
1166
1216
|
const close = nextClose(bodyFrom);
|
|
1167
1217
|
if (close < 0)
|
|
1168
1218
|
return;
|
|
@@ -1177,11 +1227,20 @@ function collectSsh2FenceSpans(scan, text, span) {
|
|
|
1177
1227
|
}
|
|
1178
1228
|
const PEM_END_OPEN = "-----END";
|
|
1179
1229
|
const PEM_FENCE_BODY_MAX = 8192;
|
|
1230
|
+
const PEM_LINE_INVALID = -1;
|
|
1231
|
+
const PEM_LINE_OPEN = -2;
|
|
1180
1232
|
function pemFenceLineEnd(text, labelFrom) {
|
|
1181
1233
|
const close = text.indexOf(PEM_FENCE_CLOSE, labelFrom);
|
|
1182
|
-
|
|
1234
|
+
if (close < 0) {
|
|
1235
|
+
let to = text.length;
|
|
1236
|
+
while (to > labelFrom && text.charCodeAt(to - 1) === 45)
|
|
1237
|
+
to -= 1;
|
|
1238
|
+
const stillPossible = text.length - to >= 2 ? isPemFenceLabel(text, labelFrom, to) : !pemLabelDashRuleBroken(text, labelFrom, to);
|
|
1239
|
+
return stillPossible ? PEM_LINE_OPEN : PEM_LINE_INVALID;
|
|
1240
|
+
}
|
|
1241
|
+
return isPemFenceLabel(text, labelFrom, close) ? close + PEM_FENCE_CLOSE.length : PEM_LINE_INVALID;
|
|
1183
1242
|
}
|
|
1184
|
-
function collectPemFenceSpans(text, span) {
|
|
1243
|
+
function collectPemFenceSpans(text, span, isHead) {
|
|
1185
1244
|
let endFrom = -1;
|
|
1186
1245
|
let endAt = -1;
|
|
1187
1246
|
let endAfter = -1;
|
|
@@ -1201,13 +1260,17 @@ function collectPemFenceSpans(text, span) {
|
|
|
1201
1260
|
if (fence < 0)
|
|
1202
1261
|
return;
|
|
1203
1262
|
const bodyFrom = pemFenceLineEnd(text, fence + PEM_FENCE_OPEN.length);
|
|
1204
|
-
if (bodyFrom
|
|
1263
|
+
if (bodyFrom === PEM_LINE_INVALID) {
|
|
1205
1264
|
from = fence + 1;
|
|
1206
1265
|
continue;
|
|
1207
1266
|
}
|
|
1208
|
-
|
|
1209
|
-
|
|
1267
|
+
if (bodyFrom !== PEM_LINE_OPEN)
|
|
1268
|
+
earliestEndFence(bodyFrom);
|
|
1269
|
+
if (bodyFrom === PEM_LINE_OPEN || endAt < 0) {
|
|
1270
|
+
if (isHead)
|
|
1271
|
+
span(fence, text.length, true);
|
|
1210
1272
|
return;
|
|
1273
|
+
}
|
|
1211
1274
|
if (endAt - bodyFrom > PEM_FENCE_BODY_MAX) {
|
|
1212
1275
|
from = fence + 1;
|
|
1213
1276
|
continue;
|
|
@@ -1248,9 +1311,19 @@ function collectPemFragmentSpans(scan, text, json, span) {
|
|
|
1248
1311
|
cur = next;
|
|
1249
1312
|
}
|
|
1250
1313
|
}
|
|
1251
|
-
function collectHeadlessPpkSpans(scan, text, span) {
|
|
1314
|
+
function collectHeadlessPpkSpans(scan, text, span, isHead) {
|
|
1252
1315
|
const KEY = "Private-Lines: ";
|
|
1253
1316
|
const n = text.length;
|
|
1317
|
+
const LINE_NONE = -1;
|
|
1318
|
+
const LINE_OPEN = -2;
|
|
1319
|
+
const openAt = (i) => {
|
|
1320
|
+
if (!isHead)
|
|
1321
|
+
return false;
|
|
1322
|
+
let k = i;
|
|
1323
|
+
while (k < n && opaqueUnitEnd(scan, k) < 0 && text.charCodeAt(k) === 92)
|
|
1324
|
+
k += 1;
|
|
1325
|
+
return k >= n;
|
|
1326
|
+
};
|
|
1254
1327
|
const isB64 = (c) => (c >= 48 && c <= 57) || (c >= 65 && c <= 90) || (c >= 97 && c <= 122) || c === 43 || c === 47 || c === 61;
|
|
1255
1328
|
const b64At = (i) => opaqueUnitEnd(scan, i) < 0 && isB64(text.charCodeAt(i));
|
|
1256
1329
|
const skipHs = (i) => { while (i < n && opaqueUnitEnd(scan, i) < 0 && (text.charCodeAt(i) === 32 || text.charCodeAt(i) === 9))
|
|
@@ -1289,7 +1362,9 @@ function collectHeadlessPpkSpans(scan, text, span) {
|
|
|
1289
1362
|
seen = true;
|
|
1290
1363
|
q = skipHs(q);
|
|
1291
1364
|
}
|
|
1292
|
-
|
|
1365
|
+
if (openAt(q))
|
|
1366
|
+
return LINE_OPEN;
|
|
1367
|
+
return seen ? q : LINE_NONE;
|
|
1293
1368
|
};
|
|
1294
1369
|
const escapedNewlineLen = (i) => {
|
|
1295
1370
|
if (opaqueUnitEnd(scan, i) >= 0)
|
|
@@ -1304,13 +1379,35 @@ function collectHeadlessPpkSpans(scan, text, span) {
|
|
|
1304
1379
|
};
|
|
1305
1380
|
const atLineEnd = (i) => {
|
|
1306
1381
|
const b = skipHs(i);
|
|
1307
|
-
if (b >= n)
|
|
1382
|
+
if (b >= n || openAt(b))
|
|
1308
1383
|
return true;
|
|
1309
1384
|
if (opaqueUnitEnd(scan, b) >= 0)
|
|
1310
1385
|
return false;
|
|
1311
1386
|
const c = text.charCodeAt(b);
|
|
1312
1387
|
return c === 10 || c === 13 || c === 34 || escapedNewlineLen(b) > 0;
|
|
1313
1388
|
};
|
|
1389
|
+
const bodyLineAt = (from) => {
|
|
1390
|
+
const q0 = skipNewlines(from);
|
|
1391
|
+
if (q0 < 0)
|
|
1392
|
+
return q0;
|
|
1393
|
+
let q = q0;
|
|
1394
|
+
const mk = opaqueUnitEnd(scan, q) < 0 ? text.charCodeAt(q) : -1;
|
|
1395
|
+
if (mk === 43 || mk === 45)
|
|
1396
|
+
q = skipHs(q + 1);
|
|
1397
|
+
let r = q;
|
|
1398
|
+
while (r < n && b64At(r))
|
|
1399
|
+
r += 1;
|
|
1400
|
+
if (r - q < 4) {
|
|
1401
|
+
if (isHead && r >= n)
|
|
1402
|
+
return LINE_OPEN;
|
|
1403
|
+
r = q0;
|
|
1404
|
+
while (r < n && b64At(r))
|
|
1405
|
+
r += 1;
|
|
1406
|
+
if (r - q0 < 4)
|
|
1407
|
+
return LINE_NONE;
|
|
1408
|
+
}
|
|
1409
|
+
return atLineEnd(r) ? r : LINE_NONE;
|
|
1410
|
+
};
|
|
1314
1411
|
let search = 0;
|
|
1315
1412
|
for (;;) {
|
|
1316
1413
|
const at = text.indexOf(KEY, search);
|
|
@@ -1324,40 +1421,28 @@ function collectHeadlessPpkSpans(scan, text, span) {
|
|
|
1324
1421
|
const d0 = p;
|
|
1325
1422
|
while (p < n && opaqueUnitEnd(scan, p) < 0 && text.charCodeAt(p) >= 48 && text.charCodeAt(p) <= 57)
|
|
1326
1423
|
p += 1;
|
|
1327
|
-
if (p === d0) {
|
|
1424
|
+
if (p === d0 && p < n) {
|
|
1328
1425
|
search = at + KEY.length;
|
|
1329
1426
|
continue;
|
|
1330
1427
|
}
|
|
1331
|
-
let end =
|
|
1332
|
-
let
|
|
1333
|
-
for (;;) {
|
|
1334
|
-
const
|
|
1335
|
-
if (
|
|
1428
|
+
let end = -1;
|
|
1429
|
+
let open = false;
|
|
1430
|
+
for (let from = p;;) {
|
|
1431
|
+
const r = bodyLineAt(from);
|
|
1432
|
+
if (r === LINE_NONE)
|
|
1336
1433
|
break;
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
q = skipHs(q + 1);
|
|
1341
|
-
let r = q;
|
|
1342
|
-
while (r < n && b64At(r))
|
|
1343
|
-
r += 1;
|
|
1344
|
-
if (r - q < 4) {
|
|
1345
|
-
r = q0;
|
|
1346
|
-
while (r < n && b64At(r))
|
|
1347
|
-
r += 1;
|
|
1348
|
-
if (r - q0 < 4)
|
|
1349
|
-
break;
|
|
1350
|
-
}
|
|
1351
|
-
if (!atLineEnd(r))
|
|
1434
|
+
end = r === LINE_OPEN ? n : r;
|
|
1435
|
+
if (r === LINE_OPEN) {
|
|
1436
|
+
open = true;
|
|
1352
1437
|
break;
|
|
1353
|
-
|
|
1354
|
-
|
|
1438
|
+
}
|
|
1439
|
+
from = r;
|
|
1355
1440
|
}
|
|
1356
|
-
if (
|
|
1441
|
+
if (end < 0) {
|
|
1357
1442
|
search = p;
|
|
1358
1443
|
continue;
|
|
1359
1444
|
}
|
|
1360
|
-
span(at, end);
|
|
1445
|
+
span(at, end, open);
|
|
1361
1446
|
search = end;
|
|
1362
1447
|
}
|
|
1363
1448
|
}
|
|
@@ -1442,10 +1527,6 @@ const isRegexSpace = (c) => c === 32 || (c >= 9 && c <= 13) || c === 0xa0 || c =
|
|
|
1442
1527
|
const isLineTerminator = (c) => c === 10 || c === 13 || c === 0x2028 || c === 0x2029;
|
|
1443
1528
|
const isControlCode = (c) => c < 0x20 || c === 0x7f;
|
|
1444
1529
|
const TOKEN_UNIT_Y = new RegExp(REDACTION_TOKEN_RE_SRC, "y");
|
|
1445
|
-
export const CREDENTIAL_PARAM_NAMES = Object.freeze([...CREDENTIAL_NAME_WORDS]);
|
|
1446
|
-
export function isCredentialParamName(decodedName) {
|
|
1447
|
-
return credentialNameAnchor(decodedName) !== undefined;
|
|
1448
|
-
}
|
|
1449
1530
|
const CREDENTIAL_NAME_SCAN_MAX = CREDENTIAL_NAME_MAX * 3 + 1;
|
|
1450
1531
|
function decodeAsciiName(name) {
|
|
1451
1532
|
let out = "";
|
|
@@ -1757,10 +1838,12 @@ function scanUrlRun(scan, text, i, kind, json) {
|
|
|
1757
1838
|
noteEvidenceRead(scan, i);
|
|
1758
1839
|
return i;
|
|
1759
1840
|
}
|
|
1760
|
-
function matchUserinfoAt(scan, text, p, json) {
|
|
1841
|
+
function matchUserinfoAt(scan, text, p, json, isHead) {
|
|
1761
1842
|
const probeEnd = scanUrlRun(scan, text, p, "plain-user", json);
|
|
1762
1843
|
const keyId = probeEnd > p && text.charCodeAt(probeEnd) === 58 && credentialSpansCover(scan, p, probeEnd);
|
|
1763
|
-
scanUrlRun(scan, text, p, keyId ? "token-pass" : "plain-pass", json);
|
|
1844
|
+
const runEnd = scanUrlRun(scan, text, p, keyId ? "token-pass" : "plain-pass", json);
|
|
1845
|
+
if (isHead && runEnd >= text.length && runEnd > p)
|
|
1846
|
+
return runEnd;
|
|
1764
1847
|
const at = scan.lastAt;
|
|
1765
1848
|
if (at <= p)
|
|
1766
1849
|
return null;
|
|
@@ -1778,7 +1861,7 @@ function growUrlSegments(list) {
|
|
|
1778
1861
|
list.a = bigger;
|
|
1779
1862
|
return true;
|
|
1780
1863
|
}
|
|
1781
|
-
function collectUrlCredentialSpans(scan, text, json, span) {
|
|
1864
|
+
function collectUrlCredentialSpans(scan, text, json, span, isHead) {
|
|
1782
1865
|
const dirty = scan.dirty;
|
|
1783
1866
|
const hr = scan.headsRead;
|
|
1784
1867
|
const hw = scan.headsWrite;
|
|
@@ -1814,12 +1897,12 @@ function collectUrlCredentialSpans(scan, text, json, span) {
|
|
|
1814
1897
|
else {
|
|
1815
1898
|
scan.footLo = FOOT_NONE;
|
|
1816
1899
|
scan.footHi = -1;
|
|
1817
|
-
at = matchUserinfoAt(scan, text, start, json) ?? -1;
|
|
1900
|
+
at = matchUserinfoAt(scan, text, start, json, isHead) ?? -1;
|
|
1818
1901
|
lo = scan.footLo;
|
|
1819
1902
|
hi = scan.footHi;
|
|
1820
1903
|
urlSegmentRuns += 1;
|
|
1821
1904
|
if (at >= 0 && at !== (known ? ph[pc + 1] : -2))
|
|
1822
|
-
span(start, at);
|
|
1905
|
+
span(start, at, isHead && at >= text.length);
|
|
1823
1906
|
}
|
|
1824
1907
|
if (hw.n + URL_SEG_STRIDE <= hw.a.length || growUrlSegments(hw)) {
|
|
1825
1908
|
const w = hw.a;
|
|
@@ -1915,7 +1998,8 @@ function jsonSecretFieldValueQuoteAt(text, p) {
|
|
|
1915
1998
|
i += 1;
|
|
1916
1999
|
return text.charCodeAt(i) === CH_QUOTE ? i : -1;
|
|
1917
2000
|
}
|
|
1918
|
-
|
|
2001
|
+
const STRING_OPEN = -2;
|
|
2002
|
+
function jsonStringEndAt(text, q, isHead) {
|
|
1919
2003
|
const n = text.length;
|
|
1920
2004
|
for (let i = q + 1; i < n;) {
|
|
1921
2005
|
const c = text.charCodeAt(i);
|
|
@@ -1926,21 +2010,23 @@ function jsonStringEndAt(text, q) {
|
|
|
1926
2010
|
i += 2;
|
|
1927
2011
|
continue;
|
|
1928
2012
|
}
|
|
1929
|
-
return -1;
|
|
2013
|
+
return isHead && i + 1 >= n ? STRING_OPEN : -1;
|
|
1930
2014
|
}
|
|
1931
2015
|
i += 1;
|
|
1932
2016
|
}
|
|
1933
|
-
return -1;
|
|
2017
|
+
return isHead ? STRING_OPEN : -1;
|
|
1934
2018
|
}
|
|
1935
|
-
function collectJsonSecretFieldSpans(text, span) {
|
|
2019
|
+
function collectJsonSecretFieldSpans(text, span, isHead) {
|
|
1936
2020
|
for (let p = text.indexOf(JSON_FIELD_ANCHOR); p >= 0;) {
|
|
1937
2021
|
const q = jsonSecretFieldValueQuoteAt(text, p);
|
|
1938
|
-
const
|
|
1939
|
-
if (
|
|
2022
|
+
const found = q < 0 ? -1 : jsonStringEndAt(text, q, isHead);
|
|
2023
|
+
if (found === -1) {
|
|
1940
2024
|
p = text.indexOf(JSON_FIELD_ANCHOR, p + 1);
|
|
1941
2025
|
continue;
|
|
1942
2026
|
}
|
|
1943
|
-
|
|
2027
|
+
const open = found === STRING_OPEN;
|
|
2028
|
+
const end = open ? text.length : found;
|
|
2029
|
+
span(q, end, open);
|
|
1944
2030
|
p = text.indexOf(JSON_FIELD_ANCHOR, scanResumeAfterHit(p, end, JSON_FIELD_ANCHOR.length));
|
|
1945
2031
|
}
|
|
1946
2032
|
}
|
|
@@ -2088,7 +2174,7 @@ function anchorHeadAt(text, op, json) {
|
|
|
2088
2174
|
}
|
|
2089
2175
|
return { strength, quotedName: quote !== 0, spacedLeft, listSep };
|
|
2090
2176
|
}
|
|
2091
|
-
function quotedValueEnd(text, cs, quote, json) {
|
|
2177
|
+
function quotedValueEnd(text, cs, quote, json, isHead) {
|
|
2092
2178
|
const n = text.length;
|
|
2093
2179
|
let i = cs;
|
|
2094
2180
|
while (i < n) {
|
|
@@ -2097,18 +2183,18 @@ function quotedValueEnd(text, cs, quote, json) {
|
|
|
2097
2183
|
if (ch === quote)
|
|
2098
2184
|
return i;
|
|
2099
2185
|
if (ch === JSON_STRING_END)
|
|
2100
|
-
|
|
2186
|
+
return -1;
|
|
2101
2187
|
i += unitLenOf(unit);
|
|
2102
2188
|
if (ch === CH_BACKSLASH && quote !== JSON_STRING_END) {
|
|
2103
2189
|
if (i >= n)
|
|
2104
2190
|
break;
|
|
2105
2191
|
const escaped = readAnchorUnit(text, i, json);
|
|
2106
2192
|
if (unitChOf(escaped) === JSON_STRING_END)
|
|
2107
|
-
|
|
2193
|
+
return -1;
|
|
2108
2194
|
i += unitLenOf(escaped);
|
|
2109
2195
|
}
|
|
2110
2196
|
}
|
|
2111
|
-
return -1;
|
|
2197
|
+
return isHead ? STRING_OPEN : -1;
|
|
2112
2198
|
}
|
|
2113
2199
|
function bareValueEnd(text, s, json, listSep) {
|
|
2114
2200
|
const n = text.length;
|
|
@@ -2153,7 +2239,7 @@ function bareValueEnd(text, s, json, listSep) {
|
|
|
2153
2239
|
}
|
|
2154
2240
|
return e;
|
|
2155
2241
|
}
|
|
2156
|
-
function literalValueAt(text, s, json, declaredIfBare, listSep) {
|
|
2242
|
+
function literalValueAt(text, s, json, declaredIfBare, listSep, isHead) {
|
|
2157
2243
|
const n = text.length;
|
|
2158
2244
|
if (s >= n)
|
|
2159
2245
|
return undefined;
|
|
@@ -2166,7 +2252,9 @@ function literalValueAt(text, s, json, declaredIfBare, listSep) {
|
|
|
2166
2252
|
return undefined;
|
|
2167
2253
|
if (!isAsciiAlnumCode(unitChOf(readAnchorUnit(text, vs, json))))
|
|
2168
2254
|
return undefined;
|
|
2169
|
-
const close = quotedValueEnd(text, vs, first, json);
|
|
2255
|
+
const close = quotedValueEnd(text, vs, first, json, isHead);
|
|
2256
|
+
if (close === STRING_OPEN)
|
|
2257
|
+
return { start: vs, end: n, quoted: true, open: true };
|
|
2170
2258
|
if (close >= 0)
|
|
2171
2259
|
return { start: vs, end: close, quoted: true };
|
|
2172
2260
|
}
|
|
@@ -2234,7 +2322,7 @@ function schemeValueAt(text, s, json) {
|
|
|
2234
2322
|
return undefined;
|
|
2235
2323
|
return { start: vs, end: e, quoted: false };
|
|
2236
2324
|
}
|
|
2237
|
-
function collectAnchoredValueSpans(text, json, span) {
|
|
2325
|
+
function collectAnchoredValueSpans(text, json, span, isHead) {
|
|
2238
2326
|
const n = text.length;
|
|
2239
2327
|
let resume = 0;
|
|
2240
2328
|
let nextEq = text.indexOf("=");
|
|
@@ -2275,10 +2363,10 @@ function collectAnchoredValueSpans(text, json, span) {
|
|
|
2275
2363
|
v = schemeValueAt(text, s, json);
|
|
2276
2364
|
break;
|
|
2277
2365
|
case "declarative":
|
|
2278
|
-
v = literalValueAt(text, s, json, head.quotedName || (!head.spacedLeft && !spacedRight), head.listSep);
|
|
2366
|
+
v = literalValueAt(text, s, json, head.quotedName || (!head.spacedLeft && !spacedRight), head.listSep, isHead);
|
|
2279
2367
|
break;
|
|
2280
2368
|
case "any-form":
|
|
2281
|
-
v = literalValueAt(text, s, json, true, head.listSep);
|
|
2369
|
+
v = literalValueAt(text, s, json, true, head.listSep, isHead);
|
|
2282
2370
|
break;
|
|
2283
2371
|
default: {
|
|
2284
2372
|
const unhandled = head.strength;
|
|
@@ -2287,14 +2375,14 @@ function collectAnchoredValueSpans(text, json, span) {
|
|
|
2287
2375
|
}
|
|
2288
2376
|
if (v === undefined)
|
|
2289
2377
|
continue;
|
|
2290
|
-
span(v.start, v.end);
|
|
2378
|
+
span(v.start, v.end, v.open === true);
|
|
2291
2379
|
resume = v.quoted ? v.start : v.end;
|
|
2292
2380
|
}
|
|
2293
2381
|
}
|
|
2294
2382
|
export function structuralScannerForTest(arm) {
|
|
2295
2383
|
const collect = (scan) => (text) => {
|
|
2296
2384
|
const out = [];
|
|
2297
|
-
scan(text, (start, end) => out.push([start, end]));
|
|
2385
|
+
scan(text, (start, end) => out.push([start, end]), false);
|
|
2298
2386
|
return out;
|
|
2299
2387
|
};
|
|
2300
2388
|
switch (arm) {
|
|
@@ -2307,7 +2395,7 @@ export function structuralScannerForTest(arm) {
|
|
|
2307
2395
|
let ppkScanBytes = 0;
|
|
2308
2396
|
export function ppkScanBytesForTest() { return ppkScanBytes; }
|
|
2309
2397
|
export function resetPpkScanBytesForTest() { ppkScanBytes = 0; }
|
|
2310
|
-
function collectPpkContainerSpans(scan, text, eat, span) {
|
|
2398
|
+
function collectPpkContainerSpans(scan, text, eat, span, isHead) {
|
|
2311
2399
|
const HEAD = "PuTTY-User-Key-File-";
|
|
2312
2400
|
const MAC = "Private-MAC: ";
|
|
2313
2401
|
const HASH = "Private-Hash: ";
|
|
@@ -2323,9 +2411,8 @@ function collectPpkContainerSpans(scan, text, eat, span) {
|
|
|
2323
2411
|
cache.set(needle, v);
|
|
2324
2412
|
return v;
|
|
2325
2413
|
};
|
|
2326
|
-
const
|
|
2414
|
+
const versionEnd = (at) => {
|
|
2327
2415
|
let p = at + HEAD.length;
|
|
2328
|
-
const start = p;
|
|
2329
2416
|
while (p < text.length && opaqueUnitEnd(scan, p) < 0) {
|
|
2330
2417
|
const c = text.charCodeAt(p);
|
|
2331
2418
|
if (c >= 48 && c <= 57)
|
|
@@ -2333,7 +2420,15 @@ function collectPpkContainerSpans(scan, text, eat, span) {
|
|
|
2333
2420
|
else
|
|
2334
2421
|
break;
|
|
2335
2422
|
}
|
|
2336
|
-
return p
|
|
2423
|
+
return p;
|
|
2424
|
+
};
|
|
2425
|
+
const isVersionedHead = (at) => {
|
|
2426
|
+
const p = versionEnd(at);
|
|
2427
|
+
return p > at + HEAD.length && text.startsWith(": ", p);
|
|
2428
|
+
};
|
|
2429
|
+
const isOpenHead = (at) => {
|
|
2430
|
+
const p = versionEnd(at);
|
|
2431
|
+
return p >= text.length || (p === text.length - 1 && p > at + HEAD.length && text.charCodeAt(p) === 58);
|
|
2337
2432
|
};
|
|
2338
2433
|
const nextVersionedHead = (from) => {
|
|
2339
2434
|
let p = find(HEAD, from);
|
|
@@ -2356,13 +2451,16 @@ function collectPpkContainerSpans(scan, text, eat, span) {
|
|
|
2356
2451
|
while (at !== Number.POSITIVE_INFINITY) {
|
|
2357
2452
|
let pos = at + HEAD.length + 1;
|
|
2358
2453
|
let end = text.length;
|
|
2359
|
-
let ateToEnd =
|
|
2454
|
+
let ateToEnd = !isHead;
|
|
2455
|
+
let open = false;
|
|
2360
2456
|
for (;;) {
|
|
2361
2457
|
const mac = find(MAC, pos);
|
|
2362
2458
|
const hash = find(HASH, pos);
|
|
2363
2459
|
const term = Math.min(mac, hash);
|
|
2364
|
-
if (term === Number.POSITIVE_INFINITY)
|
|
2460
|
+
if (term === Number.POSITIVE_INFINITY) {
|
|
2461
|
+
open = isHead;
|
|
2365
2462
|
break;
|
|
2463
|
+
}
|
|
2366
2464
|
const hexStart = term + (term === mac ? MAC.length : HASH.length);
|
|
2367
2465
|
const hexEnd = hexRunEnd(hexStart);
|
|
2368
2466
|
if (hexEnd - hexStart < 32) {
|
|
@@ -2389,17 +2487,22 @@ function collectPpkContainerSpans(scan, text, eat, span) {
|
|
|
2389
2487
|
}
|
|
2390
2488
|
}
|
|
2391
2489
|
}
|
|
2490
|
+
const undecided = isHead && limit === Number.POSITIVE_INFINITY;
|
|
2392
2491
|
ateToEnd = straggler < limit || !gapBlank;
|
|
2393
|
-
end = ateToEnd ? text.length : hexEnd;
|
|
2492
|
+
end = ateToEnd || undecided ? text.length : hexEnd;
|
|
2493
|
+
open = undecided;
|
|
2394
2494
|
break;
|
|
2395
2495
|
}
|
|
2396
2496
|
if (ateToEnd && eat !== undefined && eat.from < 0) {
|
|
2397
2497
|
eat.from = at;
|
|
2398
2498
|
eat.len = text.length;
|
|
2399
2499
|
}
|
|
2400
|
-
span(at, end);
|
|
2500
|
+
span(at, end, open);
|
|
2401
2501
|
at = end >= text.length ? Number.POSITIVE_INFINITY : nextVersionedHead(end);
|
|
2402
2502
|
}
|
|
2503
|
+
const last = isHead ? text.lastIndexOf(HEAD) : -1;
|
|
2504
|
+
if (last >= 0 && !inOpaqueUnit(scan, last) && isOpenHead(last))
|
|
2505
|
+
span(last, text.length, true);
|
|
2403
2506
|
}
|
|
2404
2507
|
const armPool = (() => {
|
|
2405
2508
|
const spanRepl = (token) => {
|
|
@@ -2421,6 +2524,7 @@ const armPool = (() => {
|
|
|
2421
2524
|
throw new Error(`redact: boundary arm /${re.source}/ has a zero-length structural head — the bounded head screen would admit every candidate position`);
|
|
2422
2525
|
}
|
|
2423
2526
|
const arm = { g: re, y, back, headSrc, headFirst, repl, rank, order };
|
|
2527
|
+
const keywordTail = LEXICAL_KEYWORD_ARMS.get(re);
|
|
2424
2528
|
const boundary = y === undefined || headSrc === undefined || headFirst === undefined ? undefined : { ...arm, y, headSrc, headFirst };
|
|
2425
2529
|
return {
|
|
2426
2530
|
token,
|
|
@@ -2428,9 +2532,17 @@ const armPool = (() => {
|
|
|
2428
2532
|
order,
|
|
2429
2533
|
phase: 1,
|
|
2430
2534
|
lexical: LEXICAL_KEYWORD_ARMS.has(re),
|
|
2431
|
-
collect:
|
|
2432
|
-
|
|
2433
|
-
|
|
2535
|
+
collect: (text, _json, _eat, out, isHead) => {
|
|
2536
|
+
if (boundary === undefined)
|
|
2537
|
+
collectRegexSpans(arm, text, out);
|
|
2538
|
+
else
|
|
2539
|
+
collectValueArmSpans(boundary, text, out);
|
|
2540
|
+
if (isHead && keywordTail !== undefined) {
|
|
2541
|
+
const at = undecidedKeywordStart(text, keywordTail);
|
|
2542
|
+
if (at >= 0)
|
|
2543
|
+
out.push({ start: at, end: text.length, repl, rank, order, undecided: true });
|
|
2544
|
+
}
|
|
2545
|
+
},
|
|
2434
2546
|
retry: boundary === undefined ? undefined : (text, at, out) => collectStickySpans(boundary, text, at, out),
|
|
2435
2547
|
headSrc: boundary === undefined ? undefined : headSrc,
|
|
2436
2548
|
headFirst: boundary === undefined ? undefined : headFirst,
|
|
@@ -2444,7 +2556,7 @@ const armPool = (() => {
|
|
|
2444
2556
|
order,
|
|
2445
2557
|
phase: 1,
|
|
2446
2558
|
lexical,
|
|
2447
|
-
collect: (text, json, eat, out) => arm(text, json, eat, (start, end) => out.push({ start, end, repl, rank, order })),
|
|
2559
|
+
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),
|
|
2448
2560
|
retry: undefined,
|
|
2449
2561
|
headSrc: undefined,
|
|
2450
2562
|
headFirst: undefined,
|
|
@@ -2459,7 +2571,7 @@ const armPool = (() => {
|
|
|
2459
2571
|
order,
|
|
2460
2572
|
phase: 2,
|
|
2461
2573
|
lexical: false,
|
|
2462
|
-
collect: (scan, text, json, eat, out) => arm(scan, text, json, eat, (start, end) => out.push({ start, end, repl, rank, order })),
|
|
2574
|
+
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),
|
|
2463
2575
|
retry: undefined,
|
|
2464
2576
|
headSrc: undefined,
|
|
2465
2577
|
headFirst: undefined,
|
|
@@ -2467,22 +2579,22 @@ const armPool = (() => {
|
|
|
2467
2579
|
};
|
|
2468
2580
|
const structuralScannerMember = (row, order) => {
|
|
2469
2581
|
if (row === PEM_FENCE_PATTERN)
|
|
2470
|
-
return scannerMember(row[1], order, (text, _json, _eat, span) => collectPemFenceSpans(text, span));
|
|
2582
|
+
return scannerMember(row[1], order, (text, _json, _eat, span, isHead) => collectPemFenceSpans(text, span, isHead));
|
|
2471
2583
|
if (row === SSH2_FENCE_PATTERN)
|
|
2472
|
-
return forensicMember(row[1], order, (scan, text, _json, _eat, span) => collectSsh2FenceSpans(scan, text, span));
|
|
2584
|
+
return forensicMember(row[1], order, (scan, text, _json, _eat, span, isHead) => collectSsh2FenceSpans(scan, text, span, isHead));
|
|
2473
2585
|
if (row === URL_CREDENTIAL_PATTERN)
|
|
2474
|
-
return forensicMember(row[1], order, (scan, text, json, _eat, span) => collectUrlCredentialSpans(scan, text, json, span));
|
|
2586
|
+
return forensicMember(row[1], order, (scan, text, json, _eat, span, isHead) => collectUrlCredentialSpans(scan, text, json, span, isHead));
|
|
2475
2587
|
if (row === JSON_SECRET_FIELD_PATTERN)
|
|
2476
|
-
return scannerMember(row[1], order, (text, _json, _eat, span) => collectJsonSecretFieldSpans(text, span));
|
|
2588
|
+
return scannerMember(row[1], order, (text, _json, _eat, span, isHead) => collectJsonSecretFieldSpans(text, span, isHead));
|
|
2477
2589
|
throw new Error(`redact: structural row /${row[0].source}/ has no hand-written scanner — structural grammars are declared as regexes but never executed as one (S-102 / S-265)`);
|
|
2478
2590
|
};
|
|
2479
2591
|
const members = [];
|
|
2480
2592
|
for (const row of STRUCTURAL_SECRET_PATTERNS)
|
|
2481
2593
|
members.push(structuralScannerMember(row, members.length));
|
|
2482
|
-
members.push(forensicMember(PPK_CONTAINER_TOKEN, members.length, (scan, text, _json, eat, span) => collectPpkContainerSpans(scan, text, eat, span)));
|
|
2594
|
+
members.push(forensicMember(PPK_CONTAINER_TOKEN, members.length, (scan, text, _json, eat, span, isHead) => collectPpkContainerSpans(scan, text, eat, span, isHead)));
|
|
2483
2595
|
members.push(forensicMember(PEM_FRAGMENT_TOKEN, members.length, (scan, text, json, _eat, span) => collectPemFragmentSpans(scan, text, json, span)));
|
|
2484
|
-
members.push(forensicMember(HEADLESS_PPK_TOKEN, members.length, (scan, text, _json, _eat, span) => collectHeadlessPpkSpans(scan, text, span)));
|
|
2485
|
-
members.push(scannerMember(ANCHORED_VALUE_TOKEN, members.length, (text, json, _eat, span) => collectAnchoredValueSpans(text, json, span), ARM_RANK.keyword, true));
|
|
2596
|
+
members.push(forensicMember(HEADLESS_PPK_TOKEN, members.length, (scan, text, _json, _eat, span, isHead) => collectHeadlessPpkSpans(scan, text, span, isHead)));
|
|
2597
|
+
members.push(scannerMember(ANCHORED_VALUE_TOKEN, members.length, (text, json, _eat, span, isHead) => collectAnchoredValueSpans(text, json, span, isHead), ARM_RANK.keyword, true));
|
|
2486
2598
|
for (const row of KEYWORD_SECRET_PATTERNS)
|
|
2487
2599
|
members.push(regexMember(row, ARM_RANK.keyword, members.length));
|
|
2488
2600
|
for (const row of SHAPE_SECRET_PATTERNS)
|
|
@@ -2513,8 +2625,9 @@ const armPool = (() => {
|
|
|
2513
2625
|
const headFirstUnit = unionMembers(retryMembers.map((m) => m.headFirst)) ?? new Uint32Array(0x10000 >>> 5).fill(0xffffffff);
|
|
2514
2626
|
poolArmCount = members.length;
|
|
2515
2627
|
poolBoundaryArmCount = retryMembers.length;
|
|
2516
|
-
return (text, wantCount, json, eat, scope) => {
|
|
2628
|
+
return (text, wantCount, json, eat, scope, reach) => {
|
|
2517
2629
|
readings += 1;
|
|
2630
|
+
const isHead = reach !== undefined;
|
|
2518
2631
|
const lane = LANES[scope];
|
|
2519
2632
|
const spans = [];
|
|
2520
2633
|
let tried;
|
|
@@ -2550,7 +2663,7 @@ const armPool = (() => {
|
|
|
2550
2663
|
}
|
|
2551
2664
|
};
|
|
2552
2665
|
for (const m of lane.phase1)
|
|
2553
|
-
m.collect(text, json, eat, spans);
|
|
2666
|
+
m.collect(text, json, eat, spans, isHead);
|
|
2554
2667
|
closeCoveragePositions(0);
|
|
2555
2668
|
if (lane.phase2.length > 0) {
|
|
2556
2669
|
const scan = buildForensicScan(credentialSpans(text), lane.phase2.length);
|
|
@@ -2567,7 +2680,7 @@ const armPool = (() => {
|
|
|
2567
2680
|
}
|
|
2568
2681
|
scan.footLo = FOOT_NONE;
|
|
2569
2682
|
scan.footHi = -1;
|
|
2570
|
-
lane.phase2[j].collect(scan, text, json, level === 0 ? eat : undefined, spans);
|
|
2683
|
+
lane.phase2[j].collect(scan, text, json, level === 0 ? eat : undefined, spans, isHead);
|
|
2571
2684
|
foot[2 * j] = scan.footLo;
|
|
2572
2685
|
foot[2 * j + 1] = scan.footHi;
|
|
2573
2686
|
forensicRuns += 1;
|
|
@@ -2582,11 +2695,12 @@ const armPool = (() => {
|
|
|
2582
2695
|
}
|
|
2583
2696
|
}
|
|
2584
2697
|
poolSpanCount += spans.length;
|
|
2585
|
-
const
|
|
2698
|
+
const survives = (sp) => sp.undecided !== true || reach === undefined || reach(sp.start);
|
|
2699
|
+
const kept = resolveSpanOverlaps(reach === undefined ? spans : spans.filter(survives));
|
|
2586
2700
|
if (!wantCount || kept.length === 0)
|
|
2587
2701
|
return { hits: NO_HITS, kept };
|
|
2588
2702
|
const tokenOf = (sp) => members[sp.order].token;
|
|
2589
|
-
const strong = spans.filter((sp) => armOfToken(tokenOf(sp)) === "full").sort((a, b) => a.start - b.start);
|
|
2703
|
+
const strong = spans.filter((sp) => armOfToken(tokenOf(sp)) === "full" && survives(sp)).sort((a, b) => a.start - b.start);
|
|
2590
2704
|
const byArmedToken = new Map();
|
|
2591
2705
|
const stamped = [];
|
|
2592
2706
|
let si = 0;
|
|
@@ -2613,8 +2727,8 @@ let onRedaction;
|
|
|
2613
2727
|
export function setRedactionObserver(fn) {
|
|
2614
2728
|
onRedaction = fn;
|
|
2615
2729
|
}
|
|
2616
|
-
function applyRules(text, wantCount, json, eat, scope) {
|
|
2617
|
-
return armPool(text, wantCount, json, eat, scope);
|
|
2730
|
+
function applyRules(text, wantCount, json, eat, scope, reach) {
|
|
2731
|
+
return armPool(text, wantCount, json, eat, scope, reach);
|
|
2618
2732
|
}
|
|
2619
2733
|
function reportHits(hits) {
|
|
2620
2734
|
if (onRedaction === undefined)
|
|
@@ -2627,6 +2741,7 @@ function reportHits(hits) {
|
|
|
2627
2741
|
}
|
|
2628
2742
|
}
|
|
2629
2743
|
}
|
|
2744
|
+
export const WHOLE_EXTENT = { kind: "whole" };
|
|
2630
2745
|
function invisibleClassesOf(layout) {
|
|
2631
2746
|
switch (layout) {
|
|
2632
2747
|
case "multi-line":
|
|
@@ -2639,7 +2754,99 @@ function invisibleClassesOf(layout) {
|
|
|
2639
2754
|
}
|
|
2640
2755
|
}
|
|
2641
2756
|
}
|
|
2642
|
-
function
|
|
2757
|
+
function trailingPartialEscapeStart(s) {
|
|
2758
|
+
const n = s.length;
|
|
2759
|
+
const runStart = (end) => {
|
|
2760
|
+
let k = end;
|
|
2761
|
+
while (k > 0 && s.charCodeAt(k - 1) === CH_BACKSLASH)
|
|
2762
|
+
k -= 1;
|
|
2763
|
+
return k;
|
|
2764
|
+
};
|
|
2765
|
+
const highSurrogateEscapeStartBefore = (end) => {
|
|
2766
|
+
let j = end;
|
|
2767
|
+
let value = -1;
|
|
2768
|
+
let bodyStart = -1;
|
|
2769
|
+
if (j >= 1 && s.charCodeAt(j - 1) === CH_RBRACE) {
|
|
2770
|
+
let k = j - 1;
|
|
2771
|
+
while (k > 0 && hexDigitValue(s.charCodeAt(k - 1)) >= 0)
|
|
2772
|
+
k -= 1;
|
|
2773
|
+
if (k >= 2 && s.charCodeAt(k - 1) === CH_LBRACE && s.charCodeAt(k - 2) === CH_u && j - 1 - k >= 1) {
|
|
2774
|
+
value = readHexRunValue(s, k, j - 1);
|
|
2775
|
+
bodyStart = k - 2;
|
|
2776
|
+
}
|
|
2777
|
+
}
|
|
2778
|
+
else if (j >= 5 && s.charCodeAt(j - 5) === CH_u) {
|
|
2779
|
+
value = readHexRun(s, j - 4, 4);
|
|
2780
|
+
bodyStart = j - 5;
|
|
2781
|
+
}
|
|
2782
|
+
if (value < 0xd800 || value > 0xdbff)
|
|
2783
|
+
return -1;
|
|
2784
|
+
const k = runStart(bodyStart);
|
|
2785
|
+
return k < bodyStart ? k : -1;
|
|
2786
|
+
};
|
|
2787
|
+
let j = n;
|
|
2788
|
+
while (j > 0 && hexDigitValue(s.charCodeAt(j - 1)) >= 0)
|
|
2789
|
+
j -= 1;
|
|
2790
|
+
const hexRun = n - j;
|
|
2791
|
+
let element = -1;
|
|
2792
|
+
if (j >= 2 && s.charCodeAt(j - 1) === CH_LBRACE && s.charCodeAt(j - 2) === CH_u)
|
|
2793
|
+
element = j - 2;
|
|
2794
|
+
else if (j >= 1 && s.charCodeAt(j - 1) === CH_u && hexRun <= 3)
|
|
2795
|
+
element = j - 1;
|
|
2796
|
+
else if (j >= 1 && s.charCodeAt(j - 1) === CH_x && hexRun <= 1)
|
|
2797
|
+
element = j - 1;
|
|
2798
|
+
else if (hexRun === 0 && n >= 1 && s.charCodeAt(n - 1) === CH_BACKSLASH)
|
|
2799
|
+
element = n;
|
|
2800
|
+
if (element >= 0) {
|
|
2801
|
+
const k = runStart(element);
|
|
2802
|
+
if (k >= element)
|
|
2803
|
+
return -1;
|
|
2804
|
+
const high = highSurrogateEscapeStartBefore(k);
|
|
2805
|
+
return high >= 0 ? high : k;
|
|
2806
|
+
}
|
|
2807
|
+
return highSurrogateEscapeStartBefore(n);
|
|
2808
|
+
}
|
|
2809
|
+
function readHexRunValue(s, from, to) {
|
|
2810
|
+
let v = 0;
|
|
2811
|
+
for (let k = from; k < to; k++) {
|
|
2812
|
+
const d = hexDigitValue(s.charCodeAt(k));
|
|
2813
|
+
if (d < 0)
|
|
2814
|
+
return -1;
|
|
2815
|
+
v = v * 16 + d;
|
|
2816
|
+
if (v > 0x10ffff)
|
|
2817
|
+
return -1;
|
|
2818
|
+
}
|
|
2819
|
+
return v;
|
|
2820
|
+
}
|
|
2821
|
+
function shortestReading(text, layout) {
|
|
2822
|
+
const raw = collectRawControlCuts(text, invisibleClassesOf(layout));
|
|
2823
|
+
const normalized = normalizeForRedaction(deleteInvisibleCharacters(text, raw, raw.present).text);
|
|
2824
|
+
const open = trailingPartialEscapeStart(normalized);
|
|
2825
|
+
const spelled = collectSpelledCuts(normalized);
|
|
2826
|
+
const length = deleteInvisibleCharacters(normalized, spelled, spelled.present).text.length;
|
|
2827
|
+
return open < 0 ? { visible: length, partial: false } : { visible: length - (normalized.length - open), partial: true };
|
|
2828
|
+
}
|
|
2829
|
+
export function readingHeadEnd(text, layout, units, from = 0) {
|
|
2830
|
+
const rest = text.length - from;
|
|
2831
|
+
let len = Math.min(rest, units);
|
|
2832
|
+
let first = true;
|
|
2833
|
+
let grow = 64;
|
|
2834
|
+
while (len < rest) {
|
|
2835
|
+
const { visible, partial } = shortestReading(text.slice(from, from + len), layout);
|
|
2836
|
+
if (visible >= units) {
|
|
2837
|
+
if (!partial)
|
|
2838
|
+
return from + len;
|
|
2839
|
+
len = Math.min(rest, len + grow);
|
|
2840
|
+
grow *= 2;
|
|
2841
|
+
continue;
|
|
2842
|
+
}
|
|
2843
|
+
const step = first && visible * 2 >= len ? Math.ceil((units - visible) / (visible / len)) + 1 : Math.max(units - visible, len);
|
|
2844
|
+
first = false;
|
|
2845
|
+
len = Math.min(rest, len + step);
|
|
2846
|
+
}
|
|
2847
|
+
return text.length;
|
|
2848
|
+
}
|
|
2849
|
+
function runRedaction(text, observe, json, wantHits, scope, layout, extent) {
|
|
2643
2850
|
const wantCount = wantHits || (onRedaction !== undefined && observe);
|
|
2644
2851
|
const eat = observe ? { from: -1, len: 0 } : undefined;
|
|
2645
2852
|
let spans = [];
|
|
@@ -2647,15 +2854,17 @@ function runRedaction(text, observe, json, wantHits, scope, layout) {
|
|
|
2647
2854
|
let readsSoFar = 0;
|
|
2648
2855
|
let unioned = false;
|
|
2649
2856
|
const read = (layers, view) => {
|
|
2650
|
-
const
|
|
2857
|
+
const toOriginal = createViewToOriginal(layers);
|
|
2858
|
+
const reach = extent.kind === "head" ? (viewStart) => toOriginal.start(viewStart) < extent.emitEnd : undefined;
|
|
2859
|
+
const r = applyRules(view, wantCount, json, eat, scope, reach);
|
|
2651
2860
|
forgetEscapedSeparatorTails();
|
|
2652
2861
|
readsSoFar += 1;
|
|
2653
2862
|
if (readsSoFar === 1) {
|
|
2654
|
-
spans =
|
|
2863
|
+
spans = toOriginal.spans(r.kept);
|
|
2655
2864
|
hits = r.hits;
|
|
2656
2865
|
return;
|
|
2657
2866
|
}
|
|
2658
|
-
const extra =
|
|
2867
|
+
const extra = toOriginal.spans(r.kept);
|
|
2659
2868
|
if (extra.length === 0)
|
|
2660
2869
|
return;
|
|
2661
2870
|
spans = resolveSpanOverlaps([...spans, ...extra]);
|
|
@@ -2697,41 +2906,36 @@ function hitsOfSpans(spans) {
|
|
|
2697
2906
|
}
|
|
2698
2907
|
return [...by.values()].map(({ token, arm, count }) => ({ label: labelOfToken(token), count, arm }));
|
|
2699
2908
|
}
|
|
2700
|
-
function
|
|
2701
|
-
let
|
|
2702
|
-
|
|
2703
|
-
if (out.length === 0)
|
|
2704
|
-
return out;
|
|
2705
|
-
let at;
|
|
2909
|
+
function createViewToOriginal(layers) {
|
|
2910
|
+
let ats;
|
|
2911
|
+
const layerMaps = () => (ats ??= layers.flatMap((layer) => {
|
|
2706
2912
|
if ("fold" in layer) {
|
|
2707
2913
|
const fold = layer.fold;
|
|
2708
|
-
|
|
2709
|
-
continue;
|
|
2710
|
-
at = (v) => viewIndexInInput(fold, v);
|
|
2914
|
+
return fold.at === undefined ? [] : [(v) => viewIndexInInput(fold, v)];
|
|
2711
2915
|
}
|
|
2712
|
-
|
|
2713
|
-
|
|
2714
|
-
|
|
2715
|
-
|
|
2716
|
-
|
|
2717
|
-
|
|
2718
|
-
|
|
2719
|
-
|
|
2720
|
-
|
|
2916
|
+
if (layer.view === layer.input)
|
|
2917
|
+
return [];
|
|
2918
|
+
const map = mapNormalizedToOriginal(layer.input, layer.view);
|
|
2919
|
+
return [(v) => map[v]];
|
|
2920
|
+
}));
|
|
2921
|
+
return {
|
|
2922
|
+
start: (viewIndex) => layerMaps().reduce((p, at) => at(p), viewIndex),
|
|
2923
|
+
spans: (kept) => (kept.length === 0 ? kept : layerMaps().reduce((out, at) => out.map((s) => ({ ...s, start: at(s.start), end: at(s.end - 1) + 1 })), kept)),
|
|
2924
|
+
};
|
|
2721
2925
|
}
|
|
2722
2926
|
export function redactSecrets(text, opts) {
|
|
2723
|
-
return runRedaction(text, opts?.observe ?? true, opts?.json === true, false, "free-text", "multi-line").text;
|
|
2927
|
+
return runRedaction(text, opts?.observe ?? true, opts?.json === true, false, "free-text", "multi-line", WHOLE_EXTENT).text;
|
|
2724
2928
|
}
|
|
2725
2929
|
export function redactSecretsWithArms(text, opts) {
|
|
2726
|
-
const r = runRedaction(text, opts?.observe ?? true, opts?.json === true, true, "free-text", "multi-line");
|
|
2930
|
+
const r = runRedaction(text, opts?.observe ?? true, opts?.json === true, true, "free-text", "multi-line", WHOLE_EXTENT);
|
|
2727
2931
|
return { text: r.text, arms: new Set(r.hits.map((h) => h.arm)) };
|
|
2728
2932
|
}
|
|
2729
2933
|
export function redactionSpansForTest(text, opts) {
|
|
2730
|
-
const r = runRedaction(text, false, opts?.json === true, false, "free-text", "multi-line");
|
|
2934
|
+
const r = runRedaction(text, false, opts?.json === true, false, "free-text", "multi-line", WHOLE_EXTENT);
|
|
2731
2935
|
return { text: r.text, spans: r.spans.map((sp) => ({ start: sp.start, end: sp.end })) };
|
|
2732
2936
|
}
|
|
2733
|
-
export function proposeRedactionSpans(text, scope, layout) {
|
|
2734
|
-
return runRedaction(text, true, false, false, scope, layout).spans.map((sp) => ({ start: sp.start, end: sp.end, repl: sp.repl }));
|
|
2937
|
+
export function proposeRedactionSpans(text, scope, layout, extent) {
|
|
2938
|
+
return runRedaction(text, true, false, false, scope, layout, extent).spans.map((sp) => ({ start: sp.start, end: sp.end, repl: sp.repl }));
|
|
2735
2939
|
}
|
|
2736
2940
|
const REDACT_MAX_DEPTH = 64;
|
|
2737
2941
|
export function redactDeep(value) {
|
|
@@ -2833,7 +3037,7 @@ function unitCarriesCredential(unit) {
|
|
|
2833
3037
|
for (const v of armValueClassViews())
|
|
2834
3038
|
views.add(boundedUnitView(spelling, v.inClass, v.filler));
|
|
2835
3039
|
views.delete("");
|
|
2836
|
-
const hit = [...views].some((view) => runRedaction(view, true, false, false, "bounded-unit", "multi-line").text !== view);
|
|
3040
|
+
const hit = [...views].some((view) => runRedaction(view, true, false, false, "bounded-unit", "multi-line", WHOLE_EXTENT).text !== view);
|
|
2837
3041
|
return { hit, unreadable: decoded.residue };
|
|
2838
3042
|
}
|
|
2839
3043
|
function displaySafePathname(pathname) {
|
|
@@ -2863,7 +3067,7 @@ function shapeLawCredentialInQueryOrFragment(canonical) {
|
|
|
2863
3067
|
const boundary = canonical.indexOf("?");
|
|
2864
3068
|
if (boundary < 0)
|
|
2865
3069
|
return false;
|
|
2866
|
-
if (runRedaction(canonical, false, false, false, "free-text", "multi-line").spans.some((sp) => sp.end > boundary))
|
|
3070
|
+
if (runRedaction(canonical, false, false, false, "free-text", "multi-line", WHOLE_EXTENT).spans.some((sp) => sp.end > boundary))
|
|
2867
3071
|
return true;
|
|
2868
3072
|
for (const unit of canonical.slice(boundary + 1).split("&").flatMap((sg) => {
|
|
2869
3073
|
const eq = sg.indexOf("=");
|