Skip to content

Back to the catalog

SreeragPramod/securepipe
github.comrepo

SreeragPramod/securepipe

SecurePipe is a small demo GitHub repo showing a GitHub Actions CI/CD pipeline for a Python project that runs Flake8, Black, Bandit SAST scanning, and pytest with an 80% coverage gate before code can be merged. Its commit history documents a real vulnerability lifecycle: a hardcoded database password (Bandit rule B105) and a shell-injectable subprocess call (Bandit rule B602) were intentionally introduced, automatically blocked by the Bandit gate, then fixed by moving secrets to environment variables and replacing shell=True with an explicit argument list and host allowlist.

TL;DR A GitHub Actions pipeline demo where Bandit automatically catches and blocks a hardcoded secret and a shell-injection bug before merge.
Document

What it is

SecurePipe is a demo repository for a GitHub Actions pipeline on a small Python project (a calculator and a data-processing module). The pipeline is defined in .github/workflows/security-pipeline.yml, and a dependabot.yml file handles dependency monitoring. It is a teaching example, not a reusable template or action.

How it works

On each push to main, an Ubuntu runner sets up Python 3.13, installs dependencies, and runs the gates in a fixed order. A failure at any step stops everything after it.

flowchart TD
    A[Push to main] --> B[Flake8]
    B -->|pass| C[Black check]
    C -->|pass| D[Bandit scan]
    D -->|pass| E[Pytest, coverage 80%+]
    E -->|pass| F[Ready for deployment]
    B -->|fail| X[Blocked]
    C -->|fail| X
    D -->|fail| X
    E -->|fail| X

Bandit runs before pytest, so a Bandit failure skips the test suite entirely. Bandit and coverage reports are uploaded as artifacts. Coverage is enforced with --cov-fail-under=80.

What to do

The README gives the local equivalents of each gate:

bandit -r src/ -f txt
flake8 src/ tests/ --max-line-length=88 --extend-ignore=E203
black --check src/ tests/
python -m pytest tests/ -v --cov=src --cov-report=term-missing

The fixes the author applied to data_processor.py are a reusable pattern:

  • Read secrets from environment variables inside functions, not at import time. Module-level reads made the code untestable.
  • Replace shell=True with shell=False, an explicit argument list and a host allowlist.
  • Mock subprocess.run in tests and use assert_called_once_with. This avoids real network calls in CI, and the test fails if shell=True is reintroduced.
  • Add a coverage threshold. The author reports it exposed that data_processor.py had zero test coverage, which they had not noticed.

Caveats

The README lists several things as not yet done: branch protection rules, so the gate is advisory rather than mandatory; CodeQL; GitHub secret scanning; DAST with OWASP ZAP; Docker; and pre-commit hooks. The pipeline stops at "ready for deployment" and has no deploy stage. Bandit is a pattern-matching scanner, and the README itself names CodeQL as the route to deeper detection. The vulnerabilities were planted by the author, so the repo shows the gate catching two known Bandit rules (B105, B602), not general detection performance.

Organisations named here

  • B105
  • B602

Technologies named here

  • Python
  • GitHub Actions
  • Bandit
  • Flake8
  • Black
  • Pytest
  • pytest-cov
  • Dependabot
  • OWASP Zap
  • CodeQL
  • GitHub Advanced Security secret scanning

Techniques named here

  • Shift-Left security
  • Hardcoded credential
  • Shell injection via subprocess
  • Pipeline-as-Code
  • Mocking subprocess calls in tests