Skip to content
Open
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
113 changes: 113 additions & 0 deletions .github/workflows/security.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
name: Security

permissions:
contents: read

on:
push:
tags-ignore:
- '*'
branches:
- '*'
pull_request:
workflow_call:
workflow_dispatch:
inputs:
debug:
description: 'Open ssh debug session.'
required: true
default: false
type: boolean

jobs:

security:
runs-on: ubuntu-latest
strategy:
matrix:
# run security checks on bleeding and trailing edges
python-version: [ '3.10', '3.12', '3.14' ]
django-version:
- '4.2' # LTS April 2026
- '5.2' # LTS April December 2027
- '6.0' #
exclude:
- python-version: '3.12'
django-version: '4.2'
- python-version: '3.14'
django-version: '4.2'

- python-version: '3.10'
django-version: '5.2'
- python-version: '3.14'
django-version: '5.2'

- python-version: '3.10'
django-version: '6.0'
- python-version: '3.12'
django-version: '6.0'

env:
TEST_PYTHON_VERSION: ${{ matrix.python-version }}
TEST_DJANGO_VERSION: ${{ matrix.django-version }}
steps:
- uses: actions/checkout@v5
with:
persist-credentials: false
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
id: sp
with:
python-version: ${{ matrix.python-version }}

- name: Install uv
uses: astral-sh/setup-uv@v6
with:
enable-cache: true
- name: Install Just
uses: extractions/setup-just@v3
- name: Install Dependencies
run: |
just setup ${{ steps.sp.outputs.python-path }}
if [[ "${{ matrix.django-version }}" =~ (a|b|rc) ]]; then
just test-lock Django==${{ matrix.django-version }}
else
just test-lock Django~=${{ matrix.django-version }}.0
fi
- name: Install Emacs
if: ${{ github.event.inputs.debug == 'true' }}
run: |
sudo apt install emacs
- name: Setup tmate session
if: ${{ github.event.inputs.debug == 'true' }}
uses: mxschmitt/[email protected]
with:
detached: true
timeout-minutes: 60
- name: Run Bandit Security Analysis
continue-on-error: true
run: |
just run bandit -c pyproject.toml -r src/polymorphic -f json -o bandit-report.json || true
just run bandit -c pyproject.toml -r src/polymorphic -f txt -o bandit-report.txt || true
- name: Run pip-audit Dependency Scanning
continue-on-error: true
run: |
just run pip-audit --disable-pip --format json --output pip-audit-report.json || true
just run pip-audit --disable-pip --format cyclonedx-json --output pip-audit-sbom.json || true
- name: Run Safety Vulnerability Checks
continue-on-error: true
run: |
just run safety check --json --output safety-report.json || true
- name: Upload Security Reports
if: always()
uses: actions/upload-artifact@v4
with:
name: security-reports-py${{ matrix.python-version }}-dj${{ matrix.django-version }}
path: |
bandit-report.json
bandit-report.txt
pip-audit-report.json
pip-audit-sbom.json
safety-report.json
retention-days: 30

14 changes: 13 additions & 1 deletion justfile
Original file line number Diff line number Diff line change
Expand Up @@ -168,8 +168,20 @@ lint: sort-imports
# fix formatting, linting issues and import sorting
fix: lint format

# run bandit static security analysis
check-security-bandit:
@just run bandit -c pyproject.toml -r src/polymorphic

# run dependency vulnerability scanning
check-security-deps:
@just run pip-audit --disable-pip
@just run safety check --json

# run all security checks
check-security: check-security-bandit check-security-deps

# run all static checks
check: check-lint check-format check-types check-package check-docs check-docs-links _check-readme-quiet
check: check-lint check-format check-types check-package check-security check-docs check-docs-links _check-readme-quiet

[script]
_lock-python:
Expand Down
13 changes: 13 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,16 @@ artifacts = ["*.mo"]
max-line-length = 100
sphinx = true

[tool.bandit]
exclude_dirs = [
"*/tests/*",
"*/migrations/*",
"example",
".venv",
"docs",
]
skips = ["B101"] # assert_used - assertions are fine in tests and Django code

[tool.ruff]
line-length = 99

Expand Down Expand Up @@ -141,18 +151,21 @@ show_missing = true

[dependency-groups]
dev = [
"bandit[toml]>=1.7.10",
"coverage>=7.6.1",
"dj-database-url>=2.2.0",
"django-test-migrations>=1.5.0",
"ipdb>=0.13.13",
"ipython>=8.18.1",
"mypy>=1.14.1",
"pip-audit>=2.7.3",
"pre-commit>=3.5.0",
"pytest>=8.3.4",
"pytest-cov>=5.0.0",
"pytest-django>=4.10.0",
"pytest-playwright>=0.7.2",
"ruff>=0.9.8",
"safety>=3.2.11",
"tomlkit>=0.13.3",
"tox>=4.24.1",
"tox-uv>=1.13.1",
Expand Down
Loading
Loading