diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3d903d5..a5ffea4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -11,6 +11,9 @@ concurrency: cancel-in-progress: false jobs: + security-codeql: + uses: ./.github/workflows/security-codeql.yml + test-unit: uses: ./.github/workflows/test-unit.yml @@ -37,3 +40,6 @@ jobs: lint-python: uses: ./.github/workflows/lint-python.yml + + lint-docker: + uses: ./.github/workflows/lint-docker.yml diff --git a/.github/workflows/lint-docker.yml b/.github/workflows/lint-docker.yml new file mode 100644 index 0000000..f54f2d7 --- /dev/null +++ b/.github/workflows/lint-docker.yml @@ -0,0 +1,40 @@ +name: Docker Linter + +on: + workflow_call: + +permissions: + contents: read + +jobs: + lint-docker: + name: Lint Dockerfile + runs-on: ubuntu-latest + permissions: + contents: read + security-events: write + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Run hadolint (produce SARIF) + id: hadolint + continue-on-error: true + uses: hadolint/hadolint-action@2332a7b74a6de0dda2e2221d575162eba76ba5e5 + with: + dockerfile: ./Dockerfile + format: sarif + output-file: hadolint-results.sarif + failure-threshold: warning + + - name: Upload analysis results to GitHub + if: always() + uses: github/codeql-action/upload-sarif@v4 + with: + sarif_file: hadolint-results.sarif + wait-for-processing: true + category: hadolint + + - name: Fail if SARIF contains warnings or errors + if: always() + run: python3 src/pkgmgr/github/check_hadolint_sarif.py hadolint-results.sarif diff --git a/.github/workflows/security-codeql.yml b/.github/workflows/security-codeql.yml new file mode 100644 index 0000000..8ebd171 --- /dev/null +++ b/.github/workflows/security-codeql.yml @@ -0,0 +1,47 @@ +name: CodeQL Advanced + +on: + workflow_call: + +jobs: + analyze: + name: Check security + runs-on: ubuntu-latest + permissions: + security-events: write + packages: read + contents: read + + strategy: + fail-fast: false + matrix: + include: + - language: actions + build-mode: none + - language: python + build-mode: none + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Initialize CodeQL + uses: github/codeql-action/init@v4 + with: + languages: ${{ matrix.language }} + build-mode: ${{ matrix.build-mode }} + queries: security-extended,security-and-quality + + - name: Run manual build steps + if: matrix.build-mode == 'manual' + shell: bash + run: | + echo 'If you are using a "manual" build mode for one or more of the' \ + 'languages you are analyzing, replace this with the commands to build' \ + 'your code.' + exit 1 + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v4 + with: + category: "/language:${{ matrix.language }}" diff --git a/src/pkgmgr/github/__init__.py b/src/pkgmgr/github/__init__.py new file mode 100644 index 0000000..1da2028 --- /dev/null +++ b/src/pkgmgr/github/__init__.py @@ -0,0 +1 @@ +"""GitHub-related Python helpers for pkgmgr.""" diff --git a/src/pkgmgr/github/check_hadolint_sarif.py b/src/pkgmgr/github/check_hadolint_sarif.py new file mode 100644 index 0000000..45053ce --- /dev/null +++ b/src/pkgmgr/github/check_hadolint_sarif.py @@ -0,0 +1,28 @@ +#!/usr/bin/env python3 +"""Fail when a hadolint SARIF report contains warnings or errors.""" + +from __future__ import annotations + +import json +import sys +from pathlib import Path + + +def main() -> int: + sarif_path = Path(sys.argv[1] if len(sys.argv) > 1 else "hadolint-results.sarif") + + with sarif_path.open("r", encoding="utf-8") as handle: + sarif = json.load(handle) + + results = sarif.get("runs", [{}])[0].get("results", []) + levels = [result.get("level", "") for result in results] + warnings = sum(1 for level in levels if level == "warning") + errors = sum(1 for level in levels if level == "error") + + print(f"SARIF results: total={len(results)} warnings={warnings} errors={errors}") + + return 1 if warnings + errors > 0 else 0 + + +if __name__ == "__main__": + raise SystemExit(main())