The workflow
Everything you can set when you run rechtzeitig, and what you get back.
§ 1Inputs
| Input | Default | Meaning |
|---|---|---|
url | empty | Start page to check. Empty scans only the repository. |
language | de | Language of the site: de, en or all. Decides which words count as "Impressum", "Datenschutz" and so on. |
subscriptions | false | true if visitors can sign up for subscriptions. Turns on the cancellation button check (§ 312k BGB). |
sales | false | true if consumers buy or sign contracts on the site. Turns on the withdrawal button check (§ 356a BGB). |
modules | all | Comma list of check groups: legal, privacy, security, sustainability, accessibility, repo. |
report-language | language | de or en. The whole report follows it: messages, details, titles and fix hints. Empty uses the site language; with all the report is in English. |
fail-on | never | When the run turns red, see fail-on. |
max-pages | 10 | Pages to visit at most. |
repository | workspace | Folder to read lockfiles, licenses and secrets from. Needs actions/checkout before rechtzeitig. |
ipinfo-token | empty | Opt-in. Token from ipinfo.io, passed from the secret IPINFO_TOKEN. Turns on the third-country check. Empty skips it, see Secrets. |
vulnerabilities | true | Looks up known vulnerabilities of locked components at osv.dev. false sends nothing there and the vulnerability check is skipped. |
require-sandbox | true | Stops the scan when Chrome cannot start with its sandbox. false scans without it, see Safe use. |
retention-days | 7 | Days GitHub keeps the report artifact, see The report. |
The output result is pass, warn or fail.
§ 2What runs when
Most checks run whenever their target is there. The ones below need a setting. A check that is off shows as skip in the report, with the reason and the setting that turns it on. The example workflow scans the website and the repository.
| Check | Runs when |
|---|---|
| All website checks | Only with a url. |
| All repository checks | Only when actions/checkout runs before rechtzeitig (folder: input repository). |
| A group of checks | modules picks groups: legal, privacy, security, sustainability, accessibility, repo. Empty runs all. |
| Cancellation button for subscriptions LEG-DE-BTN-001 | Opt-in: only with subscriptions: true. |
| Withdrawal button for online contracts LEG-DE-BTN-002 | Opt-in: only with sales: true. |
| Privacy policy names transfers outside the EU LEG-EU-DSE-005 | Opt-in: only when the secret IPINFO_TOKEN is set (input ipinfo-token, token from ipinfo.io). Without it the check is skipped and nothing is sent.Sends data to ipinfo.io (opt-in) |
| No dependencies with known vulnerabilities SEC-DEPS-001 | On by default; sends data to api.osv.dev. Turn it off with vulnerabilities: false.Sends data to api.osv.dev |
§ 3fail-on
Every check ends as pass warn fail info or skip. fail-on decides which of these make the run red. The report is the same either way.
fail-on | Run is red when | 1 fail, 2 warn | 0 fail, 2 warn |
|---|---|---|---|
never (default) | never | green | green |
fail | any check fails | red | green |
warn | any check fails or warns | red | red |
The default never turns a run red, so adding rechtzeitig cannot break anything. Choose fail once the findings are fixed and you want to keep it that way, or warn before a launch.
§ 4Secrets
Every check that needs an API token is opt-in. It runs only when you add the token as a repository secret. Without the secret it shows as skip in the report, and nothing is sent to that service. All other checks need no secret.
| Secret | Input | Turns on | Token from |
|---|---|---|---|
IPINFO_TOKEN | ipinfo-token | Transfers outside the EU (LEG-EU-DSE-005) | ipinfo.io, the free plan is enough |
To add a secret: repository Settings → Secrets and variables → Actions → New repository secret, with the name from the table. The example workflow already passes it to the input.
rechtzeitig sends only the IP addresses of third-party servers to IPinfo, never data about your visitors.
§ 5The report
The summary page of the run shows a table of all checks with details, in the report language. Findings in a file of the repository also appear as annotations: in a pull request at the line, otherwise on the run page. GitHub shows at most 10 errors and 10 warnings there; the summary and the artifact always have all of them. The artifact rechtzeitig-report contains:
| File | Content |
|---|---|
rechtzeitig-report.md | The same report as Markdown |
rechtzeitig-report.json | Findings: rule id, severity, status, message in the report language with its key and parameters, and locations. No page text or file contents |
rechtzeitig-sbom.cdx.json | SBOM in CycloneDX 1.6 format, with vulnerabilities |
Keep the SBOM with each release. It shows which versions you shipped and that you checked them.
GitHub deletes the artifact after retention-days (default 7). On a public repository anyone signed in to GitHub can download it, including the list of your dependencies and where findings were found.
§ 6How the website is visited
Each page is loaded twice in a fresh Chromium, once with JavaScript off and once with it on. Nothing is clicked and no consent is given, so every request and cookie in the report happened before consent. rechtzeitig opens the start page, every legal page it finds, a sample of other pages, and a short list of paths that must not be public (/.env, /.git/config, backups).
GitHub's runners are in US data centers. A site that shows its consent banner only to EU visitors will look different here, and that is a finding in itself. Pages behind a login are not checked.
§ 7Data that leaves the runner
Besides visiting your website, rechtzeitig sends data to two services. Nothing else leaves the runner.
| Service | What is sent | When |
|---|---|---|
| api.osv.dev | Package URLs (name and version) of all locked components, for example pkg:npm/lodash@4.17.21 | By default, when the repository is scanned. Turn it off with vulnerabilities: false |
| ipinfo.io | IP addresses of third-party servers that rechtzeitig does not know | Only when IPINFO_TOKEN is set |
§ 8Safe use
Never run rechtzeitig in a workflow triggered by pull_request_target. That trigger runs with your secrets and a token that can write to the repository, while the scanned code comes from a fork. Use workflow_dispatch, schedule, push or pull_request.
Check out with persist-credentials: false as in the example workflow. rechtzeitig does not need the token, so it should not be left in the checkout.
The scanned site is untrusted. Chrome runs with its sandbox. On GitHub-hosted runners with Ubuntu 23.10 or later, the action installs an AppArmor profile that allows the user namespaces the sandbox needs, for the Chrome binary only; those runners are discarded after the job. On self-hosted runners it changes nothing. Where the runner has no sandbox, the scan stops. require-sandbox: false scans anyway; only do that for sites you trust. Neither the browser nor any other request reaches private addresses (localhost, 10.x, cloud metadata), except the host of url itself.
rechtzeitig is a composite action and sets up Node 24 with actions/setup-node. That Node stays on the PATH for the steps after it in the same job. Set up your own Node version after rechtzeitig, or run it in a separate job.