Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
183 changes: 183 additions & 0 deletions .claude/skills/patch-cves/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
# Patching CVEs in Robusta: Automated Workflow

This skill automates the process of identifying and patching CVE vulnerabilities in the Robusta Docker image and Python dependencies, focusing on critical, high, and medium severity issues.

## Overview

The workflow follows this systematic process:

1. **Vulnerability Scanning** - Identify all CVEs in dependencies and Docker image
2. **Severity Filtering** - Focus on critical, high, and medium severity issues
3. **Root Cause Analysis** - Determine which packages/dependencies introduce vulnerabilities
4. **Upstream Research** - Check if newer releases already include fixes
5. **Patch Implementation** - Apply fixes via dependency upgrades or Dockerfile changes
6. **Validation** - Verify CVE fixes and ensure application functionality

## Step-by-Step Process

### 1. Vulnerability Scanning

Use multiple scanning tools to identify vulnerabilities:

```bash
# Scan Docker image for vulnerabilities
docker build -t robusta:latest .
docker scout cves robusta:latest

# Scan Python dependencies for vulnerabilities
pip-audit
safety check

# Validate pyproject.toml metadata and lockfile consistency (does not perform vulnerability scanning)
poetry check
# For CVE scanning of Python dependencies, use pip-audit, safety, or poetry-audit-plugin
```

**What to extract:**
- Affected package name and version
- CVE ID and severity level
- Fixed version (if available)
- Affected version range

### 2. Severity Filtering

Process vulnerabilities in this order:
1. **Critical** - Must be fixed before release
2. **High** - Should be fixed before release
3. **Medium** - Fix when safe and non-breaking

Create a prioritized list and document each CVE:

```
CVE-XXXX-XXXXX (Critical): Package X - affects >=1.0.0,<1.2.0
Fixed in: 1.2.5
Status: Needs patching

CVE-YYYY-YYYYY (High): Package Y - affects >=2.0.0,<2.1.0
Fixed in: 2.1.3
Status: Needs patching
```

### 3. Python Dependency Patches

Two main strategies:

**Strategy A: Direct Upgrade (Preferred)**
- Check `poetry.lock` for affected packages
- Update `pyproject.toml` with patched version
- Run `poetry update package-name`
- Verify in `poetry.lock` that lock file has updated to fixed version

**Strategy B: Transitive Dependency Fix**
- Identify the parent package bringing in vulnerable version
- Upgrade parent package to one with updated dependencies
- This automatically pulls in the fixed transitive dependency


### 4. Dockerfile Patches

For system-level vulnerabilities (non-Python packages):

**Strategy A: Upgrade Base Image**
- Check if newer Python 3.11-slim image includes fixes
- Update FROM statement: `FROM python:3.11-slim` → newer version

**Strategy B: Explicit Package Installation**
- Add specific package upgrade in RUN commands
- Example: `apt-get install -y libssl3` for OpenSSL CVEs

**Strategy C: Apply Patches**
- Use patching tools for targeted fixes in builder stage
- Document with comments explaining which CVEs are fixed

### 5. Validation Checklist

✓ **CVE Verification**
- Run `docker scout cves` again on patched image
- Confirm target CVE no longer appears
- Note any remaining high/critical issues for tracking

✓ **Build Verification**
```bash
# Build the Docker image
docker build -t robusta:test .

# Verify build succeeds with no errors
echo "Build successful"
```

✓ **Functional Testing**
```bash
# Run basic smoke tests
pytest tests/ -v
```

✓ **Dependency Check**
```bash
# Verify no new vulnerabilities introduced
docker scout cves robusta:test --no-cache

# Validate pyproject.toml metadata and lockfile consistency
poetry check --lock
```

### 6. Documentation

Update these files with CVE fix details:

**Dockerfile Comments:**
```dockerfile
# Patching CVE-XXXX-XXXXX (Critical): Package X
RUN apt-get install -y package-name
```

## Key Considerations

### Python Package CVEs
- Check if vulnerability is in the installed wheel vs source
- For indirect dependencies, finding the transitive source is critical
- Use `poetry why package-name` to understand dependency relationships
- Go version matters for Go-based Python bindings (e.g., Cryptography)

### System Library CVEs
- libexpat1, libssl, libc vulnerabilities are common
- These often have fixes in newer base images
- When possible, upgrade the base Python image before manual fixes

### Testing Strategy
- Always rebuild and scan after each patch
- One CVE at a time is safer; group similar fixes together
- Document any CVEs that can't be patched with reasoning

### Breaking Changes
- Verify patched versions don't introduce breaking changes
- Check release notes and migration guides
- Run full test suite, not just smoke tests for major upgrades

## Implementation Notes

1. Work through CVEs in severity order (Critical → High → Medium)
2. For each CVE, follow the complete cycle: identify → research → patch → validate
3. Commit each logical group of fixes separately
4. Keep diagnostics available: `docker scout cves` output, dependency trees, test results
5. If a patch can't be safely applied, document why in the code comments

## Common Issues and Solutions

### Issue: Patch introduces breaking changes
**Solution:**
1. Check if breaking change is in major version bump
2. Review if dependency needs to be pinned differently
3. Consider if a workaround exists (e.g., compatibility shim)

### Issue: Transitive dependency is vulnerable
**Solution:**
1. Find which package brings it in: `poetry why vulnerable-package`
2. Update the parent package instead
3. Re-lock dependencies and verify fix

### Issue: CVE disappears after unrelated patch
**Solution:**
1. Good sign - often due to transitive dependency updates
2. Still verify with `docker scout cves` on final image
3. Update documentation to credit upstream fixes
13 changes: 12 additions & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# Build stage
FROM python:3.11-slim as builder

Check warning on line 2 in Dockerfile

View workflow job for this annotation

GitHub Actions / build

The 'as' keyword should match the case of the 'from' keyword

FromAsCasing: 'as' and 'FROM' keywords' casing do not match More info: https://docs.docker.com/go/dockerfile/rule/from-as-casing/
ENV PATH="/root/.local/bin/:$PATH"

RUN apt-get update \
Expand Down Expand Up @@ -41,6 +41,9 @@
COPY playbooks/ /etc/robusta/playbooks/defaults
RUN pip install --no-cache-dir /etc/robusta/playbooks/defaults

# Patching CVE-2026-24049 (High): wheel path traversal vulnerability
RUN pip install --no-cache-dir "wheel>=0.46.2"

# Fixes k8s library bug - see https://github.com/kubernetes-client/python/issues/1867#issuecomment-1353813412
RUN find /app/venv/lib/python*/site-packages/kubernetes/client/rest.py -type f -exec sed -i 's:^\(.*logger.*\)$:#\1:' {} \;

Expand All @@ -60,17 +63,18 @@
ENV PYTHONUNBUFFERED=1
ENV VIRTUAL_ENV=/app/venv
ENV PATH="/venv/bin:$PATH"
ENV PYTHONPATH=$PYTHONPATH:.:/app/src

Check warning on line 66 in Dockerfile

View workflow job for this annotation

GitHub Actions / build

Variables should be defined before their use

UndefinedVar: Usage of undefined variable '$PYTHONPATH' More info: https://docs.docker.com/go/dockerfile/rule/undefined-var/

WORKDIR /app

# Install necessary packages for the runtime environment
# We're installing here libexpat1, to upgrade the package to include a fix to 3 high CVEs. CVE-2024-45491,CVE-2024-45490,CVE-2024-45492
# Patching glibc for CVE-2026-0861, CVE-2026-0915, CVE-2025-15281
RUN apt-get update \
&& dpkg --add-architecture arm64 \
&& pip3 install --no-cache-dir --upgrade pip \
&& apt-get install -y --no-install-recommends git ssh curl libcairo2 apt-transport-https gnupg2 \
&& apt-get install -y --no-install-recommends libexpat1 \
&& apt-get install -y --no-install-recommends libexpat1 libc6 libc-bin \
&& rm -rf /var/lib/apt/lists/*


Expand All @@ -81,12 +85,19 @@
RUN rm -rf /usr/local/lib/python3.11/ensurepip/_bundled/setuptools-65.5.0-py3-none-any.whl
RUN rm -rf /usr/local/lib/python3.11/site-packages/setuptools-65.5.1.dist-info

# Patching CVE-2026-24049 (High): wheel path traversal vulnerability
# Patching CVE-2026-23949 (High): jaraco.context path traversal vulnerability (vendored in setuptools)
RUN pip3 install --no-cache-dir "wheel>=0.46.2" "setuptools>=80.10.1" \
&& rm -rf /usr/local/lib/python3.11/site-packages/setuptools/_vendor/wheel-0.45.1.dist-info

COPY --from=builder /app/venv /venv
COPY --from=builder /etc/robusta/playbooks/defaults /etc/robusta/playbooks/defaults
# Copy virtual environment and application files from the build stage
COPY --from=builder /app /app
# remove duplicated /app/venv - already copied to /venv
RUN rm -rf /app/venv
# Remove vendored wheel 0.45.1 from setuptools in venv (CVE-2026-24049)
RUN rm -rf /venv/lib/python3.11/site-packages/setuptools/_vendor/wheel*

# Set up kubectl
COPY --from=builder /app/Release.key /tmp/Release.key
Expand Down
47 changes: 25 additions & 22 deletions poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 6 additions & 4 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ supabase = "2.5.1"
tabulate = { version = "^0.8.10" }
slack-sdk = { version = "^3.23.0" }
Flask = { version = "^3.0.0", optional = true }
werkzeug = { version = ">=3.0.6", optional = true }
werkzeug = { version = ">=3.1.6", optional = true }
jinja2 = { version = ">=3.1.5", optional = true }
grafana-api = { version = "^1.0.3", optional = true }
watchdog = { version = "^2.1.0", optional = true }
Expand Down Expand Up @@ -71,12 +71,14 @@ prometheus-api-client = "0.5.4"
requests = "^2.32.3"
certifi = "^2023.7.22"
regex = "2024.5.15"
pyjwt = "^2.4.0"
urllib3 = "^2.6.2"
pyjwt = "^2.12.0"
urllib3 = "^2.6.3"
httpx = "0.27.2"
postgrest = "0.16.8"
# Pin to fix cve https://github.com/robusta-dev/robusta/security/dependabot/85
h2 = "^4.3.0"
h2 = "^4.3.0"
# Pin to fix CVE-2026-30922 (transitive via google-auth)
pyasn1 = ">=0.6.3"

[tool.poetry.dev-dependencies]
pre-commit = "^2.13.0"
Expand Down
Loading