Skip to content

feat(039): Security Scanner Plugin System#364

Open
Dumbris wants to merge 30 commits intomainfrom
feat/039-security-scanner-plugins
Open

feat(039): Security Scanner Plugin System#364
Dumbris wants to merge 30 commits intomainfrom
feat/039-security-scanner-plugins

Conversation

@Dumbris
Copy link
Copy Markdown
Member

@Dumbris Dumbris commented Apr 3, 2026

Summary

  • Pluggable security scanner system for analyzing quarantined MCP servers before approval
  • Docker-based scanners run in isolated containers, produce SARIF 2.1.0 reports
  • 4 bundled scanners: mcp-scan, Cisco MCP Scanner, Semgrep, Trivy
  • Parallel scan execution with risk scoring (0-100), approve/reject/rescan workflow
  • Full-stack: REST API (13 endpoints), CLI (12 subcommands), Web UI (Security dashboard), SSE events

What's Included

Backend (Go)

  • internal/security/scanner/ — Scanner types, registry, Docker runner, SARIF parser, scan engine, service
  • internal/storage/scanner.go — BBolt CRUD for 4 new buckets (scanners, jobs, reports, baselines)
  • internal/httpapi/security_scanner.go — 13 REST API endpoints + SecurityController interface
  • internal/runtime/events.go — 5 new SSE event types for scan lifecycle
  • internal/config/config.go — SecurityConfig section
  • cmd/mcpproxy/security_cmd.go — CLI commands: scanners/install/scan/approve/reject/overview

Frontend (Vue 3)

  • frontend/src/views/Security.vue — Dashboard with scanner marketplace, scan trigger, findings viewer
  • API methods, router, sidebar navigation updated

Documentation

  • docs/features/security-scanner-plugins.md — Feature guide with CLI reference, API reference
  • specs/039-security-scanner-plugins/ — Spec, plan, checklist, autonomous summary

Test plan

  • Scanner package tests pass with -race (registry, SARIF, engine, service)
  • Storage tests pass with -race (CRUD for all 4 entity types)
  • HTTP API tests pass with -race (all 13 endpoints)
  • Config tests pass (SecurityConfig parsing)
  • Frontend type-checks clean (vue-tsc --noEmit)
  • Frontend builds successfully (npm run build)
  • Full binary builds (go build ./...)
  • Pre-commit hooks pass (trailing whitespace, gofmt, OAS verification)
  • Manual: Install mcp-scan scanner and scan a real quarantined server
  • Manual: Verify Web UI Security page renders and functions
  • Manual: Test CLI commands end-to-end against running instance

claude added 2 commits April 3, 2026 09:05
Implement pluggable security scanner system for analyzing quarantined
MCP servers before approval. Docker-based scanners run in isolated
containers, produce SARIF reports, and integrate with the existing
quarantine workflow.

## Changes
- Scanner plugin architecture: types, registry (4 bundled scanners),
  Docker runner, SARIF 2.1.0 parser, parallel scan engine
- Storage: 4 new BBolt buckets (scanners, jobs, reports, baselines)
- Security service: install/configure scanners, scan/approve/reject
  workflow, integrity verification, risk scoring
- REST API: 13 endpoints for scanner management, scan operations,
  approval flow, and security overview
- CLI: mcpproxy security command group with 12 subcommands
- Web UI: Security dashboard with scanner marketplace, scan trigger,
  findings viewer, approve/reject actions
- SSE events: scan lifecycle and integrity alert events
- Documentation: feature guide, spec, plan, autonomous summary

## Testing
- 500+ tests passing across scanner, storage, httpapi, config packages
- Race detector clean on all new code
- Frontend type-checks clean and builds successfully

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@cloudflare-workers-and-pages
Copy link
Copy Markdown

cloudflare-workers-and-pages bot commented Apr 3, 2026

Deploying mcpproxy-docs with  Cloudflare Pages  Cloudflare Pages

Latest commit: 42941be
Status: ✅  Deploy successful!
Preview URL: https://1743abee.mcpproxy-docs.pages.dev
Branch Preview URL: https://feat-039-security-scanner-pl.mcpproxy-docs.pages.dev

View logs

@github-actions
Copy link
Copy Markdown

github-actions bot commented Apr 3, 2026

📦 Build Artifacts

Workflow Run: View Run
Branch: feat/039-security-scanner-plugins

Available Artifacts

  • archive-darwin-amd64 (26 MB)
  • archive-darwin-arm64 (23 MB)
  • archive-linux-amd64 (15 MB)
  • archive-linux-arm64 (13 MB)
  • archive-windows-amd64 (25 MB)
  • archive-windows-arm64 (23 MB)
  • frontend-dist-pr (0 MB)
  • installer-dmg-darwin-amd64 (19 MB)
  • installer-dmg-darwin-arm64 (17 MB)

How to Download

Option 1: GitHub Web UI (easiest)

  1. Go to the workflow run page linked above
  2. Scroll to the bottom "Artifacts" section
  3. Click on the artifact you want to download

Option 2: GitHub CLI

gh run download 23996310827 --repo smart-mcp-proxy/mcpproxy-go

Note: Artifacts expire in 14 days.

claude added 26 commits April 3, 2026 09:34
Critical fixes discovered during E2E testing:
- Wire scanner.Service as SecurityController in server setup
- Register security routes unconditionally (nil-guard in handlers)
- Use background context for scan goroutines (prevent request context cancellation)
- Sync registry from storage on startup (scanner state survives restart)
- Add source_dir parameter to scan API and service
- Fix Semgrep: mount source at /src (Docker image requirement), enable network
- Disable read-only for scanner containers (they need cache writes)
- Fix Trivy image name: aquasecurity/trivy (not aquasec/trivy)
- Add GetSecurityOverview alias on Service for interface compliance

Verified E2E:
- Semgrep scan found subprocess shell=True (HIGH) and eval() (MEDIUM)
- Full approve/reject/report CLI workflow tested against running server

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
… scanning

Users no longer need to provide --source-dir. When scanning, MCPProxy
automatically:
1. Finds the running Docker container for the server (mcpproxy-<name>-*)
2. Extracts app source files via docker cp + docker diff analysis
3. Mounts extracted source for scanner containers
4. Cleans up temp files after scan completes

Fallback chain: Docker container -> working_dir -> command args -> error.
HTTP/SSE servers resolve to URL for mcp_connection scanners.

Verified E2E: mcpproxy security scan perplexity works with zero config.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Scanner API keys configured via the API or CLI are now stored in the
OS keyring (macOS Keychain / Linux Secret Service / Windows Credential
Manager) using the existing mcpproxy secrets system.

- ConfigureScanner stores values via keyring, keeps ${keyring:...} refs
- Engine resolves keyring references at scan time before passing to containers
- Secrets visible in `mcpproxy secrets list` alongside server secrets
- Fallback to direct storage if keyring is unavailable

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The Web UI expected APIResponse format {success: true, data: ...} but
security handlers were writing raw data. Changed all handlers to use
writeSuccess() which wraps via contracts.NewSuccessResponse().

Updated all 23 httpapi security tests to parse the wrapped format.
Rebuilt frontend dist for embedded Web UI.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Bugs found and fixed during E2E testing with real scanners:

1. CLI commands failed to parse wrapped API responses ({success, data})
   - Added unwrapAPIResponse() helper to extract data from envelope
   - Applied to all 8 json.Unmarshal points in security_cmd.go

2. Trivy scanner: "unknown command trivy for trivy"
   - Trivy Docker image has ENTRYPOINT=[trivy], so command was doubled
   - Fixed: command is now ["fs", "--format", "sarif", "/scan/source"]

3. Trivy scanner: "no space left on device" during DB download
   - Trivy downloads 89MB vuln DB to /tmp on first run
   - Tmpfs was 100MB with noexec — too small and restrictive
   - Fixed: increased to 500MB, removed noexec (scanners need it)

4. Trivy Docker image: aquasecurity/trivy not on Docker Hub
   - Changed to ghcr.io/aquasecurity/trivy:latest (GHCR registry)

Verified E2E: Semgrep + Trivy both complete scans successfully on
quarantined everything-server with real Docker containers.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…ores

Findings now include:
- help_uri: clickable link to CVE advisory (e.g., https://avd.aquasec.com/nvd/CVE-xxx)
- cvss_score: CVSS severity score (0-10) from SARIF properties
- package_name, installed_version, fixed_version: extracted from Trivy message format

CLI report shows:
  [HIGH] CVE-2025-66414 (trivy-mcp)
         Package: @modelcontextprotocol/sdk v0.6.0 -> fix: 1.24.0
         Details: https://avd.aquasec.com/nvd/cve-2025-66414

Web UI findings table shows clickable CVE links, package column,
fix version badge, and CVSS score.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…categories

Backend: SecurityScanSummary on Server contract, threat classification,
scan summary computation from stored reports.

Frontend: Security tab with risk score, threat summary cards, grouped
findings, scan button with polling. Scan badge on server cards.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The /api/v1/servers endpoint uses management.ListServers(), not the
legacy GetAllServers(). Moved scan summary enrichment to the httpapi
handler. Also re-classify legacy findings without threat_level on read.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
AggregateReports now classifies findings before summarizing.
ReportSummary includes dangerous/warnings/info_level counts.
Frontend reads summary from API response.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Risk score based on threat level instead of raw CVSS severity.
Scanner stdout/stderr/exit_code stored on ScannerJobStatus.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Configure button shows for all installed/configured scanners.
Dialog supports required, optional, and custom env vars with
keyring integration. Users can add OPENAI_API_KEY etc.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Each finding is now an expandable card with full details.
Scanner Execution Logs section shows stderr/stdout per scanner.
Fixed TS types to match Go API field names.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
ScanContext on ScanJob with source method, path, isolation status,
file list. File tree API with suspicious file markers.
Auto-prune keeps last 20 scans. Frontend scan context banner +
scanned files collapsible with lazy loading.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
SARIF raw data can be 2MB+ (Semgrep includes all rule definitions).
Web UI was stuck on "Loading scan report..." because of this.
Now stripped by default; add ?include_sarif=true for CLI --output sarif.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
ScanQueue with 3 concurrent workers, progress tracking, cancel support.
REST API: scan-all, queue, cancel-all endpoints.
CLI: --all flag and cancel-all subcommand.
Web UI: Scan All button with progress card.
Disabled servers auto-skipped with hint.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
SECURITY: UI now shows red error instead of green "safe" when scanners fail.
AggregatedReport tracks scan_complete, scanners_run/failed/total.
Server card badge shows "Scan Failed" (red) for incomplete scans.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Three fixes for scanner reliability:

1. resolveScanners() now checks ImageExists() before adding to run list.
   Scanners with non-existent Docker images (mcp-scan, cisco-mcp-scanner
   placeholders) are skipped with a warning log instead of failing at
   runtime with exit code 125.

2. Per-scanner cache directory persisted at ~/.mcpproxy/scanner-cache/<id>/
   mounted at /root/.cache in containers. Trivy DB (90MB) is now
   downloaded once and reused across runs instead of re-downloading
   every scan.

3. Fixed test to use nil docker runner for resolution logic tests.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Semgrep timed out on ElevenLabs (14,953 files, 361MB including 6,979
Python files from site-packages). Two fixes:

1. Semgrep timeout increased from 5min to 10min for large source trees
2. Semgrep command now excludes site-packages, node_modules, dist-packages
3. Source resolver excludes dependency directories from docker diff
   extraction — these are third-party packages, not user code

Before: ElevenLabs extracted 14,953 files (361MB) -> Semgrep timeout
After: Only actual MCP server code extracted -> fast scan

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
For servers installed via uvx --from pkg@git+URL, the actual source
code lives at /root/.cache/uv/git-v0/checkouts/<hash>/<rev>/.
Previously this was extracted as /root/.cache (too broad) or filtered
out entirely.

Changes:
- extractAppRoot recognizes UV git checkout paths specifically
- isSystemPath excludes UV archive-v0 (dependencies) but keeps git-v0
- npm node_modules exclusion now allows npx cache paths
- Tests updated for UV git checkout extraction

Note: Scanner requires the server container to be fully connected.
If uvx is still downloading from GitHub when the scan runs, the source
won't be available yet.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
For servers installed via uvx --from pkg@git+URL (like gcore-mcp-server),
the actual source lives at /root/.cache/uv/git-v0/checkouts/<hash>/<rev>/.

Fixes:
- extractAppRoot excludes /root/.cache and /root/.local (too broad)
- Fallback path uses docker exec find to locate UV git checkouts
  directly when docker diff doesn't show them
- Removed /root from fallback dirs (was copying entire 10K+ file cache)

Before: 10,536 files (174MB) including all pip/uv dependencies
After: 26 files (422KB) — just the actual MCP server source code

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Pass 1: Semgrep on source + Trivy on lockfile (fast, immediate results)
Pass 2: Trivy full filesystem (background, auto-starts after Pass 1)
Results merged in single report with pass1/pass2 completion tracking.
7 new unit tests. Frontend shows both passes with progress indicator.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
In-memory cache for scan summaries, invalidated on scan start/complete.
Cold cache: 3.0s. Warm cache: 0.8s. Was: 12.1s.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Pagination with limit/offset/suspicious_only. Progressive loading
in frontend with "Load more" button. Suspicious files sorted first.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
claude added 2 commits April 5, 2026 09:36
…PI key)

Built Docker image for Cisco AI Defense MCP Scanner. Runs YARA +
readiness analyzers offline. Detects tool poisoning, prompt injection,
credential harvesting, data exfiltration in MCP tool descriptions.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Evidence field on ScanFinding captures the tool description that
triggered Cisco YARA warnings. Shown in Web UI and CLI report.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants