• Elixir 72%
  • HTML 15.9%
  • CSS 4.3%
  • JavaScript 3.2%
  • Shell 3%
  • Other 1.6%
Find a file
Kovács Bálint Hunor 3a7b9a8521 fix: block deployment on any hardcoded credential finding
deploy.rego required the secret check to run but never inspected its
findings: only sast_passed/sca_passed were enforced, and the generic
critical_findings rule never fired because the CI gitleaks step tags
every finding as severity "high" (gitleaks has no native severity).
This demo currently has no hardcoded secret so the gap was latent, but
it matched a real, live bug found in the sibling Rust-DevSecOps-Demo.

Add a dedicated secret_findings rule with zero tolerance regardless of
severity, matching the fix applied upstream in devsecops-attestation.
Update the pinned --policy-hash to match.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-13 11:24:10 +03:00
.github fix: block deployment on any hardcoded credential finding 2026-07-13 11:24:10 +03:00
assets feat: initial commit 2026-03-25 15:48:28 +02:00
config feat: initial commit 2026-03-25 15:48:28 +02:00
lib feat: initial commit 2026-03-25 15:48:28 +02:00
priv feat: initial commit 2026-03-25 15:48:28 +02:00
results feat: initial commit 2026-03-25 15:48:28 +02:00
scripts feat: upgrade pipeline to zero-trust per-check-type architecture 2026-05-10 18:13:46 +03:00
test feat: initial commit 2026-03-25 15:48:28 +02:00
.gitignore fix: resolve pipeline failures found via act local testing 2026-05-10 16:24:25 +03:00
Dockerfile feat: initial commit 2026-03-25 15:48:28 +02:00
mix.exs chore: updated Elixir and OTP versions in mix.exs and CI workflow 2026-03-25 15:52:09 +02:00
mix.lock feat: initial commit 2026-03-25 15:48:28 +02:00
README.md feat: upgrade pipeline to zero-trust per-check-type architecture 2026-05-10 18:13:46 +03:00

Phoenix DevSecOps Demo

DevSecOps Attested Pipeline

A demo Phoenix application wired into the devsecops-attestation cryptographic pipeline.

Every push runs four security checks, signs each result into an Ed25519-linked attestation chain, then evaluates a deploy gate via OPA/Rego policy. Deployment only proceeds if every signature verifies against the authorized per-check-type key and the policy allows it.


Pipeline overview

flowchart TD
    A([Push / PR]) --> B["Build & Test\nmix deps.get · compile --warnings-as-errors · mix test"]
    B --> C["SAST\nSobelow"]
    B --> D["SCA\nmix_audit"]
    B --> E["Config Scan\nCheckov"]
    B --> F["Secret Scan\nGitleaks"]
    C --> G[results/sast.json]
    D --> H[results/sca.json]
    E --> I[results/config.json]
    F --> J[results/secret.json]
    G & H & I & J --> K[Deploy Gate]
    K --> L["Sign SAST · Sign SCA · Sign Config · Sign Secret\nchain.json  ·  Ed25519 per-check-type keys"]
    L --> M["gate evaluate\nchain.json + .github/policies/deploy.rego"]
    M --> N{Decision}
    N -->|allow| O[Deploy]
    N -->|block| P[Pipeline Fails]

Attestation chain

flowchart LR
    S1["Attestation 1\ntype: sast\nsigned: Ed25519 (sast key)\nprev: null"] -->|SHA-256 digest| S2
    S2["Attestation 2\ntype: sca\nsigned: Ed25519 (sca key)\nprev: hash(S1)"] -->|SHA-256 digest| S3
    S3["Attestation 3\ntype: config\nsigned: Ed25519 (config key)\nprev: hash(S2)"] -->|SHA-256 digest| S4
    S4["Attestation 4\ntype: secret\nsigned: Ed25519 (secret key)\nprev: hash(S3)"]

Each check type uses a dedicated key pair. A compromised SAST key cannot forge SCA, config, or secret attestations. Any insertion, deletion, or reordering of attestations breaks the SHA-256 chain linkage and causes gate evaluate to reject the deployment.


Security tools

Stage Tool What it checks
SAST Sobelow Phoenix-specific vulnerabilities (XSS, SQLi, CSRF...)
SCA mix_audit Known CVEs in Hex dependencies
Config Checkov Misconfigurations in Dockerfile and IaC files
Secret Gitleaks Hardcoded credentials and secrets in source code

Project structure

Phoenix-DevSecOps-Demo/
├── .github/
│   ├── policies/
│   │   └── deploy.rego              # OPA/Rego deployment policy
│   └── workflows/
│       └── devsecops-pipeline.yml   # GitHub Actions CI/CD workflow
├── assets/                          # JS / CSS (esbuild + Tailwind)
├── config/                          # Phoenix config (dev, test, runtime)
├── lib/
│   ├── demo/                        # Application, Repo, Mailer
│   └── demo_web/                    # Router, Endpoint, Controllers, Components
├── priv/repo/migrations/
├── results/
│   └── .gitkeep                     # CI writes scan JSONs here
├── scripts/
│   └── act-debug.sh                 # Local pipeline runner (act)
├── test/
├── Dockerfile
└── mix.exs

Setup

1. Prerequisites

  • Elixir >= 1.15 / Erlang OTP >= 26
  • PostgreSQL 14+ running locally (user postgres, password postgres)
    # Arch / CachyOS
    sudo systemctl start postgresql
    
    # Docker alternative
    docker run -d --name demo-postgres -p 5432:5432 \
      -e POSTGRES_PASSWORD=postgres postgres:16
    

2. Install and set up the app

mix setup   # deps.get + ecto.create + ecto.migrate + assets

3. Start the server

mix phx.server
# or inside IEx:
iex -S mix phx.server

Visit http://localhost:4000.


CI/CD setup (GitHub Actions)

The pipeline uses per-check-type key pairs. Each check type has its own dedicated signing key so a compromise is contained to a single check.

1. Generate four key pairs

git clone https://github.com/MemerGamer/devsecops-attestation
cd devsecops-attestation
for check in sast sca config secret; do
  go run ./cmd/keygen --out "keys/$check"
done
# Each directory contains private.hex (keep secret) and public.hex

2. Add all eight secrets to this repository

Settings -> Secrets and variables -> Actions -> New repository secret

Secret Value
SAST_SIGNING_KEY Contents of keys/sast/private.hex
SCA_SIGNING_KEY Contents of keys/sca/private.hex
CONFIG_SIGNING_KEY Contents of keys/config/private.hex
SECRET_SCANNING_SIGNING_KEY Contents of keys/secret/private.hex
SAST_PUBLIC_KEY Contents of keys/sast/public.hex
SCA_PUBLIC_KEY Contents of keys/sca/public.hex
CONFIG_PUBLIC_KEY Contents of keys/config/public.hex
SECRET_SCANNING_PUBLIC_KEY Contents of keys/secret/public.hex

Never commit any private.hex file. The keys/ directory is gitignored in the attestation repo.

3. Policy hash (keep in sync)

The gate step pins the SHA-256 of deploy.rego via --policy-hash. If you update the policy, recompute the hash and update the workflow:

sha256sum .github/policies/deploy.rego

Then update --policy-hash in .github/workflows/devsecops-pipeline.yml.

4. (Optional) Require manual approval before deploy

The deploy-gate job targets the production environment:

Settings -> Environments -> production -> Required reviewers


Local development

# Install deps and set up DB
mix setup

# Run tests
mix test

# Run SAST locally
mix sobelow

# Run SCA locally
mix deps.audit

# Full pre-commit check (compile + format + test)
mix precommit

Local pipeline simulation (act)

# Runs the full pipeline locally via act (generates test keys automatically)
bash scripts/act-debug.sh

# Run a specific job
bash scripts/act-debug.sh deploy-gate

License

MIT