Security scanning¶
The Security workflow, the dependency update policies, and the Dockerfile pinning policy. Security scans are advisory, but the go-fuzz job fails when it finds a failing input. The CI workflow is the only publishing condition (see Releases). The vulnerability disclosure process is in SECURITY.md at the repository root.
security.yml¶
Purpose:
- run continuous security checks alongside the main CI workflows
Triggers:
- push to
main - push to
develop - pull request targeting
main, including thelabeledevent type (see Fork pull request policy) workflow_dispatch
This workflow runs only on GitHub-hosted runners. The CodeQL CLI does not support linux/arm64, so act on an arm64 host cannot run it.
Jobs:
dependency-review- runs only for pull requests
- checks dependency changes with
actions/dependency-review-actioninwarn-onlymode - keeps the pull request free of comments (
comment-summary-in-pr: never); findings appear in the job summary only - reports findings of low severity and above as warnings and does not fail on them
- fails if the review does not complete
- summarizes the review outcome and advisory policy
codeql- runs CodeQL analysis for Python and Go
- uploads findings to code scanning and links to them in the job summary
- summarizes the analysis outcome and code scanning link
dependency-audit-python,dependency-audit-go, anddependency-audit-npm- run the Python, Go, and Hub frontend (npm lockfile) audits in separate jobs
- report known vulnerabilities as warnings and in the job summary, and do not fail on them
- fail if a tool cannot run or returns an invalid result
- summarize the audit target and final job status
go-fuzz- runs
make fuzz-go FUZZTIME=30s: each Go fuzz target of the Site API runs with generated inputs for 30 seconds FuzzParseContentRangechecks that an acceptedContent-Rangeheader gives a valid range inside the total sizeFuzzResolveRootPathchecks that a path thatresolveRootPathaccepts stays inside the content root, also through planted symlinks (inside the root, to the sibling of the root, absolute, loops, and dangling chains), and that the siteos.Rootfinds it- fails when it finds a failing input, because the input shows a real bug
- uploads the failing inputs from
api/testdata/fuzz/as the artifactgo-fuzz-failing-inputs(kept for 14 days) - summarizes the target and the final job status
secret-scan- checks out the full Git history (
fetch-depth: 0) - fetches the pull request refs (
refs/pull/*/head) intorefs/remotes/pull/*, with theGITHUB_TOKENin a masked, one-time HTTP header, so that the next steps read them. Pull request refs become public with the repository, and an administrator cannot delete them - downloads the pinned gitleaks release and stops if its SHA-256 is not the pinned
GITLEAKS_SHA256 - runs
gitleaks gitwith.gitleaks.toml(the default gitleaks rules and an allowlist for synthetic test values), and prints each finding with the secret value redacted - reports findings as warnings, and fails if the scan cannot run
- runs
security_scan.py refs, which lists the root commits of all refs. A repository that was imported more than once has more than one root commit, and the refs of the earlier import can hold data that the later import removed. More than one root commit is a warning. A shallow clone fails the step, because the result would not be known. If the repository has more than one root commit on purpose, for example after a subtree merge, the warning is expected - summarizes the gitleaks version and the outcome of both checks
workflow-lint- runs
actionlinton the workflow files - runs
zizmoron.github/, reports findings as warnings, and fails if the scan cannot run - summarizes both lint tools and the zizmor outcome
repository-tests- tests credential selection, validation, and masked outputs
- tests the
develop.ymlpublishing gate - tests the
CHANGELOG.mdrelease notes extraction (utilities/tests/changelog-notes.test.sh) - tests the dependency audit Makefile targets, the audit fix helper, the advisory scan helper, and the Security workflow policy (including the
go-fuzzjob and thefuzz-gotarget) - tests the documentation build and publish script
- tests Helm routing and configurable application Secret references
- tests the proxy checksum update script (
utilities/tests/update-proxy-checksums.test.sh) - tests the Dockerfile policy (
utilities/tests/dockerfile-policy.test.sh) - tests the Hub frontend vendor copy (
node --test hub/frontend/tests/copy-vendor.test.mjs) - tests and summarizes workflow job summary reporting
Dependabot (.github/dependabot.yml) proposes weekly updates for Python (uv), Go, the Hub frontend npm packages (hub/frontend), GitHub Actions, and Docker base images. An npm update that adds a package also needs an entry in hub/frontend/vendor-files.json; until then the Hub tests and the image build fail. It waits 7 days after a release before it proposes the new version.
Dependabot and Renovate do not update gitleaks. To update it, set GITLEAKS_VERSION and GITLEAKS_SHA256 in the secret-scan job together. Use the SHA-256 of gitleaks_<version>_linux_x64.tar.gz from the checksums.txt file of the gitleaks release.
Renovate (renovate.json5) updates site/proxy.Dockerfile only: the
openresty/openresty image, OPENRESTY_VERSION, and FANCYINDEX_VERSION.
A post-upgrade task runs utilities/release/update-proxy-checksums.sh, which
updates the source checksums in the same branch; post-upgrade tasks need a
self-hosted Renovate runner. Renovate waits 7 days after a
release and runs before 6 am Monday. Dependabot ignores the
openresty/openresty image. Renovate does not update the OPM Lua packages in
that file; update their =version pins by hand. See the
site applications README.
All Dockerfiles, including the local-development Dockerfiles, use UID/GID
65534:65534 for the final non-root user and for copied-file ownership.
Dockerfiles pin every public image reference, including images used by
COPY --from, with a tag and a digest (image:tag@sha256:...). Dependabot
or Renovate updates the digests. Images in eccr.ecmwf.int use fixed tags.
The Dockerfile policy test (utilities/tests/dockerfile-policy.test.sh)
enforces what it can check mechanically: every USER is 65534:65534,
--chown values are numeric, the TAG build argument exists and is
validated, APT use follows hygiene rules (--no-install-recommends, removed
lists), every opm install or opm get package carries an explicit
=version, no chmod makes a path under /usr/local/openresty or
/etc/nginx writable by the group or by other users, external COPY --from
images carry a digest, and
site/mirror.Dockerfile keeps its sitesctl repository overridable, pins
SITESCTL_VERSION and a 64-character SITESCTL_SHA256, and verifies the
download with sha256sum -c. The publish.yml workflow must pin the same
version and checksum and also run sha256sum -c. Both Hub Dockerfiles must
have a frontend stage from a node image with a digest, install with
npm ci --ignore-scripts, copy the vendor files to the Hub static/vendor
directory before uv sync, and copy the licence files to
/usr/share/licenses/sites-hub/. The tag-and-digest form of FROM references is a convention
the tests do not check.
Security findings are advisory. The CI workflow is the only publishing condition. Security findings appear as warning annotations, in the job summary and logs, and in code scanning. A Security job still fails when a scan cannot run, so that a broken scan is not mistaken for a clean result. The workflows do not change repository or organization rulesets. An authorized maintainer must remove any required Security status check from the rulesets that protect main and develop.
Permissions:
- top-level
contents: read - job-level permissions are narrowed further where needed
- the CodeQL job uses
security-events: writeto upload analysis results