diff --git a/docs/api/agentd.yaml b/docs/api/agentd.yaml index 60e5a11..b0c08a2 100644 --- a/docs/api/agentd.yaml +++ b/docs/api/agentd.yaml @@ -1177,6 +1177,7 @@ components: type: boolean approvalGranted: type: boolean + description: Ignored in production until an authoritative approval store is configured. AuthorizationResponse: type: object diff --git a/services/agentd/src/index.ts b/services/agentd/src/index.ts index 31e1937..910bf12 100644 --- a/services/agentd/src/index.ts +++ b/services/agentd/src/index.ts @@ -396,7 +396,7 @@ app.post('/v1/authorize', async (c) => { capabilityHighRisk: body.capabilityHighRisk ?? impact.allRevokedCapabilities.includes(body.capabilityId), requiresRuntimeAttestation: body.requiresRuntimeAttestation ?? false, requiresApproval: body.requiresApproval ?? false, - approvalGranted: body.approvalGranted ?? false, + approvalGranted: requestApprovalGranted(body), }) const decision = await evaluateGuard({ @@ -744,6 +744,13 @@ function authoritySignatureVerificationRequired(): boolean { return parseBooleanEnv(configured) } +function requestApprovalGranted(body: { approvalGranted?: unknown }): boolean { + if (process.env.NODE_ENV === 'production') { + return false + } + return body.approvalGranted === true +} + function parseBooleanEnv(value: string | undefined): boolean { return value === '1' || value?.toLowerCase() === 'true' } diff --git a/services/agentd/test/routes.test.ts b/services/agentd/test/routes.test.ts index c7d4d26..8707ff4 100644 --- a/services/agentd/test/routes.test.ts +++ b/services/agentd/test/routes.test.ts @@ -678,6 +678,30 @@ describe('Agentd Service Routes', () => { expect(evidence.events[0].action).toBe('authorization.allow') }) + it('ignores caller-supplied approval grants in production authorization', async () => { + process.env.NODE_ENV = 'production' + process.env.SERVICE_API_KEY = 'agentd-key' + const did = `did:fides:production-approval-${Date.now()}` + + const res = await app.request('/v1/authorize', { + method: 'POST', + headers: { 'Content-Type': 'application/json', 'X-API-Key': 'agentd-key' }, + body: JSON.stringify({ + agentDid: did, + capabilityId: 'payments.execute', + requiresApproval: true, + approvalGranted: true, + }), + }) + + expect(res.status).toBe(200) + const data = await res.json() + expect(data.decision).toBe('approve-required') + expect(data.factors).toEqual(expect.arrayContaining([ + expect.objectContaining({ source: 'approval', factor: 'approval-required' }), + ])) + }) + it('denies authorization after session revocation', async () => { const did = `did:fides:session-revoked-${Date.now()}` const sessionRes = await app.request('/v1/sessions', {