@dsh-cc/permission-rules 0.5.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/LICENSE +201 -0
- package/README.i18n.yaml +6 -0
- package/README.md +86 -0
- package/README.zh.md +86 -0
- package/lib/approval-listener.d.ts +50 -0
- package/lib/approval-listener.d.ts.map +1 -0
- package/lib/approval-listener.js +58 -0
- package/lib/approval-listener.js.map +1 -0
- package/lib/auto-stage.d.ts +138 -0
- package/lib/auto-stage.d.ts.map +1 -0
- package/lib/auto-stage.js +284 -0
- package/lib/auto-stage.js.map +1 -0
- package/lib/classifier.d.ts +57 -0
- package/lib/classifier.d.ts.map +1 -0
- package/lib/classifier.js +129 -0
- package/lib/classifier.js.map +1 -0
- package/lib/decide.d.ts +80 -0
- package/lib/decide.d.ts.map +1 -0
- package/lib/decide.js +127 -0
- package/lib/decide.js.map +1 -0
- package/lib/domain.d.ts +46 -0
- package/lib/domain.d.ts.map +1 -0
- package/lib/domain.js +103 -0
- package/lib/domain.js.map +1 -0
- package/lib/evaluate.d.ts +32 -0
- package/lib/evaluate.d.ts.map +1 -0
- package/lib/evaluate.js +176 -0
- package/lib/evaluate.js.map +1 -0
- package/lib/index.d.ts +123 -0
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js +380 -0
- package/lib/index.js.map +1 -0
- package/lib/invariant.d.ts +28 -0
- package/lib/invariant.d.ts.map +1 -0
- package/lib/invariant.js +54 -0
- package/lib/invariant.js.map +1 -0
- package/lib/llm-classifier.d.ts +107 -0
- package/lib/llm-classifier.d.ts.map +1 -0
- package/lib/llm-classifier.js +231 -0
- package/lib/llm-classifier.js.map +1 -0
- package/lib/matchers.d.ts +18 -0
- package/lib/matchers.d.ts.map +1 -0
- package/lib/matchers.js +43 -0
- package/lib/matchers.js.map +1 -0
- package/lib/mode.d.ts +91 -0
- package/lib/mode.d.ts.map +1 -0
- package/lib/mode.js +133 -0
- package/lib/mode.js.map +1 -0
- package/lib/parser.d.ts +91 -0
- package/lib/parser.d.ts.map +1 -0
- package/lib/parser.js +282 -0
- package/lib/parser.js.map +1 -0
- package/lib/session-allowlist.d.ts +76 -0
- package/lib/session-allowlist.d.ts.map +1 -0
- package/lib/session-allowlist.js +122 -0
- package/lib/session-allowlist.js.map +1 -0
- package/lib/settings-schema.d.ts +99 -0
- package/lib/settings-schema.d.ts.map +1 -0
- package/lib/settings-schema.js +64 -0
- package/lib/settings-schema.js.map +1 -0
- package/lib/types.d.ts +150 -0
- package/lib/types.d.ts.map +1 -0
- package/lib/types.js +33 -0
- package/lib/types.js.map +1 -0
- package/package.json +71 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auto-stage.d.ts","sourceRoot":"","sources":["../src/auto-stage.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The async LLM-classifier stage for `auto` mode: owns arming (per call, from
|
|
3
|
+
* the live `permissions.autoMode` settings slice), the memoized
|
|
4
|
+
* `createLlmClassifier` instance, and the `permission/classifier` session
|
|
5
|
+
* audit event. The plugin's `tools/pre-execute` listener (index.ts) only
|
|
6
|
+
* wires this stage — the escalate-only decision flow lives here.
|
|
7
|
+
*
|
|
8
|
+
* Arming predicate (§4.4): `autoMode.classifier.enabled === true` AND an llm
|
|
9
|
+
* stream capability is wired AND the alias route resolves. Enabled but
|
|
10
|
+
* unarmable ⇒ disarm with ONE warning per process (plus an `unarmed` audit
|
|
11
|
+
* event when a session is available) and the legacy decision path runs.
|
|
12
|
+
*
|
|
13
|
+
* @module @dsh-cc/permission-rules/auto-stage
|
|
14
|
+
*/
|
|
15
|
+
import { KNOWN_SESSION_EVENT_TYPES } from '@deepseek-ai/dsh-session';
|
|
16
|
+
import { createLlmClassifier, expandSoftDeny } from "./llm-classifier.js";
|
|
17
|
+
/** The session event type carrying one classifier verdict audit record. */
|
|
18
|
+
export const CLASSIFIER_EVENT = 'permission/classifier';
|
|
19
|
+
/** Consecutive per-route classifier failures before that route's breaker opens (module constant — no settings knob by design). */
|
|
20
|
+
export const CLASSIFIER_BREAKER_THRESHOLD = 3;
|
|
21
|
+
KNOWN_SESSION_EVENT_TYPES.add(CLASSIFIER_EVENT);
|
|
22
|
+
/**
|
|
23
|
+
* Append one `permission/classifier` audit record through the widened session
|
|
24
|
+
* append face (same cross-pin strategy as `./mode.ts` and
|
|
25
|
+
* `./session-allowlist.ts`).
|
|
26
|
+
*/
|
|
27
|
+
export function appendSessionClassifier(session, data) {
|
|
28
|
+
session.append(CLASSIFIER_EVENT, data);
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Fold a session log into the classifier verdict records it carries, in log
|
|
32
|
+
* order. Foreign event types are skipped; resume/replay reconstructs why a
|
|
33
|
+
* call did or did not prompt.
|
|
34
|
+
*/
|
|
35
|
+
export function foldClassifiers(events) {
|
|
36
|
+
const out = [];
|
|
37
|
+
for (const event of events) {
|
|
38
|
+
const wire = event;
|
|
39
|
+
if (wire.type !== CLASSIFIER_EVENT || typeof wire.data !== 'object' || wire.data === null)
|
|
40
|
+
continue;
|
|
41
|
+
out.push(wire.data);
|
|
42
|
+
}
|
|
43
|
+
return out;
|
|
44
|
+
}
|
|
45
|
+
function readSlice(settings) {
|
|
46
|
+
const autoMode = settings.autoMode;
|
|
47
|
+
const classifier = autoMode?.classifier;
|
|
48
|
+
const softDeny = expandSoftDeny(autoMode?.soft_deny ?? ['$defaults']);
|
|
49
|
+
return {
|
|
50
|
+
softDeny,
|
|
51
|
+
route: classifier?.route ?? 'haiku',
|
|
52
|
+
timeoutMs: classifier?.timeoutMs ?? 8000,
|
|
53
|
+
cacheMaxEntries: classifier?.cacheMaxEntries ?? 256,
|
|
54
|
+
enabled: classifier?.enabled === true,
|
|
55
|
+
raw: JSON.stringify([autoMode?.soft_deny, classifier]),
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
/** The failure tags the breaker counts; `cancelled`/`unarmed` are host noise and never count. */
|
|
59
|
+
const BREAKER_FAILURE_TAGS = ['malformed', 'error', 'timeout'];
|
|
60
|
+
/**
|
|
61
|
+
* Trailing consecutive per-route failure streak over attributed classifier
|
|
62
|
+
* audit records (R3, pure fold — unit-testable). Only events carrying
|
|
63
|
+
* `provider`/`model` attribution matching `routeKey` count (unattributed
|
|
64
|
+
* legacy events predate route keying — skipped entirely); a parsed verdict or
|
|
65
|
+
* cache hit resets the streak; malformed/error/timeout increment it; other
|
|
66
|
+
* tags (`cancelled`, `breaker`, `unarmed`) are neutral. Capped at `threshold`.
|
|
67
|
+
*/
|
|
68
|
+
export function trailingRouteFailureStreak(events, routeKey, threshold) {
|
|
69
|
+
let streak = 0;
|
|
70
|
+
for (const event of events) {
|
|
71
|
+
if (event.provider === undefined || event.model === undefined)
|
|
72
|
+
continue;
|
|
73
|
+
if (`${event.provider}/${event.model}` !== routeKey)
|
|
74
|
+
continue;
|
|
75
|
+
if (event.failure === undefined)
|
|
76
|
+
streak = 0;
|
|
77
|
+
else if (BREAKER_FAILURE_TAGS.includes(event.failure))
|
|
78
|
+
streak = Math.min(streak + 1, threshold);
|
|
79
|
+
}
|
|
80
|
+
return streak;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Build the stage. The classifier instance is memoized per autoMode slice:
|
|
84
|
+
* `rebuild()` (wired to the plugin's settings onChange/reload) drops it, and
|
|
85
|
+
* the next armed call rebuilds from the fresh slice — never per call.
|
|
86
|
+
*/
|
|
87
|
+
export function createAutoStage(deps) {
|
|
88
|
+
let slice = readSlice(deps.settingsRead());
|
|
89
|
+
/** The raw autoMode slice the memoized classifier was built from. */
|
|
90
|
+
let builtRaw = slice.raw;
|
|
91
|
+
let classifier;
|
|
92
|
+
/** Warned-once flag for enabled-but-unarmable (per process). */
|
|
93
|
+
let warnedUnarmed = false;
|
|
94
|
+
/** Consecutive classifier failures per route (`${provider}/${model}`); any success resets the route to 0. */
|
|
95
|
+
const routeFailures = new Map();
|
|
96
|
+
/** Routes whose breaker is open: maybeEscalate returns undefined without touching the stream. */
|
|
97
|
+
const breakerOpen = new Set();
|
|
98
|
+
/** Session ids that already recorded one `breaker` audit event (one per session). */
|
|
99
|
+
const breakerAudited = new Set();
|
|
100
|
+
/** Warned-once flag for an opened breaker (per process). */
|
|
101
|
+
let warnedBreaker = false;
|
|
102
|
+
/** Session ids whose durable log already seeded this process's breaker state (R3). */
|
|
103
|
+
const seededSessions = new Set();
|
|
104
|
+
const ensureClassifier = () => {
|
|
105
|
+
if (classifier !== undefined && builtRaw === slice.raw)
|
|
106
|
+
return classifier;
|
|
107
|
+
classifier = createLlmClassifier({
|
|
108
|
+
// The route for each call is passed as data to classify() (per-call
|
|
109
|
+
// argument, never ambient state) and the caller audits from the returned
|
|
110
|
+
// classification — no session/route fields live on this stage, so
|
|
111
|
+
// concurrent calls cannot cross-contaminate audit attribution.
|
|
112
|
+
stream: (opts) => {
|
|
113
|
+
const stream = deps.stream;
|
|
114
|
+
if (stream === undefined)
|
|
115
|
+
throw new Error('llm service unmounted');
|
|
116
|
+
return stream(opts);
|
|
117
|
+
},
|
|
118
|
+
softDeny: slice.softDeny,
|
|
119
|
+
timeoutMs: slice.timeoutMs,
|
|
120
|
+
cacheMaxEntries: slice.cacheMaxEntries,
|
|
121
|
+
...(deps.debug === undefined ? {} : { debug: deps.debug }),
|
|
122
|
+
});
|
|
123
|
+
builtRaw = slice.raw;
|
|
124
|
+
return classifier;
|
|
125
|
+
};
|
|
126
|
+
const disarmUnarmed = (exec) => {
|
|
127
|
+
if (!warnedUnarmed) {
|
|
128
|
+
warnedUnarmed = true;
|
|
129
|
+
deps.warn('permission classifier: enabled but unarmable (llm service or model route unavailable); stage disarmed, auto mode uses the legacy path');
|
|
130
|
+
}
|
|
131
|
+
const session = exec.agent?.session;
|
|
132
|
+
if (session !== undefined) {
|
|
133
|
+
deps.audit(session, {
|
|
134
|
+
tool: exec.name,
|
|
135
|
+
verdict: 'ask',
|
|
136
|
+
failure: 'unarmed',
|
|
137
|
+
latencyMs: 0,
|
|
138
|
+
cacheHit: false,
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
};
|
|
142
|
+
/**
|
|
143
|
+
* Seed the per-route breaker state from the session's durable log (R3) —
|
|
144
|
+
* lazily, once per session, on its first breaker-eligible call. Synchronous
|
|
145
|
+
* guard: the session id enters `seededSessions` BEFORE any suspension and
|
|
146
|
+
* the fold is over in-memory events, so concurrent first-calls cannot
|
|
147
|
+
* double-seed. A restored streak ≥ threshold opens the route at seed time
|
|
148
|
+
* (its first real call then audits/warns/notices exactly once); a log that
|
|
149
|
+
* already holds a `breaker` event pre-joins `breakerAudited` so replay never
|
|
150
|
+
* re-audits the same open. Never overwrites a live counter: seed only when
|
|
151
|
+
* the session is unseen AND the route counter is 0/absent — in-process
|
|
152
|
+
* accrual is fresher (fail-open undercounting is the accepted direction).
|
|
153
|
+
*/
|
|
154
|
+
function seedBreakerFromLog(exec, routeKey, route) {
|
|
155
|
+
const session = exec.agent?.session;
|
|
156
|
+
if (session === undefined)
|
|
157
|
+
return;
|
|
158
|
+
const sessionId = String(session.header.id);
|
|
159
|
+
if (seededSessions.has(sessionId))
|
|
160
|
+
return;
|
|
161
|
+
seededSessions.add(sessionId);
|
|
162
|
+
if ((routeFailures.get(routeKey) ?? 0) > 0)
|
|
163
|
+
return;
|
|
164
|
+
const events = foldClassifiers(session.events);
|
|
165
|
+
if (events.some(event => event.failure === 'breaker'))
|
|
166
|
+
breakerAudited.add(sessionId);
|
|
167
|
+
const streak = trailingRouteFailureStreak(events, routeKey, CLASSIFIER_BREAKER_THRESHOLD);
|
|
168
|
+
if (streak <= 0)
|
|
169
|
+
return;
|
|
170
|
+
routeFailures.set(routeKey, streak);
|
|
171
|
+
if (streak < CLASSIFIER_BREAKER_THRESHOLD)
|
|
172
|
+
return;
|
|
173
|
+
breakerOpen.add(routeKey);
|
|
174
|
+
if (!warnedBreaker) {
|
|
175
|
+
warnedBreaker = true;
|
|
176
|
+
deps.warn(`permission classifier: route ${routeKey} restored with ${streak} consecutive failures from the session log; breaker open for this route, auto mode uses the legacy path`);
|
|
177
|
+
}
|
|
178
|
+
auditBreakerOnce(exec, routeKey, route);
|
|
179
|
+
}
|
|
180
|
+
return {
|
|
181
|
+
rebuild() {
|
|
182
|
+
// Drop the memoized classifier only when the autoMode slice actually
|
|
183
|
+
// changed — an onChange for unrelated keys keeps the instance.
|
|
184
|
+
const current = readSlice(deps.settingsRead());
|
|
185
|
+
if (current.raw !== builtRaw) {
|
|
186
|
+
slice = current;
|
|
187
|
+
classifier = undefined;
|
|
188
|
+
}
|
|
189
|
+
// A settings change is the operator's "I fixed the lane": reset ALL
|
|
190
|
+
// breaker state — route counters, open routes, the per-session audit
|
|
191
|
+
// de-dup set, and the per-process warn-once flag.
|
|
192
|
+
routeFailures.clear();
|
|
193
|
+
breakerOpen.clear();
|
|
194
|
+
breakerAudited.clear();
|
|
195
|
+
warnedBreaker = false;
|
|
196
|
+
},
|
|
197
|
+
async maybeEscalate(decided, exec) {
|
|
198
|
+
slice = readSlice(deps.settingsRead());
|
|
199
|
+
if (!slice.enabled)
|
|
200
|
+
return undefined;
|
|
201
|
+
if (deps.stream === undefined) {
|
|
202
|
+
disarmUnarmed(exec);
|
|
203
|
+
return undefined;
|
|
204
|
+
}
|
|
205
|
+
// The route for this call is passed to classify as data; the audit event
|
|
206
|
+
// is appended from this call's own exec session — no ambient fields.
|
|
207
|
+
const route = deps.resolveRoute(exec);
|
|
208
|
+
if (route === undefined) {
|
|
209
|
+
disarmUnarmed(exec);
|
|
210
|
+
return undefined;
|
|
211
|
+
}
|
|
212
|
+
// Eligibility (§4.1): only auto + LOW + ask/passthrough reaches the LLM.
|
|
213
|
+
// These gates precede the read-only exemption and the breaker gate.
|
|
214
|
+
if (decided.mode !== 'auto' || decided.risk.level !== 'LOW')
|
|
215
|
+
return undefined;
|
|
216
|
+
if (decided.decision.kind !== 'ask' && decided.decision.kind !== 'passthrough')
|
|
217
|
+
return undefined;
|
|
218
|
+
// F2 read-only exemption: read-only calls cannot mutate, so the LLM
|
|
219
|
+
// round-trip adds latency with zero safety — the legacy path applies.
|
|
220
|
+
if (decided.isReadOnly)
|
|
221
|
+
return undefined;
|
|
222
|
+
const routeKey = `${route.provider}/${route.model}`;
|
|
223
|
+
seedBreakerFromLog(exec, routeKey, route);
|
|
224
|
+
if (breakerOpen.has(routeKey)) {
|
|
225
|
+
auditBreakerOnce(exec, routeKey, route);
|
|
226
|
+
return undefined;
|
|
227
|
+
}
|
|
228
|
+
const verdict = await ensureClassifier().classify(exec, { route });
|
|
229
|
+
// F4 per-route breaker bookkeeping, attributed to THIS call's route:
|
|
230
|
+
// any success (parsed verdict, cache hit included) resets the streak;
|
|
231
|
+
// malformed/error/timeout increment it; `cancelled` is caller noise and
|
|
232
|
+
// `unarmed` is a disarm outcome — neither is counted (nor resets).
|
|
233
|
+
if (verdict.failure === undefined) {
|
|
234
|
+
routeFailures.set(routeKey, 0);
|
|
235
|
+
}
|
|
236
|
+
else if (BREAKER_FAILURE_TAGS.includes(verdict.failure)) {
|
|
237
|
+
const count = (routeFailures.get(routeKey) ?? 0) + 1;
|
|
238
|
+
routeFailures.set(routeKey, count);
|
|
239
|
+
if (count >= CLASSIFIER_BREAKER_THRESHOLD) {
|
|
240
|
+
breakerOpen.add(routeKey);
|
|
241
|
+
if (!warnedBreaker) {
|
|
242
|
+
warnedBreaker = true;
|
|
243
|
+
deps.warn(`permission classifier: route ${routeKey} failed ${CLASSIFIER_BREAKER_THRESHOLD} consecutive classifications; breaker open for this route, auto mode uses the legacy path`);
|
|
244
|
+
}
|
|
245
|
+
auditBreakerOnce(exec, routeKey, route);
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
const session = exec.agent?.session;
|
|
249
|
+
if (session !== undefined) {
|
|
250
|
+
deps.audit(session, {
|
|
251
|
+
tool: verdict.tool,
|
|
252
|
+
digest: verdict.digest,
|
|
253
|
+
verdict: verdict.verdict,
|
|
254
|
+
...(verdict.failure === undefined ? {} : { failure: verdict.failure }),
|
|
255
|
+
...(verdict.routeAlias === undefined ? {} : { route: verdict.routeAlias, provider: verdict.provider, model: verdict.model }),
|
|
256
|
+
latencyMs: verdict.latencyMs,
|
|
257
|
+
cacheHit: verdict.cacheHit,
|
|
258
|
+
});
|
|
259
|
+
}
|
|
260
|
+
return verdict.verdict === 'allow' ? 'allow' : { kind: 'ask', reason: verdict.reason };
|
|
261
|
+
},
|
|
262
|
+
};
|
|
263
|
+
/** One `breaker` audit event per session (de-dup by session id), no stream involved. */
|
|
264
|
+
function auditBreakerOnce(exec, routeKey, route) {
|
|
265
|
+
const session = exec.agent?.session;
|
|
266
|
+
if (session === undefined)
|
|
267
|
+
return;
|
|
268
|
+
const sessionId = String(session.header.id);
|
|
269
|
+
if (breakerAudited.has(sessionId))
|
|
270
|
+
return;
|
|
271
|
+
breakerAudited.add(sessionId);
|
|
272
|
+
deps.audit(session, {
|
|
273
|
+
tool: exec.name,
|
|
274
|
+
verdict: 'ask',
|
|
275
|
+
failure: 'breaker',
|
|
276
|
+
route: routeKey,
|
|
277
|
+
provider: route.provider,
|
|
278
|
+
model: route.model,
|
|
279
|
+
latencyMs: 0,
|
|
280
|
+
cacheHit: false,
|
|
281
|
+
});
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
//# sourceMappingURL=auto-stage.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auto-stage.js","sourceRoot":"","sources":["../src/auto-stage.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure risk classifier for tool calls, a conservative port of Claude Code's
|
|
3
|
+
* `dangerousPatterns` / `pathValidation` heuristics. High-risk shell commands
|
|
4
|
+
* and protected/externally-scoped file writes escalate before the normal rule
|
|
5
|
+
* waterfall. Pure functions — no Cordis/session coupling — mirroring the
|
|
6
|
+
* {@link evaluatePermission} style, so hosts can classify a call directly.
|
|
7
|
+
* @module @dsh-cc/permission-rules/classifier
|
|
8
|
+
*/
|
|
9
|
+
/** The severity a classifier assigns to one call. */
|
|
10
|
+
export type RiskLevel = 'LOW' | 'MEDIUM' | 'HIGH';
|
|
11
|
+
/** The outcome of classifying one call, with the reasons that raised it. */
|
|
12
|
+
export interface RiskAssessment {
|
|
13
|
+
/** The raised risk level for the call. */
|
|
14
|
+
level: RiskLevel;
|
|
15
|
+
/** Human-readable reasons for a non-`LOW` level; empty for `LOW`. */
|
|
16
|
+
reasons: string[];
|
|
17
|
+
}
|
|
18
|
+
/** One curated dangerous-command pattern and the reason it is high risk. */
|
|
19
|
+
export interface DangerousPattern {
|
|
20
|
+
readonly regex: RegExp;
|
|
21
|
+
readonly reason: string;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* The curated conservative set of catastrophic shell command patterns. Matched
|
|
25
|
+
* against a command string; any match raises the command to `HIGH`. Ports the
|
|
26
|
+
* spirit of Claude Code's table, deliberately conservative (catastrophic only).
|
|
27
|
+
*/
|
|
28
|
+
export declare const DEFAULT_DANGEROUS_PATTERNS: readonly DangerousPattern[];
|
|
29
|
+
/**
|
|
30
|
+
* The curated set of protected file paths — dotfiles, credential stores, and
|
|
31
|
+
* sensitive config. Simple wildcard matching: `**` matches any depth, `*`
|
|
32
|
+
* matches a single path segment. A match raises a file write to `HIGH`.
|
|
33
|
+
*/
|
|
34
|
+
export declare const DEFAULT_PROTECTED_FILES: readonly string[];
|
|
35
|
+
/**
|
|
36
|
+
* Classify a shell command by its riskiness. HIGH when any dangerous pattern
|
|
37
|
+
* matches, otherwise LOW. When `patterns` is given (raw regex sources, e.g.
|
|
38
|
+
* from `permissions.dangerousPatterns`), they replace the curated defaults.
|
|
39
|
+
* @param command - the shell command string to classify.
|
|
40
|
+
* @param patterns - optional raw regex sources to use instead of the defaults.
|
|
41
|
+
* @returns the assessment — HIGH with the matching reasons, else LOW.
|
|
42
|
+
*/
|
|
43
|
+
export declare function assessBashCommand(command: string, patterns?: string[]): RiskAssessment;
|
|
44
|
+
/**
|
|
45
|
+
* Classify the target path of a file write. HIGH when it matches a protected
|
|
46
|
+
* file; MEDIUM when it resolves outside the working directory and its
|
|
47
|
+
* additional directories (an escape from the permission scope); else LOW.
|
|
48
|
+
* @param filePath - the target file path (absolute or relative).
|
|
49
|
+
* @param opts - the working directory scope and classification overrides.
|
|
50
|
+
* @returns the assessment with the matching reasons, or LOW.
|
|
51
|
+
*/
|
|
52
|
+
export declare function assessFilePath(filePath: string, opts: {
|
|
53
|
+
cwd: string;
|
|
54
|
+
additionalDirectories?: string[];
|
|
55
|
+
protectedFiles?: string[];
|
|
56
|
+
}): RiskAssessment;
|
|
57
|
+
//# sourceMappingURL=classifier.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"classifier.d.ts","sourceRoot":"","sources":["../src/classifier.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH,qDAAqD;AACrD,MAAM,MAAM,SAAS,GAAG,KAAK,GAAG,QAAQ,GAAG,MAAM,CAAA;AAEjD,4EAA4E;AAC5E,MAAM,WAAW,cAAc;IAC7B,0CAA0C;IAC1C,KAAK,EAAE,SAAS,CAAA;IAChB,qEAAqE;IACrE,OAAO,EAAE,MAAM,EAAE,CAAA;CAClB;AAED,4EAA4E;AAC5E,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;CACxB;AAED;;;;GAIG;AACH,eAAO,MAAM,0BAA0B,EAAE,SAAS,gBAAgB,EAUjE,CAAA;AAED;;;;GAIG;AACH,eAAO,MAAM,uBAAuB,EAAE,SAAS,MAAM,EAYpD,CAAA;AAED;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,EAAE,GAAG,cAAc,CAWtF;AAED;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAC5B,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,qBAAqB,CAAC,EAAE,MAAM,EAAE,CAAC;IAAC,cAAc,CAAC,EAAE,MAAM,EAAE,CAAA;CAAE,GACjF,cAAc,CAYhB"}
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure risk classifier for tool calls, a conservative port of Claude Code's
|
|
3
|
+
* `dangerousPatterns` / `pathValidation` heuristics. High-risk shell commands
|
|
4
|
+
* and protected/externally-scoped file writes escalate before the normal rule
|
|
5
|
+
* waterfall. Pure functions — no Cordis/session coupling — mirroring the
|
|
6
|
+
* {@link evaluatePermission} style, so hosts can classify a call directly.
|
|
7
|
+
* @module @dsh-cc/permission-rules/classifier
|
|
8
|
+
*/
|
|
9
|
+
import { isAbsolute, relative, resolve } from 'node:path';
|
|
10
|
+
/**
|
|
11
|
+
* The curated conservative set of catastrophic shell command patterns. Matched
|
|
12
|
+
* against a command string; any match raises the command to `HIGH`. Ports the
|
|
13
|
+
* spirit of Claude Code's table, deliberately conservative (catastrophic only).
|
|
14
|
+
*/
|
|
15
|
+
export const DEFAULT_DANGEROUS_PATTERNS = [
|
|
16
|
+
{ regex: /\brm\s+-[a-z]*[rf][a-z]*\s+(?:\/(?:\s|$)|~(?:\s|$|\/))/, reason: 'force/recursive remove of root or home' },
|
|
17
|
+
{ regex: /\bsudo\s+/i, reason: 'privilege escalation' },
|
|
18
|
+
{ regex: /\bchmod\s+[^\n]*\b777\b/, reason: 'world-writable permission change' },
|
|
19
|
+
{ regex: /\bdd\b[^\n]*\bof=\/dev\//, reason: 'write directly to a block device' },
|
|
20
|
+
{ regex: /\bmkfs(?:\.|$|\s)/, reason: 'create a file system (destructive)' },
|
|
21
|
+
{ regex: /\bshutdown\b|\breboot\b|\bhalt\b/, reason: 'system shutdown or reboot' },
|
|
22
|
+
{ regex: /\bkill\s+-9\s+1\b/, reason: 'kill PID 1 (system init)' },
|
|
23
|
+
{ regex: /\b(?:curl|wget)\b[^\n]*\|\s*(?:sh|bash)\b/i, reason: 'pipe a remote script into a shell' },
|
|
24
|
+
{ regex: /(?:^|[;&\n|\s])\s*\b(?:echo|printf|cat|tee)\b[^\n]*>\s*(?:\/etc\/|\/usr\/|\/bin\/)/, reason: 'redirect output into a system path' },
|
|
25
|
+
];
|
|
26
|
+
/**
|
|
27
|
+
* The curated set of protected file paths — dotfiles, credential stores, and
|
|
28
|
+
* sensitive config. Simple wildcard matching: `**` matches any depth, `*`
|
|
29
|
+
* matches a single path segment. A match raises a file write to `HIGH`.
|
|
30
|
+
*/
|
|
31
|
+
export const DEFAULT_PROTECTED_FILES = [
|
|
32
|
+
'.gitconfig',
|
|
33
|
+
'.bashrc',
|
|
34
|
+
'.zshrc',
|
|
35
|
+
'.profile',
|
|
36
|
+
'.bash_profile',
|
|
37
|
+
'.zprofile',
|
|
38
|
+
'.ssh/**',
|
|
39
|
+
'.aws/**',
|
|
40
|
+
'.netrc',
|
|
41
|
+
'.git-credentials',
|
|
42
|
+
'.mcp.json',
|
|
43
|
+
];
|
|
44
|
+
/**
|
|
45
|
+
* Classify a shell command by its riskiness. HIGH when any dangerous pattern
|
|
46
|
+
* matches, otherwise LOW. When `patterns` is given (raw regex sources, e.g.
|
|
47
|
+
* from `permissions.dangerousPatterns`), they replace the curated defaults.
|
|
48
|
+
* @param command - the shell command string to classify.
|
|
49
|
+
* @param patterns - optional raw regex sources to use instead of the defaults.
|
|
50
|
+
* @returns the assessment — HIGH with the matching reasons, else LOW.
|
|
51
|
+
*/
|
|
52
|
+
export function assessBashCommand(command, patterns) {
|
|
53
|
+
const source = patterns ?? [];
|
|
54
|
+
const list = source.length > 0
|
|
55
|
+
? source.map(pattern => ({ regex: compileSafe(pattern), reason: `command matches configured pattern ${JSON.stringify(pattern)}` }))
|
|
56
|
+
: DEFAULT_DANGEROUS_PATTERNS;
|
|
57
|
+
const reasons = [];
|
|
58
|
+
for (const { regex, reason } of list) {
|
|
59
|
+
regex.lastIndex = 0;
|
|
60
|
+
if (regex.test(command))
|
|
61
|
+
reasons.push(reason);
|
|
62
|
+
}
|
|
63
|
+
return reasons.length > 0 ? { level: 'HIGH', reasons } : { level: 'LOW', reasons: [] };
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Classify the target path of a file write. HIGH when it matches a protected
|
|
67
|
+
* file; MEDIUM when it resolves outside the working directory and its
|
|
68
|
+
* additional directories (an escape from the permission scope); else LOW.
|
|
69
|
+
* @param filePath - the target file path (absolute or relative).
|
|
70
|
+
* @param opts - the working directory scope and classification overrides.
|
|
71
|
+
* @returns the assessment with the matching reasons, or LOW.
|
|
72
|
+
*/
|
|
73
|
+
export function assessFilePath(filePath, opts) {
|
|
74
|
+
const protectedFiles = opts.protectedFiles ?? DEFAULT_PROTECTED_FILES;
|
|
75
|
+
if (protectedMatch(filePath, protectedFiles)) {
|
|
76
|
+
return { level: 'HIGH', reasons: [`path matches a protected file pattern`] };
|
|
77
|
+
}
|
|
78
|
+
// A non-empty cwd enables the escape check; without one we cannot scope the
|
|
79
|
+
// path and conservatively leave the escape determination at LOW.
|
|
80
|
+
const scope = [opts.cwd, ...(opts.additionalDirectories ?? [])];
|
|
81
|
+
if (opts.cwd !== '' && !inScope(resolve(opts.cwd, filePath), scope)) {
|
|
82
|
+
return { level: 'MEDIUM', reasons: ['path resolves outside the permission scope'] };
|
|
83
|
+
}
|
|
84
|
+
return { level: 'LOW', reasons: [] };
|
|
85
|
+
}
|
|
86
|
+
/** Whether a path (or its basename) matches any protected wildcard pattern. */
|
|
87
|
+
function protectedMatch(filePath, protectedFiles) {
|
|
88
|
+
const base = filePath.split('/').at(-1) ?? filePath;
|
|
89
|
+
const candidates = [
|
|
90
|
+
filePath,
|
|
91
|
+
filePath.replace(/^~\/?/, ''),
|
|
92
|
+
base,
|
|
93
|
+
];
|
|
94
|
+
for (const pattern of protectedFiles) {
|
|
95
|
+
const regex = wildcardToRegExp(pattern);
|
|
96
|
+
for (const candidate of candidates) {
|
|
97
|
+
regex.lastIndex = 0;
|
|
98
|
+
if (regex.test(candidate))
|
|
99
|
+
return true;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
return false;
|
|
103
|
+
}
|
|
104
|
+
/** Translate a `**`/`*` wildcard pattern into a whole-string matcher. */
|
|
105
|
+
function wildcardToRegExp(pattern) {
|
|
106
|
+
const body = pattern
|
|
107
|
+
.split('/')
|
|
108
|
+
.map(segment => (segment === '**' ? '.*' : segment === '*' ? '[^/]*' : segment.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')))
|
|
109
|
+
.join('/');
|
|
110
|
+
return new RegExp(`^${body}$`);
|
|
111
|
+
}
|
|
112
|
+
/** Whether `target` is at or under any of `dirs` (all pre-resolved absolute). */
|
|
113
|
+
function inScope(target, dirs) {
|
|
114
|
+
return dirs.some(dir => {
|
|
115
|
+
const rel = relative(dir, target);
|
|
116
|
+
return rel === '' || (!rel.startsWith('..') && !isAbsolute(rel));
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
/** Compile a raw pattern source into a RegExp, ignoring an invalid one. */
|
|
120
|
+
function compileSafe(source) {
|
|
121
|
+
try {
|
|
122
|
+
return new RegExp(source);
|
|
123
|
+
}
|
|
124
|
+
catch {
|
|
125
|
+
// An invalid pattern must not crash classification; the defaults still run.
|
|
126
|
+
return /(?!)/; // never matches
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
//# sourceMappingURL=classifier.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"classifier.js","sourceRoot":"","sources":["../src/classifier.ts"],"names":[],"mappings":"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"}
|
package/lib/decide.d.ts
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The decision waterfall for one tool call, extracted from the service so the
|
|
3
|
+
* engine core stays modular. Pure functions over a structural dependency face
|
|
4
|
+
* (`DecideDeps`) that `PermissionRulesService` supplies in its constructor.
|
|
5
|
+
*
|
|
6
|
+
* Stage order: the risk-classifier escalation runs first (a hard-deny HIGH in
|
|
7
|
+
* every mode; an ask MEDIUM outside bypassPermissions, with session-scoped
|
|
8
|
+
* grants overriding the ask), then the normal mode-aware waterfall proceeds.
|
|
9
|
+
* Under `auto`, a classifier-LOW call whose waterfall decision is `ask` is
|
|
10
|
+
* auto-allowed (the classifier proxies the prompt); MEDIUM/HIGH already
|
|
11
|
+
* returned above.
|
|
12
|
+
*
|
|
13
|
+
* @module @dsh-cc/permission-rules/decide
|
|
14
|
+
*/
|
|
15
|
+
import type { ToolExecution } from '@dsh-cc/tools';
|
|
16
|
+
import type { SandboxMode } from '@deepseek-ai/dsh-sandbox';
|
|
17
|
+
import { type RiskAssessment } from './classifier.ts';
|
|
18
|
+
import type { PermissionDecision, PermissionMode, PermissionRuleSet } from './types.ts';
|
|
19
|
+
/**
|
|
20
|
+
* Structural dependency face the service supplies to the decision waterfall.
|
|
21
|
+
* `settings()` returns the classifier-relevant slice of the settings-resolved
|
|
22
|
+
* section; `defaultMode()`/`rules()` read the live merged state so a settings
|
|
23
|
+
* reload is observed on the next call.
|
|
24
|
+
*/
|
|
25
|
+
export type DecideDeps = {
|
|
26
|
+
/** Whether the risk-classifier escalation stage runs. */
|
|
27
|
+
classifierEnabled: boolean;
|
|
28
|
+
/** Whether sandboxed bash skips a whole-tool `ask`. */
|
|
29
|
+
exemptSandboxedBashFromToolAsk: boolean;
|
|
30
|
+
/** The shell-command tool name for content extraction. */
|
|
31
|
+
bashToolName: string;
|
|
32
|
+
/** File-edit tool names auto-allowed under `acceptEdits` mode. */
|
|
33
|
+
fileEditTools: ReadonlySet<string>;
|
|
34
|
+
/** Read-only tool names auto-allowed under `plan` mode. */
|
|
35
|
+
readOnlyTools: ReadonlySet<string>;
|
|
36
|
+
/** The classifier-relevant slice of the current settings section. */
|
|
37
|
+
settings(): {
|
|
38
|
+
dangerousPatterns?: string[];
|
|
39
|
+
additionalDirectories?: string[];
|
|
40
|
+
protectedFiles?: string[];
|
|
41
|
+
};
|
|
42
|
+
/** The fallback (deployment-default) permission mode. */
|
|
43
|
+
defaultMode(): PermissionMode;
|
|
44
|
+
/** The live merged rule set. */
|
|
45
|
+
rules(): PermissionRuleSet;
|
|
46
|
+
/** Whether switching to `bypassPermissions` is disabled. */
|
|
47
|
+
bypassDisabled(): boolean;
|
|
48
|
+
/** Session-scoped allowlist match (seed-once handled inside the service). */
|
|
49
|
+
sessionAllowMatches(exec: ToolExecution): boolean;
|
|
50
|
+
/** The host shell service's sandbox mode, when mounted. */
|
|
51
|
+
shellMode(): SandboxMode | undefined;
|
|
52
|
+
};
|
|
53
|
+
/**
|
|
54
|
+
* The verbose result of the decision waterfall: the raw waterfall decision
|
|
55
|
+
* (BEFORE any auto-mode proxying) plus the computed risk and effective mode.
|
|
56
|
+
* The async classifier stage (§4.1 of the LLM risk-classifier design) needs
|
|
57
|
+
* all three to decide whether to consult the LLM and how to escalate.
|
|
58
|
+
*/
|
|
59
|
+
export type DecidedCall = {
|
|
60
|
+
decision: PermissionDecision;
|
|
61
|
+
risk: RiskAssessment;
|
|
62
|
+
mode: PermissionMode;
|
|
63
|
+
isReadOnly: boolean;
|
|
64
|
+
};
|
|
65
|
+
/**
|
|
66
|
+
* The sync, pure waterfall WITHOUT the auto-proxy conversion. Under `auto`, a
|
|
67
|
+
* classifier-LOW call whose waterfall decision is `ask` is returned as `ask`
|
|
68
|
+
* here — `decideCall` applies the proxy on top.
|
|
69
|
+
*/
|
|
70
|
+
export declare function decideCallVerbose(deps: DecideDeps, exec: ToolExecution): DecidedCall;
|
|
71
|
+
/**
|
|
72
|
+
* Fold the engine decision for one call. Bypass-immune matches fall to the
|
|
73
|
+
* guard layer, not here. The risk-classifier escalation runs first (a
|
|
74
|
+
* hard-deny HIGH in every mode; an ask MEDIUM outside bypassPermissions),
|
|
75
|
+
* then the normal waterfall proceeds unchanged. Under `auto`, a classifier-LOW
|
|
76
|
+
* call whose waterfall decision is `ask` is auto-allowed (the classifier
|
|
77
|
+
* proxies the prompt); MEDIUM/HIGH already returned above.
|
|
78
|
+
*/
|
|
79
|
+
export declare function decideCall(deps: DecideDeps, exec: ToolExecution): PermissionDecision;
|
|
80
|
+
//# sourceMappingURL=decide.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"decide.d.ts","sourceRoot":"","sources":["../src/decide.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,eAAe,CAAA;AAClD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAA;AAE3D,OAAO,EAAqC,KAAK,cAAc,EAAE,MAAM,iBAAiB,CAAA;AAIxF,OAAO,KAAK,EAAE,kBAAkB,EAAE,cAAc,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAA;AAEvF;;;;;GAKG;AACH,MAAM,MAAM,UAAU,GAAG;IACvB,yDAAyD;IACzD,iBAAiB,EAAE,OAAO,CAAA;IAC1B,uDAAuD;IACvD,8BAA8B,EAAE,OAAO,CAAA;IACvC,0DAA0D;IAC1D,YAAY,EAAE,MAAM,CAAA;IACpB,kEAAkE;IAClE,aAAa,EAAE,WAAW,CAAC,MAAM,CAAC,CAAA;IAClC,2DAA2D;IAC3D,aAAa,EAAE,WAAW,CAAC,MAAM,CAAC,CAAA;IAClC,qEAAqE;IACrE,QAAQ,IAAI;QAAE,iBAAiB,CAAC,EAAE,MAAM,EAAE,CAAC;QAAC,qBAAqB,CAAC,EAAE,MAAM,EAAE,CAAC;QAAC,cAAc,CAAC,EAAE,MAAM,EAAE,CAAA;KAAE,CAAA;IACzG,yDAAyD;IACzD,WAAW,IAAI,cAAc,CAAA;IAC7B,gCAAgC;IAChC,KAAK,IAAI,iBAAiB,CAAA;IAC1B,4DAA4D;IAC5D,cAAc,IAAI,OAAO,CAAA;IACzB,6EAA6E;IAC7E,mBAAmB,CAAC,IAAI,EAAE,aAAa,GAAG,OAAO,CAAA;IACjD,2DAA2D;IAC3D,SAAS,IAAI,WAAW,GAAG,SAAS,CAAA;CACrC,CAAA;AA2CD;;;;;GAKG;AACH,MAAM,MAAM,WAAW,GAAG;IAAE,QAAQ,EAAE,kBAAkB,CAAC;IAAC,IAAI,EAAE,cAAc,CAAC;IAAC,IAAI,EAAE,cAAc,CAAC;IAAC,UAAU,EAAE,OAAO,CAAA;CAAE,CAAA;AAE3H;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,aAAa,GAAG,WAAW,CAwCpF;AAED;;;;;;;GAOG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,aAAa,GAAG,kBAAkB,CAQpF"}
|