Files
gh-agentsecops-secopsagentkit/skills/devsecops/container-grype/references/vulnerability_remediation.md
2025-11-29 17:51:02 +08:00

10 KiB

Vulnerability Remediation Patterns

Common patterns for remediating dependency vulnerabilities detected by Grype.

Table of Contents

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:

# 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):

// package.json - Override transitive dependency
{
  "overrides": {
    "lodash": "^4.17.21"
  }
}

Example (pip):

# 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:

# 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):

{
  "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):

# 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):

# 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:

# 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:

# 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:

# ❌ 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:

# 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:

# Dependabot config for Dockerfile
version: 2
updates:
  - package-ecosystem: "docker"
    directory: "/"
    schedule:
      interval: "weekly"

Manual update process:

# 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:

# ✅ 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
// 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:

// package.json - Allow patch updates
{
  "dependencies": {
    "express": "~4.18.2",  // Allow 4.18.x
    "lodash": "^4.17.21"   // Allow 4.x.x
  }
}
# 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:

# 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:

# 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:

# 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:

# 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:

# 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:

# 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:

{
  "overrides": {
    "vulnerable-package": "^2.0.0"
  }
}

Go

Update vulnerable module:

# 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:

<!-- pom.xml - Update version -->
<dependency>
    <groupId>org.example</groupId>
    <artifactId>vulnerable-lib</artifactId>
    <version>2.0.0</version> <!-- Updated from 1.0.0 -->
</dependency>

Force dependency version:

<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.example</groupId>
            <artifactId>vulnerable-lib</artifactId>
            <version>2.0.0</version>
        </dependency>
    </dependencies>
</dependencyManagement>

Rust

Update vulnerable crate:

# 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