SW-A2A-011: Agent card signature is malformed or uses a refused algorithm
Last updated 7 November 2026
| Fact | Value |
|---|---|
| Severity | High |
| Checks | A2A agent card |
| Malicious indicator | No |
| OWASP Agentic Top 10 | ASI07 |
| OWASP MCP Top 10 | None mapped |
| NIST SP 800-53 | SI-7 (provisional mapping to SP 800-53 base controls) |
| First shipped in | Scanner 1.3.0 |
What it detects
The rule reports each signature whose outcome is malformed or refused_alg. An entry is malformed when its protected header is not base64url without padding, is over 4 KiB or is not a JSON object; when the header has no alg or no kid; when it has a crit member or sets b64 to false, since the scanner understands no such extension; when the signature is not base64url; when the card holds a number outside what canonical form can carry (an integer above 2 to the power 53); and when an ECDSA signature has the wrong length. The last case includes the DER form that cloud key services return, where a JWS needs the fixed-length R and S form of RFC 7518.
An entry is refused when its algorithm is none or any name the scanner does not know. The scanner verifies ES256, ES384, ES512, RS256, RS384, RS512, PS256, PS384, PS512, Ed25519 and EdDSA with an Ed25519 key. The HMAC algorithms and Ed448 are not refused here; they belong to SW-A2A-017. There is one finding per entry, at the entry's index in the signatures list, and its evidence says why.
Why it matters
A signature entry that cannot be read, or that uses the algorithm none, gives a client nothing to check, but its presence can still make the card look signed to a person or a tool that only counts entries. The algorithm none is a known way to turn a signed document into an unsigned one that still carries a signature member. The scanner maps this rule to OWASP ASI07, Insecure Inter-Agent Communication, and to NIST SP 800-53 control SI-7, Software, Firmware, and Information Integrity.
Example
A card with one signature whose protected header names the algorithm none and whose signature is empty:
.well-known/agent-card.json
{
"name": "demo-agent",
"description": "Drafts release notes from merged pull requests.",
"version": "1.0.0",
"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"
]
}
],
"signatures": [
{
"protected": "eyJhbGciOiJub25lIiwia2lkIjoiZGVtby1rZXkifQ",
"signature": ""
}
]
}The finding points at the first entry of the signatures list and says algorithm none is refused.
How to fix
Fix the signer, not the card. Produce a JWS in compact form with one of the algorithms above, an alg and a kid in the protected header and, for ECDSA, the fixed-length signature of RFC 7518 (many key service libraries return DER, which has to be converted). Then sign again and scan again. If the card should not be signed yet, remove the signatures member and accept SW-A2A-012 instead of leaving an entry that proves nothing.
If it is a false positive
An entry the scanner calls malformed because of a feature it does not support, such as a crit header or an unencoded payload, may be valid for your own tooling. Other A2A clients could not check it either, so it is of little use as the signature of a published card. Accept the finding only for a card that nobody but your own tooling reads, with a note that says so.
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.