@goodandready/dsh-approval-gate 0.1.20 → 0.1.22
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 +16 -0
- package/lib/audit-log.js +348 -345
- package/lib/bands.js +243 -239
- package/lib/engine.js +75 -80
- package/lib/escalation.js +151 -0
- package/lib/guidance.js +119 -0
- package/lib/index.js +85 -86
- package/lib/lineage.js +182 -0
- package/lib/measure-kinds.js +138 -0
- package/lib/messages.js +33 -0
- package/lib/snapshots.js +268 -0
- package/package.json +55 -55
package/lib/messages.js
CHANGED
|
@@ -22,6 +22,7 @@ export const MESSAGES = Object.freeze({
|
|
|
22
22
|
detailUnsupported: "unsupported shell syntax",
|
|
23
23
|
approvalRequired: "Approval required",
|
|
24
24
|
askUnknown: "The command could not be fully inspected; review it before allowing",
|
|
25
|
+
askUnmatched: "The operation was not recognized by safety rules; human confirmation is required by default",
|
|
25
26
|
askUnparseable: "Shell syntax could not be fully inspected; review it before allowing",
|
|
26
27
|
askUnknownFormat: "The tool call format could not be inspected; review it before allowing",
|
|
27
28
|
askUnknownWriteTarget: "The file write target could not be identified; review it before allowing",
|
|
@@ -81,6 +82,12 @@ export const MESSAGES = Object.freeze({
|
|
|
81
82
|
ruleAgentsMdProhibitedPath: "File modification prohibited by rules in AGENTS.md",
|
|
82
83
|
askMajorVersionBump: "Package.json modification contains major dependency bump; review before allowing",
|
|
83
84
|
ruleMajorVersionBump: "Unauthorized major dependency version bump",
|
|
85
|
+
askRedundantSandbox: "Redundant sandbox elevation requested for safe workspace operation; review or execute in sandboxed mode",
|
|
86
|
+
ruleRedundantSandbox: "Unnecessary sandbox elevation requested for workspace-internal task",
|
|
87
|
+
askMissingJustification: "Privilege escalation requires an explicit justification; review before allowing",
|
|
88
|
+
ruleMissingJustification: "System privilege escalation requested without justification",
|
|
89
|
+
askInsufficientJustification: "Privilege escalation justification is too brief or generic; review before allowing",
|
|
90
|
+
ruleInsufficientJustification: "Insufficient privilege escalation justification",
|
|
84
91
|
hintRecursiveRm: "Specify exact target files instead of recursive deletion",
|
|
85
92
|
hintPathTraversal: "Keep operations strictly within the project directory",
|
|
86
93
|
hintSecretWrite: "Modify configuration via settings or environment variables, not raw file overwrite",
|
|
@@ -107,6 +114,10 @@ export const MESSAGES = Object.freeze({
|
|
|
107
114
|
hintTrashAlternative: "Move files to reversible .dsh/trash/<session>/ directory instead of rm",
|
|
108
115
|
hintAgentsMdProhibited: "Review project-specific guidelines and constraints defined in AGENTS.md",
|
|
109
116
|
hintMajorVersionBump: "Verify compatibility before bumping major dependency versions in package.json",
|
|
117
|
+
hintUnmatchedAsk: "Unrecognized operations default to fail-closed approval under defaultVerdict='ask'",
|
|
118
|
+
hintRedundantSandbox: "Execute the command in standard sandbox mode without danger-full-access",
|
|
119
|
+
hintMissingJustification: "Provide a justification field with at least 10 non-placeholder characters",
|
|
120
|
+
hintInsufficientJustification: "Describe the specific operational reason for privilege escalation",
|
|
110
121
|
},
|
|
111
122
|
zh: {
|
|
112
123
|
blockedPrefix: "dsh-approval-gate 规则已阻止",
|
|
@@ -128,6 +139,7 @@ export const MESSAGES = Object.freeze({
|
|
|
128
139
|
detailUnsupported: "不支持的 shell 语法",
|
|
129
140
|
approvalRequired: "需要批准",
|
|
130
141
|
askUnknown: "命令无法完整检查;请在允许前进行审查",
|
|
142
|
+
askUnmatched: "该操作未被安全规则识别;默认需要人工确认",
|
|
131
143
|
askUnparseable: "Shell 语法无法完整检查;请在允许前进行审查",
|
|
132
144
|
askUnknownFormat: "工具调用格式无法检查;请在允许前进行审查",
|
|
133
145
|
askUnknownWriteTarget: "无法识别文件写入目标;请在允许前进行审查",
|
|
@@ -187,6 +199,12 @@ export const MESSAGES = Object.freeze({
|
|
|
187
199
|
ruleAgentsMdProhibitedPath: "文件修改被 AGENTS.md 中的规则明确禁止",
|
|
188
200
|
askMajorVersionBump: "package.json 包含破坏性依赖主版本升级;请在允许前进行审查",
|
|
189
201
|
ruleMajorVersionBump: "未授权的依赖包主版本升级",
|
|
202
|
+
askRedundantSandbox: "对安全的内部操作请求了冗余的沙箱提权;请在允许前审查或以标准沙箱模式执行",
|
|
203
|
+
ruleRedundantSandbox: "工作区内部任务请求了不必要的沙箱提权",
|
|
204
|
+
askMissingJustification: "特权提升需要明确的理由说明;请在允许前进行审查",
|
|
205
|
+
ruleMissingJustification: "未提供理由即请求系统提权",
|
|
206
|
+
askInsufficientJustification: "特权提升理由过短或为占位符;请在允许前进行审查",
|
|
207
|
+
ruleInsufficientJustification: "特权提升理由不充分",
|
|
190
208
|
hintRecursiveRm: "请指定具体目标文件,避免递归删除",
|
|
191
209
|
hintPathTraversal: "请将操作限制在项目根目录内",
|
|
192
210
|
hintSecretWrite: "请通过设置或环境变量修改配置,不要直接覆盖敏感配置文件",
|
|
@@ -213,6 +231,10 @@ export const MESSAGES = Object.freeze({
|
|
|
213
231
|
hintTrashAlternative: "建议将文件移动到可逆的 .dsh/trash/<session>/ 目录,避免直接 rm",
|
|
214
232
|
hintAgentsMdProhibited: "请核对项目根目录 AGENTS.md 中定义的安全约束和禁止指令",
|
|
215
233
|
hintMajorVersionBump: "在升级 package.json 依赖主版本前请确认破坏性变更与兼容性",
|
|
234
|
+
hintUnmatchedAsk: "在 defaultVerdict='ask' 模式下,未识别的操作默认需要人工审核确认",
|
|
235
|
+
hintRedundantSandbox: "在标准沙箱模式下重新执行该命令,无需申请 danger-full-access",
|
|
236
|
+
hintMissingJustification: "请提供至少10个字符的有效理由说明字段",
|
|
237
|
+
hintInsufficientJustification: "请详细说明提权操作的具体业务或技术原因",
|
|
216
238
|
},
|
|
217
239
|
});
|
|
218
240
|
|
|
@@ -287,6 +309,9 @@ const RULE_KEYS = Object.freeze({
|
|
|
287
309
|
"agents-md-prohibited-command": "ruleAgentsMdProhibitedCommand",
|
|
288
310
|
"agents-md-prohibited-path": "ruleAgentsMdProhibitedPath",
|
|
289
311
|
"major-version-bump": "askMajorVersionBump",
|
|
312
|
+
"redundant-sandbox-privilege": "ruleRedundantSandbox",
|
|
313
|
+
"missing-escalation-justification": "ruleMissingJustification",
|
|
314
|
+
"insufficient-escalation-justification": "ruleInsufficientJustification",
|
|
290
315
|
});
|
|
291
316
|
|
|
292
317
|
const HINT_KEYS = Object.freeze({
|
|
@@ -317,12 +342,20 @@ const HINT_KEYS = Object.freeze({
|
|
|
317
342
|
"agents-md-prohibited-command": "hintAgentsMdProhibited",
|
|
318
343
|
"agents-md-prohibited-path": "hintAgentsMdProhibited",
|
|
319
344
|
"major-version-bump": "hintMajorVersionBump",
|
|
345
|
+
"unmatched-ask": "hintUnmatchedAsk",
|
|
346
|
+
"redundant-sandbox-privilege": "hintRedundantSandbox",
|
|
347
|
+
"missing-escalation-justification": "hintMissingJustification",
|
|
348
|
+
"insufficient-escalation-justification": "hintInsufficientJustification",
|
|
320
349
|
});
|
|
321
350
|
|
|
322
351
|
const ASK_KEYS = Object.freeze({
|
|
323
352
|
unparseable: "askUnparseable",
|
|
324
353
|
"unknown-format": "askUnknownFormat",
|
|
325
354
|
"unknown-write-target": "askUnknownWriteTarget",
|
|
355
|
+
"unmatched-ask": "askUnmatched",
|
|
356
|
+
"redundant-sandbox-privilege": "askRedundantSandbox",
|
|
357
|
+
"missing-escalation-justification": "askMissingJustification",
|
|
358
|
+
"insufficient-escalation-justification": "askInsufficientJustification",
|
|
326
359
|
"dynamic-command": "askDynamicCommand",
|
|
327
360
|
"dynamic-arguments": "askDynamicArguments",
|
|
328
361
|
"dynamic-redirect": "askDynamicRedirect",
|
package/lib/snapshots.js
ADDED
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
// lib/snapshots.js
|
|
2
|
+
// Pre-execution Snapshots (#77), Undo/Rollback (#76), and Dual-level Cleanup (#78).
|
|
3
|
+
// Zero external dependencies; safe filesystem operations with in-memory metadata.
|
|
4
|
+
|
|
5
|
+
import fs from "node:fs";
|
|
6
|
+
import path from "node:path";
|
|
7
|
+
import crypto from "node:crypto";
|
|
8
|
+
|
|
9
|
+
export const DEFAULT_MAX_SNAPSHOTS_PER_SESSION = 50;
|
|
10
|
+
export const DEFAULT_MAX_SNAPSHOT_BYTES = 1024 * 1024; // 1 MB
|
|
11
|
+
|
|
12
|
+
export class SnapshotManager {
|
|
13
|
+
/**
|
|
14
|
+
* @param {Record<string, any>} [options]
|
|
15
|
+
*/
|
|
16
|
+
constructor(options = {}) {
|
|
17
|
+
this.maxSnapshotsPerSession = Number(options.maxSnapshotsPerSession) || DEFAULT_MAX_SNAPSHOTS_PER_SESSION;
|
|
18
|
+
this.maxSnapshotBytes = Number(options.maxSnapshotBytes) || DEFAULT_MAX_SNAPSHOT_BYTES;
|
|
19
|
+
this.workspaceDir = options.workspaceDir ? path.resolve(options.workspaceDir) : "";
|
|
20
|
+
|
|
21
|
+
/** @type {Map<string, Record<string, any>>} snapshotId -> Snapshot */
|
|
22
|
+
this.snapshots = new Map();
|
|
23
|
+
/** @type {Map<string, string[]>} sessionId -> [snapshotId, ...] */
|
|
24
|
+
this.sessionIndex = new Map();
|
|
25
|
+
/** @type {Map<string, string>} callId -> snapshotId */
|
|
26
|
+
this.callIndex = new Map();
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Resolves absolute file path within workspace or host.
|
|
31
|
+
* @param {string} targetPath
|
|
32
|
+
* @returns {string}
|
|
33
|
+
*/
|
|
34
|
+
resolvePath(targetPath) {
|
|
35
|
+
if (!targetPath || typeof targetPath !== "string") return "";
|
|
36
|
+
if (path.isAbsolute(targetPath)) return path.normalize(targetPath);
|
|
37
|
+
if (this.workspaceDir) return path.resolve(this.workspaceDir, targetPath);
|
|
38
|
+
return path.resolve(targetPath);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Captures a pre-execution snapshot before file mutation (#77).
|
|
43
|
+
* @param {string} targetPath - Relative or absolute path to target file
|
|
44
|
+
* @param {Record<string, any>} [meta] - Metadata (sessionId, callId, tool)
|
|
45
|
+
* @returns {Record<string, any>} Created snapshot record
|
|
46
|
+
*/
|
|
47
|
+
takeSnapshot(targetPath, meta = {}) {
|
|
48
|
+
const resolved = this.resolvePath(targetPath);
|
|
49
|
+
if (!resolved) {
|
|
50
|
+
return { success: false, error: "invalid-target-path" };
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const sessionId = String(meta.sessionId || "default");
|
|
54
|
+
const callId = meta.callId ? String(meta.callId) : null;
|
|
55
|
+
const tool = meta.tool ? String(meta.tool) : "unknown";
|
|
56
|
+
|
|
57
|
+
let existed = false;
|
|
58
|
+
let content = null;
|
|
59
|
+
let tooLarge = false;
|
|
60
|
+
let size = 0;
|
|
61
|
+
|
|
62
|
+
try {
|
|
63
|
+
if (fs.existsSync(resolved)) {
|
|
64
|
+
existed = true;
|
|
65
|
+
const stat = fs.statSync(resolved);
|
|
66
|
+
size = stat.size;
|
|
67
|
+
if (size > this.maxSnapshotBytes) {
|
|
68
|
+
tooLarge = true;
|
|
69
|
+
} else {
|
|
70
|
+
content = fs.readFileSync(resolved, "utf8");
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
} catch {
|
|
74
|
+
// If file unreadable or permission error, proceed safely
|
|
75
|
+
existed = false;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const id = "snap_" + Date.now() + "_" + crypto.randomBytes(4).toString("hex");
|
|
79
|
+
const snapshot = {
|
|
80
|
+
id,
|
|
81
|
+
sessionId,
|
|
82
|
+
callId,
|
|
83
|
+
tool,
|
|
84
|
+
targetPath: resolved,
|
|
85
|
+
originalPath: targetPath,
|
|
86
|
+
existed,
|
|
87
|
+
content,
|
|
88
|
+
size,
|
|
89
|
+
tooLarge,
|
|
90
|
+
createdAt: Date.now(),
|
|
91
|
+
rolledBack: false,
|
|
92
|
+
rolledBackAt: null,
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
this.snapshots.set(id, snapshot);
|
|
96
|
+
|
|
97
|
+
if (callId) {
|
|
98
|
+
this.callIndex.set(callId, id);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
if (!this.sessionIndex.has(sessionId)) {
|
|
102
|
+
this.sessionIndex.set(sessionId, []);
|
|
103
|
+
}
|
|
104
|
+
const sessionList = this.sessionIndex.get(sessionId);
|
|
105
|
+
sessionList.push(id);
|
|
106
|
+
|
|
107
|
+
// Evict oldest if exceeding maxSnapshotsPerSession
|
|
108
|
+
if (sessionList.length > this.maxSnapshotsPerSession) {
|
|
109
|
+
const evictedId = sessionList.shift();
|
|
110
|
+
if (evictedId) {
|
|
111
|
+
const evicted = this.snapshots.get(evictedId);
|
|
112
|
+
if (evicted && evicted.callId) {
|
|
113
|
+
this.callIndex.delete(evicted.callId);
|
|
114
|
+
}
|
|
115
|
+
this.snapshots.delete(evictedId);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
return snapshot;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Retrieves a snapshot by ID.
|
|
124
|
+
* @param {string} id
|
|
125
|
+
* @returns {Record<string, any> | null}
|
|
126
|
+
*/
|
|
127
|
+
getSnapshot(id) {
|
|
128
|
+
return this.snapshots.get(id) || null;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Retrieves a snapshot by tool callId.
|
|
133
|
+
* @param {string} callId
|
|
134
|
+
* @returns {Record<string, any> | null}
|
|
135
|
+
*/
|
|
136
|
+
getSnapshotByCallId(callId) {
|
|
137
|
+
const id = this.callIndex.get(String(callId));
|
|
138
|
+
return id ? this.getSnapshot(id) : null;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Retrieves all snapshots for a session.
|
|
143
|
+
* @param {string} sessionId
|
|
144
|
+
* @returns {Record<string, any>[]}
|
|
145
|
+
*/
|
|
146
|
+
getSnapshots(sessionId) {
|
|
147
|
+
const ids = this.sessionIndex.get(String(sessionId)) || [];
|
|
148
|
+
const res = [];
|
|
149
|
+
for (const id of ids) {
|
|
150
|
+
const snap = this.snapshots.get(id);
|
|
151
|
+
if (snap) res.push(snap);
|
|
152
|
+
}
|
|
153
|
+
return res;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Undoes a file change by rolling back to its pre-execution snapshot (#76).
|
|
158
|
+
* @param {string} snapshotIdOrCallback - Snapshot ID or Call ID
|
|
159
|
+
* @returns {{ success: boolean, error?: string, snapshotId?: string, targetPath?: string, restoredExisted?: boolean }}
|
|
160
|
+
*/
|
|
161
|
+
rollbackSnapshot(snapshotIdOrCallback) {
|
|
162
|
+
if (!snapshotIdOrCallback) {
|
|
163
|
+
return { success: false, error: "snapshot-id-required" };
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
let snap = this.snapshots.get(snapshotIdOrCallback);
|
|
167
|
+
if (!snap) {
|
|
168
|
+
const id = this.callIndex.get(String(snapshotIdOrCallback));
|
|
169
|
+
if (id) snap = this.snapshots.get(id);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
if (!snap) {
|
|
173
|
+
return { success: false, error: "snapshot-not-found" };
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
if (snap.rolledBack) {
|
|
177
|
+
return { success: false, error: "snapshot-already-rolled-back", snapshotId: snap.id };
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
if (snap.tooLarge) {
|
|
181
|
+
return { success: false, error: "snapshot-size-limit-exceeded", snapshotId: snap.id };
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
try {
|
|
185
|
+
if (snap.existed && snap.content !== null) {
|
|
186
|
+
// Restore previous content
|
|
187
|
+
const dir = path.dirname(snap.targetPath);
|
|
188
|
+
if (!fs.existsSync(dir)) {
|
|
189
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
190
|
+
}
|
|
191
|
+
fs.writeFileSync(snap.targetPath, snap.content, "utf8");
|
|
192
|
+
} else if (!snap.existed) {
|
|
193
|
+
// File was newly created by this call: delete it to undo
|
|
194
|
+
if (fs.existsSync(snap.targetPath)) {
|
|
195
|
+
fs.unlinkSync(snap.targetPath);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
snap.rolledBack = true;
|
|
200
|
+
snap.rolledBackAt = Date.now();
|
|
201
|
+
|
|
202
|
+
return {
|
|
203
|
+
success: true,
|
|
204
|
+
snapshotId: snap.id,
|
|
205
|
+
targetPath: snap.targetPath,
|
|
206
|
+
restoredExisted: snap.existed,
|
|
207
|
+
rolledBackAt: snap.rolledBackAt,
|
|
208
|
+
};
|
|
209
|
+
} catch (err) {
|
|
210
|
+
return {
|
|
211
|
+
success: false,
|
|
212
|
+
error: "rollback-io-error",
|
|
213
|
+
detail: String(err && err.message || err),
|
|
214
|
+
snapshotId: snap.id,
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* Cleans up snapshots for a single session safely (#78).
|
|
221
|
+
* @param {string} sessionId
|
|
222
|
+
* @returns {{ cleared: number, sessionId: string }}
|
|
223
|
+
*/
|
|
224
|
+
clearSnapshots(sessionId) {
|
|
225
|
+
const sId = String(sessionId || "default");
|
|
226
|
+
const ids = this.sessionIndex.get(sId) || [];
|
|
227
|
+
let cleared = 0;
|
|
228
|
+
for (const id of ids) {
|
|
229
|
+
const snap = this.snapshots.get(id);
|
|
230
|
+
if (snap && snap.callId) {
|
|
231
|
+
this.callIndex.delete(snap.callId);
|
|
232
|
+
}
|
|
233
|
+
if (this.snapshots.delete(id)) {
|
|
234
|
+
cleared++;
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
this.sessionIndex.delete(sId);
|
|
238
|
+
return { cleared, sessionId: sId };
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* Cleans up snapshots globally across all sessions (#78).
|
|
243
|
+
* @returns {{ cleared: number }}
|
|
244
|
+
*/
|
|
245
|
+
clearAllSnapshots() {
|
|
246
|
+
const total = this.snapshots.size;
|
|
247
|
+
this.snapshots.clear();
|
|
248
|
+
this.sessionIndex.clear();
|
|
249
|
+
this.callIndex.clear();
|
|
250
|
+
return { cleared: total };
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
/**
|
|
254
|
+
* Returns snapshot usage metrics and statistics.
|
|
255
|
+
* @returns {{ totalSnapshots: number, sessionCount: number, totalBytes: number }}
|
|
256
|
+
*/
|
|
257
|
+
getStats() {
|
|
258
|
+
let totalBytes = 0;
|
|
259
|
+
for (const snap of this.snapshots.values()) {
|
|
260
|
+
totalBytes += snap.size || 0;
|
|
261
|
+
}
|
|
262
|
+
return {
|
|
263
|
+
totalSnapshots: this.snapshots.size,
|
|
264
|
+
sessionCount: this.sessionIndex.size,
|
|
265
|
+
totalBytes,
|
|
266
|
+
};
|
|
267
|
+
}
|
|
268
|
+
}
|
package/package.json
CHANGED
|
@@ -1,55 +1,55 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@goodandready/dsh-approval-gate",
|
|
3
|
-
"version": "0.1.
|
|
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
|
-
"type": "module",
|
|
6
|
-
"main": "lib/index.js",
|
|
7
|
-
"exports": {
|
|
8
|
-
".": "./lib/index.js",
|
|
9
|
-
"./package.json": "./package.json",
|
|
10
|
-
"./cordis.patch.yml": "./cordis.patch.yml"
|
|
11
|
-
},
|
|
12
|
-
"files": [
|
|
13
|
-
"lib",
|
|
14
|
-
"rules",
|
|
15
|
-
"cordis.patch.yml",
|
|
16
|
-
"README.md",
|
|
17
|
-
"README.zh.md",
|
|
18
|
-
"README.ru.md",
|
|
19
|
-
"CHANGELOG.md",
|
|
20
|
-
"LICENSE"
|
|
21
|
-
],
|
|
22
|
-
"dsh": {
|
|
23
|
-
"bundle": {
|
|
24
|
-
"patch": "./cordis.patch.yml"
|
|
25
|
-
}
|
|
26
|
-
},
|
|
27
|
-
"license": "MIT",
|
|
28
|
-
"peerDependencies": {
|
|
29
|
-
"@deepseek-ai/cordis": "^4.0.1",
|
|
30
|
-
"@deepseek-ai/schemastery": "^3.18.1"
|
|
31
|
-
},
|
|
32
|
-
"repository": {
|
|
33
|
-
"type": "git",
|
|
34
|
-
"url": "git+https://github.com/GooDAnDReaDY/dsh-approval-gate.git"
|
|
35
|
-
},
|
|
36
|
-
"homepage": "https://github.com/GooDAnDReaDY/dsh-approval-gate#readme",
|
|
37
|
-
"bugs": {
|
|
38
|
-
"url": "https://github.com/GooDAnDReaDY/dsh-approval-gate/issues"
|
|
39
|
-
},
|
|
40
|
-
"scripts": {
|
|
41
|
-
"test": "node --test test/*.test.mjs"
|
|
42
|
-
},
|
|
43
|
-
"publishConfig": {
|
|
44
|
-
"registry": "https://registry.npmjs.org",
|
|
45
|
-
"access": "public"
|
|
46
|
-
},
|
|
47
|
-
"keywords": [
|
|
48
|
-
"dsh",
|
|
49
|
-
"dsh-plugin",
|
|
50
|
-
"deepseek-harness",
|
|
51
|
-
"security",
|
|
52
|
-
"approval-gate",
|
|
53
|
-
"safety"
|
|
54
|
-
]
|
|
55
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "@goodandready/dsh-approval-gate",
|
|
3
|
+
"version": "0.1.22",
|
|
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
|
+
"type": "module",
|
|
6
|
+
"main": "lib/index.js",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": "./lib/index.js",
|
|
9
|
+
"./package.json": "./package.json",
|
|
10
|
+
"./cordis.patch.yml": "./cordis.patch.yml"
|
|
11
|
+
},
|
|
12
|
+
"files": [
|
|
13
|
+
"lib",
|
|
14
|
+
"rules",
|
|
15
|
+
"cordis.patch.yml",
|
|
16
|
+
"README.md",
|
|
17
|
+
"README.zh.md",
|
|
18
|
+
"README.ru.md",
|
|
19
|
+
"CHANGELOG.md",
|
|
20
|
+
"LICENSE"
|
|
21
|
+
],
|
|
22
|
+
"dsh": {
|
|
23
|
+
"bundle": {
|
|
24
|
+
"patch": "./cordis.patch.yml"
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
"license": "MIT",
|
|
28
|
+
"peerDependencies": {
|
|
29
|
+
"@deepseek-ai/cordis": "^4.0.1",
|
|
30
|
+
"@deepseek-ai/schemastery": "^3.18.1"
|
|
31
|
+
},
|
|
32
|
+
"repository": {
|
|
33
|
+
"type": "git",
|
|
34
|
+
"url": "git+https://github.com/GooDAnDReaDY/dsh-approval-gate.git"
|
|
35
|
+
},
|
|
36
|
+
"homepage": "https://github.com/GooDAnDReaDY/dsh-approval-gate#readme",
|
|
37
|
+
"bugs": {
|
|
38
|
+
"url": "https://github.com/GooDAnDReaDY/dsh-approval-gate/issues"
|
|
39
|
+
},
|
|
40
|
+
"scripts": {
|
|
41
|
+
"test": "node --test test/*.test.mjs"
|
|
42
|
+
},
|
|
43
|
+
"publishConfig": {
|
|
44
|
+
"registry": "https://registry.npmjs.org",
|
|
45
|
+
"access": "public"
|
|
46
|
+
},
|
|
47
|
+
"keywords": [
|
|
48
|
+
"dsh",
|
|
49
|
+
"dsh-plugin",
|
|
50
|
+
"deepseek-harness",
|
|
51
|
+
"security",
|
|
52
|
+
"approval-gate",
|
|
53
|
+
"safety"
|
|
54
|
+
]
|
|
55
|
+
}
|