Repository navigation
🤖 fix: check a CoderAIProvider's provider ID before Coder calls, and trust only Coder's own delete 404 - #325
Merged
Merged
Conversation
…trust only Coder's own delete 404
status.providerID goes into /api/v2/ai/providers/{id}, which also takes a
name, and the SDK resolves ".." segments. The sync, disable and delete
paths now fail an assertion before any Coder request unless the value is a
canonical UUID. The delete path also releases the finalizer on a 404 only
when the answer carries Coder's fixed not-found message, so an HTML 404 from
a proxy no longer leaks the provider.
Fixes #323
Change-Id: I7080fde6ce8329872f072efd01f3e0e992889a75
Signed-off-by: Thomas Kosiewski <tk@coder.com>
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
Member
Author
|
@codex security review |
🛡️ Codex Security ReviewSecurity review completed. No security issues were found in this pull request. Reviewed commit: Only the user who started this review can view the report in Codex. ℹ️ About Codex security reviews in GitHubThis is an experimental Codex feature. Security reviews are triggered when:
Once complete, Codex will leave suggestions, or a comment if no findings are found. |
This was referenced Oct 14, 2026
Closed
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.
Summary
Two hardening fixes for the dormant
CoderAIProvidercontroller, from the #312 final assessment. Both gate activation (#315). The controller is still not registered, so users see no change.Fixes #323
Refs #154
1.
status.providerIDmust be a canonical UUID before any Coder callThe controller puts
status.providerIDinto/api/v2/ai/providers/{id}. That route also accepts a provider name, and the SDK builds the URL withURL.Parse, which resolves..segments:/api/v2/ai/providers/../../users/xbecomes/api/v2/users/x(checked with the Go standard library). A forged value could therefore send a request with the operator's site-owner token to another Coder route. Only the controller's own role writes the status subresource, so this needs cluster-admin-level access today.checkAIProviderIDnow runs before every GET, PATCH or DELETE that uses the ID: at the start of the sync path (syncAIProvider), the credential disable path (disableWithoutConsent) and the delete path (deleteInCoder). Anything other than a canonical, non-nil, lowercase UUID fails withassertion failed: AI provider <ns>/<name> has status.providerID "<value>", which is not a canonical UUID, and no Coder request is sent. The finalizer stays. While the control plane is being deleted, the existing one-attempt rule still releases the finalizer and logs the error.2. A delete trusts only Coder's own 404
ReadBodyAsErrorturns any non-2xx answer into a*codersdk.Error, including an HTML 404 from a proxy or a wrong base URL, so the delete path could release the finalizer while the provider stays in Coder. The delete path now treats a 404 as "gone" only when the answer carries Coder's fixed not-found message (Resource not found or you do not have access to this resource,httpapi.ResourceNotFoundatcoderd/httpapi/httpapi.go:149-155in v2.37.3, which the provider routes answer throughwriteAIProviderErroratcoderd/ai_providers.go:636-638). An unknown route answersRoute not found.instead, so a wrong base URL that still reaches some Coder does not count either. That applies to the DELETE by ID and to the name lookup for an unconfirmed create. Any other 404 waits withCoderUnavailableand retries every 60 seconds.Tests
TestCoderAIProviderForgedProviderID: on the delete, sync and disable paths, five forged values (../../users/<uuid>, a name, braces, upper case, the nil UUID) each fail the assertion with zero Coder requests, and the finalizer stays.TestCoderAIProviderDeleteNotFoundFromProxy: a DELETE answered with an HTML 404, or a JSON 404 with another message, keeps the finalizer (CoderUnavailable, 60 s); the next reconcile with Coder's own answer deletes and releases.TestCoderAIProviderDeletePendingCreateNotFoundFromProxy: the same for the name lookup of an unconfirmed create.Mutation checks (each one breaks a named test, then I reverted it): no check on the delete path, no check on the sync path, no check on the disable path, a non-canonical form accepted, the nil UUID accepted, any 404 trusted on DELETE, any 404 trusted on the lookup.
Local gates on the pushed tree, exit 0 each:
make verify-vendor,make test,make test-integration,make build,make lint,go tool govulncheck ./...,go test -race ./internal/controller/, andmake codegen manifests build-installer docs-referencewith no diff.Generated with
mux• Model:anthropic:claude-opus-5-5• Thinking:high• Cost:$107.36