Skip to content

feat: allow device keys (agent_user) on risk.createPolicyBypassRequest - #5082

Open
bradcypert wants to merge 1 commit into
brad/dno-802-featserver-emit-structured-block-effect-on-shadow-mcp-denyfrom
brad/dno-803-featserver-allow-device-keys-agent_user-on
Open

feat: allow device keys (agent_user) on risk.createPolicyBypassRequest#5082
bradcypert wants to merge 1 commit into
brad/dno-802-featserver-emit-structured-block-effect-on-shadow-mcp-denyfrom
brad/dno-803-featserver-allow-device-keys-agent_user-on

Conversation

@bradcypert

@bradcypert bradcypert commented Aug 8, 2026

Copy link
Copy Markdown
Contributor

Part 2 of Request access for blocked AI tool calls (DNO-803). Stacked on #5081.

Why

The device agent files bypass requests on the user's behalf with the per-user agent_user key minted by token-exchange, so risk.createPolicyBypassRequest needs to accept key auth alongside the dashboard session.

What

  • Security(ByKey, agent_user) added to the Goa design; session auth unchanged; codegen + TS SDK regenerated.
  • No handler changes. Key auth resolves authCtx.UserID to the key owner — for an agent_user key that IS the enrolled user — so the existing org + requester binding applies unchanged.
  • Deliberately no vouched-email param (unlike agent.getPlugins): this feeds an admin-approval workflow, and a leaked shared org install key must not be able to file requests as arbitrary users. An org key redeeming an attributed token 403s via the same requester binding.

Test plan

  • New key-auth matrix in policy_bypass_test.go: agent_user key of the requester succeeds (and audits); another user's key 403s; the org install key with an attributed token 403s.
  • go test ./internal/risk/ green; dashboard tsc clean against the regenerated SDK.

🤖 Generated with Claude Code

https://claude.ai/code/session_01LqNdUoJxcz85saAAyPodoW


Summary by cubic

Allow per-user device keys to create policy bypass requests via risk.createPolicyBypassRequest. Adds agent_user API key auth with the Gram-Key header alongside session so the device agent can submit for the enrolled user (DNO-803).

  • New Features
    • Added Security(ByKey) with agent_user scope; session path unchanged.
    • Accepts Gram-Key header; OpenAPI and server codegen regenerated; dashboard SDK supports gramKey; CLI adds --apikey-token.
    • No handler changes; requester binding enforces owner-only with no vouched-email param. Other users’ keys or org install keys 403. Tests cover owner success, forbidden cases, and audit attribution.

Written for commit e49fbc6. Summary will update on new commits.

Review in cubic

@bradcypert
bradcypert requested review from a team as code owners August 8, 2026 01:10
@linear-code

linear-code Bot commented Aug 8, 2026

Copy link
Copy Markdown
Contributor

DNO-803

@changeset-bot

changeset-bot Bot commented Aug 8, 2026

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: e49fbc6

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

cubic analysis

1 issue found across 13 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="server/internal/risk/policy_bypass_test.go">

<violation number="1" location="server/internal/risk/policy_bypass_test.go:918">
P3: The new tests verify the owner/requester binding but never the new ByKey `agent_user` scope gate. Because the handler performs no APIKeyScopes check and these tests call it directly, removing the `agent_user` scope requirement from the security design would leave all three tests green — the feature's actual authorization boundary has no coverage. Consider an integration test that drives the ByKey middleware (scope-missing key → 403), or assert `authCtx.APIKeyScopes` inside the handler so the scope gate is testable here.</violation>
</file>

Linked issue analysis

Linked issue: DNO-803: feat(server): allow device keys (agent_user) on risk.createPolicyBypassRequest

Status Acceptance criteria Notes
Accept Gram-Key (API key) header with agent_user scope alongside session for risk.createPolicyBypassRequest Design and generated code add a ByKey scheme and header handling; OpenAPI/SDK/CLI were updated to accept Gram-Key in addition to Gram-Session.
Permit an agent_user key owned by the requester to create a policy bypass request attributed to that user Unit test exercises signing/redeeming with an agent_user key and asserts created request RequesterUserID equals key owner and request is created.
Reject a key owned by a different user (403) Unit test demonstrates that using another user's agent_user key to redeem the token returns forbidden.
Reject org install (shared) key with an attributed token (403) Unit test asserts that an org/shared key (agent scope / attributed token) cannot file requests for the named developer — returns forbidden.
Preserve existing session-based dashboard path (no regression) The design still includes Session security and the generated code continues to handle Gram-Session; the PR explicitly states and tests for session path regression.
⚠️ Audit the create action and record the key owner as the actor Tests verify an audit event was created (count increment), and the request is attributed to the key owner, but the diff does not show an explicit assertion that the audit record's actor field equals the key owner.

Reply with feedback, questions, or to request a fix.

Fix all with cubic | Re-trigger cubic

// authenticated device agent: no session, the given key scopes, and the key
// owner as the caller. Mirrors what internal/auth/key.go builds for a
// Gram-Key request.
func withAgentKeyAuth(t *testing.T, ctx context.Context, scopes []string, ownerUserID string) context.Context {

@cubic-dev-ai cubic-dev-ai Bot Aug 8, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P3: The new tests verify the owner/requester binding but never the new ByKey agent_user scope gate. Because the handler performs no APIKeyScopes check and these tests call it directly, removing the agent_user scope requirement from the security design would leave all three tests green — the feature's actual authorization boundary has no coverage. Consider an integration test that drives the ByKey middleware (scope-missing key → 403), or assert authCtx.APIKeyScopes inside the handler so the scope gate is testable here.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At server/internal/risk/policy_bypass_test.go, line 918:

<comment>The new tests verify the owner/requester binding but never the new ByKey `agent_user` scope gate. Because the handler performs no APIKeyScopes check and these tests call it directly, removing the `agent_user` scope requirement from the security design would leave all three tests green — the feature's actual authorization boundary has no coverage. Consider an integration test that drives the ByKey middleware (scope-missing key → 403), or assert `authCtx.APIKeyScopes` inside the handler so the scope gate is testable here.</comment>

<file context>
@@ -908,3 +910,112 @@ func TestDenyPolicyBypassRequest_AllowAllLeavesBlockedListUntouched(t *testing.T
+// authenticated device agent: no session, the given key scopes, and the key
+// owner as the caller. Mirrors what internal/auth/key.go builds for a
+// Gram-Key request.
+func withAgentKeyAuth(t *testing.T, ctx context.Context, scopes []string, ownerUserID string) context.Context {
+	t.Helper()
+	authCtx, ok := contextvalues.GetAuthContext(ctx)
</file context>
Fix with cubic

@bradcypert
bradcypert force-pushed the brad/dno-803-featserver-allow-device-keys-agent_user-on branch from d9536e3 to a009fc3 Compare August 8, 2026 02:05
…ssRequest (DNO-803)

The device agent files bypass requests on the user's behalf with the
per-user agent_user key minted by token-exchange. The key owner IS the
enrolled user, so the handler's existing org + requester binding applies
unchanged — no handler logic change.

Deliberately no vouched-email param (unlike agent.getPlugins): this
endpoint feeds an admin-approval workflow, and a leaked shared org
install key must not be able to file requests as arbitrary users. An
org key redeeming an attributed token 403s via the same requester
binding; the daemon only ever calls with the per-user key.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LqNdUoJxcz85saAAyPodoW
@bradcypert
bradcypert force-pushed the brad/dno-803-featserver-allow-device-keys-agent_user-on branch from a009fc3 to e49fbc6 Compare August 8, 2026 02:09
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