This repo is a DevSecOps hardening exercise, built around the roadmap.sh STANS project: containerizing a React/TypeScript app and shipping it through a hardened, security-scanned CI/CD pipeline.
See DEPLOYMENT.md for the full pipeline writeup.
- Phase 1 — Container & pipeline hardening: complete
- Phase 2 — Advanced security hardening: in progress
- Multi-stage Dockerfile —
node:24-alpine3.20build stage,nginx:1.27-alpine3.20runtime stage, with only the compileddist/output andnginx.confcopied into the final image. - Pinned base image versions — no floating tags.
- Non-root container user — runs as the built-in
nginxuser, not root. .dockerignore— keepsnode_modules,.git,.env, and docs out of the build context.- OCI image labels —
org.opencontainers.image.revision/.created, injected at build time so any running image traces back to its exact commit and build timestamp. - Health check — an exact-match
/healthroute innginx.conf, backed by a DockerHEALTHCHECKpolling every 30s. docker-compose.yaml— for local dev/prod parity.- Nginx security headers —
server_tokens off,X-Content-Type-Options,X-Frame-Options,Referrer-Policy,Permissions-Policy. - Hardened CI/CD pipeline — all third-party GitHub Actions pinned to a full commit SHA; the image is built and health-verified before being pushed, so
:lateston GHCR can never point at a build that failed its own health check. Images are tagged both:latestand:<commit-sha>.
Completed so far:
- Dependabot — weekly update PRs for both the
npmandgithub-actionsecosystems. - Trivy vulnerability scanning — filesystem/dependency scan in CI, image scan before push in CD, both gating the pipeline on
CRITICAL/HIGHfindings.trivy-actionis pinned by commit SHA rather than tag, in direct response to the March 2026 supply-chain tag-hijacking incident affecting that action. - CodeQL static analysis (SAST) —
security-extendedquery pack, running on push/PR plus a weekly schedule, with results surfaced in the repo's Security tab.
Not yet done:
- Read-only root filesystem + dropped Linux capabilities at the container level.
- SBOM generation (Syft) per image build.
- Image signing / provenance verification (cosign/Sigstore).
- Content-Security-Policy header.
- Live deployment (Part 4 of the original brief) — provisioning a real VPS, Certbot TLS, and firewall rules. No server has been provisioned yet; the SSH deploy step in
deploy.ymlis written but commented out until one exists.
# Local dev (underlying app)
npm install
npm run dev# Plain Docker
docker build \
--build-arg REVISION=$(git rev-parse HEAD) \
--build-arg BUILD_DATE=$(date -u +'%Y-%m-%dT%H:%M:%SZ') \
-t stans-app .
docker run -d --name stans-app -p 8080:80 stans-app# Docker Compose
docker compose up -d --build
docker compose ps # confirm (healthy) status