CtrlK
BlogDocsLog inGet started
Tessl Logo

jbaruch/spring-security-ai

Secure AI agent APIs with Spring Security 7 - RBAC, method security, OAuth2, and per-user agent access control

90

1.24x
Quality

90%

Does it follow best practices?

Impact

92%

1.24x

Average score across 3 eval scenarios

SecuritybySnyk

Passed

No known issues

Overview
Quality
Evals
Security
Files

criteria.jsonevals/scenario-3/

{
  "context": "Tests whether the agent applies @PreAuthorize/@PostAuthorize on @Tool methods, creates reusable meta-annotations for shared role requirements, defines a RoleHierarchy bean, and wires authenticated user identity and authorities into the ChatClient system prompt.",
  "type": "weighted_checklist",
  "checklist": [
    {
      "name": "@PreAuthorize on @Tool methods",
      "description": "At least three `@Tool`-annotated methods have `@PreAuthorize` annotations with different role requirements (e.g. USER, AGENT, ADMIN)",
      "max_score": 12
    },
    {
      "name": "@PostAuthorize usage",
      "description": "At least one `@Tool`-annotated method uses `@PostAuthorize` (e.g. to check `returnObject.owner == authentication.name`)",
      "max_score": 8
    },
    {
      "name": "Reusable meta-annotation",
      "description": "At least one custom annotation (e.g. `@AgentOnly`) is defined with `@PreAuthorize` as a meta-annotation, and is applied to at least two tool methods instead of repeating the `@PreAuthorize` expression",
      "max_score": 12
    },
    {
      "name": "Meta-annotation structure",
      "description": "Custom annotation has both `@Target({ElementType.METHOD, ElementType.TYPE})` and `@Retention(RetentionPolicy.RUNTIME)`",
      "max_score": 8
    },
    {
      "name": "RoleHierarchy bean",
      "description": "A `RoleHierarchy` bean is declared using `RoleHierarchyImpl.fromHierarchy()` with at least two levels of hierarchy (e.g. ADMIN > AGENT > USER)",
      "max_score": 10
    },
    {
      "name": "Authentication param in controller",
      "description": "Chat endpoint method accepts `Authentication authentication` as a parameter",
      "max_score": 8
    },
    {
      "name": "User name in system prompt",
      "description": "ChatClient system prompt includes the authenticated user's name via `authentication.getName()`",
      "max_score": 10
    },
    {
      "name": "Authorities in system prompt",
      "description": "ChatClient system prompt includes the authenticated user's authorities/roles via `authentication.getAuthorities()`",
      "max_score": 8
    },
    {
      "name": "AccessDenied guidance in system prompt",
      "description": "ChatClient system prompt instructs the AI to explain permission denial when a tool call fails with AccessDenied",
      "max_score": 8
    },
    {
      "name": "@EnableMethodSecurity present",
      "description": "A configuration class is annotated with `@EnableMethodSecurity`",
      "max_score": 8
    },
    {
      "name": "AgentTools passed to ChatClient",
      "description": "The `AgentTools` component is passed to `chatClient.prompt().tools(agentTools)` in the chat endpoint",
      "max_score": 8
    }
  ]
}

evals

tile.json