From faccbb58e70f932356f800022f05b5cde303895b Mon Sep 17 00:00:00 2001 From: Michal Date: Fri, 24 Apr 2026 00:46:54 +0100 Subject: [PATCH] fix(secrets): describe --show-values resolves through the backend driver Post-migration, every Secret on a non-plaintext backend has empty `Secret.data` (the actual value lives in the backend; only externalRef is on the row). `describe secret --show-values` was reading the raw row, so the user saw "Data: (empty)" for every migrated secret. - Route GET /api/v1/secrets/:id accepts ?reveal=true; when set, resolves the value via SecretService.resolveData() so the response carries the actual data dispatched through the right driver. - CLI --show-values flips that query param. Without --show-values the route returns the raw row exactly as before (no leak risk). Caught running the wizard end-to-end on the live cluster after the ClusterMesh fix on the kubernetes-deployment side made bao reachable. Co-Authored-By: Claude Opus 4.7 (1M context) --- src/cli/src/commands/describe.ts | 10 +++++++++- src/mcpd/src/routes/secrets.ts | 22 +++++++++++++++++++--- 2 files changed, 28 insertions(+), 4 deletions(-) diff --git a/src/cli/src/commands/describe.ts b/src/cli/src/commands/describe.ts index bb48203..ed7f0d4 100644 --- a/src/cli/src/commands/describe.ts +++ b/src/cli/src/commands/describe.ts @@ -928,7 +928,15 @@ export function createDescribeCommand(deps: DescribeCommandDeps): Command { } } - const item = await deps.fetchResource(resource, id) as Record; + let item: Record; + if (resource === 'secrets' && opts.showValues === true) { + // --show-values needs the resolved data (the raw row's `data` is + // empty for non-plaintext backends — values live in the backend). + // Use ?reveal=true so mcpd dispatches through the backing driver. + item = await deps.client.get>(`/api/v1/secrets/${id}?reveal=true`); + } else { + item = await deps.fetchResource(resource, id) as Record; + } // Enrich instances with container inspect data let inspect: Record | undefined; diff --git a/src/mcpd/src/routes/secrets.ts b/src/mcpd/src/routes/secrets.ts index a1fa9a7..115d25e 100644 --- a/src/mcpd/src/routes/secrets.ts +++ b/src/mcpd/src/routes/secrets.ts @@ -9,9 +9,25 @@ export function registerSecretRoutes( return service.list(); }); - app.get<{ Params: { id: string } }>('/api/v1/secrets/:id', async (request) => { - return service.getById(request.params.id); - }); + app.get<{ Params: { id: string }; Querystring: { reveal?: string } }>( + '/api/v1/secrets/:id', + async (request) => { + const secret = await service.getById(request.params.id); + // ?reveal=true → resolve `data` through the backing driver. Without + // reveal we return the raw row, which for non-plaintext backends has + // an empty `data` (the real values live in the backend; only the + // externalRef is on the row). The CLI's `describe secret` already + // masks values by default; --show-values flips this query param so the + // describe output matches what the user expects pre/post migration. + if (request.query.reveal === 'true') { + const resolved = await service.resolveData(secret).catch(() => null); + if (resolved !== null) { + return { ...secret, data: resolved }; + } + } + return secret; + }, + ); app.post('/api/v1/secrets', async (request, reply) => { const secret = await service.create(request.body);