# Vulnerability Remediation Patterns Common patterns for remediating dependency vulnerabilities detected by Grype. ## Table of Contents - [General Remediation Strategies](#general-remediation-strategies) - [Package Update Patterns](#package-update-patterns) - [Base Image Updates](#base-image-updates) - [Dependency Pinning](#dependency-pinning) - [Compensating Controls](#compensating-controls) - [Language-Specific Patterns](#language-specific-patterns) ## General Remediation Strategies ### Strategy 1: Direct Dependency Update **When to use**: Vulnerability in a directly declared dependency **Pattern**: 1. Identify fixed version from Grype output 2. Update dependency version in manifest file 3. Test application compatibility 4. Re-scan to verify fix 5. Deploy updated application **Example**: ```bash # Grype reports: lodash@4.17.15 has CVE-2020-8203, fixed in 4.17.19 # Update package.json npm install lodash@4.17.19 npm test grype dir:. --only-fixed ``` ### Strategy 2: Transitive Dependency Update **When to use**: Vulnerability in an indirect dependency **Pattern**: 1. Identify which direct dependency includes the vulnerable package 2. Check if direct dependency has an update that resolves the issue 3. Update direct dependency or use dependency override mechanism 4. Re-scan to verify fix **Example (npm)**: ```json // package.json - Override transitive dependency { "overrides": { "lodash": "^4.17.21" } } ``` **Example (pip)**: ```txt # constraints.txt lodash>=4.17.21 ``` ### Strategy 3: Base Image Update **When to use**: Vulnerability in OS packages from container base image **Pattern**: 1. Identify vulnerable OS package and fixed version 2. Update to newer base image tag or rebuild with package updates 3. Re-scan updated image 4. Test application on new base image **Example**: ```dockerfile # Before: Alpine 3.14 with vulnerable openssl FROM alpine:3.14 # After: Alpine 3.19 with fixed openssl FROM alpine:3.19 # Or: Explicit package update FROM alpine:3.14 RUN apk upgrade --no-cache openssl ``` ### Strategy 4: Patch or Backport **When to use**: No fixed version available or update breaks compatibility **Pattern**: 1. Research if security patch exists separately from full version update 2. Apply patch using package manager's patching mechanism 3. Consider backporting fix if feasible 4. Document patch and establish review schedule **Example (npm postinstall)**: ```json { "scripts": { "postinstall": "patch-package" } } ``` ### Strategy 5: Compensating Controls **When to use**: Fix not available and risk must be accepted **Pattern**: 1. Document vulnerability and risk acceptance 2. Implement network, application, or operational controls 3. Enhance monitoring and detection 4. Schedule regular review (quarterly) 5. Track for future remediation when fix becomes available ## Package Update Patterns ### Pattern: Semantic Versioning Updates **Minor/Patch Updates** (Generally Safe): ```bash # Python: Update to latest patch version pip install --upgrade 'package>=1.2.0,<1.3.0' # Node.js: Update to latest minor version npm update package # Go: Update to latest patch go get -u=patch github.com/org/package ``` **Major Updates** (Breaking Changes): ```bash # Review changelog before updating npm show package versions pip index versions package # Update and test thoroughly npm install package@3.0.0 npm test ``` ### Pattern: Lock File Management **Update specific package**: ```bash # npm npm install package@latest npm install # Update lock file # pip pip install --upgrade package pip freeze > requirements.txt # Go go get -u github.com/org/package go mod tidy ``` **Update all dependencies**: ```bash # npm (interactive) npm-check-updates --interactive # pip pip list --outdated | cut -d ' ' -f1 | xargs -n1 pip install -U # Go go get -u ./... go mod tidy ``` ## Base Image Updates ### Pattern: Minimal Base Images **Reduce attack surface with minimal images**: ```dockerfile # ❌ Large attack surface FROM ubuntu:22.04 # ✅ Minimal attack surface FROM alpine:3.19 # or FROM gcr.io/distroless/base-debian12 # ✅ Minimal for specific language FROM python:3.11-slim FROM node:20-alpine ``` **Benefits**: - Fewer packages = fewer vulnerabilities - Smaller image size - Faster scans ### Pattern: Multi-Stage Builds **Separate build dependencies from runtime**: ```dockerfile # Build stage with full toolchain FROM node:20 AS builder WORKDIR /app COPY package*.json ./ RUN npm ci COPY . . RUN npm run build # Production stage with minimal image FROM node:20-alpine AS production WORKDIR /app COPY --from=builder /app/dist ./dist COPY --from=builder /app/node_modules ./node_modules USER node CMD ["node", "dist/index.js"] ``` **Benefits**: - Build tools not present in final image - Reduced vulnerability exposure - Smaller production image ### Pattern: Regular Base Image Updates **Automate base image updates**: ```yaml # Dependabot config for Dockerfile version: 2 updates: - package-ecosystem: "docker" directory: "/" schedule: interval: "weekly" ``` **Manual update process**: ```bash # Check for newer base image versions docker pull alpine:3.19 docker images alpine # Update Dockerfile sed -i 's/FROM alpine:3.18/FROM alpine:3.19/' Dockerfile # Rebuild and scan docker build -t myapp:latest . grype myapp:latest ``` ## Dependency Pinning ### Pattern: Pin to Secure Versions **Lock to known-good versions**: ```dockerfile # ✅ Pin specific versions FROM alpine:3.19.0@sha256:abc123... # Install specific package versions RUN apk add --no-cache \ ca-certificates=20240226-r0 \ openssl=3.1.4-r0 ``` ```json // package.json - Exact versions { "dependencies": { "express": "4.18.2", "lodash": "4.17.21" } } ``` **Benefits**: - Reproducible builds - Controlled updates - Prevent automatic vulnerability introduction **Drawbacks**: - Manual update effort - May miss security patches - Requires active maintenance ### Pattern: Range-Based Pinning **Allow patch updates, lock major/minor**: ```json // package.json - Allow patch updates { "dependencies": { "express": "~4.18.2", // Allow 4.18.x "lodash": "^4.17.21" // Allow 4.x.x } } ``` ```python # requirements.txt - Compatible releases express>=4.18.2,<5.0.0 lodash>=4.17.21,<5.0.0 ``` ## Compensating Controls ### Pattern: Network Segmentation **Isolate vulnerable systems**: ```yaml # Docker Compose network isolation services: vulnerable-service: image: myapp:vulnerable networks: - internal # No external port exposure gateway: image: nginx:alpine ports: - "80:80" networks: - internal - external networks: internal: internal: true external: ``` **Benefits**: - Limits attack surface - Contains potential breaches - Buys time for proper remediation ### Pattern: Web Application Firewall (WAF) **Block exploit attempts at perimeter**: ```nginx # ModSecurity/OWASP Core Rule Set location / { modsecurity on; modsecurity_rules_file /etc/nginx/modsec/main.conf; proxy_pass http://vulnerable-backend; } ``` **Virtual Patching**: - Create WAF rules for specific CVEs - Block known exploit patterns - Monitor for exploitation attempts ### Pattern: Runtime Application Self-Protection (RASP) **Detect and prevent exploitation at runtime**: ```python # Example: Add input validation def process_user_input(data): # Validate against known exploit patterns if contains_sql_injection(data): log_security_event("SQL injection attempt blocked") raise SecurityException("Invalid input") return sanitize_input(data) ``` ## Language-Specific Patterns ### Python **Update vulnerable package**: ```bash # Check for vulnerabilities grype dir:/path/to/project -o json # Update package pip install --upgrade vulnerable-package # Freeze updated dependencies pip freeze > requirements.txt # Verify fix grype dir:/path/to/project ``` **Use constraints files**: ```bash # constraints.txt vulnerable-package>=1.2.3 # CVE-2024-XXXX fixed # Install with constraints pip install -r requirements.txt -c constraints.txt ``` ### Node.js **Update vulnerable package**: ```bash # Check for vulnerabilities npm audit grype dir:. -o json # Fix automatically (if possible) npm audit fix # Manual update npm install package@version # Verify fix npm audit grype dir:. ``` **Override transitive dependencies**: ```json { "overrides": { "vulnerable-package": "^2.0.0" } } ``` ### Go **Update vulnerable module**: ```bash # Check for vulnerabilities go list -m all | grype # Update specific module go get -u github.com/org/vulnerable-module # Update all modules go get -u ./... # Verify and tidy go mod tidy grype dir:. ``` ### Java/Maven **Update vulnerable dependency**: ```xml org.example vulnerable-lib 2.0.0 ``` **Force dependency version**: ```xml org.example vulnerable-lib 2.0.0 ``` ### Rust **Update vulnerable crate**: ```bash # Check for vulnerabilities cargo audit grype dir:. -o json # Update specific crate cargo update -p vulnerable-crate # Update all crates cargo update # Verify fix cargo audit grype dir:. ``` ## Verification Workflow After applying any remediation: Progress: [ ] 1. **Re-scan**: Run Grype scan to verify vulnerability resolved [ ] 2. **Test**: Execute test suite to ensure no functionality broken [ ] 3. **Document**: Record CVE, fix applied, and verification results [ ] 4. **Deploy**: Roll out fix to affected environments [ ] 5. **Monitor**: Watch for related security issues or regressions Work through each step systematically. Check off completed items. ## References - [npm Security Best Practices](https://docs.npmjs.com/security-best-practices) - [Python Packaging Security](https://packaging.python.org/en/latest/guides/security/) - [Go Modules Security](https://go.dev/blog/vuln) - [OWASP Dependency Check](https://owasp.org/www-project-dependency-check/)