SW-A2A-009: Skill inherits every scope of the agent
Last updated 7 November 2026
| Fact | Value |
|---|---|
| Severity | Low |
| Checks | A2A agent card |
| Malicious indicator | No |
| OWASP Agentic Top 10 | ASI03 |
| OWASP MCP Top 10 | None mapped |
| NIST SP 800-53 | AC-6 (provisional mapping to SP 800-53 base controls) |
| First shipped in | Scanner 1.3.0 |
What it detects
The rule collects the OAuth scopes that the card's own requirements ask for. It counts only requirements that name an OAuth 2 or OpenID Connect scheme, and it takes the scopes from the list of each. When the total is two or more different scopes, it reports every skill that has no requirement of its own (the member securityRequirements, or security in the 0.x shape). There is one finding per skill, with the reason inherits_scopes and the number of scopes. A card with one scope or none is not reported, and neither is a skill that lists its own requirements.
Why it matters
A skill with no requirement of its own inherits the card's, so a client has to hold every scope to call even the most harmless skill, and a token that carries them all opens every skill. That is more access than any one task needs, which is the opposite of least privilege. The scanner maps this rule to OWASP ASI03, Identity and Privilege Abuse, and to NIST SP 800-53 control AC-6, Least Privilege. The finding is low because it is a hint about how the card is built, not a flaw in itself.
Example
A card whose requirement asks for two scopes. The first skill names no requirement, and the second names its own:
.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": {
"oauth": {
"oauth2SecurityScheme": {
"flows": {
"clientCredentials": {
"tokenUrl": "https://auth.example.invalid/token",
"scopes": {
"notes.read": "Read release notes",
"notes.write": "Publish release notes"
}
}
}
}
}
},
"securityRequirements": [
{
"schemes": {
"oauth": {
"list": [
"notes.read",
"notes.write"
]
}
}
}
],
"defaultInputModes": [
"text/plain"
],
"defaultOutputModes": [
"text/plain"
],
"skills": [
{
"id": "release-notes",
"name": "Release notes",
"description": "Drafts release notes for one tag.",
"tags": [
"release"
]
},
{
"id": "list-tags",
"name": "List tags",
"description": "Lists the tags of a repository.",
"tags": [
"release"
],
"securityRequirements": [
{
"schemes": {
"oauth": {
"list": [
"notes.read"
]
}
}
}
]
}
]
}The scanner reports the first skill, at /skills/0, and says it inherits 2 scopes. The second skill is left alone because it asks only for the scope it needs.
How to fix
Give each skill its own securityRequirements that names only the scopes it needs, so a read-only skill asks for the read scope. Keep the card-level requirement for what every skill really shares. Check that the identity provider issues tokens with just the scopes a client asks for, because a narrow request does no good if the provider hands out the full set.
If it is a false positive
A card whose skills all do need every scope, such as an agent that always reads and writes together, is reported anyway. Accept the finding with a note that says why the scopes cannot be split. If one of the two card-level scopes is used by a single skill only, split the requirement instead of accepting the finding.
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.