ax-audit 3.6.0 → 4.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +138 -0
- package/LICENSE +1 -1
- package/README.md +58 -36
- package/dist/baseline.d.ts +2 -0
- package/dist/baseline.d.ts.map +1 -1
- package/dist/baseline.js +42 -4
- package/dist/baseline.js.map +1 -1
- package/dist/check-ids.d.ts +19 -0
- package/dist/check-ids.d.ts.map +1 -0
- package/dist/check-ids.js +53 -0
- package/dist/check-ids.js.map +1 -0
- package/dist/checks/agent-access.d.ts +23 -6
- package/dist/checks/agent-access.d.ts.map +1 -1
- package/dist/checks/agent-access.js +200 -54
- package/dist/checks/agent-access.js.map +1 -1
- package/dist/checks/agent-card.d.ts +37 -0
- package/dist/checks/agent-card.d.ts.map +1 -0
- package/dist/checks/agent-card.js +352 -0
- package/dist/checks/agent-card.js.map +1 -0
- package/dist/checks/agent-operability.d.ts +66 -0
- package/dist/checks/agent-operability.d.ts.map +1 -0
- package/dist/checks/agent-operability.js +383 -0
- package/dist/checks/agent-operability.js.map +1 -0
- package/dist/checks/agent-skills.d.ts +24 -0
- package/dist/checks/agent-skills.d.ts.map +1 -0
- package/dist/checks/agent-skills.js +316 -0
- package/dist/checks/agent-skills.js.map +1 -0
- package/dist/checks/ai-catalog.d.ts +28 -0
- package/dist/checks/ai-catalog.d.ts.map +1 -0
- package/dist/checks/ai-catalog.js +254 -0
- package/dist/checks/ai-catalog.js.map +1 -0
- package/dist/checks/ai-directives.d.ts +57 -0
- package/dist/checks/ai-directives.d.ts.map +1 -0
- package/dist/checks/ai-directives.js +263 -0
- package/dist/checks/ai-directives.js.map +1 -0
- package/dist/checks/api-discovery.d.ts +26 -0
- package/dist/checks/api-discovery.d.ts.map +1 -0
- package/dist/checks/api-discovery.js +432 -0
- package/dist/checks/api-discovery.js.map +1 -0
- package/dist/checks/auth-discovery.d.ts +28 -0
- package/dist/checks/auth-discovery.d.ts.map +1 -0
- package/dist/checks/auth-discovery.js +213 -0
- package/dist/checks/auth-discovery.js.map +1 -0
- package/dist/checks/commerce-discovery.d.ts +40 -0
- package/dist/checks/commerce-discovery.d.ts.map +1 -0
- package/dist/checks/commerce-discovery.js +295 -0
- package/dist/checks/commerce-discovery.js.map +1 -0
- package/dist/checks/content-negotiation.d.ts.map +1 -1
- package/dist/checks/content-negotiation.js +135 -20
- package/dist/checks/content-negotiation.js.map +1 -1
- package/dist/checks/crawl-efficiency.d.ts +13 -1
- package/dist/checks/crawl-efficiency.d.ts.map +1 -1
- package/dist/checks/crawl-efficiency.js +65 -1
- package/dist/checks/crawl-efficiency.js.map +1 -1
- package/dist/checks/frontmatter.d.ts +34 -0
- package/dist/checks/frontmatter.d.ts.map +1 -0
- package/dist/checks/frontmatter.js +100 -0
- package/dist/checks/frontmatter.js.map +1 -0
- package/dist/checks/html-rendering.d.ts.map +1 -1
- package/dist/checks/html-rendering.js +0 -1
- package/dist/checks/html-rendering.js.map +1 -1
- package/dist/checks/html-utils.d.ts +10 -0
- package/dist/checks/html-utils.d.ts.map +1 -1
- package/dist/checks/html-utils.js +19 -0
- package/dist/checks/html-utils.js.map +1 -1
- package/dist/checks/http-headers.d.ts.map +1 -1
- package/dist/checks/http-headers.js +82 -10
- package/dist/checks/http-headers.js.map +1 -1
- package/dist/checks/http-hygiene.d.ts +26 -0
- package/dist/checks/http-hygiene.d.ts.map +1 -0
- package/dist/checks/http-hygiene.js +257 -0
- package/dist/checks/http-hygiene.js.map +1 -0
- package/dist/checks/index.d.ts.map +1 -1
- package/dist/checks/index.js +24 -8
- package/dist/checks/index.js.map +1 -1
- package/dist/checks/llms-txt.d.ts +15 -0
- package/dist/checks/llms-txt.d.ts.map +1 -1
- package/dist/checks/llms-txt.js +162 -2
- package/dist/checks/llms-txt.js.map +1 -1
- package/dist/checks/mcp-discovery.d.ts +30 -0
- package/dist/checks/mcp-discovery.d.ts.map +1 -0
- package/dist/checks/mcp-discovery.js +523 -0
- package/dist/checks/mcp-discovery.js.map +1 -0
- package/dist/checks/meta-tags.d.ts.map +1 -1
- package/dist/checks/meta-tags.js +6 -5
- package/dist/checks/meta-tags.js.map +1 -1
- package/dist/checks/robots-parser.d.ts +110 -0
- package/dist/checks/robots-parser.d.ts.map +1 -0
- package/dist/checks/robots-parser.js +277 -0
- package/dist/checks/robots-parser.js.map +1 -0
- package/dist/checks/robots-txt.d.ts +2 -20
- package/dist/checks/robots-txt.d.ts.map +1 -1
- package/dist/checks/robots-txt.js +219 -120
- package/dist/checks/robots-txt.js.map +1 -1
- package/dist/checks/rsl.d.ts +0 -2
- package/dist/checks/rsl.d.ts.map +1 -1
- package/dist/checks/rsl.js +1 -11
- package/dist/checks/rsl.js.map +1 -1
- package/dist/checks/security-txt.d.ts.map +1 -1
- package/dist/checks/security-txt.js +0 -1
- package/dist/checks/security-txt.js.map +1 -1
- package/dist/checks/seo-basics.d.ts.map +1 -1
- package/dist/checks/seo-basics.js +0 -1
- package/dist/checks/seo-basics.js.map +1 -1
- package/dist/checks/sitemap.d.ts.map +1 -1
- package/dist/checks/sitemap.js +0 -1
- package/dist/checks/sitemap.js.map +1 -1
- package/dist/checks/structured-data.d.ts.map +1 -1
- package/dist/checks/structured-data.js +215 -4
- package/dist/checks/structured-data.js.map +1 -1
- package/dist/checks/structured-fields.d.ts +46 -0
- package/dist/checks/structured-fields.d.ts.map +1 -0
- package/dist/checks/structured-fields.js +112 -0
- package/dist/checks/structured-fields.js.map +1 -0
- package/dist/checks/surface.d.ts +59 -0
- package/dist/checks/surface.d.ts.map +1 -0
- package/dist/checks/surface.js +106 -0
- package/dist/checks/surface.js.map +1 -0
- package/dist/checks/tls-https.d.ts.map +1 -1
- package/dist/checks/tls-https.js +0 -1
- package/dist/checks/tls-https.js.map +1 -1
- package/dist/checks/usage-policy.d.ts +53 -0
- package/dist/checks/usage-policy.d.ts.map +1 -0
- package/dist/checks/usage-policy.js +339 -0
- package/dist/checks/usage-policy.js.map +1 -0
- package/dist/checks/utils.d.ts +25 -1
- package/dist/checks/utils.d.ts.map +1 -1
- package/dist/checks/utils.js +33 -1
- package/dist/checks/utils.js.map +1 -1
- package/dist/checks/waf.d.ts +75 -0
- package/dist/checks/waf.d.ts.map +1 -0
- package/dist/checks/waf.js +203 -0
- package/dist/checks/waf.js.map +1 -0
- package/dist/checks/webmcp.d.ts +55 -0
- package/dist/checks/webmcp.d.ts.map +1 -0
- package/dist/checks/webmcp.js +209 -0
- package/dist/checks/webmcp.js.map +1 -0
- package/dist/checks/well-known.d.ts +38 -0
- package/dist/checks/well-known.d.ts.map +1 -0
- package/dist/checks/well-known.js +202 -0
- package/dist/checks/well-known.js.map +1 -0
- package/dist/cli.d.ts +14 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +124 -4
- package/dist/cli.js.map +1 -1
- package/dist/constants.d.ts +195 -14
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.js +598 -70
- package/dist/constants.js.map +1 -1
- package/dist/fetcher.d.ts.map +1 -1
- package/dist/fetcher.js +44 -14
- package/dist/fetcher.js.map +1 -1
- package/dist/guide-urls.js +1 -1
- package/dist/guide-urls.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/orchestrator.d.ts.map +1 -1
- package/dist/orchestrator.js +5 -1
- package/dist/orchestrator.js.map +1 -1
- package/dist/reporter/html.d.ts +9 -0
- package/dist/reporter/html.d.ts.map +1 -1
- package/dist/reporter/html.js +49 -11
- package/dist/reporter/html.js.map +1 -1
- package/dist/reporter/markdown.d.ts.map +1 -1
- package/dist/reporter/markdown.js +36 -6
- package/dist/reporter/markdown.js.map +1 -1
- package/dist/reporter/terminal.d.ts.map +1 -1
- package/dist/reporter/terminal.js +36 -1
- package/dist/reporter/terminal.js.map +1 -1
- package/dist/scorer.d.ts +10 -0
- package/dist/scorer.d.ts.map +1 -1
- package/dist/scorer.js +22 -5
- package/dist/scorer.js.map +1 -1
- package/dist/types.d.ts +90 -3
- package/dist/types.d.ts.map +1 -1
- package/docs/architecture.md +27 -11
- package/docs/checks.md +285 -52
- package/docs/cli.md +36 -0
- package/docs/concepts.md +27 -13
- package/docs/faq.md +18 -6
- package/docs/getting-started.md +20 -13
- package/docs/roadmap.md +367 -0
- package/package.json +13 -5
- package/dist/checks/agent-json.d.ts +0 -14
- package/dist/checks/agent-json.d.ts.map +0 -1
- package/dist/checks/agent-json.js +0 -167
- package/dist/checks/agent-json.js.map +0 -1
- package/dist/checks/mcp.d.ts +0 -4
- package/dist/checks/mcp.d.ts.map +0 -1
- package/dist/checks/mcp.js +0 -162
- package/dist/checks/mcp.js.map +0 -1
- package/dist/checks/openapi.d.ts +0 -4
- package/dist/checks/openapi.d.ts.map +0 -1
- package/dist/checks/openapi.js +0 -121
- package/dist/checks/openapi.js.map +0 -1
- package/dist/checks/well-known-ai.d.ts +0 -17
- package/dist/checks/well-known-ai.d.ts.map +0 -1
- package/dist/checks/well-known-ai.js +0 -123
- package/dist/checks/well-known-ai.js.map +0 -1
|
@@ -0,0 +1,352 @@
|
|
|
1
|
+
import { A2A_MEDIA_TYPE, AGENT_CARD_REQUIRED_V03, AGENT_CARD_REQUIRED_V1, A2A_PROTOCOL_BINDINGS, } from '../constants.js';
|
|
2
|
+
import { guideUrl } from '../guide-urls.js';
|
|
3
|
+
import { buildResult, checkContentType, isHtmlDocument, notApplicable } from './utils.js';
|
|
4
|
+
import { hasAgentSurface } from './surface.js';
|
|
5
|
+
import { WELL_KNOWN, standingNote } from './well-known.js';
|
|
6
|
+
/**
|
|
7
|
+
* "agent-card" — the A2A (Agent2Agent) Agent Card.
|
|
8
|
+
*
|
|
9
|
+
* Two things changed since this check was written, and both matter:
|
|
10
|
+
*
|
|
11
|
+
* 1. **The path moved.** A2A v0.3.0 (2025-07-30) relocated the card from
|
|
12
|
+
* `/.well-known/agent.json` to `/.well-known/agent-card.json`, which is now
|
|
13
|
+
* an IANA-registered well-known URI. A site serving only the old path is
|
|
14
|
+
* invisible to a current client.
|
|
15
|
+
*
|
|
16
|
+
* 2. **The shape changed.** A2A 1.0 (2026-03-12, breaking) folded the
|
|
17
|
+
* top-level `url`, `protocolVersion`, `preferredTransport` and
|
|
18
|
+
* `additionalInterfaces` fields into a single `supportedInterfaces[]` array,
|
|
19
|
+
* and renamed `supportsAuthenticatedExtendedCard` to
|
|
20
|
+
* `capabilities.extendedAgentCard`. Most deployed cards are still 0.3-shaped.
|
|
21
|
+
*
|
|
22
|
+
* Validating a 1.0 card against 0.3 rules (or the reverse) produces nonsense,
|
|
23
|
+
* so the check detects which generation a card belongs to from its own
|
|
24
|
+
* structure and applies the matching rules. `authentication`, dropped back in
|
|
25
|
+
* 0.2.x in favour of `securitySchemes`, is flagged wherever it appears.
|
|
26
|
+
*
|
|
27
|
+
* Spec: https://a2a-protocol.org/latest/specification/
|
|
28
|
+
* Normative field list: specification/a2a.proto in github.com/a2aproject/A2A
|
|
29
|
+
*/
|
|
30
|
+
export const meta = {
|
|
31
|
+
id: 'agent-card',
|
|
32
|
+
name: 'Agent Card (A2A)',
|
|
33
|
+
description: 'Checks the A2A Agent Card at /.well-known/agent-card.json',
|
|
34
|
+
category: 'protocols',
|
|
35
|
+
aliases: ['agent-json'],
|
|
36
|
+
};
|
|
37
|
+
const CANONICAL_PATH = '/.well-known/agent-card.json';
|
|
38
|
+
const LEGACY_PATH = '/.well-known/agent.json';
|
|
39
|
+
/**
|
|
40
|
+
* Detect the card generation from its own structure rather than from a version
|
|
41
|
+
* field, because 0.3 cards state `protocolVersion` at the top level and 1.0
|
|
42
|
+
* cards state it per interface.
|
|
43
|
+
*/
|
|
44
|
+
export function detectGeneration(data) {
|
|
45
|
+
if (Array.isArray(data.supportedInterfaces))
|
|
46
|
+
return 'v1';
|
|
47
|
+
if (typeof data.url === 'string' || typeof data.protocolVersion === 'string')
|
|
48
|
+
return 'v0.3';
|
|
49
|
+
return 'unknown';
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Try the registered path first, then the pre-0.3 one. An HTML response counts
|
|
53
|
+
* as absent: an SPA catch-all returns its index shell for every unknown path,
|
|
54
|
+
* and reporting that as a malformed card would send an operator looking for a
|
|
55
|
+
* bug in a file they never wrote.
|
|
56
|
+
*/
|
|
57
|
+
async function locateCard(ctx) {
|
|
58
|
+
for (const path of [CANONICAL_PATH, LEGACY_PATH]) {
|
|
59
|
+
const res = await ctx.fetch(`${ctx.url}${path}`);
|
|
60
|
+
if (res.ok && res.body.trim().length > 0 && !isHtmlDocument(res.body))
|
|
61
|
+
return { res, path };
|
|
62
|
+
}
|
|
63
|
+
return null;
|
|
64
|
+
}
|
|
65
|
+
export default async function check(ctx) {
|
|
66
|
+
const start = performance.now();
|
|
67
|
+
const findings = [];
|
|
68
|
+
let score = 100;
|
|
69
|
+
const found = await locateCard(ctx);
|
|
70
|
+
if (found === null) {
|
|
71
|
+
// An Agent Card describes capabilities another agent can invoke. Most sites
|
|
72
|
+
// have none, and asking a restaurant to publish one is noise — so the check
|
|
73
|
+
// only applies where the site is already agent-facing.
|
|
74
|
+
const surface = await hasAgentSurface(ctx);
|
|
75
|
+
if (!surface.found) {
|
|
76
|
+
findings.push({
|
|
77
|
+
status: 'pass',
|
|
78
|
+
message: 'No agent-facing surface — an Agent Card does not apply to this site',
|
|
79
|
+
detail: 'No API, MCP server or existing card was found. An Agent Card advertises capabilities another agent can ' +
|
|
80
|
+
'invoke; a site that offers none has nothing to put in it. Run with --profile agent to audit as though it did.',
|
|
81
|
+
});
|
|
82
|
+
return notApplicable(meta, findings, start);
|
|
83
|
+
}
|
|
84
|
+
findings.push({
|
|
85
|
+
status: 'fail',
|
|
86
|
+
message: `${CANONICAL_PATH} not found`,
|
|
87
|
+
detail: `Site is agent-facing (${surface.reason}). Also tried the pre-0.3 path ${LEGACY_PATH}. ${standingNote(CANONICAL_PATH) ?? ''}`.trim(),
|
|
88
|
+
hint: 'This site offers something an agent could call, but nothing tells an agent what. Publish an A2A Agent Card ' +
|
|
89
|
+
'at /.well-known/agent-card.json. A minimal 1.0 card needs name, description, version, capabilities, ' +
|
|
90
|
+
`supportedInterfaces, defaultInputModes, defaultOutputModes and skills. Spec: ${WELL_KNOWN[CANONICAL_PATH].specUrl}`,
|
|
91
|
+
learnMoreUrl: guideUrl(meta.id, 'not-found'),
|
|
92
|
+
});
|
|
93
|
+
return buildResult(meta, 0, findings, start);
|
|
94
|
+
}
|
|
95
|
+
const { res, path } = found;
|
|
96
|
+
if (path === LEGACY_PATH) {
|
|
97
|
+
findings.push({
|
|
98
|
+
status: 'warn',
|
|
99
|
+
message: `Agent Card served only from the pre-0.3 path ${LEGACY_PATH}`,
|
|
100
|
+
detail: standingNote(LEGACY_PATH),
|
|
101
|
+
hint: 'A2A moved the card to /.well-known/agent-card.json in v0.3.0 (2025-07-30), and that path is the one ' +
|
|
102
|
+
'registered with IANA. Serve the card there; keep the old path as a redirect if existing clients depend on it.',
|
|
103
|
+
learnMoreUrl: guideUrl(meta.id, 'legacy-path'),
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
else {
|
|
107
|
+
findings.push({ status: 'pass', message: `${CANONICAL_PATH} exists` });
|
|
108
|
+
}
|
|
109
|
+
const ctFinding = checkContentType(res, ['application/json', A2A_MEDIA_TYPE], {
|
|
110
|
+
checkId: meta.id,
|
|
111
|
+
resourceLabel: path,
|
|
112
|
+
anchor: 'wrong-content-type',
|
|
113
|
+
});
|
|
114
|
+
if (ctFinding) {
|
|
115
|
+
findings.push(ctFinding);
|
|
116
|
+
score -= 5;
|
|
117
|
+
}
|
|
118
|
+
let data;
|
|
119
|
+
try {
|
|
120
|
+
data = JSON.parse(res.body);
|
|
121
|
+
}
|
|
122
|
+
catch {
|
|
123
|
+
findings.push({
|
|
124
|
+
status: 'fail',
|
|
125
|
+
message: 'Invalid JSON',
|
|
126
|
+
hint: 'Fix the JSON syntax in your Agent Card. Validate it with a JSON linter.',
|
|
127
|
+
learnMoreUrl: guideUrl(meta.id, 'invalid-json'),
|
|
128
|
+
});
|
|
129
|
+
return buildResult(meta, 10, findings, start);
|
|
130
|
+
}
|
|
131
|
+
findings.push({ status: 'pass', message: 'Valid JSON' });
|
|
132
|
+
const generation = detectGeneration(data);
|
|
133
|
+
score = validateGeneration(generation, data, ctx, findings, score);
|
|
134
|
+
score = validateCommon(data, findings, score);
|
|
135
|
+
return buildResult(meta, score, findings, start);
|
|
136
|
+
}
|
|
137
|
+
/** Apply the field rules of whichever spec generation this card was written against. */
|
|
138
|
+
function validateGeneration(generation, data, ctx, findings, score) {
|
|
139
|
+
if (generation === 'unknown') {
|
|
140
|
+
findings.push({
|
|
141
|
+
status: 'fail',
|
|
142
|
+
message: 'Card shape matches neither A2A 1.0 nor 0.3',
|
|
143
|
+
detail: 'Expected either supportedInterfaces[] (1.0) or a top-level url + protocolVersion (0.3).',
|
|
144
|
+
hint: 'Rebuild the card against the current specification. A 1.0 card declares every endpoint inside supportedInterfaces[].',
|
|
145
|
+
learnMoreUrl: guideUrl(meta.id, 'unknown-generation'),
|
|
146
|
+
});
|
|
147
|
+
return score - 30;
|
|
148
|
+
}
|
|
149
|
+
const required = generation === 'v1' ? AGENT_CARD_REQUIRED_V1 : AGENT_CARD_REQUIRED_V03;
|
|
150
|
+
findings.push({
|
|
151
|
+
status: 'pass',
|
|
152
|
+
message: generation === 'v1' ? 'Card follows A2A 1.0 (supportedInterfaces)' : 'Card follows A2A 0.3 (top-level url)',
|
|
153
|
+
...(generation === 'v0.3'
|
|
154
|
+
? {
|
|
155
|
+
detail: 'A2A 1.0 (2026-03-12) replaced the top-level url, protocolVersion, preferredTransport and ' +
|
|
156
|
+
'additionalInterfaces fields with a single supportedInterfaces[] array.',
|
|
157
|
+
}
|
|
158
|
+
: {}),
|
|
159
|
+
});
|
|
160
|
+
for (const field of required) {
|
|
161
|
+
if (data[field] !== undefined && data[field] !== null) {
|
|
162
|
+
findings.push({ status: 'pass', message: `Required field "${field}" present` });
|
|
163
|
+
}
|
|
164
|
+
else {
|
|
165
|
+
findings.push({
|
|
166
|
+
status: 'fail',
|
|
167
|
+
message: `Required field "${field}" missing`,
|
|
168
|
+
hint: `Add "${field}" to your Agent Card. A2A ${generation === 'v1' ? '1.0' : '0.3'} requires it.`,
|
|
169
|
+
learnMoreUrl: guideUrl(meta.id, 'missing-field'),
|
|
170
|
+
});
|
|
171
|
+
score -= 15;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
return generation === 'v1'
|
|
175
|
+
? validateInterfaces(data, ctx, findings, score)
|
|
176
|
+
: validateLegacyUrl(data, ctx, findings, score);
|
|
177
|
+
}
|
|
178
|
+
/** A2A 1.0: every entry in `supportedInterfaces[]` needs url, binding and version. */
|
|
179
|
+
function validateInterfaces(data, ctx, findings, score) {
|
|
180
|
+
const interfaces = Array.isArray(data.supportedInterfaces)
|
|
181
|
+
? data.supportedInterfaces
|
|
182
|
+
: [];
|
|
183
|
+
if (interfaces.length === 0) {
|
|
184
|
+
findings.push({
|
|
185
|
+
status: 'fail',
|
|
186
|
+
message: 'supportedInterfaces[] is empty',
|
|
187
|
+
hint: 'Declare at least one interface: { "url": "https://…", "protocolBinding": "JSONRPC", "protocolVersion": "1.0" }.',
|
|
188
|
+
learnMoreUrl: guideUrl(meta.id, 'empty-interfaces'),
|
|
189
|
+
});
|
|
190
|
+
return score - 15;
|
|
191
|
+
}
|
|
192
|
+
const incomplete = interfaces.filter((i) => !i.url || !i.protocolBinding || !i.protocolVersion);
|
|
193
|
+
if (incomplete.length > 0) {
|
|
194
|
+
findings.push({
|
|
195
|
+
status: 'warn',
|
|
196
|
+
message: `${incomplete.length}/${interfaces.length} interface(s) missing url, protocolBinding or protocolVersion`,
|
|
197
|
+
hint: 'Each entry in supportedInterfaces[] must declare all three so a client knows where to connect and how to speak.',
|
|
198
|
+
learnMoreUrl: guideUrl(meta.id, 'incomplete-interface'),
|
|
199
|
+
});
|
|
200
|
+
score -= 10;
|
|
201
|
+
}
|
|
202
|
+
else {
|
|
203
|
+
findings.push({ status: 'pass', message: `${interfaces.length} interface(s) fully declared` });
|
|
204
|
+
}
|
|
205
|
+
const badBinding = interfaces
|
|
206
|
+
.map((i) => String(i.protocolBinding ?? ''))
|
|
207
|
+
.filter((b) => b !== '' && !A2A_PROTOCOL_BINDINGS.includes(b));
|
|
208
|
+
if (badBinding.length > 0) {
|
|
209
|
+
findings.push({
|
|
210
|
+
status: 'warn',
|
|
211
|
+
message: 'Interface with an unrecognised protocolBinding',
|
|
212
|
+
detail: badBinding.join(', '),
|
|
213
|
+
hint: `A2A 1.0 defines: ${A2A_PROTOCOL_BINDINGS.join(', ')}.`,
|
|
214
|
+
learnMoreUrl: guideUrl(meta.id, 'unknown-binding'),
|
|
215
|
+
});
|
|
216
|
+
score -= 5;
|
|
217
|
+
}
|
|
218
|
+
const offOrigin = interfaces
|
|
219
|
+
.map((i) => String(i.url ?? ''))
|
|
220
|
+
.filter((u) => u !== '' && sameHost(u, ctx.url) === false);
|
|
221
|
+
if (offOrigin.length > 0) {
|
|
222
|
+
findings.push({
|
|
223
|
+
status: 'warn',
|
|
224
|
+
message: `${offOrigin.length} interface URL(s) point to a different origin`,
|
|
225
|
+
detail: offOrigin.join(', '),
|
|
226
|
+
hint: 'Interfaces on another origin can be intentional, but agents treat the card as authoritative for this site. Confirm the endpoints are yours.',
|
|
227
|
+
learnMoreUrl: guideUrl(meta.id, 'url-mismatch'),
|
|
228
|
+
});
|
|
229
|
+
score -= 5;
|
|
230
|
+
}
|
|
231
|
+
return score;
|
|
232
|
+
}
|
|
233
|
+
/** A2A 0.3: the single top-level `url` should be absolute and on this origin. */
|
|
234
|
+
function validateLegacyUrl(data, ctx, findings, score) {
|
|
235
|
+
if (typeof data.url !== 'string' || data.url.length === 0)
|
|
236
|
+
return score;
|
|
237
|
+
const sameOrigin = sameHost(data.url, ctx.url);
|
|
238
|
+
if (sameOrigin === false) {
|
|
239
|
+
findings.push({
|
|
240
|
+
status: 'warn',
|
|
241
|
+
message: `Agent Card "url" points to a different origin: ${data.url}`,
|
|
242
|
+
hint: 'The url field should match the audited site origin. Pointing it elsewhere can confuse agents about the canonical agent endpoint.',
|
|
243
|
+
learnMoreUrl: guideUrl(meta.id, 'url-mismatch'),
|
|
244
|
+
});
|
|
245
|
+
score -= 5;
|
|
246
|
+
}
|
|
247
|
+
else if (sameOrigin === null) {
|
|
248
|
+
findings.push({
|
|
249
|
+
status: 'warn',
|
|
250
|
+
message: `Agent Card "url" is not a valid absolute URL: ${data.url}`,
|
|
251
|
+
hint: 'Provide an absolute https:// URL for the url field.',
|
|
252
|
+
learnMoreUrl: guideUrl(meta.id, 'url-invalid'),
|
|
253
|
+
});
|
|
254
|
+
score -= 5;
|
|
255
|
+
}
|
|
256
|
+
return score;
|
|
257
|
+
}
|
|
258
|
+
/** Rules that hold across both generations. */
|
|
259
|
+
function validateCommon(data, findings, score) {
|
|
260
|
+
if (Array.isArray(data.skills) && data.skills.length > 0) {
|
|
261
|
+
const skills = data.skills;
|
|
262
|
+
findings.push({ status: 'pass', message: `${skills.length} skill(s) defined` });
|
|
263
|
+
const incomplete = skills.filter((s) => !s.id || !s.description);
|
|
264
|
+
if (incomplete.length === 0) {
|
|
265
|
+
findings.push({ status: 'pass', message: 'All skills have id + description' });
|
|
266
|
+
}
|
|
267
|
+
else {
|
|
268
|
+
findings.push({
|
|
269
|
+
status: 'warn',
|
|
270
|
+
message: `${incomplete.length}/${skills.length} skill(s) missing id or description`,
|
|
271
|
+
hint: 'Each entry in skills[] should include both an id and a description so agents can address it and reason about its purpose.',
|
|
272
|
+
learnMoreUrl: guideUrl(meta.id, 'incomplete-skills'),
|
|
273
|
+
});
|
|
274
|
+
score -= 5;
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
else if (Array.isArray(data.skills)) {
|
|
278
|
+
findings.push({
|
|
279
|
+
status: 'warn',
|
|
280
|
+
message: 'Skills array is empty',
|
|
281
|
+
hint: 'Add at least one skill to the skills array describing what your agent can do.',
|
|
282
|
+
learnMoreUrl: guideUrl(meta.id, 'empty-skills'),
|
|
283
|
+
});
|
|
284
|
+
score -= 10;
|
|
285
|
+
}
|
|
286
|
+
// `authentication` was replaced by `securitySchemes` in A2A 0.2.x. A card
|
|
287
|
+
// still carrying it is describing its auth to nobody.
|
|
288
|
+
if (data.authentication !== undefined) {
|
|
289
|
+
findings.push({
|
|
290
|
+
status: 'warn',
|
|
291
|
+
message: 'Card uses "authentication", removed from the A2A spec',
|
|
292
|
+
hint: 'Replace it with "securitySchemes" (OpenAPI-style scheme definitions) plus the matching security requirements.',
|
|
293
|
+
learnMoreUrl: guideUrl(meta.id, 'deprecated-authentication'),
|
|
294
|
+
});
|
|
295
|
+
score -= 5;
|
|
296
|
+
}
|
|
297
|
+
if (data.securitySchemes !== undefined) {
|
|
298
|
+
findings.push({ status: 'pass', message: 'securitySchemes declared' });
|
|
299
|
+
}
|
|
300
|
+
if (Array.isArray(data.signatures) && data.signatures.length > 0) {
|
|
301
|
+
findings.push({
|
|
302
|
+
status: 'pass',
|
|
303
|
+
message: `Card carries ${data.signatures.length} JWS signature(s)`,
|
|
304
|
+
detail: 'Signed cards let a client verify the card was issued by the domain it claims.',
|
|
305
|
+
});
|
|
306
|
+
}
|
|
307
|
+
const capabilities = (data.capabilities ?? {});
|
|
308
|
+
const extensions = Array.isArray(capabilities.extensions)
|
|
309
|
+
? capabilities.extensions
|
|
310
|
+
: [];
|
|
311
|
+
if (extensions.length > 0) {
|
|
312
|
+
findings.push({
|
|
313
|
+
status: 'pass',
|
|
314
|
+
message: `${extensions.length} capability extension(s) declared`,
|
|
315
|
+
detail: extensions.map((e) => String(e.uri ?? '(no uri)')).join(', '),
|
|
316
|
+
});
|
|
317
|
+
}
|
|
318
|
+
const optionalFields = ['provider', 'documentationUrl', 'iconUrl'];
|
|
319
|
+
const presentOptional = optionalFields.filter((f) => data[f] !== undefined);
|
|
320
|
+
if (presentOptional.length === optionalFields.length) {
|
|
321
|
+
findings.push({
|
|
322
|
+
status: 'pass',
|
|
323
|
+
message: 'All optional descriptive fields present (provider, documentationUrl, iconUrl)',
|
|
324
|
+
});
|
|
325
|
+
}
|
|
326
|
+
else if (presentOptional.length > 0) {
|
|
327
|
+
findings.push({
|
|
328
|
+
status: 'pass',
|
|
329
|
+
message: `${presentOptional.length}/${optionalFields.length} optional descriptive fields present`,
|
|
330
|
+
});
|
|
331
|
+
}
|
|
332
|
+
else {
|
|
333
|
+
findings.push({
|
|
334
|
+
status: 'warn',
|
|
335
|
+
message: 'No optional descriptive fields (provider, documentationUrl, iconUrl)',
|
|
336
|
+
hint: 'Add provider (who runs this agent), documentationUrl (where to read more) and iconUrl. Agents surface these to users when choosing between agents.',
|
|
337
|
+
learnMoreUrl: guideUrl(meta.id, 'missing-optional'),
|
|
338
|
+
});
|
|
339
|
+
score -= 5;
|
|
340
|
+
}
|
|
341
|
+
return score;
|
|
342
|
+
}
|
|
343
|
+
/** Returns `true` when the two URLs share host, `false` if hosts differ, `null` on parse error. */
|
|
344
|
+
function sameHost(a, b) {
|
|
345
|
+
try {
|
|
346
|
+
return new URL(a).host.toLowerCase() === new URL(b).host.toLowerCase();
|
|
347
|
+
}
|
|
348
|
+
catch {
|
|
349
|
+
return null;
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
//# sourceMappingURL=agent-card.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent-card.js","sourceRoot":"","sources":["../../src/checks/agent-card.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import type { CheckContext, CheckResult, CheckMeta } from '../types.js';
|
|
2
|
+
/**
|
|
3
|
+
* "agent-operability" — can a browser agent work this page, or only look at it?
|
|
4
|
+
*
|
|
5
|
+
* Every major browser agent reads the accessibility tree, not the pixels.
|
|
6
|
+
* Anthropic's browser tool returns the page as `link "Documentation" [ref_1]`,
|
|
7
|
+
* `button "Search" [ref_4]`; ChatGPT Atlas is documented as using ARIA roles and
|
|
8
|
+
* labels; Chrome's own guidance for agent-friendly sites asks for real
|
|
9
|
+
* `<button>` and `<a>` elements, labelled controls and stable layout.
|
|
10
|
+
*
|
|
11
|
+
* Which means the failure mode is specific and old: a `<div onclick>` styled to
|
|
12
|
+
* look like a button has no name and no role, so it does not appear in the tree
|
|
13
|
+
* at all. The agent does not see a button it cannot press. It sees nothing.
|
|
14
|
+
* An unlabelled input is a box the agent must guess the meaning of, which is how
|
|
15
|
+
* an email address ends up in a search field.
|
|
16
|
+
*
|
|
17
|
+
* This is the accessibility work that has been the right thing to do for twenty
|
|
18
|
+
* years, with a new and less patient audience. Every finding here is a genuine
|
|
19
|
+
* accessibility defect too.
|
|
20
|
+
*
|
|
21
|
+
* The check is a static approximation and says so: it reads markup, not a
|
|
22
|
+
* rendered accessibility tree, so it cannot see labels attached by script or
|
|
23
|
+
* roles computed at runtime. It reports proportions rather than absolutes for
|
|
24
|
+
* that reason.
|
|
25
|
+
*/
|
|
26
|
+
export declare const meta: CheckMeta;
|
|
27
|
+
export interface OperabilityReport {
|
|
28
|
+
buttons: {
|
|
29
|
+
total: number;
|
|
30
|
+
named: number;
|
|
31
|
+
unnamed: string[];
|
|
32
|
+
};
|
|
33
|
+
controls: {
|
|
34
|
+
total: number;
|
|
35
|
+
labelled: number;
|
|
36
|
+
unlabelled: string[];
|
|
37
|
+
};
|
|
38
|
+
fakeInteractive: number;
|
|
39
|
+
deadLinks: {
|
|
40
|
+
noHref: number;
|
|
41
|
+
javascriptHref: number;
|
|
42
|
+
};
|
|
43
|
+
tables: {
|
|
44
|
+
total: number;
|
|
45
|
+
withHeaders: number;
|
|
46
|
+
};
|
|
47
|
+
media: {
|
|
48
|
+
total: number;
|
|
49
|
+
sized: number;
|
|
50
|
+
};
|
|
51
|
+
iframes: {
|
|
52
|
+
total: number;
|
|
53
|
+
titled: number;
|
|
54
|
+
};
|
|
55
|
+
timeElements: {
|
|
56
|
+
total: number;
|
|
57
|
+
withDatetime: number;
|
|
58
|
+
};
|
|
59
|
+
headingSkips: string[];
|
|
60
|
+
lang: string | null;
|
|
61
|
+
blockers: string[];
|
|
62
|
+
}
|
|
63
|
+
/** Analyse the markup for everything a browser agent needs in order to act. */
|
|
64
|
+
export declare function analyseOperability(rawHtml: string): OperabilityReport;
|
|
65
|
+
export default function check(ctx: CheckContext): Promise<CheckResult>;
|
|
66
|
+
//# sourceMappingURL=agent-operability.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent-operability.d.ts","sourceRoot":"","sources":["../../src/checks/agent-operability.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,WAAW,EAAE,SAAS,EAAW,MAAM,aAAa,CAAC;AAIjF;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,eAAO,MAAM,IAAI,EAAE,SAKlB,CAAC;AAKF,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC;IAC7D,QAAQ,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC;IACpE,eAAe,EAAE,MAAM,CAAC;IACxB,SAAS,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,cAAc,EAAE,MAAM,CAAA;KAAE,CAAC;IACtD,MAAM,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAA;KAAE,CAAC;IAC/C,KAAK,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;IACxC,OAAO,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IAC3C,YAAY,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,MAAM,CAAA;KAAE,CAAC;IACtD,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAoCD,+EAA+E;AAC/E,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,iBAAiB,CAqIrE;AAED,wBAA8B,KAAK,CAAC,GAAG,EAAE,YAAY,GAAG,OAAO,CAAC,WAAW,CAAC,CA8B3E"}
|