Skip to content

GoogleGcpCredentials: Enforce dictionary type #1207

Description

@vprashrex

Is your feature request related to a problem?
The current typing of GoogleGcpCredentials.sa_key as JsonValue allows for string inputs, leading to runtime failures when the service account key is treated as a string instead of a dictionary. This results in high diagnostic costs and failed batch assessments.

Describe the solution you'd like
Narrow the annotation of sa_key to enforce a dictionary type:

  • Change sa_key: JsonValue to sa_key: dict[str, JsonValue]
  • This will ensure validation fails at credential-creation time with a clear error, eliminating the need for casting in the code.
  • Note that existing string inputs will need to be re-saved as JSON objects to avoid issues.
Original issue

Problem

GoogleGcpCredentials.sa_key is typed JsonValue:

# backend/app/core/providers.py:76
sa_key: JsonValue = Field(description="Service account key JSON")

Pydantic's JsonValue is the union of all JSON value types — str included. A service account key submitted as an escaped JSON string therefore passes validation and is persisted as-is.

The failure surfaces much later, at provider runtime:

# backend/app/core/batch/google_gcp.py:91
creds = build_gcp_sa_credentials(cast(dict[str, Any], creds_model.sa_key))
# backend/app/core/cloud/storage.py:516
def build_gcp_sa_credentials(sa_key: dict[str, Any]) -> service_account.Credentials:
    return service_account.Credentials.from_service_account_info(sa_key, scopes=list(GCS_SCOPES))

from_service_account_info calls .keys() on the argument, so a stored string raises:

'str' object has no attribute 'keys'

Impact

A batch assessment run fails with status: FAILED and error: "'str' object has no attribute 'keys'", after all items have already been dispatched. Observed with 9/9 items returning assessment: null. The message gives no hint that the stored credential is malformed, so the cost of diagnosis is high.

The cast(dict[str, Any], ...) at the call site also hides this from pyright — the cast asserts a shape the type system never guaranteed.

Proposed fix

Narrow the annotation so the type system enforces what the consumer requires:

sa_key: dict[str, JsonValue] = Field(description="Service account key JSON")

Validation then fails at credential-creation time with a clear Pydantic error instead of at run time. The cast in google_gcp.py becomes unnecessary and should be dropped.

Notes

  • Existing rows holding a string sa_key will not be repaired by the type change; they need to be re-saved (or backfilled) with the key as a JSON object.
  • Masking in mask_credentials (providers.py:260) already assumes non-string secrets for sa_key, so the dict shape is the one the rest of the code expects.

Activity

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

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions