511 lines
10 KiB
Markdown
511 lines
10 KiB
Markdown
# 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
|
|
<!-- 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**:
|
|
```xml
|
|
<dependencyManagement>
|
|
<dependencies>
|
|
<dependency>
|
|
<groupId>org.example</groupId>
|
|
<artifactId>vulnerable-lib</artifactId>
|
|
<version>2.0.0</version>
|
|
</dependency>
|
|
</dependencies>
|
|
</dependencyManagement>
|
|
```
|
|
|
|
### 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/)
|