Audit dependencies and type-check on PRs; drop scheduled SBOM scan

This commit is contained in:
mdaleo404 committed 2026-10-13 07:39:58 +01:00
1 parent 71ea28d33e
commit 194a0eb812
4 files changed
+193 -118

No files matched your search

+95 -12
View File
@@ -2,28 +2,111 @@ name: CI
on:
pull_request:
workflow_dispatch:
permissions:
contents: read
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
test:
lint-and-security:
name: lint-and-security
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
persist-credentials: false
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.13"
- name: Install Poetry
run: pip install "poetry>=2,<3"
- name: Install dev dependencies (lockfile-pinned pre-commit)
run: poetry install --with dev --no-root
- name: Run pre-commit hooks
run: poetry run pre-commit run --all-files --color always
- name: Type check
run: poetry run mypy
- name: Install pip-audit
run: pip install "pip-audit==2.10.1"
- name: Audit dependencies (Poetry lockfile)
run: |
set -euo pipefail
poetry self add poetry-plugin-export
poetry export -f requirements.txt --without-hashes -o requirements-audit.txt
# pip-audit exits non-zero for ANY vulnerability; capture the report, then gate below.
pip-audit --no-deps -r requirements-audit.txt -f json -o pip-audit.json || true
echo "Fixable vulnerabilities:"
jq -r '
.dependencies[]?
| select(has("vulns"))
| . as $d
| .vulns[]
| select((.fix_versions | length) > 0)
| "\($d.name)@\($d.version) -> \(.id) [fix: \(.fix_versions | join(", "))]"
' pip-audit.json
echo
echo "All reported vulnerabilities:"
jq -r '
.dependencies[]?
| select(has("vulns"))
| . as $d
| .vulns[]
| "\($d.name)@\($d.version) -> \(.id) [fix: \(if (.fix_versions | length) > 0 then (.fix_versions | join(", ")) else "none" end)]"
' pip-audit.json
# Fail only on fixable findings. The type guard ensures a crashed or empty
# report fails rather than silently passing. Dependency security updates
# are handled by the central Renovate job.
jq -e '
(.dependencies | type) == "array"
and
( [ .dependencies[] | (.vulns // [])[] | select((.fix_versions | length) > 0) ] | length == 0 )
' pip-audit.json
tests:
name: tests
runs-on: ubuntu-latest
timeout-minutes: 15
strategy:
fail-fast: false
matrix:
python-version: ["3.11", "3.13"]
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
- name: Checkout code
uses: actions/checkout@v4
with:
persist-credentials: false
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- name: Install project and tooling
run: pip install -e . pytest mypy pre-commit
- name: Install Poetry
run: pip install "poetry>=2,<3"
- name: Lint and typecheck
if: matrix.python-version == '3.13'
run: |
pre-commit run --all-files --color always
mypy
- name: Install project and dependencies
run: poetry install --with dev
- name: Unit and security tests
run: pytest tests/unit tests/security
- name: Run tests
run: poetry run pytest
-91
View File
@@ -1,91 +0,0 @@
name: Security Scan
on:
schedule:
- cron: 27 8 * * *
workflow_dispatch:
jobs:
security-scan:
runs-on: running-man
env:
TARGET_DIR: .
steps:
- name: Checkout
uses: actions/checkout@v4
# Tool versions are centralised in the shared actions; bump them there and
# re-tag rather than editing every repository.
- name: Install Grype
uses: https://git.sysmd.uk/mdaleo404/actions/install-grype@v1
- name: Install Syft
uses: https://git.sysmd.uk/mdaleo404/actions/install-syft@v1
- name: Generate SBOM
working-directory: ${{ env.TARGET_DIR }}
run: |
syft dir:. -o json > sbom.json
- name: Show SBOM contents
working-directory: ${{ env.TARGET_DIR }}
run: |
echo "Packages discovered by Syft:"
jq -r '.artifacts[] | "\(.name)@\(.version) [\(.type)]"' sbom.json | sort
# Composite actions run from the workspace root, so pass the target and
# report relative to it (TARGET_DIR is "." for most repositories).
- name: Scan SBOM with Grype
id: audit
continue-on-error: true
uses: https://git.sysmd.uk/mdaleo404/actions/scan-image@v1
with:
target: sbom:${{ env.TARGET_DIR }}/sbom.json
severities: Medium,High,Critical
report-path: ${{ env.TARGET_DIR }}/grype.json
- name: Show full Grype table
working-directory: ${{ env.TARGET_DIR }}
run: |
echo "Full Grype report:"
grype sbom:sbom.json -o table
- name: Notify Node-RED on vulnerabilities
if: steps.audit.outcome == 'failure'
working-directory: ${{ env.TARGET_DIR }}
run: |
jq '
{
repo: "mdaleo404/schedls",
summary: (
"Total: " +
(
[
.matches[]
| select((.vulnerability.fix.versions | length) > 0)
] | length | tostring
)
),
vulnerabilities: [
.matches[]
| select((.vulnerability.fix.versions | length) > 0)
| {
library: .artifact.name,
cve: .vulnerability.id,
severity: .vulnerability.severity,
installed: .artifact.version,
fixed: (.vulnerability.fix.versions[0]),
title: .vulnerability.description,
url: .vulnerability.dataSource
}
]
}
' grype.json \
| curl -s -X POST https://nodered.sysmd.uk/vulns-alert \
-H "Content-Type: application/json" \
--data-binary @-
- name: Fail workflow if vulnerabilities found
if: steps.audit.outcome == 'failure'
run: exit 1
+95 -12
View File
@@ -2,28 +2,111 @@ name: CI
on:
pull_request:
workflow_dispatch:
permissions:
contents: read
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
test:
lint-and-security:
name: lint-and-security
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
persist-credentials: false
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.13"
- name: Install Poetry
run: pip install "poetry>=2,<3"
- name: Install dev dependencies (lockfile-pinned pre-commit)
run: poetry install --with dev --no-root
- name: Run pre-commit hooks
run: poetry run pre-commit run --all-files --color always
- name: Type check
run: poetry run mypy
- name: Install pip-audit
run: pip install "pip-audit==2.10.1"
- name: Audit dependencies (Poetry lockfile)
run: |
set -euo pipefail
poetry self add poetry-plugin-export
poetry export -f requirements.txt --without-hashes -o requirements-audit.txt
# pip-audit exits non-zero for ANY vulnerability; capture the report, then gate below.
pip-audit --no-deps -r requirements-audit.txt -f json -o pip-audit.json || true
echo "Fixable vulnerabilities:"
jq -r '
.dependencies[]?
| select(has("vulns"))
| . as $d
| .vulns[]
| select((.fix_versions | length) > 0)
| "\($d.name)@\($d.version) -> \(.id) [fix: \(.fix_versions | join(", "))]"
' pip-audit.json
echo
echo "All reported vulnerabilities:"
jq -r '
.dependencies[]?
| select(has("vulns"))
| . as $d
| .vulns[]
| "\($d.name)@\($d.version) -> \(.id) [fix: \(if (.fix_versions | length) > 0 then (.fix_versions | join(", ")) else "none" end)]"
' pip-audit.json
# Fail only on fixable findings. The type guard ensures a crashed or empty
# report fails rather than silently passing. Dependency security updates
# are handled by the central Renovate job.
jq -e '
(.dependencies | type) == "array"
and
( [ .dependencies[] | (.vulns // [])[] | select((.fix_versions | length) > 0) ] | length == 0 )
' pip-audit.json
tests:
name: tests
runs-on: ubuntu-latest
timeout-minutes: 15
strategy:
fail-fast: false
matrix:
python-version: ["3.11", "3.13"]
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
- name: Checkout code
uses: actions/checkout@v4
with:
persist-credentials: false
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- name: Install project and tooling
run: pip install -e . pytest mypy pre-commit
- name: Install Poetry
run: pip install "poetry>=2,<3"
- name: Lint and typecheck
if: matrix.python-version == '3.13'
run: |
pre-commit run --all-files --color always
mypy
- name: Install project and dependencies
run: poetry install --with dev
- name: Unit and security tests
run: pytest tests/unit tests/security
- name: Run tests
run: poetry run pytest
+3 -3
View File
@@ -193,9 +193,9 @@ Formatting and linting use [ruff](https://docs.astral.sh/ruff/); security
scanning uses [Bandit](https://bandit.readthedocs.io/); both run through
[pre-commit](https://pre-commit.com/) alongside trailing-whitespace, end-of-file,
YAML and TOML checks. CI runs on pull requests, applying the same hooks and a
strict type check on Python 3.13, and running the unit and security tests on
Python 3.11 and 3.13. Build distributions locally with `poetry build`. The
Gitea scheduled workflow builds an SBOM and scans it with Grype.
strict type check on Python 3.13, a `pip-audit` dependency audit, and the test
suite on Python 3.11 and 3.13. Dependency security updates are handled centrally
by Renovate. Build distributions locally with `poetry build`.
## Support