dsh-runtime-kit 0.1.1 → 0.3.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.
@@ -0,0 +1,196 @@
1
+ // ============================================================================
2
+ // Governance policy — pure functions (no I/O, no DSH types)
3
+ //
4
+ // Everything here is deliberately dependency-free and synchronous so it can be
5
+ // unit-tested without a Host: secret redaction, injection detection, and the
6
+ // extraction of paths/commands/URLs from arbitrary tool arguments.
7
+ // ============================================================================
8
+ export const DEFAULT_GOVERNANCE = {
9
+ enabled: true,
10
+ audit: true,
11
+ redaction: true,
12
+ injection: 'annotate',
13
+ sandbox: false,
14
+ profile: 'default',
15
+ };
16
+ export function normalizeGovernance(raw) {
17
+ const input = (raw ?? {});
18
+ const injection = input.injection === 'off' || input.injection === 'block' || input.injection === 'annotate'
19
+ ? input.injection
20
+ : DEFAULT_GOVERNANCE.injection;
21
+ return {
22
+ enabled: input.enabled !== false,
23
+ audit: input.audit !== false,
24
+ redaction: input.redaction !== false,
25
+ injection,
26
+ sandbox: input.sandbox === true,
27
+ profile: typeof input.profile === 'string' && input.profile ? input.profile : DEFAULT_GOVERNANCE.profile,
28
+ };
29
+ }
30
+ const SECRET_PATTERNS = [
31
+ // GitHub PAT / OAuth / app token
32
+ { name: 'github-token', re: /\bgh[pousr]_[A-Za-z0-9]{20,}\b/g },
33
+ // npm granular token
34
+ { name: 'npm-token', re: /\bnpm_[A-Za-z0-9]{30,}\b/g },
35
+ // AWS access key id
36
+ { name: 'aws-key', re: /\b(?:AKIA|ASIA)[0-9A-Z]{16}\b/g },
37
+ // Slack token
38
+ { name: 'slack-token', re: /\bxox[abprs]-[A-Za-z0-9-]{10,}\b/g },
39
+ // 常见 PEM 私钥块
40
+ {
41
+ name: 'private-key',
42
+ re: /-----BEGIN [A-Z ]*PRIVATE KEY-----[\s\S]*?-----END [A-Z ]*PRIVATE KEY-----/g,
43
+ },
44
+ // Authorization: Bearer xxx
45
+ {
46
+ name: 'bearer',
47
+ re: /\bBearer\s+([A-Za-z0-9._~+/-]{16,}=*)/gi,
48
+ replace: () => 'Bearer [redacted:bearer]',
49
+ },
50
+ // scheme://user:password@host —— 只抹掉密码,保留 scheme 与用户名
51
+ {
52
+ name: 'url-password',
53
+ re: /\b([a-z][a-z0-9+.-]*:\/\/[^\s/:@]+):[^\s/@]+@/gi,
54
+ replace: (match, groups) => `${groups[0]}:***@`,
55
+ },
56
+ ];
57
+ /** 抹掉一段文本里的凭据。没有命中时原样返回,便于调用方判断是否需要改写。 */
58
+ export function redactSecrets(input) {
59
+ if (typeof input !== 'string' || input.length === 0)
60
+ return { text: input, hits: [] };
61
+ let text = input;
62
+ const hits = [];
63
+ for (const pattern of SECRET_PATTERNS) {
64
+ // 带捕获组的替换函数需要拿到分组,先收集再统一替换
65
+ text = text.replace(pattern.re, (...args) => {
66
+ hits.push(pattern.name);
67
+ if (pattern.replace) {
68
+ const groups = args.slice(1, -2);
69
+ return pattern.replace(String(args[0]), groups);
70
+ }
71
+ return `[redacted:${pattern.name}]`;
72
+ });
73
+ }
74
+ return { text, hits: [...new Set(hits)] };
75
+ }
76
+ // ============================================================================
77
+ // 提示注入检测
78
+ // ============================================================================
79
+ /** 保守的高置信度模式:宁可漏报,也不要把正常代码/文档判成注入。 */
80
+ const INJECTION_PATTERNS = [
81
+ { name: 'ignore-previous', re: /ignore\s+(?:all\s+)?(?:the\s+)?(?:previous|prior|above)\s+(?:instructions?|prompts?|rules?)/i },
82
+ { name: 'disregard-instructions', re: /disregard\s+(?:all\s+)?(?:the\s+)?(?:previous|prior|above|system)\s+(?:instructions?|prompts?)/i },
83
+ { name: 'ignore-zh', re: /忽略(?:以上|之前|前面|上述)(?:的)?(?:所有)?(?:指令|提示|要求|规则)/ },
84
+ { name: 'role-override', re: /\byou\s+are\s+now\s+(?:a|an|the)\s+[a-z]/i },
85
+ { name: 'system-tag', re: /<\|?im_start\|?>|<\/?system>/i },
86
+ { name: 'system-colon', re: /(?:^|\n)\s*system\s*(?:prompt|message)?\s*[::]/i },
87
+ { name: 'reveal-prompt', re: /reveal\s+(?:your|the)\s+(?:system\s+)?(?:prompt|instructions)/i },
88
+ { name: 'hide-from-user', re: /do\s+not\s+(?:tell|inform|mention\s+to)\s+the\s+user/i },
89
+ ];
90
+ /** 检测工具返回里是否夹带“让模型改行为”的指令。 */
91
+ export function detectInjection(text) {
92
+ if (typeof text !== 'string' || text.length === 0)
93
+ return { hits: [] };
94
+ const hits = [];
95
+ for (const pattern of INJECTION_PATTERNS) {
96
+ if (pattern.re.test(text))
97
+ hits.push(pattern.name);
98
+ }
99
+ return { hits };
100
+ }
101
+ const PATH_KEY = /(?:^|[_-])(path|paths|file|files|filepath|filename|dir|directory|folder|target|root|cwd|workdir|workspace)$/i;
102
+ const COMMAND_KEY = /(?:^|[_-])(command|cmd|script|shell|line)$/i;
103
+ const URL_KEY = /(?:url|uri|endpoint|href|src)$/i;
104
+ const MAX_DEPTH = 4;
105
+ const MAX_ITEMS = 32;
106
+ function looksLikePath(value) {
107
+ if (value.length === 0 || value.length > 4096)
108
+ return false;
109
+ if (value.startsWith('file:'))
110
+ return true;
111
+ if (/^[a-zA-Z]:[\\/]/.test(value))
112
+ return true;
113
+ if (value.startsWith('/') || value.startsWith('./') || value.startsWith('../'))
114
+ return true;
115
+ return /[\\/]/.test(value);
116
+ }
117
+ function looksLikeCommand(value) {
118
+ if (value.length === 0 || value.length > 4096)
119
+ return false;
120
+ return /^[\w./\\:-]+\s+/.test(value) || /^(?:sudo|git|npm|pnpm|node|python|pwsh|powershell|cmd|bash|sh|curl|rm|mv|cp|del)\b/i.test(value);
121
+ }
122
+ function looksLikeUrl(value) {
123
+ return /^https?:\/\//i.test(value);
124
+ }
125
+ /**
126
+ * 从工具参数里抽出可能触发沙箱规则的值。
127
+ *
128
+ * 工具的参数形状各不相同(`path` / `file_path` / `paths[]` / `cwd` …),这里按
129
+ * 键名与值的形态双重判断,宁可多抽几个交给规则匹配,也不漏掉真正的写操作。
130
+ */
131
+ export function extractTargets(args) {
132
+ const out = { paths: [], commands: [], urls: [] };
133
+ const push = (list, value) => {
134
+ if (list.length < MAX_ITEMS && !list.includes(value))
135
+ list.push(value);
136
+ };
137
+ const walk = (value, key, depth) => {
138
+ if (depth > MAX_DEPTH)
139
+ return;
140
+ if (typeof value === 'string') {
141
+ if (looksLikeUrl(value) || URL_KEY.test(key)) {
142
+ if (looksLikeUrl(value))
143
+ push(out.urls, value);
144
+ return;
145
+ }
146
+ if (COMMAND_KEY.test(key) || looksLikeCommand(value)) {
147
+ if (looksLikeCommand(value))
148
+ push(out.commands, value);
149
+ }
150
+ if (PATH_KEY.test(key) || looksLikePath(value)) {
151
+ if (looksLikePath(value))
152
+ push(out.paths, value);
153
+ }
154
+ return;
155
+ }
156
+ if (Array.isArray(value)) {
157
+ for (const item of value)
158
+ walk(item, key, depth + 1);
159
+ return;
160
+ }
161
+ if (value && typeof value === 'object') {
162
+ for (const [childKey, child] of Object.entries(value)) {
163
+ walk(child, childKey, depth + 1);
164
+ }
165
+ }
166
+ };
167
+ walk(args, '', 0);
168
+ return out;
169
+ }
170
+ /** 工具名 → 文件操作类型(决定读/写/删三种文件规则里用哪一组)。 */
171
+ export function inferFileOperation(toolName) {
172
+ const name = String(toolName ?? '');
173
+ if (/delete|remove|\brm\b|^rm[_-]|unlink|trash|purge/i.test(name))
174
+ return 'delete';
175
+ if (/write|edit|create|append|move|rename|patch|apply|replace|mkdir|touch/i.test(name))
176
+ return 'write';
177
+ return 'read';
178
+ }
179
+ /** 稳定短哈希:审计里只记参数指纹,不记原文。 */
180
+ export function shortHash(value) {
181
+ // 延迟引入,避免把 crypto 拖进纯函数模块的静态依赖图
182
+ const text = (() => {
183
+ try {
184
+ return JSON.stringify(value ?? null);
185
+ }
186
+ catch {
187
+ return String(value);
188
+ }
189
+ })();
190
+ let h1 = 0x811c9dc5;
191
+ for (let i = 0; i < text.length; i++) {
192
+ h1 ^= text.charCodeAt(i);
193
+ h1 = Math.imul(h1, 0x01000193);
194
+ }
195
+ return (h1 >>> 0).toString(16).padStart(8, '0');
196
+ }
@@ -0,0 +1 @@
1
+ {"version":3,"file":"policy.js","sourceRoot":"","sources":["../../src/hooks/policy.ts"],"names":[],"mappings":"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"}