UCDL Accessibility Use Case Definition Language
Contents

Accessibility Use Case Definition Language (UCDL) and Runner

16. Security and Privacy Considerations

This section is normative.

16.1 Secrets in data and data_files

Use case documents and external data files often contain credentials. They are checked in to source control by default. Implementations and authors SHOULD:

  • treat data_files as sensitive material;
  • prefer environment variables or CI secret stores for production credentials;
  • use the --set CLI override or a separate, untracked data file for real credentials;
  • never commit production credentials to a public repository.

The secrets block. A value declared under secrets: is interpolated normally at run time and MUST NOT appear in any artifact. Wherever the value would otherwise be written — step_text, error, accessibility_notes, the HTML report, and generated .spec.ts literals — a processor MUST substitute «secret:<name>».

data:
  username: '[email protected]'
secrets:
  password: 'from a secret store, not from source control'
before:
  - 'enter: field "Password" value "{{ password }}"'

Secrecy is declared once, on the value, rather than encoded in each reference: a step writes {{ password }} as it would any other variable. A namespace ({{ secret.password }}) was considered and rejected, because it makes every reference site load-bearing — miss one and the value is published, with nothing to say so.

secrets merges through inheritance the way data does, so an extension can override a credential without restating the flow.

Generated tests read secrets from the environment. A generated .spec.ts is normally committed, so a processor MUST NOT embed a secret in one. The reference implementation emits a preamble reading process.env.UCDL_SECRET_<NAME> and failing with a named error when it is unset, rather than typing undefined into the page.

This bears most on the before block. §4.6 nominates it for exactly the work that handles credentials — a log-in — and §10.1 requires its results to be reported.

A value not declared secret still reaches the report: step_text is the source line after interpolation (§7.4), which §10.1 requires so that a report shows what a human tester would have read. Redaction is the one exception that requirement admits.

16.2 Page snapshots and audit steps

The audit keyword hands the engine a serialised snapshot of the live page (§9.3). The snapshot carries whatever the page rendered — personal data, and any secret the application placed in the DOM — and is taken from an authenticated session. Cookies and storage state are not forwarded. Implementations MUST NOT include the snapshot in reports, MUST NOT log cookie values anywhere, and SHOULD NOT persist the snapshot beyond the audit step.

The base URL of §9.3 makes the engine fetch: the snapshot's relative stylesheets, fonts, images and scripts are requested from the page's own origin, which a snapshot with no base URL would not do. Two consequences follow from cookies not being forwarded. The requests are unauthenticated, so an asset behind the same gate as the page does not load and the audit is decided against a partly-unstyled rendering — the failure §9.3 describes, narrowed to subresources rather than removed. And they reach the origin as a second, anonymous client, appearing in its server logs, analytics and rate limits; a processor SHOULD document that an audit step issues them, because an audit reads as an offline operation on an already-captured snapshot and is not one. A processor that forwards the session to those subresources MUST do so through the engine's cookie interface, and MUST NOT embed credentials in the base URL: the engine writes that URL into the document as a <base href>, where it is readable by page scripts and carried into anything rendered from it.

Screenshots captured on failure are written beneath a directory named for the use case (§8.2), so a batch run's evidence for one case cannot be overwritten by another's. Screenshots a step requests are written beneath the same configured directory, under a name the grammar restricts to a filename (§5.6.12). They may contain personally identifying or sensitive information rendered on the page. Implementations SHOULD treat the screenshot directory as containing sensitive data and SHOULD include a .gitignore entry for it by default.

16.3.1 Artifact paths derived from a use case id

An id is free text (§4.2), and a processor writes at least three artifacts named after it: the JSON report, the HTML report, and the generated .spec.ts. A processor MUST reduce the id to a single path component before using it in a filename. Interpolating it directly lets id: "../x" write outside the directory the processor was given — over a hand-written source file, in the case of generated tests, which carry a banner saying they are safe to overwrite — and makes an id containing a path separator fail with a filesystem error after the run has completed.

The same reduction MUST be used for every such artifact, so that one case's outputs share one name.

16.4 Code injection in generated tests

Generated .spec.ts files embed accessible names, URLs, and template variable values as JavaScript single-quoted string literals. Implementations MUST escape backslash, single-quote, newline, carriage-return, and tab when emitting such literals. The escapeForJs function in the reference implementation provides this escaping. Failure to escape is a critical defect.

The same duty applies to CSS. The id and data-* escape hatches (§6.1) and the audit context selector (§9.3) build a selector from a value the language did not choose — an authored name, a template variable, an attribute read from the page — and MUST escape it as a CSS identifier or CSS string before interpolation, in both execution modes. Generated code escapes for CSS first and for the JavaScript literal second.

16.5 Custom Handlebars templates

The reference implementation compiles its template with noEscape: true because the output is TypeScript source, not HTML. Implementations that allow user-supplied templates MUST review this trade-off; do not feed arbitrary HTML through a noEscape template.