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,383 @@
|
|
|
1
|
+
import { guideUrl } from '../guide-urls.js';
|
|
2
|
+
import { buildResult } from './utils.js';
|
|
3
|
+
import { getAttribute, getTagAttribute } from './html-utils.js';
|
|
4
|
+
/**
|
|
5
|
+
* "agent-operability" — can a browser agent work this page, or only look at it?
|
|
6
|
+
*
|
|
7
|
+
* Every major browser agent reads the accessibility tree, not the pixels.
|
|
8
|
+
* Anthropic's browser tool returns the page as `link "Documentation" [ref_1]`,
|
|
9
|
+
* `button "Search" [ref_4]`; ChatGPT Atlas is documented as using ARIA roles and
|
|
10
|
+
* labels; Chrome's own guidance for agent-friendly sites asks for real
|
|
11
|
+
* `<button>` and `<a>` elements, labelled controls and stable layout.
|
|
12
|
+
*
|
|
13
|
+
* Which means the failure mode is specific and old: a `<div onclick>` styled to
|
|
14
|
+
* look like a button has no name and no role, so it does not appear in the tree
|
|
15
|
+
* at all. The agent does not see a button it cannot press. It sees nothing.
|
|
16
|
+
* An unlabelled input is a box the agent must guess the meaning of, which is how
|
|
17
|
+
* an email address ends up in a search field.
|
|
18
|
+
*
|
|
19
|
+
* This is the accessibility work that has been the right thing to do for twenty
|
|
20
|
+
* years, with a new and less patient audience. Every finding here is a genuine
|
|
21
|
+
* accessibility defect too.
|
|
22
|
+
*
|
|
23
|
+
* The check is a static approximation and says so: it reads markup, not a
|
|
24
|
+
* rendered accessibility tree, so it cannot see labels attached by script or
|
|
25
|
+
* roles computed at runtime. It reports proportions rather than absolutes for
|
|
26
|
+
* that reason.
|
|
27
|
+
*/
|
|
28
|
+
export const meta = {
|
|
29
|
+
id: 'agent-operability',
|
|
30
|
+
name: 'Agent Operability',
|
|
31
|
+
description: 'Checks whether interactive elements are nameable and operable by a browser agent',
|
|
32
|
+
category: 'content',
|
|
33
|
+
};
|
|
34
|
+
/** Below this share of named controls, the page is guesswork for an agent. */
|
|
35
|
+
const NAMED_CONTROL_THRESHOLD = 0.9;
|
|
36
|
+
/** Strip comments, scripts and styles: their contents are not part of the page. */
|
|
37
|
+
function contentOf(html) {
|
|
38
|
+
return html
|
|
39
|
+
.replace(/<!--[\s\S]*?-->/g, ' ')
|
|
40
|
+
.replace(/<script\b[^>]*>[\s\S]*?<\/script>/gi, ' ')
|
|
41
|
+
.replace(/<style\b[^>]*>[\s\S]*?<\/style>/gi, ' ');
|
|
42
|
+
}
|
|
43
|
+
/** Text content of an element, with tags removed. */
|
|
44
|
+
function innerText(html) {
|
|
45
|
+
return html
|
|
46
|
+
.replace(/<[^>]+>/g, ' ')
|
|
47
|
+
.replace(/\s+/g, ' ')
|
|
48
|
+
.trim();
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Whether an element has something an accessibility tree can use as its name:
|
|
52
|
+
* visible text, an ARIA label, a title, or an image with alt text.
|
|
53
|
+
*/
|
|
54
|
+
function hasAccessibleName(attrs, inner) {
|
|
55
|
+
if ((getAttribute(attrs, 'aria-label') ?? '').trim() !== '')
|
|
56
|
+
return true;
|
|
57
|
+
if ((getAttribute(attrs, 'aria-labelledby') ?? '').trim() !== '')
|
|
58
|
+
return true;
|
|
59
|
+
if ((getAttribute(attrs, 'title') ?? '').trim() !== '')
|
|
60
|
+
return true;
|
|
61
|
+
if (innerText(inner) !== '')
|
|
62
|
+
return true;
|
|
63
|
+
// An icon-only control is named by its image's alt text.
|
|
64
|
+
for (const img of inner.matchAll(/<img\b([^>]*)>/gi)) {
|
|
65
|
+
if ((getAttribute(img[1], 'alt') ?? '').trim() !== '')
|
|
66
|
+
return true;
|
|
67
|
+
}
|
|
68
|
+
// An inline SVG is named by its <title> child.
|
|
69
|
+
if (/<svg\b[^>]*>[\s\S]*?<title\b[^>]*>\s*\S/i.test(inner))
|
|
70
|
+
return true;
|
|
71
|
+
return false;
|
|
72
|
+
}
|
|
73
|
+
/** Analyse the markup for everything a browser agent needs in order to act. */
|
|
74
|
+
export function analyseOperability(rawHtml) {
|
|
75
|
+
const html = contentOf(rawHtml);
|
|
76
|
+
const report = {
|
|
77
|
+
buttons: { total: 0, named: 0, unnamed: [] },
|
|
78
|
+
controls: { total: 0, labelled: 0, unlabelled: [] },
|
|
79
|
+
fakeInteractive: 0,
|
|
80
|
+
deadLinks: { noHref: 0, javascriptHref: 0 },
|
|
81
|
+
tables: { total: 0, withHeaders: 0 },
|
|
82
|
+
media: { total: 0, sized: 0 },
|
|
83
|
+
iframes: { total: 0, titled: 0 },
|
|
84
|
+
timeElements: { total: 0, withDatetime: 0 },
|
|
85
|
+
headingSkips: [],
|
|
86
|
+
lang: getTagAttribute(rawHtml, 'html', 'lang'),
|
|
87
|
+
blockers: [],
|
|
88
|
+
};
|
|
89
|
+
/* Buttons and links: can the agent name what it is about to press? */
|
|
90
|
+
for (const m of html.matchAll(/<button\b([^>]*)>([\s\S]*?)<\/button>/gi)) {
|
|
91
|
+
report.buttons.total++;
|
|
92
|
+
if (hasAccessibleName(m[1], m[2]))
|
|
93
|
+
report.buttons.named++;
|
|
94
|
+
else
|
|
95
|
+
report.buttons.unnamed.push('<button>');
|
|
96
|
+
}
|
|
97
|
+
for (const m of html.matchAll(/<input\b([^>]*)>/gi)) {
|
|
98
|
+
const type = (getAttribute(m[1], 'type') ?? '').toLowerCase();
|
|
99
|
+
if (type !== 'submit' && type !== 'button' && type !== 'reset' && type !== 'image')
|
|
100
|
+
continue;
|
|
101
|
+
report.buttons.total++;
|
|
102
|
+
const named = (getAttribute(m[1], 'value') ?? '').trim() !== '' ||
|
|
103
|
+
(getAttribute(m[1], 'aria-label') ?? '').trim() !== '' ||
|
|
104
|
+
(getAttribute(m[1], 'alt') ?? '').trim() !== '';
|
|
105
|
+
if (named)
|
|
106
|
+
report.buttons.named++;
|
|
107
|
+
else
|
|
108
|
+
report.buttons.unnamed.push(`<input type="${type}">`);
|
|
109
|
+
}
|
|
110
|
+
for (const m of html.matchAll(/<a\b([^>]*)>([\s\S]*?)<\/a>/gi)) {
|
|
111
|
+
const href = getAttribute(m[1], 'href');
|
|
112
|
+
if (href === null) {
|
|
113
|
+
report.deadLinks.noHref++;
|
|
114
|
+
continue;
|
|
115
|
+
}
|
|
116
|
+
if (/^javascript:/i.test(href.trim())) {
|
|
117
|
+
report.deadLinks.javascriptHref++;
|
|
118
|
+
continue;
|
|
119
|
+
}
|
|
120
|
+
report.buttons.total++;
|
|
121
|
+
if (hasAccessibleName(m[1], m[2]))
|
|
122
|
+
report.buttons.named++;
|
|
123
|
+
else
|
|
124
|
+
report.buttons.unnamed.push(`<a href="${href.slice(0, 40)}">`);
|
|
125
|
+
}
|
|
126
|
+
/* Elements dressed as controls that no tree will report. */
|
|
127
|
+
for (const m of html.matchAll(/<(div|span|li|td)\b([^>]*)>/gi)) {
|
|
128
|
+
const attrs = m[2];
|
|
129
|
+
if (!/\bonclick\s*=/i.test(attrs))
|
|
130
|
+
continue;
|
|
131
|
+
const role = (getAttribute(attrs, 'role') ?? '').toLowerCase();
|
|
132
|
+
const tabindex = getAttribute(attrs, 'tabindex');
|
|
133
|
+
if (role === '' || tabindex === null)
|
|
134
|
+
report.fakeInteractive++;
|
|
135
|
+
}
|
|
136
|
+
/* Form controls: does the agent know what each box is for? */
|
|
137
|
+
const labelFor = new Set();
|
|
138
|
+
for (const m of html.matchAll(/<label\b([^>]*)>/gi)) {
|
|
139
|
+
const forAttr = getAttribute(m[1], 'for');
|
|
140
|
+
if (forAttr !== null)
|
|
141
|
+
labelFor.add(forAttr);
|
|
142
|
+
}
|
|
143
|
+
const wrappedIds = new Set();
|
|
144
|
+
for (const m of html.matchAll(/<label\b[^>]*>([\s\S]*?)<\/label>/gi)) {
|
|
145
|
+
for (const inner of m[1].matchAll(/<(input|select|textarea)\b([^>]*)>/gi)) {
|
|
146
|
+
const id = getAttribute(inner[2], 'id');
|
|
147
|
+
wrappedIds.add(id ?? `__wrapped-${wrappedIds.size}`);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
const wrappedCount = wrappedIds.size;
|
|
151
|
+
let wrappedSeen = 0;
|
|
152
|
+
for (const m of html.matchAll(/<(input|select|textarea)\b([^>]*)>/gi)) {
|
|
153
|
+
const attrs = m[2];
|
|
154
|
+
const type = (getAttribute(attrs, 'type') ?? '').toLowerCase();
|
|
155
|
+
if (['hidden', 'submit', 'button', 'reset', 'image'].includes(type))
|
|
156
|
+
continue;
|
|
157
|
+
report.controls.total++;
|
|
158
|
+
const id = getAttribute(attrs, 'id');
|
|
159
|
+
const labelled = (id !== null && labelFor.has(id)) ||
|
|
160
|
+
(id !== null && wrappedIds.has(id)) ||
|
|
161
|
+
(getAttribute(attrs, 'aria-label') ?? '').trim() !== '' ||
|
|
162
|
+
(getAttribute(attrs, 'aria-labelledby') ?? '').trim() !== '' ||
|
|
163
|
+
(getAttribute(attrs, 'title') ?? '').trim() !== '' ||
|
|
164
|
+
(id === null && wrappedSeen++ < wrappedCount);
|
|
165
|
+
if (labelled)
|
|
166
|
+
report.controls.labelled++;
|
|
167
|
+
else
|
|
168
|
+
report.controls.unlabelled.push(`<${m[1]}${type ? ` type="${type}"` : ''}>`);
|
|
169
|
+
}
|
|
170
|
+
/* Structure an agent reads meaning from. */
|
|
171
|
+
for (const m of html.matchAll(/<table\b[^>]*>([\s\S]*?)<\/table>/gi)) {
|
|
172
|
+
report.tables.total++;
|
|
173
|
+
if (/<th\b/i.test(m[1]))
|
|
174
|
+
report.tables.withHeaders++;
|
|
175
|
+
}
|
|
176
|
+
for (const m of html.matchAll(/<(img|iframe|video)\b([^>]*)>/gi)) {
|
|
177
|
+
const attrs = m[2];
|
|
178
|
+
if (m[1].toLowerCase() === 'iframe') {
|
|
179
|
+
report.iframes.total++;
|
|
180
|
+
if ((getAttribute(attrs, 'title') ?? '').trim() !== '')
|
|
181
|
+
report.iframes.titled++;
|
|
182
|
+
}
|
|
183
|
+
report.media.total++;
|
|
184
|
+
const sized = (getAttribute(attrs, 'width') !== null && getAttribute(attrs, 'height') !== null) ||
|
|
185
|
+
/aspect-ratio\s*:/i.test(getAttribute(attrs, 'style') ?? '');
|
|
186
|
+
if (sized)
|
|
187
|
+
report.media.sized++;
|
|
188
|
+
}
|
|
189
|
+
for (const m of html.matchAll(/<time\b([^>]*)>/gi)) {
|
|
190
|
+
report.timeElements.total++;
|
|
191
|
+
if (getAttribute(m[1], 'datetime') !== null)
|
|
192
|
+
report.timeElements.withDatetime++;
|
|
193
|
+
}
|
|
194
|
+
/* Heading hierarchy: an agent outlines a page from it. */
|
|
195
|
+
const levels = [...html.matchAll(/<h([1-6])\b/gi)].map((m) => Number(m[1]));
|
|
196
|
+
for (let i = 1; i < levels.length; i++) {
|
|
197
|
+
if (levels[i] - levels[i - 1] > 1) {
|
|
198
|
+
report.headingSkips.push(`h${levels[i - 1]} → h${levels[i]}`);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
/* Things that stop an agent before it starts. */
|
|
202
|
+
if (/<meta\b[^>]+http-equiv\s*=\s*["']refresh["']/i.test(rawHtml)) {
|
|
203
|
+
report.blockers.push('<meta http-equiv="refresh"> on the page');
|
|
204
|
+
}
|
|
205
|
+
if (/www\.google\.com\/recaptcha\/|js\.hcaptcha\.com\/|challenges\.cloudflare\.com\/turnstile/i.test(rawHtml)) {
|
|
206
|
+
report.blockers.push('a CAPTCHA widget on the entry page');
|
|
207
|
+
}
|
|
208
|
+
return report;
|
|
209
|
+
}
|
|
210
|
+
export default async function check(ctx) {
|
|
211
|
+
const start = performance.now();
|
|
212
|
+
const findings = [];
|
|
213
|
+
let score = 100;
|
|
214
|
+
if (!ctx.html || ctx.html.trim().length === 0) {
|
|
215
|
+
findings.push({
|
|
216
|
+
status: 'fail',
|
|
217
|
+
message: 'Homepage HTML unavailable — cannot assess operability',
|
|
218
|
+
learnMoreUrl: guideUrl(meta.id, 'no-html'),
|
|
219
|
+
});
|
|
220
|
+
return buildResult(meta, 0, findings, start);
|
|
221
|
+
}
|
|
222
|
+
const r = analyseOperability(ctx.html);
|
|
223
|
+
score = reportNames(r, findings, score);
|
|
224
|
+
score = reportFakeControls(r, findings, score);
|
|
225
|
+
score = reportStructure(r, findings, score);
|
|
226
|
+
score = reportBlockers(r, findings, score);
|
|
227
|
+
findings.push({
|
|
228
|
+
status: 'pass',
|
|
229
|
+
message: 'Method note: this reads markup, not a rendered accessibility tree',
|
|
230
|
+
detail: 'Labels attached by script and roles computed at runtime are invisible here, so treat low proportions as a ' +
|
|
231
|
+
'prompt to check the real tree rather than as a count. Every finding is also a plain accessibility defect.',
|
|
232
|
+
});
|
|
233
|
+
return buildResult(meta, score, findings, start);
|
|
234
|
+
}
|
|
235
|
+
/** Can the agent name what it is about to click or type into? */
|
|
236
|
+
function reportNames(r, findings, score) {
|
|
237
|
+
if (r.buttons.total > 0) {
|
|
238
|
+
const ratio = r.buttons.named / r.buttons.total;
|
|
239
|
+
if (ratio >= NAMED_CONTROL_THRESHOLD) {
|
|
240
|
+
findings.push({
|
|
241
|
+
status: 'pass',
|
|
242
|
+
message: `${r.buttons.named}/${r.buttons.total} interactive elements have an accessible name`,
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
else {
|
|
246
|
+
findings.push({
|
|
247
|
+
status: 'warn',
|
|
248
|
+
message: `${r.buttons.total - r.buttons.named}/${r.buttons.total} buttons or links have no accessible name`,
|
|
249
|
+
detail: [...new Set(r.buttons.unnamed)].slice(0, 8).join(', '),
|
|
250
|
+
hint: 'An agent addresses a control by its name: it presses button "Search", not the button at 412×88. ' +
|
|
251
|
+
'Give each one visible text, an aria-label, or an image with alt text.',
|
|
252
|
+
learnMoreUrl: guideUrl(meta.id, 'unnamed-controls'),
|
|
253
|
+
});
|
|
254
|
+
score -= 20;
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
if (r.controls.total > 0) {
|
|
258
|
+
const ratio = r.controls.labelled / r.controls.total;
|
|
259
|
+
if (ratio >= NAMED_CONTROL_THRESHOLD) {
|
|
260
|
+
findings.push({
|
|
261
|
+
status: 'pass',
|
|
262
|
+
message: `${r.controls.labelled}/${r.controls.total} form controls are labelled`,
|
|
263
|
+
});
|
|
264
|
+
}
|
|
265
|
+
else {
|
|
266
|
+
findings.push({
|
|
267
|
+
status: 'warn',
|
|
268
|
+
message: `${r.controls.total - r.controls.labelled}/${r.controls.total} form controls have no label`,
|
|
269
|
+
detail: [...new Set(r.controls.unlabelled)].slice(0, 8).join(', '),
|
|
270
|
+
hint: 'An unlabelled box is one the agent has to guess the meaning of, which is how an email address ends up in ' +
|
|
271
|
+
'a search field. Use <label for>, wrap the control in a label, or add aria-label.',
|
|
272
|
+
learnMoreUrl: guideUrl(meta.id, 'unlabelled-controls'),
|
|
273
|
+
});
|
|
274
|
+
score -= 20;
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
return score;
|
|
278
|
+
}
|
|
279
|
+
/** Controls that exist visually but not in the tree the agent reads. */
|
|
280
|
+
function reportFakeControls(r, findings, score) {
|
|
281
|
+
if (r.fakeInteractive > 0) {
|
|
282
|
+
findings.push({
|
|
283
|
+
status: 'warn',
|
|
284
|
+
message: `${r.fakeInteractive} clickable element(s) are not buttons or links`,
|
|
285
|
+
hint: 'A <div onclick> styled as a button has no role and no name, so it does not appear in the accessibility tree ' +
|
|
286
|
+
'at all. The agent does not see a button it cannot press; it sees nothing. Use <button>, or add role="button" ' +
|
|
287
|
+
'and tabindex="0".',
|
|
288
|
+
learnMoreUrl: guideUrl(meta.id, 'fake-controls'),
|
|
289
|
+
});
|
|
290
|
+
score -= 15;
|
|
291
|
+
}
|
|
292
|
+
if (r.deadLinks.javascriptHref > 0 || r.deadLinks.noHref > 0) {
|
|
293
|
+
findings.push({
|
|
294
|
+
status: 'warn',
|
|
295
|
+
message: `${r.deadLinks.javascriptHref + r.deadLinks.noHref} link(s) lead nowhere without JavaScript`,
|
|
296
|
+
detail: `${r.deadLinks.noHref} with no href, ${r.deadLinks.javascriptHref} with a javascript: href`,
|
|
297
|
+
hint: 'A link with no destination cannot be followed by a fetch-only agent and cannot be opened in a new tab by a ' +
|
|
298
|
+
'browsing one. Give it a real href, or make it a <button> if it is an action rather than a destination.',
|
|
299
|
+
learnMoreUrl: guideUrl(meta.id, 'dead-links'),
|
|
300
|
+
});
|
|
301
|
+
score -= 10;
|
|
302
|
+
}
|
|
303
|
+
return score;
|
|
304
|
+
}
|
|
305
|
+
/** Structure an agent extracts meaning from. */
|
|
306
|
+
function reportStructure(r, findings, score) {
|
|
307
|
+
if (r.tables.total > 0 && r.tables.withHeaders < r.tables.total) {
|
|
308
|
+
findings.push({
|
|
309
|
+
status: 'warn',
|
|
310
|
+
message: `${r.tables.total - r.tables.withHeaders}/${r.tables.total} tables have no header cells`,
|
|
311
|
+
hint: 'Without <th>, a table is a grid of strings: an agent reading a price table cannot tell which column is the ' +
|
|
312
|
+
'price. Add header cells, with scope on anything non-trivial.',
|
|
313
|
+
learnMoreUrl: guideUrl(meta.id, 'tables-no-headers'),
|
|
314
|
+
});
|
|
315
|
+
score -= 10;
|
|
316
|
+
}
|
|
317
|
+
if (r.iframes.total > 0 && r.iframes.titled < r.iframes.total) {
|
|
318
|
+
findings.push({
|
|
319
|
+
status: 'warn',
|
|
320
|
+
message: `${r.iframes.total - r.iframes.titled}/${r.iframes.total} iframes have no title`,
|
|
321
|
+
hint: 'An untitled frame is an opaque region. A title tells an agent whether it is worth entering.',
|
|
322
|
+
learnMoreUrl: guideUrl(meta.id, 'iframe-no-title'),
|
|
323
|
+
});
|
|
324
|
+
score -= 5;
|
|
325
|
+
}
|
|
326
|
+
if (r.timeElements.total > 0 && r.timeElements.withDatetime < r.timeElements.total) {
|
|
327
|
+
findings.push({
|
|
328
|
+
status: 'warn',
|
|
329
|
+
message: `${r.timeElements.total - r.timeElements.withDatetime}/${r.timeElements.total} <time> elements have no datetime attribute`,
|
|
330
|
+
hint: 'Without datetime, "last Tuesday" is unparseable. Add the machine-readable value: <time datetime="2026-09-04">.',
|
|
331
|
+
learnMoreUrl: guideUrl(meta.id, 'time-no-datetime'),
|
|
332
|
+
});
|
|
333
|
+
score -= 5;
|
|
334
|
+
}
|
|
335
|
+
if (r.headingSkips.length > 0) {
|
|
336
|
+
findings.push({
|
|
337
|
+
status: 'warn',
|
|
338
|
+
message: `Heading hierarchy skips ${r.headingSkips.length} level(s)`,
|
|
339
|
+
detail: [...new Set(r.headingSkips)].join(', '),
|
|
340
|
+
hint: 'Agents outline a page from its headings. A skipped level puts a section under the wrong parent, so a summary attributes it to the wrong topic.',
|
|
341
|
+
learnMoreUrl: guideUrl(meta.id, 'heading-skips'),
|
|
342
|
+
});
|
|
343
|
+
score -= 5;
|
|
344
|
+
}
|
|
345
|
+
if (r.media.total >= 3) {
|
|
346
|
+
const ratio = r.media.sized / r.media.total;
|
|
347
|
+
if (ratio < 0.5) {
|
|
348
|
+
findings.push({
|
|
349
|
+
status: 'warn',
|
|
350
|
+
message: `${r.media.total - r.media.sized}/${r.media.total} images, frames or videos have no declared dimensions`,
|
|
351
|
+
hint: 'Undeclared dimensions shift the layout as media loads. An agent working from a screenshot clicks where the ' +
|
|
352
|
+
'button was a moment ago. Set width and height, or aspect-ratio.',
|
|
353
|
+
learnMoreUrl: guideUrl(meta.id, 'unsized-media'),
|
|
354
|
+
});
|
|
355
|
+
score -= 5;
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
if (r.lang === null || r.lang.trim() === '') {
|
|
359
|
+
findings.push({
|
|
360
|
+
status: 'warn',
|
|
361
|
+
message: 'No lang attribute on <html>',
|
|
362
|
+
hint: 'Agents route, translate and pick a voice from this. Add lang="en" or whichever applies.',
|
|
363
|
+
learnMoreUrl: guideUrl(meta.id, 'no-lang'),
|
|
364
|
+
});
|
|
365
|
+
score -= 5;
|
|
366
|
+
}
|
|
367
|
+
return score;
|
|
368
|
+
}
|
|
369
|
+
/** Things that stop an agent at the door. */
|
|
370
|
+
function reportBlockers(r, findings, score) {
|
|
371
|
+
if (r.blockers.length === 0)
|
|
372
|
+
return score;
|
|
373
|
+
findings.push({
|
|
374
|
+
status: 'warn',
|
|
375
|
+
message: `${r.blockers.length} obstacle(s) on the entry page`,
|
|
376
|
+
detail: r.blockers.join('; '),
|
|
377
|
+
hint: 'A CAPTCHA or a meta refresh on the landing page stops an agent before it reaches any content. If bot ' +
|
|
378
|
+
'protection is needed, apply it to the actions that need it, not to reading a page.',
|
|
379
|
+
learnMoreUrl: guideUrl(meta.id, 'entry-blockers'),
|
|
380
|
+
});
|
|
381
|
+
return score - 15;
|
|
382
|
+
}
|
|
383
|
+
//# sourceMappingURL=agent-operability.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent-operability.js","sourceRoot":"","sources":["../../src/checks/agent-operability.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { CheckContext, CheckResult, CheckMeta } from '../types.js';
|
|
2
|
+
/**
|
|
3
|
+
* "agent-skills" — procedural instructions an agent can install, rather than
|
|
4
|
+
* prose it has to infer from.
|
|
5
|
+
*
|
|
6
|
+
* llms.txt answers "what is on this site". A skill answers "how do I do the
|
|
7
|
+
* thing this site is for" — the setup steps, the argument shapes, the mistakes
|
|
8
|
+
* to avoid — in a format a coding agent installs and follows. For a
|
|
9
|
+
* documentation site or a developer tool, that is the difference between an
|
|
10
|
+
* agent reading about your API and an agent using it correctly.
|
|
11
|
+
*
|
|
12
|
+
* Discovery has not settled. Cloudflare's RFC specifies
|
|
13
|
+
* `/.well-known/agent-skills/index.json`; Mintlify and Docus ship the shorter
|
|
14
|
+
* `/.well-known/skills/index.json`; a single-skill site may just serve
|
|
15
|
+
* `/skill.md`. All three are probed.
|
|
16
|
+
*
|
|
17
|
+
* The check is conditional. A restaurant's website has no skills to publish,
|
|
18
|
+
* and scoring it zero would say something false. It reports N/A unless the site
|
|
19
|
+
* shows developer-facing signals — a docs section, an llms.txt, or an API
|
|
20
|
+
* description — in which case a missing skills index is worth mentioning.
|
|
21
|
+
*/
|
|
22
|
+
export declare const meta: CheckMeta;
|
|
23
|
+
export default function check(ctx: CheckContext): Promise<CheckResult>;
|
|
24
|
+
//# sourceMappingURL=agent-skills.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent-skills.d.ts","sourceRoot":"","sources":["../../src/checks/agent-skills.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,WAAW,EAAE,SAAS,EAA0B,MAAM,aAAa,CAAC;AAMhG;;;;;;;;;;;;;;;;;;;GAmBG;AACH,eAAO,MAAM,IAAI,EAAE,SAKlB,CAAC;AA8BF,wBAA8B,KAAK,CAAC,GAAG,EAAE,YAAY,GAAG,OAAO,CAAC,WAAW,CAAC,CAoC3E"}
|