Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 57 additions & 0 deletions .github/workflows/security.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
name: Security

on:
push:
branches: [main]
pull_request:
schedule:
- cron: '0 6 * * 1'

permissions:
contents: read

jobs:
codeql:
name: CodeQL (${{ matrix.language }})
runs-on: ubuntu-latest
timeout-minutes: 30
concurrency:
group: check-${{ github.workflow }}-${{ github.ref }}-codeql-${{ matrix.language }}
cancel-in-progress: true
permissions:
contents: read
security-events: write
strategy:
fail-fast: false
matrix:
language: [python, actions]
steps:
- uses: actions/checkout@v6

- name: Initialize CodeQL
uses: github/codeql-action/init@v4
with:
languages: ${{ matrix.language }}

- name: Autobuild
uses: github/codeql-action/autobuild@v4

- name: Analyze
uses: github/codeql-action/analyze@v4

dependency-review:
name: Dependency Review
if: github.event_name == 'pull_request'
runs-on: ubuntu-latest
timeout-minutes: 10
permissions:
contents: read
pull-requests: write
steps:
- uses: actions/checkout@v6

- name: Review dependency changes
uses: actions/dependency-review-action@v5
with:
fail-on-severity: high
comment-summary-in-pr: on-failure
4 changes: 4 additions & 0 deletions changelog.d/20260809_issue_48_security_scanning.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
### Security

- Add CodeQL scanning for Python code and GitHub Actions workflows, plus
dependency review for pull requests.
29 changes: 29 additions & 0 deletions tests/test_workflows.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,35 @@ def test_workflow_run_blocks_do_not_interpolate_untrusted_inputs() -> None:
)


def test_security_workflow_scans_code_actions_and_dependencies() -> None:
"""Security checks must cover pushes, pull requests, and scheduled scans."""
workflow = read_workflow("security.yml")
codeql_job = workflow_job_block(workflow, "codeql")
dependency_job = workflow_job_block(workflow, "dependency-review")

assert "branches: [main]" in workflow
assert "pull_request:" in workflow
assert "schedule:" in workflow
assert "cron: '0 6 * * 1'" in workflow
assert "permissions:\n contents: read" in workflow

assert "timeout-minutes: 30" in codeql_job
assert "security-events: write" in codeql_job
assert "language: [python, actions]" in codeql_job
assert "languages: ${{ matrix.language }}" in codeql_job
assert "uses: github/codeql-action/init@v4" in codeql_job
assert "uses: github/codeql-action/autobuild@v4" in codeql_job
assert "uses: github/codeql-action/analyze@v4" in codeql_job
assert "cancel-in-progress: true" in codeql_job

assert "if: github.event_name == 'pull_request'" in dependency_job
assert "timeout-minutes: 10" in dependency_job
assert "pull-requests: write" in dependency_job
assert "uses: actions/dependency-review-action@v5" in dependency_job
assert "fail-on-severity: high" in dependency_job
assert "comment-summary-in-pr: on-failure" in dependency_job


def test_changelog_check_safely_requires_a_fragment() -> None:
"""Source-changing pull requests must fail safely without a fragment."""
workflow = read_workflow("release.yml")
Expand Down
Loading