Container security (#32)

* feat: container vulnerability scanning

* fix: Skip safety for dependabot
This commit is contained in:
Sean P. Kane
2026-01-12 13:23:32 -08:00
committed by GitHub
parent 75726329c7
commit 9dde1cef21
8 changed files with 170 additions and 49 deletions
+3
View File
@@ -133,6 +133,7 @@ jobs:
exit-code: "1" exit-code: "1"
format: "table" format: "table"
cache-dir: .trivy-cache cache-dir: .trivy-cache
trivyignores: ".trivyignore"
- name: Scan for MEDIUM/LOW vulnerabilities (warning only) - name: Scan for MEDIUM/LOW vulnerabilities (warning only)
uses: aquasecurity/trivy-action@0.33.1 uses: aquasecurity/trivy-action@0.33.1
@@ -142,6 +143,7 @@ jobs:
exit-code: "0" exit-code: "0"
format: "table" format: "table"
cache-dir: .trivy-cache cache-dir: .trivy-cache
trivyignores: ".trivyignore"
continue-on-error: true continue-on-error: true
- name: Generate SARIF report for GitHub Security - name: Generate SARIF report for GitHub Security
@@ -152,6 +154,7 @@ jobs:
format: "sarif" format: "sarif"
output: "trivy-results.sarif" output: "trivy-results.sarif"
cache-dir: .trivy-cache cache-dir: .trivy-cache
trivyignores: ".trivyignore"
continue-on-error: true continue-on-error: true
- name: Upload Trivy scan results - name: Upload Trivy scan results
+4 -1
View File
@@ -47,9 +47,12 @@ jobs:
# Skip hooks that don't work well in CI: # Skip hooks that don't work well in CI:
# - no-commit-to-branch: Always fails in CI (we're on main/master) # - no-commit-to-branch: Always fails in CI (we're on main/master)
# - trufflehog: Has wasm/go-re2 panic bug in GitHub Actions environment # - trufflehog: Has wasm/go-re2 panic bug in GitHub Actions environment
# - safety: Skipped if SAFETY_API_KEY secret is not configured
# Note: shellcheck, hadolint, trivy use mise-managed binaries which ARE # Note: shellcheck, hadolint, trivy use mise-managed binaries which ARE
# installed by mise-action above, so they should work in CI. # installed by mise-action above, so they should work in CI.
SKIP: no-commit-to-branch,trufflehog SKIP: no-commit-to-branch,trufflehog${{ secrets.SAFETY_API_KEY == '' && ',safety' || '' }}
# Safety CLI API key for dependency vulnerability scanning # Safety CLI API key for dependency vulnerability scanning
# Get your key at: https://safetycli.com/ (free account)
# Add as repository secret: Settings → Secrets → Actions → SAFETY_API_KEY
SAFETY_API_KEY: ${{ secrets.SAFETY_API_KEY }} SAFETY_API_KEY: ${{ secrets.SAFETY_API_KEY }}
run: uv run pre-commit run --all-files --show-diff-on-failure run: uv run pre-commit run --all-files --show-diff-on-failure
+1 -1
View File
@@ -127,5 +127,5 @@
} }
], ],
"results": {}, "results": {},
"generated_at": "2026-01-12T16:32:14Z" "generated_at": "2026-01-12T20:07:54Z"
} }
+44
View File
@@ -0,0 +1,44 @@
# Trivy Vulnerability Ignore File
# ================================
# This file tells trivy to skip specific CVEs during image scanning.
# Use this for vulnerabilities that:
# - Have no fix available yet (check Alpine security tracker)
# - Are false positives for our use case
# - Are accepted risks with documented justification
#
# Format: One CVE ID per line, optionally with comment
# Docs: https://aquasecurity.github.io/trivy/latest/docs/configuration/filtering/
#
# Before adding a CVE here:
# 1. Verify no fix is available: https://security.alpinelinux.org/
# 2. Document the reason and expected fix date if known
# 3. Set a reminder to revisit when fixes become available
#
# To check if fixes are now available, run:
# just docker::scan
#
# =============================================================================
# IGNORED VULNERABILITIES
# =============================================================================
# --- Alpine Base Image CVEs (No Fix Available) ---
# These are in the python:3.11-alpine base image's system packages.
# We run `apk upgrade --no-cache` in the Dockerfile to get the latest patches,
# but some CVEs may not have fixes yet.
# CVE-2026-22184 - zlib vulnerability (alpine/zlib 1.3.1-r2)
# Status: No fix available as of 2025-01-12
# Tracker: https://security.alpinelinux.org/
# CVE-2026-22184
# CVE-2025-60876 - busybox vulnerability (alpine/busybox 1.37.0-r30)
# Status: No fix available as of 2025-01-12
# Tracker: https://security.alpinelinux.org/
# CVE-2025-60876
# =============================================================================
# NOTES
# =============================================================================
# - Uncomment CVE lines above ONLY if you've verified no fix is available
# - Review this file monthly to remove CVEs that now have fixes
# - The `apk upgrade` in Dockerfile should auto-fix most CVEs when rebuilding
+5
View File
@@ -68,6 +68,11 @@ LABEL org.opencontainers.image.title="FreeCAD Robust MCP Server" \
org.opencontainers.image.authors="Sean P. Kane <spkane@gmail.com>" \ org.opencontainers.image.authors="Sean P. Kane <spkane@gmail.com>" \
org.opencontainers.image.base.name="python:3.11-alpine" org.opencontainers.image.base.name="python:3.11-alpine"
# Upgrade all Alpine packages to fix CVEs in base image (zlib, busybox, etc.)
# This ensures we get security patches even if the base image is slightly stale
# hadolint ignore=DL3018
RUN apk upgrade --no-cache
# Create non-root user for security (Alpine uses addgroup/adduser) # Create non-root user for security (Alpine uses addgroup/adduser)
RUN addgroup -g 1000 mcpuser && \ RUN addgroup -g 1000 mcpuser && \
adduser -u 1000 -G mcpuser -s /bin/sh -D mcpuser adduser -u 1000 -G mcpuser -s /bin/sh -D mcpuser
+1 -1
View File
@@ -10,7 +10,7 @@
|Icon=FreecadRobustMCPBridge.svg |Icon=FreecadRobustMCPBridge.svg
|Description=Bridge workbench designed to provide an interface between FreeCAD and the Robust MCP Server to enable AI assistants (like Claude) to control FreeCAD via the Model Context Protocol (MCP). It provides XML-RPC and JSON-RPC interfaces for external automation. |Description=Bridge workbench designed to provide an interface between FreeCAD and the Robust MCP Server to enable AI assistants (like Claude) to control FreeCAD via the Model Context Protocol (MCP). It provides XML-RPC and JSON-RPC interfaces for external automation.
|Author=Sean P. Kane |Author=Sean P. Kane
|Version=0.6.1 |Version=99.99.99-test
|Date=2026-01-12 |Date=2026-01-12
|FCVersion=0.21+ |FCVersion=0.21+
|Download=[https://github.com/spkane/freecad-robust-mcp-and-more/releases Latest Release] |Download=[https://github.com/spkane/freecad-robust-mcp-and-more/releases Latest Release]
+38 -6
View File
@@ -92,34 +92,66 @@ clean-all:
@echo "Docker images and build cache cleaned." @echo "Docker images and build cache cleaned."
# Scan Docker image for vulnerabilities (warn on all severities) # Scan Docker image for vulnerabilities (warn on all severities)
# Uses .trivyignore file to skip CVEs without available fixes
scan: scan:
#!/usr/bin/env bash #!/usr/bin/env bash
set -euo pipefail set -euo pipefail
IGNOREFILE="{{project_root}}/.trivyignore"
IGNORE_FLAG=""
if [ -f "$IGNOREFILE" ]; then
IGNORE_FLAG="--ignorefile $IGNOREFILE"
echo "Using ignore file: $IGNOREFILE"
fi
echo "Scanning {{image_name}} for vulnerabilities..." echo "Scanning {{image_name}} for vulnerabilities..."
echo "" echo ""
trivy image --severity HIGH,CRITICAL {{image_name}} trivy image --severity HIGH,CRITICAL $IGNORE_FLAG {{image_name}}
echo "" echo ""
echo "Scanning for MEDIUM/LOW (informational)..." echo "Scanning for MEDIUM/LOW (informational)..."
trivy image --severity MEDIUM,LOW {{image_name}} || true trivy image --severity MEDIUM,LOW $IGNORE_FLAG {{image_name}} || true
# Scan Docker image with strict settings (fail on HIGH or CRITICAL) # Scan Docker image with strict settings (fail on HIGH or CRITICAL)
# Uses .trivyignore file to skip CVEs without available fixes
scan-strict: scan-strict:
#!/usr/bin/env bash #!/usr/bin/env bash
set -euo pipefail set -euo pipefail
IGNOREFILE="{{project_root}}/.trivyignore"
IGNORE_FLAG=""
if [ -f "$IGNOREFILE" ]; then
IGNORE_FLAG="--ignorefile $IGNOREFILE"
echo "Using ignore file: $IGNOREFILE"
fi
echo "Scanning {{image_name}} for HIGH/CRITICAL vulnerabilities..." echo "Scanning {{image_name}} for HIGH/CRITICAL vulnerabilities..."
echo "(Build will fail if any HIGH or CRITICAL CVEs are found)" echo "(Build will fail if any HIGH or CRITICAL CVEs are found)"
echo "" echo ""
trivy image --severity HIGH,CRITICAL --exit-code 1 {{image_name}} trivy image --severity HIGH,CRITICAL --exit-code 1 $IGNORE_FLAG {{image_name}}
echo "" echo ""
echo "✓ No HIGH or CRITICAL vulnerabilities found!" echo "✓ No HIGH or CRITICAL vulnerabilities found!"
echo "" echo ""
echo "Scanning for MEDIUM/LOW (informational only)..." echo "Scanning for MEDIUM/LOW (informational only)..."
trivy image --severity MEDIUM,LOW --exit-code 0 {{image_name}} || true trivy image --severity MEDIUM,LOW --exit-code 0 $IGNORE_FLAG {{image_name}} || true
# Scan Docker image WITHOUT ignore file (shows all CVEs including unfixable)
scan-all:
#!/usr/bin/env bash
set -euo pipefail
echo "Scanning {{image_name}} for ALL vulnerabilities (ignoring .trivyignore)..."
echo ""
trivy image --severity HIGH,CRITICAL {{image_name}}
echo ""
echo "Scanning for MEDIUM/LOW..."
trivy image --severity MEDIUM,LOW {{image_name}} || true
# Scan Docker image and output SARIF report # Scan Docker image and output SARIF report
scan-sarif output="trivy-results.sarif": scan-sarif output="trivy-results.sarif":
trivy image --format sarif --output {{project_root}}/{{output}} {{image_name}} #!/usr/bin/env bash
@echo "SARIF report written to {{project_root}}/{{output}}" set -euo pipefail
IGNOREFILE="{{project_root}}/.trivyignore"
IGNORE_FLAG=""
if [ -f "$IGNOREFILE" ]; then
IGNORE_FLAG="--ignorefile $IGNOREFILE"
fi
trivy image --format sarif --output {{project_root}}/{{output}} $IGNORE_FLAG {{image_name}}
echo "SARIF report written to {{project_root}}/{{output}}"
# Create and configure buildx builder for multi-arch builds # Create and configure buildx builder for multi-arch builds
setup-buildx: setup-buildx:
+74 -40
View File
@@ -782,67 +782,101 @@ rollback-release tag:
echo "Git/GitHub Rollback complete!" echo "Git/GitHub Rollback complete!"
echo "==========================================" echo "=========================================="
# MCP Server: Offer to yank from PyPI and provide Docker cleanup info # MCP Server: Provide PyPI and Docker cleanup instructions
if [ "$COMPONENT" = "mcp-server" ]; then if [ "$COMPONENT" = "mcp-server" ]; then
echo "" echo ""
echo "--- PyPI Cleanup ---" echo "--- PyPI Cleanup ---"
echo "" echo ""
echo "Would you like to yank version $VERSION from PyPI?" echo "To yank version $VERSION from PyPI (hides from 'pip install'):"
echo " - Yanking hides the version from 'pip install freecad-robust-mcp'"
echo " - Users who pin to this version can still install it"
echo " - This can be undone later with 'twine yank --undo'"
echo "" echo ""
read -p "Yank from PyPI? [y/N] " -n 1 -r echo " 1. Go to: https://pypi.org/manage/project/freecad-robust-mcp/releases/"
echo " 2. Click 'Options' → 'Yank'"
echo " 3. Enter a reason (e.g., 'Released from wrong branch')"
echo " 4. Confirm"
echo ""
echo "Note: Yanking hides the version from default pip install, but users"
echo " who pin to this exact version can still install it."
echo " Yanking can be undone from the same page."
echo ""
read -p "Open PyPI release page in browser? [y/N] " -n 1 -r
echo echo
if [[ $REPLY =~ ^[Yy]$ ]]; then if [[ $REPLY =~ ^[Yy]$ ]]; then
echo "" PYPI_URL="https://pypi.org/manage/project/freecad-robust-mcp/releases/"
echo "Attempting to yank from PyPI..." if command -v open &> /dev/null; then
echo "(This requires PyPI authentication - you may be prompted to login)" open "$PYPI_URL"
echo "" elif command -v xdg-open &> /dev/null; then
if command -v twine &> /dev/null || uv run twine --version &>/dev/null; then xdg-open "$PYPI_URL"
if uv run twine yank freecad-robust-mcp "$VERSION" 2>&1; then
echo " ✓ Version $VERSION yanked from PyPI."
else
echo ""
echo " Yank failed. You can try manually:"
echo " uv run twine yank freecad-robust-mcp $VERSION"
echo ""
echo " Or use the PyPI web interface:"
echo " https://pypi.org/manage/project/freecad-robust-mcp/release/$VERSION/"
fi
else else
echo " twine not available. Install with: uv add twine" echo "Could not open browser. Visit: $PYPI_URL"
echo ""
echo " Or use the PyPI web interface:"
echo " https://pypi.org/manage/project/freecad-robust-mcp/release/$VERSION/"
fi fi
else
echo ""
echo "Skipped PyPI yank. You can do this later with:"
echo " uv run twine yank freecad-robust-mcp $VERSION"
echo ""
echo "Or via web interface:"
echo " https://pypi.org/manage/project/freecad-robust-mcp/release/$VERSION/"
fi fi
echo "" echo ""
echo "--- Docker Hub Cleanup ---" echo "--- Docker Hub Cleanup ---"
echo "" echo ""
echo "Docker Hub tags must be deleted manually." echo "To delete Docker Hub tags:"
echo "Tags to delete: $VERSION, latest (if this was the latest release)"
echo "" echo ""
echo "Web interface:" echo " 1. Go to: https://hub.docker.com/repository/docker/spkane/freecad-robust-mcp/tags"
echo " https://hub.docker.com/r/spkane/freecad-robust-mcp/tags" echo " 2. Find tag: $VERSION"
echo " 3. Click the checkbox and 'Delete'"
echo " 4. Also delete 'latest' tag if this was the latest release"
echo "" echo ""
echo "Or via API (requires DOCKER_HUB_TOKEN):" read -p "Open Docker Hub tags page in browser? [y/N] " -n 1 -r
echo " curl -X DELETE -H \"Authorization: Bearer \$DOCKER_HUB_TOKEN\" \\" echo
echo " https://hub.docker.com/v2/repositories/spkane/freecad-robust-mcp/tags/$VERSION/" if [[ $REPLY =~ ^[Yy]$ ]]; then
DOCKER_URL="https://hub.docker.com/repository/docker/spkane/freecad-robust-mcp/tags"
if command -v open &> /dev/null; then
open "$DOCKER_URL"
elif command -v xdg-open &> /dev/null; then
xdg-open "$DOCKER_URL"
else
echo "Could not open browser. Visit: $DOCKER_URL"
fi
fi
fi fi
echo "" echo ""
echo "==========================================" echo "=========================================="
echo "Rollback process complete!" echo "ROLLBACK SUMMARY"
echo "==========================================" echo "=========================================="
echo ""
# Show what was done and what remains
case "$COMPONENT" in
mcp-server)
echo "COMPLETED (automated):"
echo " ✓ GitHub Release deleted"
echo " ✓ Git tag deleted (local and remote)"
echo ""
echo "MANUAL STEPS REQUIRED:"
echo " □ PyPI: Yank version $VERSION"
echo " URL: https://pypi.org/manage/project/freecad-robust-mcp/releases/"
echo ""
echo " □ Docker Hub: Delete tag $VERSION"
echo " URL: https://hub.docker.com/repository/docker/spkane/freecad-robust-mcp/tags"
echo ""
echo " □ Docker Hub: Delete 'latest' tag (if this was the latest release)"
echo " URL: https://hub.docker.com/repository/docker/spkane/freecad-robust-mcp/tags"
;;
workbench|macro-magnets|macro-export)
echo "COMPLETED (automated):"
echo " ✓ GitHub Release deleted"
echo " ✓ Git tag deleted (local and remote)"
echo ""
echo "MANUAL STEPS REQUIRED:"
echo " (none - rollback is complete!)"
;;
*)
echo "COMPLETED (automated):"
echo " ✓ GitHub Release deleted (if existed)"
echo " ✓ Git tag deleted (local and remote)"
echo ""
echo "MANUAL STEPS REQUIRED:"
echo " (unknown component - verify no additional cleanup needed)"
;;
esac
echo ""
# Delete only the GitHub Release (keep tag) # Delete only the GitHub Release (keep tag)
delete-github-release tag: delete-github-release tag: