@makefinks/daemon 0.1.3 → 0.1.4
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/package.json +1 -1
- package/src/ai/tools/run-bash.ts +4 -115
- package/src/security/bash-security-policy.ts +250 -0
package/package.json
CHANGED
package/src/ai/tools/run-bash.ts
CHANGED
|
@@ -1,123 +1,12 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
1
2
|
import { tool } from "ai";
|
|
2
3
|
import { z } from "zod";
|
|
3
|
-
import {
|
|
4
|
+
import { isDangerousCommand, isSensitivePathAccess } from "../../security/bash-security-policy";
|
|
4
5
|
import { getDaemonManager } from "../../state/daemon-state";
|
|
5
6
|
|
|
6
|
-
const DEFAULT_TIMEOUT_MS = 30000;
|
|
7
|
+
const DEFAULT_TIMEOUT_MS = 30000;
|
|
7
8
|
const MAX_OUTPUT_LENGTH = 50000;
|
|
8
9
|
|
|
9
|
-
const DANGEROUS_COMMANDS = [
|
|
10
|
-
"rm",
|
|
11
|
-
"rmdir",
|
|
12
|
-
"mv",
|
|
13
|
-
"kill",
|
|
14
|
-
"killall",
|
|
15
|
-
"pkill",
|
|
16
|
-
"shutdown",
|
|
17
|
-
"reboot",
|
|
18
|
-
"halt",
|
|
19
|
-
"poweroff",
|
|
20
|
-
"init",
|
|
21
|
-
"systemctl",
|
|
22
|
-
"chmod",
|
|
23
|
-
"chown",
|
|
24
|
-
"chgrp",
|
|
25
|
-
"mkfs",
|
|
26
|
-
"fdisk",
|
|
27
|
-
"dd",
|
|
28
|
-
"format",
|
|
29
|
-
"sudo",
|
|
30
|
-
"su",
|
|
31
|
-
"doas",
|
|
32
|
-
"passwd",
|
|
33
|
-
"useradd",
|
|
34
|
-
"userdel",
|
|
35
|
-
"usermod",
|
|
36
|
-
"groupadd",
|
|
37
|
-
"groupdel",
|
|
38
|
-
"visudo",
|
|
39
|
-
"crontab",
|
|
40
|
-
"iptables",
|
|
41
|
-
"ufw",
|
|
42
|
-
"firewall-cmd",
|
|
43
|
-
"mount",
|
|
44
|
-
"umount",
|
|
45
|
-
"fstab",
|
|
46
|
-
"apt-get remove",
|
|
47
|
-
"apt-get purge",
|
|
48
|
-
"apt remove",
|
|
49
|
-
"apt purge",
|
|
50
|
-
"yum remove",
|
|
51
|
-
"yum erase",
|
|
52
|
-
"dnf remove",
|
|
53
|
-
"pacman -R",
|
|
54
|
-
"brew uninstall",
|
|
55
|
-
"npm uninstall -g",
|
|
56
|
-
"pip uninstall",
|
|
57
|
-
"truncate",
|
|
58
|
-
"shred",
|
|
59
|
-
"wipefs",
|
|
60
|
-
">",
|
|
61
|
-
">>",
|
|
62
|
-
"git push --force",
|
|
63
|
-
"git push -f",
|
|
64
|
-
"git reset --hard",
|
|
65
|
-
"git clean -fd",
|
|
66
|
-
"docker rm",
|
|
67
|
-
"docker rmi",
|
|
68
|
-
"docker system prune",
|
|
69
|
-
"kubectl delete",
|
|
70
|
-
"terraform destroy",
|
|
71
|
-
"drop database",
|
|
72
|
-
"drop table",
|
|
73
|
-
"delete from",
|
|
74
|
-
"truncate table",
|
|
75
|
-
];
|
|
76
|
-
|
|
77
|
-
const DANGEROUS_PATTERNS = [
|
|
78
|
-
/\brm\s+(-[a-zA-Z]*r[a-zA-Z]*|-[a-zA-Z]*f[a-zA-Z]*|\s).*\//i,
|
|
79
|
-
/\brm\s+-rf?\s/i,
|
|
80
|
-
/\bkill\s+-9\b/i,
|
|
81
|
-
/\bsudo\s/i,
|
|
82
|
-
/\bsu\s+-?\s*$/i,
|
|
83
|
-
/\bchmod\s+[0-7]{3,4}\s/i,
|
|
84
|
-
/\bchown\s/i,
|
|
85
|
-
/\bdd\s+if=/i,
|
|
86
|
-
/>\s*\/dev\//i,
|
|
87
|
-
/\|.*\bsh\b/i,
|
|
88
|
-
/\|.*\bbash\b/i,
|
|
89
|
-
/curl.*\|\s*(ba)?sh/i,
|
|
90
|
-
/wget.*\|\s*(ba)?sh/i,
|
|
91
|
-
/eval\s*\$/i,
|
|
92
|
-
/\$\(.*\)/,
|
|
93
|
-
/`.*`/,
|
|
94
|
-
];
|
|
95
|
-
|
|
96
|
-
function isDangerousCommand(command: string): boolean {
|
|
97
|
-
const normalizedCmd = command.toLowerCase().trim();
|
|
98
|
-
|
|
99
|
-
for (const dangerous of DANGEROUS_COMMANDS) {
|
|
100
|
-
if (dangerous.includes(" ")) {
|
|
101
|
-
if (normalizedCmd.includes(dangerous.toLowerCase())) {
|
|
102
|
-
return true;
|
|
103
|
-
}
|
|
104
|
-
} else {
|
|
105
|
-
const wordBoundaryRegex = new RegExp(`\\b${dangerous}\\b`, "i");
|
|
106
|
-
if (wordBoundaryRegex.test(command)) {
|
|
107
|
-
return true;
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
for (const pattern of DANGEROUS_PATTERNS) {
|
|
113
|
-
if (pattern.test(command)) {
|
|
114
|
-
return true;
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
return false;
|
|
119
|
-
}
|
|
120
|
-
|
|
121
10
|
export const runBash = tool({
|
|
122
11
|
description:
|
|
123
12
|
"Execute a bash command on the user's system. Use this to run shell commands, scripts, install packages, manage files, or perform any terminal operation. Commands run in the current working directory by default.",
|
|
@@ -152,7 +41,7 @@ export const runBash = tool({
|
|
|
152
41
|
return true;
|
|
153
42
|
}
|
|
154
43
|
|
|
155
|
-
return isDangerousCommand(command);
|
|
44
|
+
return isDangerousCommand(command) || isSensitivePathAccess(command);
|
|
156
45
|
},
|
|
157
46
|
execute: async ({ command, workdir, timeout }) => {
|
|
158
47
|
return new Promise((resolve) => {
|
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
import { homedir } from "node:os";
|
|
2
|
+
|
|
3
|
+
const SENSITIVE_PATHS = [
|
|
4
|
+
"~/.ssh",
|
|
5
|
+
"~/.gnupg",
|
|
6
|
+
"~/.gpg",
|
|
7
|
+
"~/.aws",
|
|
8
|
+
"~/.azure",
|
|
9
|
+
"~/.config/gcloud",
|
|
10
|
+
"~/.kube",
|
|
11
|
+
"~/Library/Application Support/Google/Chrome",
|
|
12
|
+
"~/Library/Application Support/Firefox",
|
|
13
|
+
"~/Library/Application Support/Microsoft Edge",
|
|
14
|
+
"~/Library/Safari",
|
|
15
|
+
"~/.config/google-chrome",
|
|
16
|
+
"~/.config/chromium",
|
|
17
|
+
"~/.mozilla/firefox",
|
|
18
|
+
"~/Library/Keychains",
|
|
19
|
+
"~/.password-store",
|
|
20
|
+
"~/.local/share/keyrings",
|
|
21
|
+
"~/.env",
|
|
22
|
+
"~/.envrc",
|
|
23
|
+
"~/.netrc",
|
|
24
|
+
"~/Downloads",
|
|
25
|
+
"~/Documents",
|
|
26
|
+
"~/Desktop",
|
|
27
|
+
"~/Pictures",
|
|
28
|
+
"~/Movies",
|
|
29
|
+
"~/Music",
|
|
30
|
+
"~/Library/Messages",
|
|
31
|
+
"~/Library/Mail",
|
|
32
|
+
"~/Library/Calendars",
|
|
33
|
+
"~/Library/Contacts",
|
|
34
|
+
"~/Library/Cookies",
|
|
35
|
+
"~/.docker/config.json",
|
|
36
|
+
"~/.npmrc",
|
|
37
|
+
"~/.pypirc",
|
|
38
|
+
"~/.gem/credentials",
|
|
39
|
+
"~/.config/gh",
|
|
40
|
+
"~/.config/hub",
|
|
41
|
+
"~/.bash_history",
|
|
42
|
+
"~/.zsh_history",
|
|
43
|
+
"~/.node_repl_history",
|
|
44
|
+
"~/.python_history",
|
|
45
|
+
];
|
|
46
|
+
|
|
47
|
+
const SENSITIVE_PATH_PATTERNS = [
|
|
48
|
+
/\bid_rsa\b/i,
|
|
49
|
+
/\bid_ed25519\b/i,
|
|
50
|
+
/\bid_ecdsa\b/i,
|
|
51
|
+
/\bid_dsa\b/i,
|
|
52
|
+
/\bauthorized_keys\b/i,
|
|
53
|
+
/\bknown_hosts\b/i,
|
|
54
|
+
/\.pem\b/i,
|
|
55
|
+
/\.key\b/i,
|
|
56
|
+
/private.*key/i,
|
|
57
|
+
/\.env(\.|$)/i,
|
|
58
|
+
/\.envrc\b/i,
|
|
59
|
+
/aws.*credentials/i,
|
|
60
|
+
/aws.*config/i,
|
|
61
|
+
/\bkeychain\b/i,
|
|
62
|
+
/\bkeyring\b/i,
|
|
63
|
+
/\bLogin Data\b/i,
|
|
64
|
+
/\bCookies\b/i,
|
|
65
|
+
/\bWeb Data\b/i,
|
|
66
|
+
/\bsecurity\s+(find|dump|export)/i,
|
|
67
|
+
];
|
|
68
|
+
|
|
69
|
+
const DANGEROUS_COMMANDS = [
|
|
70
|
+
"rm",
|
|
71
|
+
"rmdir",
|
|
72
|
+
"mv",
|
|
73
|
+
"kill",
|
|
74
|
+
"killall",
|
|
75
|
+
"pkill",
|
|
76
|
+
"shutdown",
|
|
77
|
+
"reboot",
|
|
78
|
+
"halt",
|
|
79
|
+
"poweroff",
|
|
80
|
+
"init",
|
|
81
|
+
"systemctl",
|
|
82
|
+
"chmod",
|
|
83
|
+
"chown",
|
|
84
|
+
"chgrp",
|
|
85
|
+
"mkfs",
|
|
86
|
+
"fdisk",
|
|
87
|
+
"dd",
|
|
88
|
+
"format",
|
|
89
|
+
"sudo",
|
|
90
|
+
"su",
|
|
91
|
+
"doas",
|
|
92
|
+
"env",
|
|
93
|
+
"printenv",
|
|
94
|
+
"export",
|
|
95
|
+
"passwd",
|
|
96
|
+
"useradd",
|
|
97
|
+
"userdel",
|
|
98
|
+
"usermod",
|
|
99
|
+
"groupadd",
|
|
100
|
+
"groupdel",
|
|
101
|
+
"visudo",
|
|
102
|
+
"crontab",
|
|
103
|
+
"iptables",
|
|
104
|
+
"ufw",
|
|
105
|
+
"firewall-cmd",
|
|
106
|
+
"mount",
|
|
107
|
+
"umount",
|
|
108
|
+
"fstab",
|
|
109
|
+
"apt-get remove",
|
|
110
|
+
"apt-get purge",
|
|
111
|
+
"apt remove",
|
|
112
|
+
"apt purge",
|
|
113
|
+
"yum remove",
|
|
114
|
+
"yum erase",
|
|
115
|
+
"dnf remove",
|
|
116
|
+
"pacman -R",
|
|
117
|
+
"brew uninstall",
|
|
118
|
+
"npm uninstall -g",
|
|
119
|
+
"pip uninstall",
|
|
120
|
+
"truncate",
|
|
121
|
+
"shred",
|
|
122
|
+
"wipefs",
|
|
123
|
+
">",
|
|
124
|
+
">>",
|
|
125
|
+
"git push --force",
|
|
126
|
+
"git push -f",
|
|
127
|
+
"git reset --hard",
|
|
128
|
+
"git clean -fd",
|
|
129
|
+
"docker rm",
|
|
130
|
+
"docker rmi",
|
|
131
|
+
"docker system prune",
|
|
132
|
+
"kubectl delete",
|
|
133
|
+
"terraform destroy",
|
|
134
|
+
"drop database",
|
|
135
|
+
"drop table",
|
|
136
|
+
"delete from",
|
|
137
|
+
"truncate table",
|
|
138
|
+
];
|
|
139
|
+
|
|
140
|
+
const DANGEROUS_PATTERNS = [
|
|
141
|
+
/\brm\s+(-[a-zA-Z]*r[a-zA-Z]*|-[a-zA-Z]*f[a-zA-Z]*|\s).*\//i,
|
|
142
|
+
/\brm\s+-rf?\s/i,
|
|
143
|
+
/\bkill\s+-9\b/i,
|
|
144
|
+
/\bsudo\s/i,
|
|
145
|
+
/\bsu\s+-?\s*$/i,
|
|
146
|
+
/\bchmod\s+[0-7]{3,4}\s/i,
|
|
147
|
+
/\bchown\s/i,
|
|
148
|
+
/\bdd\s+if=/i,
|
|
149
|
+
/>\s*\/dev\//i,
|
|
150
|
+
/\|.*\bsh\b/i,
|
|
151
|
+
/\|.*\bbash\b/i,
|
|
152
|
+
/curl.*\|\s*(ba)?sh/i,
|
|
153
|
+
/wget.*\|\s*(ba)?sh/i,
|
|
154
|
+
/eval\s*\$/i,
|
|
155
|
+
/\$\(.*\)/,
|
|
156
|
+
/`.*`/,
|
|
157
|
+
/\benv\s*$/i,
|
|
158
|
+
/\bprintenv\s*$/i,
|
|
159
|
+
/\bexport\s+-p/i,
|
|
160
|
+
/\bset\s*\|/i,
|
|
161
|
+
/echo\s+\$\w*_?(KEY|TOKEN|SECRET|PASSWORD|CREDENTIALS)/i,
|
|
162
|
+
];
|
|
163
|
+
|
|
164
|
+
function expandPath(path: string): string {
|
|
165
|
+
if (path.startsWith("~/")) {
|
|
166
|
+
return path.replace("~", homedir());
|
|
167
|
+
}
|
|
168
|
+
if (path === "~") {
|
|
169
|
+
return homedir();
|
|
170
|
+
}
|
|
171
|
+
return path;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
function isSensitivePathAccess(command: string): boolean {
|
|
175
|
+
const normalizedCmd = command.trim();
|
|
176
|
+
const home = homedir();
|
|
177
|
+
|
|
178
|
+
for (const sensitivePath of SENSITIVE_PATHS) {
|
|
179
|
+
const expandedPath = expandPath(sensitivePath);
|
|
180
|
+
if (normalizedCmd.includes(expandedPath)) {
|
|
181
|
+
return true;
|
|
182
|
+
}
|
|
183
|
+
if (sensitivePath.startsWith("~/") && normalizedCmd.includes(sensitivePath)) {
|
|
184
|
+
return true;
|
|
185
|
+
}
|
|
186
|
+
if (normalizedCmd.includes(sensitivePath.replace("~", "$HOME"))) {
|
|
187
|
+
return true;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
for (const pattern of SENSITIVE_PATH_PATTERNS) {
|
|
192
|
+
if (pattern.test(normalizedCmd)) {
|
|
193
|
+
return true;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
const homeAccessPattern = new RegExp(
|
|
198
|
+
`(cat|less|head|tail|more|bat|grep|rg|awk|sed|find|ls|tree|du)\\s+[^|;]*?(~(?:/[^\\s/]+)?(?:\\s|$)|${home.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}(?:/[^\\s/]+)?(?:\\s|$))`
|
|
199
|
+
);
|
|
200
|
+
if (homeAccessPattern.test(normalizedCmd)) {
|
|
201
|
+
const allowedHomePaths = [
|
|
202
|
+
"~/projects",
|
|
203
|
+
"~/code",
|
|
204
|
+
"~/dev",
|
|
205
|
+
"~/src",
|
|
206
|
+
"~/repos",
|
|
207
|
+
"~/workspace",
|
|
208
|
+
"~/work",
|
|
209
|
+
"~/.local/bin",
|
|
210
|
+
"~/go",
|
|
211
|
+
"~/bin",
|
|
212
|
+
];
|
|
213
|
+
const isAllowedPath = allowedHomePaths.some((allowed) => {
|
|
214
|
+
const expanded = expandPath(allowed);
|
|
215
|
+
return normalizedCmd.includes(expanded) || normalizedCmd.includes(allowed);
|
|
216
|
+
});
|
|
217
|
+
if (!isAllowedPath) {
|
|
218
|
+
return true;
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
return false;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
function isDangerousCommand(command: string): boolean {
|
|
226
|
+
const normalizedCmd = command.toLowerCase().trim();
|
|
227
|
+
|
|
228
|
+
for (const dangerous of DANGEROUS_COMMANDS) {
|
|
229
|
+
if (dangerous.includes(" ")) {
|
|
230
|
+
if (normalizedCmd.includes(dangerous.toLowerCase())) {
|
|
231
|
+
return true;
|
|
232
|
+
}
|
|
233
|
+
} else {
|
|
234
|
+
const wordBoundaryRegex = new RegExp(`\\b${dangerous}\\b`, "i");
|
|
235
|
+
if (wordBoundaryRegex.test(command)) {
|
|
236
|
+
return true;
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
for (const pattern of DANGEROUS_PATTERNS) {
|
|
242
|
+
if (pattern.test(command)) {
|
|
243
|
+
return true;
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
return false;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
export { isDangerousCommand, isSensitivePathAccess };
|