Files
cameleer-server/.claude/rules/security.md

2.2 KiB

Security invariants

Agent self-service endpoint ownership

Every endpoint under /api/v1/agents/{id}/... that is intended for agent self-service must enforce AgentOwnershipGuard.requireAgentOwnership(id, httpRequest) as its FIRST statement, before any registry lookup, audit log, or other side effect.

Currently enforced on: SSE events, heartbeat, deregister, command-ack. Currently NOT enforced (out of scope): register (bootstrap-token-gated), refresh, replayExchange. Adding the guard there is a follow-up.

RBAC revocation invariant

Any code path that mutates the effective role set of one or more users — including:

  • user_roles (direct role assignment)
  • user_groups (group membership)
  • group_roles (group's role list)
  • Role deletion
  • User deletion
  • Password reset

— MUST bump users.token_revoked_before for each affected user (use Instant.now().plusMillis(1) to guard against same-millisecond races with token issuance, since JWT iat is millisecond-quantized).

UiAuthController.refresh and JwtAuthenticationFilter.tryInternalToken both consult users.token_revoked_before; without the bump on RBAC mutation, refresh- token role-retention privesc is possible (Vuln 2 of the 2026-04-29 review).

ClaimMappingAdminController mutations are EXEMPT — they affect role assignment at the next OIDC login (via OidcAuthController.applyClaimMappings), not currently- issued tokens.

SSE command signing

Every command emitted via SseConnectionManager.onCommandReady is signed with Ed25519 by SsePayloadSigner. The signer THROWS on null/empty/blank/non-object input — there is no silent unsigned fallback.

Emit-site contract: build the payload via objectMapper.writeValueAsString(...) so the input is in Jackson's canonical (no-whitespace, insertion-order) form. The agent verifies by parsing the wire JSON, removing the signature field, re-serializing, and Ed25519-verifying against the resulting bytes — this requires byte-equivalence between the server-signed bytes and the agent-reconstructed bytes.

The requireSignedCommands capability bit on AgentInfo (set from AgentRegistrationRequest) is currently informational only — server-side gating on this bit is a future release per the agent team's migration sequence step 4.