grundschutz-checks — checks that measure Grundschutz++ requirements every night.
An ISMS says what is in place. grundschutz-checks looks at whether it actually is. Five checks read GitLab and TLS endpoints and write what they saw to a run file. grundschutz-worksheets turns it into OSCAL assessment results. Run every night, they build a dated series per requirement: did the control work over a period, not only on one day?
- In use: as a test, every night against my own GitLab; 14 nights up to 27 October 2026 will show whether the checks run without false alarms
- Checks: branch protection, enforced two-factor, TLS, SBOM, dependency updates
- Requires: Node 24, a GitLab token with
read_api, no dependencies - Licence: MIT · read-only, no write access needed
Claimed is not measured
Until now
- The status of a requirement sits in the ISMS tool and is updated by hand before the audit
- Evidence means screenshots from one day, not from a period
- Nobody notices when a setting flips between two audits
- Exceptions live in people's heads, not in a file with a reason
With grundschutz-checks
- One run per check every night, as a dated OSCAL assessment result
- For each project, group or endpoint it says what is satisfied and what is not, and why
- A check that could not look writes nothing and fails, instead of reporting “satisfied”
- Exceptions are in the config with their reason, and every run names them
Does it fit my ISMS?
Use it if …
- your code is in GitLab and you want to evidence branch protection, enforced two-factor, SBOMs and dependency updates
- you have public endpoints whose TLS someone should look at regularly
- you work with Grundschutz++ and need assessment results as OSCAL
- you would rather read a small check than configure a compliance tool
Don't use it if …
- your code is on GitHub or Bitbucket: the four repository checks only talk to the GitLab API
- you expect ready-made coverage of Grundschutz++: five checks evidence nine requirements, most of them only in part
- you want to adopt a result without checking the mapping yourself
- you are looking for evidence for laptops, offices or people
Why checks of my own
The BSI publishes Grundschutz++ as a machine-readable catalog. grundschutz-worksheets turns it into an OSCAL profile, a security plan and a plan of action. What was missing was the measurement: assessment results written by a run, not by me. These five checks are the start. They cover what can be read off GitLab and TLS without an agent on the system.
A check only reads. If it could not look, after a failed request or with an empty config, it writes no run file and exits 1. A check that could not look has not found anything satisfied either. Requirements a check evidences only in part are listed under partial in the run file, and the security plan keeps such a pass partial.
Every check is tested against a fake GitLab and a fake TLS probe. In CI the checks also run for real, go through results.mjs of grundschutz-worksheets and are validated against the NIST schema. grundschutz-viewer shows the result as one HTML page with the history of every run.
How a check becomes an assessment result
The config names what to look at: the GitLab instance, groups and projects, TLS endpoints, limits and exceptions. Tokens come from the environment, never from the config. GITLAB_TOKEN needs read_api and nothing more.
# what there is
node run.mjs --list
# one check, one run, one result
GITLAB_TOKEN=... node run.mjs branch-protection --config checks.config.json > run.json
node ../grundschutz-worksheets/results.mjs run.json > results.json
The five checks and the requirements they evidence:
branch-protection: default branch protected, no force push, pushes by maintainers only, merges only after a green pipeline. DEV.2.4, TEST.4.1, both in partmfa-enforced: every listed group enforces two-factor, grace period at most 48 hours. BER.5.9, in parttls-endpoints: TLS 1.2 or 1.3, no 1.0 or 1.1, certificate valid for at least 14 more days. KONF.14.1, KONF.14.1.1, both in partsbom-present: the latest release of every project has an SBOM. DEV.4.3, ASST.2.3.2, the latter in partdependency-updates: scan job passed in the latest pipeline, no update from the bot open for more than 14 days. DET.5.3, DET.5.10, both in part
Deliberate exceptions go into the config under accept, with project and reason. An accepted project is skipped, and every run names it with its reason. An exception stays visible instead of disappearing.
The documentation
The checks
What each check looks at, which requirements it evidences and which of those only in part. Plus the commands from the list to the assessment result.
Example config
Every setting in one place: catalog, assessment plan, GitLab groups, TLS endpoints, limits and exceptions with their reason.
grundschutz-worksheets
Node 24+ · MIT
Turns the run files into OSCAL assessment results, and the catalog into profile, security plan and plan of action. Without worksheets a run stays a JSON file.
GitHub
Node 24+ · MIT
Source, releases, issues. Development happens in GitLab; GitHub is the public mirror. I carry pull requests over by hand.
Measure Grundschutz++ instead of claiming it?
I help you get started: first one check against your GitLab, then the chain up to the plan of action.