SW-A2A-001: Agent card does not follow A2A 1.0
Last updated 7 November 2026
| Fact | Value |
|---|---|
| Severity | Medium |
| Checks | A2A agent card |
| Malicious indicator | No |
| OWASP Agentic Top 10 | ASI07 |
| OWASP MCP Top 10 | None mapped |
| NIST SP 800-53 | SI-10 (provisional mapping to SP 800-53 base controls) |
| First shipped in | Scanner 1.3.0 |
What it detects
The rule reads each agent card the scanner finds: a file named .well-known/agent-card.json at any depth, a file listed under a2a.cards, and a .well-known/agent.json or other agent-card.json file that parses as a card. A card in the A2A 1.0 shape (it has supportedInterfaces) is checked member by member against the protocol's definition. Every problem is one finding that names the member by its JSON pointer and gives a reason: missing, wrong_type, empty, version_format, binding_unknown, not_absolute_uri, duplicate_skill_id, scheme_type, flow_count or undefined_scheme.
The required members are name, description, version, capabilities, supportedInterfaces, defaultInputModes, defaultOutputModes and skills. Each interface needs a url, a protocolBinding (JSONRPC, GRPC, HTTP+JSON or an absolute URI) and a protocolVersion such as 1.0. Each skill needs an id, a name, a description and at least one tag, and skill ids must not repeat. A security scheme holds exactly one kind, an OAuth 2 scheme holds exactly one flow with the URLs that flow needs, and a security requirement may name only a scheme the card defines. Members the definition does not know are ignored.
Three more cases give the same rule. A file at a .well-known/agent-card.json path, or listed in a2a.cards, that parses but has no card shape (no string name, and neither a supportedInterfaces list nor a url) is reported as not_a_card. A card in the 0.2 or 0.3 shape is not validated further, but a protocolVersion that is neither Major.Minor nor Major.Minor.Patch is a version_format finding. And a member that the card writes only with its proto field name, such as security_requirements instead of securityRequirements, is reported as proto_field_name; a removed 0.x member written only in snake_case is legacy_field_name, because not every client reads those names.
Why it matters
A client builds its connection from the card: which address to call, which protocol binding to speak, which authentication to offer, which skills exist. A card that breaks the definition is read differently by different clients. One rejects it, another guesses, a third drops the broken member without a word, so the card a reviewer approved is not always the card a client acts on. The scanner maps this rule to OWASP ASI07, Insecure Inter-Agent Communication, and to NIST SP 800-53 control SI-10, Information Input Validation.
A security requirement that names a scheme the card never defines, for example, leaves each client to decide for itself what the card meant. That is why the rule reads the whole structure and not only the top level.
Example
This card is complete except for two members that A2A 1.0 requires, the description and the version:
.well-known/agent-card.json
{
"name": "demo-agent",
"supportedInterfaces": [
{
"url": "https://agent.example.invalid/a2a",
"protocolBinding": "JSONRPC",
"protocolVersion": "1.0"
}
],
"capabilities": {},
"securitySchemes": {
"bearer": {
"httpAuthSecurityScheme": {
"scheme": "Bearer"
}
}
},
"securityRequirements": [
{
"schemes": {
"bearer": {
"list": []
}
}
}
],
"defaultInputModes": [
"text/plain"
],
"defaultOutputModes": [
"text/plain"
],
"skills": [
{
"id": "release-notes",
"name": "Release notes",
"description": "Drafts release notes for one tag.",
"tags": [
"release"
]
}
]
}The scanner reports one finding for each missing member, at /description and /version, with the reason missing. The card carries no signature, so SW-A2A-012 also reports it at low severity, which is a separate rule.
How to fix
Add the members that are missing, correct the type of the ones that are wrong and write each protocolVersion as 1.0. The sample card of the A2A 1.0 specification is a good starting point. Scan again after each change, because fixing a missing member sometimes shows the next problem under it.
For proto_field_name, rename the member to its camelCase JSON name. For legacy_field_name, finish the move to A2A 1.0 instead and drop the old member (see SW-A2A-002).
If it is a false positive
An extension member of your own is not a finding, because members the definition does not know are ignored. A not_a_card finding on a file you meant as something else means the file sits at a path the scanner always treats as a card. If it is a sample or a test input, exclude its folder in the settings, for example exclude: ["samples/**"], so a real card at that path is never skipped.
If the finding is not a problem in your case, accept it with a note: in the dashboard a manager or owner can accept a finding and the note is kept with it; with the scanner on its own, add it to the baseline file with a reason and an expiry date.