Skip to content

[Security] Permission middleware trusts client-supplied caller identity headers #422

@santoshkumarradha

Description

@santoshkumarradha

Summary

The tag-policy permission middleware falls back to reading caller identity from client-controlled HTTP headers when DID resolution fails, allowing any caller to spoof another agent's tags and bypass policy enforcement.

Context

In permission.go:151, when GetVerifiedCallerDID cannot produce a callerAgentID, the middleware falls back to reading X-Caller-Agent-ID and then X-Agent-Node-ID from request headers. These headers are set by the client, not the control plane. An attacker can set these headers to impersonate a high-privilege agent, inherit its tags, and pass tag-based policy checks for resources they should not access. The fallback was likely added for backward compatibility but creates a complete authorization bypass for all tag-policy-protected routes.

Scope

In Scope

  • Remove the header-based caller identity fallback from the tag-policy permission middleware.
  • When DID resolution fails or produces no caller ID, treat the caller as anonymous/untagged (never trust client-supplied identity headers for authorization decisions).
  • Log a warning when DID resolution fails so operators can detect misconfigured agents.

Out of Scope

  • Changing the DID resolution logic itself.
  • Removing the headers from the wire entirely — they may still be used for non-authorization purposes (e.g. logging/tracing), just not for security decisions.
  • Refactoring the permission middleware beyond the identity-source fix.

Files

  • control-plane/internal/server/middleware/permission.go:151 — remove fallback to X-Caller-Agent-ID / X-Agent-Node-ID for authorization decisions
  • control-plane/internal/server/middleware/permission_test.go — add test: request with spoofed X-Caller-Agent-ID header is NOT granted the spoofed agent's tags

Acceptance Criteria

  • Tag-policy evaluation never uses client-supplied headers as the source of caller identity
  • A request presenting a X-Caller-Agent-ID header for a high-privilege agent but no valid DID is treated as anonymous/untagged, not as that agent
  • Existing DID-authenticated policy tests continue to pass
  • Tests pass (go test ./control-plane/...)
  • Linting passes (make lint)

Notes for Contributors

Severity: HIGH

Before removing the fallback, check whether any integration tests or the agent SDK relies on these headers for the happy path. If so, fix those callers to use DID auth instead, rather than keeping the insecure fallback. The GetVerifiedCallerDID helper should be the single source of truth for caller identity in this middleware.

Metadata

Metadata

Assignees

Labels

area:control-planeControl plane server functionalitybugSomething isn't workingsecuritySecurity vulnerability

Type

No type
No fields configured for issues without a type.

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions