@goodandready/dsh-approval-gate 0.1.9 → 0.1.10
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 +6 -0
- package/lib/index.js +87 -4
- package/lib/messages.js +5 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.1.10
|
|
4
|
+
|
|
5
|
+
- Policy: точное определение сессионной политики апрува в `readKnobs` через `approval.effectivePolicy(session)`, историю событий `session.eventAt` и пресет `danger-full-access` (Refs: #140).
|
|
6
|
+
- Execution: корректный пропуск некритичных команд в режиме «Полный доступ» (`never`) без ложного поднятия `ask` и автоматического отказа DSH `Error: the user rejected tool` (Refs: #140).
|
|
7
|
+
- Observability: форматирование `sessionTag` с получением строкового идентификатора сессии `session.id` / `session.name` вместо `[object Object]` (Refs: #140).
|
|
8
|
+
|
|
3
9
|
## 0.1.9
|
|
4
10
|
|
|
5
11
|
- Inspect: декомпозиция `lib/inspect.js` с выносом `checkEnvExfiltration` в `lib/inspect-env.js`, строгое соблюдение порога 600 строк (Refs: #35).
|
package/lib/index.js
CHANGED
|
@@ -44,8 +44,15 @@ export function isUnattended(knobs) {
|
|
|
44
44
|
return policies.includes(policy);
|
|
45
45
|
}
|
|
46
46
|
|
|
47
|
-
/**
|
|
48
|
-
|
|
47
|
+
/**
|
|
48
|
+
* Effective permission knobs, as the harness exposes them.
|
|
49
|
+
* Resolves session-level approval policy if an active execution carrying a session is provided.
|
|
50
|
+
*
|
|
51
|
+
* @param {any} ctx - Cordis context
|
|
52
|
+
* @param {any} [execution] - Optional tool execution carrying agent/session
|
|
53
|
+
* @returns {{ policy: string, sandbox: string }}
|
|
54
|
+
*/
|
|
55
|
+
export function readKnobs(ctx, execution) {
|
|
49
56
|
const read = (name) => {
|
|
50
57
|
try {
|
|
51
58
|
return ctx && typeof ctx.get === "function" ? ctx.get(name) : undefined;
|
|
@@ -55,8 +62,84 @@ function readKnobs(ctx) {
|
|
|
55
62
|
};
|
|
56
63
|
const approval = read("approval");
|
|
57
64
|
const shell = read("shell");
|
|
65
|
+
|
|
66
|
+
const agent = execution && execution.agent;
|
|
67
|
+
const session = agent && (agent.session || (typeof agent === "object" && typeof agent.eventAt === "function" ? agent : undefined));
|
|
68
|
+
|
|
69
|
+
let policy = "";
|
|
70
|
+
|
|
71
|
+
// 1. Live approval service effectivePolicy(session)
|
|
72
|
+
if (approval && session && typeof approval.effectivePolicy === "function") {
|
|
73
|
+
try {
|
|
74
|
+
policy = approval.effectivePolicy(session) || "";
|
|
75
|
+
} catch {
|
|
76
|
+
// ignore
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// 2. Direct session property fallback
|
|
81
|
+
if (!policy && session && typeof session.approvalPolicy === "string") {
|
|
82
|
+
policy = session.approvalPolicy;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// 3. Fallback: inspect session events backwards for approval/policy
|
|
86
|
+
if (!policy && session && typeof session.eventAt === "function" && typeof session.seq === "number") {
|
|
87
|
+
try {
|
|
88
|
+
for (let seq = session.seq - 1; seq >= 0; seq -= 1) {
|
|
89
|
+
const event = session.eventAt(seq);
|
|
90
|
+
if (event && event.type === "approval/policy" && event.data && event.data.policy) {
|
|
91
|
+
policy = event.data.policy;
|
|
92
|
+
break;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
} catch {
|
|
96
|
+
// ignore
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// 4. Fallback: inspect session events for permission/preset === 'danger-full-access'
|
|
101
|
+
if (!policy && session && typeof session.eventAt === "function" && typeof session.seq === "number") {
|
|
102
|
+
try {
|
|
103
|
+
for (let seq = session.seq - 1; seq >= 0; seq -= 1) {
|
|
104
|
+
const event = session.eventAt(seq);
|
|
105
|
+
if (event && event.type === "permission/preset" && event.data && event.data.preset) {
|
|
106
|
+
if (event.data.preset === "danger-full-access") {
|
|
107
|
+
policy = "never";
|
|
108
|
+
}
|
|
109
|
+
break;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
} catch {
|
|
113
|
+
// ignore
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// 5. In-memory session.events array fallback (common in mocks / tests)
|
|
118
|
+
if (!policy && session && Array.isArray(session.events)) {
|
|
119
|
+
try {
|
|
120
|
+
for (let i = session.events.length - 1; i >= 0; i -= 1) {
|
|
121
|
+
const event = session.events[i];
|
|
122
|
+
if (event && event.type === "approval/policy" && event.data && event.data.policy) {
|
|
123
|
+
policy = event.data.policy;
|
|
124
|
+
break;
|
|
125
|
+
}
|
|
126
|
+
if (event && event.type === "permission/preset" && event.data && event.data.preset === "danger-full-access") {
|
|
127
|
+
policy = "never";
|
|
128
|
+
break;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
} catch {
|
|
132
|
+
// ignore
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// 6. Global static fallback from approval service config
|
|
137
|
+
if (!policy && approval && approval.config && approval.config.policy) {
|
|
138
|
+
policy = approval.config.policy;
|
|
139
|
+
}
|
|
140
|
+
|
|
58
141
|
return {
|
|
59
|
-
policy:
|
|
142
|
+
policy: policy || "",
|
|
60
143
|
sandbox: (shell && shell.sandboxMode) || "",
|
|
61
144
|
};
|
|
62
145
|
}
|
|
@@ -106,7 +189,7 @@ export function apply(ctx, config) {
|
|
|
106
189
|
const hasPreExecute = ctx && typeof ctx.on === "function";
|
|
107
190
|
if (hasPreExecute) {
|
|
108
191
|
ctx.on("tools/pre-execute", function (execution, next) {
|
|
109
|
-
const knobs = readKnobs(ctx);
|
|
192
|
+
const knobs = readKnobs(ctx, execution);
|
|
110
193
|
const who = sessionTag(execution);
|
|
111
194
|
const sessionId = (execution && execution.agent && (execution.agent.session || execution.agent)) || "default";
|
|
112
195
|
const decision = engine.decide(execution, { sessionId, knobs });
|
package/lib/messages.js
CHANGED
|
@@ -178,7 +178,11 @@ export const MESSAGES = Object.freeze({
|
|
|
178
178
|
export function sessionTag(execution) {
|
|
179
179
|
const agent = execution && execution.agent;
|
|
180
180
|
if (typeof agent === "string") return " (session " + agent + ")";
|
|
181
|
-
if (agent && agent.session)
|
|
181
|
+
if (agent && agent.session) {
|
|
182
|
+
const s = agent.session;
|
|
183
|
+
const sid = typeof s === "string" ? s : (s.id || s.name || s.sessionId || s.sessionKey);
|
|
184
|
+
return sid ? " (session " + String(sid) + ")" : "";
|
|
185
|
+
}
|
|
182
186
|
return "";
|
|
183
187
|
}
|
|
184
188
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@goodandready/dsh-approval-gate",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.10",
|
|
4
4
|
"description": "Host-only command safety gate for DeepSeek Harness: blocks recognized dangerous operations and requests DSH approval when a command cannot be inspected.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/index.js",
|