Skip to content

fix(ci): repair security pipeline and comprehensive tests #36

fix(ci): repair security pipeline and comprehensive tests

fix(ci): repair security pipeline and comprehensive tests #36

name: Security Pipeline
# Unified security gate: SAST, SCA, secret scanning, IaC scanning, and SBOM.
# Runs on every PR and nightly on main. Secret scanning blocks the merge gate.
# SAST and SCA run in report-only mode (upload SARIF for visibility).
# See docs/SECURITY_PIPELINE.md for policy, baselines, and runbooks.
on:
push:
branches: [main]
pull_request:
branches: [main]
schedule:
- cron: "0 3 * * *"
workflow_dispatch:
permissions:
contents: read
security-events: write
actions: read
concurrency:
group: security-${{ github.ref }}
cancel-in-progress: false
jobs:
# ---------------------------------------------------------------
# Secret scanning — gitleaks CLI (no license required for org repos)
# ---------------------------------------------------------------
secret-scan:
name: Secret Scan (gitleaks)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with: { fetch-depth: 0 }
- name: Install gitleaks
run: |
GITLEAKS_VERSION="8.21.2"
curl -sSL "https://github.com/gitleaks/gitleaks/releases/download/v${GITLEAKS_VERSION}/gitleaks_${GITLEAKS_VERSION}_linux_x64.tar.gz" -o gitleaks.tar.gz
tar -xzf gitleaks.tar.gz gitleaks
chmod +x gitleaks
sudo mv gitleaks /usr/local/bin/
gitleaks version
- name: Run gitleaks scan
run: |
gitleaks detect \
--config .github/security/gitleaks.toml \
--no-git \
--verbose \
--redact \
--report-format json \
--report-path gitleaks-report.json || true
if [ -f gitleaks-report.json ]; then
CONTENT=$(cat gitleaks-report.json | tr -d '[:space:]')
if [ "$CONTENT" != "[]" ] && [ "$CONTENT" != "null" ] && [ -n "$CONTENT" ]; then
echo "::error::Secrets detected by gitleaks. See report for details."
cat gitleaks-report.json
exit 1
fi
fi
echo "✅ No secrets detected"
# ---------------------------------------------------------------
# SAST — Semgrep CLI (report-only mode)
# ---------------------------------------------------------------
sast:
name: SAST (semgrep)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install semgrep
run: |
python3 -m pip install --user semgrep==1.85.0
semgrep --version
- name: Semgrep scan
run: |
semgrep scan \
--config p/typescript \
--config p/javascript \
--config p/owasp-top-ten \
--config p/react \
--config p/nodejs \
--config p/docker \
--config p/sql-injection \
--config p/xss \
--sarif --output semgrep.sarif \
|| true
echo "✅ Semgrep scan completed (report-only mode)"
- name: Upload Semgrep SARIF
if: always()
uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: semgrep.sarif
# ---------------------------------------------------------------
# SCA + IaC — Trivy (report-only mode, upload SARIF)
# ---------------------------------------------------------------
sca-and-iac:
name: SCA + IaC (trivy)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Trivy FS (dependency vulnerabilities)
uses: aquasecurity/trivy-action@v0.36.0
with:
scan-type: fs
scan-ref: .
severity: HIGH,CRITICAL
exit-code: "0"
ignore-unfixed: true
format: sarif
output: trivy-fs.sarif
- name: Trivy config (IaC misconfigs)
uses: aquasecurity/trivy-action@v0.36.0
with:
scan-type: config
scan-ref: .
severity: HIGH,CRITICAL
exit-code: "0"
format: sarif
output: trivy-config.sarif
- name: Upload Trivy SARIF
if: always()
uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: trivy-fs.sarif
# ---------------------------------------------------------------
# SBOM — CycloneDX via Syft
# ---------------------------------------------------------------
sbom:
name: SBOM (syft)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Generate CycloneDX SBOM
uses: anchore/sbom-action@v0
with:
format: cyclonedx-json
output-file: sbom.cyclonedx.json
upload-artifact: true
upload-artifact-name: sbom.cyclonedx.json
- name: Upload SBOM artifact
uses: actions/upload-artifact@v4
with:
name: sbom-cyclonedx
path: sbom.cyclonedx.json
retention-days: 90
# ---------------------------------------------------------------
# Aggregate security report (digest) — non-blocking, always runs
# ---------------------------------------------------------------
security-report:
name: Security Report Digest
runs-on: ubuntu-latest
needs: [secret-scan, sast, sca-and-iac, sbom]
if: always()
steps:
- uses: actions/checkout@v4
- name: Setup Node
uses: actions/setup-node@v4
with: { node-version: "20" }
- name: Install
run: npm ci
working-directory: ./security-tests
- name: Generate report
run: npm run report:security
working-directory: ./security-tests
if: always()
- name: Upload report
uses: actions/upload-artifact@v4
if: always()
with:
name: security-report-digest
path: security-tests/reports/
retention-days: 30