FlowSec
A Python command-line security tool that scans CI/CD pipeline configurations for attack vectors across GitHub Actions, GitLab CI, and Azure DevOps. Every finding maps to a MITRE ATT&CK technique and an OWASP CICD Top 10 category.
The pipeline is the attack surface. FlowSec treats it that way.
Install
Homebrew (macOS):
brew install VanshBhardwaj1945/flowsec/flowsec
PyPI:
pip install flowsec # local file/directory scanning
pip install "flowsec[remote]" # + remote repo scanning
pip install "flowsec[ai]" # + AI attack narratives
Quick Start
# Scan your repo checkout (finds .github/workflows/ automatically)
flowsec scan --github --dir .
# Scan a remote GitHub repo (public repos work without a token)
flowsec scan --github --repo owner/repo
# Scan a local GitHub Actions workflow file
flowsec scan --github --file .github/workflows/ci.yml
# Scan a GitLab CI file
flowsec scan --gitlab --file .gitlab-ci.yml
# Scan an Azure DevOps pipeline file
flowsec scan --azure --file azure-pipelines.yml
# JSON to stdout — pipe it anywhere
flowsec scan --github --dir . --format json
# SARIF for GitHub code scanning
flowsec scan --github --dir . --format sarif --output results.sarif
# Generate an HTML report
flowsec scan --github --dir . --format html --output report.html
# Generate AI attack narratives (requires ANTHROPIC_API_KEY)
flowsec scan --github --dir . --ai
# Fail pipeline if findings at or above threshold
flowsec scan --github --dir . --fail-on critical
# Ignore specific rules
flowsec scan --github --dir . --ignore FS006 --ignore FS011
Exit codes: 0 clean scan, 1 when --fail-on triggers, 2 for usage or scan errors.
Platforms and Tokens
| Platform | Local scan | Remote scan | Token |
|---|---|---|---|
| GitHub Actions | --github --file or --dir |
--github --repo owner/repo |
GITHUB_TOKEN — optional for public repos |
| GitLab CI | --gitlab --file or --dir |
--gitlab --repo namespace/project |
GITLAB_TOKEN (remote only) |
| Azure DevOps | --azure --file or --dir |
--azure --repo org/project |
AZURE_DEVOPS_TOKEN (always — Azure requires auth even for public projects) |
Set tokens in a .env file in your working directory or as environment variables. FlowSec loads .env automatically. Remote scanning needs pip install "flowsec[remote]".
38 Security Rules
| ID | Rule | Severity | MITRE | OWASP |
|---|---|---|---|---|
| FS001 | Hardcoded Secret — Plaintext Credential in Workflow | CRITICAL | T1552.001 | CICD-SEC-6 |
| FS002 | Unpinned Action — Supply Chain Attack Vector | CRITICAL | T1195.001 | CICD-SEC-3 |
| FS003 | Excessive Permissions — Overprivileged Workflow Token | HIGH | T1078 | CICD-SEC-5 |
| FS004 | Missing OIDC — Long-Lived Cloud Credential in Use | HIGH | T1552.004 | CICD-SEC-6 |
| FS005 | Pull Request Target — Secrets Exposed to Fork Code | CRITICAL | T1611 | CICD-SEC-4 |
| FS006 | Missing Timeout — Job Runs Up to 6 Hours Unchecked | LOW | T1499 | CICD-SEC-10 |
| FS007 | Self-Hosted Runner — Persistent Environment Risk | HIGH | T1053 | CICD-SEC-7 |
| FS008 | Missing Artifact Signing — No Tamper Protection | MEDIUM | T1553 | CICD-SEC-8 |
| FS009 | Unpinned Dependency — Package Installed Without Version Lock | HIGH | T1195.002 | CICD-SEC-3 |
| FS010 | Secret in Run Command — Plaintext Credential in Shell Step | CRITICAL | T1552.001 | CICD-SEC-6 |
| FS011 | GitHub Context Injection — Untrusted Event Data in Run Step | CRITICAL | T1059.004 | CICD-SEC-4 |
| FS012 | Missing Environment Protection — Deploy Job Has No Approval Gate | HIGH | T1078 | CICD-SEC-5 |
| FS013 | Workflow Dispatch Injection — Unvalidated Input in Shell Command | CRITICAL | T1059 | CICD-SEC-9 |
| FS014 | Mutable Container Image — Unpinned Image Tag in Pipeline | MEDIUM | T1195.001 | CICD-SEC-3 |
| FS015 | Persist Credentials — GitHub Token Remains in Git Config After Checkout | MEDIUM | T1552.001 | CICD-SEC-6 |
| FS016 | workflow_run Trigger — Privileged Execution from Untrusted Workflow | HIGH | T1059 | CICD-SEC-1 |
| FS017 | Security Scan Silenced — Failures Suppressed with continue-on-error | MEDIUM | T1562.001 | CICD-SEC-7 |
| FS018 | Secret as CLI Argument — Credential Exposed in Process List | HIGH | T1552 | CICD-SEC-6 |
| FS019 | Unverified Install Script — Remote Code Fetched and Executed Directly | HIGH | T1195.002 | CICD-SEC-3 |
| FS020 | Container Running as Root — Elevated Privilege in Pipeline | HIGH | T1611 | CICD-SEC-7 |
| FS021 | Secret in Docker Build Argument — Credential Stored in Image History | HIGH | T1552.001 | CICD-SEC-6 |
| FS022 | Broad Artifact Upload — Entire Workspace Exposed as Artifact | MEDIUM | T1560 | CICD-SEC-9 |
| FS023 | Insecure curl — SSL Verification Disabled in Pipeline | HIGH | T1071 | CICD-SEC-3 |
| FS024 | Privileged Docker Container — Full Host Access Granted in Pipeline | CRITICAL | T1611 | CICD-SEC-7 |
| FS025 | Environment Variables Printed to Logs — Secrets Exposed in Pipeline Output | MEDIUM | T1552.001 | CICD-SEC-6 |
| FS026 | Unguarded Deploy — Deployment Job Runs on Untrusted Branches | HIGH | T1078 | CICD-SEC-1 |
| FS027 | Docker Socket Mounted — Full Host Control from Pipeline | CRITICAL | T1611 | CICD-SEC-7 |
| FS028 | Credential in Git URL — Token Leaked to Logs and History | HIGH | T1552.001 | CICD-SEC-6 |
| FS029 | github-script Injection — Untrusted Event Data in Inline Script | CRITICAL | T1059.004 | CICD-SEC-4 |
| FS030 | secrets: inherit — All Secrets Passed to Called Workflow | MEDIUM | T1078 | CICD-SEC-5 |
| FS031 | Cache Poisoning Risk — Cache Used With Privileged Trigger | HIGH | T1195.001 | CICD-SEC-3 |
| FS032 | Unpinned Remote Include — External Pipeline Config Pulled at Runtime | HIGH | T1195.001 | CICD-SEC-3 |
| FS033 | Unsecure Commands Enabled — Deprecated set-env Injection Re-Enabled | HIGH | T1059.004 | CICD-SEC-4 |
| FS034 | Plain-HTTP Download — Unencrypted Fetch in Pipeline | MEDIUM | T1071 | CICD-SEC-3 |
| FS035 | Environment File Injection — Untrusted Data Written to GITHUB_ENV | HIGH | T1059.004 | CICD-SEC-4 |
| FS036 | Persist Credentials — Azure Checkout Leaves Token in Git Config | MEDIUM | T1552.001 | CICD-SEC-6 |
| FS037 | Obfuscated Execution — Encoded Payload Piped to a Shell | MEDIUM | T1027 | CICD-SEC-3 |
| FS038 | Docker-in-Docker Service — Privileged Runner Required | HIGH | T1611 | CICD-SEC-7 |
Risk Score
Every scan produces a 0-100 risk score using severity-weighted diminishing returns and exponential normalization. A single critical finding scores ~28. A heavily vulnerable pipeline scores ~90-97. The score cannot exceed 100.
Rule Suppression
# CLI flag
flowsec scan --github --repo owner/repo --ignore FS006 --ignore FS011
# .flowsec.yml in your repo root
ignore:
- rule_id: FS006
reason: "We use external timeout management"
- rule_id: FS002
file: "legacy/*.yml"
reason: "Legacy pipelines are being retired, not fixed"
The optional file field limits the ignore to files matching a glob.
Use as a Pipeline Gate
name: FlowSec Security Scan
on: [push, pull_request]
jobs:
security:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: pip install flowsec
- run: flowsec scan --github --dir . --fail-on critical
Or upload SARIF to GitHub's Security tab:
- run: flowsec scan --github --dir . --format sarif --output flowsec.sarif
- uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: flowsec.sarif
Security
FlowSec is built to the same standard it enforces:
- All GitHub Actions pinned to commit SHAs with least-privilege permissions
- PyPI publishing via OIDC trusted publishing — no long-lived tokens
- Own CI runs gitleaks, bandit, pip-audit, and FlowSec self-scan on every push
- Branch protection on main — force push blocked, 4 required status checks
- YAML parsed with a SafeLoader subclass — no code execution risk
- HTML reports use Jinja2 autoescape — XSS from finding content prevented
Report vulnerabilities via GitHub Security Advisories or email Scorpio.vansh@gmail.com.
License
MIT — see LICENSE on GitHub.
Release files for flowsec 0.7.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| flowsec-0.7.0.tar.gz | 1.1 MB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| flowsec-0.7.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 1.2 MB
Release files / flowsec-0.7.0.tar.gz
| Download URL | flowsec-0.7.0.tar.gz |
|---|---|
| Size | 1.1 MB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
f145f0a15b56d3493c78b4a96e13319375072a2aa4358441d53f24536de66d6b
|
|
BLAKE2b-256 checksum How to use checksums |
83d4f283789882a24315bc3c21592fc74708a953c318019aa01714156ad617d9
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.13
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jul 25, 2026.
Transparency logRelease files / flowsec-0.7.0-py3-none-any.whl
| Download URL | flowsec-0.7.0-py3-none-any.whl |
|---|---|
| Size | 67.5 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
c6a3889c1ad1738d13351f261bf528ed2e44f1998c18a3efa8f3c9f2a887e796
|
|
BLAKE2b-256 checksum How to use checksums |
b52be3509870337abcdaadf205a23caca1d76558f1076ca4f8c12eb698385177
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.13
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jul 25, 2026.
Transparency log