Skip to content

fix(coderd): store chat ACL keys as canonical UUIDs - #31037

Draft
ibetitsmike wants to merge 3 commits into
mainfrom
mike/chat-acl-canonical-keys
Draft

ibetitsmike wants to merge 3 commits into
mainfrom
mike/chat-acl-canonical-keys

Conversation

@ibetitsmike

@ibetitsmike ibetitsmike commented Oct 19, 2026 •

Copy link
Copy Markdown
Collaborator

PATCH /api/v2/chats/{chat}/acl stored user and group IDs as the request spelled them. uuid.Parse accepts uppercase spellings, so such a request passed validation and returned 204, but RBAC matches ACL keys as canonical UUID strings: the share granted nothing, and a deleted role sent with another spelling left the stored entry in place. This PR rekeys both role maps with canonicalACLRoles, as the MCP server config and organization skill ACLs do, and returns 400 when two spellings name one principal.

Releases v2.34.0 through v2.38.0 store keys as sent, so before it applies a request, the handler also deletes stored keys that spell a requested principal's ID in a noncanonical form: a deleted role then removes every spelling, and a read role leaves one canonical entry. Principals that the request does not name keep their stored keys, since rekeying them would grant access that RBAC never enforced.

Why it is safe: a canonical ID maps to itself, so requests with canonical IDs behave as before. The chat project ACL already canonicalizes its keys.

Red tests: TestChatACLNoncanonicalIDs shares a chat with a user and a group by uppercase IDs, checks that the user and a group member can read it, then removes both by uppercase IDs and checks that they cannot. It then writes uppercase user and group keys straight to the database, as older servers stored them. A deleted request with either spelling must leave no stored key, and a read request must leave only the canonical keys, let both readers in, and notify the user. Two new TestChatACLValidation cases send one user ID or one group ID in two spellings and expect a 400. On main:

TestChatACLNoncanonicalIDs: GET /api/v2/chats/<id>: unexpected status code 404: Resource not found or you do not have access to this resource
TestChatACLValidation/DuplicateUserSpellings: An error is expected but got nil.
TestChatACLValidation/DuplicateGroupSpellings: An error is expected but got nil.

With the rekey but without the cleanup (79865b4afc9), each legacy case fails when run on its own:

deleted, canonical spelling: Should be empty, but was map[<UPPERCASE ID>:{[read]}]
deleted, uppercase spelling: Should be empty, but was map[<UPPERCASE ID>:{[read]}]
read: Not equal: expected: database.ChatACL{"<canonical ID>": {[read]}}, actual: database.ChatACL{"<UPPERCASE ID>": {[read]}, "<canonical ID>": {[read]}}

These tests also fail on seven go test -overlay mutants. Each skips the rekey, the duplicate check, or the cleanup for users or for groups, or runs the cleanup after the oldChat snapshot.

Review focus: the maps are rekeyed in place after acl.Validate, which still reports IDs that do not parse, so the role loops below read canonical keys. The cleanup runs before the oldChat snapshot, so the share notification treats a user whose legacy entry granted nothing as newly added. On main, that user is not notified again.

#31016 replaces those loops with applyACLReadRoles. The two merge without conflicts and the merged loops read the rekeyed maps; whichever merges second will merge main first.

Validation: go test ./coderd -run '^(TestChatACLValidation|TestChatACLNoncanonicalIDs)$' -count=1 fails on main and on 79865b4afc9 as above and passes here (9 tests with subtests). Also passing here: go test ./coderd -run '(?i)acl|mcp|skill' -count=1 (65 with subtests), go test ./coderd -run '^(TestChatProjectSharing|TestChatSharingDisabled|TestListChatsSharedScope|TestSharedReaderStreamChat|TestChatFileDownloadURL)$' -count=1 (22), and go test ./enterprise/coderd -run '(?i)acl|mcp|skill|TestEnterpriseAuditLogs' -count=1 (46). A local merge with #31016 passes go test ./coderd -run '^(TestChatACLValidation|TestChatACLNoncanonicalIDs|TestChatACLSharingLifecycle|TestChatProjectSharing)$' -count=1 (16 with subtests). go vet ./coderd/ is clean on both, and the pre-commit hook passed (make gen, formatting, golangci-lint, slim build).

Xum, an AI agent, opened this PR for @ibetitsmike and wrote this description.

patchChatACL stored user and group IDs as the request spelled them.
uuid.Parse accepts uppercase spellings, so the update passed validation,
but RBAC matches ACL keys as canonical UUID strings: the share granted
nothing, and a deleted role sent with another spelling left the stored
entry in place. Rekey both role maps with canonicalACLRoles, as the MCP
server config and organization skill ACLs do, and reject requests where
two spellings name one principal.
@ibetitsmike

Copy link
Copy Markdown
Collaborator Author

@codex review

Xum, an AI agent, requested this review for @ibetitsmike.

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 19, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-20T00:46:31.695204Z 34b3d4e Manual request
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 79865b4afc

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread coderd/exp_chats_acl.go
Releases v2.34.0 through v2.38.0 stored chat ACL keys as the request
spelled them. Requests are now rekeyed to canonical IDs, so no request
could remove an entry stored under an uppercase key. Before applying a
request, delete stored keys that spell a requested principal's ID in a
noncanonical form.
@ibetitsmike

Copy link
Copy Markdown
Collaborator Author

@codex review

Xum, an AI agent, requested this review for @ibetitsmike.

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. 🚀

Reviewed commit: b3cf61e517

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Seed uppercase user and group keys straight in the database. Check that
a deleted request with the canonical or the uppercase spelling leaves no
stored key, and that a read request leaves only the canonical keys,
lets the user and a group member read the chat, and notifies the user
again.
@ibetitsmike

Copy link
Copy Markdown
Collaborator Author

@codex review

Xum, an AI agent, requested this review for @ibetitsmike.

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Keep them coming!

Reviewed commit: 34b3d4ed9e

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

This branch has not been deployed

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant