chore: security hardening for CI/CD and file permissions (#99) #35
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # Copyright (C) 2026 Sten Tijhuis | |
| # SPDX-License-Identifier: MIT | |
| name: Config validation | |
| # The bot configs are the one part of CI that nothing else exercises: a broken | |
| # renovate.json or dependabot.yml does not fail a build, it just quietly stops | |
| # doing its job. This workflow is the thing that notices. | |
| on: | |
| push: | |
| branches: [main, development] | |
| paths: | |
| - 'renovate.json' | |
| - '.github/renovate.json' | |
| - '.github/dependabot.yml' | |
| - '.github/dependabot.yaml' | |
| - '.github/scripts/check-renovate-patterns.py' | |
| # Broader than the other repos: the actionlint job below covers every | |
| # workflow, so every workflow change is relevant here. | |
| - '.github/workflows/**' | |
| pull_request: | |
| branches: [main, development] | |
| paths: | |
| - 'renovate.json' | |
| - '.github/renovate.json' | |
| - '.github/dependabot.yml' | |
| - '.github/dependabot.yaml' | |
| - '.github/scripts/check-renovate-patterns.py' | |
| # Broader than the other repos: the actionlint job below covers every | |
| # workflow, so every workflow change is relevant here. | |
| - '.github/workflows/**' | |
| workflow_dispatch: | |
| permissions: {} | |
| jobs: | |
| bot-configs: | |
| name: Renovate and Dependabot config | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| steps: | |
| - name: Check out source code | |
| uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | |
| with: | |
| persist-credentials: false | |
| - name: Set up Node.js | |
| uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7 | |
| with: | |
| node-version: 'lts/*' | |
| # Renovate's own validator. --strict also fails on warnings, such as an | |
| # option that is valid but deprecated. Given no arguments it finds the | |
| # config files itself and validates them as repository config; passing a | |
| # path instead makes it validate them as global config, which is a | |
| # different and weaker set of rules. | |
| # | |
| # Deliberately unpinned. This is a linter for our config, not something we | |
| # ship, and the newest release is the one that knows about the newest | |
| # deprecations. Its own version is not worth a pull request. | |
| # | |
| # NPM_CONFIG_LOGLEVEL: npm prints "npm warn deprecated ..." for packages | |
| # deep inside Renovate's own dependency tree. Those say nothing about the | |
| # config being validated, and reading them as if they did is the obvious | |
| # mistake to make when they appear directly above the validator's output. | |
| - name: Validate Renovate config | |
| env: | |
| NPM_CONFIG_LOGLEVEL: error | |
| run: npx --yes --package renovate -- renovate-config-validator --strict | |
| # The validator above accepts a well-formed pattern that matches nothing, | |
| # so this covers the gap it leaves. | |
| - name: Check Renovate file patterns | |
| run: python3 .github/scripts/check-renovate-patterns.py renovate.json .github/renovate.json | |
| # GitHub validates dependabot.yml only after it is on the default branch, | |
| # and reports the result on a tab nobody opens. This brings that forward | |
| # to the pull request. | |
| - name: Validate Dependabot config | |
| env: | |
| # renovate: datasource=pypi depName=check-jsonschema | |
| CHECK_JSONSCHEMA_VERSION: "0.38.0" | |
| run: | | |
| config="" | |
| for candidate in .github/dependabot.yml .github/dependabot.yaml; do | |
| if [ -f "$candidate" ]; then | |
| config="$candidate" | |
| break | |
| fi | |
| done | |
| if [ -z "$config" ]; then | |
| echo "No dependabot.yml in this repository; nothing to validate." | |
| exit 0 | |
| fi | |
| pipx install "check-jsonschema==${CHECK_JSONSCHEMA_VERSION}" | |
| check-jsonschema --builtin-schema vendor.dependabot "$config" | |
| # The workflow files are config too. The other repositories in the | |
| # organisation run actionlint from their quality workflow; this one had no | |
| # equivalent, so it lives here. | |
| workflow-lint: | |
| name: Check workflow files | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| steps: | |
| - name: Check out source code | |
| uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | |
| with: | |
| persist-credentials: false | |
| # Pinned release plus checksum, rather than piping a script from a | |
| # branch straight into bash. | |
| - name: Install actionlint | |
| env: | |
| # renovate: datasource=github-releases depName=rhysd/actionlint | |
| ACTIONLINT_VERSION: "1.7.12" | |
| ACTIONLINT_SHA256: "8aca8db96f1b94770f1b0d72b6dddcb1ebb8123cb3712530b08cc387b349a3d8" | |
| run: | | |
| curl -sSL --fail-with-body -o actionlint.tar.gz \ | |
| --retry 5 --retry-delay 3 --retry-all-errors \ | |
| "https://github.com/rhysd/actionlint/releases/download/v${ACTIONLINT_VERSION}/actionlint_${ACTIONLINT_VERSION}_linux_amd64.tar.gz" | |
| echo "${ACTIONLINT_SHA256} actionlint.tar.gz" | sha256sum -c - | |
| tar -xzf actionlint.tar.gz actionlint | |
| sudo install -m 0755 actionlint /usr/local/bin/actionlint | |
| - name: Run actionlint | |
| run: actionlint -color |