@elinpf/dsh-ops-access-gate 0.3.0 → 0.4.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/lib/index.js +21 -2
- package/package.json +5 -5
package/lib/index.js
CHANGED
|
@@ -322,7 +322,15 @@ export function apply(ctx, config) {
|
|
|
322
322
|
// never issued without a session to key the grant on); approval-required
|
|
323
323
|
// kinds deny outright — their credential is effectively rw (there is no
|
|
324
324
|
// read-only shell), so an untracked caller must not get it at all.
|
|
325
|
-
|
|
325
|
+
//
|
|
326
|
+
// Per-call tier declaration (request.tier):
|
|
327
|
+
// - 'ro' = deliberate downgrade: the session may hold an rw grant, but this
|
|
328
|
+
// call only reads. Serve ro; the grant is not exercised and no rw-issue
|
|
329
|
+
// is audited. (Lockdown still refuses even this.)
|
|
330
|
+
// - 'rw' = explicit elevation: without a grant this denies LOUDLY with
|
|
331
|
+
// request_access guidance — silently serving ro would leave the agent
|
|
332
|
+
// believing it wrote when it only read.
|
|
333
|
+
const broker = (kind, profileName, agent, request) => {
|
|
326
334
|
// Deny (ticket 12) outranks every other state: a locked profile refuses
|
|
327
335
|
// even ro, for session and internal callers alike.
|
|
328
336
|
if (gate.isDenied(kind, profileName)) {
|
|
@@ -337,14 +345,25 @@ export function apply(ctx, config) {
|
|
|
337
345
|
}
|
|
338
346
|
const authorized = gate.isAuthorized(agent.id, kind, profileName);
|
|
339
347
|
// Approval-required kinds (ssh): the credential lives in the ro registry —
|
|
340
|
-
// the grant is a timed pass to use it at all.
|
|
348
|
+
// the grant is a timed pass to use it at all. There IS no rw tier, so an
|
|
349
|
+
// explicit rw request is a category error worth teaching.
|
|
341
350
|
if (config.approvalRequiredKinds.includes(kind)) {
|
|
351
|
+
if (request?.tier === 'rw') {
|
|
352
|
+
return { deny: kind + ' has no rw tier — its single credential is effectively rw already, and the grant IS the access. Request timed access via the ' + REQUEST_ACCESS + ' tool (profile "' + kind + '/' + profileName + '", with a reason) and call WITHOUT a tier' };
|
|
353
|
+
}
|
|
342
354
|
if (authorized) {
|
|
343
355
|
audit({ event: 'gated-issue', session: agent.id, parentSession: parentSessionOf(agent), kind, name: profileName });
|
|
344
356
|
return 'ro';
|
|
345
357
|
}
|
|
346
358
|
return { deny: kind + ' has no read-only tier; request timed access via the ' + REQUEST_ACCESS + ' tool (profile "' + kind + '/' + profileName + '", with a reason)' };
|
|
347
359
|
}
|
|
360
|
+
// Deliberate downgrade: serve ro without touching the grant.
|
|
361
|
+
if (request?.tier === 'ro')
|
|
362
|
+
return 'ro';
|
|
363
|
+
if (request?.tier === 'rw' && !authorized) {
|
|
364
|
+
audit({ event: 'rw-denied', session: agent.id, parentSession: parentSessionOf(agent), kind, name: profileName });
|
|
365
|
+
return { deny: 'explicit rw requested for ' + kind + '/' + profileName + ' but this session holds no grant — request timed access via the ' + REQUEST_ACCESS + ' tool (with a reason), or drop the tier to use the read-only credential' };
|
|
366
|
+
}
|
|
348
367
|
if (authorized) {
|
|
349
368
|
audit({ event: 'rw-issue', session: agent.id, parentSession: parentSessionOf(agent), kind, name: profileName });
|
|
350
369
|
return 'rw';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@elinpf/dsh-ops-access-gate",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "Ops access gate — per-session credential brokering. Owns an in-process grant ledger keyed by agent.id and registers a pure-decision broker (ro/rw) into the ops-access seam. Never touches credential fields.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/index.js",
|
|
@@ -34,14 +34,14 @@
|
|
|
34
34
|
},
|
|
35
35
|
"dependencies": {
|
|
36
36
|
"@deepseek-ai/schemastery": "^3.18.1",
|
|
37
|
-
"@elinpf/dsh-ops-panel": "^0.
|
|
37
|
+
"@elinpf/dsh-ops-panel": "^0.4.0"
|
|
38
38
|
},
|
|
39
39
|
"peerDependencies": {
|
|
40
40
|
"@deepseek-ai/cordis": "^4.0.1",
|
|
41
41
|
"@deepseek-ai/dsh-invariants": "^0.1.0-rc.8",
|
|
42
42
|
"@deepseek-ai/dsh-llm": "^0.1.0-rc.8",
|
|
43
43
|
"@deepseek-ai/dsh-tools": "^0.1.0-rc.8",
|
|
44
|
-
"@elinpf/dsh-ops-access": "^0.
|
|
44
|
+
"@elinpf/dsh-ops-access": "^0.4.0"
|
|
45
45
|
},
|
|
46
46
|
"devDependencies": {
|
|
47
47
|
"@deepseek-ai/cordis": "4.0.1",
|
|
@@ -51,8 +51,8 @@
|
|
|
51
51
|
"typescript": "^5.4.0",
|
|
52
52
|
"vitest": "^4.1.11",
|
|
53
53
|
"zod": "^4.4.3",
|
|
54
|
-
"@elinpf/dsh-ops-
|
|
55
|
-
"@elinpf/dsh-ops-
|
|
54
|
+
"@elinpf/dsh-ops-access": "0.4.0",
|
|
55
|
+
"@elinpf/dsh-ops-panel": "0.4.0"
|
|
56
56
|
},
|
|
57
57
|
"license": "MIT",
|
|
58
58
|
"publishConfig": {
|