From 9792e16e1613e0065523d601b13a2b39b877946e Mon Sep 17 00:00:00 2001 From: "Petrov, Nikolay A" Date: Thu, 29 Oct 2026 23:05:40 -0700 Subject: [PATCH] ci: dependency review and Renovate config validation dependency-review.yml fails a PR that adds or moves a dependency to a version with a High or Critical advisory, allowing the thirteen filelock advisories SECURITY.md documents (kept in sync by test_advisory_lists.py). renovate-validation.yml runs Renovate's config validator in strict mode, with its version pinned and bumped by a Renovate custom manager. Signed-off-by: Petrov, Nikolay A --- .github/renovate.json5 | 9 +++++ .github/workflows/dependency-review.yml | 41 +++++++++++++++++++++++ .github/workflows/renovate-validation.yml | 41 +++++++++++++++++++++++ dokimi/SECURITY.md | 7 ++-- dokimi/tests/test_advisory_lists.py | 15 ++++++++- 5 files changed, 110 insertions(+), 3 deletions(-) create mode 100644 .github/workflows/dependency-review.yml create mode 100644 .github/workflows/renovate-validation.yml diff --git a/.github/renovate.json5 b/.github/renovate.json5 index 80d2fea60..4d66dded1 100644 --- a/.github/renovate.json5 +++ b/.github/renovate.json5 @@ -74,6 +74,15 @@ datasourceTemplate: "github-releases", extractVersionTemplate: "^v(?.+)$", }, + { + // The renovate-config-validator version renovate-validation.yml pins + // (the action would otherwise install the latest from npm). + customType: "regex", + managerFilePatterns: ["/^\\.github/workflows/renovate-validation\\.yml$/"], + matchStrings: ["validator_version: '?(?\\d+\\.\\d+\\.\\d+)'?"], + depNameTemplate: "renovate", + datasourceTemplate: "npm", + }, { // The micromamba release conda-forge.yml installs. Its tags carry a // build number (2.9.0-0), which semver would read as a prerelease. diff --git a/.github/workflows/dependency-review.yml b/.github/workflows/dependency-review.yml new file mode 100644 index 000000000..1c299e546 --- /dev/null +++ b/.github/workflows/dependency-review.yml @@ -0,0 +1,41 @@ +# Copyright (C) 2026 Intel Corporation +# SPDX-License-Identifier: Apache-2.0 + +name: dependency-review + +# Judges only the dependencies a PR changes (pip-audit.yml audits the locks daily). +# Path-filtered, so it must stay out of the required checks. +on: + pull_request: + paths: + - 'dokimi/requirements*.txt' + - 'dokimi/pyproject.toml' + - '.github/workflows/**' + - '.github/actions/**' + +permissions: + contents: read + +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number }} + cancel-in-progress: true + +jobs: + dependency-review: + name: Dependency review + # A short job: ubuntu-latest, as for the other short jobs (see ci.yml). + runs-on: ubuntu-latest + timeout-minutes: 10 + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + - uses: actions/dependency-review-action@a1d282b36b6f3519aa1f3fc636f609c47dddb294 # v5.0.0 + with: + fail-on-severity: high + # The filelock advisories SECURITY.md documents; tests/test_advisory_lists.py + # keeps them in sync with pip-audit.yml and dokimi/osv-scanner.toml. + allow-ghsas: GHSA-567q-5qvw-pf92,GHSA-6627-m3xw-93mp,GHSA-69hj-f68j-2879,GHSA-8mx5-7m5m-r77g,GHSA-9c4x-gm8v-wgc4,GHSA-9j6q-wvj5-jgjg,GHSA-9vhv-jxww-xfm5,GHSA-cwqr-hh33-gh44,GHSA-fq56-3cx3-fjxp,GHSA-j8f7-rjxc-mr56,GHSA-m462-7rxw-wvpg,GHSA-rfpc-qcw5-3pc4,GHSA-v4x8-3wr5-3wq8 + # The summary only: a PR comment would need pull-requests: write, + # which a fork PR's token does not have. + comment-summary-in-pr: never diff --git a/.github/workflows/renovate-validation.yml b/.github/workflows/renovate-validation.yml new file mode 100644 index 000000000..aa5850e13 --- /dev/null +++ b/.github/workflows/renovate-validation.yml @@ -0,0 +1,41 @@ +# Copyright (C) 2026 Intel Corporation +# SPDX-License-Identifier: Apache-2.0 + +name: renovate-validation + +# Renovate silently stops opening PRs on a config it cannot parse; +# --strict also fails on deprecated options. +on: + push: + branches: [ main ] + paths: &renovate-paths + - '.github/renovate.json5' + - '.github/workflows/renovate-validation.yml' + pull_request: + paths: *renovate-paths + workflow_dispatch: + +permissions: + contents: read + +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.run_id }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + +jobs: + validate: + name: Renovate config + # A short job: ubuntu-latest, as for the other short jobs (see ci.yml). + runs-on: ubuntu-latest + timeout-minutes: 10 + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + - uses: suzuki-shunsuke/github-action-renovate-config-validator@1e1604219a6b38ae9a10aff42a28a3656322c31a # v2.4.1 + with: + config_file_path: .github/renovate.json5 + strict: 'true' + # The action installs the latest validator from npm unless pinned; + # the custom manager in renovate.json5 bumps this line. + validator_version: '44.198.0' diff --git a/dokimi/SECURITY.md b/dokimi/SECURITY.md index 60c632da5..17d1f707a 100644 --- a/dokimi/SECURITY.md +++ b/dokimi/SECURITY.md @@ -122,5 +122,8 @@ on `main`; a daily run that finds a new advisory opens an issue rather than failing unrelated PRs. The `static` job in `.github/workflows/ci.yml` runs `bandit -ll` with no Medium or High finding allowed. The same IDs are ignored by osv-scanner through `dokimi/osv-scanner.toml`, -which OpenSSF Scorecard runs. A removed advisory comes out of the first list -above, the `pip-audit` step, and that file together. +which OpenSSF Scorecard runs, and by `allow-ghsas` in +`.github/workflows/dependency-review.yml`, which fails a PR that adds or moves a +dependency to a version with any other High or Critical advisory. A removed +advisory comes out of the first list above, the `pip-audit` step, that file and +`allow-ghsas` together. diff --git a/dokimi/tests/test_advisory_lists.py b/dokimi/tests/test_advisory_lists.py index 64c7d9ea2..1d46adda1 100644 --- a/dokimi/tests/test_advisory_lists.py +++ b/dokimi/tests/test_advisory_lists.py @@ -17,6 +17,7 @@ REPO = PROJECT.parent OSV_CONFIG = PROJECT / "osv-scanner.toml" CI_WORKFLOW = REPO / ".github" / "workflows" / "pip-audit.yml" +DEPENDENCY_REVIEW = REPO / ".github" / "workflows" / "dependency-review.yml" SECURITY = PROJECT / "SECURITY.md" ADVISORY_ID = r"PYSEC-\d{4}-\d+|GHSA(?:-[0-9a-z]{4}){3}" @@ -35,12 +36,24 @@ def _pip_audit_ids() -> set[str]: ) +def _dependency_review_ids() -> set[str]: + match = re.search(r"allow-ghsas:\s*(.+)", DEPENDENCY_REVIEW.read_text(encoding="utf-8")) + assert match, "dependency-review.yml has no allow-ghsas line" + return {i.strip() for i in match.group(1).split(",")} + + def test_osv_and_pip_audit_ignore_the_same_ids(): assert _osv_ids() == _pip_audit_ids() +def test_dependency_review_allows_the_same_ghsa_ids(): + """allow-ghsas takes GHSA IDs only, so a PYSEC entry has no counterpart there.""" + ghsa = {i for i in _osv_ids() if i.startswith("GHSA-")} + assert _dependency_review_ids() == ghsa + + def test_ci_and_security_name_the_osv_config(): - """A removed advisory must be dropped from all three lists, so each names the file.""" + """A removed advisory must be dropped from every list, so each names the file.""" assert "dokimi/osv-scanner.toml" in CI_WORKFLOW.read_text(encoding="utf-8") assert "osv-scanner.toml" in SECURITY.read_text(encoding="utf-8")