From 886d828462f03080fbf509b9112130e72ac1137d Mon Sep 17 00:00:00 2001 From: Byron Williams Date: Wed, 2 Sep 2026 14:17:46 -0700 Subject: [PATCH] ci: drop GitHub Advanced Security dependent CI (CodeQL, dependency-review, SARIF ingestion) GitHub now bills Advanced Security (Code Security), so CodeQL code scanning, actions/dependency-review-action (needs the dependency-graph diff), and github/codeql-action/upload-sarif (needs Code Scanning) no longer function without a paid plan. Remove them fleet-wide from this template and its generated projects; replace SARIF ingestion with plain build artifacts wherever a scanner's only output was the SARIF upload. Removed (dev CI, this repo): - .github/workflows/codeql.yml (init/analyze job) - .github/workflows/dependency-review.yml - .github/codeql-config.yml, .github/codeql/codeql-config.yml, .codeqlignore (orphaned CodeQL config with no consumer left) Removed (generated-project template): - {{cookiecutter.project_slug}}/.github/workflows/dependency-review.yml (no codeql.yml equivalent ever existed in generated projects; CodeQL there is opt-in only via security-analysis.yml's run-codeql input) Replaced upload-sarif with actions/upload-artifact (findings preserved, now downloadable instead of posted to the Security tab): - {{cookiecutter.project_slug}}/.github/workflows/cifuzzy.yml: fuzzing SARIF per sanitizer Disabled upload-sarif on reusable-workflow callers where the callee gates the step on this input and has an unconditional artifact fallback (verified against the pinned/main callee content): - .github/workflows/scorecard.yml (root dev CI) - {{cookiecutter.project_slug}}/.github/workflows/container-security.yml - {{cookiecutter.project_slug}}/.github/workflows/scorecard.yml (org-workflow branch) Removed a redundant upload-sarif step with no new replacement, since an adjacent step already uploads the identical file as a plain artifact: - {{cookiecutter.project_slug}}/.github/workflows/scorecard.yml (standalone branch: "Upload Scorecard results as artifact" already covers results.sarif) Pruned now-unconsumed security-events: write permissions from all of the above, at both workflow and job level. Docs and tests updated to match: - README.md: corrected the security-tools bullet; CodeQL and dependency-review-action are no longer claimed as active, and are described as opt-in org-workflow features that require GHAS - CHANGELOG.md: added this removal to the existing [Unreleased] > Removed section - docs/secure.md: dropped an orphaned security-events: write from an example snippet; replaced the dependency-review-action example (section renamed to "Dependency Vetting") with google/osv-scanner-action and a note not to add dependency-review.yml without verifying GHAS - docs/org-workflows/python-sbom.yml: this reference copy's Trivy runtime-deps SARIF upload replaced with actions/upload-artifact, matching the behavior verified on the live ByronWilliamsCPA/.github workflow - tests/test_supply_chain_security.py: rewrote three tests that asserted dependency-review.yml's presence or its deny-licenses content to instead assert its absence, with docstrings explaining why (test_dependency_review_workflow_created renamed to test_dependency_review_workflow_not_generated) Explicitly NOT touched: - security-analysis.yml (root and template): already has run-codeql: false / run-dependency-review: false; a separate coordinated change owns these input lines against ByronWilliamsCPA/.github, whose defaults are true - {{cookiecutter.project_slug}}/.github/workflows/security-analysis.yml still defaults run-codeql/run-dependency-review to true for the org-workflow branch; flagged for the coordinated change, not fixed here to avoid scope creep into that separate effort - The sbom: job's security-events: write in both sbom.yml files: the pinned/main python-sbom.yml reusable workflow has no upload-sarif toggle input, so this permission cannot be safely removed yet Known gaps (scanner findings with no destination, pending the coordinated ByronWilliamsCPA/.github update): - python-sbom.yml's Trivy runtime-deps SARIF upload (github/codeql-action/upload-sarif) has no artifact fallback at the currently referenced version; only docs/org-workflows/python-sbom.yml, a reference copy, was fixed here - The copyleft license deny-list (AGPL/GPL/LGPL SPDX ids) lived only in dependency-review.yml's deny-licenses input; generated projects have no equivalent gate now (sbom.yml's fail-on-forbidden-licenses defaults to false and carries a different list) Verification: - uv run pytest: 185 passed, 2 skipped - uv run ruff check / format --check: no new issues (diffed against pre-edit baseline via git stash) - actionlint: no structural errors on non-templated workflow files - pre-commit run: all hooks passed Co-Authored-By: Claude Sonnet 4.6 --- .codeqlignore | 4 - .github/codeql-config.yml | 17 ---- .github/codeql/codeql-config.yml | 34 ------- .github/workflows/codeql.yml | 69 -------------- .github/workflows/dependency-review.yml | 38 -------- .github/workflows/sbom.yml | 9 +- .github/workflows/scorecard.yml | 8 +- .secrets.baseline | 4 +- CHANGELOG.md | 22 +++++ README.md | 5 +- docs/org-workflows/python-sbom.yml | 20 ++-- docs/secure.md | 29 +++--- tests/test_supply_chain_security.py | 94 +++++++------------ .../.github/workflows/cifuzzy.yml | 21 +++-- .../.github/workflows/container-security.yml | 8 +- .../.github/workflows/dependency-review.yml | 49 ---------- .../.github/workflows/sbom.yml | 7 +- .../.github/workflows/scorecard.yml | 18 ++-- 18 files changed, 146 insertions(+), 310 deletions(-) delete mode 100644 .codeqlignore delete mode 100644 .github/codeql-config.yml delete mode 100644 .github/codeql/codeql-config.yml delete mode 100644 .github/workflows/codeql.yml delete mode 100644 .github/workflows/dependency-review.yml delete mode 100644 {{cookiecutter.project_slug}}/.github/workflows/dependency-review.yml diff --git a/.codeqlignore b/.codeqlignore deleted file mode 100644 index 4de5af0f..00000000 --- a/.codeqlignore +++ /dev/null @@ -1,4 +0,0 @@ -# Exclude Jinja2 cookiecutter template directories from CodeQL analysis. -# These directories contain {{cookiecutter.variable}} template expressions -# inside Python files, causing CodeQL to report syntax errors during extraction. -{{cookiecutter.project_slug}}/ diff --git a/.github/codeql-config.yml b/.github/codeql-config.yml deleted file mode 100644 index 96bb4851..00000000 --- a/.github/codeql-config.yml +++ /dev/null @@ -1,17 +0,0 @@ ---- -# CodeQL configuration for cookiecutter-python-template -# Excludes template files containing Jinja2 syntax from analysis - -name: "CodeQL Config" - -# Exclude template directory - contains Jinja2 template syntax -# that appears invalid to static analyzers before rendering -paths-ignore: - - '{{cookiecutter.project_slug}}/**' - - '.venv/**' - - 'venv/**' - - 'node_modules/**' - - '.git/**' - - 'dist/**' - - 'build/**' - - '**/*.egg-info/**' diff --git a/.github/codeql/codeql-config.yml b/.github/codeql/codeql-config.yml deleted file mode 100644 index 1008df07..00000000 --- a/.github/codeql/codeql-config.yml +++ /dev/null @@ -1,34 +0,0 @@ -# CodeQL configuration for cookiecutter-python-template. -# -# Files under `hooks/` are Python cookiecutter pre/post-generation hooks -# rendered by Jinja2 BEFORE execution. Lines such as -# `if "{{ cookiecutter.include_editorconfig }}" == "no":` look like -# "comparison of constants" or "constant in conditional" to a static analyzer -# that reads the file as plain Python, but at runtime the left-hand side is -# substituted with the value the project author chose ("yes" or "no") and the -# comparison is meaningful. CodeQL has no awareness of cookiecutter templating, -# so the queries below are filtered out for the `hooks/` path only. -# -# The same suppression scope intentionally does NOT cover -# `{{cookiecutter.project_slug}}/`, because the generated project's Python is -# executed as-is after rendering and benefits from full CodeQL coverage. - -name: "Custom CodeQL config for cookiecutter hooks" - -queries: - - uses: security-extended - - uses: security-and-quality - -query-filters: - - exclude: - id: py/constant-comparison - paths: - - hooks/** - - exclude: - id: py/constant-conditional-expression - paths: - - hooks/** - - exclude: - id: py/comparison-of-constants - paths: - - hooks/** diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml deleted file mode 100644 index 608cc8e8..00000000 --- a/.github/workflows/codeql.yml +++ /dev/null @@ -1,69 +0,0 @@ -# CodeQL Security Analysis -# Performs static application security testing (SAST) using GitHub CodeQL. -# -# IMPORTANT: GitHub's CodeQL "default setup" must remain DISABLED for this repo. -# Default setup and custom advanced configuration cannot both upload SARIF to the -# Security tab. To verify or disable: Settings > Code security > Code scanning > Default setup. -name: CodeQL Analysis - -on: - push: - branches: [main, master] - pull_request: - branches: [main, master] - schedule: - - cron: "0 7 * * 1" - workflow_dispatch: - -permissions: - contents: read - -concurrency: - group: codeql-${{ github.ref }} - cancel-in-progress: false - -jobs: - analyze: - name: CodeQL Analyze (Python) - runs-on: ubuntu-latest - timeout-minutes: 30 - permissions: - actions: read - contents: read - security-events: write - - steps: - - name: Harden the runner - uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4 - with: - egress-policy: audit - - - name: Checkout repository - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7 - with: - persist-credentials: false - - - name: Set up Python - uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0 - with: - python-version: "3.12" - - - name: Install uv - uses: astral-sh/setup-uv@fac544c07dec837d0ccb6301d7b5580bf5edae39 # v8.2.0 - with: - enable-cache: true - - - name: Install dependencies - run: uv sync --no-dev - - - name: Initialize CodeQL - uses: github/codeql-action/init@8aad20d150bbac5944a9f9d289da16a4b0d87c1e # v4.36.2 - with: - languages: python - build-mode: none - config-file: ./.github/codeql/codeql-config.yml - - - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@8aad20d150bbac5944a9f9d289da16a4b0d87c1e # v4.36.2 - with: - category: "/language:python" diff --git a/.github/workflows/dependency-review.yml b/.github/workflows/dependency-review.yml deleted file mode 100644 index 02651b03..00000000 --- a/.github/workflows/dependency-review.yml +++ /dev/null @@ -1,38 +0,0 @@ -# Dependency Review - Analyzes dependency changes in PRs -# Checks for vulnerabilities and license compliance -# -# Features: -# - Vulnerability detection in dependency changes -# - License compliance checking -# - Blocks PRs with high severity vulnerabilities -name: Dependency Review - -on: - pull_request: - branches: [main, master, develop] - -permissions: - contents: read - pull-requests: write - -jobs: - dependency-review: - name: Dependency Review - runs-on: ubuntu-latest - steps: - - name: Harden the runner - uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4 - with: - egress-policy: audit - - - name: Checkout repository - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7 - - - name: Dependency Review - uses: actions/dependency-review-action@a1d282b36b6f3519aa1f3fc636f609c47dddb294 # v5.0.0 - with: - fail-on-severity: high - allow-licenses: MIT, Apache-2.0, BSD-2-Clause, BSD-3-Clause, ISC, MPL-2.0, LGPL-2.1, LGPL-3.0, Python-2.0, Unlicense, CC0-1.0, GPL-3.0-or-later - # codespell is a dev-only spell checker; its GPL-2.0 license never attaches to generated projects or distributed artifacts - allow-dependencies-licenses: pkg:pypi/codespell - comment-summary-in-pr: on-failure diff --git a/.github/workflows/sbom.yml b/.github/workflows/sbom.yml index 5e958be4..02c82e12 100644 --- a/.github/workflows/sbom.yml +++ b/.github/workflows/sbom.yml @@ -6,7 +6,14 @@ # - CycloneDX SBOM generation # - Trivy vulnerability scanning # - License compliance checking -# - SARIF upload to GitHub Security tab +# - SBOM/artifact results published as downloadable build artifacts. The +# upstream python-sbom.yml reusable workflow also attempts a SARIF +# upload to the GitHub Security tab for its Trivy runtime-deps scan; +# that upload is a no-op on this repo since GitHub Advanced Security +# (Code Security) is not enabled, and updating it is out of scope here +# (owned by ByronWilliamsCPA/.github). That scan has no artifact +# fallback at the currently pinned SHA, so its findings currently go +# nowhere; flagged as a follow-up. name: SBOM & Security Scan on: diff --git a/.github/workflows/scorecard.yml b/.github/workflows/scorecard.yml index 70b21c4d..6e64a56a 100644 --- a/.github/workflows/scorecard.yml +++ b/.github/workflows/scorecard.yml @@ -23,8 +23,14 @@ jobs: scorecard: name: Scorecard Analysis permissions: - security-events: write id-token: write contents: read actions: read uses: ByronWilliamsCPA/.github/.github/workflows/python-scorecard.yml@961eb17d8e9b7fe0d8bfc5dbe9d23c824484fb11 # main + with: + # GitHub Advanced Security (Code Security) is not enabled on this repo, + # so the reusable workflow's upload-sarif step (default: true) has no + # destination to post to. The callee's "Upload Scorecard artifact" + # step runs unconditionally, so results remain reviewable as a build + # artifact. + upload-sarif: false diff --git a/.secrets.baseline b/.secrets.baseline index 7889add6..145ce97e 100644 --- a/.secrets.baseline +++ b/.secrets.baseline @@ -211,7 +211,7 @@ "filename": "README.md", "hashed_secret": "f7485c89e918d73cb3096af4ff0b916a27bf9a5d", "is_verified": false, - "line_number": 317 + "line_number": 320 } ], "central-services/auth-service/README.md": [ @@ -306,5 +306,5 @@ } ] }, - "generated_at": "2026-05-31T05:04:52Z" + "generated_at": "2026-09-02T21:12:25Z" } diff --git a/CHANGELOG.md b/CHANGELOG.md index 4e881694..d8aac336 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -123,6 +123,28 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 SonarCloud quality-gate API. Generated child projects continue to opt into their own SonarCloud analysis via the rendered config that ships inside the template's rendered output directory. +- GitHub Advanced Security (Code Security) dependent CI, fleet-wide: GitHub + now bills this feature, so CodeQL code scanning, `actions/dependency-review-action`, + and SARIF ingestion into the Security tab no longer function without it. + Removed `.github/workflows/codeql.yml`, `.github/workflows/dependency-review.yml`, + `.github/codeql-config.yml`, `.github/codeql/codeql-config.yml`, and + `.codeqlignore` from this template repository's own CI, plus + `{{cookiecutter.project_slug}}/.github/workflows/dependency-review.yml` + from generated projects. Removed the direct `github/codeql-action/upload-sarif` + step from `{{cookiecutter.project_slug}}/.github/workflows/cifuzzy.yml` + (replaced with an `actions/upload-artifact` step) and from the standalone + branch of `{{cookiecutter.project_slug}}/.github/workflows/scorecard.yml` + (an artifact upload of the same file already existed). Flipped + `upload-sarif: true` to `false` on the org-workflow callers that expose + that toggle (`.github/workflows/scorecard.yml`, + `{{cookiecutter.project_slug}}/.github/workflows/scorecard.yml`, + `{{cookiecutter.project_slug}}/.github/workflows/container-security.yml`); + each callee already publishes an unconditional `actions/upload-artifact` + fallback, so no findings are lost. `run-codeql` / `run-dependency-review` + input lines on `security-analysis.yml` callers were intentionally left + untouched: their defaults live in the shared `ByronWilliamsCPA/.github` + reusable workflow and are being retired there in a separate coordinated + change. ### Fixed diff --git a/README.md b/README.md index 452419c4..14b94818 100644 --- a/README.md +++ b/README.md @@ -221,7 +221,10 @@ Complete project structure with: - ✅ **Architecture Decision Records** (ADRs) **Security (OWASP Top 10 Compliant):** -- ✅ **7+ security tools** (Bandit, Safety, OSV-Scanner, CodeQL, Gitleaks, etc.) +- ✅ **Security tools** (Bandit, OSV-Scanner, Trivy, Gitleaks, etc.); the org-workflow variant + can also enable CodeQL and `actions/dependency-review-action` via `security-analysis.yml`, + but both require GitHub Advanced Security (Code Security), a paid GitHub feature not assumed + to be enabled on generated projects - ✅ **SBOM generation** (CycloneDX format) - ✅ **API security middleware** (headers, rate limiting, SSRF prevention) - ✅ **Secrets scanning** with Gitleaks diff --git a/docs/org-workflows/python-sbom.yml b/docs/org-workflows/python-sbom.yml index 1a85bd98..9d9a7014 100644 --- a/docs/org-workflows/python-sbom.yml +++ b/docs/org-workflows/python-sbom.yml @@ -11,7 +11,10 @@ # - CycloneDX SBOM generation # - Trivy vulnerability scanning # - License compliance checking -# - SARIF upload to GitHub Security tab +# - Trivy runtime-deps SARIF published as a downloadable build artifact. +# github/codeql-action/upload-sarif requires GitHub Advanced Security +# (Code Security), a paid GitHub feature; do not assume it is enabled on +# the calling repo. # ============================================================================ name: Reusable Python SBOM @@ -106,7 +109,6 @@ jobs: runs-on: ubuntu-latest permissions: contents: read - security-events: write # Required for SARIF upload steps: - name: Download SBOM artifacts uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8 @@ -131,12 +133,18 @@ jobs: format: sarif output: trivy-runtime-results.sarif - - name: Upload Trivy results to GitHub Security tab + # GitHub Advanced Security (Code Security) requires a paid plan; do not + # assume it is enabled on the calling repo. github/codeql-action/upload-sarif + # has no destination to post to without it. Publish the SARIF as a + # downloadable artifact instead so findings remain reviewable. + - name: Upload Trivy SARIF artifact if: always() - uses: github/codeql-action/upload-sarif@48ab28a6f5dbc2a99bf1e0131198dd8f1df78169 # v3.27.5 + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: - sarif_file: trivy-runtime-results.sarif - category: trivy-runtime-deps + name: trivy-runtime-sarif + path: trivy-runtime-results.sarif + retention-days: ${{ inputs.artifact-retention-days }} + if-no-files-found: warn # ============================================================================ # Job 3: License Compliance Check diff --git a/docs/secure.md b/docs/secure.md index 1c33e14d..0378cd56 100644 --- a/docs/secure.md +++ b/docs/secure.md @@ -211,7 +211,6 @@ on: permissions: contents: read - security-events: write # For SARIF upload env: INFISICAL_DOMAIN: https://secrets.byronwilliamscpa.com @@ -288,34 +287,42 @@ jobs: INFISICAL_CLIENT_SECRET: ${{ secrets.INFISICAL_CLIENT_SECRET }} ``` -### 3.3 Dependency Review (PRs) +### 3.3 Dependency Vetting (PRs) -Add dependency review for pull requests: +`actions/dependency-review-action` relies on GitHub's dependency-graph diff +and GitHub Advanced Security (Code Security), a paid GitHub feature; without +it the action either fails to resolve a diff or (with `continue-on-error`) +silently reports nothing. Do not add `dependency-review.yml` unless GHAS is +actually enabled and verified on the target repo. + +For projects without GHAS, gate PRs with OSV-Scanner against the lockfile +instead, which needs no GitHub billing feature and no API key: ```yaml # .github/workflows/dependency-review.yml -name: Dependency Review +name: Dependency Vetting on: pull_request: branches: [main] + paths: + - 'pyproject.toml' + - 'uv.lock' permissions: contents: read - pull-requests: write jobs: - dependency-review: + osv-scan: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - name: Dependency Review - uses: actions/dependency-review-action@v4 + - name: OSV-Scanner dependency scan + uses: google/osv-scanner-action/osv-scanner-action@v2.3.8 with: - fail-on-severity: high - deny-licenses: GPL-3.0, AGPL-3.0 - allow-licenses: MIT, Apache-2.0, BSD-2-Clause, BSD-3-Clause, ISC + scan-args: |- + --lockfile=./uv.lock ``` --- diff --git a/tests/test_supply_chain_security.py b/tests/test_supply_chain_security.py index cbd08b5a..371351ac 100644 --- a/tests/test_supply_chain_security.py +++ b/tests/test_supply_chain_security.py @@ -3,7 +3,6 @@ from __future__ import annotations import json -import re from typing import TYPE_CHECKING, Any import pytest @@ -78,24 +77,26 @@ def test_setup_supply_chain_script_created( "Script should reference Infisical" ) - def test_dependency_review_workflow_created( + def test_dependency_review_workflow_not_generated( self, template_dir: Path, temp_dir: Path, supply_chain_config: dict[str, Any] ) -> None: - """Verify dependency-review.yml workflow is created.""" + """Verify dependency-review.yml is never generated, even with supply chain security enabled. + + `actions/dependency-review-action` depends on GitHub's dependency-graph + diff and GitHub Advanced Security (Code Security), a paid GitHub + feature. GitHub now bills that feature, so the action no longer + functions without it. The workflow template was removed fleet-wide; + see CHANGELOG.md [Unreleased] > Removed. + """ from tests.conftest import generate_project project_dir = generate_project(template_dir, temp_dir, supply_chain_config) workflow_file = project_dir / ".github" / "workflows" / "dependency-review.yml" - assert workflow_file.exists(), "dependency-review.yml should exist" - - content = workflow_file.read_text() - - # Verify workflow structure - assert "Dependency Review" in content, "Workflow should have Dependency Review name" - assert "pull_request" in content, "Workflow should trigger on pull requests" - assert "dependency-review-action" in content, "Workflow should use dependency-review-action" - assert "fail-on-severity" in content, "Workflow should have severity configuration" + assert not workflow_file.exists(), ( + "dependency-review.yml should not exist; actions/dependency-review-action " + "requires GitHub Advanced Security and was removed fleet-wide" + ) def test_pyproject_has_uv_index_config( self, template_dir: Path, temp_dir: Path, supply_chain_config: dict[str, Any] @@ -276,7 +277,12 @@ def test_setup_script_not_created( def test_dependency_review_workflow_not_created( self, template_dir: Path, temp_dir: Path, no_supply_chain_config: dict[str, Any] ) -> None: - """Verify dependency-review.yml is NOT created when disabled.""" + """Verify dependency-review.yml is NOT created when disabled. + + The workflow template was removed fleet-wide regardless of this flag + (see test_dependency_review_workflow_not_generated), so this remains + true; kept as regression coverage for the disabled path specifically. + """ from tests.conftest import generate_project project_dir = generate_project(template_dir, temp_dir, no_supply_chain_config) @@ -411,55 +417,23 @@ def supply_chain_project( return generate_project(template_dir, temp_dir, config) def test_dependency_review_license_config(self, supply_chain_project: Path) -> None: - """Verify dependency review workflow has proper license configuration.""" + """Verify the copyleft license deny-list is gone along with dependency-review.yml. + + This deny-list (AGPL/GPL/LGPL family SPDX ids) previously lived + exclusively in dependency-review.yml's `deny-licenses` input. + actions/dependency-review-action requires GitHub Advanced Security + (Code Security), a paid GitHub feature, so the workflow was removed + fleet-wide (see CHANGELOG.md [Unreleased] > Removed). Generated + projects currently have no equivalent copyleft-license gate; + `sbom.yml`'s `fail-on-forbidden-licenses` input defaults to `false` + and does not carry this deny-list. Tracked as a known coverage gap + pending a replacement license gate. + """ workflow = supply_chain_project / ".github" / "workflows" / "dependency-review.yml" - content = workflow.read_text() - - # Uses deny-list approach: copyleft licenses explicitly blocked, - # permissive licenses (MIT, Apache-2.0) allowed implicitly. - - assert "deny-licenses" in content, "Should use deny-list approach" - - # Strong copyleft variants that must be denied (AGPL family + GPLv2/3) - denied_required = [ - "AGPL-3.0", - "AGPL-3.0-only", - "AGPL-3.0-or-later", - "GPL-2.0-only", - "GPL-2.0-or-later", - "GPL-3.0-only", - "GPL-3.0-or-later", - "LGPL-2.0-only", - "LGPL-2.1-only", - "LGPL-3.0-only", - "LGPL-3.0-or-later", - ] - for spdx in denied_required: - assert spdx in content, f"Should deny {spdx}" - - # Negative test: permissive licenses MUST NOT appear in the deny-list. - # The comment above claims they are allowed implicitly; this assertion - # verifies the claim by failing if any permissive SPDX identifier - # accidentally lands in deny-licenses. - permissive_must_not_be_denied = [ - "MIT", - "Apache-2.0", - "BSD-3-Clause", - "BSD-2-Clause", - "ISC", - ] - # Look at only the deny-licenses section to avoid false positives from - # other YAML keys that may legitimately mention permissive licenses. - deny_section_match = re.search( - r"deny-licenses:\s*[\"']?([^\n]+(?:\n\s+[^\n]+)*)", - content, + assert not workflow.exists(), ( + "dependency-review.yml should not exist; its deny-licenses " + "configuration was removed along with it" ) - deny_block = deny_section_match.group(1) if deny_section_match else content - for spdx in permissive_must_not_be_denied: - # Use word-boundary regex so MIT does not match e.g. MITRE - assert not re.search(rf"\b{re.escape(spdx)}\b", deny_block), ( - f"{spdx} is a permissive license and must not appear in deny-licenses" - ) def test_gcp_variables_rendered_in_pyproject(self, supply_chain_project: Path) -> None: """Verify GCP variables are properly rendered in pyproject.toml.""" diff --git a/{{cookiecutter.project_slug}}/.github/workflows/cifuzzy.yml b/{{cookiecutter.project_slug}}/.github/workflows/cifuzzy.yml index 1c9cba21..73f7d141 100644 --- a/{{cookiecutter.project_slug}}/.github/workflows/cifuzzy.yml +++ b/{{cookiecutter.project_slug}}/.github/workflows/cifuzzy.yml @@ -11,7 +11,10 @@ # # Duration: 600 seconds per run # Sanitizer: AddressSanitizer for memory safety -# Reporting: SARIF format uploaded to Security tab +# Reporting: SARIF format published as a downloadable build artifact. +# GitHub Advanced Security (Code Security) requires a paid plan and is not +# assumed to be enabled on projects generated from this template, so +# results are not uploaded to the Security tab. {%- if cookiecutter.include_fuzzing == "yes" %} name: Continuous Fuzzing @@ -29,7 +32,6 @@ on: permissions: contents: read - security-events: write # For SARIF upload jobs: fuzzing: @@ -79,12 +81,19 @@ jobs: sanitizer: {% raw %}${{ matrix.sanitizer }}{% endraw %} output-sarif: true - - name: Upload SARIF + # GitHub Advanced Security (Code Security) requires a paid plan and is + # not assumed to be enabled on projects generated from this template, + # so github/codeql-action/upload-sarif has no destination to post to. + # Publish the SARIF as a downloadable artifact instead so findings + # remain reviewable. + - name: Upload fuzzing SARIF artifact if: always() && steps.check-fuzz.outputs.has-targets == 'true' - uses: github/codeql-action/upload-sarif@8aad20d150bbac5944a9f9d289da16a4b0d87c1e # v4.36.2 + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: - sarif_file: results.sarif - category: fuzzing-{% raw %}${{ matrix.sanitizer }}{% endraw %} + name: fuzzing-sarif-{% raw %}${{ matrix.sanitizer }}{% endraw %} + path: results.sarif + retention-days: 30 + if-no-files-found: warn - name: Upload Crash Artifacts if: failure() && steps.check-fuzz.outputs.has-targets == 'true' diff --git a/{{cookiecutter.project_slug}}/.github/workflows/container-security.yml b/{{cookiecutter.project_slug}}/.github/workflows/container-security.yml index 4d7d84dd..ee8b5841 100644 --- a/{{cookiecutter.project_slug}}/.github/workflows/container-security.yml +++ b/{{cookiecutter.project_slug}}/.github/workflows/container-security.yml @@ -34,7 +34,6 @@ concurrency: permissions: contents: read - security-events: write packages: read jobs: @@ -51,7 +50,12 @@ jobs: run-hadolint: true hadolint-failure-threshold: 'warning' generate-sbom: true - upload-sarif: true + # GitHub Advanced Security (Code Security) requires a paid plan and is + # not assumed to be enabled on projects generated from this template. + # The reusable workflow's "Upload container security artifacts" step + # runs unconditionally (not gated on upload-sarif), so results remain + # reviewable as a build artifact even with SARIF upload disabled. + upload-sarif: false artifact-retention-days: 90 secrets: inherit {%- elif cookiecutter.include_github_actions == "yes" %} diff --git a/{{cookiecutter.project_slug}}/.github/workflows/dependency-review.yml b/{{cookiecutter.project_slug}}/.github/workflows/dependency-review.yml deleted file mode 100644 index 830c28a5..00000000 --- a/{{cookiecutter.project_slug}}/.github/workflows/dependency-review.yml +++ /dev/null @@ -1,49 +0,0 @@ -# Dependency Review - Analyzes dependency changes in PRs -# Checks for vulnerabilities and license compliance -# -# Features: -# - Vulnerability detection in dependency changes -# - License compliance checking -# - Blocks PRs with high severity vulnerabilities -{%- if cookiecutter.include_github_actions == "yes" and cookiecutter.include_supply_chain_security == "yes" %} -name: Dependency Review - -on: - pull_request: - branches: [main, master, develop] - paths: - - 'pyproject.toml' - - 'uv.lock' - - 'requirements*.txt' - -permissions: - contents: read - pull-requests: write - -jobs: - dependency-review: - name: Dependency Review - runs-on: ubuntu-latest - permissions: - contents: read - pull-requests: write - steps: - - name: Harden the runner - uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4 - with: - egress-policy: audit - - - name: Checkout repository - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7 - - - name: Dependency Review - uses: actions/dependency-review-action@a1d282b36b6f3519aa1f3fc636f609c47dddb294 # v5.0.0 - with: - fail-on-severity: high - # Deny copyleft and restrictive licenses (deny-list approach) - deny-licenses: AGPL-3.0, AGPL-3.0-only, AGPL-3.0-or-later, GPL-2.0-only, GPL-2.0-or-later, GPL-3.0-only, GPL-3.0-or-later, LGPL-2.0-only, LGPL-2.1-only, LGPL-3.0-only, LGPL-3.0-or-later - comment-summary-in-pr: on-failure -{%- else %} -# Dependency Review disabled -# Enable include_github_actions and include_supply_chain_security to use this workflow -{%- endif %} diff --git a/{{cookiecutter.project_slug}}/.github/workflows/sbom.yml b/{{cookiecutter.project_slug}}/.github/workflows/sbom.yml index 0c3b5460..3bfe4159 100644 --- a/{{cookiecutter.project_slug}}/.github/workflows/sbom.yml +++ b/{{cookiecutter.project_slug}}/.github/workflows/sbom.yml @@ -6,7 +6,12 @@ # - CycloneDX SBOM generation # - Trivy vulnerability scanning # - License compliance checking -# - SARIF upload to GitHub Security tab +# - SBOM/artifact results published as downloadable build artifacts. The +# upstream python-sbom.yml reusable workflow also attempts a SARIF +# upload to the GitHub Security tab for its Trivy runtime-deps scan; +# GitHub Advanced Security (Code Security) requires a paid plan and is +# not assumed to be enabled on projects generated from this template, so +# that upload will be a no-op unless the downstream project enables it. {%- if cookiecutter.include_github_actions == "yes" %} name: SBOM & Security Scan diff --git a/{{cookiecutter.project_slug}}/.github/workflows/scorecard.yml b/{{cookiecutter.project_slug}}/.github/workflows/scorecard.yml index 5d6b14d0..56f36bec 100644 --- a/{{cookiecutter.project_slug}}/.github/workflows/scorecard.yml +++ b/{{cookiecutter.project_slug}}/.github/workflows/scorecard.yml @@ -20,7 +20,6 @@ on: permissions: contents: read - security-events: write id-token: write actions: read @@ -31,7 +30,11 @@ jobs: name: Scorecard Analysis uses: {{ cookiecutter.github_org_or_user }}/.github/.github/workflows/python-scorecard.yml@main with: - upload-sarif: true + # GitHub Advanced Security (Code Security) requires a paid plan and is + # not assumed to be enabled on projects generated from this template. + # The reusable workflow's "Upload Scorecard artifact" step runs + # unconditionally, so results remain reviewable as a build artifact. + upload-sarif: false artifact-retention-days: 5 {%- else -%} # Standalone OpenSSF Scorecard workflow @@ -41,7 +44,6 @@ jobs: runs-on: ubuntu-latest permissions: contents: read - security-events: write id-token: write actions: read steps: @@ -66,11 +68,11 @@ jobs: # input (hyphen) removed from the org-workflow caller above. publish_results: true - - name: Upload SARIF results - uses: github/codeql-action/upload-sarif@8aad20d150bbac5944a9f9d289da16a4b0d87c1e # v4.36.2 - with: - sarif_file: results.sarif - + # GitHub Advanced Security (Code Security) requires a paid plan and is + # not assumed to be enabled on projects generated from this template, + # so github/codeql-action/upload-sarif has no destination to post to. + # The artifact upload immediately below already publishes the same + # results.sarif file, so no separate SARIF upload step is needed. - name: Upload Scorecard results as artifact uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: