Summary
PR #946 (which closed #944) hardened the createPlan storage-error handling by wrapping call sites #1 (CreatePurchasePlan) and #3 (SetPlanAccounts) with mapCreatePlanStorageError (ErrNotFound -> 404, otherwise a structured log + generic 500 with no raw-error leak).
It left call site #2 unwrapped: the inner GetCloudAccount DB-error path of validatePlanAccountProviders in internal/api/handler_accounts.go (~line 1167-1168) still returns:
return fmt.Errorf("accounts: failed to get account %s: %w", aid, getErr)
On a DB error there, the non-ClientError propagates up to the router's logging.Errorf("API error: %v", err), which logs the raw account UUID and the raw DB error string. This is a PII / info-leak into logs.
Issue #944(b) explicitly required structured logs carrying the account count, not IDs, and "never raw account IDs". The user-facing body is already a generic 500 (the leak is log-side only), but the log line is the gap.
Scope
validatePlanAccountProviders is shared by both createPlan (POST /plans) and setPlanAccounts (PUT /plans/:id/accounts), so the fix applies to both.
Fix
Wrap the GetCloudAccount DB-error case so it (a) emits a structured logging.Errorf with provider/service/account-COUNT (never the UUID), and (b) returns a generic 500 ClientError ("failed to validate plan accounts") carrying no raw UUID or DB string. Reuse the existing mapCreatePlanStorageError helper. Keep ErrNotFound -> 404 (account-not-found must still 404).
Regression test
Assert that a DB error in the provider-validation path yields a 500 whose error message and the emitted log contain neither the account UUID nor the raw DB error string, and that account-not-found still returns 404.
Refs #944, #946.
Summary
PR #946 (which closed #944) hardened the
createPlanstorage-error handling by wrapping call sites #1 (CreatePurchasePlan) and #3 (SetPlanAccounts) withmapCreatePlanStorageError(ErrNotFound -> 404, otherwise a structured log + generic 500 with no raw-error leak).It left call site #2 unwrapped: the inner
GetCloudAccountDB-error path ofvalidatePlanAccountProvidersininternal/api/handler_accounts.go(~line 1167-1168) still returns:On a DB error there, the non-
ClientErrorpropagates up to the router'slogging.Errorf("API error: %v", err), which logs the raw account UUID and the raw DB error string. This is a PII / info-leak into logs.Issue #944(b) explicitly required structured logs carrying the account count, not IDs, and "never raw account IDs". The user-facing body is already a generic 500 (the leak is log-side only), but the log line is the gap.
Scope
validatePlanAccountProvidersis shared by bothcreatePlan(POST /plans) andsetPlanAccounts(PUT /plans/:id/accounts), so the fix applies to both.Fix
Wrap the
GetCloudAccountDB-error case so it (a) emits a structuredlogging.Errorfwith provider/service/account-COUNT (never the UUID), and (b) returns a generic 500ClientError("failed to validate plan accounts") carrying no raw UUID or DB string. Reuse the existingmapCreatePlanStorageErrorhelper. KeepErrNotFound-> 404 (account-not-found must still 404).Regression test
Assert that a DB error in the provider-validation path yields a 500 whose error message and the emitted log contain neither the account UUID nor the raw DB error string, and that account-not-found still returns 404.
Refs #944, #946.