@getmarrow/mcp 3.9.108 → 3.9.109
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/README.md +16 -1
- package/dist/cli.js +26 -1
- package/dist/cli.js.map +1 -1
- package/dist/control-notice.d.ts +13 -0
- package/dist/control-notice.d.ts.map +1 -0
- package/dist/control-notice.js +76 -0
- package/dist/control-notice.js.map +1 -0
- package/dist/control-routes.d.ts +10 -0
- package/dist/control-routes.d.ts.map +1 -0
- package/dist/control-routes.js +81 -0
- package/dist/control-routes.js.map +1 -0
- package/dist/hook-context.d.ts.map +1 -1
- package/dist/hook-context.js +34 -11
- package/dist/hook-context.js.map +1 -1
- package/dist/hook-contract.d.ts +2 -2
- package/dist/hook-contract.js +1 -1
- package/dist/hook-pre-action.d.ts +2 -0
- package/dist/hook-pre-action.d.ts.map +1 -1
- package/dist/hook-pre-action.js +86 -20
- package/dist/hook-pre-action.js.map +1 -1
- package/dist/hook-session.d.ts.map +1 -1
- package/dist/hook-session.js +12 -7
- package/dist/hook-session.js.map +1 -1
- package/dist/hook-tool-policy.d.ts +1 -1
- package/dist/hook-tool-policy.d.ts.map +1 -1
- package/dist/hook.d.ts.map +1 -1
- package/dist/hook.js +19 -14
- package/dist/hook.js.map +1 -1
- package/dist/ipv4-first.d.ts +7 -0
- package/dist/ipv4-first.d.ts.map +1 -0
- package/dist/ipv4-first.js +60 -0
- package/dist/ipv4-first.js.map +1 -0
- package/dist/key-class.d.ts +16 -0
- package/dist/key-class.d.ts.map +1 -0
- package/dist/key-class.js +37 -0
- package/dist/key-class.js.map +1 -0
- package/dist/keyless-notice.d.ts +10 -0
- package/dist/keyless-notice.d.ts.map +1 -0
- package/dist/keyless-notice.js +47 -0
- package/dist/keyless-notice.js.map +1 -0
- package/dist/normalized-action.d.ts +4 -0
- package/dist/normalized-action.d.ts.map +1 -1
- package/dist/normalized-action.js +60 -27
- package/dist/normalized-action.js.map +1 -1
- package/dist/owner-bypass.d.ts +9 -0
- package/dist/owner-bypass.d.ts.map +1 -0
- package/dist/owner-bypass.js +79 -0
- package/dist/owner-bypass.js.map +1 -0
- package/dist/redact.d.ts +5 -0
- package/dist/redact.d.ts.map +1 -1
- package/dist/redact.js +61 -9
- package/dist/redact.js.map +1 -1
- package/dist/seatbelt/hook.d.ts +3 -1
- package/dist/seatbelt/hook.d.ts.map +1 -1
- package/dist/seatbelt/hook.js +7 -3
- package/dist/seatbelt/hook.js.map +1 -1
- package/dist/seatbelt/mapper.d.ts.map +1 -1
- package/dist/seatbelt/mapper.js +849 -12
- package/dist/seatbelt/mapper.js.map +1 -1
- package/dist/seatbelt/patch-text.d.ts +49 -0
- package/dist/seatbelt/patch-text.d.ts.map +1 -0
- package/dist/seatbelt/patch-text.js +376 -0
- package/dist/seatbelt/patch-text.js.map +1 -0
- package/dist/seatbelt/tables.d.ts +66 -0
- package/dist/seatbelt/tables.d.ts.map +1 -1
- package/dist/seatbelt/tables.js +467 -3
- package/dist/seatbelt/tables.js.map +1 -1
- package/dist/seatbelt/win-protected.d.ts +52 -0
- package/dist/seatbelt/win-protected.d.ts.map +1 -0
- package/dist/seatbelt/win-protected.js +1279 -0
- package/dist/seatbelt/win-protected.js.map +1 -0
- package/dist/self-protect.d.ts +18 -0
- package/dist/self-protect.d.ts.map +1 -0
- package/dist/self-protect.js +185 -0
- package/dist/self-protect.js.map +1 -0
- package/dist/session-id.d.ts +2 -0
- package/dist/session-id.d.ts.map +1 -0
- package/dist/session-id.js +32 -0
- package/dist/session-id.js.map +1 -0
- package/dist/session-loop-guard.d.ts +7 -0
- package/dist/session-loop-guard.d.ts.map +1 -1
- package/dist/session-loop-guard.js +168 -1
- package/dist/session-loop-guard.js.map +1 -1
- package/dist/session-marker.d.ts +7 -0
- package/dist/session-marker.d.ts.map +1 -0
- package/dist/session-marker.js +42 -0
- package/dist/session-marker.js.map +1 -0
- package/dist/win-private-state.d.ts +117 -0
- package/dist/win-private-state.d.ts.map +1 -0
- package/dist/win-private-state.js +544 -0
- package/dist/win-private-state.js.map +1 -0
- package/package.json +1 -1
- package/server.json +2 -2
|
@@ -11,7 +11,13 @@ exports.normalizedHookAction = normalizedHookAction;
|
|
|
11
11
|
const node_crypto_1 = require("node:crypto");
|
|
12
12
|
const node_os_1 = require("node:os");
|
|
13
13
|
const hook_tool_policy_1 = require("./hook-tool-policy");
|
|
14
|
+
const key_class_1 = require("./key-class");
|
|
14
15
|
const SECRET = '[secret]';
|
|
16
|
+
/** The marker of a replaced Stripe-style key keeps its class (a closed set: never built from input). */
|
|
17
|
+
const SECRET_LIVE = '[secret-live]';
|
|
18
|
+
const SECRET_TEST = '[secret-test]';
|
|
19
|
+
const SECRET_MARKER = /\[secret(?:-(?:live|test))?\]/;
|
|
20
|
+
const SECRET_MARKER_ALL = /\[secret-(?:live|test)\]/g;
|
|
15
21
|
const HASH_VERSION = 'marrow-normalized-action-v3';
|
|
16
22
|
const EDIT_TOOLS = /^(?:edit|write|multiedit|apply_patch|notebookedit|replace|write_file|edit_file|delete_file|search_replace|delete|create_file)$/i;
|
|
17
23
|
const MAX_DEPTH = 3;
|
|
@@ -36,9 +42,35 @@ function newExactness(deadline = Date.now() + DEFAULT_NORMALIZE_MS, exact = fals
|
|
|
36
42
|
* A value withheld only because it MAY be a secret (it may also be an id, a commit or a name that decides the
|
|
37
43
|
* action): replaced and the action marked truncated; the exact pass keeps it as it is.
|
|
38
44
|
*/
|
|
39
|
-
function withheld(state, original, replacement
|
|
45
|
+
function withheld(state, original, replacement) {
|
|
40
46
|
state.truncated = true; // exact: kept
|
|
41
|
-
return state.exact ? original : replacement;
|
|
47
|
+
return state.exact ? original : (replacement ?? secretFor(original));
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* The marker for a replaced span: `[secret-live]` / `[secret-test]` when the span holds a Stripe-style key of that class
|
|
51
|
+
* (key-class.ts: unknown, malformed and look-alike prefixes are live), else `[secret]`. A span past the scan limit is
|
|
52
|
+
* not read (it is withheld, truncated, and the exact pass keeps it as it is).
|
|
53
|
+
*/
|
|
54
|
+
function secretFor(span) {
|
|
55
|
+
if (span.length > exports.MAX_SCAN_CHARS)
|
|
56
|
+
return SECRET;
|
|
57
|
+
const keyClass = (0, key_class_1.keyClassOf)(span);
|
|
58
|
+
return keyClass === 'live' ? SECRET_LIVE : keyClass === 'test' ? SECRET_TEST : SECRET;
|
|
59
|
+
}
|
|
60
|
+
/** The marker for a replaced value of any type (a tool input field): an object or array is read as bounded JSON. */
|
|
61
|
+
function secretOfValue(value) {
|
|
62
|
+
if (typeof value === 'string')
|
|
63
|
+
return secretFor(value);
|
|
64
|
+
try {
|
|
65
|
+
return secretFor((JSON.stringify(value) ?? '').slice(0, exports.MAX_SCAN_CHARS));
|
|
66
|
+
}
|
|
67
|
+
catch {
|
|
68
|
+
return SECRET;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
/** Whether a text holds a replaced-secret marker (any class). */
|
|
72
|
+
function hasSecretMarker(text) {
|
|
73
|
+
return SECRET_MARKER.test(text);
|
|
42
74
|
}
|
|
43
75
|
/** Counts text about to be scanned against the shared budget; past it, normalization stops. */
|
|
44
76
|
function spend(state, chars) {
|
|
@@ -94,7 +126,7 @@ function redactKeyShapes(text, state) {
|
|
|
94
126
|
if (whole && !/\s/.test(whole) && looksLikeKey(whole)) {
|
|
95
127
|
if (!exports.KNOWN_KEY_PREFIX.test(whole) && !PEM.test(whole))
|
|
96
128
|
return withheld(state, text);
|
|
97
|
-
return state.exact && PUBLIC_KNOWN_KEY.test(whole) ? text :
|
|
129
|
+
return state.exact && PUBLIC_KNOWN_KEY.test(whole) ? text : secretFor(whole);
|
|
98
130
|
}
|
|
99
131
|
if (PEM.test(text)) {
|
|
100
132
|
// The exact pass replaces only a private key block; a certificate or a public key block is kept.
|
|
@@ -105,7 +137,7 @@ function redactKeyShapes(text, state) {
|
|
|
105
137
|
return part;
|
|
106
138
|
if (!exports.KNOWN_KEY_PREFIX.test(part))
|
|
107
139
|
return withheld(state, part);
|
|
108
|
-
return state.exact && PUBLIC_KNOWN_KEY.test(part) ? part :
|
|
140
|
+
return state.exact && PUBLIC_KNOWN_KEY.test(part) ? part : secretFor(part);
|
|
109
141
|
}).join('');
|
|
110
142
|
}
|
|
111
143
|
// ---------------------------------------------------------------------------
|
|
@@ -236,10 +268,10 @@ function cutCredential(value, state) {
|
|
|
236
268
|
if (at >= value.length) {
|
|
237
269
|
if (holdsMoreThanCredential(value))
|
|
238
270
|
return withheld(state, value);
|
|
239
|
-
return
|
|
271
|
+
return secretFor(value);
|
|
240
272
|
}
|
|
241
273
|
state.truncated = true; // exact: certain (the credential before the field; the rest is scanned in the same pass)
|
|
242
|
-
return
|
|
274
|
+
return secretFor(value.slice(0, at)) + redactText(value.slice(at), state);
|
|
243
275
|
}
|
|
244
276
|
/** One-value auth header (`Authorization: Bearer X`, `X-Api-Key: K`): the whole value is the credential. */
|
|
245
277
|
const SINGLE_VALUE_HEADER = /^[^:\s]{1,128}:[ \t]*(?:(?:Bearer|Basic|Token|Bot)[ \t]+)?[^\s;,&]+[ \t]*$/i;
|
|
@@ -260,7 +292,7 @@ function redactText(text, state) {
|
|
|
260
292
|
// goes to host `deploy`), while libpq, mysql and redis clients take the whole span as the password. It is
|
|
261
293
|
// hidden either way, but it is not a certain credential: the action is marked truncated and the exact pass
|
|
262
294
|
// keeps the span, so it never shares a key with `https://deploy:<pw>@host`.
|
|
263
|
-
let out = text.replace(URL_CREDENTIALS, (_match, scheme, user, password) => (`${scheme}${user}:${/[#?]/.test(password) ? withheld(state, password) :
|
|
295
|
+
let out = text.replace(URL_CREDENTIALS, (_match, scheme, user, password) => (`${scheme}${user}:${/[#?]/.test(password) ? withheld(state, password) : secretFor(password)}@`));
|
|
264
296
|
// The exact pass keeps a `Token`/`Bot` value that is not key-shaped (`git commit -m "Token rotation"`).
|
|
265
297
|
out = replaceValuesAfter(out, AUTH_SCHEME, state.exact ? (scheme, value) => /^(?:Token|Bot)$/.test(scheme) && !looksLikeKey(value) : null);
|
|
266
298
|
// The exact pass keeps a URL `code` value: it may be an OAuth code, but it may as well be a country or product code.
|
|
@@ -286,7 +318,7 @@ function replaceValuesAfter(text, pattern, keep) {
|
|
|
286
318
|
pattern.lastIndex = end;
|
|
287
319
|
continue;
|
|
288
320
|
}
|
|
289
|
-
out += text.slice(copied, start) +
|
|
321
|
+
out += text.slice(copied, start) + secretFor(text.slice(start, end));
|
|
290
322
|
copied = end;
|
|
291
323
|
pattern.lastIndex = end;
|
|
292
324
|
}
|
|
@@ -326,7 +358,7 @@ function redactAssignments(text, state) {
|
|
|
326
358
|
continue;
|
|
327
359
|
const value = text.slice(start, end);
|
|
328
360
|
if (!value.startsWith('[secret') && !value.startsWith('[data:')) {
|
|
329
|
-
const replacement = kind === 'ambiguous' || holdsMoreThanCredential(value) ? withheld(state, value) :
|
|
361
|
+
const replacement = kind === 'ambiguous' || holdsMoreThanCredential(value) ? withheld(state, value) : secretFor(value);
|
|
330
362
|
out += text.slice(copied, start) + replacement;
|
|
331
363
|
copied = end;
|
|
332
364
|
}
|
|
@@ -349,7 +381,7 @@ function dataMarker(text, feedsSecrets, state, words = null) {
|
|
|
349
381
|
}
|
|
350
382
|
if (feedsSecrets) {
|
|
351
383
|
state.truncated = true; // exact: kept (hashed above, certain credentials replaced)
|
|
352
|
-
return
|
|
384
|
+
return secretFor(text);
|
|
353
385
|
}
|
|
354
386
|
// Secret-shaped data (`'{"api_key":"S","env":"production"}'`): the secrets are
|
|
355
387
|
// replaced and the rest is hashed, so data that differs outside a secret gives
|
|
@@ -573,7 +605,7 @@ function assignedValue(name, value, state) {
|
|
|
573
605
|
if (kind === 'credential' || kind === 'ambiguous') {
|
|
574
606
|
if (kind === 'ambiguous' || holdsMoreThanCredential(value))
|
|
575
607
|
return `${name}=${withheld(state, state.exact ? dataMarker(value, false, state) : value)}`;
|
|
576
|
-
return `${name}=${
|
|
608
|
+
return `${name}=${secretFor(value)}`;
|
|
577
609
|
}
|
|
578
610
|
return value ? `${name}=${dataMarker(value, false, state)}` : `${name}=`;
|
|
579
611
|
}
|
|
@@ -699,7 +731,7 @@ function redactSegment(words, ctx) {
|
|
|
699
731
|
const envStore = storeContext(lowered.slice(0, 4));
|
|
700
732
|
const stem = programStem(program);
|
|
701
733
|
const redactAt = (i, ambiguous = false) => {
|
|
702
|
-
out[i] = ambiguous || holdsMoreThanCredential(out[i]) ? withheld(state, out[i]) :
|
|
734
|
+
out[i] = ambiguous || holdsMoreThanCredential(out[i]) ? withheld(state, out[i]) : secretFor(out[i]);
|
|
703
735
|
handled.add(i);
|
|
704
736
|
};
|
|
705
737
|
// An auth header: a one-value header is the credential; any other (`Cookie: a=1; env=prod`)
|
|
@@ -711,7 +743,7 @@ function redactSegment(words, ctx) {
|
|
|
711
743
|
if (holdsMoreThanCredential(value))
|
|
712
744
|
return withheld(state, header);
|
|
713
745
|
if (!state.exact)
|
|
714
|
-
return
|
|
746
|
+
return secretFor(value);
|
|
715
747
|
// The exact pass: the value of a credential header (or a Bearer/Basic value) is certain; any other
|
|
716
748
|
// one-value header (`X-Idempotency-Key: k`) and the header name are kept.
|
|
717
749
|
const name = header.slice(0, colon);
|
|
@@ -720,7 +752,7 @@ function redactSegment(words, ctx) {
|
|
|
720
752
|
// Basic USER:PASSWORD names who acts, as `curl -u` does: only the password is certain.
|
|
721
753
|
const basic = /^[ \t]*Basic[ \t]+([A-Za-z0-9+/]+={0,2})[ \t]*$/i.exec(value);
|
|
722
754
|
const decoded = basic ? Buffer.from(basic[1], 'base64').toString('latin1') : '';
|
|
723
|
-
return /^[\x20-\x7e]*:/.test(decoded) ? `${name}:Basic ${JSON.stringify(decoded.slice(0, decoded.indexOf(':')))}:${
|
|
755
|
+
return /^[\x20-\x7e]*:/.test(decoded) ? `${name}:Basic ${JSON.stringify(decoded.slice(0, decoded.indexOf(':')))}:${secretFor(decoded.slice(decoded.indexOf(':') + 1))}` : `${name}:${secretFor(value)}`;
|
|
724
756
|
}
|
|
725
757
|
const redacted = redactText(header, state);
|
|
726
758
|
return redacted === header ? withheld(state, header) : redacted;
|
|
@@ -739,9 +771,9 @@ function redactSegment(words, ctx) {
|
|
|
739
771
|
return `${name}=${cutCredential(value, state)}`;
|
|
740
772
|
};
|
|
741
773
|
// The exact pass: `curl -u USER:PASSWORD` names who acts; only the password is certain.
|
|
742
|
-
const exactUser = (value) => (value.includes(':') ? `${value.slice(0, value.indexOf(':'))}:${
|
|
774
|
+
const exactUser = (value) => (value.includes(':') ? `${value.slice(0, value.indexOf(':'))}:${secretFor(value.slice(value.indexOf(':') + 1))}` : value);
|
|
743
775
|
// The exact pass: openssl `pass:X` and a bare value are the password; `env:`, `file:`, `fd:` name where it is.
|
|
744
|
-
const exactOpensslPass = (value) => (/^(?:env|file|fd):/.test(value) ? value : /^pass:/.test(value) ? `pass:${
|
|
776
|
+
const exactOpensslPass = (value) => (/^(?:env|file|fd):/.test(value) ? value : /^pass:/.test(value) ? `pass:${secretFor(value.slice(5))}` : secretFor(value));
|
|
745
777
|
// A program named for a secret (set-password.sh): its arguments cannot be told apart (the exact pass keeps them).
|
|
746
778
|
if (!SECRET_PROGRAMS.has(stem) && classifySecretName(stem) === 'credential') {
|
|
747
779
|
for (const i of positionals)
|
|
@@ -771,7 +803,7 @@ function redactSegment(words, ctx) {
|
|
|
771
803
|
};
|
|
772
804
|
const attachedOrNext = (prefix) => {
|
|
773
805
|
if (arg.length > prefix.length) {
|
|
774
|
-
out[i] = `${prefix}${
|
|
806
|
+
out[i] = `${prefix}${secretFor(arg.slice(prefix.length))}`;
|
|
775
807
|
handled.add(i);
|
|
776
808
|
}
|
|
777
809
|
else {
|
|
@@ -790,12 +822,12 @@ function redactSegment(words, ctx) {
|
|
|
790
822
|
if (long) {
|
|
791
823
|
const name = long[1].replace(/^-+/, '');
|
|
792
824
|
if (name === 'from-literal' || name === 'from-env-literal') {
|
|
793
|
-
const replaced = `${long[1]}=${long[2].replace(/^([^=]*)
|
|
825
|
+
const replaced = `${long[1]}=${long[2].replace(/^([^=]*)=(.*)$/s, (_m, literal, secret) => `${literal}=${secretFor(secret)}`)}`;
|
|
794
826
|
out[i] = holdsMoreThanCredential(long[2].replace(/^[^=]*=/, '')) ? withheld(state, arg, replaced) : replaced;
|
|
795
827
|
handled.add(i);
|
|
796
828
|
}
|
|
797
829
|
else if (CURL_FAMILY.has(program) && /^(?:u|user|proxy-user)$/.test(name)) {
|
|
798
|
-
out[i] = `${long[1]}=${state.exact ? exactUser(long[2]) :
|
|
830
|
+
out[i] = `${long[1]}=${state.exact ? exactUser(long[2]) : secretFor(long[2])}`;
|
|
799
831
|
handled.add(i);
|
|
800
832
|
}
|
|
801
833
|
else if (CURL_FAMILY.has(program) && name === 'header' && AUTH_HEADER.test(long[2])) {
|
|
@@ -846,7 +878,7 @@ function redactSegment(words, ctx) {
|
|
|
846
878
|
// Short flags by program; single-dash long names (-password, -token) by name.
|
|
847
879
|
if (/^-[A-Za-z]/.test(arg)) {
|
|
848
880
|
if (MYSQL_FAMILY.has(program) && /^-p.+/.test(arg)) {
|
|
849
|
-
out[i] = `-p${
|
|
881
|
+
out[i] = `-p${secretFor(arg.slice(2))}`;
|
|
850
882
|
handled.add(i);
|
|
851
883
|
}
|
|
852
884
|
else if (ALWAYS_P.has(program) && /^-p/.test(arg))
|
|
@@ -889,7 +921,7 @@ function redactSegment(words, ctx) {
|
|
|
889
921
|
const [, name, value] = assignment;
|
|
890
922
|
if (secretContext) {
|
|
891
923
|
// In a secrets command every value is a secret: withheld whole, and truncated if it held more fields.
|
|
892
|
-
out[i] = /[&#]/.test(value) || holdsMoreThanCredential(value) ? withheld(state, arg, `${name}=${
|
|
924
|
+
out[i] = /[&#]/.test(value) || holdsMoreThanCredential(value) ? withheld(state, arg, `${name}=${secretFor(value)}`) : `${name}=${secretFor(value)}`;
|
|
893
925
|
handled.add(i);
|
|
894
926
|
}
|
|
895
927
|
else if (holdsSecret(name)) {
|
|
@@ -909,7 +941,7 @@ function redactSegment(words, ctx) {
|
|
|
909
941
|
continue;
|
|
910
942
|
}
|
|
911
943
|
if (program === 'openssl' && /^(?:pass|env|file|fd):/.test(arg)) {
|
|
912
|
-
out[i] = state.exact ? exactOpensslPass(arg) : arg.replace(
|
|
944
|
+
out[i] = state.exact ? exactOpensslPass(arg) : arg.replace(/:(.*)$/s, (_m, rest) => `:${secretFor(rest)}`);
|
|
913
945
|
handled.add(i);
|
|
914
946
|
continue;
|
|
915
947
|
}
|
|
@@ -1264,7 +1296,7 @@ function redactedInput(value, state, depth = 0) {
|
|
|
1264
1296
|
if (state.exact)
|
|
1265
1297
|
throw new NormalizationLimit();
|
|
1266
1298
|
state.truncated = true; // exact: raw (canonical input)
|
|
1267
|
-
return
|
|
1299
|
+
return secretOfValue(value);
|
|
1268
1300
|
}
|
|
1269
1301
|
if (typeof value === 'string')
|
|
1270
1302
|
return redactText(value, state);
|
|
@@ -1289,7 +1321,7 @@ function redactedInput(value, state, depth = 0) {
|
|
|
1289
1321
|
// name (`credentials: {user, role, password}`): only a value itself is certain.
|
|
1290
1322
|
if (kind === 'ambiguous')
|
|
1291
1323
|
state.truncated = true; // exact: kept (below)
|
|
1292
|
-
out[key] = state.exact && (kind === 'ambiguous' || (item !== null && typeof item === 'object')) ? redactedInput(item, state, depth + 1) :
|
|
1324
|
+
out[key] = state.exact && (kind === 'ambiguous' || (item !== null && typeof item === 'object')) ? redactedInput(item, state, depth + 1) : secretOfValue(item);
|
|
1293
1325
|
}
|
|
1294
1326
|
else {
|
|
1295
1327
|
out[key] = redactedInput(item, state, depth + 1);
|
|
@@ -1456,7 +1488,7 @@ function inputPreimage(input, state) {
|
|
|
1456
1488
|
if (input === undefined || input === null)
|
|
1457
1489
|
return null;
|
|
1458
1490
|
const text = JSON.stringify(sortedValue(redactedInput(input, state)));
|
|
1459
|
-
if (text
|
|
1491
|
+
if (hasSecretMarker(text))
|
|
1460
1492
|
state.truncated = true; // exact: n/a (pass 1 only; exactMaterial is the exact form)
|
|
1461
1493
|
return `${HASH_VERSION}\ninput\n${text}`;
|
|
1462
1494
|
}
|
|
@@ -1800,7 +1832,7 @@ function normalizedOfKind(event, kind, hostToolName, state) {
|
|
|
1800
1832
|
state.freeText = !(0, hook_tool_policy_1.isShellTextTool)(event);
|
|
1801
1833
|
const { text, programs } = normalizeInner(raw, state, 0);
|
|
1802
1834
|
// A secret was replaced: the action is never bound or reused (it asks each time).
|
|
1803
|
-
if (text
|
|
1835
|
+
if (hasSecretMarker(text))
|
|
1804
1836
|
state.truncated = true; // exact: n/a (pass 1 only; the exact pass decides each value)
|
|
1805
1837
|
// The command text itself only for a deploy the service can judge word for word (see deployCommandText).
|
|
1806
1838
|
// Judged on the host's exact text (a trimmed non-ASCII space must not slip through).
|
|
@@ -1841,7 +1873,8 @@ function normalizedOfKind(event, kind, hostToolName, state) {
|
|
|
1841
1873
|
if (targets.length > 64)
|
|
1842
1874
|
state.truncated = true; // exact: n/a (paths are not exact material; the whole input is)
|
|
1843
1875
|
const paths = targets.slice(0, 64).map((path) => {
|
|
1844
|
-
|
|
1876
|
+
// The paths go to the service's path classifier as they did before the key class: class-free (the class is in the hashes).
|
|
1877
|
+
const redacted = redactPathSpans(redactText(withoutUserName(path, state), state)).replace(SECRET_MARKER_ALL, SECRET);
|
|
1845
1878
|
if (redacted.length > 512 || redacted.includes(SECRET) || path.length > PATH_SCAN_CHARS)
|
|
1846
1879
|
state.truncated = true; // exact: n/a (as above)
|
|
1847
1880
|
return redacted.slice(0, 512);
|