openfox 2.0.73 → 2.0.75
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/dist/{chat-handler-LK5RUAGO.js → chat-handler-QLMBYRDY.js} +15 -14
- package/dist/{chunk-YD6NDTKF.js → chunk-5GOOSZX7.js} +1 -1
- package/dist/chunk-6COWK53A.js +7 -0
- package/dist/{chunk-GMF36EZ3.js → chunk-6ZMGWDOD.js} +87 -506
- package/dist/{chunk-2DCJERYX.js → chunk-CJ2AJ7HU.js} +3 -3
- package/dist/{chunk-O2BE7KVQ.js → chunk-J3VQBVGO.js} +8 -6
- package/dist/chunk-KEVRZWL3.js +495 -0
- package/dist/{chunk-TK4BZIXA.js → chunk-LKUAKQZY.js} +42 -20
- package/dist/{chunk-PYB2UEC3.js → chunk-LTFB2WWE.js} +4 -4
- package/dist/{chunk-CHPRGEY5.js → chunk-LUG5QTFC.js} +2 -2
- package/dist/{chunk-X2Y3AJPP.js → chunk-MF3UW36X.js} +10 -2
- package/dist/{chunk-EYXDM2FY.js → chunk-MIJX54CG.js} +395 -116
- package/dist/{chunk-NOU2QIL5.js → chunk-O3NUNU4H.js} +5 -2
- package/dist/{chunk-KKRLQH6F.js → chunk-PJQW6TYI.js} +17 -7
- package/dist/{chunk-6XKM25OT.js → chunk-YFFP4DR4.js} +4 -4
- package/dist/cli/dev.js +1 -1
- package/dist/cli/index.js +1 -1
- package/dist/{compactor-4UKYWC5F.js → compactor-AERNYEMP.js} +4 -4
- package/dist/{events-AEQ6GQXI.js → events-X353XCPN.js} +3 -3
- package/dist/install-CPOPMCSE.js +120 -0
- package/dist/{manager-24C2AXVF.js → manager-5GILDCVD.js} +3 -3
- package/dist/{orchestrator-RTZVMWQV.js → orchestrator-BIFTALBG.js} +13 -12
- package/dist/package.json +1 -1
- package/dist/path-security-3FAIPJSV.js +50 -0
- package/dist/{processor-3O26I7DO.js → processor-MQ47QSDR.js} +15 -14
- package/dist/{protocol-4QFU7WAI.js → protocol-EWEVW65B.js} +3 -3
- package/dist/{protocol-DGQWVFcP.d.ts → protocol-Sr6Jr4ZS.d.ts} +5 -2
- package/dist/{serve-I6K7A7RL.js → serve-UD27KOL2.js} +18 -17
- package/dist/server/index.d.ts +5 -1
- package/dist/server/index.js +16 -15
- package/dist/{server-G2J4RAXL.js → server-TJ3PP5EW.js} +15 -14
- package/dist/shared/index.d.ts +1 -1
- package/dist/shared/index.js +1 -1
- package/dist/{tools-X52DFUS6.js → tools-RZ464GUJ.js} +17 -15
- package/dist/{update-RJB4GPHE.js → update-MWIZH4P2.js} +12 -3
- package/dist/web/assets/{index-CmechlaK.css → index-BNVBxKnB.css} +1 -1
- package/dist/web/assets/{index-C7SW16F7.js → index-Bn4hwZeP.js} +74 -74
- package/dist/web/index.html +2 -2
- package/dist/web/sw.js +1 -1
- package/dist/{workspace-ET3FHZYN.js → workspace-6QTTUMMW.js} +4 -2
- package/package.json +1 -1
- package/dist/chunk-NE7OWIIO.js +0 -7
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
getEventStore,
|
|
6
6
|
getRuntimeConfig,
|
|
7
7
|
updateSessionMetadata
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-YFFP4DR4.js";
|
|
9
9
|
import {
|
|
10
10
|
buildMessagesFromStoredEvents,
|
|
11
11
|
foldPendingConfirmations
|
|
@@ -13,7 +13,7 @@ import {
|
|
|
13
13
|
import {
|
|
14
14
|
createContextStateMessage,
|
|
15
15
|
createSessionStateMessage
|
|
16
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-O3NUNU4H.js";
|
|
17
17
|
import {
|
|
18
18
|
logger
|
|
19
19
|
} from "./chunk-K44MW7JJ.js";
|
|
@@ -184,4 +184,4 @@ export {
|
|
|
184
184
|
buildRunChatTurnParams,
|
|
185
185
|
generateSessionNameForSession
|
|
186
186
|
};
|
|
187
|
-
//# sourceMappingURL=chunk-
|
|
187
|
+
//# sourceMappingURL=chunk-CJ2AJ7HU.js.map
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import {
|
|
2
|
-
PathAccessDeniedError,
|
|
3
2
|
createAssemblyResult,
|
|
4
3
|
findAgentById,
|
|
5
4
|
getAllInstructions,
|
|
@@ -10,7 +9,7 @@ import {
|
|
|
10
9
|
loadAllAgentsDefault,
|
|
11
10
|
processEventsForConversation,
|
|
12
11
|
runTopLevelAgentLoop
|
|
13
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-6ZMGWDOD.js";
|
|
14
13
|
import {
|
|
15
14
|
TurnMetrics,
|
|
16
15
|
buildAgentReminder,
|
|
@@ -19,12 +18,15 @@ import {
|
|
|
19
18
|
createChatDoneEvent,
|
|
20
19
|
createMessageStartEvent
|
|
21
20
|
} from "./chunk-XWBNYU2L.js";
|
|
21
|
+
import {
|
|
22
|
+
PathAccessDeniedError
|
|
23
|
+
} from "./chunk-KEVRZWL3.js";
|
|
22
24
|
import {
|
|
23
25
|
getCurrentContextWindowId,
|
|
24
26
|
getCurrentWindowMessageOptions,
|
|
25
27
|
getEventStore,
|
|
26
28
|
getRuntimeConfig
|
|
27
|
-
} from "./chunk-
|
|
29
|
+
} from "./chunk-YFFP4DR4.js";
|
|
28
30
|
import {
|
|
29
31
|
buildSnapshotFromSessionState
|
|
30
32
|
} from "./chunk-DJASQ2ZB.js";
|
|
@@ -64,7 +66,7 @@ function resolveAgentDef(sessionManager, sessionId) {
|
|
|
64
66
|
}
|
|
65
67
|
async function buildCachedPrompt(sessionManager, sessionId, agentDef) {
|
|
66
68
|
const { instructionContent, skills } = await loadSessionContext(sessionManager, sessionId);
|
|
67
|
-
const { getToolRegistryForAgent: getToolRegistryForAgent2 } = await import("./tools-
|
|
69
|
+
const { getToolRegistryForAgent: getToolRegistryForAgent2 } = await import("./tools-RZ464GUJ.js");
|
|
68
70
|
const tools = getToolRegistryForAgent2(agentDef).definitions;
|
|
69
71
|
const toolFingerprint = getToolFingerprint(tools);
|
|
70
72
|
const allAgents = await loadAllAgentsDefault();
|
|
@@ -77,7 +79,7 @@ async function buildCachedPrompt(sessionManager, sessionId, agentDef) {
|
|
|
77
79
|
async function computeSessionHash(sessionManager, sessionId) {
|
|
78
80
|
const { instructionContent, skills } = await loadSessionContext(sessionManager, sessionId);
|
|
79
81
|
const agentDef = await resolveAgentDef(sessionManager, sessionId);
|
|
80
|
-
const { getToolRegistryForAgent: getToolRegistryForAgent2 } = await import("./tools-
|
|
82
|
+
const { getToolRegistryForAgent: getToolRegistryForAgent2 } = await import("./tools-RZ464GUJ.js");
|
|
81
83
|
const tools = getToolRegistryForAgent2(agentDef).definitions;
|
|
82
84
|
const toolFingerprint = getToolFingerprint(tools);
|
|
83
85
|
return computeDynamicContextHash(instructionContent, skills, toolFingerprint);
|
|
@@ -357,4 +359,4 @@ export {
|
|
|
357
359
|
runChatTurn,
|
|
358
360
|
runAgentTurn
|
|
359
361
|
};
|
|
360
|
-
//# sourceMappingURL=chunk-
|
|
362
|
+
//# sourceMappingURL=chunk-J3VQBVGO.js.map
|
|
@@ -0,0 +1,495 @@
|
|
|
1
|
+
import {
|
|
2
|
+
getEventStore
|
|
3
|
+
} from "./chunk-YFFP4DR4.js";
|
|
4
|
+
import {
|
|
5
|
+
createChatPathConfirmationMessage
|
|
6
|
+
} from "./chunk-O3NUNU4H.js";
|
|
7
|
+
|
|
8
|
+
// src/server/tools/path-security.ts
|
|
9
|
+
import { realpath } from "fs/promises";
|
|
10
|
+
import { resolve, normalize, join, basename, sep, posix, win32 } from "path";
|
|
11
|
+
import { homedir, tmpdir } from "os";
|
|
12
|
+
var SAFE_PATHS = /* @__PURE__ */ new Set([
|
|
13
|
+
"/dev/null",
|
|
14
|
+
"/dev/zero",
|
|
15
|
+
"/dev/random",
|
|
16
|
+
"/dev/urandom",
|
|
17
|
+
"/dev/stdin",
|
|
18
|
+
"/dev/stdout",
|
|
19
|
+
"/dev/stderr",
|
|
20
|
+
"/dev/fd/0",
|
|
21
|
+
"/dev/fd/1",
|
|
22
|
+
"/dev/fd/2"
|
|
23
|
+
]);
|
|
24
|
+
var ALLOWED_ROOTS = ["/tmp", "/var/tmp", tmpdir()];
|
|
25
|
+
var SENSITIVE_FILE_PATTERNS = [
|
|
26
|
+
// Dotenv files (but not .envrc, .env-example, .env.example)
|
|
27
|
+
/^\.env$/,
|
|
28
|
+
// .env
|
|
29
|
+
/^\.env\.(?!example)[a-zA-Z0-9_.-]+$/,
|
|
30
|
+
// .env.local, .env.production (not .env.example)
|
|
31
|
+
// Credential files
|
|
32
|
+
/^credentials\.json$/i,
|
|
33
|
+
/^secrets?\.(?:json|ya?ml|toml)$/i,
|
|
34
|
+
// Private keys
|
|
35
|
+
/\.pem$/i,
|
|
36
|
+
/\.key$/i,
|
|
37
|
+
/^id_rsa/,
|
|
38
|
+
/^id_ed25519/,
|
|
39
|
+
/^id_ecdsa/,
|
|
40
|
+
/^id_dsa/,
|
|
41
|
+
// Auth configs
|
|
42
|
+
/^\.netrc$/
|
|
43
|
+
];
|
|
44
|
+
function isSensitivePath(path) {
|
|
45
|
+
const fileName = basename(path);
|
|
46
|
+
if (!fileName) return false;
|
|
47
|
+
return SENSITIVE_FILE_PATTERNS.some((pattern) => pattern.test(fileName));
|
|
48
|
+
}
|
|
49
|
+
var sessionAllowedPaths = /* @__PURE__ */ new Map();
|
|
50
|
+
function addAllowedPath(sessionId, path) {
|
|
51
|
+
if (!sessionAllowedPaths.has(sessionId)) {
|
|
52
|
+
sessionAllowedPaths.set(sessionId, /* @__PURE__ */ new Set());
|
|
53
|
+
}
|
|
54
|
+
sessionAllowedPaths.get(sessionId).add(normalize(path));
|
|
55
|
+
}
|
|
56
|
+
function addAllowedPaths(sessionId, paths) {
|
|
57
|
+
for (const path of paths) {
|
|
58
|
+
addAllowedPath(sessionId, path);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
function isPathAllowed(sessionId, path) {
|
|
62
|
+
const allowed = sessionAllowedPaths.get(sessionId);
|
|
63
|
+
if (!allowed) return false;
|
|
64
|
+
return allowed.has(normalize(path));
|
|
65
|
+
}
|
|
66
|
+
function clearAllowedPaths(sessionId) {
|
|
67
|
+
sessionAllowedPaths.delete(sessionId);
|
|
68
|
+
}
|
|
69
|
+
async function safeRealpath(path) {
|
|
70
|
+
const absolutePath = normalize(resolve(path));
|
|
71
|
+
try {
|
|
72
|
+
return await realpath(absolutePath);
|
|
73
|
+
} catch {
|
|
74
|
+
const missingSegments = [];
|
|
75
|
+
let current = absolutePath;
|
|
76
|
+
while (true) {
|
|
77
|
+
try {
|
|
78
|
+
const canonicalParent = await realpath(current);
|
|
79
|
+
return normalize(join(canonicalParent, ...missingSegments.reverse()));
|
|
80
|
+
} catch {
|
|
81
|
+
const parent = resolve(current, "..");
|
|
82
|
+
if (parent === current) return absolutePath;
|
|
83
|
+
missingSegments.push(basename(current));
|
|
84
|
+
current = parent;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
async function isPathWithinSandbox(path, workdir, sessionId) {
|
|
90
|
+
const normalizedWorkdir = normalize((await safeRealpath(workdir)).replace(/\/+$/, ""));
|
|
91
|
+
let resolvedPath;
|
|
92
|
+
try {
|
|
93
|
+
resolvedPath = normalize(await realpath(path));
|
|
94
|
+
} catch {
|
|
95
|
+
try {
|
|
96
|
+
const { readlink } = await import("fs/promises");
|
|
97
|
+
const linkTarget = await readlink(path);
|
|
98
|
+
const linkDir = resolve(path, "..");
|
|
99
|
+
resolvedPath = await safeRealpath(resolve(linkDir, linkTarget));
|
|
100
|
+
} catch {
|
|
101
|
+
resolvedPath = await safeRealpath(path);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
resolvedPath = resolvedPath.replace(/\/+$/, "");
|
|
105
|
+
if (resolvedPath === normalizedWorkdir || resolvedPath.startsWith(normalizedWorkdir + sep)) {
|
|
106
|
+
return { allowed: true, resolvedPath };
|
|
107
|
+
}
|
|
108
|
+
for (const root of ALLOWED_ROOTS) {
|
|
109
|
+
const normalizedRoot = normalize((await safeRealpath(root)).replace(/\/+$/, ""));
|
|
110
|
+
if (resolvedPath === normalizedRoot || resolvedPath.startsWith(normalizedRoot + sep)) {
|
|
111
|
+
return { allowed: true, resolvedPath };
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
if (sessionId && (isPathAllowed(sessionId, path) || isPathAllowed(sessionId, resolvedPath))) {
|
|
115
|
+
return { allowed: true, resolvedPath };
|
|
116
|
+
}
|
|
117
|
+
return { allowed: false, resolvedPath };
|
|
118
|
+
}
|
|
119
|
+
function looksLikeRegex(str) {
|
|
120
|
+
return /[*?+[\]\\]/.test(str);
|
|
121
|
+
}
|
|
122
|
+
var isWindows = () => process.platform === "win32";
|
|
123
|
+
function isWindowsAbsolutePath(str) {
|
|
124
|
+
return /^[A-Za-z]:[\\/]/.test(str);
|
|
125
|
+
}
|
|
126
|
+
function normalizeExtracted(path) {
|
|
127
|
+
return isWindowsAbsolutePath(path) ? win32.normalize(path) : posix.normalize(path);
|
|
128
|
+
}
|
|
129
|
+
function isPlaceholderToken(str) {
|
|
130
|
+
return str.includes("__URL__") || str.includes("__FILEURL__") || str.includes("__SED__") || str.includes("__COMMIT_MSG__");
|
|
131
|
+
}
|
|
132
|
+
function extractAbsolutePathsFromCommand(command) {
|
|
133
|
+
if (!command.trim()) {
|
|
134
|
+
return [];
|
|
135
|
+
}
|
|
136
|
+
const paths = [];
|
|
137
|
+
const home = homedir();
|
|
138
|
+
let sanitized = command.replace(/https?:\/\/[^\s'"]+/g, " __URL__ ").replace(/ftp:\/\/[^\s'"]+/g, " __URL__ ");
|
|
139
|
+
sanitized = sanitized.replace(/(?<!\w)s\/[^/]*\/[^/]*\/[gip]*/g, " __SED__ ");
|
|
140
|
+
sanitized = sanitized.replace(/(?<!\w)s\|[^|]*\|[^|]*\|[gip]*/g, " __SED__ ");
|
|
141
|
+
sanitized = sanitized.replace(/(?<!\w)s:[^:]*:[^:]*:[gip]*/g, " __SED__ ");
|
|
142
|
+
sanitized = sanitized.replace(
|
|
143
|
+
/git\s+commit\b.*?(?:-(?:[a-zA-Z]*m)(?=\s)|--message)\s+(["'])(?:(?!\1).)*\1/g,
|
|
144
|
+
(match2) => match2.replace(/\/[^\s"'|&;<>`()]+/g, " __COMMIT_MSG__ ")
|
|
145
|
+
);
|
|
146
|
+
const fileUrlMatches = command.matchAll(/file:\/\/([^\s'"]+)/g);
|
|
147
|
+
for (const match2 of fileUrlMatches) {
|
|
148
|
+
const filePath = match2[1];
|
|
149
|
+
if (filePath && !isSafePath(filePath)) {
|
|
150
|
+
paths.push(normalizeExtracted(filePath));
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
sanitized = sanitized.replace(/file:\/\/[^\s'"]+/g, " __FILEURL__ ");
|
|
154
|
+
const tildePattern = /(?:^|[\s='"(])~(\/[^\s'"()]*)?(?=[\s'"()]|$)/g;
|
|
155
|
+
let match;
|
|
156
|
+
while ((match = tildePattern.exec(sanitized)) !== null) {
|
|
157
|
+
const pathPart = match[1] ?? "";
|
|
158
|
+
const fullPath = home + pathPart;
|
|
159
|
+
const resolved = normalize(resolve(fullPath));
|
|
160
|
+
if (!isSafePath(resolved)) {
|
|
161
|
+
paths.push(resolved);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
const quotedPattern = /["']([^"']+)["']/g;
|
|
165
|
+
while ((match = quotedPattern.exec(sanitized)) !== null) {
|
|
166
|
+
const content = match[1];
|
|
167
|
+
if (content.startsWith("/") && content.endsWith("/")) {
|
|
168
|
+
continue;
|
|
169
|
+
}
|
|
170
|
+
if (content.startsWith("/") && looksLikeRegex(content)) {
|
|
171
|
+
continue;
|
|
172
|
+
}
|
|
173
|
+
if (isWindowsAbsolutePath(content)) {
|
|
174
|
+
const resolved = normalizeExtracted(content);
|
|
175
|
+
if (!isSafePath(resolved)) {
|
|
176
|
+
paths.push(resolved);
|
|
177
|
+
}
|
|
178
|
+
} else if (!isWindows() && content.startsWith("/")) {
|
|
179
|
+
const resolved = normalizeExtracted(content);
|
|
180
|
+
if (!isSafePath(resolved)) {
|
|
181
|
+
paths.push(resolved);
|
|
182
|
+
}
|
|
183
|
+
} else if (content.startsWith("~")) {
|
|
184
|
+
const pathPart = content.slice(1);
|
|
185
|
+
const fullPath = join(home, pathPart);
|
|
186
|
+
const resolved = normalize(resolve(fullPath));
|
|
187
|
+
if (!isSafePath(resolved)) {
|
|
188
|
+
paths.push(resolved);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
if (isWindows()) {
|
|
193
|
+
const winAbsolutePattern = /(?:^|[\s=(])([A-Za-z]:[\\/][^\s"'|&;<>`()]+)/g;
|
|
194
|
+
while ((match = winAbsolutePattern.exec(sanitized)) !== null) {
|
|
195
|
+
const candidate = match[1];
|
|
196
|
+
if (isPlaceholderToken(candidate)) continue;
|
|
197
|
+
const resolved = normalizeExtracted(candidate);
|
|
198
|
+
if (!isSafePath(resolved)) {
|
|
199
|
+
paths.push(resolved);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
} else {
|
|
203
|
+
const absolutePattern = /(?:^|[\s=(])(\/[^\s"'|&;<>`()]+)/g;
|
|
204
|
+
while ((match = absolutePattern.exec(sanitized)) !== null) {
|
|
205
|
+
const candidate = match[1];
|
|
206
|
+
if (isPlaceholderToken(candidate)) continue;
|
|
207
|
+
if (looksLikeRegex(candidate)) continue;
|
|
208
|
+
const resolved = normalizeExtracted(candidate);
|
|
209
|
+
if (resolved === "/" || resolved === "") continue;
|
|
210
|
+
if (!isSafePath(resolved)) {
|
|
211
|
+
paths.push(resolved);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
return [...new Set(paths)];
|
|
216
|
+
}
|
|
217
|
+
function extractSensitivePathsFromCommand(command) {
|
|
218
|
+
if (!command.trim()) {
|
|
219
|
+
return [];
|
|
220
|
+
}
|
|
221
|
+
const paths = [];
|
|
222
|
+
const quotedPattern = /["']([^"']+)["']/g;
|
|
223
|
+
let match;
|
|
224
|
+
while ((match = quotedPattern.exec(command)) !== null) {
|
|
225
|
+
const content = match[1];
|
|
226
|
+
if (isSensitivePath(content)) {
|
|
227
|
+
paths.push(content);
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
const unquoted = command.replace(/["'][^"']*["']/g, " ").replace(/[|&;><]/g, " ");
|
|
231
|
+
const tokens = unquoted.split(/\s+/).filter(Boolean);
|
|
232
|
+
for (const token of tokens) {
|
|
233
|
+
if (token.startsWith("-")) continue;
|
|
234
|
+
if (token.includes("://")) continue;
|
|
235
|
+
if (isSensitivePath(token)) {
|
|
236
|
+
paths.push(token);
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
return [...new Set(paths)];
|
|
240
|
+
}
|
|
241
|
+
function isSafePath(path) {
|
|
242
|
+
const normalized = posix.normalize(path);
|
|
243
|
+
return SAFE_PATHS.has(normalized);
|
|
244
|
+
}
|
|
245
|
+
async function checkPathsAccess(paths, workdir, sessionId) {
|
|
246
|
+
if (paths.length === 0) {
|
|
247
|
+
return { needsConfirmation: false, deniedPaths: [], sensitivePaths: [] };
|
|
248
|
+
}
|
|
249
|
+
const deniedPaths = [];
|
|
250
|
+
const sensitivePaths = [];
|
|
251
|
+
for (const path of paths) {
|
|
252
|
+
const result = await isPathWithinSandbox(path, workdir, sessionId);
|
|
253
|
+
if (!result.allowed) {
|
|
254
|
+
deniedPaths.push(result.resolvedPath);
|
|
255
|
+
}
|
|
256
|
+
if (isSensitivePath(path) && !isPathAllowed(sessionId ?? "", path)) {
|
|
257
|
+
sensitivePaths.push(result.resolvedPath);
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
const uniqueDenied = [...new Set(deniedPaths)];
|
|
261
|
+
const uniqueSensitive = [...new Set(sensitivePaths)];
|
|
262
|
+
return {
|
|
263
|
+
needsConfirmation: uniqueDenied.length > 0 || uniqueSensitive.length > 0,
|
|
264
|
+
deniedPaths: uniqueDenied,
|
|
265
|
+
sensitivePaths: uniqueSensitive
|
|
266
|
+
};
|
|
267
|
+
}
|
|
268
|
+
var DANGEROUS_PATTERNS = [
|
|
269
|
+
/sudo\s/,
|
|
270
|
+
/rm\s+(-rf?|--recursive)\s+[~]/,
|
|
271
|
+
/chmod\s+777/,
|
|
272
|
+
/>\s*\/dev\/sd/,
|
|
273
|
+
/mkfs\s/,
|
|
274
|
+
/dd\s+if=/,
|
|
275
|
+
/:\(\)\s*\{\s*:\s*\|\s*:\s*&\s*\}\s*;/
|
|
276
|
+
];
|
|
277
|
+
function extractDangerousPatterns(command) {
|
|
278
|
+
const dangerous = [];
|
|
279
|
+
for (const pattern of DANGEROUS_PATTERNS) {
|
|
280
|
+
if (pattern.test(command)) {
|
|
281
|
+
dangerous.push(pattern.source);
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
return dangerous;
|
|
285
|
+
}
|
|
286
|
+
function extractGitNoVerify(command) {
|
|
287
|
+
const subCommands = command.split(/\s*(?:&&|\|\||\||;)\s*/);
|
|
288
|
+
for (const sub of subCommands) {
|
|
289
|
+
const parts = sub.trim().split(/\s+/);
|
|
290
|
+
const gitIndex = parts.indexOf("git");
|
|
291
|
+
const subCmd = parts[gitIndex + 1];
|
|
292
|
+
if (gitIndex >= 0 && subCmd && !subCmd.startsWith("-")) {
|
|
293
|
+
const gitArgs = parts.slice(gitIndex + 2);
|
|
294
|
+
if (gitArgs.some((a) => a === "--no-verify" || a === "-n")) {
|
|
295
|
+
return true;
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
return false;
|
|
300
|
+
}
|
|
301
|
+
async function requestPathAccess(paths, workdir, sessionId, callId, tool, onEvent, dangerLevel, command, isSubAgent) {
|
|
302
|
+
if (isSubAgent) {
|
|
303
|
+
const result2 = await checkPathsAccess(paths, workdir, sessionId);
|
|
304
|
+
if (!result2.needsConfirmation) return;
|
|
305
|
+
if (dangerLevel === "dangerous") {
|
|
306
|
+
const allPaths2 = [.../* @__PURE__ */ new Set([...result2.deniedPaths, ...result2.sensitivePaths])];
|
|
307
|
+
addAllowedPaths(sessionId, allPaths2);
|
|
308
|
+
return;
|
|
309
|
+
}
|
|
310
|
+
const allPaths = [.../* @__PURE__ */ new Set([...result2.deniedPaths, ...result2.sensitivePaths])];
|
|
311
|
+
const hasDenied2 = result2.deniedPaths.length > 0;
|
|
312
|
+
const hasSensitive2 = result2.sensitivePaths.length > 0;
|
|
313
|
+
const reason2 = hasDenied2 && hasSensitive2 ? "both" : hasDenied2 ? "outside_workdir" : "sensitive_file";
|
|
314
|
+
throw new PathAccessDeniedError(allPaths, tool, reason2);
|
|
315
|
+
}
|
|
316
|
+
const emitPendingEvent = (confirmationPaths, confirmationReason) => {
|
|
317
|
+
try {
|
|
318
|
+
const eventStore = getEventStore();
|
|
319
|
+
eventStore.append(sessionId, {
|
|
320
|
+
type: "path.confirmation_pending",
|
|
321
|
+
data: { callId, tool, paths: confirmationPaths, workdir, reason: confirmationReason }
|
|
322
|
+
});
|
|
323
|
+
} catch {
|
|
324
|
+
}
|
|
325
|
+
};
|
|
326
|
+
if (command && extractGitNoVerify(command)) {
|
|
327
|
+
emitPendingEvent([workdir], "git_no_verify");
|
|
328
|
+
const confirmationPromise2 = registerPathConfirmation(callId, [workdir], sessionId, tool, workdir, "git_no_verify");
|
|
329
|
+
onEvent(createChatPathConfirmationMessage(callId, tool, ["git --no-verify detected"], workdir, "git_no_verify"));
|
|
330
|
+
const approved2 = await confirmationPromise2;
|
|
331
|
+
if (!approved2) {
|
|
332
|
+
throw new PathAccessDeniedError(
|
|
333
|
+
["git --no-verify"],
|
|
334
|
+
tool,
|
|
335
|
+
"git_no_verify",
|
|
336
|
+
"User denied git command with --no-verify. The agent must not use --no-verify and must resolve the issue (e.g., fix lint errors, resolve conflicts) that prevents the commit."
|
|
337
|
+
);
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
if (dangerLevel !== "dangerous" && command) {
|
|
341
|
+
const dangerousPatterns = extractDangerousPatterns(command);
|
|
342
|
+
if (dangerousPatterns.length > 0) {
|
|
343
|
+
emitPendingEvent([workdir], "dangerous_command");
|
|
344
|
+
const confirmationPromise2 = registerPathConfirmation(
|
|
345
|
+
callId,
|
|
346
|
+
[workdir],
|
|
347
|
+
sessionId,
|
|
348
|
+
tool,
|
|
349
|
+
workdir,
|
|
350
|
+
"dangerous_command"
|
|
351
|
+
);
|
|
352
|
+
onEvent(
|
|
353
|
+
createChatPathConfirmationMessage(callId, tool, [dangerousPatterns.join(", ")], workdir, "dangerous_command")
|
|
354
|
+
);
|
|
355
|
+
const approved2 = await confirmationPromise2;
|
|
356
|
+
if (!approved2) {
|
|
357
|
+
throw new PathAccessDeniedError(dangerousPatterns, tool, "dangerous_command");
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
const result = await checkPathsAccess(paths, workdir, sessionId);
|
|
362
|
+
if (!result.needsConfirmation) {
|
|
363
|
+
return;
|
|
364
|
+
}
|
|
365
|
+
if (dangerLevel === "dangerous") {
|
|
366
|
+
const allPaths = [.../* @__PURE__ */ new Set([...result.deniedPaths, ...result.sensitivePaths])];
|
|
367
|
+
addAllowedPaths(sessionId, allPaths);
|
|
368
|
+
return;
|
|
369
|
+
}
|
|
370
|
+
const allPathsNeedingConfirmation = [.../* @__PURE__ */ new Set([...result.deniedPaths, ...result.sensitivePaths])];
|
|
371
|
+
const hasDenied = result.deniedPaths.length > 0;
|
|
372
|
+
const hasSensitive = result.sensitivePaths.length > 0;
|
|
373
|
+
const reason = hasDenied && hasSensitive ? "both" : hasDenied ? "outside_workdir" : "sensitive_file";
|
|
374
|
+
emitPendingEvent(allPathsNeedingConfirmation, reason);
|
|
375
|
+
const confirmationPromise = registerPathConfirmation(
|
|
376
|
+
callId,
|
|
377
|
+
allPathsNeedingConfirmation,
|
|
378
|
+
sessionId,
|
|
379
|
+
tool,
|
|
380
|
+
workdir,
|
|
381
|
+
reason
|
|
382
|
+
);
|
|
383
|
+
onEvent(createChatPathConfirmationMessage(callId, tool, allPathsNeedingConfirmation, workdir, reason));
|
|
384
|
+
const approved = await confirmationPromise;
|
|
385
|
+
if (!approved) {
|
|
386
|
+
throw new PathAccessDeniedError(allPathsNeedingConfirmation, tool, reason);
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
var PathAccessDeniedError = class extends Error {
|
|
390
|
+
constructor(paths, tool, reason = "outside_workdir", customMessage) {
|
|
391
|
+
const reasonText = reason === "sensitive_file" ? "sensitive files (may contain secrets)" : reason === "both" ? "paths outside workdir and sensitive files" : reason === "git_no_verify" ? "git commands with --no-verify" : "paths outside workdir";
|
|
392
|
+
super(customMessage ?? `User denied access to ${reasonText}: ${paths.join(", ")}`);
|
|
393
|
+
this.paths = paths;
|
|
394
|
+
this.tool = tool;
|
|
395
|
+
this.reason = reason;
|
|
396
|
+
this.customMessage = customMessage;
|
|
397
|
+
this.name = "PathAccessDeniedError";
|
|
398
|
+
}
|
|
399
|
+
};
|
|
400
|
+
var pendingConfirmations = /* @__PURE__ */ new Map();
|
|
401
|
+
function registerPathConfirmation(callId, paths, sessionId, tool, workdir, reason) {
|
|
402
|
+
return new Promise((resolve2, reject) => {
|
|
403
|
+
pendingConfirmations.set(callId, { resolve: resolve2, reject, paths, sessionId, tool, workdir, reason });
|
|
404
|
+
});
|
|
405
|
+
}
|
|
406
|
+
function providePathConfirmation(callId, approved, alwaysAllow) {
|
|
407
|
+
const pending = pendingConfirmations.get(callId);
|
|
408
|
+
if (!pending) {
|
|
409
|
+
return { found: false };
|
|
410
|
+
}
|
|
411
|
+
try {
|
|
412
|
+
const eventStore = getEventStore();
|
|
413
|
+
eventStore.append(pending.sessionId, {
|
|
414
|
+
type: "path.confirmation_responded",
|
|
415
|
+
data: { callId, approved, alwaysAllow: alwaysAllow ?? false }
|
|
416
|
+
});
|
|
417
|
+
} catch {
|
|
418
|
+
}
|
|
419
|
+
if (approved) {
|
|
420
|
+
if (alwaysAllow) {
|
|
421
|
+
addAllowedPaths(pending.sessionId, pending.paths);
|
|
422
|
+
} else {
|
|
423
|
+
addAllowedPaths(pending.sessionId, pending.paths);
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
pending.resolve(approved);
|
|
427
|
+
pendingConfirmations.delete(callId);
|
|
428
|
+
return { found: true, sessionId: pending.sessionId, approved };
|
|
429
|
+
}
|
|
430
|
+
function cancelPathConfirmation(callId, reason) {
|
|
431
|
+
const pending = pendingConfirmations.get(callId);
|
|
432
|
+
if (!pending) {
|
|
433
|
+
return false;
|
|
434
|
+
}
|
|
435
|
+
pending.reject(new Error(reason));
|
|
436
|
+
pendingConfirmations.delete(callId);
|
|
437
|
+
return true;
|
|
438
|
+
}
|
|
439
|
+
function cancelPathConfirmationsForSession(sessionId, reason) {
|
|
440
|
+
let cancelledCount = 0;
|
|
441
|
+
for (const [callId, pending] of pendingConfirmations.entries()) {
|
|
442
|
+
if (pending.sessionId !== sessionId) {
|
|
443
|
+
continue;
|
|
444
|
+
}
|
|
445
|
+
pending.reject(new Error(reason));
|
|
446
|
+
pendingConfirmations.delete(callId);
|
|
447
|
+
cancelledCount += 1;
|
|
448
|
+
}
|
|
449
|
+
return cancelledCount;
|
|
450
|
+
}
|
|
451
|
+
function hasPendingPathConfirmation(callId) {
|
|
452
|
+
return pendingConfirmations.has(callId);
|
|
453
|
+
}
|
|
454
|
+
function getPendingConfirmationsBySession() {
|
|
455
|
+
const bySession = {};
|
|
456
|
+
for (const [_callId, pending] of pendingConfirmations.entries()) {
|
|
457
|
+
const entry = {
|
|
458
|
+
callId: _callId,
|
|
459
|
+
tool: pending.tool,
|
|
460
|
+
paths: pending.paths,
|
|
461
|
+
workdir: pending.workdir,
|
|
462
|
+
reason: pending.reason
|
|
463
|
+
};
|
|
464
|
+
const existing = bySession[pending.sessionId];
|
|
465
|
+
if (existing) {
|
|
466
|
+
existing.push(entry);
|
|
467
|
+
} else {
|
|
468
|
+
bySession[pending.sessionId] = [entry];
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
return bySession;
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
export {
|
|
475
|
+
isSensitivePath,
|
|
476
|
+
addAllowedPath,
|
|
477
|
+
addAllowedPaths,
|
|
478
|
+
isPathAllowed,
|
|
479
|
+
clearAllowedPaths,
|
|
480
|
+
isPathWithinSandbox,
|
|
481
|
+
extractAbsolutePathsFromCommand,
|
|
482
|
+
extractSensitivePathsFromCommand,
|
|
483
|
+
checkPathsAccess,
|
|
484
|
+
extractDangerousPatterns,
|
|
485
|
+
extractGitNoVerify,
|
|
486
|
+
requestPathAccess,
|
|
487
|
+
PathAccessDeniedError,
|
|
488
|
+
registerPathConfirmation,
|
|
489
|
+
providePathConfirmation,
|
|
490
|
+
cancelPathConfirmation,
|
|
491
|
+
cancelPathConfirmationsForSession,
|
|
492
|
+
hasPendingPathConfirmation,
|
|
493
|
+
getPendingConfirmationsBySession
|
|
494
|
+
};
|
|
495
|
+
//# sourceMappingURL=chunk-KEVRZWL3.js.map
|