Skip to content

[BUG] agent-gateway Unauthenticated A2A Task Control and Push Spoofing #8

Description

@N0zoM1z0

Summary

At tangle-network/agent-gateway commit
4d1429c4b1947bd4ca65472aadfdfd89d0daddeb, the A2A JSON-RPC endpoint exposes
both authenticated message methods and task-control methods on POST /:slug.

message/send and message/stream call the shared authenticateAndGuard
pipeline, which resolves a trusted consumerId. However, tasks/get,
tasks/cancel, tasks/resubscribe, and
tasks/pushNotificationConfig/{set,get,list,delete} dispatch directly to task
store operations by bare task id.

The authority path is:

Alice authenticated message/send
-> Task persisted by taskId/contextId without consumer owner binding
Anonymous caller
-> tasks/get(taskId) reads Alice task/artifact
-> tasks/pushNotificationConfig/set(taskId, attacker URL)
-> tasks/cancel(taskId)
-> gateway posts full task body to attacker push URL

Affected Target Verified

  • Repository: https://github.com/tangle-network/agent-gateway
  • Commit verified: 4d1429c4b1947bd4ca65472aadfdfd89d0daddeb
  • Package version in checkout: 0.7.0
  • Components:
    • src/middleware.ts
    • src/a2a/handler.ts
    • src/a2a/task-store.ts
    • src/a2a/push-notifications.ts
    • src/dispatch.ts
  • Evidence level: target-package replay against the built Hono app with source
    anchors and negative-control policy

Preconditions

  • A deployment exposes the A2A JSON-RPC route.
  • A victim creates or owns an A2A task.
  • An attacker can learn, guess, choose, or collide with a task id. The gateway
    accepts caller-supplied task ids for new tasks, and task ids are also returned
    in normal A2A responses/headers.
  • Push impact requires GatewayConfig.a2a.pushStore to be enabled. Direct
    unauthenticated task read/cancel does not require push support.

Steps To Reproduce

From this report directory:

bash reproduce.sh

Expected vulnerable result:

{
  "verdict": "fail-under-consumer-bound-a2a-task-control-and-push-policy",
  "missingAnchors": []
}

The replay executes the target's built Hono app:

  1. Alice authenticates with Authorization: Bearer sk_agent_alice.
  2. Alice creates task_alice_private_get and receives a private artifact.
  3. An anonymous request calls tasks/get for that task id and receives the
    full task/artifact.
  4. Alice creates task_alice_private_paused, which enters input-required.
  5. An anonymous request registers a push callback for Alice's paused task.
  6. An anonymous request cancels Alice's paused task.
  7. The gateway posts the full canceled task to the attacker callback URL.

Observed oracle facts include:

{
  "anonymousGetStatus": 200,
  "anonymousGetPrivateArtifactText": "ALICE_PRIVATE_RESULT=flight-772",
  "anonymousPushSetStatus": 200,
  "anonymousCancelState": "canceled",
  "pushedTaskId": "task_alice_private_paused",
  "pushedTaskState": "canceled"
}

Technical Details

Source facts validated by the oracle:

  • POST /:slug routes all A2A JSON-RPC methods into handleJsonRpc.
  • message/send and message/stream enter authenticateAndGuard.
  • tasks/get, tasks/cancel, tasks/resubscribe, and push-config methods do
    not pass through authenticateAndGuard.
  • authenticateAndGuard resolves a trusted consumerId, but persisted A2A
    Task objects do not store an owner/consumer field.
  • New tasks are persisted by caller-supplied or gateway-generated taskId and
    contextId.
  • tasks/get returns the full task object by bare task id.
  • tasks/cancel mutates task state by bare task id.
  • tasks/pushNotificationConfig/set checks that the task exists and that the
    callback URL is a string, then stores the supplied config.
  • Push delivery posts the full task body to the stored callback URL.

Expected Behavior

Task-control and push-config operations should be authenticated and authorized
against the same consumer, tenant, agent, or payment authority that created the
task. taskId should identify the object, not authorize access to it.

Actual Behavior

The task-control methods operate on bare task ids without an authenticated
caller. An anonymous caller can read a task, cancel a non-terminal task, and
register a push receiver on a task created by an authenticated user.

Impact

Depending on deployment and task contents, this can cause:

  • unauthorized task history/artifact disclosure through tasks/get or
    tasks/resubscribe;
  • unauthorized task lifecycle mutation through tasks/cancel;
  • push receiver spoofing and externalization of full task bodies to an
    attacker-controlled URL;
  • attacker-controlled webhook authentication headers being sent by the gateway
    during push delivery.

Push URL validation is also weak in this path: the replay stores an http://
URL. The main reportable boundary is missing task authority; URL validation
should be fixed as part of push receiver hardening.

Suggested Fix

  • Run all A2A JSON-RPC methods through a shared authentication step.
  • Persist task ownership metadata such as consumerId, key id, tenant, or
    payment signer with each task.
  • Check read/write authority before tasks/get, tasks/cancel,
    tasks/resubscribe, and every push-config operation.
  • Bind push configs to the same task owner authority and require callback URL
    policy, preferably HTTPS plus SSRF-safe validation.
  • Add Alice/Bob/anonymous regression tests for tasks/get, tasks/cancel,
    tasks/resubscribe, and push-config CRUD.

Attachments

attachments.zip

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions