Skip to content

Commit f802e14

Browse files
authored
Merge pull request #549 from TencentCloudBase/automation/attribution-issue-mns3pyqp-xkjdhl-querypermissions-bucket
fix: queryPermissions 工具查询不存在 Bucket 时未返回错误
2 parents 90c4edd + 3815ef9 commit f802e14

2 files changed

Lines changed: 87 additions & 0 deletions

File tree

mcp/src/tools/permissions.test.ts

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,13 @@ describe("permission tools", () => {
106106
RequestId: "req-create-user",
107107
});
108108
mockGetCloudBaseManager.mockResolvedValue({
109+
env: {
110+
getEnvInfo: vi.fn().mockResolvedValue({
111+
EnvInfo: {
112+
Storages: [{ Bucket: "bucket-1" }],
113+
},
114+
}),
115+
},
109116
permission: {
110117
describeResourcePermission: mockDescribeResourcePermission,
111118
describeRoleList: mockDescribeRoleList,
@@ -194,6 +201,56 @@ describe("permission tools", () => {
194201
]);
195202
});
196203

204+
it("queryPermissions(action=getResourcePermission) should fail when storage bucket does not exist", async () => {
205+
const result = await tools.queryPermissions.handler({
206+
action: "getResourcePermission",
207+
resourceType: "storage",
208+
resourceId: "missing-bucket",
209+
});
210+
const payload = JSON.parse(result.content[0].text);
211+
212+
expect(mockDescribeResourcePermission).not.toHaveBeenCalled();
213+
expect(payload).toMatchObject({
214+
success: false,
215+
message: "存储 Bucket missing-bucket 不存在",
216+
});
217+
});
218+
219+
it("queryPermissions(action=getResourcePermission) should allow existing storage bucket", async () => {
220+
mockDescribeResourcePermission.mockResolvedValueOnce({
221+
Data: {
222+
TotalCount: 1,
223+
PermissionList: [
224+
{
225+
ResourceType: "storage",
226+
Resource: "bucket-1",
227+
Permission: "ADMINWRITE",
228+
},
229+
],
230+
},
231+
RequestId: "req-storage-perm",
232+
});
233+
234+
const result = await tools.queryPermissions.handler({
235+
action: "getResourcePermission",
236+
resourceType: "storage",
237+
resourceId: "bucket-1",
238+
});
239+
const payload = JSON.parse(result.content[0].text);
240+
241+
expect(mockDescribeResourcePermission).toHaveBeenCalledWith({
242+
resourceType: "storage",
243+
resources: ["bucket-1"],
244+
});
245+
expect(payload).toMatchObject({
246+
success: true,
247+
data: {
248+
resourceType: "storage",
249+
resourceId: "bucket-1",
250+
},
251+
});
252+
});
253+
197254
it("queryPermissions(action=listResourcePermissions) should include resource-level hints for risky custom rules", async () => {
198255
mockDescribeResourcePermission.mockResolvedValueOnce({
199256
Data: {

mcp/src/tools/permissions.ts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -301,6 +301,30 @@ function buildPermissionHints(securityRule: string | undefined, resourceId: stri
301301
].filter(Boolean) as PermissionHint[];
302302
}
303303

304+
async function ensureStorageBucketsExist(cloudbase: any, resourceIds: string[]) {
305+
if (!resourceIds.length) {
306+
return;
307+
}
308+
309+
const envInfo = await cloudbase.env.getEnvInfo();
310+
const existingBuckets = new Set(
311+
(envInfo?.EnvInfo?.Storages ?? [])
312+
.map((item: { Bucket?: string }) => item?.Bucket)
313+
.filter((bucket: string | undefined): bucket is string => Boolean(bucket)),
314+
);
315+
const missingBuckets = resourceIds.filter((resourceId) => !existingBuckets.has(resourceId));
316+
317+
if (missingBuckets.length === 0) {
318+
return;
319+
}
320+
321+
if (missingBuckets.length === 1) {
322+
throw new Error(`存储 Bucket ${missingBuckets[0]} 不存在`);
323+
}
324+
325+
throw new Error(`以下存储 Bucket 不存在: ${missingBuckets.join(", ")}`);
326+
}
327+
304328
export function registerPermissionTools(server: ExtendedMcpServer) {
305329
const cloudBaseOptions = server.cloudBaseOptions;
306330
const getManager = () => getCloudBaseManager({ cloudBaseOptions });
@@ -374,6 +398,9 @@ export function registerPermissionTools(server: ExtendedMcpServer) {
374398
if (!resourceType || !resourceId) {
375399
throw new Error("action=getResourcePermission 时必须提供 resourceType 和 resourceId");
376400
}
401+
if (resourceType === "storage") {
402+
await ensureStorageBucketsExist(cloudbase, [resourceId]);
403+
}
377404
const result = await cloudbase.permission.describeResourcePermission({
378405
resourceType: mapResourceType(resourceType),
379406
resources: [resourceId],
@@ -401,6 +428,9 @@ export function registerPermissionTools(server: ExtendedMcpServer) {
401428
if (!resourceType) {
402429
throw new Error("action=listResourcePermissions 时必须提供 resourceType");
403430
}
431+
if (resourceType === "storage" && resourceIds?.length) {
432+
await ensureStorageBucketsExist(cloudbase, resourceIds);
433+
}
404434
const result = await cloudbase.permission.describeResourcePermission({
405435
resourceType: mapResourceType(resourceType),
406436
resources: resourceIds,

0 commit comments

Comments
 (0)