Skip to content

fix(security): enforce authorization for listTasks - #1038

Open
ez-lbz wants to merge 1 commit into
a2aproject:mainfrom
ez-lbz:fix/listtasks-authz-bypass
Open

fix(security): enforce authorization for listTasks#1038
ez-lbz wants to merge 1 commit into
a2aproject:mainfrom
ez-lbz:fix/listtasks-authz-bypass

Conversation

@ez-lbz

@ez-lbz ez-lbz commented Aug 10, 2026

Copy link
Copy Markdown

What changed

1. Enforce authorization for onListTasks (CWE-862)

Problem: onListTasks in AuthorizationRequestHandlerDecorator delegated directly to the wrapped handler without any authorization check, unlike every other handler method which calls an enforce* check first. With a TaskAuthorizationProvider configured, a caller could invoke listTasks and bypass the authorization model entirely — only per-task filtering in the TaskStore (which custom store implementations may skip) stood between the caller and other users' tasks.

Fix (server-common/src/main/java/org/a2aproject/sdk/server/requesthandlers/AuthorizationRequestHandlerDecorator.java):

  • Added a list-scoped read check (enforceListRead) invoked from onListTasks before delegation. Since LIST_TASKS has no single task ID, the provider is invoked with an empty-string sentinel (LIST_TASKS_SCOPE_ID) representing the whole list scope; a denial throws TaskNotFoundError (the same fail-closed error used by all other checks, avoiding information leakage). A null task ID was deliberately avoided because it breaks providers that key lookups on the task ID (e.g. ConcurrentHashMap-backed stores throw on get(null)).
  • Per-task checkRead filtering in the TaskStore.list() implementation remains in place and is unchanged.

Fix (server-common/src/test/java/org/a2aproject/sdk/server/requesthandlers/AuthorizationRequestHandlerDecoratorTest.java):

  • Added onListTasks_denied — when checkRead(context, "", LIST_TASKS) returns false, the call throws TaskNotFoundError and the delegate is never invoked.
  • Added onListTasks_allowed — when the check returns true, the call is delegated and the result returned.

Behavior change: with a TaskAuthorizationProvider configured, listTasks now goes through a list-scoped authorization gate; a provider that denies LIST_TASKS will reject list requests outright. Providers that allow unknown tasks (e.g. TestTaskAuthorizationProvider, whose lookup for the sentinel ID yields no owner and therefore allows) see no behavior change.

Testing

  • mvn -pl server-common test -Dtest=AuthorizationRequestHandlerDecoratorTest29 tests run, 0 failures, 0 errors (includes the 2 new regression tests).
  • mvn -pl reference/jsonrpc test -Dtest=QuarkusA2AJSONRPCWithTaskAuthorizationVertxTest7 tests run, 0 failures, 0 errors (this suite previously failed in CI with Cannot invoke "Object.hashCode()" because "key" is null; the list-scoped authorization test now passes with the sentinel-based check).
  • CI re-run after the fix: build matrix green.

onListTasks in AuthorizationRequestHandlerDecorator delegated directly
without any authorization check, unlike every other handler method.

Add a list-scoped read check before delegation. The provider is invoked
with an empty-string sentinel for the whole list scope (a null task ID
would break ConcurrentHashMap-backed providers with an NPE on lookup).
Per-task checkRead filtering in the TaskStore list() remains unchanged.
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