fix(agent): request approval for protected path access
This commit is contained in:
+1
-1
@@ -74,7 +74,7 @@ const payloadSchema = z.object({
|
||||
.optional()
|
||||
.default("request")
|
||||
.describe(
|
||||
"request forwards approval prompts; auto approves only low-risk allowlisted tools; always approves every prompt not explicitly denied by OpenCode",
|
||||
"request forwards approval prompts; auto approves only low-risk allowlisted tools and forwards other requests; always approves routine prompts but forwards sensitive paths and force removal for user approval",
|
||||
),
|
||||
});
|
||||
|
||||
|
||||
@@ -47,7 +47,7 @@ export const canAutoApprovePermission = (
|
||||
}
|
||||
|
||||
if (normalized === "bash") {
|
||||
return isConversationWorkspace(context.workspaceRoot);
|
||||
return isConversationWorkspace(context.workspaceRoot) && !isSensitiveBashCommand(context);
|
||||
}
|
||||
|
||||
if (normalized === "read" || normalized === "edit" || normalized === "write") {
|
||||
@@ -70,12 +70,20 @@ export const resolvePermissionApproval = (
|
||||
permission: string,
|
||||
context: PermissionApprovalContext = {},
|
||||
) => {
|
||||
if (isDirectRecursiveForceRemove(permission, context)) {
|
||||
const normalized = normalizePermission(permission);
|
||||
if (
|
||||
isSensitiveBashCommand(context, normalized) ||
|
||||
(approvalMode === "always" &&
|
||||
(normalized === "external_directory" ||
|
||||
normalized === "read" ||
|
||||
((normalized === "edit" || normalized === "write") &&
|
||||
!isSafeConversationFileAccess(context))))
|
||||
) {
|
||||
return {
|
||||
autoApprove: false,
|
||||
autoReject: true,
|
||||
title: "已拒绝递归强制删除",
|
||||
detail: "当前安全策略禁止直接执行带 recursive 和 force 参数的 rm 命令。",
|
||||
autoReject: false,
|
||||
title: "等待权限确认",
|
||||
detail: undefined,
|
||||
} as const;
|
||||
}
|
||||
|
||||
@@ -85,7 +93,7 @@ export const resolvePermissionApproval = (
|
||||
autoReject: false,
|
||||
title: "已按始终允许模式放行",
|
||||
detail:
|
||||
"当前会话处于始终允许模式,已放行本次权限请求;明确禁止的权限仍由 OpenCode 拒绝。",
|
||||
"当前会话处于始终允许模式,已放行本次常规权限请求。",
|
||||
} as const;
|
||||
}
|
||||
|
||||
@@ -106,6 +114,21 @@ export const resolvePermissionApproval = (
|
||||
} as const;
|
||||
};
|
||||
|
||||
const isSensitiveBashCommand = (
|
||||
context: PermissionApprovalContext,
|
||||
normalizedPermission = "bash",
|
||||
): boolean => {
|
||||
if (normalizedPermission !== "bash") return false;
|
||||
const command =
|
||||
typeof context.metadata?.command === "string"
|
||||
? context.metadata.command
|
||||
: context.patterns?.join("\n");
|
||||
return Boolean(
|
||||
(command && containsProtectedPath(command, true)) ||
|
||||
isDirectRecursiveForceRemove("bash", context),
|
||||
);
|
||||
};
|
||||
|
||||
const isDirectRecursiveForceRemove = (
|
||||
permission: string,
|
||||
context: PermissionApprovalContext,
|
||||
@@ -412,9 +435,16 @@ const isSafeConversationFileAccess = (
|
||||
relativeTarget !== ".." &&
|
||||
!relativeTarget.startsWith(`..${sep}`) &&
|
||||
!isAbsolute(relativeTarget) &&
|
||||
!containsProtectedPath(relativeTarget, true)
|
||||
!containsSensitiveFileSegment(relativeTarget)
|
||||
);
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
const containsSensitiveFileSegment = (value: string): boolean =>
|
||||
value
|
||||
.replaceAll("\\", "/")
|
||||
.toLowerCase()
|
||||
.split("/")
|
||||
.some((segment) => segment.includes(".env") || segment === "data" || segment === "logs");
|
||||
|
||||
Reference in New Issue
Block a user