Skip to content

Security Scanning

Security Scanning #90

Workflow file for this run

name: Security Scanning
on:
push:
branches: [ main, develop ]
pull_request:
branches: [ main ]
schedule:
# Run security scans daily at 2 AM UTC
- cron: '0 2 * * *'
workflow_dispatch:
env:
PYTHON_VERSION: "3.10"
jobs:
dependency-scan:
name: Dependency Vulnerability Scan
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: ${{ env.PYTHON_VERSION }}
- name: Install Poetry
uses: snok/install-poetry@v1
with:
version: "1.6.1"
virtualenvs-create: true
virtualenvs-in-project: true
- name: Install dependencies
run: |
poetry install --with dev
- name: Run Safety check
run: |
poetry run safety check --json --output safety-report.json || true
poetry run safety check --short-report
- name: Run pip-audit
run: |
pip install pip-audit
pip-audit --format=json --output=pip-audit-report.json || true
pip-audit --format=cyclone-json --output=pip-audit-cyclone.json || true
- name: Upload dependency scan results
uses: actions/upload-artifact@v3
if: always()
with:
name: dependency-scan-results
path: |
safety-report.json
pip-audit-report.json
pip-audit-cyclone.json
code-security-scan:
name: Code Security Analysis
runs-on: ubuntu-latest
timeout-minutes: 20
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: ${{ env.PYTHON_VERSION }}
- name: Install Poetry
uses: snok/install-poetry@v1
with:
version: "1.6.1"
- name: Install dependencies
run: |
poetry install --with dev
- name: Run Bandit security linter
run: |
poetry run bandit -r . -x tests/ -f json -o bandit-report.json
poetry run bandit -r . -x tests/ -f txt -o bandit-report.txt
- name: Run semgrep security analysis
uses: returntocorp/semgrep-action@v1
with:
config: >-
p/security-audit
p/secrets
p/python
p/owasp-top-ten
generateSarif: "1"
- name: Upload code security results
uses: actions/upload-artifact@v3
if: always()
with:
name: code-security-results
path: |
bandit-report.json
bandit-report.txt
- name: Upload SARIF file
uses: github/codeql-action/upload-sarif@v2
if: always()
with:
sarif_file: semgrep.sarif
secret-scan:
name: Secret Detection
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Run TruffleHog secret scan
uses: trufflesecurity/trufflehog@main
with:
path: ./
base: main
head: HEAD
extra_args: --debug --only-verified
- name: Run GitLeaks secret scan
uses: gitleaks/gitleaks-action@v2
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GITLEAKS_LICENSE: ${{ secrets.GITLEAKS_LICENSE }}
docker-security-scan:
name: Docker Security Scan
runs-on: ubuntu-latest
timeout-minutes: 20
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Build Docker image
run: |
docker build -t rag-templates:security-test .
- name: Run Trivy vulnerability scanner
uses: aquasecurity/trivy-action@master
with:
image-ref: 'rag-templates:security-test'
format: 'sarif'
output: 'trivy-results.sarif'
- name: Run Trivy filesystem scan
uses: aquasecurity/trivy-action@master
with:
scan-type: 'fs'
scan-ref: '.'
format: 'sarif'
output: 'trivy-fs-results.sarif'
- name: Upload Trivy scan results
uses: github/codeql-action/upload-sarif@v2
if: always()
with:
sarif_file: |
trivy-results.sarif
trivy-fs-results.sarif
- name: Run Docker Bench Security
run: |
docker run --rm --net host --pid host --userns host --cap-add audit_control \
-e DOCKER_CONTENT_TRUST=$DOCKER_CONTENT_TRUST \
-v /etc:/etc:ro \
-v /usr/bin/containerd:/usr/bin/containerd:ro \
-v /usr/bin/runc:/usr/bin/runc:ro \
-v /usr/lib/systemd:/usr/lib/systemd:ro \
-v /var/lib:/var/lib:ro \
-v /var/run/docker.sock:/var/run/docker.sock:ro \
--label docker_bench_security \
docker/docker-bench-security > docker-bench-results.txt || true
- name: Upload Docker security results
uses: actions/upload-artifact@v3
if: always()
with:
name: docker-security-results
path: |
docker-bench-results.txt
infrastructure-scan:
name: Infrastructure Security Scan
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Run Checkov IaC security scan
uses: bridgecrewio/checkov-action@master
with:
directory: .
framework: dockerfile,kubernetes,terraform
output_format: sarif
output_file_path: checkov-results.sarif
- name: Upload Checkov scan results
uses: github/codeql-action/upload-sarif@v2
if: always()
with:
sarif_file: checkov-results.sarif
- name: Run Terrascan
uses: tenable/terrascan-action@main
with:
iac_type: 'docker'
iac_version: 'v1'
policy_type: 'aws'
only_warn: true
sarif_upload: true
compliance-check:
name: Compliance & License Check
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: ${{ env.PYTHON_VERSION }}
- name: Install Poetry
uses: snok/install-poetry@v1
with:
version: "1.6.1"
- name: Install dependencies
run: |
poetry install --with dev
- name: Check license compatibility
run: |
pip install licensecheck
licensecheck --no-deps --zero --format json > license-report.json
licensecheck --no-deps --zero
- name: Run FOSSA license scan
if: env.FOSSA_API_KEY != ''
uses: fossas/fossa-action@main
with:
api-key: ${{ secrets.FOSSA_API_KEY }}
run-tests: true
- name: Upload compliance results
uses: actions/upload-artifact@v3
if: always()
with:
name: compliance-results
path: |
license-report.json
codeql-analysis:
name: CodeQL Security Analysis
runs-on: ubuntu-latest
timeout-minutes: 30
permissions:
actions: read
contents: read
security-events: write
strategy:
fail-fast: false
matrix:
language: [ 'python', 'javascript' ]
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Initialize CodeQL
uses: github/codeql-action/init@v2
with:
languages: ${{ matrix.language }}
queries: security-extended,security-and-quality
- name: Autobuild
uses: github/codeql-action/autobuild@v2
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v2
with:
category: "/language:${{matrix.language}}"
security-scorecard:
name: OSSF Security Scorecard
runs-on: ubuntu-latest
timeout-minutes: 15
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
permissions:
security-events: write
id-token: write
actions: read
contents: read
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
persist-credentials: false
- name: Run analysis
uses: ossf/scorecard-action@v2.3.1
with:
results_file: results.sarif
results_format: sarif
publish_results: true
- name: Upload SARIF results
uses: github/codeql-action/upload-sarif@v2
with:
sarif_file: results.sarif
security-report:
name: Generate Security Report
runs-on: ubuntu-latest
timeout-minutes: 10
needs: [dependency-scan, code-security-scan, secret-scan, docker-security-scan, compliance-check]
if: always()
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Download all security artifacts
uses: actions/download-artifact@v3
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: ${{ env.PYTHON_VERSION }}
- name: Generate security report
run: |
python scripts/ci/generate-security-report.py \
--output security-report.md \
--format markdown
- name: Upload security report
uses: actions/upload-artifact@v3
with:
name: security-report
path: security-report.md
- name: Comment security report on PR
if: github.event_name == 'pull_request'
uses: actions/github-script@v6
with:
script: |
const fs = require('fs');
const report = fs.readFileSync('security-report.md', 'utf8');
github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: `## 🔒 Security Scan Results\n\n${report}`
});
security-policy-check:
name: Security Policy Validation
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Check for security policy
run: |
if [ ! -f SECURITY.md ]; then
echo "❌ SECURITY.md file not found"
exit 1
fi
echo "✅ SECURITY.md file exists"
- name: Validate security policy content
run: |
python scripts/ci/validate-security-policy.py SECURITY.md
- name: Check for vulnerability disclosure
run: |
if ! grep -q "vulnerability" SECURITY.md; then
echo "❌ Security policy should include vulnerability disclosure process"
exit 1
fi
echo "✅ Vulnerability disclosure process found"
penetration-test:
name: Basic Penetration Testing
runs-on: ubuntu-latest
timeout-minutes: 25
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Start application
run: |
docker-compose -f docker-compose.test.yml up -d
sleep 30
- name: Install OWASP ZAP
run: |
docker pull owasp/zap2docker-stable
- name: Run OWASP ZAP baseline scan
run: |
docker run -v $(pwd):/zap/wrk/:rw -t owasp/zap2docker-stable \
zap-baseline.py -t http://localhost:8000 -J zap-report.json || true
- name: Run OWASP ZAP full scan
run: |
docker run -v $(pwd):/zap/wrk/:rw -t owasp/zap2docker-stable \
zap-full-scan.py -t http://localhost:8000 -J zap-full-report.json || true
- name: Upload penetration test results
uses: actions/upload-artifact@v3
if: always()
with:
name: penetration-test-results
path: |
zap-report.json
zap-full-report.json
- name: Stop application
if: always()
run: |
docker-compose -f docker-compose.test.yml down