16. Security and Privacy Considerations
Permalink to 16. Security and Privacy ConsiderationsThis section is normative.
16.1 Secrets in data and data_files
Permalink to 16.1 Secrets in data and data_filesUse case documents and external data files often contain credentials. They are checked in to source control by default. Implementations and authors SHOULD:
- treat
data_filesas sensitive material; - prefer environment variables or CI secret stores for production credentials;
- use the
--setCLI 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
Permalink to 16.2 Page snapshots and audit stepsThe 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.
16.3 URLs and screenshots
Permalink to 16.3 URLs and screenshotsScreenshots 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
Permalink to 16.3.1 Artifact paths derived from a use case idAn 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
Permalink to 16.4 Code injection in generated testsGenerated .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
Permalink to 16.5 Custom Handlebars templatesThe 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.