@sema-agent/server 7.75.1 → 7.75.2
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/dist/trace/redact.d.ts +2 -0
- package/dist/trace/redact.js +113 -55
- package/package.json +2 -2
package/dist/trace/redact.d.ts
CHANGED
|
@@ -30,6 +30,8 @@
|
|
|
30
30
|
*
|
|
31
31
|
* 两组臂,顺序即优先序:{@link FULL_SECRET_PATTERNS}(完整形)在前,{@link FRAGMENT_SECRET_PATTERNS}
|
|
32
32
|
* ([ref] 前缀残片,上游先截后脱的产物 ⇒ 带 `:partial` 后缀的 token)在后。
|
|
33
|
+
* ⚠️ **一个例外,自 7.75.2**:完整臂里的**裸形段**内部没有优先序 —— 该段全臂一次收候选跨度、解重叠、一次替换
|
|
34
|
+
* ({@link shapeSectionRule}),段内臂序对输出无语义。段**之间**的先后照旧是契约。
|
|
33
35
|
*/
|
|
34
36
|
/** 本模块替换 token 的**闭形**正则源(`«redacted»` / `«redacted:aws-key»` / `«redacted:gh-token:partial»` …):
|
|
35
37
|
* URL-userinfo 臂用它把「用户名位上已经是本模块 token」整个当一个用户名单元吃掉。模块加载时对
|
package/dist/trace/redact.js
CHANGED
|
@@ -207,32 +207,65 @@ function forgetEscapedSeparatorTails() {
|
|
|
207
207
|
}
|
|
208
208
|
const VALUE_ARM_STICKY = new Map();
|
|
209
209
|
const VALUE_ARM_BODIES = [];
|
|
210
|
-
const valueArm = (prefixSrc, valueClass, min) => {
|
|
210
|
+
const valueArm = (prefixSrc, valueClass, min, evidence) => {
|
|
211
211
|
const body = `${prefixSrc}${twoStrongAhead(valueClass)}${atLeast(valueClass, min)}`;
|
|
212
|
-
const g = new RegExp(`(?<!${WORD_INTERIOR_CLASS})${body}`, "g");
|
|
213
|
-
VALUE_ARM_STICKY.set(g, new RegExp(body, "y"));
|
|
214
212
|
VALUE_ARM_BODIES.push(body);
|
|
215
|
-
|
|
213
|
+
switch (evidence) {
|
|
214
|
+
case "self-evident":
|
|
215
|
+
return new RegExp(body, "g");
|
|
216
|
+
case "needs-left-boundary": {
|
|
217
|
+
const g = new RegExp(`(?<!${WORD_INTERIOR_CLASS})${body}`, "g");
|
|
218
|
+
VALUE_ARM_STICKY.set(g, new RegExp(body, "y"));
|
|
219
|
+
return g;
|
|
220
|
+
}
|
|
221
|
+
default: {
|
|
222
|
+
const unhandled = evidence;
|
|
223
|
+
throw new Error(`redact: unhandled value-arm evidence ${String(unhandled)}`);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
216
226
|
};
|
|
217
227
|
let anyValueArm;
|
|
218
228
|
function anyValueArmRe() {
|
|
219
229
|
return (anyValueArm ??= new RegExp(`(?:${VALUE_ARM_BODIES.join("|")})`, "y"));
|
|
220
230
|
}
|
|
221
|
-
function
|
|
231
|
+
function collectRegexSpans(g, repl, order, text, out) {
|
|
232
|
+
g.lastIndex = 0;
|
|
233
|
+
for (let m = g.exec(text); m !== null; m = g.exec(text)) {
|
|
234
|
+
const end = m.index + m[0].length;
|
|
235
|
+
out.push({ start: m.index, end, repl, order });
|
|
236
|
+
g.lastIndex = end > m.index ? end : m.index + 1;
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
function resolveSpanOverlaps(spans) {
|
|
240
|
+
spans.sort((a, b) => a.start - b.start || b.end - a.end || a.order - b.order);
|
|
241
|
+
const kept = [];
|
|
242
|
+
let covered = 0;
|
|
243
|
+
for (const s of spans) {
|
|
244
|
+
if (s.end <= covered)
|
|
245
|
+
continue;
|
|
246
|
+
kept.push(s.start >= covered ? s : { ...s, start: covered });
|
|
247
|
+
covered = s.end;
|
|
248
|
+
}
|
|
249
|
+
return kept;
|
|
250
|
+
}
|
|
251
|
+
function applySpans(text, spans) {
|
|
252
|
+
if (spans.length === 0)
|
|
253
|
+
return { out: text, count: 0 };
|
|
254
|
+
const parts = [];
|
|
255
|
+
let copied = 0;
|
|
256
|
+
for (const s of spans) {
|
|
257
|
+
parts.push(text.slice(copied, s.start), s.repl);
|
|
258
|
+
copied = s.end;
|
|
259
|
+
}
|
|
260
|
+
parts.push(text.slice(copied));
|
|
261
|
+
return { out: parts.join(""), count: spans.length };
|
|
262
|
+
}
|
|
263
|
+
function collectValueArmSpans(g, y, repl, order, text, out) {
|
|
222
264
|
const tails = escapedSeparatorTails(text);
|
|
223
265
|
if (tails === undefined) {
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
let hits = 0;
|
|
227
|
-
const replaced = text.replace(g, () => {
|
|
228
|
-
hits += 1;
|
|
229
|
-
return repl;
|
|
230
|
-
});
|
|
231
|
-
return { out: replaced, count: hits };
|
|
266
|
+
collectRegexSpans(g, repl, order, text, out);
|
|
267
|
+
return;
|
|
232
268
|
}
|
|
233
|
-
let out;
|
|
234
|
-
let copied = 0;
|
|
235
|
-
let count = 0;
|
|
236
269
|
let pos = 0;
|
|
237
270
|
let ti = 0;
|
|
238
271
|
let bIdx = -1;
|
|
@@ -275,15 +308,14 @@ function runValueArm(g, y, repl, text, wantCount) {
|
|
|
275
308
|
}
|
|
276
309
|
else
|
|
277
310
|
break;
|
|
278
|
-
|
|
279
|
-
copied = end;
|
|
280
|
-
count += 1;
|
|
311
|
+
out.push({ start: idx, end, repl, order });
|
|
281
312
|
pos = end;
|
|
282
313
|
}
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
314
|
+
}
|
|
315
|
+
function runValueArm(g, y, repl, text) {
|
|
316
|
+
const spans = [];
|
|
317
|
+
collectValueArmSpans(g, y, repl, 0, text, spans);
|
|
318
|
+
return applySpans(text, spans);
|
|
287
319
|
}
|
|
288
320
|
const WORD_SEGMENT_CLASS = "[A-Za-z0-9._~+/-]";
|
|
289
321
|
const keywordArm = (keywordSrc, valueClass, min, tailSrc = "") => new RegExp(`(?:${keywordSrc}\\s+${atLeast(valueClass, min)}${tailSrc}` +
|
|
@@ -303,7 +335,6 @@ const KEYWORD_SECRET_PATTERNS = [
|
|
|
303
335
|
[keywordArm("[Tt]oken", "[A-Za-z0-9._~+/-]", 20), "token «redacted»"],
|
|
304
336
|
];
|
|
305
337
|
const SHAPE_SECRET_PATTERNS = [
|
|
306
|
-
[valueArm("sk-", "[A-Za-z0-9_-]", 16), "«redacted:key»"],
|
|
307
338
|
[new RegExp(String.raw `(?:ghp|gho|ghu|ghs|ghr)_${atLeast("[A-Za-z0-9]", 20)}`, "g"), "«redacted:gh-token»"],
|
|
308
339
|
[new RegExp(String.raw `github_pat_[A-Za-z0-9]{22}_${atLeast("[A-Za-z0-9]", 59)}`, "g"), "«redacted:gh-token»"],
|
|
309
340
|
[new RegExp(String.raw `xox[baprs]-${atLeast("[A-Za-z0-9-]", 10)}`, "g"), "«redacted:slack-token»"],
|
|
@@ -311,6 +342,7 @@ const SHAPE_SECRET_PATTERNS = [
|
|
|
311
342
|
[new RegExp(String.raw `ASIA${atLeast("[0-9A-Z]", 16)}`, "g"), "«redacted:aws-key»"],
|
|
312
343
|
[new RegExp(String.raw `(?:gl(?:pat|dt|rt|rtr|cbt|ptt|oas|ft|imt|agent|wt|soat|ffct)-${atLeast("[A-Za-z0-9_-]", 20)}|(?:sk|rk)_(?:live|test|org)_${atLeast("[A-Za-z0-9]", 24)}|pypi-${atLeast("[A-Za-z0-9_-]", 85)})`, "g"), "«redacted:key»"],
|
|
313
344
|
[new RegExp(String.raw `xapp-\d-${atLeast("[A-Za-z0-9-]", 20)}`, "g"), "«redacted:slack-token»"],
|
|
345
|
+
[valueArm("sk-", "[A-Za-z0-9_-]", 16, "self-evident"), "«redacted:key»"],
|
|
314
346
|
];
|
|
315
347
|
const FULL_SECRET_PATTERNS = [...STRUCTURAL_SECRET_PATTERNS, ...KEYWORD_SECRET_PATTERNS, ...SHAPE_SECRET_PATTERNS];
|
|
316
348
|
const PEM_HEADER_IDENT_MAX = 31;
|
|
@@ -688,14 +720,14 @@ function redactHeadlessPpkBodies(text) {
|
|
|
688
720
|
return count === 0 ? { out: text, count: 0 } : { out: out + text.slice(last), count };
|
|
689
721
|
}
|
|
690
722
|
const FRAGMENT_SECRET_PATTERNS = [
|
|
691
|
-
[valueArm("sk-", "[A-Za-z0-9_-]", 6), "«redacted:key:partial»"],
|
|
692
|
-
[valueArm("(?:ghp|gho|ghu|ghs|ghr)_", "[A-Za-z0-9]", 8), "«redacted:gh-token:partial»"],
|
|
723
|
+
[valueArm("sk-", "[A-Za-z0-9_-]", 6, "needs-left-boundary"), "«redacted:key:partial»"],
|
|
724
|
+
[valueArm("(?:ghp|gho|ghu|ghs|ghr)_", "[A-Za-z0-9]", 8, "needs-left-boundary"), "«redacted:gh-token:partial»"],
|
|
693
725
|
[new RegExp(String.raw `github_pat_${atLeast("[A-Za-z0-9_]", 8)}`, "g"), "«redacted:gh-token:partial»"],
|
|
694
|
-
[valueArm("xox[baprs]-", "[A-Za-z0-9-]", 4), "«redacted:slack-token:partial»"],
|
|
695
|
-
[valueArm("AKIA", "[0-9A-Z]", 6), "«redacted:aws-key:partial»"],
|
|
696
|
-
[valueArm("ASIA", "[0-9A-Z]", 8), "«redacted:aws-key:partial»"],
|
|
697
|
-
[valueArm("(?:gl(?:pat|dt|rt|rtr|cbt|ptt|oas|ft|imt|agent|wt|soat|ffct)-|(?:sk|rk)_(?:live|test|org)_|pypi-)", "[A-Za-z0-9_-]", 8), "«redacted:key:partial»"],
|
|
698
|
-
[valueArm("xapp-\\d-", "[A-Za-z0-9-]", 4), "«redacted:slack-token:partial»"],
|
|
726
|
+
[valueArm("xox[baprs]-", "[A-Za-z0-9-]", 4, "needs-left-boundary"), "«redacted:slack-token:partial»"],
|
|
727
|
+
[valueArm("AKIA", "[0-9A-Z]", 6, "needs-left-boundary"), "«redacted:aws-key:partial»"],
|
|
728
|
+
[valueArm("ASIA", "[0-9A-Z]", 8, "needs-left-boundary"), "«redacted:aws-key:partial»"],
|
|
729
|
+
[valueArm("(?:gl(?:pat|dt|rt|rtr|cbt|ptt|oas|ft|imt|agent|wt|soat|ffct)-|(?:sk|rk)_(?:live|test|org)_|pypi-)", "[A-Za-z0-9_-]", 8, "needs-left-boundary"), "«redacted:key:partial»"],
|
|
730
|
+
[valueArm("xapp-\\d-", "[A-Za-z0-9-]", 4, "needs-left-boundary"), "«redacted:slack-token:partial»"],
|
|
699
731
|
];
|
|
700
732
|
const REDACTION_TOKENS = new Set([...FULL_SECRET_PATTERNS.map(([, r]) => r), PEM_FRAGMENT_TOKEN, ...FRAGMENT_SECRET_PATTERNS.map(([, r]) => r)]
|
|
701
733
|
.flatMap((r) => r.match(/«[^»]*»/g) ?? []));
|
|
@@ -715,6 +747,8 @@ function redactionTokenEndAt(s, p, limit) {
|
|
|
715
747
|
return 0;
|
|
716
748
|
}
|
|
717
749
|
const labelOfToken = (token) => token.replace(/[«»]/g, "").replace(/^redacted:/, "");
|
|
750
|
+
const NO_HITS = [];
|
|
751
|
+
const singleHit = (label, arm, r) => r.count > 0 ? { out: r.out, hits: [{ label, count: r.count, arm }] } : { out: r.out, hits: NO_HITS };
|
|
718
752
|
const regexRule = ([re, repl], arm) => {
|
|
719
753
|
const groupCount = (new RegExp(`${re.source}|`).exec("")?.length ?? 1) - 1;
|
|
720
754
|
const refs = [...repl.matchAll(/\$(.)/g)].map(([, d]) => d ?? "");
|
|
@@ -726,23 +760,20 @@ const regexRule = ([re, repl], arm) => {
|
|
|
726
760
|
const sticky = VALUE_ARM_STICKY.get(re);
|
|
727
761
|
if (sticky !== undefined && refs.length > 0)
|
|
728
762
|
throw new Error(`redact: value arm replacement ${JSON.stringify(repl)} must not use $N refs`);
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
return expand === undefined ? repl : expand(args);
|
|
742
|
-
});
|
|
743
|
-
return { out, count: n };
|
|
744
|
-
},
|
|
763
|
+
const label = labelOfToken(repl);
|
|
764
|
+
const applyOnce = (text, wantCount) => {
|
|
765
|
+
if (sticky !== undefined)
|
|
766
|
+
return runValueArm(re, sticky, repl, text);
|
|
767
|
+
if (!wantCount)
|
|
768
|
+
return { out: text.replace(re, repl), count: 0 };
|
|
769
|
+
let n = 0;
|
|
770
|
+
const out = text.replace(re, (...args) => {
|
|
771
|
+
n += 1;
|
|
772
|
+
return expand === undefined ? repl : expand(args);
|
|
773
|
+
});
|
|
774
|
+
return { out, count: n };
|
|
745
775
|
};
|
|
776
|
+
return { run: (text, wantCount) => singleHit(label, arm, applyOnce(text, wantCount)) };
|
|
746
777
|
};
|
|
747
778
|
const URL_USERINFO_PATTERN = (() => {
|
|
748
779
|
const row = FULL_SECRET_PATTERNS.find(([, repl]) => repl.endsWith("»@"));
|
|
@@ -937,16 +968,43 @@ function redactPpkContainers(text, record) {
|
|
|
937
968
|
}
|
|
938
969
|
return count === 0 ? { out: text, count: 0 } : { out: out + text.slice(last), count };
|
|
939
970
|
}
|
|
940
|
-
const
|
|
971
|
+
const URL_USERINFO_LABEL = labelOfToken(URL_USERINFO_PATTERN[1]);
|
|
972
|
+
const urlUserinfoRule = { run: (text, _wantCount, json) => singleHit(URL_USERINFO_LABEL, "full", redactUrlUserinfo(text, json)) };
|
|
973
|
+
const shapeSectionRule = (() => {
|
|
974
|
+
const arms = SHAPE_SECRET_PATTERNS.map(([re, repl], order) => {
|
|
975
|
+
if (/\$./.test(repl))
|
|
976
|
+
throw new Error(`redact: shape-section replacement ${JSON.stringify(repl)} must not use $N refs`);
|
|
977
|
+
return { g: re, y: VALUE_ARM_STICKY.get(re), repl, order };
|
|
978
|
+
});
|
|
979
|
+
return {
|
|
980
|
+
run: (text, wantCount) => {
|
|
981
|
+
const spans = [];
|
|
982
|
+
for (const a of arms) {
|
|
983
|
+
if (a.y === undefined)
|
|
984
|
+
collectRegexSpans(a.g, a.repl, a.order, text, spans);
|
|
985
|
+
else
|
|
986
|
+
collectValueArmSpans(a.g, a.y, a.repl, a.order, text, spans);
|
|
987
|
+
}
|
|
988
|
+
const kept = resolveSpanOverlaps(spans);
|
|
989
|
+
const out = applySpans(text, kept).out;
|
|
990
|
+
if (!wantCount || kept.length === 0)
|
|
991
|
+
return { out, hits: NO_HITS };
|
|
992
|
+
const byToken = new Map();
|
|
993
|
+
for (const sp of kept)
|
|
994
|
+
byToken.set(sp.repl, (byToken.get(sp.repl) ?? 0) + 1);
|
|
995
|
+
return { out, hits: [...byToken].map(([token, count]) => ({ label: labelOfToken(token), count, arm: "full" })) };
|
|
996
|
+
},
|
|
997
|
+
};
|
|
998
|
+
})();
|
|
941
999
|
const SECRET_RULES = [
|
|
942
1000
|
...STRUCTURAL_SECRET_PATTERNS.map((p) => (p === URL_USERINFO_PATTERN ? urlUserinfoRule : regexRule(p, "full"))),
|
|
943
|
-
{
|
|
944
|
-
{
|
|
945
|
-
{
|
|
1001
|
+
{ run: (text, _wantCount, _json, observe) => singleHit(labelOfToken(PPK_CONTAINER_TOKEN), "full", redactPpkContainers(text, observe)) },
|
|
1002
|
+
{ run: (text, _wantCount, json) => singleHit(labelOfToken(PEM_FRAGMENT_TOKEN), "fragment", redactPemFragments(text, json)) },
|
|
1003
|
+
{ run: (text) => singleHit(labelOfToken(HEADLESS_PPK_TOKEN), "fragment", redactHeadlessPpkBodies(text)) },
|
|
946
1004
|
...KEYWORD_SECRET_PATTERNS.map((p) => regexRule(p, "full")),
|
|
947
|
-
|
|
1005
|
+
shapeSectionRule,
|
|
948
1006
|
...FRAGMENT_SECRET_PATTERNS.map((p) => regexRule(p, "fragment")),
|
|
949
|
-
{
|
|
1007
|
+
{ run: (text, _wantCount, json) => ({ out: redactUrlUserinfo(text, json).out, hits: NO_HITS }) },
|
|
950
1008
|
];
|
|
951
1009
|
let onRedaction;
|
|
952
1010
|
export function setRedactionObserver(fn) {
|
|
@@ -958,8 +1016,8 @@ function applyRules(text, wantCount, json, observe) {
|
|
|
958
1016
|
for (const rule of SECRET_RULES) {
|
|
959
1017
|
const r = rule.run(out, wantCount, json, observe);
|
|
960
1018
|
out = r.out;
|
|
961
|
-
if (wantCount && r.
|
|
962
|
-
hits.push(
|
|
1019
|
+
if (wantCount && r.hits.length > 0)
|
|
1020
|
+
hits.push(...r.hits);
|
|
963
1021
|
}
|
|
964
1022
|
return { out, hits };
|
|
965
1023
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sema-agent/server",
|
|
3
|
-
"version": "7.75.
|
|
3
|
+
"version": "7.75.2",
|
|
4
4
|
"description": "Sema Server — the server/API implementation layer for Sema, wiring core, registry, model providers, and cloud agent execution. Built on @sema-agent/core.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "BUSL-1.1",
|
|
@@ -54,7 +54,7 @@
|
|
|
54
54
|
"build:binary:run-local:darwin-arm64": "bun build --compile --target=bun-darwin-arm64 src/run-local.ts --outfile dist/run-local-darwin-arm64"
|
|
55
55
|
},
|
|
56
56
|
"dependencies": {
|
|
57
|
-
"@sema-agent/core": "7.17.
|
|
57
|
+
"@sema-agent/core": "7.17.5",
|
|
58
58
|
"@sema-agent/settings-schema": "1.10.0",
|
|
59
59
|
"e2b": "^2.28.0",
|
|
60
60
|
"libsodium-wrappers": "^0.8.4",
|