@sema-agent/server 7.143.0 → 7.145.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 +295 -0
- package/README.md +3 -0
- package/README.zh-CN.md +1 -0
- package/USAGE.md +34 -16
- package/dist/approval-card.d.ts +31 -14
- package/dist/approval-card.js +8 -8
- package/dist/audit.d.ts +2 -21
- package/dist/audit.js +0 -9
- package/dist/boot/execution-env.js +1 -1
- package/dist/boot/runner-deps.d.ts +10 -5
- package/dist/boot/runner-deps.js +5 -4
- package/dist/boot/session-faces.d.ts +1 -1
- package/dist/boot/session-faces.js +3 -3
- package/dist/boot/shutdown.d.ts +1 -1
- package/dist/boot/shutdown.js +4 -2
- package/dist/boot/stage-01-config.js +2 -0
- package/dist/boot/stage-07-capability-layer.js +1 -1
- package/dist/boot/stage-09-leader.d.ts +1 -1
- package/dist/boot/stage-10-http-server.d.ts +1 -1
- package/dist/boot/stage-10-http-server.js +4 -9
- package/dist/capabilities/scenarios.d.ts +14 -18
- package/dist/capabilities/scenarios.js +1 -2
- package/dist/config-d-migration.d.ts +3 -0
- package/dist/delegated-session-owner.d.ts +33 -151
- package/dist/delegated-session-owner.js +1 -83
- package/dist/entry-mode-clamp-notice.d.ts +1 -1
- package/dist/execution-placement.d.ts +7 -4
- package/dist/fleet/fleet-bus.d.ts +13 -3
- package/dist/fleet/fleet-bus.js +17 -3
- package/dist/hooks/classic-stations.d.ts +3 -1
- package/dist/hooks/classic-stations.js +2 -0
- package/dist/http/admission.js +2 -1
- package/dist/http/checkpoint-ctx.d.ts +1 -0
- package/dist/http/checkpoint-ctx.js +1 -0
- package/dist/http/durable-run-leg.js +7 -6
- package/dist/http/principal-gate.d.ts +1 -1
- package/dist/http/refusal-body.d.ts +7 -3
- package/dist/http/refusal-body.js +1 -0
- package/dist/http/resume-legs.js +16 -7
- package/dist/http/route-ctx.d.ts +15 -0
- package/dist/http/routes/approvals-assistant.d.ts +20 -1
- package/dist/http/routes/approvals-assistant.js +28 -6
- package/dist/http/routes/rules.d.ts +1 -1
- package/dist/http/routes/rules.js +5 -2
- package/dist/http/routes/session-sync.js +12 -0
- package/dist/http/routes/tasks.js +12 -9
- package/dist/http/run-meta.d.ts +14 -7
- package/dist/http/run-meta.js +5 -2
- package/dist/http/server.d.ts +13 -5
- package/dist/http/server.js +3 -0
- package/dist/http/submit-ctx.d.ts +12 -0
- package/dist/http/submit-ctx.js +23 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/lane-home-probe.d.ts +15 -0
- package/dist/lane-home-probe.js +4 -0
- package/dist/observability/corrupt-read-seat.d.ts +5 -9
- package/dist/observability/corrupt-read-seat.js +1 -2
- package/dist/observability/fail-open.d.ts +26 -10
- package/dist/observability/fail-open.js +26 -10
- package/dist/observability/run-terminal.d.ts +1 -1
- package/dist/observability/run-terminal.js +2 -2
- package/dist/parked-revive-surface.d.ts +6 -7
- package/dist/peer-inbound-face.d.ts +29 -34
- package/dist/peer-inbound-face.js +28 -63
- package/dist/plugins/approval-ask-store-file.d.ts +16 -2
- package/dist/plugins/approval-ask-store-file.js +21 -1
- package/dist/plugins/approval-ask-store-memory.d.ts +8 -2
- package/dist/plugins/approval-ask-store-memory.js +11 -8
- package/dist/plugins/caching-session-store.d.ts +3 -3
- package/dist/plugins/caching-session-store.js +3 -3
- package/dist/plugins/checkpoint-store-sql.d.ts +62 -37
- package/dist/plugins/checkpoint-store-sql.js +123 -40
- package/dist/plugins/file-run-store.d.ts +13 -1
- package/dist/plugins/file-run-store.js +11 -1
- package/dist/plugins/fork-routing-session-store.d.ts +8 -1
- package/dist/plugins/fork-routing-session-store.js +6 -0
- package/dist/plugins/local-checkpoint-store.d.ts +17 -8
- package/dist/plugins/local-checkpoint-store.js +26 -7
- package/dist/plugins/local-session-store.d.ts +105 -34
- package/dist/plugins/local-session-store.js +168 -65
- package/dist/plugins/local-session-window.d.ts +4 -2
- package/dist/plugins/memory-run-store.d.ts +2 -0
- package/dist/plugins/memory-run-store.js +6 -0
- package/dist/plugins/one-time-migrations.d.ts +12 -12
- package/dist/plugins/one-time-migrations.js +13 -13
- package/dist/plugins/pg-session-storage.d.ts +1 -9
- package/dist/plugins/pg-session-storage.js +2 -19
- package/dist/plugins/remote-env-host.d.ts +8 -4
- package/dist/plugins/remote-env-host.js +7 -9
- package/dist/plugins/remote-shell.js +12 -2
- package/dist/plugins/run-store-sql.d.ts +4 -0
- package/dist/plugins/run-store-sql.js +3 -0
- package/dist/plugins/schema-consistency.d.ts +5 -1
- package/dist/plugins/session-fork.d.ts +1 -1
- package/dist/plugins/session-ownership.d.ts +9 -0
- package/dist/plugins/session-ownership.js +6 -0
- package/dist/plugins/session-store.d.ts +13 -16
- package/dist/plugins/session-store.js +9 -10
- package/dist/plugins/sql-json-column.d.ts +24 -2
- package/dist/plugins/sql-json-column.js +38 -3
- package/dist/plugins/store-backend.js +3 -2
- package/dist/plugins/tidb-session-store.d.ts +2 -14
- package/dist/plugins/tidb-session-store.js +2 -20
- package/dist/prompt-screen.d.ts +21 -0
- package/dist/prompt-screen.js +8 -0
- package/dist/rules-consent.d.ts +63 -51
- package/dist/rules-consent.js +6 -10
- package/dist/run-acceptance.d.ts +40 -10
- package/dist/run-acceptance.js +38 -5
- package/dist/run-local.js +2 -1
- package/dist/runs.js +5 -1
- package/dist/security.d.ts +10 -7
- package/dist/session-agent.d.ts +39 -0
- package/dist/session-agent.js +16 -0
- package/dist/session-error-wire.d.ts +25 -26
- package/dist/session-error-wire.js +4 -5
- package/dist/session-incarnation.d.ts +2 -2
- package/dist/session-notify-door.d.ts +2 -2
- package/dist/session-notify-door.js +5 -1
- package/dist/tool-approval.d.ts +3 -1
- package/dist/tool-approval.js +47 -18
- package/dist/trace/core-keyset-guard.d.ts +1 -1
- package/dist/trace/engine-notice-wire.d.ts +1 -1
- package/dist/trace/engine-notice-wire.js +1 -1
- package/dist/trace/ledger-sink.js +12 -4
- package/dist/trace/model-segment.d.ts +26 -6
- package/dist/trace/model-segment.js +58 -9
- package/dist/trace/project.d.ts +8 -2
- package/dist/trace/project.js +6 -4
- package/dist/trace/redact.d.ts +77 -2
- package/dist/trace/redact.js +234 -99
- package/dist/trace/self-minted-wire.d.ts +160 -9
- package/dist/trace/self-minted-wire.js +52 -3
- package/package.json +3 -3
- package/wire-changes/server@7.144.0.json +291 -0
- package/wire-changes/server@7.145.0.json +146 -0
- package/dist/session-records-skipped-notice.d.ts +0 -21
- package/dist/session-records-skipped-notice.js +0 -27
package/dist/trace/redact.js
CHANGED
|
@@ -73,7 +73,7 @@ function collectSpelledCuts(s) {
|
|
|
73
73
|
let present = 0;
|
|
74
74
|
let i = s.indexOf("\\");
|
|
75
75
|
if (i < 0)
|
|
76
|
-
return { cuts, present };
|
|
76
|
+
return { cuts, present, open: -1 };
|
|
77
77
|
const n = s.length;
|
|
78
78
|
const read = { end: 0 };
|
|
79
79
|
while (i >= 0) {
|
|
@@ -81,6 +81,8 @@ function collectSpelledCuts(s) {
|
|
|
81
81
|
while (body < n && s.charCodeAt(body) === CH_BACKSLASH)
|
|
82
82
|
body += 1;
|
|
83
83
|
const cp = readSpelledCodePoint(s, body, read);
|
|
84
|
+
if (cp === ESCAPE_TRUNCATED)
|
|
85
|
+
return { cuts, present, open: i };
|
|
84
86
|
const cls = cp < 0 ? 0 : isFormatCodePoint(cp) ? SPELLED_FORMAT : cp < 0x10000 && INVISIBLE_UNIT_CLASS[cp] === 1 ? SPELLED_CONTROL : 0;
|
|
85
87
|
if (cls !== 0) {
|
|
86
88
|
cuts.push(i, read.end, cls);
|
|
@@ -89,7 +91,7 @@ function collectSpelledCuts(s) {
|
|
|
89
91
|
}
|
|
90
92
|
i = s.indexOf("\\", body);
|
|
91
93
|
}
|
|
92
|
-
return { cuts, present };
|
|
94
|
+
return { cuts, present, open: -1 };
|
|
93
95
|
}
|
|
94
96
|
function deleteInvisibleCharacters(s, all, mask) {
|
|
95
97
|
const atList = [];
|
|
@@ -127,9 +129,12 @@ function viewIndexInInput(fold, v) {
|
|
|
127
129
|
}
|
|
128
130
|
return lo === 0 ? v : v + fold.cum[lo - 1];
|
|
129
131
|
}
|
|
132
|
+
const ESCAPE_TRUNCATED = -2;
|
|
130
133
|
function readHexRun(s, at, count) {
|
|
131
134
|
let v = 0;
|
|
132
135
|
for (let k = at; k < at + count; k++) {
|
|
136
|
+
if (k >= s.length)
|
|
137
|
+
return ESCAPE_TRUNCATED;
|
|
133
138
|
const d = hexDigitValue(s.charCodeAt(k));
|
|
134
139
|
if (d < 0)
|
|
135
140
|
return -1;
|
|
@@ -138,6 +143,8 @@ function readHexRun(s, at, count) {
|
|
|
138
143
|
return v;
|
|
139
144
|
}
|
|
140
145
|
function readEscapeElement(s, at, out) {
|
|
146
|
+
if (at >= s.length)
|
|
147
|
+
return ESCAPE_TRUNCATED;
|
|
141
148
|
const kind = s.charCodeAt(at);
|
|
142
149
|
if (kind === CH_x) {
|
|
143
150
|
out.end = at + 3;
|
|
@@ -145,6 +152,8 @@ function readEscapeElement(s, at, out) {
|
|
|
145
152
|
}
|
|
146
153
|
if (kind !== CH_u)
|
|
147
154
|
return -1;
|
|
155
|
+
if (at + 1 >= s.length)
|
|
156
|
+
return ESCAPE_TRUNCATED;
|
|
148
157
|
if (s.charCodeAt(at + 1) === CH_LBRACE) {
|
|
149
158
|
let v = 0;
|
|
150
159
|
let k = at + 2;
|
|
@@ -156,6 +165,8 @@ function readEscapeElement(s, at, out) {
|
|
|
156
165
|
if (v > 0x10ffff)
|
|
157
166
|
return -1;
|
|
158
167
|
}
|
|
168
|
+
if (k >= s.length)
|
|
169
|
+
return ESCAPE_TRUNCATED;
|
|
159
170
|
if (k === at + 2 || s.charCodeAt(k) !== CH_RBRACE)
|
|
160
171
|
return -1;
|
|
161
172
|
out.end = k + 1;
|
|
@@ -171,13 +182,20 @@ function readSpelledCodePoint(s, at, out) {
|
|
|
171
182
|
if (v > 0xdbff)
|
|
172
183
|
return -1;
|
|
173
184
|
let k = out.end;
|
|
185
|
+
if (k >= s.length)
|
|
186
|
+
return ESCAPE_TRUNCATED;
|
|
174
187
|
if (s.charCodeAt(k) !== CH_BACKSLASH)
|
|
175
188
|
return -1;
|
|
176
189
|
while (s.charCodeAt(k) === CH_BACKSLASH)
|
|
177
190
|
k += 1;
|
|
178
191
|
const lo = readEscapeElement(s, k, out);
|
|
192
|
+
if (lo === ESCAPE_TRUNCATED)
|
|
193
|
+
return ESCAPE_TRUNCATED;
|
|
179
194
|
return lo >= 0xdc00 && lo <= 0xdfff ? 0x10000 + ((v - 0xd800) << 10) + (lo - 0xdc00) : -1;
|
|
180
195
|
}
|
|
196
|
+
function isEscapeBodyUnit(c) {
|
|
197
|
+
return c === CH_BACKSLASH || c === CH_u || c === CH_x || c === CH_LBRACE || c === CH_RBRACE || hexDigitValue(c) >= 0;
|
|
198
|
+
}
|
|
181
199
|
const FORMAT_CODEPOINT_TABLE = (() => {
|
|
182
200
|
const table = new Uint8Array(0x10000);
|
|
183
201
|
const units = [];
|
|
@@ -463,9 +481,13 @@ let ssh2ScanBytes = 0;
|
|
|
463
481
|
let mappedUnits = 0;
|
|
464
482
|
let coverageVisits = 0;
|
|
465
483
|
let rowVisits = 0;
|
|
484
|
+
let scannedUnits = 0;
|
|
485
|
+
let windowAdvances = 0;
|
|
486
|
+
let windowRefusals = 0;
|
|
487
|
+
let windowEvaluations = 0;
|
|
466
488
|
let readings = 0;
|
|
467
489
|
export function armPoolBudgetForTest() {
|
|
468
|
-
return { spanCount: poolSpanCount, coveredPositions, headScreenHits, fullYCalls, armCount: poolArmCount, boundaryArmCount: poolBoundaryArmCount, forensicLevels, forensicRuns, forensicSkips, urlSegmentRuns, urlSegmentReplays, ssh2ScanBytes, mappedUnits, readings, coverageVisits, rowVisits };
|
|
490
|
+
return { spanCount: poolSpanCount, coveredPositions, headScreenHits, fullYCalls, armCount: poolArmCount, boundaryArmCount: poolBoundaryArmCount, forensicLevels, forensicRuns, forensicSkips, urlSegmentRuns, urlSegmentReplays, ssh2ScanBytes, mappedUnits, readings, coverageVisits, rowVisits, scannedUnits, windowAdvances, windowRefusals, windowEvaluations };
|
|
469
491
|
}
|
|
470
492
|
export function resetArmPoolBudgetForTest() {
|
|
471
493
|
poolSpanCount = 0;
|
|
@@ -482,6 +504,10 @@ export function resetArmPoolBudgetForTest() {
|
|
|
482
504
|
readings = 0;
|
|
483
505
|
coverageVisits = 0;
|
|
484
506
|
rowVisits = 0;
|
|
507
|
+
scannedUnits = 0;
|
|
508
|
+
windowAdvances = 0;
|
|
509
|
+
windowRefusals = 0;
|
|
510
|
+
windowEvaluations = 0;
|
|
485
511
|
}
|
|
486
512
|
const unitBit = (t, c) => (t[c >>> 5] >>> (c & 31)) & 1;
|
|
487
513
|
let codeUnitDomain;
|
|
@@ -3169,7 +3195,7 @@ function anchorHeadAt(scan, text, op, json) {
|
|
|
3169
3195
|
if (i < 0)
|
|
3170
3196
|
return undefined;
|
|
3171
3197
|
const c = text.charCodeAt(i);
|
|
3172
|
-
if (c
|
|
3198
|
+
if (isInlineSpaceUnit(c)) {
|
|
3173
3199
|
i -= 1;
|
|
3174
3200
|
spacedLeft = true;
|
|
3175
3201
|
anchoredScanUnits += 1;
|
|
@@ -3237,7 +3263,7 @@ function anchorHeadAt(scan, text, op, json) {
|
|
|
3237
3263
|
let listSep = 0;
|
|
3238
3264
|
if (!afterEscape) {
|
|
3239
3265
|
let k = before;
|
|
3240
|
-
while (k >= 0 && (text.charCodeAt(k)
|
|
3266
|
+
while (k >= 0 && isInlineSpaceUnit(text.charCodeAt(k)))
|
|
3241
3267
|
k -= 1;
|
|
3242
3268
|
anchoredScanUnits += before - k;
|
|
3243
3269
|
const left = k >= 0 ? text.charCodeAt(k) : 0;
|
|
@@ -3695,6 +3721,7 @@ function collectPpkContainerSpans(scan, text, eat, span, isHead) {
|
|
|
3695
3721
|
if (last >= 0 && isOpenHead(last) && !lineHeld(scan, last, text.length, readingKey(fam, last)))
|
|
3696
3722
|
span(last, text.length, last, NOT_CLOSED, true);
|
|
3697
3723
|
}
|
|
3724
|
+
const NO_GAP = (_unit) => false;
|
|
3698
3725
|
const armPool = (() => {
|
|
3699
3726
|
const spanRepl = (token) => {
|
|
3700
3727
|
const body = token.startsWith("$1") ? token.slice(2) : token;
|
|
@@ -3739,9 +3766,10 @@ const armPool = (() => {
|
|
|
3739
3766
|
headFirst: boundary === undefined ? undefined : headFirst,
|
|
3740
3767
|
family: "prose",
|
|
3741
3768
|
reach: regexDecisionReach(re, LEXICAL_KEYWORD_ARMS.has(re)),
|
|
3769
|
+
prior: { kind: "bounded", gap: keywordTail === undefined ? NO_GAP : (c) => keywordTail.isSeparator(String.fromCharCode(c)) },
|
|
3742
3770
|
};
|
|
3743
3771
|
};
|
|
3744
|
-
const scannerMember = (token, order, arm, reach, family) => {
|
|
3772
|
+
const scannerMember = (token, order, arm, reach, family, prior) => {
|
|
3745
3773
|
const repl = spanRepl(token);
|
|
3746
3774
|
const rank = ARM_RANK.structural;
|
|
3747
3775
|
return {
|
|
@@ -3756,9 +3784,10 @@ const armPool = (() => {
|
|
|
3756
3784
|
headFirst: undefined,
|
|
3757
3785
|
family,
|
|
3758
3786
|
reach,
|
|
3787
|
+
prior,
|
|
3759
3788
|
};
|
|
3760
3789
|
};
|
|
3761
|
-
const forensicMember = (token, order, arm, reach, family, rank = ARM_RANK.structural, lexical = false) => {
|
|
3790
|
+
const forensicMember = (token, order, arm, reach, family, prior, rank = ARM_RANK.structural, lexical = false) => {
|
|
3762
3791
|
const repl = spanRepl(token);
|
|
3763
3792
|
return {
|
|
3764
3793
|
token,
|
|
@@ -3773,26 +3802,27 @@ const armPool = (() => {
|
|
|
3773
3802
|
family,
|
|
3774
3803
|
repl,
|
|
3775
3804
|
reach,
|
|
3805
|
+
prior,
|
|
3776
3806
|
};
|
|
3777
3807
|
};
|
|
3778
3808
|
const structuralScannerMember = (row, order) => {
|
|
3779
3809
|
if (row === PEM_FENCE_PATTERN)
|
|
3780
|
-
return forensicMember(row[1], order, (scan, text, _json, _eat, span, isHead) => collectPemFenceSpans(scan, text, span, isHead), PEM_FENCE_OPEN.length, "pem-armor");
|
|
3810
|
+
return forensicMember(row[1], order, (scan, text, _json, _eat, span, isHead) => collectPemFenceSpans(scan, text, span, isHead), PEM_FENCE_OPEN.length, "pem-armor", { kind: "anchored", anchors: [PEM_FENCE_OPEN, PEM_END_OPEN] });
|
|
3781
3811
|
if (row === SSH2_FENCE_PATTERN)
|
|
3782
|
-
return forensicMember(row[1], order, (scan, text, json, _eat, span, isHead) => collectSsh2FenceSpans(scan, text, json, span, isHead), SSH2_FENCE_OPEN.length, "ssh2-armor");
|
|
3812
|
+
return forensicMember(row[1], order, (scan, text, json, _eat, span, isHead) => collectSsh2FenceSpans(scan, text, json, span, isHead), SSH2_FENCE_OPEN.length, "ssh2-armor", { kind: "anchored", anchors: [SSH2_FENCE_OPEN, SSH2_FENCE_CLOSE] });
|
|
3783
3813
|
if (row === URL_CREDENTIAL_PATTERN)
|
|
3784
|
-
return forensicMember(row[1], order, (scan, text, json, _eat, span, isHead) => collectUrlCredentialSpans(scan, text, json, span, isHead), "://".length, "url");
|
|
3814
|
+
return forensicMember(row[1], order, (scan, text, json, _eat, span, isHead) => collectUrlCredentialSpans(scan, text, json, span, isHead), "://".length, "url", { kind: "bounded", gap: NO_GAP });
|
|
3785
3815
|
if (row === JSON_SECRET_FIELD_PATTERN)
|
|
3786
|
-
return scannerMember(row[1], order, (text, _json, _eat, span, isHead) => collectJsonSecretFieldSpans(text, span, isHead), JSON_FIELD_BACK, "json-field");
|
|
3816
|
+
return scannerMember(row[1], order, (text, _json, _eat, span, isHead) => collectJsonSecretFieldSpans(text, span, isHead), JSON_FIELD_BACK, "json-field", { kind: "bounded", gap: isRegexSpace });
|
|
3787
3817
|
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)`);
|
|
3788
3818
|
};
|
|
3789
3819
|
const members = [];
|
|
3790
3820
|
for (const row of STRUCTURAL_SECRET_PATTERNS)
|
|
3791
3821
|
members.push(structuralScannerMember(row, members.length));
|
|
3792
|
-
members.push(forensicMember(PPK_CONTAINER_TOKEN, members.length, (scan, text, _json, eat, span, isHead) => collectPpkContainerSpans(scan, text, eat, span, isHead), PPK_FILE_HEAD.length, "putty-ppk"));
|
|
3793
|
-
members.push(forensicMember(PEM_FRAGMENT_TOKEN, members.length, (scan, text, json, _eat, span, isHead) => collectPemFragmentSpans(scan, text, json, span, isHead), PEM_FENCE_OPEN.length, "pem-armor"));
|
|
3794
|
-
members.push(forensicMember(HEADLESS_PPK_TOKEN, members.length, (scan, text, _json, _eat, span, isHead) => collectHeadlessPpkSpans(scan, text, span, isHead), PPK_LINES_FIELD.length, "putty-ppk"));
|
|
3795
|
-
members.push(forensicMember(ANCHORED_VALUE_TOKEN, members.length, (scan, text, json, _eat, span, isHead) => collectAnchoredValueSpans(scan, text, json, span, isHead), Math.max(BEARER_VALUE_MIN, ABSENT_OR_BOOLEAN_MAX + 1, STRONG_AUTH_SCHEME_MAX + 1), "anchor", ARM_RANK.keyword, true));
|
|
3822
|
+
members.push(forensicMember(PPK_CONTAINER_TOKEN, members.length, (scan, text, _json, eat, span, isHead) => collectPpkContainerSpans(scan, text, eat, span, isHead), PPK_FILE_HEAD.length, "putty-ppk", { kind: "anchored", anchors: [PPK_FILE_HEAD] }));
|
|
3823
|
+
members.push(forensicMember(PEM_FRAGMENT_TOKEN, members.length, (scan, text, json, _eat, span, isHead) => collectPemFragmentSpans(scan, text, json, span, isHead), PEM_FENCE_OPEN.length, "pem-armor", { kind: "anchored", anchors: [PEM_FENCE_OPEN, PEM_END_OPEN] }));
|
|
3824
|
+
members.push(forensicMember(HEADLESS_PPK_TOKEN, members.length, (scan, text, _json, _eat, span, isHead) => collectHeadlessPpkSpans(scan, text, span, isHead), PPK_LINES_FIELD.length, "putty-ppk", { kind: "anchored", anchors: [PPK_LINES_FIELD] }));
|
|
3825
|
+
members.push(forensicMember(ANCHORED_VALUE_TOKEN, members.length, (scan, text, json, _eat, span, isHead) => collectAnchoredValueSpans(scan, text, json, span, isHead), Math.max(BEARER_VALUE_MIN, ABSENT_OR_BOOLEAN_MAX + 1, STRONG_AUTH_SCHEME_MAX + 1), "anchor", { kind: "bounded", gap: (c) => isInlineSpaceUnit(c) || isSchemeDelimiterCode(c) }, ARM_RANK.keyword, true));
|
|
3796
3826
|
for (const row of KEYWORD_SECRET_PATTERNS)
|
|
3797
3827
|
members.push(regexMember(row, ARM_RANK.keyword, members.length));
|
|
3798
3828
|
for (const row of SHAPE_SECRET_PATTERNS)
|
|
@@ -3963,7 +3993,7 @@ const armPool = (() => {
|
|
|
3963
3993
|
}
|
|
3964
3994
|
return { hits: [...byArmedToken.values()].map(({ token, arm, count }) => ({ label: labelOfToken(token), count, arm })), kept: stamped };
|
|
3965
3995
|
};
|
|
3966
|
-
return Object.assign(run, { reaches, pendingTailUnits: Math.max(...reaches.map((r) => r.reach)) });
|
|
3996
|
+
return Object.assign(run, { reaches, pendingTailUnits: Math.max(...reaches.map((r) => r.reach)), priors: members.map((m) => m.prior) });
|
|
3967
3997
|
})();
|
|
3968
3998
|
let onRedaction;
|
|
3969
3999
|
export function setRedactionObserver(fn) {
|
|
@@ -3996,70 +4026,6 @@ function invisibleClassesOf(layout) {
|
|
|
3996
4026
|
}
|
|
3997
4027
|
}
|
|
3998
4028
|
}
|
|
3999
|
-
function trailingPartialEscapeStart(s) {
|
|
4000
|
-
const n = s.length;
|
|
4001
|
-
const runStart = (end) => {
|
|
4002
|
-
let k = end;
|
|
4003
|
-
while (k > 0 && s.charCodeAt(k - 1) === CH_BACKSLASH)
|
|
4004
|
-
k -= 1;
|
|
4005
|
-
return k;
|
|
4006
|
-
};
|
|
4007
|
-
const highSurrogateEscapeStartBefore = (end) => {
|
|
4008
|
-
let j = end;
|
|
4009
|
-
let value = -1;
|
|
4010
|
-
let bodyStart = -1;
|
|
4011
|
-
if (j >= 1 && s.charCodeAt(j - 1) === CH_RBRACE) {
|
|
4012
|
-
let k = j - 1;
|
|
4013
|
-
while (k > 0 && hexDigitValue(s.charCodeAt(k - 1)) >= 0)
|
|
4014
|
-
k -= 1;
|
|
4015
|
-
if (k >= 2 && s.charCodeAt(k - 1) === CH_LBRACE && s.charCodeAt(k - 2) === CH_u && j - 1 - k >= 1) {
|
|
4016
|
-
value = readHexRunValue(s, k, j - 1);
|
|
4017
|
-
bodyStart = k - 2;
|
|
4018
|
-
}
|
|
4019
|
-
}
|
|
4020
|
-
else if (j >= 5 && s.charCodeAt(j - 5) === CH_u) {
|
|
4021
|
-
value = readHexRun(s, j - 4, 4);
|
|
4022
|
-
bodyStart = j - 5;
|
|
4023
|
-
}
|
|
4024
|
-
if (value < 0xd800 || value > 0xdbff)
|
|
4025
|
-
return -1;
|
|
4026
|
-
const k = runStart(bodyStart);
|
|
4027
|
-
return k < bodyStart ? k : -1;
|
|
4028
|
-
};
|
|
4029
|
-
let j = n;
|
|
4030
|
-
while (j > 0 && hexDigitValue(s.charCodeAt(j - 1)) >= 0)
|
|
4031
|
-
j -= 1;
|
|
4032
|
-
const hexRun = n - j;
|
|
4033
|
-
let element = -1;
|
|
4034
|
-
if (j >= 2 && s.charCodeAt(j - 1) === CH_LBRACE && s.charCodeAt(j - 2) === CH_u)
|
|
4035
|
-
element = j - 2;
|
|
4036
|
-
else if (j >= 1 && s.charCodeAt(j - 1) === CH_u && hexRun <= 3)
|
|
4037
|
-
element = j - 1;
|
|
4038
|
-
else if (j >= 1 && s.charCodeAt(j - 1) === CH_x && hexRun <= 1)
|
|
4039
|
-
element = j - 1;
|
|
4040
|
-
else if (hexRun === 0 && n >= 1 && s.charCodeAt(n - 1) === CH_BACKSLASH)
|
|
4041
|
-
element = n;
|
|
4042
|
-
if (element >= 0) {
|
|
4043
|
-
const k = runStart(element);
|
|
4044
|
-
if (k >= element)
|
|
4045
|
-
return -1;
|
|
4046
|
-
const high = highSurrogateEscapeStartBefore(k);
|
|
4047
|
-
return high >= 0 ? high : k;
|
|
4048
|
-
}
|
|
4049
|
-
return highSurrogateEscapeStartBefore(n);
|
|
4050
|
-
}
|
|
4051
|
-
function readHexRunValue(s, from, to) {
|
|
4052
|
-
let v = 0;
|
|
4053
|
-
for (let k = from; k < to; k++) {
|
|
4054
|
-
const d = hexDigitValue(s.charCodeAt(k));
|
|
4055
|
-
if (d < 0)
|
|
4056
|
-
return -1;
|
|
4057
|
-
v = v * 16 + d;
|
|
4058
|
-
if (v > 0x10ffff)
|
|
4059
|
-
return -1;
|
|
4060
|
-
}
|
|
4061
|
-
return v;
|
|
4062
|
-
}
|
|
4063
4029
|
function shortestReading(text, layout) {
|
|
4064
4030
|
const r = readShortest(text, layout);
|
|
4065
4031
|
return { visible: r.visible, partial: r.partial };
|
|
@@ -4068,8 +4034,8 @@ function readShortest(text, layout) {
|
|
|
4068
4034
|
const raw = collectRawControlCuts(text, invisibleClassesOf(layout));
|
|
4069
4035
|
const rfold = deleteInvisibleCharacters(text, raw, raw.present);
|
|
4070
4036
|
const normalized = normalizeForRedaction(rfold.text);
|
|
4071
|
-
const open = trailingPartialEscapeStart(normalized);
|
|
4072
4037
|
const spelled = collectSpelledCuts(normalized);
|
|
4038
|
+
const open = spelled.open;
|
|
4073
4039
|
const sfold = deleteInvisibleCharacters(normalized, spelled, spelled.present);
|
|
4074
4040
|
const layers = [{ fold: sfold }, { input: rfold.text, view: normalized }, { fold: rfold }];
|
|
4075
4041
|
return open < 0 ? { visible: sfold.text.length, partial: false, layers } : { visible: sfold.text.length - (normalized.length - open), partial: true, layers };
|
|
@@ -4094,7 +4060,8 @@ export function readingHeadEnd(text, layout, units, from = 0) {
|
|
|
4094
4060
|
}
|
|
4095
4061
|
return text.length;
|
|
4096
4062
|
}
|
|
4097
|
-
function runRedaction(text, observe, json, wantHits, scope, layout, extent) {
|
|
4063
|
+
function runRedaction(text, observe, json, wantHits, scope, layout, extent, observeFrom = 0) {
|
|
4064
|
+
scannedUnits += text.length;
|
|
4098
4065
|
const wantCount = wantHits || (onRedaction !== undefined && observe);
|
|
4099
4066
|
const eat = observe ? { from: -1, len: 0, capFrom: -1, capLen: 0 } : undefined;
|
|
4100
4067
|
let spans = [];
|
|
@@ -4140,7 +4107,7 @@ function runRedaction(text, observe, json, wantHits, scope, layout, extent) {
|
|
|
4140
4107
|
if (eat !== undefined && eat.capFrom >= 0)
|
|
4141
4108
|
recordFailOpen("server.redact.forensic-level-cap", `from=${eat.capFrom} len=${eat.capLen}`);
|
|
4142
4109
|
if (observe)
|
|
4143
|
-
reportHits(hits);
|
|
4110
|
+
reportHits(observeFrom === 0 ? hits : hitsOfSpans(spans.filter((sp) => sp.end > observeFrom)));
|
|
4144
4111
|
return { text: applySpans(text, spans).out, hits, spans };
|
|
4145
4112
|
}
|
|
4146
4113
|
function hitsOfSpans(spans) {
|
|
@@ -4175,7 +4142,12 @@ function createViewToOriginal(layers) {
|
|
|
4175
4142
|
};
|
|
4176
4143
|
}
|
|
4177
4144
|
export function redactSecrets(text, opts) {
|
|
4178
|
-
|
|
4145
|
+
const before = opts?.before ?? "";
|
|
4146
|
+
if (before === "")
|
|
4147
|
+
return runRedaction(text, opts?.observe ?? true, opts?.json === true, false, "free-text", "multi-line", WHOLE_EXTENT).text;
|
|
4148
|
+
const whole = before + text;
|
|
4149
|
+
const r = runRedaction(whole, opts?.observe ?? true, opts?.json === true, false, "free-text", "multi-line", WHOLE_EXTENT, before.length);
|
|
4150
|
+
return renderRange(whole, r.spans, before.length, whole.length);
|
|
4179
4151
|
}
|
|
4180
4152
|
export function redactSecretsWithArms(text, opts) {
|
|
4181
4153
|
const r = runRedaction(text, opts?.observe ?? true, opts?.json === true, true, "free-text", "multi-line", WHOLE_EXTENT);
|
|
@@ -4220,56 +4192,219 @@ function renderRange(text, spans, from, until) {
|
|
|
4220
4192
|
}
|
|
4221
4193
|
const STREAM_RESCAN_MIN = 64;
|
|
4222
4194
|
const streamRescanStep = (n) => Math.max(STREAM_RESCAN_MIN, n >>> 5);
|
|
4195
|
+
const STREAM_WINDOW_HORIZON = 2;
|
|
4196
|
+
const STREAM_WINDOW_MIN = 2048;
|
|
4197
|
+
function deriveStreamWindowAlphabets(priors) {
|
|
4198
|
+
const blank = new Uint8Array(0x10000);
|
|
4199
|
+
const gaps = new Set();
|
|
4200
|
+
const containerDelimiters = [];
|
|
4201
|
+
for (const prior of priors) {
|
|
4202
|
+
switch (prior.kind) {
|
|
4203
|
+
case "bounded":
|
|
4204
|
+
if (prior.gap !== NO_GAP)
|
|
4205
|
+
gaps.add(prior.gap);
|
|
4206
|
+
break;
|
|
4207
|
+
case "anchored":
|
|
4208
|
+
if (prior.anchors.length === 0)
|
|
4209
|
+
throw new Error("redact: an anchored arm declares no anchor literal — the stream window cannot tell where its reading of prior text starts");
|
|
4210
|
+
for (const anchor of prior.anchors) {
|
|
4211
|
+
if (anchor.length === 0)
|
|
4212
|
+
throw new Error("redact: an anchored arm declares an empty anchor literal");
|
|
4213
|
+
for (let i = 0; i < anchor.length; i++) {
|
|
4214
|
+
const c = anchor.charCodeAt(i);
|
|
4215
|
+
if (INVISIBLE_UNIT_CLASS[c] !== 0 || FORMAT_CODEPOINT_TABLE[c] === 1 || (c & 0xf800) === 0xd800 || c === CH_BACKSLASH) {
|
|
4216
|
+
throw new Error(`redact: anchor literal ${JSON.stringify(anchor)} holds a unit some reading deletes or reads as an escape (index ${i}) — its presence is not monotone under deletion, so the stream window's delimiter check on the most-deleted reading would be incomplete`);
|
|
4217
|
+
}
|
|
4218
|
+
}
|
|
4219
|
+
if (isEscapeBodyUnit(anchor.charCodeAt(0))) {
|
|
4220
|
+
throw new Error(`redact: anchor literal ${JSON.stringify(anchor)} starts with a unit an escape spelling's body can read — an escape begun just before it deletes that unit in the most-deleted reading, so its presence is not monotone under deletion and the stream window's delimiter check would be incomplete`);
|
|
4221
|
+
}
|
|
4222
|
+
if (!containerDelimiters.includes(anchor))
|
|
4223
|
+
containerDelimiters.push(anchor);
|
|
4224
|
+
}
|
|
4225
|
+
break;
|
|
4226
|
+
default: {
|
|
4227
|
+
const unhandled = prior;
|
|
4228
|
+
throw new Error(`redact: unhandled prior reading ${JSON.stringify(unhandled)}`);
|
|
4229
|
+
}
|
|
4230
|
+
}
|
|
4231
|
+
}
|
|
4232
|
+
for (const gap of gaps)
|
|
4233
|
+
for (let u = 0; u < 0x10000; u++)
|
|
4234
|
+
if (gap(u))
|
|
4235
|
+
blank[u] = 1;
|
|
4236
|
+
return { blank, containerDelimiters };
|
|
4237
|
+
}
|
|
4238
|
+
const STREAM_WINDOW_ALPHABETS = deriveStreamWindowAlphabets(armPool.priors);
|
|
4239
|
+
export function streamWindowAlphabetsForTest(extra = []) {
|
|
4240
|
+
const a = extra.length === 0 ? STREAM_WINDOW_ALPHABETS : deriveStreamWindowAlphabets([...armPool.priors, ...extra]);
|
|
4241
|
+
return { isBlank: (unit) => a.blank[unit] === 1, containerDelimiters: a.containerDelimiters };
|
|
4242
|
+
}
|
|
4243
|
+
export function escapeGrammarForTest() {
|
|
4244
|
+
return {
|
|
4245
|
+
read: (s) => {
|
|
4246
|
+
let body = 0;
|
|
4247
|
+
while (body < s.length && s.charCodeAt(body) === CH_BACKSLASH)
|
|
4248
|
+
body += 1;
|
|
4249
|
+
const out = { end: -1 };
|
|
4250
|
+
const value = readSpelledCodePoint(s, body, out);
|
|
4251
|
+
return { value, end: value >= 0 ? out.end : -1 };
|
|
4252
|
+
},
|
|
4253
|
+
isBodyUnit: isEscapeBodyUnit,
|
|
4254
|
+
};
|
|
4255
|
+
}
|
|
4256
|
+
function windowAdvancePoint(text, units, alphabets = STREAM_WINDOW_ALPHABETS) {
|
|
4257
|
+
const r = readShortest(text, "multi-line");
|
|
4258
|
+
if (r.partial)
|
|
4259
|
+
return -1;
|
|
4260
|
+
const view = r.layers[0].fold.text;
|
|
4261
|
+
let k = r.visible;
|
|
4262
|
+
let n = 0;
|
|
4263
|
+
while (k > 0 && n < units) {
|
|
4264
|
+
k -= 1;
|
|
4265
|
+
if (alphabets.blank[view.charCodeAt(k)] !== 1)
|
|
4266
|
+
n += 1;
|
|
4267
|
+
}
|
|
4268
|
+
if (n < units)
|
|
4269
|
+
return 0;
|
|
4270
|
+
for (const d of alphabets.containerDelimiters) {
|
|
4271
|
+
const at = view.indexOf(d);
|
|
4272
|
+
if (at >= 0 && at < k)
|
|
4273
|
+
k = at;
|
|
4274
|
+
}
|
|
4275
|
+
const at = createViewToOriginal(r.layers).start(k);
|
|
4276
|
+
return at <= 0 ? 0 : text.lastIndexOf("\n", at - 1) + 1;
|
|
4277
|
+
}
|
|
4278
|
+
function windowIndependentAt(text, q) {
|
|
4279
|
+
const full = runRedaction(text, false, false, false, "free-text", "multi-line", { kind: "head", emitEnd: text.length }).spans;
|
|
4280
|
+
if (full.some((s) => s.start < q && s.end > q))
|
|
4281
|
+
return false;
|
|
4282
|
+
const after = full.filter((s) => s.start >= q);
|
|
4283
|
+
const tail = text.slice(q);
|
|
4284
|
+
const alone = runRedaction(tail, false, false, false, "free-text", "multi-line", { kind: "head", emitEnd: tail.length }).spans;
|
|
4285
|
+
if (after.length !== alone.length)
|
|
4286
|
+
return false;
|
|
4287
|
+
return after.every((a, i) => a.start - q === alone[i].start && a.end - q === alone[i].end && a.repl === alone[i].repl);
|
|
4288
|
+
}
|
|
4289
|
+
export function windowAdvancePointForTest(text, units, extra = []) {
|
|
4290
|
+
return windowAdvancePoint(text, units, extra.length === 0 ? STREAM_WINDOW_ALPHABETS : deriveStreamWindowAlphabets([...armPool.priors, ...extra]));
|
|
4291
|
+
}
|
|
4292
|
+
export function windowIndependentAtForTest(text, q) {
|
|
4293
|
+
return windowIndependentAt(text, q);
|
|
4294
|
+
}
|
|
4223
4295
|
export function createRedactionStream() {
|
|
4224
4296
|
return openRedactionStream(PENDING_TAIL_UNITS);
|
|
4225
4297
|
}
|
|
4298
|
+
export function createRedactionStreamWithoutWindowForTest() {
|
|
4299
|
+
return openRedactionStream(PENDING_TAIL_UNITS, false);
|
|
4300
|
+
}
|
|
4301
|
+
export function createRedactionStreamWithCertifierForTest(certify) {
|
|
4302
|
+
return openRedactionStream(PENDING_TAIL_UNITS, true, certify);
|
|
4303
|
+
}
|
|
4226
4304
|
export function createRedactionStreamWithTailForTest(units) {
|
|
4227
4305
|
return openRedactionStream(units);
|
|
4228
4306
|
}
|
|
4229
|
-
function openRedactionStream(pendingTail) {
|
|
4230
|
-
let
|
|
4307
|
+
function openRedactionStream(pendingTail, windowed = true, certify = windowIndependentAt) {
|
|
4308
|
+
let past = [];
|
|
4309
|
+
let total = 0;
|
|
4310
|
+
let win = "";
|
|
4311
|
+
let base = 0;
|
|
4312
|
+
let blockFrom = 0;
|
|
4231
4313
|
let released = 0;
|
|
4232
4314
|
let deliveredFrom = 0;
|
|
4233
4315
|
let sent = "";
|
|
4234
4316
|
let scannedAt = 0;
|
|
4317
|
+
let evaluatedAt = -1;
|
|
4318
|
+
let retryAt = 0;
|
|
4319
|
+
const whole = () => (past.length === 0 ? win : past.join("") + win);
|
|
4320
|
+
const advanceWindow = () => {
|
|
4321
|
+
if (!windowed || win.length < Math.max(STREAM_WINDOW_MIN, retryAt) || released < total || total === evaluatedAt)
|
|
4322
|
+
return;
|
|
4323
|
+
evaluatedAt = total;
|
|
4324
|
+
retryAt = win.length + (win.length >> 1);
|
|
4325
|
+
windowEvaluations += 1;
|
|
4326
|
+
const q = windowAdvancePoint(win, STREAM_WINDOW_HORIZON * pendingTail);
|
|
4327
|
+
if (q < 0) {
|
|
4328
|
+
retryAt = 0;
|
|
4329
|
+
return;
|
|
4330
|
+
}
|
|
4331
|
+
if (q === 0 || q * 2 < win.length)
|
|
4332
|
+
return;
|
|
4333
|
+
if (!certify(win, q)) {
|
|
4334
|
+
windowRefusals += 1;
|
|
4335
|
+
return;
|
|
4336
|
+
}
|
|
4337
|
+
past.push(win.slice(0, q));
|
|
4338
|
+
win = win.slice(q);
|
|
4339
|
+
base += q;
|
|
4340
|
+
retryAt = 0;
|
|
4341
|
+
windowAdvances += 1;
|
|
4342
|
+
};
|
|
4235
4343
|
return {
|
|
4236
4344
|
feed(chunk) {
|
|
4237
|
-
|
|
4345
|
+
win += chunk;
|
|
4346
|
+
total += chunk.length;
|
|
4238
4347
|
},
|
|
4239
4348
|
takeSettled() {
|
|
4240
|
-
if (
|
|
4349
|
+
if (total - released <= pendingTail)
|
|
4241
4350
|
return "";
|
|
4242
|
-
if (
|
|
4351
|
+
if (total - scannedAt < streamRescanStep(total))
|
|
4243
4352
|
return "";
|
|
4244
|
-
scannedAt =
|
|
4245
|
-
const emitEnd = pendingTailStart(
|
|
4246
|
-
|
|
4353
|
+
scannedAt = total;
|
|
4354
|
+
const emitEnd = pendingTailStart(win, pendingTail);
|
|
4355
|
+
const from = released - base;
|
|
4356
|
+
if (emitEnd <= from)
|
|
4247
4357
|
return "";
|
|
4248
|
-
const spans = runRedaction(
|
|
4358
|
+
const spans = runRedaction(win, false, false, false, "free-text", "multi-line", { kind: "head", emitEnd }).spans;
|
|
4249
4359
|
let cut = emitEnd;
|
|
4250
4360
|
for (const s of spans)
|
|
4251
4361
|
if (s.start < cut && s.end > cut)
|
|
4252
4362
|
cut = s.start;
|
|
4253
|
-
if (cut > 0 && cut <
|
|
4363
|
+
if (cut > 0 && cut < win.length && (win.charCodeAt(cut - 1) & 0xfc00) === 0xd800 && (win.charCodeAt(cut) & 0xfc00) === 0xdc00)
|
|
4254
4364
|
cut -= 1;
|
|
4255
|
-
if (cut <=
|
|
4365
|
+
if (cut <= from)
|
|
4256
4366
|
return "";
|
|
4257
|
-
const out = renderRange(
|
|
4258
|
-
released = cut;
|
|
4367
|
+
const out = renderRange(win, spans, from, cut);
|
|
4368
|
+
released = base + cut;
|
|
4369
|
+
sent += out;
|
|
4370
|
+
return out;
|
|
4371
|
+
},
|
|
4372
|
+
takeAll() {
|
|
4373
|
+
if (released === total)
|
|
4374
|
+
return "";
|
|
4375
|
+
const spans = runRedaction(win, false, false, false, "free-text", "multi-line", WHOLE_EXTENT).spans;
|
|
4376
|
+
const out = renderRange(win, spans, released - base, win.length);
|
|
4377
|
+
released = scannedAt = total;
|
|
4259
4378
|
sent += out;
|
|
4260
4379
|
return out;
|
|
4261
4380
|
},
|
|
4262
4381
|
flush() {
|
|
4382
|
+
if (deliveredFrom === total)
|
|
4383
|
+
return { rest: "", diverged: false };
|
|
4384
|
+
const buf = whole();
|
|
4263
4385
|
const spans = runRedaction(buf, false, false, false, "free-text", "multi-line", WHOLE_EXTENT).spans;
|
|
4264
4386
|
const split = released > deliveredFrom && spans.some((s) => s.start < released && s.end > released);
|
|
4265
4387
|
return { rest: renderRange(buf, spans, released, buf.length), diverged: split || renderRange(buf, spans, deliveredFrom, released) !== sent };
|
|
4266
4388
|
},
|
|
4267
4389
|
skip() {
|
|
4268
|
-
released = deliveredFrom = scannedAt =
|
|
4390
|
+
released = deliveredFrom = scannedAt = total;
|
|
4269
4391
|
sent = "";
|
|
4270
4392
|
},
|
|
4393
|
+
endBlock() {
|
|
4394
|
+
blockFrom = total;
|
|
4395
|
+
advanceWindow();
|
|
4396
|
+
},
|
|
4397
|
+
blockContext() {
|
|
4398
|
+
return win.slice(0, blockFrom - base);
|
|
4399
|
+
},
|
|
4400
|
+
blockBytes() {
|
|
4401
|
+
return win.slice(blockFrom - base);
|
|
4402
|
+
},
|
|
4271
4403
|
get fed() {
|
|
4272
|
-
return
|
|
4404
|
+
return whole();
|
|
4405
|
+
},
|
|
4406
|
+
get length() {
|
|
4407
|
+
return total;
|
|
4273
4408
|
},
|
|
4274
4409
|
};
|
|
4275
4410
|
}
|