fix(reconcile): harden the PlatformUser kind against silent access removal#1775
Merged
Conversation
…nt silent access removal
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Contributor
|
Caution Review failedThe pull request is closed. ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (9)
📝 WalkthroughSummary by CodeRabbit
WalkthroughChangesPlatformUser reconciliation hardening
Estimated code review effort: 3 (Moderate) | ~25 minutes Possibly related PRs
Suggested reviewers: Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Coverage Report for CI Build 29758034465Coverage increased (+0.06%) to 46.201%Details
Uncovered ChangesNo uncovered changes found. Coverage RegressionsNo coverage regressions found. Coverage Stats
💛 - Coveralls |
rohilsurana
force-pushed
the
fix/reconcile-platform-user-hardening
branch
from
July 20, 2026 15:10
434dbf9 to
d4ed916
Compare
rohilsurana
force-pushed
the
fix/reconcile-platform-user-hardening
branch
from
July 20, 2026 15:22
d4ed916 to
b410a3e
Compare
…e but the bootstrap account
rohilsurana
force-pushed
the
fix/reconcile-platform-user-hardening
branch
from
July 20, 2026 16:04
b410a3e to
1b83493
Compare
rohilsurana
marked this pull request as ready for review
July 20, 2026 17:19
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Hardening fixes for the
PlatformUserreconcile kind, found by an independent review. Eachcommit is one finding, most-severe first. The headline fix (M1) is security-relevant: it
closes a path where a hand-authored file could silently remove a superuser's access. One fix
(m2) also touches the server: it moves the bootstrap-account identity check into a shared
helper so the server's removal guard matches the id in any uuid form, not just the exact
string.
The fixes
M1 (major): validate and canonicalize refs to prevent silent access removal. The diff
matched a file entry to a live principal with a raw string compare, but the server resolves
refs loosely (an uppercase or
urn:uuid:id, a name slug, or a display-name email). So anentry meant to keep
alice-id, written asALICE-IDor a slug, failed to match, and becauseadds run before removes the run quietly stripped the access it meant to keep and never
converged. Now a user ref must be a plain email or a uuid and a service user ref must be a
uuid (a slug or
Alice <a@x.com>is rejected at plan time), and a uuid ref is canonicalizedso a non-canonical id matches its stored principal.
m1: reject unknown fields in PlatformUser specs. This kind decoded with plain
yaml.Unmarshal, so a mistyped field was silently ignored. It now uses the framework's strictdecodeSpec, matching every other kind.m2: guard the bootstrap service account by uuid value, on the reconciler and the server.
The check now lives in one place,
schema.IsBootstrapServiceUser, which parses both ids andcompares the 128-bit uuid values, so any spelling (uppercase,
urn:uuid:, braces, no dashes)matches. Two call sites use it: the reconciler rejects a
serviceuserentry that names thebootstrap id, and the server's
RemovePlatformUserrefuses to remove it. The server guard usedan exact string compare before, so a non-canonical spelling could slip past it and reach
UnSudoon the protected account even though the uuid column treats the ids as equal. Theguard is scoped to service-user ids on purpose: the bootstrap id is a service-user id, users
are a separate id space, so a user that happens to share that uuid is a different principal and
must not be rejected.
m5 (docs): warn about the empty-file lock-out. Documents that
spec: []removes everyplatform user except the bootstrap account, so
app.admin.bootstrapmust stay configured asthe recovery path.
Not in this PR (need server-side changes)
Appliedwhen the server no-ops an add (for example a uuiduser ref that does not exist). The admin API does not tell the client whether a call changed
anything, so this needs a server signal to fix cleanly.
loudly. The user path swallows
ErrNotExistserver-side; aligning them is a server change.--header. It was removedbecause passing the token via the
-Hflag is interim; the auth mechanism will change, sovalidating that flag is not worth carrying.
"defense in depth". That was the same conflation: the bootstrap id is a service-user id, so a
user that happens to share that uuid is a different principal and should still be reconciled,
not hidden.
Testing
rejected; service user ref must be a uuid; unknown field fails the plan; the bootstrap
account rejected in a non-canonical uuid form; a user that shares the bootstrap uuid is not
rejected; the server refuses to remove the bootstrap account in a non-canonical form; and a
table test for
schema.IsBootstrapServiceUseracross uuid forms.go build,go test ./cmd/... ./internal/reconcile/...,go vet,gofmt, andgolangci-lintall pass.