auditmysite 1.4.0

Resource-efficient WCAG 2.1 AA (+ select 2.2 criteria) Accessibility Checker in Rust
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
# auditmysite Architecture

This document describes the current implementation. For older browser-design explorations and proposals that are not fully implemented, see [browser-architecture.md](browser-architecture.md).

## Overview

auditmysite is a WCAG 2.1 AA (plus select 2.2 criteria) accessibility checker written in Rust. It uses Chrome DevTools Protocol (CDP) to extract the browser's native Accessibility Tree and analyze it for violations.

```
┌─────────────────────────────────────────────────────────────────┐
│                         CLI (main.rs)                           │
├─────────────────────────────────────────────────────────────────┤
│                      Audit Pipeline                             │
│  ┌──────────┐  ┌──────────┐  ┌──────────┐  ┌──────────┐        │
│  │ Browser  │→ │ AXTree   │→ │  WCAG    │→ │  Report  │        │
│  │ Manager  │  │ Extract  │  │  Engine  │  │  Output  │        │
│  └──────────┘  └──────────┘  └──────────┘  └──────────┘        │
└─────────────────────────────────────────────────────────────────┘
```

## Module Structure

```
src/
├── main.rs              # CLI entry point
├── lib.rs               # Library exports
├── error.rs             # Error types (AuditError)
├── util.rs              # Utility functions
│
├── cli/                 # Command-line interface
│   ├── args.rs          # Clap argument parsing (Args, WcagLevel, OutputFormat, AnnexKind)
│   ├── config.rs        # auditmysite.toml config support
│   ├── commands.rs      # Subcommand handlers (browser, doctor, plan)
│   ├── runners.rs       # Mode runners (single, batch, compare)
│   ├── report_writers.rs # Output dispatch (single/batch/comparison)
│   ├── output_paths.rs  # File path generation for reports
│   ├── plan.rs          # Pre-audit plan/banner printing
│   ├── doctor.rs        # `auditmysite doctor` diagnostics
│   └── sitemap_suggest.rs # Sitemap discovery + interactive prompt
│
├── browser/             # Chrome/Chromium management
│   ├── mod.rs
│   ├── detection.rs     # Find Chrome installation
│   ├── installer.rs     # Download browser via `browser install`
│   ├── resolver.rs      # Browser path resolution
│   ├── manager.rs       # Browser lifecycle (launch, navigate, close)
│   ├── pool.rs          # Page pool for concurrent audits
│   ├── consent.rs       # CMP cookie injection + consent-banner dismissal
│   ├── throttle.rs      # CPU/network throttling profiles
│   ├── registry.rs      # Known-browser registry
│   └── mock.rs          # Test-only mock browser
│
├── accessibility/       # Accessibility Tree handling
│   ├── mod.rs
│   ├── extractor.rs     # CDP AXTree extraction
│   ├── tree.rs          # AXNode, AXTree structures
│   ├── styles.rs        # Computed style extraction
│   ├── enrichment.rs    # AXNode enrichment (roles, computed properties)
│   ├── code_gen.rs      # Selector/snippet generation for findings
│   ├── element_capture.rs # CDP screenshot capture + element highlighting for evidence
│   ├── snapshot.rs      # Cached snapshot (de)serialization
│   └── diff.rs          # Snapshot diffing for regression comparisons
│
├── wcag/                # WCAG rule engine
│   ├── mod.rs
│   ├── engine.rs        # Rule orchestration
│   ├── types.rs         # Violation, Severity, WcagResults
│   ├── coverage.rs      # WCAG success-criterion coverage accounting
│   ├── en301549.rs      # EN 301 549 (chapter 9, Web) clause mapping + annex derivation
│   └── rules/           # Individual WCAG rules (85+ files, Level A/AA/AAA)
│       ├── text_alternatives.rs  # 1.1.1
│       ├── contrast.rs           # 1.4.3
│       ├── keyboard.rs           # 2.1.1
│       ├── bypass_blocks.rs      # 2.4.1
│       ├── link_purpose.rs       # 2.4.4
│       ├── headings.rs           # 2.4.6
│       ├── language_of_parts.rs  # Conservative language-change check for 3.1.2
│       ├── labels.rs             # 3.3.2
│       ├── target_size_minimum.rs # 2.5.8
│       ├── text_spacing.rs       # 1.4.12
│       └── ... (registered in rules/mod.rs)
│
├── taxonomy/            # Rule taxonomy & classification
│   ├── mod.rs
│   ├── rules.rs         # Canonical rule list (RULES) with metadata
│   ├── criteria.rs      # WCAG success-criterion definitions
│   ├── dimensions.rs    # Dimension enum (Barrierefreiheit, Usability, ...)
│   ├── issue_class.rs   # IssueClass enum (Fehlend, Falsch, Unvollständig)
│   ├── score.rs         # Taxonomy-based score impact helpers
│   └── severity.rs      # Severity mapping helpers
│
├── registry/            # Canonical metric registry (#506, Report Quality Layer)
│   ├── mod.rs           # MetricSpec/BandSet/MetricKind/Direction/Scope/Aggregation
│   ├── metrics.rs       # REGISTRY — one entry per specialized JSON/PDF/docs number
│   ├── bands.rs         # Named score→label band families (FIVE_BAND, ...)
│   └── paths.rs         # json_path_candidates() — interprets MetricSpec.json_path text
│
├── lint/                # Deterministic report-lint (#507, Report Quality Layer)
│   ├── mod.rs           # LintFinding/LintReport, lint() entry point
│   └── checks.rs        # Score/grade/certificate/count-sum/metric_context checks
│
├── audit/               # Audit orchestration
│   ├── mod.rs
│   ├── pipeline.rs      # Single page audit flow
│   ├── batch.rs         # Sitemap/batch processing
│   ├── module.rs        # AuditModule trait + AuditCatalog registry (topo-sorted)
│   ├── catalog.rs       # Module registration/wiring
│   ├── report.rs        # AuditReport structure (raw data)
│   ├── normalized.rs    # NormalizedReport (enriched, score-corrected)
│   ├── scoring.rs       # Score calculation
│   ├── interpretation.rs # Pre-computed DE/EN interpretation texts
│   ├── summary.rs       # Cross-page aggregation logic
│   ├── template_dedup.rs # Template-level root-cause deduplication (batch)
│   ├── occurrence_analysis.rs # Occurrence counting across categories
│   ├── prioritization.rs # Fix-guidance prioritization
│   ├── verdict.rs       # Pass/fail verdict derivation
│   ├── baseline.rs      # Baseline/compare-mode support
│   ├── batch_consistency.rs # Cross-page consistency checks
│   ├── budget.rs        # Concurrency/rate budgeting
│   ├── crawl.rs         # Link crawling (html5ever-based)
│   ├── duplicate.rs     # Duplicate-page detection
│   ├── artifacts.rs     # Cache artifact persistence (--reuse-cache)
│   └── performance_interpretation.rs # Performance-specific interpretation texts
│
├── output/              # Report generation
│   ├── mod.rs
│   ├── cli.rs           # Terminal table output
│   ├── json.rs          # JSON reports (via NormalizedReport)
│   ├── json/            # JSON detail/helper builders
│   ├── sr_audit_json.rs # Standalone screen-reader audit JSON sidecar
│   ├── sarif.rs         # SARIF output format
│   ├── builder/         # AuditReport → PDF ViewModel transformation
│   │   ├── mod.rs, actions.rs, modules.rs, helpers.rs, batch.rs
│   │   └── single/      # Single-report builder (findings, etc.)
│   ├── pdf/             # PDF reports (via renderreport/Typst)
│   │   ├── mod.rs, single_report.rs, batch_report.rs, batch_report/
│   │   ├── cover.rs, findings.rs, wcag_coverage.rs, en301549.rs
│   │   ├── diagnosis.rs, appendix.rs, design.rs (4-color law)
│   │   ├── sanitize.rs  # Drops glyphs no report font can display (PDF/UA)
│   │   └── detail_modules/ # Per-module chapter renderers
│   ├── report_model.rs  # ViewModel structs for PDF
│   ├── explanations.rs  # interpret_score() and shared explanation text
│   ├── search_experience.rs # Search/AI-visibility presentation helpers
│   ├── localized.rs     # Locale-aware text helpers
│   ├── renderer.rs      # Top-level format dispatch
│   └── snapshot_export.rs # AXTree + journey traces → YAML (--export-snapshot)
│
├── a11y_journey/        # Accessibility Journey Layer (--interactive)
│   ├── mod.rs           # Orchestrator: run() single entry point, commerce gating
│   ├── tab_walk.rs      # Tab-order recording + focus evaluation
│   ├── skip_link.rs     # Skip-link activation journey
│   ├── disclosure_journey.rs # Accordion/disclosure toggle
│   ├── modal_journey.rs # Modal focus-trap verification
│   ├── tabs_journey.rs  # TabList navigation
│   ├── menu_journey.rs  # DisclosureMenu journey
│   ├── form_error.rs    # Form-error announcement (per-form clustering, multi-form pages)
│   ├── spa_navigation.rs # SPA single-page navigation detection
│   ├── link_inventory.rs # Linktext/heading/landmark inventory (pure AXTree)
│   ├── add_to_cart.rs   # Add-to-cart status/feedback journey (commerce-gated, PDP only)
│   ├── quantity_stepper.rs # Quantity-stepper keyboard operability (commerce-gated, PDP only)
│   └── evaluate.rs      # Tab-walk finding evaluation
│
├── screen_reader/       # Screen-reader reading-order primitives
│   ├── mod.rs
│   ├── types.rs         # ReadingItem and ignored-node diagnostics
│   ├── announcer.rs     # Localized screen-reader announcement strings
│   ├── navigator.rs     # Virtual SR navigation lists
│   ├── analyzer.rs      # SR-specific issue detection
│   ├── bfsg.rs          # Thin wrapper over wcag::en301549 for legacy call sites
│   └── linearizer.rs    # AXTree DFS reading-order linearization
│
├── best_practices/      # Console errors and vulnerable JS library detection
│   ├── mod.rs, module.rs
│   ├── console_errors.rs # CDP-based console error/warning collection
│   └── vulnerable_libs.rs # Known-CVE JS library detection (jQuery, Bootstrap, ...)
│
├── performance/         # Core Web Vitals, render-blocking, content weight
│   ├── mod.rs, vitals.rs
│   ├── animations.rs    # Non-composited animation detection
│   ├── coverage.rs      # Unused JS/CSS detection via CDP Coverage API
│   ├── critical_chain.rs # Critical request chain analysis
│   ├── minification.rs  # Unminified JS/CSS asset detection
│   └── third_party.rs   # Third-party resource attribution per origin
│
├── seo/                 # Meta, headings, schema, social, technical SEO
│   ├── mod.rs, module.rs
│   ├── meta.rs          # Meta tags
│   ├── headings.rs      # Heading structure
│   ├── profile.rs       # SEO content profile
│   ├── schema.rs        # Structured data (JSON-LD/schema.org)
│   ├── schema_rules.rs  # Feature-specific required/recommended property rules
│   ├── schema_fit.rs    # Page-intent and URL based primary-schema fit
│   ├── schema_parity.rs # Conservative visible-content vs. JSON-LD comparison
│   ├── social.rs        # Open Graph / Twitter Card
│   ├── technical.rs     # robots/canonical/hreflang
│   ├── page_health.rs   # Aggregated issue collection (collect_issues)
│   ├── image_efficiency.rs # Image format and resolution analysis
│   └── ...
│
├── security/            # Security header analysis
│   └── mod.rs, module.rs
│
├── mobile/              # Mobile friendliness analysis
│   ├── mod.rs, module.rs
│   └── ux_heuristics.rs
│
├── dark_mode/           # Dark mode support detection and contrast
│   └── mod.rs, module.rs
│
├── html_conform/        # HTML5 spec-conformance checking via the html-conform crate (scored, part of --full)
│   └── mod.rs, module.rs
│
├── design_quality/      # Opt-in, score-neutral UX/readability heuristics (#528)
│   ├── mod.rs, module.rs
│   └── extract.js
│
├── ai_transparency/     # Opt-in, score-neutral C2PA image-provenance check (EU AI Act Art. 50)
│   ├── mod.rs, module.rs
│   ├── image_provenance.rs # cfg(feature = "ai-transparency") — fetch + c2pa parsing
│   └── extract.js
│
├── network/             # Opt-in, score-neutral, host-scoped network checks (#545)
│   └── dns/             # DNS configuration: CAA, DNSSEC (best-effort), SPF/MX
│       └── mod.rs, module.rs # hickory-resolver queries, once per unique host per run
│
├── ux/                  # UX analysis (5 dimensions, saturation curves)
│   ├── mod.rs, module.rs
│   ├── analysis.rs
│   └── scoring.rs
│
├── journey/             # User journey analysis, page intent detection
│   ├── mod.rs, module.rs
│   ├── analysis.rs
│   ├── page_intent.rs
│   └── scoring.rs
│
├── ai_visibility/       # AI/LLM discoverability analysis
│   ├── mod.rs, module.rs
│   ├── chunks.rs, citation.rs, knowledge_graph.rs, readability.rs
│
├── content_visibility/  # Cross-module signal aggregation (SEO+AI+Quality)
│   └── mod.rs, module.rs
│
├── commerce/            # Shop audit (derive-only, shop-gated): product schema-completeness,
│   └── mod.rs, module.rs # mandatory/trust-page links, page-kind (PDP/Category only), batch roll-up
│
├── source_quality/      # Source quality signals (headers, schema, HTTPS)
│   └── mod.rs, module.rs
│
├── tech_stack/          # CMS/framework detection from in-page signals
│   └── mod.rs, module.rs
│
├── patterns/            # UI pattern detection (nav, accordion, modal, ...)
│   ├── mod.rs           # PatternKind/JourneyKind registry, analyze()
│   ├── main_navigation.rs, accordion.rs, modal_dialog.rs
│   ├── disclosure_menu.rs, tab_list.rs, skip_link.rs
│   ├── form.rs          # Per-form clustering (ancestor_chain_from_root)
│   ├── add_to_cart.rs   # Add-to-cart button/feedback detection
│   └── quantity_stepper.rs # Quantity-stepper control detection
│
├── interaction/         # Cross-cutting interaction analysis
│   └── mod.rs, focus.rs, keyboard.rs, pointer.rs, stability.rs # Bounded DOM/app-ready settling
│
├── assessment/          # Shared assessment types and evidence model
│   └── mod.rs
│
├── studio/              # Studio contract types (GUI data contract)
│   └── mod.rs
│
└── i18n/                # Project Fluent (.ftl) loader, default language: German
    └── mod.rs
```

## Data Flow

### Single URL Audit

```
1. CLI parses arguments
   │
2. validate_url() checks for SSRF
   │
3. BrowserManager launches Chrome
   │
4. Navigate to URL, wait for load
   │
5. Extract AXTree via CDP
   │  └── GetFullAXTreeParams
   │
6. Run WCAG rules against AXTree
   │  ├── Level A rules
   │  ├── Level AA rules (if requested)
   │  └── Level AAA rules (if requested)
   │
6b. Run optional modules (`--full`, or individually enabled): performance, seo,
    security, mobile, dark-mode, ux, journey, ai-visibility, content-visibility,
    source-quality, tech-stack, best-practices, commerce (shop-gated)
   │
6c. Run Accessibility Journey Layer (`--interactive off|basic|full`): tab-walk,
    skip-link, disclosure/modal/tablist/menu journeys, form-error announcement,
    SPA navigation, commerce-gated add-to-cart/quantity-stepper journeys
   │
7. Calculate raw score → AuditReport
   │  └── Preserve requested scope, module/subcheck runs, rule outcomes,
   │      navigation, consent state, environment and audit quality
   │
8. Normalize: AuditReport → NormalizedReport
   │  ├── Apply score corrections (e.g. 3.1.1 lang suppression)
   │  ├── Enrich findings with taxonomy fields
   │  ├── Normalize warnings/manual-review items separately from violations
   │  ├── Retain Journey execution coverage and compact focus evidence
   │  └── Compute grade + certificate from corrected score
   │
9. Generate report (JSON/PDF/Table)
   │  └── All formats use NormalizedReport as single source of truth
   │
10. Close browser
```

### Batch Audit (Sitemap)

```
1. Fetch and parse sitemap.xml
   │
2. Create BrowserPool with N pages
   │
3. For each URL (concurrent):
   │  ├── Acquire page from pool
   │  ├── Run single audit
   │  ├── Return page to pool
   │  └── Report progress
   │
4. Aggregate results
   │
5. Generate batch report
```

## Key Components

### NormalizedReport

Central data layer between raw `AuditReport` and output formats:
- Applies score corrections (e.g. 3.1.1 language suppression)
- Enriches violations with taxonomy metadata (dimension, subcategory, issue_class, etc.)
- Carries explicit audit quality, module/rule execution status, accessibility assessments, and Journey coverage
- Computes grade and certificate from corrected score
- Single source of truth for JSON, PDF, and CLI output — ensures consistent scores

### BrowserManager

Handles Chrome lifecycle:
- Auto-detects Chrome/Chromium installation
- Explicit download via `auditmysite browser install`
- Configures headless mode with security flags
- Manages page navigation with timeouts

### BrowserPool

For concurrent audits:
- Pre-creates N pages
- Reuses pages between audits (reset to about:blank)
- Handles page failures gracefully
- Timeout protection on page reset

### AXTree Extractor

Uses CDP `Accessibility.getFullAXTree`:
- Converts CDP response to internal AXNode structure
- Preserves parent/child relationships
- Extracts properties (role, name, focusable, etc.)

### WCAG Engine

Orchestrates rule checking:
- Filters rules by WCAG level (A, AA, AAA)
- Runs each rule against relevant nodes
- Collects violations with severity
- Counts passes for scoring

### Rule Structure

Each rule implements checking logic:

```rust
pub fn check(tree: &AXTree, _styles: &[NodeStyle], level: WcagLevel) -> Vec<Violation> {
    let mut violations = Vec::new();

    for node in tree.iter() {
        if should_check(node) && has_violation(node) {
            violations.push(Violation::new(
                "1.1.1",
                "Non-text Content",
                level,
                Severity::High,
                "Image missing alt text",
                &node.node_id,
            ));
        }
    }

    violations
}
```

### Taxonomy

Maps WCAG rule IDs to rich metadata:
- **Dimension**: Barrierefreiheit, Usability, etc.
- **Subcategory**: Inhalte & Alternativen, Navigation, etc.
- **IssueClass**: Fehlend, Falsch, Unvollständig
- **ScoreImpact**: Base penalty, max penalty, scaling curve
- **ReportVisibility**: Which report levels (Executive/Standard/Technical) include each finding

## Security Considerations

### SSRF Protection

`validate_url()` blocks:
- Private IPs (10.x, 172.16-31.x, 192.168.x)
- Localhost (127.x, ::1)
- Link-local (169.254.x)
- Non-HTTP schemes

### Third-Party Image Fetching (ai_transparency)

`src/ai_transparency/image_provenance.rs::safe_fetch_image` is the first fetch in this
codebase against **arbitrary third-party origins** (image URLs sourced from the audited
page's own markup, not same-origin like `seo::robots`/`security`). Before every request:
resolves DNS itself, rejects private/loopback/link-local/multicast/unspecified addresses
(including IPv4-mapped IPv6), and pins the connection to the exact validated IP via
`reqwest::ClientBuilder::resolve()` to close a DNS-rebinding TOCTOU gap between the check
and the actual connect.

### Path Traversal

`read_url_file()` uses `canonicalize()` to prevent `../` attacks.

### Chromium Downloads

- Pinned version constant
- Trusted CDN (storage.googleapis.com)
- HTTPS only

## Testing Strategy

```
tests/
├── output_format_tests.rs        # Report generation
├── error_handling_tests.rs       # Error paths
├── parity_contract.rs            # Frozen-count guard vs. docs/PARITY_CONTRACT.jsonc
├── wcag_coverage.rs               # Level A/AA/AAA rule-set coverage checks
├── wcag_unit_tests.rs             # Per-rule unit tests (fixtures in wcag_fixtures/)
├── integration_test.rs / integration_mocked.rs # End-to-end pipeline tests
├── report_consistency_tests.rs   # Score/finding consistency across formats
├── batch_consistency (see src/audit/batch_consistency.rs)
├── release_contract_tests.rs     # Release-gate invariants
└── snapshot_tests.rs              # insta snapshot tests (snapshots/)

src/*/tests (inline)               # Unit tests per module
```

Run all tests:
```bash
cargo test
```

## Performance

- Single audit: ~2-5 seconds (depends on page complexity)
- Batch audit: Linear scaling with concurrency
- Memory: ~100MB per Chrome page

## Dependencies

Key crates:
- `chromiumoxide` - CDP client
- `tokio` - Async runtime
- `clap` - CLI parsing
- `serde` / `serde_json` - Serialization
- `tracing` - Logging
- `html5ever` / `markup5ever_rcdom` - Local HTML5 parsing (link crawling, no remote validator)
- `html-conform` - Deep HTML5 spec-conformance checking (RelaxNG + Schematron) for the html_conform module
- `fluent-bundle` / `unic-langid` - i18n (Project Fluent, default language German)
- `reqwest` (rustls) - Sitemap fetching, Chromium download
- `renderreport` (optional, `pdf` feature) - PDF generation (Typst-based)
- `c2pa` (optional, `ai-transparency` feature, `rust_native_crypto` only — no OpenSSL, no HTTP client) - C2PA Content Credentials manifest reading for the ai_transparency module