From e67954d80fb283c355f41bc50ca0c9b8d0deb63d Mon Sep 17 00:00:00 2001 From: Sebastian Mendel Date: Fri, 19 Dec 2025 11:01:19 +0100 Subject: [PATCH] Security: Harden GitHub workflows and document accepted risks Changes: - ci.yml: Add explicit permissions, pin all action SHAs - release.yml: Pin all action SHAs to immutable commits - Add CodeQL config documenting accepted low-risk URL substring alerts Security hardening: - actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 (v4.2.2) - actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 (v5.6.0) - codecov/codecov-action@18283e04ce6e62d37312384ff67231eb8fd56d24 (v5.4.3) - actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 (v4.6.2) - actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 (v4.3.0) - softprops/action-gh-release@da05d552573ad5aba039eaac05058a918a7bf631 (v2.2.2) - pypa/gh-action-pypi-publish@76f52bc884231f62b9a034ebfe128415bbaabdfc (v1.12.4) Accepted risks documented in codeql-config.yml: - py/incomplete-url-substring-sanitization: Used for version detection only - py/overly-permissive-file: Test code operating on temp directories --- .github/codeql/codeql-config.yml | 23 ++++++++++++++++++++++ .github/workflows/ci.yml | 33 +++++++++++++++++--------------- .github/workflows/release.yml | 22 ++++++++++----------- 3 files changed, 52 insertions(+), 26 deletions(-) create mode 100644 .github/codeql/codeql-config.yml diff --git a/.github/codeql/codeql-config.yml b/.github/codeql/codeql-config.yml new file mode 100644 index 0000000..9857748 --- /dev/null +++ b/.github/codeql/codeql-config.yml @@ -0,0 +1,23 @@ +# CodeQL Configuration +# Documentation of alert handling for this repository + +name: "CLI Audit CodeQL Config" + +# Note: Some alerts are documented as accepted risks: +# +# 1. py/incomplete-url-substring-sanitization in cli_audit/catalog.py +# - These are URL substring checks used for VERSION DETECTION only +# - They determine which API to query for latest versions (npm, pypi, etc.) +# - The package names come from the internal catalog, not user input +# - No installation decisions are made based on these checks +# - Security impact is LOW - worst case is incorrect version lookup +# +# 2. py/overly-permissive-file in tests/test_bulk.py +# - This is TEST CODE operating on temporary directories +# - Testing permission handling and error recovery +# - Using 0o755 on temp dir is appropriate for test cleanup + +# Query configuration +queries: + - uses: security-extended + - uses: security-and-quality diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 59c96c7..557c0ca 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -7,6 +7,9 @@ on: branches: [main, develop] workflow_dispatch: +permissions: + contents: read + jobs: lint: name: Lint and Type Check @@ -14,10 +17,10 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v6 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - name: Set up Python - uses: actions/setup-python@v5 + uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0 with: python-version: '3.14' cache: 'pip' @@ -48,10 +51,10 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v6 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v5 + uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0 with: python-version: ${{ matrix.python-version }} cache: 'pip' @@ -70,7 +73,7 @@ jobs: pytest tests/integration -v --cov=cli_audit --cov-append --cov-report=xml --cov-report=term - name: Upload coverage to Codecov - uses: codecov/codecov-action@v5 + uses: codecov/codecov-action@18283e04ce6e62d37312384ff67231eb8fd56d24 # v5.4.3 with: file: ./coverage.xml flags: unittests @@ -83,10 +86,10 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v6 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - name: Set up Python - uses: actions/setup-python@v5 + uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0 with: python-version: '3.14' @@ -114,10 +117,10 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v6 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - name: Set up Python - uses: actions/setup-python@v5 + uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0 with: python-version: '3.14' @@ -135,7 +138,7 @@ jobs: twine check dist/* - name: Upload artifacts - uses: actions/upload-artifact@v6 + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 with: name: distributions path: dist/ @@ -146,10 +149,10 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v6 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - name: Set up Python - uses: actions/setup-python@v5 + uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0 with: python-version: '3.14' @@ -171,10 +174,10 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v6 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - name: Set up Python - uses: actions/setup-python@v5 + uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0 with: python-version: '3.14' @@ -190,4 +193,4 @@ jobs: - name: Test programmatic API run: | - python -c "from cli_audit import Config, Environment, load_config; c = Config(); print('✓ API works')" + python -c "from cli_audit import Config, Environment, load_config; c = Config(); print('API works')" diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 9af4282..cdaad9d 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -16,10 +16,10 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v6 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - name: Set up Python - uses: actions/setup-python@v5 + uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0 with: python-version: '3.14' @@ -37,7 +37,7 @@ jobs: twine check dist/* - name: Upload artifacts - uses: actions/upload-artifact@v6 + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 with: name: distributions path: dist/ @@ -49,10 +49,10 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v6 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - name: Download artifacts - uses: actions/download-artifact@v7 + uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 with: name: distributions path: dist/ @@ -71,13 +71,13 @@ jobs: sed -n "/^## \[${VERSION}\]/,/^## \[/p" CHANGELOG.md | sed '$d' >> $GITHUB_OUTPUT || echo "See CHANGELOG.md for details" >> $GITHUB_OUTPUT echo "EOF" >> $GITHUB_OUTPUT else - echo "CHANGES=Release ${{ steps.version.outputs.VERSION }}" >> $GITHUB_OUTPUT + echo "CHANGES=Release ${VERSION}" >> $GITHUB_OUTPUT fi env: VERSION: ${{ steps.version.outputs.VERSION }} - name: Create Release - uses: softprops/action-gh-release@v2 + uses: softprops/action-gh-release@da05d552573ad5aba039eaac05058a918a7bf631 # v2.2.2 with: files: dist/* body: ${{ steps.changelog.outputs.CHANGES }} @@ -94,13 +94,13 @@ jobs: steps: - name: Download artifacts - uses: actions/download-artifact@v7 + uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 with: name: distributions path: dist/ - name: Publish to PyPI - uses: pypa/gh-action-pypi-publish@release/v1 + uses: pypa/gh-action-pypi-publish@76f52bc884231f62b9a034ebfe128415bbaabdfc # v1.12.4 with: password: ${{ secrets.PYPI_API_TOKEN }} skip-existing: true @@ -113,13 +113,13 @@ jobs: steps: - name: Download artifacts - uses: actions/download-artifact@v7 + uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 with: name: distributions path: dist/ - name: Publish to Test PyPI - uses: pypa/gh-action-pypi-publish@release/v1 + uses: pypa/gh-action-pypi-publish@76f52bc884231f62b9a034ebfe128415bbaabdfc # v1.12.4 with: password: ${{ secrets.TEST_PYPI_API_TOKEN }} repository-url: https://test.pypi.org/legacy/