dsh-perm-gate 5.1.2-compat.7 → 5.2.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/CHANGELOG.md +104 -0
- package/THIRD_PARTY_NOTICES.md +44 -0
- package/dsh.plugin.json +1 -1
- package/lib/ask-context.d.ts +207 -0
- package/lib/ask-context.js +504 -0
- package/lib/bridge.d.ts +9 -2
- package/lib/bridge.js +7 -4
- package/lib/classifier.d.ts +8 -14
- package/lib/classifier.js +24 -2
- package/lib/client.js +126 -126
- package/lib/client.js.map +1 -1
- package/lib/command-normalize.d.ts +80 -0
- package/lib/command-normalize.js +246 -0
- package/lib/config.d.ts +92 -83
- package/lib/config.js +65 -41
- package/lib/engine.js +78 -58
- package/lib/evaluate.d.ts +5 -0
- package/lib/evaluate.js +24 -13
- package/lib/events.d.ts +26 -0
- package/lib/events.js +7 -0
- package/lib/grader-rules.d.ts +32 -0
- package/lib/grader-rules.js +48 -0
- package/lib/index.js +29 -0
- package/lib/irreversible.d.ts +54 -0
- package/lib/irreversible.js +376 -0
- package/lib/parsers/shell-cmds.d.ts +9 -0
- package/lib/parsers/shell-cmds.js +37 -3
- package/lib/preset-rules.d.ts +70 -0
- package/lib/preset-rules.js +388 -0
- package/lib/redact.d.ts +55 -0
- package/lib/redact.js +183 -0
- package/lib/risk.d.ts +32 -5
- package/lib/risk.js +44 -40
- package/lib/runtime.d.ts +82 -0
- package/lib/runtime.js +222 -27
- package/lib/tool-names.d.ts +43 -0
- package/lib/tool-names.js +85 -0
- package/package.json +5 -4
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Command normalization — decode the layers a call hides behind, as a scan signal.
|
|
3
|
+
*
|
|
4
|
+
* Why this exists: every deterministic layer reads the command *as written*.
|
|
5
|
+
* `deny-defaults` is a word list, the parsers switch on `baseName`, and
|
|
6
|
+
* `isCleanupSafe` matches path segments. All three are blind to
|
|
7
|
+
* `powershell -EncodedCommand UgBlAG0A...`, to `R="-rf"; rm $R /`, and to a
|
|
8
|
+
* function wrapper around a wipe. Measured on the eval set, the encoded cases are
|
|
9
|
+
* exactly where a graded model ships an unrecoverable act past the gate
|
|
10
|
+
* (`adv_013` decodes to `Remove-Item -Recurse -Force C:\`, `adv_032` to
|
|
11
|
+
* `DROP DATABASE orders_prod`).
|
|
12
|
+
*
|
|
13
|
+
* Two rules keep this from becoming its own vulnerability:
|
|
14
|
+
*
|
|
15
|
+
* 1. **Additive only.** The caller feeds these strings to *stricter* scans
|
|
16
|
+
* (deny-keyword, P0 wipe detection, tier grading). Nothing here can widen a
|
|
17
|
+
* decision, and no result is written back to `exec.arguments` or the event
|
|
18
|
+
* log, so fingerprints, grants and audit text stay about the call as received.
|
|
19
|
+
* 2. **Bounded.** Length caps, depth cap, variant cap and a printable-ratio gate,
|
|
20
|
+
* because decoding is attacker-controlled input on the decision path. Oversized
|
|
21
|
+
* or undecodable input yields nothing, which leaves the call
|
|
22
|
+
* `targetResolvable=false` — opaque, never optimistically safe.
|
|
23
|
+
*
|
|
24
|
+
* On the plugin's own `--exec-b64` launcher wrapper: it is decoded like anything
|
|
25
|
+
* else rather than exempted. An exemption would hand the agent a disguise
|
|
26
|
+
* (`ac-launcher --config x --exec-b64 <rm -rf ...>`), while decoding it can only
|
|
27
|
+
* ever escalate — so the cost is at most a second confirmation, never a block
|
|
28
|
+
* that could not have been reached honestly.
|
|
29
|
+
*/
|
|
30
|
+
/** Where a decoded string came from, and whether the original runs it as code. */
|
|
31
|
+
export interface NormalizedText {
|
|
32
|
+
readonly text: string;
|
|
33
|
+
readonly via: 'base64' | 'hex' | 'varsub';
|
|
34
|
+
/**
|
|
35
|
+
* True when the payload is handed to an execution primitive
|
|
36
|
+
* (`-EncodedCommand`, `FromBase64String`, `Invoke-Expression`, `eval`,
|
|
37
|
+
* `bash -c`, `xxd -r -p` …), i.e. it will really run. Only these variants may
|
|
38
|
+
* feed P0 hard-deny; a stray base64 blob in a comment must not.
|
|
39
|
+
*/
|
|
40
|
+
readonly executed: boolean;
|
|
41
|
+
}
|
|
42
|
+
export interface NormalizeOptions {
|
|
43
|
+
/** Refuse to touch a command longer than this (DoS + false-positive guard). */
|
|
44
|
+
readonly maxInputChars?: number;
|
|
45
|
+
/** How many decode layers to peel. */
|
|
46
|
+
readonly maxDepth?: number;
|
|
47
|
+
/** Shortest base64 blob considered (below this, too noisy). */
|
|
48
|
+
readonly minBlobChars?: number;
|
|
49
|
+
/** Longest decoded output kept. */
|
|
50
|
+
readonly maxOutputChars?: number;
|
|
51
|
+
/** Hard cap on returned variants. */
|
|
52
|
+
readonly maxVariants?: number;
|
|
53
|
+
/**
|
|
54
|
+
* Wall-clock budget for the whole peel. On expiry **all** partial work is
|
|
55
|
+
* dropped, not just the remaining layers: a decoded fragment found late could
|
|
56
|
+
* turn into a P0 deny, and the guardrail this plugin cannot trade away is that
|
|
57
|
+
* the normalizer never decides — an expired peel leaves the call opaque, so the
|
|
58
|
+
* decision falls back to the ask it was already heading for.
|
|
59
|
+
*/
|
|
60
|
+
readonly maxMs?: number;
|
|
61
|
+
/** Injected clock (tests drive the deadline without sleeping). */
|
|
62
|
+
readonly now?: () => number;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Peel encoding layers off one command and return every readable variant.
|
|
66
|
+
*
|
|
67
|
+
* Never throws: any failure yields whatever decoded fine, and an empty result
|
|
68
|
+
* simply means the call stays opaque to the deterministic layers. Exceeding the
|
|
69
|
+
* time budget returns **nothing** rather than the partial set (see `maxMs`).
|
|
70
|
+
*/
|
|
71
|
+
export declare function normalizeCommandText(text: string, options?: NormalizeOptions): NormalizedText[];
|
|
72
|
+
/**
|
|
73
|
+
* Whether a command is the plugin's own AppContainer launcher wrapper.
|
|
74
|
+
* Informational only — the payload is still decoded and judged on its merits.
|
|
75
|
+
*/
|
|
76
|
+
export declare function isOwnLauncher(text: string): boolean;
|
|
77
|
+
/** Variants strict enough to feed a deterministic deny: the payload actually runs. */
|
|
78
|
+
export declare function executableVariants(variants: readonly NormalizedText[]): string[];
|
|
79
|
+
/** All decoded variants, for the additive-only keyword/tier scans. */
|
|
80
|
+
export declare function allVariants(variants: readonly NormalizedText[]): string[];
|
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Command normalization — decode the layers a call hides behind, as a scan signal.
|
|
3
|
+
*
|
|
4
|
+
* Why this exists: every deterministic layer reads the command *as written*.
|
|
5
|
+
* `deny-defaults` is a word list, the parsers switch on `baseName`, and
|
|
6
|
+
* `isCleanupSafe` matches path segments. All three are blind to
|
|
7
|
+
* `powershell -EncodedCommand UgBlAG0A...`, to `R="-rf"; rm $R /`, and to a
|
|
8
|
+
* function wrapper around a wipe. Measured on the eval set, the encoded cases are
|
|
9
|
+
* exactly where a graded model ships an unrecoverable act past the gate
|
|
10
|
+
* (`adv_013` decodes to `Remove-Item -Recurse -Force C:\`, `adv_032` to
|
|
11
|
+
* `DROP DATABASE orders_prod`).
|
|
12
|
+
*
|
|
13
|
+
* Two rules keep this from becoming its own vulnerability:
|
|
14
|
+
*
|
|
15
|
+
* 1. **Additive only.** The caller feeds these strings to *stricter* scans
|
|
16
|
+
* (deny-keyword, P0 wipe detection, tier grading). Nothing here can widen a
|
|
17
|
+
* decision, and no result is written back to `exec.arguments` or the event
|
|
18
|
+
* log, so fingerprints, grants and audit text stay about the call as received.
|
|
19
|
+
* 2. **Bounded.** Length caps, depth cap, variant cap and a printable-ratio gate,
|
|
20
|
+
* because decoding is attacker-controlled input on the decision path. Oversized
|
|
21
|
+
* or undecodable input yields nothing, which leaves the call
|
|
22
|
+
* `targetResolvable=false` — opaque, never optimistically safe.
|
|
23
|
+
*
|
|
24
|
+
* On the plugin's own `--exec-b64` launcher wrapper: it is decoded like anything
|
|
25
|
+
* else rather than exempted. An exemption would hand the agent a disguise
|
|
26
|
+
* (`ac-launcher --config x --exec-b64 <rm -rf ...>`), while decoding it can only
|
|
27
|
+
* ever escalate — so the cost is at most a second confirmation, never a block
|
|
28
|
+
* that could not have been reached honestly.
|
|
29
|
+
*/
|
|
30
|
+
const DEFAULTS = {
|
|
31
|
+
maxInputChars: 4096,
|
|
32
|
+
maxDepth: 3,
|
|
33
|
+
minBlobChars: 16,
|
|
34
|
+
maxOutputChars: 2048,
|
|
35
|
+
maxVariants: 8,
|
|
36
|
+
maxMs: 25,
|
|
37
|
+
now: Date.now,
|
|
38
|
+
};
|
|
39
|
+
/** Primitives that run a following encoded string / blob as code. */
|
|
40
|
+
const EXEC_PRIMITIVE = /(?:-EncodedCommand|FromBase64String|Invoke-Expression|\biex\b|\beval\b|bash\s+-c|sh\s+-c|pwsh\s+-c|powershell\s+-c|xxd\s+-r|--decode\b)/i;
|
|
41
|
+
/** base64 blobs long enough to be a real payload, quoted or bare. */
|
|
42
|
+
const B64_RE = /["']?([A-Za-z0-9+/]{16,}={0,2})["']?/g;
|
|
43
|
+
/** `\x72\x6d` escapes, or a spaced hex dump. */
|
|
44
|
+
const HEX_ESCAPE_RE = /\\x([0-9a-fA-F]{2})/g;
|
|
45
|
+
const HEX_PAIR_RE = /[0-9a-fA-F]{2}/g;
|
|
46
|
+
/**
|
|
47
|
+
* Simple assignments we can substitute textually: `X="v"`, `$X='v'`, `${X}: v`.
|
|
48
|
+
*
|
|
49
|
+
* The value is matched lazily without escape handling, because a Windows path is
|
|
50
|
+
* the common case and ends in a backslash right before the closing quote
|
|
51
|
+
* (`$t="C:\"`): an escape-aware pattern cannot find that terminator and silently
|
|
52
|
+
* yields nothing, which is the exact blind spot this folding exists to close.
|
|
53
|
+
* Truncating a value that contains a literal quote is acceptable here — variants
|
|
54
|
+
* only ever feed stricter scans.
|
|
55
|
+
*/
|
|
56
|
+
const ASSIGN_RE = /\$?\{?([A-Za-z_][A-Za-z0-9_]*)\}?\s*=\s*(["'])(.*?)\2/g;
|
|
57
|
+
/** Share of decoded bytes that must be printable before we trust it as a command. */
|
|
58
|
+
const PRINTABLE_MIN = 0.85;
|
|
59
|
+
function printableRatio(text) {
|
|
60
|
+
if (text === '')
|
|
61
|
+
return 0;
|
|
62
|
+
let ok = 0;
|
|
63
|
+
for (const ch of text) {
|
|
64
|
+
const code = ch.codePointAt(0) ?? 0;
|
|
65
|
+
if (code === 0x09 || code === 0x0a || code === 0x0d || (code >= 0x20 && code !== 0x7f))
|
|
66
|
+
ok += 1;
|
|
67
|
+
}
|
|
68
|
+
return ok / text.length;
|
|
69
|
+
}
|
|
70
|
+
function clip(text, max) {
|
|
71
|
+
return text.length <= max ? text : text.slice(0, max);
|
|
72
|
+
}
|
|
73
|
+
/** Decoded text is only useful if it plausibly is a command, not binary noise. */
|
|
74
|
+
function looksLikeText(text, minPrintable = PRINTABLE_MIN) {
|
|
75
|
+
return text !== '' && printableRatio(text) >= minPrintable && /[A-Za-z]{3}/.test(text);
|
|
76
|
+
}
|
|
77
|
+
/** Decode a blob as one encoding; '' when it is not valid base64 at all. */
|
|
78
|
+
function decodeB64(blob, encoding) {
|
|
79
|
+
try {
|
|
80
|
+
return Buffer.from(blob, 'base64').toString(encoding);
|
|
81
|
+
}
|
|
82
|
+
catch {
|
|
83
|
+
return '';
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Decode one base64 blob, or undefined when it is not decodable text.
|
|
88
|
+
*
|
|
89
|
+
* Two readings are attempted because they look identical from outside and both
|
|
90
|
+
* are common:
|
|
91
|
+
* - **utf16le** — `powershell/pwsh -EncodedCommand` and
|
|
92
|
+
* `[Convert]::ToBase64String([Text.Encoding]::Unicode…)` carry UTF-16LE. Read
|
|
93
|
+
* as UTF-8 the result is `R\0e\0m\0o\0v\0e…`, which a printable check alone
|
|
94
|
+
* rejects: a silent false negative on exactly the highest-value case, caught
|
|
95
|
+
* only once the test asserted the decoded text.
|
|
96
|
+
* - **utf8** — POSIX `base64 -d` and generic payloads.
|
|
97
|
+
*/
|
|
98
|
+
function tryB64(blob, opts) {
|
|
99
|
+
if (blob.length < opts.minBlobChars)
|
|
100
|
+
return undefined;
|
|
101
|
+
const utf8 = decodeB64(blob, 'utf8');
|
|
102
|
+
const interleavedNull = /\x00/.test(utf8);
|
|
103
|
+
if (!interleavedNull && looksLikeText(utf8))
|
|
104
|
+
return clip(utf8, opts.maxOutputChars);
|
|
105
|
+
const utf16 = decodeB64(blob, 'utf16le');
|
|
106
|
+
if (looksLikeText(utf16))
|
|
107
|
+
return clip(utf16, opts.maxOutputChars);
|
|
108
|
+
if (looksLikeText(utf8))
|
|
109
|
+
return clip(utf8, opts.maxOutputChars);
|
|
110
|
+
return undefined;
|
|
111
|
+
}
|
|
112
|
+
/** Decode `\x72\x6d` escapes or a spaced hex dump into text. */
|
|
113
|
+
function tryHex(blob, opts) {
|
|
114
|
+
const escaped = [];
|
|
115
|
+
HEX_ESCAPE_RE.lastIndex = 0;
|
|
116
|
+
let e = HEX_ESCAPE_RE.exec(blob);
|
|
117
|
+
while (e !== null) {
|
|
118
|
+
escaped.push(parseInt(e[1] ?? '0', 16));
|
|
119
|
+
e = HEX_ESCAPE_RE.exec(blob);
|
|
120
|
+
}
|
|
121
|
+
const bytes = escaped.length > 0 ? escaped : [];
|
|
122
|
+
if (bytes.length === 0) {
|
|
123
|
+
const compact = blob.replace(/\s+/g, '');
|
|
124
|
+
if (compact.length % 2 !== 0)
|
|
125
|
+
return undefined;
|
|
126
|
+
HEX_PAIR_RE.lastIndex = 0;
|
|
127
|
+
let m = HEX_PAIR_RE.exec(compact);
|
|
128
|
+
while (m !== null) {
|
|
129
|
+
bytes.push(parseInt(m[0], 16));
|
|
130
|
+
m = HEX_PAIR_RE.exec(compact);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
if (bytes.length === 0)
|
|
134
|
+
return undefined;
|
|
135
|
+
let hex;
|
|
136
|
+
try {
|
|
137
|
+
hex = Buffer.from(bytes).toString('utf8');
|
|
138
|
+
}
|
|
139
|
+
catch {
|
|
140
|
+
return undefined;
|
|
141
|
+
}
|
|
142
|
+
if (!looksLikeText(hex))
|
|
143
|
+
return undefined;
|
|
144
|
+
return clip(hex, opts.maxOutputChars);
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Substitute `X="rm -rf"` / `$X` pairs so `rm $X /` becomes visible as one
|
|
148
|
+
* string. Only literal assignments are folded; nothing is evaluated.
|
|
149
|
+
*/
|
|
150
|
+
function expandVars(text, opts) {
|
|
151
|
+
const table = new Map();
|
|
152
|
+
ASSIGN_RE.lastIndex = 0;
|
|
153
|
+
let m = ASSIGN_RE.exec(text);
|
|
154
|
+
while (m !== null) {
|
|
155
|
+
const name = m[1];
|
|
156
|
+
const value = m[3] ?? '';
|
|
157
|
+
if (name !== undefined && value !== '')
|
|
158
|
+
table.set(name, value);
|
|
159
|
+
m = ASSIGN_RE.exec(text);
|
|
160
|
+
}
|
|
161
|
+
if (table.size === 0)
|
|
162
|
+
return undefined;
|
|
163
|
+
let out = text;
|
|
164
|
+
for (const [name, value] of table) {
|
|
165
|
+
// Skip the assignment's own left-hand side (`$t=` …), which would otherwise
|
|
166
|
+
// be rewritten into noise like `C:\="C:\"`; only uses are folded.
|
|
167
|
+
const use = new RegExp(`\\$\\{?${name}\\}?(?!\\s*=)`, 'g');
|
|
168
|
+
out = out.replace(use, () => value);
|
|
169
|
+
}
|
|
170
|
+
return out === text ? undefined : clip(out, opts.maxOutputChars);
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* Peel encoding layers off one command and return every readable variant.
|
|
174
|
+
*
|
|
175
|
+
* Never throws: any failure yields whatever decoded fine, and an empty result
|
|
176
|
+
* simply means the call stays opaque to the deterministic layers. Exceeding the
|
|
177
|
+
* time budget returns **nothing** rather than the partial set (see `maxMs`).
|
|
178
|
+
*/
|
|
179
|
+
export function normalizeCommandText(text, options = {}) {
|
|
180
|
+
const opts = { ...DEFAULTS, ...options };
|
|
181
|
+
if (typeof text !== 'string' || text.trim() === '' || text.length > opts.maxInputChars)
|
|
182
|
+
return [];
|
|
183
|
+
const startedAt = opts.now();
|
|
184
|
+
const spent = () => opts.now() - startedAt;
|
|
185
|
+
const seen = new Set();
|
|
186
|
+
const out = [];
|
|
187
|
+
const push = (variant) => {
|
|
188
|
+
const trimmed = variant.text.trim();
|
|
189
|
+
if (trimmed === '' || seen.has(trimmed))
|
|
190
|
+
return;
|
|
191
|
+
seen.add(trimmed);
|
|
192
|
+
out.push({ ...variant, text: trimmed });
|
|
193
|
+
};
|
|
194
|
+
let frontier = [{ text, executed: false }];
|
|
195
|
+
for (let depth = 0; depth < opts.maxDepth && frontier.length > 0 && out.length < opts.maxVariants; depth += 1) {
|
|
196
|
+
if (spent() > opts.maxMs)
|
|
197
|
+
return [];
|
|
198
|
+
const next = [];
|
|
199
|
+
for (const item of frontier) {
|
|
200
|
+
const runsCode = item.executed || EXEC_PRIMITIVE.test(item.text);
|
|
201
|
+
B64_RE.lastIndex = 0;
|
|
202
|
+
let b = B64_RE.exec(item.text);
|
|
203
|
+
while (b !== null) {
|
|
204
|
+
const decoded = tryB64(b[1] ?? '', opts);
|
|
205
|
+
if (decoded !== undefined) {
|
|
206
|
+
push({ text: decoded, via: 'base64', executed: runsCode });
|
|
207
|
+
next.push({ text: decoded, executed: runsCode });
|
|
208
|
+
}
|
|
209
|
+
b = B64_RE.exec(item.text);
|
|
210
|
+
}
|
|
211
|
+
if (/\\x[0-9a-fA-F]{2}/.test(item.text) || /(?:\b[0-9a-fA-F]{2}\b){6,}/.test(item.text)) {
|
|
212
|
+
const decoded = tryHex(item.text, opts);
|
|
213
|
+
if (decoded !== undefined) {
|
|
214
|
+
push({ text: decoded, via: 'hex', executed: runsCode });
|
|
215
|
+
next.push({ text: decoded, executed: runsCode });
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
const expanded = expandVars(item.text, opts);
|
|
219
|
+
if (expanded !== undefined) {
|
|
220
|
+
// A folded variable is the same command the shell will run, not a hidden
|
|
221
|
+
// payload, so it always counts as executed.
|
|
222
|
+
push({ text: expanded, via: 'varsub', executed: true });
|
|
223
|
+
next.push({ text: expanded, executed: true });
|
|
224
|
+
}
|
|
225
|
+
if (out.length >= opts.maxVariants)
|
|
226
|
+
break;
|
|
227
|
+
}
|
|
228
|
+
frontier = next;
|
|
229
|
+
}
|
|
230
|
+
return out.slice(0, opts.maxVariants);
|
|
231
|
+
}
|
|
232
|
+
/**
|
|
233
|
+
* Whether a command is the plugin's own AppContainer launcher wrapper.
|
|
234
|
+
* Informational only — the payload is still decoded and judged on its merits.
|
|
235
|
+
*/
|
|
236
|
+
export function isOwnLauncher(text) {
|
|
237
|
+
return /--exec-b64\s+[A-Za-z0-9+/=]{8,}/.test(text) && /--config\s+/.test(text);
|
|
238
|
+
}
|
|
239
|
+
/** Variants strict enough to feed a deterministic deny: the payload actually runs. */
|
|
240
|
+
export function executableVariants(variants) {
|
|
241
|
+
return variants.filter((v) => v.executed).map((v) => v.text);
|
|
242
|
+
}
|
|
243
|
+
/** All decoded variants, for the additive-only keyword/tier scans. */
|
|
244
|
+
export function allVariants(variants) {
|
|
245
|
+
return variants.map((v) => v.text);
|
|
246
|
+
}
|
package/lib/config.d.ts
CHANGED
|
@@ -95,6 +95,13 @@ export interface PermGateConfig {
|
|
|
95
95
|
* (possibly empty) replaces it. Editable from the settings card as a list.
|
|
96
96
|
*/
|
|
97
97
|
readonly denyKeywords?: string[];
|
|
98
|
+
/**
|
|
99
|
+
* One-shot marker for the preset deny-table seed (`preset-rules.ts`, ported
|
|
100
|
+
* from cc-safety-net): set once the `[deny-rule:*]` entries have been merged
|
|
101
|
+
* into the live rules source. A full revocation (deleting every preset entry)
|
|
102
|
+
* persists — the seed never re-adds them.
|
|
103
|
+
*/
|
|
104
|
+
readonly presetRulesSeeded?: boolean;
|
|
98
105
|
/**
|
|
99
106
|
* Extra tool names classified as read-only/internal and therefore auto-allowed.
|
|
100
107
|
*
|
|
@@ -332,126 +339,128 @@ export declare function readRulesFromSettings(scope: {
|
|
|
332
339
|
export declare const Config: z<Schemastery.ObjectS<NoInfer<{
|
|
333
340
|
rulesFile: z<string, string, "plain">;
|
|
334
341
|
dshHome: z<string, string, "plain">;
|
|
335
|
-
defaultAction: z<"allow" | "ask" | "deny", "allow" | "ask" | "deny", "
|
|
336
|
-
caseInsensitivePaths: z<boolean, boolean, "
|
|
337
|
-
classifierEnabled: z<boolean, boolean, "
|
|
338
|
-
classifierEndpoint: z<string, string, "
|
|
339
|
-
classifierModel: z<string, string, "
|
|
340
|
-
classifierApiKey: z<string, string, "
|
|
341
|
-
riskTimeoutMs: z<number, number, "
|
|
342
|
-
riskLearning: z<boolean, boolean, "
|
|
343
|
-
riskSediment: z<boolean, boolean, "
|
|
344
|
-
classifierSource: z<"host" | "custom", "host" | "custom", "
|
|
345
|
-
classifierProvider: z<string, string, "
|
|
346
|
-
riskThreshold: z<number, number, "
|
|
342
|
+
defaultAction: z<"allow" | "ask" | "deny", "allow" | "ask" | "deny", "defined">;
|
|
343
|
+
caseInsensitivePaths: z<boolean, boolean, "defined">;
|
|
344
|
+
classifierEnabled: z<boolean, boolean, "defined">;
|
|
345
|
+
classifierEndpoint: z<string, string, "plain">;
|
|
346
|
+
classifierModel: z<string, string, "defined">;
|
|
347
|
+
classifierApiKey: z<string, string, "plain">;
|
|
348
|
+
riskTimeoutMs: z<number, number, "defined">;
|
|
349
|
+
riskLearning: z<boolean, boolean, "defined">;
|
|
350
|
+
riskSediment: z<boolean, boolean, "defined">;
|
|
351
|
+
classifierSource: z<"host" | "custom", "host" | "custom", "defined">;
|
|
352
|
+
classifierProvider: z<string, string, "plain">;
|
|
353
|
+
riskThreshold: z<number, number, "defined">;
|
|
347
354
|
learningFile: z<string, string, "plain">;
|
|
348
355
|
eventsFile: z<string, string, "plain">;
|
|
349
|
-
grantTtlMs: z<number, number, "
|
|
350
|
-
grantMaxUses: z<number, number, "
|
|
351
|
-
permissive: z<boolean, boolean, "
|
|
356
|
+
grantTtlMs: z<number, number, "defined">;
|
|
357
|
+
grantMaxUses: z<number, number, "defined">;
|
|
358
|
+
permissive: z<boolean, boolean, "defined">;
|
|
352
359
|
gatePresets: z<string[], string[], "plain">;
|
|
353
|
-
permissiveStrategies: z<
|
|
360
|
+
permissiveStrategies: z<Schemastery.ObjectS<NoInfer<{
|
|
354
361
|
trustAutoAllow: z<boolean, boolean, "defined">;
|
|
355
362
|
alwaysConfirm: z<boolean, boolean, "defined">;
|
|
356
363
|
llmAssist: z<boolean, boolean, "defined">;
|
|
357
364
|
trustEscalation: z<boolean, boolean, "defined">;
|
|
358
|
-
}
|
|
365
|
+
}>>, Schemastery.ObjectT<NoInfer<{
|
|
359
366
|
trustAutoAllow: z<boolean, boolean, "defined">;
|
|
360
367
|
alwaysConfirm: z<boolean, boolean, "defined">;
|
|
361
368
|
llmAssist: z<boolean, boolean, "defined">;
|
|
362
369
|
trustEscalation: z<boolean, boolean, "defined">;
|
|
363
|
-
}
|
|
364
|
-
allowlist: z<
|
|
365
|
-
denyKeywords: z<
|
|
366
|
-
|
|
367
|
-
|
|
370
|
+
}>>, "plain">;
|
|
371
|
+
allowlist: z<string[], string[], "plain">;
|
|
372
|
+
denyKeywords: z<string[], string[], "plain">;
|
|
373
|
+
presetRulesSeeded: z<boolean, boolean, "defined">;
|
|
374
|
+
autoAllowTools: z<string[], string[], "plain">;
|
|
375
|
+
sessionSweep: z<boolean, boolean, "defined">;
|
|
368
376
|
workspaceStoreFile: z<string, string, "plain">;
|
|
369
|
-
searchUp: z<boolean, boolean, "
|
|
377
|
+
searchUp: z<boolean, boolean, "defined">;
|
|
370
378
|
fallbackPath: z<string, string, "plain">;
|
|
371
|
-
badFilePolicy: z<"fail" | "warn", "fail" | "warn", "
|
|
372
|
-
maxChainLength: z<number, number, "
|
|
373
|
-
networkEnabled: z<boolean, boolean, "
|
|
374
|
-
networkMode: z<"deny-all" | "whitelist" | "allow-all", "deny-all" | "whitelist" | "allow-all", "
|
|
375
|
-
networkUnlisted: z<"ask" | "deny", "ask" | "deny", "
|
|
376
|
-
networkUnattributed: z<"allow" | "deny", "allow" | "deny", "
|
|
377
|
-
networkLoopback: z<"allow" | "policy", "allow" | "policy", "
|
|
378
|
-
networkBind: z<string, string, "
|
|
379
|
-
networkPort: z<number, number, "
|
|
380
|
-
networkNoProxy: z<"clear" | "preserve", "clear" | "preserve", "
|
|
381
|
-
networkInjectEnv: z<boolean, boolean, "
|
|
382
|
-
networkAskTimeoutMs: z<number, number, "
|
|
383
|
-
networkGrantTtlMs: z<number, number, "
|
|
384
|
-
sandboxEnabled: z<boolean, boolean, "
|
|
385
|
-
sandboxMode: z<"read-only" | "workspace-write", "read-only" | "workspace-write", "
|
|
386
|
-
sandboxProxy: z<string, string, "
|
|
387
|
-
sandboxLoopback: z<"exempt" | "off", "exempt" | "off", "
|
|
388
|
-
rules: z<
|
|
379
|
+
badFilePolicy: z<"fail" | "warn", "fail" | "warn", "defined">;
|
|
380
|
+
maxChainLength: z<number, number, "defined">;
|
|
381
|
+
networkEnabled: z<boolean, boolean, "defined">;
|
|
382
|
+
networkMode: z<"deny-all" | "whitelist" | "allow-all", "deny-all" | "whitelist" | "allow-all", "defined">;
|
|
383
|
+
networkUnlisted: z<"ask" | "deny", "ask" | "deny", "defined">;
|
|
384
|
+
networkUnattributed: z<"allow" | "deny", "allow" | "deny", "defined">;
|
|
385
|
+
networkLoopback: z<"allow" | "policy", "allow" | "policy", "defined">;
|
|
386
|
+
networkBind: z<string, string, "defined">;
|
|
387
|
+
networkPort: z<number, number, "defined">;
|
|
388
|
+
networkNoProxy: z<"clear" | "preserve", "clear" | "preserve", "defined">;
|
|
389
|
+
networkInjectEnv: z<boolean, boolean, "defined">;
|
|
390
|
+
networkAskTimeoutMs: z<number, number, "defined">;
|
|
391
|
+
networkGrantTtlMs: z<number, number, "defined">;
|
|
392
|
+
sandboxEnabled: z<boolean, boolean, "defined">;
|
|
393
|
+
sandboxMode: z<"read-only" | "workspace-write", "read-only" | "workspace-write", "defined">;
|
|
394
|
+
sandboxProxy: z<string, string, "defined">;
|
|
395
|
+
sandboxLoopback: z<"exempt" | "off", "exempt" | "off", "defined">;
|
|
396
|
+
rules: z<RulesConfig>;
|
|
389
397
|
watch: z<boolean, boolean, "defined">;
|
|
390
398
|
watchDebounceMs: z<number, number, "defined">;
|
|
391
399
|
}>>, Schemastery.ObjectT<NoInfer<{
|
|
392
400
|
rulesFile: z<string, string, "plain">;
|
|
393
401
|
dshHome: z<string, string, "plain">;
|
|
394
|
-
defaultAction: z<"allow" | "ask" | "deny", "allow" | "ask" | "deny", "
|
|
395
|
-
caseInsensitivePaths: z<boolean, boolean, "
|
|
396
|
-
classifierEnabled: z<boolean, boolean, "
|
|
397
|
-
classifierEndpoint: z<string, string, "
|
|
398
|
-
classifierModel: z<string, string, "
|
|
399
|
-
classifierApiKey: z<string, string, "
|
|
400
|
-
riskTimeoutMs: z<number, number, "
|
|
401
|
-
riskLearning: z<boolean, boolean, "
|
|
402
|
-
riskSediment: z<boolean, boolean, "
|
|
403
|
-
classifierSource: z<"host" | "custom", "host" | "custom", "
|
|
404
|
-
classifierProvider: z<string, string, "
|
|
405
|
-
riskThreshold: z<number, number, "
|
|
402
|
+
defaultAction: z<"allow" | "ask" | "deny", "allow" | "ask" | "deny", "defined">;
|
|
403
|
+
caseInsensitivePaths: z<boolean, boolean, "defined">;
|
|
404
|
+
classifierEnabled: z<boolean, boolean, "defined">;
|
|
405
|
+
classifierEndpoint: z<string, string, "plain">;
|
|
406
|
+
classifierModel: z<string, string, "defined">;
|
|
407
|
+
classifierApiKey: z<string, string, "plain">;
|
|
408
|
+
riskTimeoutMs: z<number, number, "defined">;
|
|
409
|
+
riskLearning: z<boolean, boolean, "defined">;
|
|
410
|
+
riskSediment: z<boolean, boolean, "defined">;
|
|
411
|
+
classifierSource: z<"host" | "custom", "host" | "custom", "defined">;
|
|
412
|
+
classifierProvider: z<string, string, "plain">;
|
|
413
|
+
riskThreshold: z<number, number, "defined">;
|
|
406
414
|
learningFile: z<string, string, "plain">;
|
|
407
415
|
eventsFile: z<string, string, "plain">;
|
|
408
|
-
grantTtlMs: z<number, number, "
|
|
409
|
-
grantMaxUses: z<number, number, "
|
|
410
|
-
permissive: z<boolean, boolean, "
|
|
416
|
+
grantTtlMs: z<number, number, "defined">;
|
|
417
|
+
grantMaxUses: z<number, number, "defined">;
|
|
418
|
+
permissive: z<boolean, boolean, "defined">;
|
|
411
419
|
gatePresets: z<string[], string[], "plain">;
|
|
412
|
-
permissiveStrategies: z<
|
|
420
|
+
permissiveStrategies: z<Schemastery.ObjectS<NoInfer<{
|
|
413
421
|
trustAutoAllow: z<boolean, boolean, "defined">;
|
|
414
422
|
alwaysConfirm: z<boolean, boolean, "defined">;
|
|
415
423
|
llmAssist: z<boolean, boolean, "defined">;
|
|
416
424
|
trustEscalation: z<boolean, boolean, "defined">;
|
|
417
|
-
}
|
|
425
|
+
}>>, Schemastery.ObjectT<NoInfer<{
|
|
418
426
|
trustAutoAllow: z<boolean, boolean, "defined">;
|
|
419
427
|
alwaysConfirm: z<boolean, boolean, "defined">;
|
|
420
428
|
llmAssist: z<boolean, boolean, "defined">;
|
|
421
429
|
trustEscalation: z<boolean, boolean, "defined">;
|
|
422
|
-
}
|
|
423
|
-
allowlist: z<
|
|
424
|
-
denyKeywords: z<
|
|
425
|
-
|
|
426
|
-
|
|
430
|
+
}>>, "plain">;
|
|
431
|
+
allowlist: z<string[], string[], "plain">;
|
|
432
|
+
denyKeywords: z<string[], string[], "plain">;
|
|
433
|
+
presetRulesSeeded: z<boolean, boolean, "defined">;
|
|
434
|
+
autoAllowTools: z<string[], string[], "plain">;
|
|
435
|
+
sessionSweep: z<boolean, boolean, "defined">;
|
|
427
436
|
workspaceStoreFile: z<string, string, "plain">;
|
|
428
|
-
searchUp: z<boolean, boolean, "
|
|
437
|
+
searchUp: z<boolean, boolean, "defined">;
|
|
429
438
|
fallbackPath: z<string, string, "plain">;
|
|
430
|
-
badFilePolicy: z<"fail" | "warn", "fail" | "warn", "
|
|
431
|
-
maxChainLength: z<number, number, "
|
|
432
|
-
networkEnabled: z<boolean, boolean, "
|
|
433
|
-
networkMode: z<"deny-all" | "whitelist" | "allow-all", "deny-all" | "whitelist" | "allow-all", "
|
|
434
|
-
networkUnlisted: z<"ask" | "deny", "ask" | "deny", "
|
|
435
|
-
networkUnattributed: z<"allow" | "deny", "allow" | "deny", "
|
|
436
|
-
networkLoopback: z<"allow" | "policy", "allow" | "policy", "
|
|
437
|
-
networkBind: z<string, string, "
|
|
438
|
-
networkPort: z<number, number, "
|
|
439
|
-
networkNoProxy: z<"clear" | "preserve", "clear" | "preserve", "
|
|
440
|
-
networkInjectEnv: z<boolean, boolean, "
|
|
441
|
-
networkAskTimeoutMs: z<number, number, "
|
|
442
|
-
networkGrantTtlMs: z<number, number, "
|
|
443
|
-
sandboxEnabled: z<boolean, boolean, "
|
|
444
|
-
sandboxMode: z<"read-only" | "workspace-write", "read-only" | "workspace-write", "
|
|
445
|
-
sandboxProxy: z<string, string, "
|
|
446
|
-
sandboxLoopback: z<"exempt" | "off", "exempt" | "off", "
|
|
447
|
-
rules: z<
|
|
439
|
+
badFilePolicy: z<"fail" | "warn", "fail" | "warn", "defined">;
|
|
440
|
+
maxChainLength: z<number, number, "defined">;
|
|
441
|
+
networkEnabled: z<boolean, boolean, "defined">;
|
|
442
|
+
networkMode: z<"deny-all" | "whitelist" | "allow-all", "deny-all" | "whitelist" | "allow-all", "defined">;
|
|
443
|
+
networkUnlisted: z<"ask" | "deny", "ask" | "deny", "defined">;
|
|
444
|
+
networkUnattributed: z<"allow" | "deny", "allow" | "deny", "defined">;
|
|
445
|
+
networkLoopback: z<"allow" | "policy", "allow" | "policy", "defined">;
|
|
446
|
+
networkBind: z<string, string, "defined">;
|
|
447
|
+
networkPort: z<number, number, "defined">;
|
|
448
|
+
networkNoProxy: z<"clear" | "preserve", "clear" | "preserve", "defined">;
|
|
449
|
+
networkInjectEnv: z<boolean, boolean, "defined">;
|
|
450
|
+
networkAskTimeoutMs: z<number, number, "defined">;
|
|
451
|
+
networkGrantTtlMs: z<number, number, "defined">;
|
|
452
|
+
sandboxEnabled: z<boolean, boolean, "defined">;
|
|
453
|
+
sandboxMode: z<"read-only" | "workspace-write", "read-only" | "workspace-write", "defined">;
|
|
454
|
+
sandboxProxy: z<string, string, "defined">;
|
|
455
|
+
sandboxLoopback: z<"exempt" | "off", "exempt" | "off", "defined">;
|
|
456
|
+
rules: z<RulesConfig>;
|
|
448
457
|
watch: z<boolean, boolean, "defined">;
|
|
449
458
|
watchDebounceMs: z<number, number, "defined">;
|
|
450
459
|
}>>, "plain">;
|
|
451
460
|
/** Live reference the 0.1.7 loader hands `apply` for `.volatile()` config fields. */
|
|
452
461
|
export type VolatileRef<T> = Volatile<T>;
|
|
453
462
|
/** The config fields marked `.volatile()` — live refs inside `apply`'s config. */
|
|
454
|
-
export declare const VOLATILE_CONFIG_KEYS: readonly ["defaultAction", "caseInsensitivePaths", "classifierEnabled", "classifierEndpoint", "classifierModel", "classifierApiKey", "riskTimeoutMs", "riskLearning", "riskSediment", "classifierSource", "classifierProvider", "riskThreshold", "grantTtlMs", "grantMaxUses", "permissive", "permissiveStrategies", "allowlist", "denyKeywords", "autoAllowTools", "sessionSweep", "searchUp", "badFilePolicy", "maxChainLength", "networkEnabled", "networkMode", "networkUnlisted", "networkUnattributed", "networkLoopback", "networkBind", "networkPort", "networkNoProxy", "networkInjectEnv", "networkAskTimeoutMs", "networkGrantTtlMs", "rules", "sandboxEnabled", "sandboxMode", "sandboxProxy", "sandboxLoopback"];
|
|
463
|
+
export declare const VOLATILE_CONFIG_KEYS: readonly ["defaultAction", "caseInsensitivePaths", "classifierEnabled", "classifierEndpoint", "classifierModel", "classifierApiKey", "riskTimeoutMs", "riskLearning", "riskSediment", "classifierSource", "classifierProvider", "riskThreshold", "grantTtlMs", "grantMaxUses", "permissive", "permissiveStrategies", "allowlist", "denyKeywords", "presetRulesSeeded", "autoAllowTools", "sessionSweep", "searchUp", "badFilePolicy", "maxChainLength", "networkEnabled", "networkMode", "networkUnlisted", "networkUnattributed", "networkLoopback", "networkBind", "networkPort", "networkNoProxy", "networkInjectEnv", "networkAskTimeoutMs", "networkGrantTtlMs", "rules", "sandboxEnabled", "sandboxMode", "sandboxProxy", "sandboxLoopback"];
|
|
455
464
|
/** Resolve one possibly-volatile field: a live ref on 0.1.7+, a plain value otherwise. */
|
|
456
465
|
export declare function readVolatileValue<T>(value: T | Volatile<T> | undefined): VolatileSnapshot<T> | undefined;
|
|
457
466
|
/** Shallow-resolve every volatile field into a plain snapshot (one per read). */
|