Skip to content

Latest commit

 

History

History
328 lines (245 loc) · 18.3 KB

File metadata and controls

328 lines (245 loc) · 18.3 KB
id security-scanner-plugins
title Security Scanner Plugin System
sidebar_label Security Scanners
description Docker-based scanner plugins for detecting tool poisoning, prompt injection, CVEs, and supply-chain risks in upstream MCP servers
keywords
security
scanner
sarif
quarantine
mcp
tool-poisoning
supply-chain

Security Scanner Plugin System

MCPProxy integrates external security scanners as Docker-based plugins. Scanners analyze quarantined servers before approval, detecting tool poisoning attacks, prompt injection, malware, secrets leakage, and supply chain risks.

CLI reference: for the full breakdown of every mcpproxy security subcommand, flags, and examples see Security Commands.

Overview

Every scanner is a plugin — there is no built-in scanner. MCPProxy exposes a universal plugin interface that scanner authors implement by shipping a Docker image that reads from /scan/source and writes SARIF to /scan/report/results.sarif. Users browse a bundled scanner registry, enable scanners with one command, configure API keys if needed, and start scanning.

Key features

  • Plugin-only architecture — every scanner is a Docker-based plugin.
  • Universal scanner interface — source filesystem input, SARIF output.
  • Parallel scanning — multiple scanners run concurrently; per-scanner failures are isolated.
  • Source resolver — detects the right thing to scan for npx/uvx/pipx/bunx package-runner commands, falls back to working dir or tool definitions for HTTP/SSE servers.
  • Risk scoring — composite 0–100 risk score from aggregated findings.
  • Integrity verification — image digest checks on server restart.
  • Multi-UI — REST API, CLI, Web UI all powered by the same backend, consistent status vocabulary.
  • Failed-scanner visibility — both CLI and Web UI surface per-scanner failures so silent crashes don't look like "clean".

What changed recently (2026-04)

  • security approve now actually unquarantines the server and indexes its tools (was a no-op stub).
  • Source resolver tries the package cache first for package-runner commands, so filesystem-server-style data-dir args are no longer mistaken for source code.
  • The scanner configure path no longer touches the OS keyring by default on macOS. Env values are stored in the scanner record in BBolt. See Security Commands → configure for details and the opt-in flag.
  • security report surfaces the count of failed scanners and their names.
  • security scan --dry-run prints a source-resolution plan without starting any containers.
  • security scan blocking mode has a real wait loop (no more hangs) with a hard timeout.
  • scanner status vocabulary unified between table and JSON: available / pulling / installed / configured / error.

Quick start

1. List available scanners

mcpproxy security scanners
ID                   NAME                     VENDOR                  STATUS       INPUTS
-------------------------------------------------------------------------------------------------------
cisco-mcp-scanner    Cisco MCP Scanner        Cisco AI Defense        available    source
mcp-ai-scanner       MCP AI Scanner           MCPProxy                available    source
mcp-scan             Snyk Agent Scan          Snyk (Invariant Labs)   available    source
nova-proximity       Nova Proximity           MCPProxy                available    source
ramparts             Ramparts MCP Scanner     Javelin                 available    source
semgrep-mcp          Semgrep MCP Rules        Semgrep                 available    source
tpa-descriptions     Tool Description Analy... MCPProxy                installed    source
trivy-mcp            Trivy Vulnerability...   Aqua Security           available    source, container_image

tpa-descriptions is a built-in, Docker-less scanner and is installed (always on) out of the box — there is no image to pull. It analyzes a connected server's tool descriptions/schemas in-process, so it runs even for remote http/sse servers that have no source files or Docker container.

2. Enable scanners

mcpproxy security enable nova-proximity
mcpproxy security enable trivy-mcp
mcpproxy security enable mcp-ai-scanner

(install is a hidden alias for enable, kept for backward compatibility.)

3. Configure API keys (if the scanner needs them)

Only a subset of scanners requires an API key. mcp-scan (Snyk Agent Scan) needs a free token from Snyk; mcp-ai-scanner can use an optional Anthropic API key or Claude Code OAuth token for richer analysis but works in pattern-only mode without one.

mcpproxy security configure mcp-scan --env SNYK_TOKEN=snyk_xxx

Values are stored directly in the scanner record in BBolt. The scanner container receives them via Docker --env flags at scan time.

4. Scan a quarantined server

mcpproxy security scan github-server

The CLI runs all enabled scanners in parallel, prints live progress, and shows a summary.

5. Review the findings

mcpproxy security report github-server
mcpproxy security status github-server     # per-scanner detail including stderr
mcpproxy security report github-server -o sarif > github-server.sarif

6. Approve or reject

# Approve (unquarantines and indexes tools)
mcpproxy security approve github-server

# Or reject (deletes scan artifacts, keeps server quarantined)
mcpproxy security reject github-server

Scanner registry

MCPProxy ships with a bundled registry of 8 scanners. The bundled list lives in internal/security/scanner/registry_bundled.go.

Scanner Vendor Inputs Required env Notes
cisco-mcp-scanner Cisco AI Defense source YARA rules + readiness analysis. Needs tools.json in the source dir.
mcp-ai-scanner MCPProxy source — (optional ANTHROPIC_API_KEY / CLAUDE_CODE_OAUTH_TOKEN) Agent-based AI analysis with a pattern-only fallback. Lives in a separate repo.
mcp-scan Snyk (Invariant Labs) source SNYK_TOKEN Tool poisoning, prompt injection, tool shadowing, toxic flows, secrets, rug pulls.
nova-proximity MCPProxy (NOVA-inspired rules) source Keyword-based, fully offline. Very fast.
ramparts Javelin source Rust-based YARA scanner. (Known upstream issue on arm64 macOS — see Scanner Images.)
semgrep-mcp Semgrep source Static analysis with MCP-specific rules. Uses the upstream returntocorp/semgrep:latest image.
tpa-descriptions MCPProxy source Built-in, Docker-less, always on. In-process analysis of tool descriptions/schemas for Tool-Poisoning-Attack indicators (hidden instructions, prompt-injection phrasing, data-exfiltration hints) and embedded secrets. Runs for any connected server — including remote http/sse servers with no source or Docker.
trivy-mcp Aqua Security source, container_image Filesystem + CVE scan. Uses the upstream ghcr.io/aquasecurity/trivy:latest image.

See Scanner Images for the image sources and why vendor images are preferred over custom wrappers.

Custom / out-of-tree scanners

Custom scanners can be added by pushing a Docker image that implements the plugin contract (see the Plugin Interface section below) and registering it via the REST API:

curl -X POST http://localhost:8080/api/v1/security/scanners/my-custom-scanner/enable \
  -H "X-API-Key: $API_KEY"

Remote scanner registries (not just the bundled one) are planned but currently opt-in via the security.scanner_registry_url config field.

CLI commands

The complete CLI reference is in docs/cli/security-commands.md. At a glance:

# Scanner lifecycle
mcpproxy security scanners                           # list with status
mcpproxy security enable <scanner-id>                # pull image
mcpproxy security disable <scanner-id>               # remove image
mcpproxy security configure <id> --env KEY=VALUE     # set env vars (repeatable)

# Scan operations
mcpproxy security scan <server>                      # blocking, with live progress
mcpproxy security scan <server> --async              # return job id
mcpproxy security scan <server> --dry-run            # print plan, no containers
mcpproxy security scan --all                         # batch scan, progress table
mcpproxy security scan <server> --scanners a,b,c     # subset of scanners
mcpproxy security rescan <server>                    # alias for scan
mcpproxy security status <server>                    # per-scanner detail
mcpproxy security report <server> [-o json|yaml|sarif]
mcpproxy security cancel-all                         # cancel batch in progress

# Approval workflow
mcpproxy security approve <server> [--force]         # unquarantine + index
mcpproxy security reject <server>                    # delete artifacts, keep quarantined
mcpproxy security integrity <server>                 # verify against baseline

# Dashboard
mcpproxy security overview

All subcommands support -o json, -o yaml, and --json (shorthand) for scripting.

REST API

Scanner management

Method Endpoint Description
GET /api/v1/security/scanners List all scanners from the registry
GET /api/v1/security/scanners/{id}/status Per-scanner detail
POST /api/v1/security/scanners/{id}/enable Enable (pull image)
POST /api/v1/security/scanners/{id}/disable Disable (remove image)
PUT /api/v1/security/scanners/{id}/config Set env vars (JSON body {"env": {"KEY": "VALUE"}})
POST /api/v1/security/scanners/install Legacy install endpoint — prefer the per-scanner enable
DELETE /api/v1/security/scanners/{id} Legacy remove endpoint — prefer disable

Scan operations

Method Endpoint Description
POST /api/v1/servers/{name}/scan Start a scan (body: {"scanners": [...], "dry_run": false})
GET /api/v1/servers/{name}/scan/status Current/latest scan job with per-scanner statuses
GET /api/v1/servers/{name}/scan/report Aggregated report (add ?include_sarif=true for raw SARIF)
GET /api/v1/servers/{name}/scan/files Source-resolution preview (source_method, path, file list)
POST /api/v1/servers/{name}/scan/cancel Cancel the current scan
GET /api/v1/security/scans Scan history across all servers
GET /api/v1/security/scans/{jobId}/report Fetch a specific scan's aggregated report
POST /api/v1/security/scan-all Kick off a batch scan of all servers
GET /api/v1/security/queue Batch scan progress
POST /api/v1/security/cancel-all Cancel the current batch

Approval workflow

Method Endpoint Description
POST /api/v1/servers/{name}/security/approve Approve: saves integrity baseline + unquarantines + indexes tools. Body {"force": true} bypasses the critical-findings guard.
POST /api/v1/servers/{name}/security/reject Reject: deletes scan artifacts, keeps server quarantined.
GET /api/v1/servers/{name}/integrity Integrity check against the approved baseline.

Dashboard

Method Endpoint Description
GET /api/v1/security/overview Totals: scanners installed, total scans, findings by severity, Docker availability, last scan time

SSE events

Emitted on the /events SSE stream:

Event Payload fields
security.scan_started server_name, scanners[], job_id
security.scan_progress server_name, scanner_id, status, progress
security.scan_completed server_name, findings_summary
security.scan_failed server_name, scanner_id, error
security.integrity_alert server_name, alert_type, action

Plugin interface

Every scanner plugin is a Docker image with the following contract:

Mount / path Direction Description
/scan/source read-only The resolved source directory for the server under scan
/scan/report read-write The scanner must emit results.sarif here
/scan/source/tools.json read-only The server's exported MCP tool schemas ([{ "name": ..., "description": ..., "inputSchema": {...} }, ...]). Written by mcpproxy before starting the scanner container.

Scanners communicate results via SARIF 2.1.0. Exit code 0 indicates "scan completed (with or without findings)". Non-zero exit codes are surfaced as scanner failed in the aggregated report.

Source resolution order

1. Docker extraction (for Docker-isolated servers)
2. Package cache (npx/uvx/pipx/bunx) — PREFERRED for package runners
3. WorkingDir (from server config)
4. Arg-scan fallback — accepts only directories containing source markers
5. Tool definitions only — last resort (HTTP / SSE / unresolvable)

The resolved method and path are recorded on the scan job and visible via both the text and JSON report. See Security Commands → scan for more.

SARIF normalization

Scanners produce results in SARIF 2.1.0 format. MCPProxy normalizes findings to a consistent severity vocabulary:

SARIF level MCPProxy severity
error high
warning medium
note low
none info

critical severity is reserved for findings explicitly marked critical in the scanner's SARIF properties.severity field or via a rule-level override. Critical findings block security approve unless --force is supplied.

MCPProxy also augments each finding with a user-facing threat_type (tool_poisoning / prompt_injection / rug_pull / supply_chain / malicious_code / uncategorized) and threat_level (dangerous / warning / info) so the Web UI can group findings in a way that's meaningful to MCP server trust decisions.

Configuration

{
  "security": {
    "auto_scan_quarantined": false,
    "scan_timeout_default": "60s",
    "integrity_check_interval": "1h",
    "integrity_check_on_restart": false,
    "scanner_registry_url": "",
    "runtime_read_only": false,
    "runtime_tmpfs_size": "100M"
  }
}
Setting Default Description
auto_scan_quarantined false Auto-scan newly quarantined servers as soon as they're added.
scan_timeout_default 60s Per-scanner timeout. The blocking CLI security scan computes a hard ceiling as scan_timeout_default × scanner_count + 30s, clamped between 15 and 30 minutes.
integrity_check_interval 1h Periodic integrity check interval (when running as a daemon with integrity checks enabled).
integrity_check_on_restart false Re-verify integrity baseline every time an approved server is restarted.
scanner_registry_url "" Remote scanner registry URL (opt-in). When empty, only the bundled registry is used.
runtime_read_only false Run approved server containers with --read-only and a tmpfs overlay. (P2 feature, requires Docker isolation.)
runtime_tmpfs_size 100M Tmpfs size for read-only runtime containers.

Environment variables

Variable Effect
MCPPROXY_KEYRING_WRITE Set to 1, true, or yes to opt in to writing secrets to the OS keyring on macOS. Default (empty) routes writes to the in-config fallback. Linux and Windows default to enabled.

Data storage

Scanner data is stored in the BBolt database (~/.mcpproxy/config.db) in these buckets:

Bucket Content
security_scanners Installed scanner configurations (including configured_env)
security_scan_jobs Scan job records and per-scanner statuses
security_reports Aggregated reports, per-scanner SARIF payloads, normalized findings
integrity_baselines Per-server approval records (image digest, scan report IDs, approved-by)

Web UI

The Security page at /security in the Web UI mirrors the CLI and provides:

  • Dashboard stats — scanners installed, total scans, findings by severity, Docker availability, Last scan timestamp.
  • Scanner table — every scanner in the registry with its current status, vendor link, and configure button.
  • Scan All Servers — batch scan trigger with live progress.
  • Scan report viewer at /security/scans/{jobId} — risk-score badge, finding detail cards with rule/location/scanner, per-scanner execution logs including stderr from failed scanners, and scan context (source method + path + file count).
  • Approve Server / Force Approve / Reject — scanner-gated approval dialog that requires a completed scan (or explicit force) before calling the approval endpoint.

Known limitations

  • Ramparts on arm64 macOS — the upstream scanner image ships a binary linked against a newer GLIBC than the image base and fails every run on arm64. Track the scanner-ramparts image rebuild for a fix. Other 6 of 7 scanners work out of the box on arm64 macOS.
  • Cisco scanner output has a hardcoded server_url header in its stdout (https://mcp.deepwiki.com/mcp). This is cosmetic and does not affect findings. Since #383, mcpproxy strips this line from the user-visible execution log and replaces it with an annotation explaining no network request was made.
  • Pass 2 (supply-chain audit) currently requires Docker isolation to be enabled, otherwise it fails source resolution. The UI doesn't yet surface this precondition.

Related reading