Repository navigation
fix(api): name protected source create, withdrawal and dropped-proxy refusals - #3735
Conversation
…refusals Finding 2536, slice 3. - Credential source create with a Credential Proxy: a type the Proxy cannot protect is a 409 listing the types it protects; a Secret reference to another Namespace is the Gateway path's 400 (both were 404). - Prepared and credential-free create: an Agent being deleted is 409 AGENT_DELETING, an embedded Agent a 409 (were 404). A missing Agent the caller may operate stays 404; the checks still run after the operate grant. - Withdrawal of a source the active revision does not use is a 409 (was 404); the check never reads the source. - Agent save and deploy that bind a Proxy-owned source after the Installation dropped the Proxy answered 500 (DriverSelectionError); now the sourcesWithoutProxy 409. - The failed device-login 503 keeps the Refresh Driver's error as its cause, so the API's 503 log record names its class and code. Tests: new protected-source-refusals (in-memory static egress fixture with a restarted controller for the Installation change), the device-login log record, the prepared issuer reference, and the conformance withdrawal case.
|
🦞👀 Pull request received. I will update this pull request when review starts. ClawSweeper review completeClawSweeper finished reviewing this revision. The review result is being finalized. |
|
Codex review: needs changes before merge. What this changesThis PR gives protected credential-source refusals actionable HTTP errors and preserves safe diagnostic information for failed device logins. Example: Create a codex-pat credential source while the Installation selects a Credential Proxy that cannot protect that type.
Review scores
ProductKind: Bug fix · Worth it: Yes · Fix scope: Complete Merge readiness⛔ Needs changes before merge - 1 item remains Keep this useful, collaborator-authored PR open; the production changes preserve authorization boundaries, with one required correction to the new integration test. Priority: P2 Before merge
Findings
Tests
Agent review detailsHow this fits togetherThe API passes authenticated resource operations to OCC, which authorizes exact resources, checks credential-source and Driver ownership, and returns errors through the HTTP mapper. flowchart LR
Client[API client] --> API[HTTP API]
API --> OCC[OCC admission]
OCC --> IAM[IAM authorization]
OCC --> State[Platform state]
OCC --> Drivers[Credential Drivers]
OCC --> Mapper[HTTP error mapper]
Mapper --> Client
Technical reviewBest possible solution: Retain the focused OCC error changes and convert the new integration test to TypeScript with its CI references updated. Do we have a high-confidence way to reproduce the issue? Current-main rejection sites and the HTTP mapper establish the generic responses and missing-owner failure without executing target code. Is this the best way to solve the issue? The patch uses existing OCC error classes and authorization owners, preserving rejection conditions while making their responses actionable. Full review comments:
Overall correctness: patch is correct AGENTS.md: found and applied where relevant. Codex review notes: model internal, reasoning medium; reviewed against 8193a16a896e. Provenance checked
TestingProof path: in-process harness. SecurityNone. EvidenceAcceptance criteria:
What I checked:
Review metrics
LabelsLabel changes:
Label justifications:
Rating scale6/6 🦀 challenger crab · 5/6 🦞 diamond lobster · 4/6 🐚 platinum hermit · 3/6 🦐 gold shrimp · 2/6 🦪 silver shellfish · 1/6 🧂 unranked krab. Overall follows the weaker of proof and patch quality; ✨ marks media proof (a screenshot, video, or linked artifact) that directly shows the changed behavior. WorkflowClawSweeper edits this one comment on every review. Comment Reviewed October 26, 2026, 3:49 AM ET / 07:49 UTC. |
…pared Agent refusals Review of #3735: with prepared GitHub metadata enabled the type refusal ran past the 256-character cap and lost "ask an Installation administrator". The who now comes before the type list; the longest bundled type and list fit (249 characters). The create test runs with prepared GitHub metadata enabled, asserts the full text, and covers deleting, embedded and missing Agents for github-app-metadata as well as credential-free metadata (shared inputs in tests/helpers/protected-source-refusals.mjs).
Problem
Finding 2536, slice 3 (after #3725 and #3732). Some refusals still answered a generic 404, or a 500, although each comes after the caller's grants and is about the request or an Installation property:
codex-pat) and a Secret reference to another Namespace both answered404 The requested platform resource was not found.config.agent_idanswered the same 404.drivers.credential_proxyanswered500 INTERNAL_ERROR(aDriverSelectionErrorfrom the owner lookup). The intendedsourcesWithoutProxy409 was reachable only for live-adopted Gateway sources.catch {}, so the log record named only the 503 itself.sourcesWithoutProxy,dedicatedSandbox,gitAloneor theagents.defaults.model400.Change
admitStaticEgressSource: a type the selected Proxy cannot protect is a409 RESOURCE_CONFLICTthat names the type and lists the types it protects; a Secret reference to another Namespace is the Credential Gateway path's400(Credential source Secrets cannot cross Namespaces.).admitPreparedTokenSource,admitCredentialFreeMetadata: after the Agent operate grant and lookup, a deleting Agent is409 AGENT_DELETINGand an embedded Agent a409that namesconfig.agent_id. A missing Agent stays the generic 404. The prepared issuer reference to another Namespace is the same400.withdrawAgentCredentialSource: a source the active revision does not use is a409. The check reads only the revision's bindings, never the source, so any source ID gets the same answer.authorizeAgentCredentialSources(Agent create, update, deploy): a Proxy-owned source with no Proxy selected isCredentialProxyAdmissionError("sourcesWithoutProxy"), 409, instead of a 500.RetryLaterError("deviceLoginFailed", cause): the caught Refresh Driver error is the cause. The 503 log record keeps only a cause's class and code (dependencyUnavailableLogFields), never its message.Not changed, recorded in the finding's site list:
dedicatedSandboxis unreachable:selectedProxyOwner()refuses a Proxy without its paired Sandbox first, and every composition installs both together. No test, since no supported Installation reaches it.gitAloneis reachable only with protected repository Git on a qualified sender (an external Driver with exact dispatch and no policy reduction, or the OpenShell observed-hook preview) plus a protected source. No in-memory fixture deploys either with a source today, so it stays untested here; it belongs with the PostgreSQL external Git journey.Verification
tests/integration/protected-source-refusals.test.mjs(checks-baseline-1): real app, IAM, controller and OpenShell Proxy and Gateway Drivers on the static egress fixture. It runs with prepared GitHub metadata enabled (the longest list of protected types) and asserts status, code and the full text (no cap ellipsis) for each create refusal, deleting, embedded and missing Agents for bothcredential-free-repository-metadataandgithub-app-metadata, theagents.defaults.model400 andsourcesWithoutProxyfor both a live-adopted Gateway source and a Proxy-owned source (a second controller opens the same state after the Installation drops the Proxy, as a new API process would; its answer goes through the real HTTP error mapper), and the withdrawal 409 for an existing and a missing source ID.RetryLaterErrorwith cause classTypeErrorand does not contain the private failure text. The prepared metadata test covers the issuer reference. The conformance withdrawal case asserts the mapped 409 for two IDs.admitProxyPlancheck made to return; the model 400 reverted; the Agent check moved before the operate grant (identical-answer test: 409 against 403); the prepared issuer retype reverted; the Agent check removed from the prepared path; the type refusal text put back with the administrator after the type list.tsc --build, eslint and Prettier on the touched files; the docs word count (breaking-changes 2477, earlier page 2494).Risks