spinny:~/writing $ vim devsecops-shift-left-security-guide.md
1~2A vulnerability found while writing code costs a developer minutes to fix. The same vulnerability caught in production costs a sprint. And if an attacker finds it first, it costs millions. This is the core argument behind **shift-left security** - moving security checks as early as possible in the development lifecycle.3~4DevSecOps takes this idea and turns it into a practice: security is not a separate phase at the end but a continuous process woven into every stage of development, from the first line of code to the production deployment.5~6## The Cost of Late Security7~8IBM's Cost of a Data Breach Report has consistently shown that the cost of fixing security issues grows exponentially the later they are caught:9~10| Stage | Cost to Fix | Time to Fix |11|-------|------------|-------------|12| **IDE / Local Dev** | Minutes | Seconds to minutes |13| **Code Review / PR** | Hours | Minutes to hours |14| **CI/CD Pipeline** | Days | Hours to days |15| **Staging / QA** | Weeks | Days |16| **Production** | Months | Weeks to months |17| **Post-breach** | Millions ($) | Months to years |18~19The takeaway is clear: every stage you push security earlier saves an order of magnitude in cost and time.20~21## The DevSecOps Pipeline22~23A mature DevSecOps pipeline integrates security checks at every stage:24~25```mermaid26graph LR27 IDE[IDE / Editor] --> PC[Pre-commit Hooks]28 PC --> PR[Pull Request]29 PR --> CI[CI Pipeline]30 CI --> Build[Build / Package]31 Build --> Deploy[Deploy]32 Deploy --> Runtime[Runtime / Production]33~34 IDE -.- S1[SAST\nSecret Detection\nLinting]35 PC -.- S2[Secrets Scan\nFormat Check]36 PR -.- S3[Code Review\nDependency Audit]37 CI -.- S4[SAST\nSCA\nContainer Scan\nSBOM]38 Build -.- S5[Image Signing\nArtifact Verification]39 Deploy -.- S6[Policy Enforcement\nAdmission Control]40 Runtime -.- S7[DAST\nWAF\nMonitoring]41```42~43Let's break down each stage with concrete tools and configuration.44~45## Stage 1: Security in the IDE46~47The fastest feedback loop. Catch vulnerabilities before you even save the file.48~49### Recommended Tools50~51- **Semgrep**: lightweight static analysis with community rules for OWASP vulnerabilities52- **Snyk IDE Extension**: real-time dependency vulnerability scanning53- **GitLens + GitLeaks**: detect secrets in your editor54- **ESLint Security Plugins**: `eslint-plugin-security` for Node.js, `eslint-plugin-no-unsanitized` for DOM XSS55~56### Example: ESLint Security Configuration57~58```json59{60 "extends": ["eslint:recommended"],61 "plugins": ["security", "no-unsanitized"],62 "rules": {63 "security/detect-object-injection": "warn",64 "security/detect-non-literal-regexp": "warn",65 "security/detect-unsafe-regex": "error",66 "security/detect-buffer-noassert": "error",67 "security/detect-eval-with-expression": "error",68 "security/detect-no-csrf-before-method-override": "error",69 "security/detect-possible-timing-attacks": "warn",70 "no-unsanitized/method": "error",71 "no-unsanitized/property": "error"72 }73}74```75~76## Stage 2: Pre-commit Hooks77~78The second line of defense. Runs automatically before every commit, blocking dangerous code from entering the repository.79~80### Gitleaks: Catch Secrets Before They Hit Git81~82The most common security mistake in codebases is committing secrets - API keys, database passwords, tokens. Once a secret hits git history, it is extremely hard to remove completely (even with force pushes, forks and caches may retain it).83~84```yaml85# .pre-commit-config.yaml86repos:87 - repo: https://github.com/gitleaks/gitleaks88 rev: v8.21.089 hooks:90 - id: gitleaks91~92 - repo: https://github.com/semgrep/semgrep93 rev: v1.90.094 hooks:95 - id: semgrep96 args: ['--config', 'auto']97```98~99Install and activate:100~101```bash102pip install pre-commit103pre-commit install104```105~106Now every `git commit` automatically scans for leaked secrets and common vulnerabilities. If anything is found, the commit is blocked.107~108### Custom Gitleaks Rules109~110You can add custom patterns for your organization's secrets:111~112```toml113# .gitleaks.toml114title = "Custom Gitleaks Config"115~116[[rules]]117id = "internal-api-key"118description = "Internal API key detected"119regex = '''INTERNAL_KEY_[A-Za-z0-9]{32}'''120tags = ["key", "internal"]121```122~123## Stage 3: CI/CD Pipeline Security124~125This is where the heavy lifting happens. Your CI pipeline should run multiple security scans on every pull request.126~127### SAST (Static Application Security Testing)128~129SAST tools analyze source code without executing it, looking for patterns that indicate vulnerabilities.130~131```yaml132# .github/workflows/security.yml133name: Security Scan134on:135 pull_request:136 branches: [main]137~138jobs:139 sast:140 name: Static Analysis141 runs-on: ubuntu-latest142 steps:143 - uses: actions/checkout@v4144~145 - name: Run Semgrep146 uses: semgrep/semgrep-action@v1147 with:148 config: >-149 p/owasp-top-ten150 p/typescript151 p/nodejs152 p/react153 generateSarif: true154~155 - name: Upload SARIF156 uses: github/codeql-action/upload-sarif@v3157 with:158 sarif_file: semgrep.sarif159```160~161Semgrep's `p/owasp-top-ten` ruleset catches the most common vulnerabilities: SQL injection, XSS, SSRF, path traversal, insecure deserialization, and more.162~163### SCA (Software Composition Analysis)164~165SCA scans your dependencies for known vulnerabilities. This is critical - over 80% of modern application code comes from open-source dependencies.166~167```yaml168 dependency-scan:169 name: Dependency Audit170 runs-on: ubuntu-latest171 steps:172 - uses: actions/checkout@v4173~174 - name: Run Snyk175 uses: snyk/actions/node@master176 env:177 SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}178 with:179 args: --severity-threshold=high180~181 - name: npm audit182 run: npm audit --audit-level=high183```184~185### Container Security with Trivy186~187If you build Docker images, scanning them for vulnerabilities is essential. **Trivy** is the most popular open-source container scanner.188~189```yaml190 container-scan:191 name: Container Security192 runs-on: ubuntu-latest193 steps:194 - uses: actions/checkout@v4195~196 - name: Build image197 run: docker build -t my-app:${{ github.sha }} .198~199 - name: Run Trivy200 uses: aquasecurity/trivy-action@master201 with:202 image-ref: my-app:${{ github.sha }}203 format: 'sarif'204 output: 'trivy-results.sarif'205 severity: 'CRITICAL,HIGH'206 exit-code: '1'207~208 - name: Upload Trivy SARIF209 uses: github/codeql-action/upload-sarif@v3210 with:211 sarif_file: trivy-results.sarif212```213~214### SBOM Generation215~216A **Software Bill of Materials (SBOM)** is a complete inventory of every component in your application. Increasingly required by compliance frameworks and government regulations (the US Executive Order on Cybersecurity mandates SBOM for federal software).217~218```yaml219 sbom:220 name: Generate SBOM221 runs-on: ubuntu-latest222 steps:223 - uses: actions/checkout@v4224~225 - name: Generate SBOM with Syft226 uses: anchore/sbom-action@v0227 with:228 format: spdx-json229 output-file: sbom.spdx.json230~231 - name: Upload SBOM232 uses: actions/upload-artifact@v4233 with:234 name: sbom235 path: sbom.spdx.json236```237~238## Stage 4: Secure Docker Images239~240A production Docker image should follow the principle of least privilege. This is what a hardened Dockerfile looks like:241~242```dockerfile243# Build stage244FROM node:22-alpine AS builder245WORKDIR /app246COPY package.json package-lock.json ./247RUN npm ci248COPY . .249RUN npm run build250~251# Production stage252FROM node:22-alpine AS runner253WORKDIR /app254~255# Install dumb-init before dropping root256RUN apk add --no-cache dumb-init257~258# Don't run as root259RUN addgroup -S app && adduser -S app -G app260~261# Copy only what's needed262COPY --from=builder --chown=app:app /app/dist ./dist263COPY --from=builder --chown=app:app /app/node_modules ./node_modules264COPY --from=builder --chown=app:app /app/package.json ./265~266# Drop to non-root user267USER app268ENTRYPOINT ["dumb-init", "--"]269~270# Health check271HEALTHCHECK --interval=30s --timeout=3s --retries=3 \272 CMD wget -qO- http://localhost:3000/health || exit 1273~274EXPOSE 3000275CMD ["node", "dist/server.js"]276```277~278Key practices:279~2801. **Use multi-stage builds**: the builder stage has dev dependencies; the runner stage has only production code2812. **Don't run as root**: create a non-root user and switch to it2823. **Use Alpine images**: smaller attack surface (fewer packages installed by default)2834. **Pin image versions**: `node:22-alpine` instead of `node:latest` to avoid supply chain attacks2845. **Use `npm ci`**: deterministic installs from lock file, not `npm install`285~286## Stage 5: Secret Management287~288Hard-coded secrets are the number one cause of breaches in developer-caused incidents.289~290### What NOT to Do291~292```typescript293// NEVER do this294const API_KEY = "sk-1234567890abcdef";295const DB_PASSWORD = "supersecret123";296~297const client = new Client({298 connectionString: `postgres://admin:${DB_PASSWORD}@db.example.com/prod`299});300```301~302### What to Do Instead303~304```typescript305// Use environment variables306const client = new Client({307 connectionString: process.env.DATABASE_URL308});309~310// Or use a secret manager311import { SecretManagerServiceClient } from '@google-cloud/secret-manager';312~313const client = new SecretManagerServiceClient();314const [version] = await client.accessSecretVersion({315 name: 'projects/my-project/secrets/db-password/versions/latest',316});317const dbPassword = version.payload?.data?.toString();318```319~320### Secret Management Hierarchy321~322```mermaid323graph TD324 A[Secret Manager\nAWS Secrets Manager\nGCP Secret Manager\nHashiCorp Vault] --> B[Best: Rotated, audited, centralized]325 C[Environment Variables\nInjected at deploy time] --> D[Good: Not in code, but static]326 E[.env files\nWith .gitignore] --> F[Acceptable: Local development only]327 G[Hard-coded in source] --> H[NEVER: Instant breach risk]328~329 style A fill:#d4edda330 style C fill:#fff3cd331 style E fill:#ffeeba332 style G fill:#f8d7da333```334~335## The OWASP Top 10: A Quick Reference336~337Every developer should know the OWASP Top 10. Condensed version:338~339| # | Vulnerability | What It Is | Prevention |340|---|--------------|-----------|------------|341| 1 | **Broken Access Control** | Users accessing resources they shouldn't | Deny by default, validate on server side |342| 2 | **Cryptographic Failures** | Weak encryption, plaintext data | Use strong algorithms (AES-256, bcrypt), TLS everywhere |343| 3 | **Injection** | SQL, NoSQL, OS command injection | Parameterized queries, input validation |344| 4 | **Insecure Design** | Flawed architecture | Threat modeling, secure design patterns |345| 5 | **Security Misconfiguration** | Default credentials, open cloud buckets | Hardened defaults, automated config audits |346| 6 | **Vulnerable Components** | Known CVEs in dependencies | SCA scanning, regular updates |347| 7 | **Auth Failures** | Weak passwords, broken sessions | MFA, rate limiting, secure session management |348| 8 | **Data Integrity Failures** | Unsigned updates, untrusted CI/CD | Code signing, SBOM, pipeline integrity |349| 9 | **Logging Failures** | No audit trail | Structured logging, alerting on anomalies |350| 10 | **SSRF** | Server-side request forgery | Allowlist outbound URLs, validate inputs |351~352## Complete GitHub Actions Security Workflow353~354A complete, production-ready workflow that combines everything above:355~356```yaml357# .github/workflows/security.yml358name: Security Pipeline359on:360 pull_request:361 branches: [main]362 push:363 branches: [main]364~365permissions:366 contents: read367 security-events: write368~369jobs:370 secrets-scan:371 name: Secret Detection372 runs-on: ubuntu-latest373 steps:374 - uses: actions/checkout@v4375 with:376 fetch-depth: 0377 - uses: gitleaks/gitleaks-action@v2378 env:379 GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}380~381 sast:382 name: Static Analysis (SAST)383 runs-on: ubuntu-latest384 steps:385 - uses: actions/checkout@v4386 - uses: semgrep/semgrep-action@v1387 with:388 config: p/owasp-top-ten p/typescript p/nodejs389~390 dependency-audit:391 name: Dependency Scan (SCA)392 runs-on: ubuntu-latest393 steps:394 - uses: actions/checkout@v4395 - uses: actions/setup-node@v4396 with:397 node-version: 22398 - run: npm ci399 - run: npm audit --audit-level=high400 - uses: snyk/actions/node@master401 env:402 SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}403 with:404 args: --severity-threshold=high405 continue-on-error: true406~407 container-scan:408 name: Container Scan409 runs-on: ubuntu-latest410 needs: [sast, dependency-audit]411 steps:412 - uses: actions/checkout@v4413 - run: docker build -t app:${{ github.sha }} .414 - uses: aquasecurity/trivy-action@master415 with:416 image-ref: app:${{ github.sha }}417 severity: CRITICAL,HIGH418 exit-code: '1'419~420 sbom:421 name: SBOM Generation422 runs-on: ubuntu-latest423 needs: [container-scan]424 steps:425 - uses: actions/checkout@v4426 - uses: anchore/sbom-action@v0427 with:428 format: spdx-json429 output-file: sbom.spdx.json430```431~432```mermaid433graph TD434 PR[Pull Request] --> S1[Secret Detection]435 PR --> S2[SAST - Semgrep]436 PR --> S3[SCA - Snyk + npm audit]437 S2 --> S4[Container Scan - Trivy]438 S3 --> S4439 S4 --> S5[SBOM Generation]440 S5 --> Deploy[Deploy]441~442 S1 -.- F1[Block if secrets found]443 S2 -.- F2[Block on critical vulns]444 S3 -.- F3[Block on high severity]445 S4 -.- F4[Block on critical CVEs]446```447~448## Metrics to Track449~450How do you know your DevSecOps program is working? Track these metrics:451~452- **Mean time to remediate (MTTR)**: how fast you fix vulnerabilities after detection453- **Vulnerability escape rate**: percentage of vulnerabilities that reach production454- **False positive rate**: too many false positives lead to alert fatigue and ignored warnings455- **Dependency freshness**: average age of your dependencies (older = more likely to have known CVEs)456- **SBOM coverage**: percentage of projects with up-to-date SBOMs457~458## Getting Started: A Practical Roadmap459~460Don't try to implement everything at once. A phased approach works better:461~462```mermaid463flowchart TD464 A[Week 1-2: Foundations] --> B[Week 3-4: CI/CD Integration]465 B --> C[Month 2: Container Security]466 C --> D[Month 3+: Advanced]467~468 A --> A1[Add Gitleaks pre-commit hooks]469 A --> A2[Enable npm audit in CI]470 A --> A3[Add .gitignore for .env files]471~472 B --> B1[Add Semgrep to GitHub Actions]473 B --> B2[Add Snyk dependency scanning]474 B --> B3[Set up SARIF upload to GitHub]475~476 C --> C1[Add Trivy container scanning]477 C --> C2[Harden Dockerfiles]478 C --> C3[Generate SBOMs]479~480 D --> D1[Secret manager integration]481 D --> D2[Runtime protection - DAST]482 D --> D3[Policy as code - OPA]483```484~485## Conclusion486~487DevSecOps is not about adding more tools to your pipeline - it is about making security a natural part of how you build software. The goal is not to block every PR with security warnings but to give developers fast feedback so they can fix issues while the code is still fresh in their minds.488~489Start with the basics: pre-commit hooks for secrets, dependency scanning in CI, and container scanning for Docker images. Then iterate based on what your team needs.490~491Security is not a feature you ship once. It is a practice you build into every commit.492~493> **DevSecOps Starter Checklist:**494>495> - [x] Gitleaks pre-commit hooks installed496> - [x] .env and secret files in .gitignore497> - [x] Semgrep SAST in CI pipeline498> - [x] Snyk or npm audit for dependency scanning499> - [x] Trivy for container image scanning500> - [x] Non-root user in Dockerfiles501> - [x] Secrets in environment variables or secret manager502> - [x] SBOM generation on every release503> - [x] OWASP Top 10 awareness across the team504~
NORMAL · devsecops-shift-left-security-guide.md [readonly]504 lines · :q to close