Skip to content

sec(api/plans): validatePlanAccountProviders leaks raw account UUID + DB error into logs (gap left by #946) #965

Description

@cristim

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.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    impact/internalTeam-internal onlypr-createdA PR has been opened for this issue (dedup guard for the auto-PR loop)pr-mergedThe PR for this issue has been mergedpriority/p2Backlog-worthyseverity/mediumModerate harmtriagedItem has been triagedtype/securitySecurity findingurgency/this-sprintWithin the current sprint

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions