Skip to content

audit: normalise timestamps to UTC across auth + cluster (follow-up to PR #459) #460

Description

@xe-nvdk

Background

PR #459 (Phase A.2 Item 2) fixed a UTC drift in the RBAC FSM apply path: nextProposerTimestamp() on the proposer side and every time.Unix(0, entry.{Created,Updated}AtUnixNano) in internal/auth/cluster_rbac_apply.go now use .UTC() so SQLite text-encoded created_at values round-trip byte-identically under the WHERE created_at = ? readback in CreateRole / CreateMeasurementPermission.

That fix was narrowly scoped to RBAC. The same shape exists elsewhere in the codebase and should be audited.

Scope

Confirmed-needs-audit sites

The Phase A token applier path uses the same time.Unix(0, ...) shape without .UTC():

  • internal/auth/cluster_apply.go:132 — createdAt := time.Unix(0, entry.CreatedAtUnixNano)
  • internal/auth/cluster_apply.go:135 — expiresAt = time.Unix(0, entry.ExpiresAtUnixNano)
  • internal/auth/cluster_apply.go:204 — expiresAt = time.Unix(0, entry.ExpiresAtUnixNano)

There is no current WHERE created_at = ? or WHERE expires_at = ? equality readback paired with these inserts, so it doesn't round-trip-break today — but it's the same latent bug shape that bit PR #459, and any future readback added against these columns would inherit the timezone-mismatch risk.

Broader audit (lower priority)

Walk the codebase for proposer/applier-side timestamp pairs that pass through SQLite text serialisation:

  1. Grep for time.Unix(0, and time.Now() in internal/auth/, internal/cluster/raft/, internal/database/, internal/audit/, and anywhere else timestamps land in SQLite.
  2. For each site that inserts a time.Time into SQLite, confirm whether any read path uses WHERE <col> = ? equality. If yes, both sides must be UTC.
  3. For sites that only insert and never equality-match, normalise to UTC anyway for log/metric/diagnostic consistency.

Suggested approach

  • Add .UTC() to all time.Unix(0, ...) sites in internal/auth/cluster_apply.go (3 sites confirmed above) as a no-behaviour-change defensive normalisation.
  • Run a broader grep + reviewer-agent sweep to enumerate every timestamp insert/readback pair across the auth, audit, and cluster packages.
  • Add a unit test (or extend an existing one) that runs with TZ=America/Argentina/Buenos_Aires set, exercising every Create/Update/Delete RBAC + token path. A round-trip-UTC bug only manifests in non-UTC environments; CI normally runs UTC so we won't catch regressions otherwise.

Why this should be tracked separately

PR #459's scope was the cascade-on-delete soft cap. The UTC fix landed inside that PR because Gemini's High finding on nextProposerTimestamp exposed it. Doing the broader sweep in the same PR would expand the diff well past Item 2's scope and make review harder. Better as its own focused PR.

References

Acceptance criteria

  • All 3 confirmed time.Unix(0, ...) sites in internal/auth/cluster_apply.go normalised to .UTC().
  • Codebase grepped for other time.Unix(0, ...) + time.Now() sites that land in SQLite; each documented as "audited, UTC-safe" or "fixed".
  • CI runs at least one test under a non-UTC TZ to catch regressions.

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

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions