From d8fc25931dff72587b5d4c665f6416a5e355cd5b Mon Sep 17 00:00:00 2001 From: Paul Calnon Date: Sun, 3 May 2026 19:22:10 -0500 Subject: [PATCH] ci: add blocking bandit gate (P-20) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cross-repo CI audit found juniper-data-client's bandit step was a single ``bandit … || true`` with no follow-up blocking invocation, so any genuine finding was silently tolerated. Mirror the canonical two-step pattern used in cascor / data (and now extended in juniper-data PR #67): 1. **SARIF generation** — ``bandit … -f sarif -o ... --exit-zero``. Always succeeds so the GitHub Security upload has a file regardless of findings. 2. **Blocking gate** — ``bandit … --confidence-level medium --severity-level medium``. Fails the job on medium-or-higher findings. Also harden the SARIF upload with ``hashFiles('reports/security/bandit.sarif') != ''`` to keep the upload step green if a future security-tools regression prevents bandit from emitting the file. Verified locally — current ``juniper_data_client`` has 0 medium / high findings, so the new gate is non-blocking on this commit. Any future medium-or-higher finding will now fail CI. Refs cross-repo CI audit P-20. --- .github/workflows/ci.yml | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e170ce1..8b56455 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -384,21 +384,36 @@ jobs: - name: Install security tools run: | python -m pip install --upgrade pip - pip install bandit pip-audit + # ``[sarif]`` extra installs the optional SARIF formatter (jschema-to-python). + pip install "bandit[sarif]" pip-audit - name: Create reports directory run: mkdir -p reports/security - - name: Run Bandit (SAST) -> SARIF + - name: Run Bandit (SAST) + # P-20 (cross-repo CI audit): mirror cascor / data's two-step + # bandit pattern. Step 1 emits SARIF with --exit-zero so the + # report always uploads; step 2 is the actual gate that fails + # the job on medium-severity findings. Previously this step was + # a single ``bandit … || true`` with no follow-up blocking + # invocation, so any genuine finding was silently tolerated. run: | echo "╔════════════════════════════════════════════════════════════╗" echo "║ juniper-data-client - Bandit Security Scan ║" echo "╚════════════════════════════════════════════════════════════╝" - bandit -r juniper_data_client -f sarif -o reports/security/bandit.sarif || true + # SARIF (always succeeds for upload). + bandit -r juniper_data_client -f sarif -o reports/security/bandit.sarif --exit-zero + # Blocking — fails on medium or higher. + bandit -r juniper_data_client --confidence-level medium --severity-level medium - name: Upload Bandit SARIF to GitHub Security + # ``hashFiles`` returns empty when the file is absent; only + # attempt the upload when SARIF actually exists. Combined with + # ``continue-on-error`` this keeps the job green even if a + # security-tools regression prevents bandit from emitting the + # file (mirrors the canonical pattern from juniper-data PR #67). + if: always() && hashFiles('reports/security/bandit.sarif') != '' uses: github/codeql-action/upload-sarif@95e58e9a2cdfd71adc6e0353d5c52f41a045d225 # v4.35.2 - if: always() with: sarif_file: reports/security/bandit.sarif continue-on-error: true