Dependency Vulnerability Scanning and Secret Management
Dependency Vulnerability Scanning and Secret Management
The Feature
Marketflow automatically checks Python and JavaScript dependencies for known vulnerabilities on every pull request, and secrets are stored in environment variables that never appear in git history or logs.
The Decision
Automated dependency scanning catches vulnerabilities before they reach production. The cost is zero (GitHub provides Dependabot for free). The alternative, manually checking CVE databases, is tedious, unreliable, and gets skipped under deadline pressure.
The Implementation
GitHub Dependabot
# .github/dependabot.yml
version: 2
updates:
- package-ecosystem: "pip"
directory: "/backend"
schedule:
interval: "weekly"
open-pull-requests-limit: 5
- package-ecosystem: "npm"
directory: "/frontend"
schedule:
interval: "weekly"
open-pull-requests-limit: 5
- package-ecosystem: "docker"
directory: "/"
schedule:
interval: "weekly"
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "weekly"
Dependabot opens pull requests for outdated or vulnerable dependencies. The limit of 5 open PRs prevents the repository from being overwhelmed with update PRs.
CI Security Scanning
# .github/workflows/security.yml
name: Security Scan
on:
pull_request:
push:
branches: [main]
jobs:
python-audit:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.12"
- run: pip install pip-audit
- run: pip-audit -r backend/requirements.txt
npm-audit:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
- working-directory: frontend
run: npm ci
- working-directory: frontend
run: npm audit --audit-level=high
Secret Management Rules
# .gitignore (security-relevant entries)
.env
.env.local
.env.production
*.pem
*.key
backend/.env
frontend/.env
# TRAP: Logging secrets
import logging
logger = logging.getLogger(__name__)
async def process_webhook(request: Request):
payload = await request.body()
logger.debug(f"Webhook payload: {payload}") # Contains Stripe API data
logger.debug(f"Headers: {request.headers}") # Contains auth tokens
# SAFE: Log only what you need
async def process_webhook(request: Request):
payload = await request.body()
event = stripe.Webhook.construct_event(...)
logger.info(f"Processing Stripe event: {event['type']} id={event['id']}")
# Never log the full payload or headers
Logging the full request payload or headers writes API keys, JWTs, and Stripe data to log files. Log files are often less secured than the application itself, forwarded to third-party log aggregation services, and retained longer than necessary.
The Trap
# TRAP: Committing .env to fix a CI issue
git add .env # "I'll remove it later"
git commit -m "fix: add env file for CI"
git push
# The secret is now in git history permanently.
# Even after removing the file, `git log --all -- .env` shows the content.
# Rotating every key in that file is now mandatory.
# SAFE: Use GitHub Actions secrets for CI
# Settings > Secrets and variables > Actions > New repository secret
# Reference in workflow: ${{ secrets.STRIPE_SECRET_KEY }}
Once a secret is in git history, it is compromised. Git history rewriting (git filter-repo) can remove it, but every clone and fork of the repository may still have the original history. The correct approach is to never commit secrets and to use GitHub Actions secrets for CI.
The Cost
| Tool | Cost |
|---|---|
| GitHub Dependabot | $0 (included with GitHub) |
| pip-audit | $0 (open source) |
| npm audit | $0 (included with npm) |
| GitHub Actions secrets | $0 (included with GitHub) |
Total security scanning cost: $0. The CI minutes consumed by the security scan are approximately 2 minutes per run, well within GitHub’s free tier.