Doramagic Project Pack · Human Manual
decoy-scan
Related topics: Installation and Quick Start, Security Checks and Detection
Overview
Related topics: Installation and Quick Start, Security Checks and Detection
Continue reading this section for the full explanation and source context.
Continue reading this section for the full explanation and source context.
Continue reading this section for the full explanation and source context.
Continue reading this section for the full explanation and source context.
Related Pages
Related topics: Installation and Quick Start, Security Checks and Detection
Overview
decoy-scan is a command-line security scanner for the MCP (Model Context Protocol) ecosystem. It identifies security risks in MCP server configurations, detects prompt injection attacks, analyzes dangerous tool permissions, and maps findings to the OWASP Agentic Top 10 security framework. The tool operates with zero dependencies, requiring only Node.js 18+, and needs no installation, account, or configuration to run.
Sources: README.md:1
Purpose and Scope
The primary purpose of decoy-scan is to proactively discover security vulnerabilities in MCP server configurations before attackers can exploit them. The tool addresses a critical gap in the MCP supply chain by providing automated security scanning that was previously unavailable to developers and security teams.
Key objectives:
- Scan local MCP client configurations (Claude Desktop, Cursor, Windsurf, VS Code, Claude Code, Zed, Cline)
- Classify tool risk levels based on name patterns and description analysis
- Detect prompt injection attacks hidden within tool descriptions
- Identify environment variable exposure and credential leakage
- Analyze toxic data flows across server boundaries
- Provide machine-readable output (JSON, SARIF) for CI/CD integration
Sources: README.md:1-5
Architecture Overview
decoy-scan follows a modular architecture where a single ES module (index.mjs) contains all core functionality without external dependencies.
graph TD
A[User runs decoy-scan] --> B[Discover MCP Configs]
B --> C[Parse Server Configurations]
C --> D[Probe Servers via stdio]
D --> E{Analysis Engine}
E --> F[Tool Risk Classification]
E --> G[Poisoning Detection]
E --> H[Command Analysis]
E --> I[Env Exposure Check]
E --> J[Readiness Analysis]
E --> K[Advisory Cross-Reference]
F --> L[Aggregate Results]
G --> L
H --> L
I --> L
J --> L
K --> L
L --> M[Output Formatter]
M --> N[Pretty Print / JSON / SARIF]The scan orchestrator (scan()) coordinates all analysis modules and produces structured output. Each module operates independently, allowing the tool to continue analysis even if individual checks fail.
Sources: CONTRIBUTING.md:14-29
Core Security Checks
decoy-scan performs multiple simultaneous security checks across different attack vectors:
| Check Category | What it Detects |
|---|---|
| Tool Risk Classification | Critical/high/medium/low tools by name and description |
| Prompt Injection Detection | 37 patterns across 20 attack categories in tool descriptions |
| Toxic Flow Analysis | Cross-server data leak (TF001) and destructive (TF002) attack chains |
| Tool Manifest Hashing | Tool additions, removals, and description changes between scans |
| Skill Scanning | Prompt injection, hardcoded secrets, suspicious URLs in Claude Code skills |
| Server Command Analysis | Pipe-to-shell, inline code, typosquatting, temp directory spawning |
| Environment Variable Exposure | API keys, tokens, secrets, cloud credentials passed to servers |
| Supply Chain Advisories | 40+ known vulnerable MCP packages via Decoy advisory database |
| Transport Security | HTTP without TLS, missing auth, wildcard CORS, public-bound SSE |
| Input Sanitization | Unconstrained parameters, missing maxLength, open schemas |
| Permission Scope | Over-privileged servers, dangerous capability combinations |
| OWASP Mapping | Every finding mapped to ASI01–ASI05 |
Sources: README.md:58-70
Tool Risk Tiers
Tools are classified into four risk tiers based on their potential impact:
| Tier | Risk Level | Description | Examples |
|---|---|---|---|
| Critical | Can execute code, modify data, or cause irreversible changes | execute_command, write_file, delete_database | |
| High | Can read files, make network requests, or access sensitive data | read_file, fetch_url, get_credentials | |
| Medium | Moderate scope with limited blast radius | list_directory, search_logs | |
| Low | Minimal risk, read-only or sandboxed operations | ping, get_status |
Sources: AGENTS.md:30-40
Poisoning Pattern Categories
The scanner detects 37 distinct prompt injection patterns organized into 20 attack categories:
graph LR
A[Tool Description] --> B[Poisoning Detection Engine]
B --> C[Instruction Override]
B --> D[Concealment]
B --> E[Data Exfiltration]
B --> F[Credential Harvesting]
B --> G[Coercive Execution]
B --> H[Tool Shadowing]
B --> I[Evasion Techniques]
C --> J[Findings with Severity]
D --> J
E --> J
F --> J
G --> J
H --> J
I --> JSources: CONTRIBUTING.md:30-45
Supported MCP Hosts
decoy-scan automatically discovers and scans MCP configurations across multiple clients. Config paths are platform-aware for macOS, Windows, and Linux.
| Host | Platform Support | Config Location |
|---|---|---|
| Claude Desktop | macOS, Windows, Linux | Platform-specific config directory |
| Cursor | macOS, Windows, Linux | Platform-specific config directory |
| Windsurf | macOS, Windows, Linux | Platform-specific config directory |
| VS Code | macOS, Windows, Linux | Platform-specific config directory |
| Claude Code | macOS, Windows, Linux | Platform-specific config directory |
| Zed | macOS, Windows, Linux | Platform-specific config directory |
| Cline | macOS, Windows, Linux | Platform-specific config directory |
The tool also supports project-level .mcp.json configuration files when run from a project root.
Sources: AGENTS.md:85-93
Output Formats
decoy-scan provides multiple output formats to support different use cases:
Pretty Print (Default)
Human-readable output with colored severity badges and visual hierarchy:
▸ Discovering MCP servers…
▸ Running 12 checks…
✗ server-name 2 critical
Critical tools: execute_command, write_file
✓ another-server passed
3 issues found · 2 critical, 1 high · 12 checks passed · 2.3s
JSON Output
Machine-readable format with full structural data:
{
"timestamp": "ISO-8601",
"hosts": ["Claude Desktop", "Cursor"],
"servers": [{
"name": "server-name",
"hosts": ["Claude Desktop"],
"command": "npx",
"args": ["@modelcontextprotocol/server-filesystem"],
"tools": [{
"name": "read_file",
"description": "...",
"risk": "high",
"poisoning": [{ "type": "...", "severity": "...", "description": "..." }]
}],
"risk": "high",
"error": null,
"findings": [{
"type": "env-exposure",
"severity": "high",
"description": "...",
"source": "env-config"
}]
}],
"summary": {
"total": 2,
"critical": 1,
"high": 1,
"medium": 0,
"low": 0,
"poisoned": 0
}
}
SARIF Output
Standard format for CI/CD integration with GitHub Security tab:
{
"$schema": "https://docs.oasis-open.org/sarif/sarif/v2.1.0/sarif-v2.1.0.json",
"version": "2.1.0",
"runs": [{
"tool": { "driver": { "name": "decoy-scan", "version": "0.7.0" } },
"results": [...]
}]
}
Brief Output
Minimal summary for agent consumption:
{
"servers": 3,
"critical": 1,
"high": 2,
"medium": 4,
"low": 5,
"poisoned": 0,
"status": "fail",
"exitCode": 2
}
Sources: AGENTS.md:65-84
Exit Codes
The tool uses standardized exit codes for programmatic integration:
| Exit Code | Meaning | Triggers |
|---|---|---|
| 0 | No critical or high-risk issues | Clean scan |
| 1 | High-risk issues found | High-risk tools or findings |
| 2 | Critical issues, tool poisoning, toxic flows, or policy violation | Critical tools, prompt injection detected, or policy failure |
The exit code is also surfaced as exitCode on --json and --brief output for agent branching without re-deriving severity from summary counts.
Sources: AGENTS.md:75-80
Command-Line Interface
Basic Usage
npx decoy-scan # Full scan
npx decoy-scan --json # Machine-readable output
npx decoy-scan --sarif # SARIF 2.1.0 for CI/CD
npx decoy-scan --verbose # Show all tools including low-risk
npx decoy-scan --brief # Minimal summary
Explain Subcommand
For resolving what a scan finding means without parsing full scan output:
decoy-scan explain critical # Severity tier
decoy-scan explain tool-description # Finding category
decoy-scan explain prompt-override # Poisoning type
decoy-scan explain read_file # Tool name
decoy-scan explain list # Enumerate all explainable targets
decoy-scan explain <target> --json # Structured output
Global Flags
| Flag | Short | Description |
|---|---|---|
--verbose | -v | Show all tools including low-risk |
--quiet | -q | Suppress status output |
--version | -V | Print version |
--help | -h | Print help |
Sources: AGENTS.md:10-28
GitHub Action Integration
The official GitHub Action enables automated scanning on push and pull request events:
name: MCP Security
on: [push, pull_request]
jobs:
scan:
runs-on: ubuntu-latest
permissions:
security-events: write
steps:
- uses: actions/checkout@v4
- uses: decoy-run/decoy-scan@v1
Action Inputs
| Input | Default | Description |
|---|---|---|
policy | no-critical,no-poisoning | Comma-separated policy rules |
sarif | true | Upload SARIF to GitHub Security tab |
report | false | Upload to Decoy Guard dashboard |
token | — | Decoy API token (for report) |
verbose | false | Show all tools including low-risk |
Policy Rules
no-critical Fail on critical tools (code exec, file write)
no-high Fail on high-risk tools (file read, network)
no-poisoning Fail on prompt injection in tool descriptions
no-toxic-flows Fail on cross-server data leak / destructive chains
no-secrets Fail on secrets exposed in MCP config
require-tripwires Fail if decoy-tripwire not installed
max-critical=N Fail if more than N critical tools found
Sources: README.md:88-108
Library API
decoy-scan can be imported as a module for programmatic use:
import {
scan,
toSarif,
classifyTool,
detectPoisoning,
analyzeToxicFlows,
hashToolManifest,
detectManifestChanges,
discoverSkills,
analyzeSkill,
} from 'decoy-scan';
const results = await scan({ skills: true });
console.log(results.toxicFlows); // [{ id: "TF001", severity: "critical", roles: {...} }]
console.log(results.skills); // [{ name: "...", findings: [...] }]
console.log(results.servers[0].manifestHash); // "45c4c571f03c78a2"
Sources: README.md:55-63
Design Principles
The project adheres to strict architectural constraints that differentiate it from similar tools:
| Principle | Implementation |
|---|---|
| Zero dependencies | Node.js builtins only. No npm packages. |
| No build step | Raw ES modules. No TypeScript, no bundler. |
| Fast execution | Scan completes in seconds. Servers timeout aggressively. |
| Safe operation | Read-only scanning. Never modifies configs. Kills spawned servers promptly. |
| Agent-first | JSON and SARIF output are machine-parseable. AGENTS.md is comprehensive. |
These principles ensure the tool remains reliable, auditable, and easy to deploy across different environments.
Sources: CONTRIBUTING.md:90-98
Version History
| Version | Release Date | Key Additions |
|---|---|---|
| 0.7.0 | 2026-05-10 | v2 telemetry envelope, retry + persistent queue, first-run dashboard link |
| 0.6.2 | 2026-05-10 | Fixed telemetry for empty config scenarios |
| 0.5.8 | 2026-05-06 | GitHub star ask |
| 0.5.7 | 2026-04-28 | Fixed dashboard links for token setup |
| 0.5.6 | 2026-04-28 | Exit code in JSON output, --brief implies --json |
| 0.5.5 | 2026-04-25 | Pretty CLI output overhaul, fixed code-execution tool classification |
| 0.5.4 | 2026-04-25 | Fixed explain --json second payload bug |
| 0.5.0 | 2026-04-21 | Added explain subcommand |
| 0.2.0 | 2026-03-20 | SSE transport security, input sanitization, dynamic tripwire detection |
| 0.1.0 | 2026-03-15 | Initial release |
Sources: CHANGELOG.md:1-30
Comparison with Similar Tools
| Feature | decoy-scan | Snyk agent-scan |
|---|---|---|
| Language | JavaScript | Python |
| Dependencies | 0 | 15 (aiohttp, pydantic, mcp, etc.) |
| Install | npx decoy-scan | uvx snyk-agent-scan |
| MCP Hosts | 7 (Claude Desktop, Cursor, Windsurf, VS Code, Claude Code, Zed, Cline) | Varies |
| OWASP Mapping | ASI01–ASI05 | Limited |
Sources: README.md:64-67
Sources: README.md:1
Installation and Quick Start
Related topics: Overview, CLI Reference
Continue reading this section for the full explanation and source context.
Continue reading this section for the full explanation and source context.
Continue reading this section for the full explanation and source context.
Continue reading this section for the full explanation and source context.
Related Pages
Related topics: Overview, CLI Reference
Installation and Quick Start
Overview
decoy-scan is a zero-dependency MCP (Model Context Protocol) supply chain security scanner. It requires no installation, no configuration, and no account to begin scanning. Users can run it directly via npx or clone the repository for development purposes.
The tool scans local MCP client configurations across seven supported hosts, analyzes server commands for security risks, detects prompt injection in tool descriptions, and provides structured output for CI/CD integration.
Sources: README.md:1-5
System Requirements
| Requirement | Specification |
|---|---|
| Runtime | Node.js 18+ |
| Package Manager | Not required |
| Build Tools | Not required |
| OS Support | macOS, Windows, Linux |
The tool uses only Node.js built-in modules. No external npm packages are installed or required.
Sources: CONTRIBUTING.md:10
Installation Methods
Method 1: Direct Execution (Recommended)
The fastest way to run decoy-scan is through npx, which downloads and executes the package without affecting local dependencies:
npx decoy-scan
This single command discovers all MCP configurations on the machine, probes configured servers, and produces a security report.
Sources: README.md:14
Method 2: GitHub Action (CI/CD)
For automated security scanning in repositories, use the official GitHub Action:
name: MCP Security
on: [push, pull_request]
jobs:
scan:
runs-on: ubuntu-latest
permissions:
security-events: write
steps:
- uses: actions/checkout@v4
- uses: decoy-run/decoy-scan@v1
The action supports configurable policy enforcement and SARIF output uploads to the GitHub Security tab.
Sources: action.yml:1-20
Method 3: Local Clone (Development)
For contributing or modifying the scanner:
git clone https://github.com/decoy-run/decoy-scan
cd decoy-scan
node bin/cli.mjs --help
No build step is required. The codebase uses raw ES modules with no bundler or TypeScript compilation.
Sources: CONTRIBUTING.md:5-9
Quick Start Workflow
graph TD
A[Run npx decoy-scan] --> B{Node.js installed?}
B -->|No| C[Install Node.js 18+]
C --> A
B -->|Yes| D[Discover MCP Configs]
D --> E[Supported Hosts Found?]
E -->|No| F[Print empty discovery message]
E -->|Yes| G[Probe MCP Servers]
G --> H[Analyze Tool Risk]
H --> I[Detect Poisoning Patterns]
I --> J[Check Environment Exposure]
J --> K[Generate Report]
K --> L{Human or CI Mode?}
L -->|Human| M[Pretty Print Output]
L -->|CI| N[JSON or SARIF Output]Supported MCP Hosts
decoy-scan automatically discovers configurations for the following MCP clients:
| Host | Platform Support |
|---|---|
| Claude Desktop | macOS, Windows, Linux |
| Cursor | macOS, Windows, Linux |
| Windsurf | macOS, Windows, Linux |
| VS Code | macOS, Windows, Linux |
| Claude Code | macOS, Windows, Linux |
| Zed | macOS, Windows, Linux |
| Cline | macOS, Windows, Linux |
Config paths are platform-aware, detecting macOS, Windows, and Linux configuration locations automatically.
Sources: AGENTS.md:45-50
Command Line Interface
Basic Usage
| Command | Description |
|---|---|
npx decoy-scan | Full scan with pretty CLI output |
npx decoy-scan --json | Machine-readable JSON output |
npx decoy-scan --sarif | SARIF 2.1.0 format for CI/CD |
Sources: AGENTS.md:6-8
Output Modes
#### Pretty Output (Default)
Human-readable format with color-coded severity badges and per-server summaries:
✗ server-name N critical
! server-name poisoned tool
✓ server-name passed
#### JSON Output
Structured machine-readable format for programmatic consumption:
{
"timestamp": "ISO-8601",
"hosts": ["Claude Desktop", "Cursor"],
"servers": [{
"name": "server-name",
"tools": [{
"name": "read_file",
"risk": "high",
"poisoning": []
}],
"risk": "high"
}],
"summary": {
"total": 2,
"critical": 1,
"high": 2
}
}
#### SARIF Output
Standardized format for integration with security tools and GitHub Security tab:
npx decoy-scan --sarif | jq
Sources: AGENTS.md:52-85
Common Flags
| Flag | Short | Description |
|---|---|---|
--json | — | Machine-readable JSON output |
--sarif | — | SARIF 2.1.0 output format |
--brief | — | Minimal summary (implies --json) |
--verbose | -v | Show all tools including low-risk |
--quiet | -q | Suppress status output |
--no-probe | — | Config-only scan, skip server probing |
--no-advisories | — | Skip network calls to advisory database |
--help | -h | Print help message |
--version | -V | Print version |
Sources: AGENTS.md:22-32
Exit Codes
The CLI returns exit codes for programmatic error handling:
| Code | Meaning |
|---|---|
0 | No critical or high-risk issues |
1 | High-risk issues found |
2 | Critical issues, tool poisoning, toxic flows, or policy violation |
The exitCode field is also surfaced in --json and --brief output for agent consumption.
Sources: AGENTS.md:35-45
GitHub Action Configuration
Action Inputs
| Input | Default | Description |
|---|---|---|
policy | no-critical,no-poisoning | Comma-separated policy rules |
sarif | true | Upload SARIF to GitHub Security tab |
report | false | Upload to Decoy Guard dashboard |
token | — | Decoy API token (required for report) |
verbose | false | Show all tools including low-risk |
Policy Rules
no-critical Fail on critical tools (code exec, file write)
no-high Fail on high-risk tools (file read, network)
no-poisoning Fail on prompt injection in tool descriptions
no-toxic-flows Fail on cross-server data leak / destructive chains
no-secrets Fail on secrets exposed in MCP config
require-tripwires Fail if decoy-tripwire not installed
max-critical=N Fail if more than N critical tools found
Full Example
name: MCP Security
on: [push, pull_request]
jobs:
scan:
runs-on: ubuntu-latest
permissions:
security-events: write
steps:
- uses: actions/checkout@v4
- uses: decoy-run/decoy-scan@v1
with:
policy: no-critical,no-poisoning,no-toxic-flows
sarif: true
verbose: true
Sources: action.yml:1-30
Running Tests
Before submitting changes, run the full test suite to ensure all 48 tests pass:
npm test
Tests cover CLI output, JSON/SARIF structure, policy gates, toxic flow detection, skill analysis, and manifest hashing.
For manual testing with different output modes:
| Command | Purpose |
|---|---|
node bin/cli.mjs --no-probe | Config-only scan |
node bin/cli.mjs --no-advisories | Skip network calls |
node bin/cli.mjs --json | Verify JSON structure |
node bin/cli.mjs --sarif | Verify SARIF structure |
node bin/cli.mjs --verbose | Show everything |
Sources: CONTRIBUTING.md:68-80
Project Structure
decoy-scan/
├── bin/
│ └── cli.mjs # CLI entry point
├── index.mjs # Core scanner logic
├── package.json # Package metadata
└── *.test.mjs # Test files
All scanner logic lives in index.mjs including:
| Section | Function |
|---|---|
RISK_PATTERNS + classifyTool() | Tool risk classification |
POISONING_PATTERNS + detectPoisoning() | Prompt injection detection |
analyzeServerCommand() | Server spawn command analysis |
SENSITIVE_ENV_PATTERNS + analyzeEnvExposure() | Environment variable exposure |
analyzeReadiness() | Production readiness heuristics |
OWASP_MAP + mapToOwasp() | OWASP Agentic Top 10 mapping |
HOST_CONFIGS + discoverConfigs() | MCP client config discovery |
probeServer() | MCP stdio probing |
scan() | Full scan orchestrator |
toSarif() | SARIF output generator |
Sources: CONTRIBUTING.md:14-32
Design Principles
The installation and runtime model follows these principles:
- Zero dependencies — Only Node.js built-ins are used. No npm packages added.
- No build step — Raw ES modules executed directly.
- Fast execution — Servers are probed with aggressive timeouts.
- Read-only scanning — Configs are never modified; spawned servers are killed promptly.
- Agent-first output — JSON and SARIF formats are machine-parseable.
Sources: CONTRIBUTING.md:82-88
Next Steps
After installation, explore these topics:
- Explain Command — Resolve finding types using
decoy-scan explain <target> - Output Formats — Understand JSON Schema and SARIF Schema
- What It Checks — Review the complete security checks list
- Contributing — Read CONTRIBUTING.md for development guidelines
Sources: README.md:1-5
System Architecture
Related topics: Core Modules Reference, Security Checks and Detection
Continue reading this section for the full explanation and source context.
Continue reading this section for the full explanation and source context.
Continue reading this section for the full explanation and source context.
Continue reading this section for the full explanation and source context.
Related Pages
Related topics: Core Modules Reference, Security Checks and Detection
System Architecture
Overview
decoy-scan is a zero-dependency MCP (Model Context Protocol) supply chain security scanner built with Node.js >= 18. The architecture follows a modular design where a single index.mjs file contains all core analysis logic, while bin/cli.mjs provides the command-line interface. The tool discovers MCP server configurations from supported hosts, probes servers via stdio, and performs multi-layered security analysis.
Sources: CONTRIBUTING.md
Architecture Principles
The system is built on four core principles:
| Principle | Description |
|---|---|
| Zero Dependencies | Node.js builtins only; no npm packages |
| No Build Step | Raw ES modules; no TypeScript or bundler |
| Fast Execution | Aggressive server timeouts; scan completes in seconds |
| Read-Only | Never modifies configs; only reads and analyzes |
Sources: CONTRIBUTING.md
High-Level System Flow
graph TD
A[User invokes decoy-scan] --> B[Discover MCP Configs]
B --> C{Hosts Found?}
C -->|Yes| D[For each server]
C -->|No| E[Log telemetry, exit]
D --> F[Probe Server via stdio]
F --> G[Analyze Tool List]
G --> H{Analysis Results}
H --> I[Security Findings]
H --> J[Readiness Issues]
I --> K[Generate Output]
J --> K
K --> L{Output Format}
L -->|JSON| M[JSON to stdout]
L -->|SARIF| N[SARIF to stdout]
L -->|Pretty| O[Terminal formatting]
K --> P[Send telemetry]
P --> Q[Exit with code]Core Components
1. Configuration Discovery (`discoverConfigs`)
The discovery module locates MCP server configurations across supported host applications. Configuration paths are platform-aware, supporting macOS, Windows, and Linux.
#### Supported Hosts
| Host | Platform Support |
|---|---|
| Claude Desktop | macOS, Windows, Linux |
| Cursor | macOS, Windows, Linux |
| Windsurf | macOS, Windows, Linux |
| VS Code | macOS, Windows, Linux |
| Claude Code | macOS, Windows, Linux |
| Zed | macOS, Windows, Linux |
| Cline | macOS, Windows, Linux |
Sources: AGENTS.md
#### Host Configuration Structure
"Claude Desktop": () => {
const p = platform();
if (p === "darwin") return join(homedir(), "path", "to", "config.json");
if (p === "win32") return join(process.env.APPDATA || "", "path", "config.json");
return join(homedir(), ".config", "path", "config.json");
}
Sources: CONTRIBUTING.md
2. Server Probing (`probeServer`)
The probing component spawns each MCP server via stdio protocol and queries its tool list. Servers are spawned with aggressive timeouts to ensure fast scanning.
#### Probe Behavior
- Spawns server process with configured command and arguments
- Sends
initializeandtools/listrequests via stdio - Captures tool definitions including name, description, and input schemas
- Kills spawned servers promptly after receiving response
- Records probe errors for failed servers
Sources: AGENTS.md
3. Security Analysis Engine
The analysis engine performs multi-layered security checks on discovered tools and server configurations.
#### 3.1 Tool Risk Classification (classifyTool)
Classifies every tool into risk tiers based on name patterns and description analysis:
| Risk Level | Description | Examples |
|---|---|---|
| Critical | Can execute code, modify data, cause irreversible changes | execute_command, write_file, eval_code |
| High | File read, network access, credential exposure | read_file, fetch, run_sql |
| Medium | Environment access, configuration changes | get_env, set_config |
| Low | Read-only, informational | list_files, get_time |
#### Risk Pattern Matching
RISK_PATTERNS = {
critical: [
/^execute[_-]?(command|shell|code|script)$/i,
/^run[_-]?(script|code|js|javascript|python|sql)$/i,
/^eval[_-]?(script|code)$/i,
/^evaluate[_-]?(script|code)$/i,
// ... more patterns
],
high: [
/^read[_-]?(file|dir|directory)$/i,
/^fetch[_-]?(url|http|https)?$/i,
// ... more patterns
]
}
The classifier also uses substring fallback on lowercased names for tools without descriptions.
Sources: CONTRIBUTING.md
#### 3.2 Prompt Injection Detection (detectPoisoning)
Detects 37 regex patterns across 20 attack categories in tool descriptions:
| Category | Description |
|---|---|
| Instruction Override | Tools that override system instructions |
| Concealment | Hidden or disguised malicious intent |
| Data Exfiltration | Credential or data stealing patterns |
| Credential Harvesting | Requests for sensitive credentials |
| Coercive Execution | Forced execution patterns |
| Tool Shadowing | Impersonation of legitimate tools |
| Evasion Techniques | Patterns to bypass detection |
Each pattern includes:
pattern: Regex to matchtype: Finding category (used for OWASP mapping)severity: critical, high, medium, or lowdescription: Human-readable explanation
Sources: CONTRIBUTING.md
#### 3.3 Server Command Analysis (analyzeServerCommand)
Checks spawn commands for security issues:
| Check | What it detects | |
|---|---|---|
| Pipe-to-shell | Commands using ` | ` operators |
| Temp directories | Spawning from /tmp or similar | |
| Inline code | Commands with embedded scripts | |
| Typosquatting | Similar names to popular packages | |
| Network tools | Suspicious network utilities |
#### 3.4 Environment Variable Analysis (analyzeEnvExposure)
Flags 12 categories of sensitive credentials passed to MCP servers:
| Category | Examples |
|---|---|
| API Keys | OPENAI_API_KEY, ANTHROPIC_API_KEY |
| Tokens | GITHUB_TOKEN, AWS_TOKEN |
| Passwords | DB_PASSWORD, SERVICE_PASSWORD |
| Database URLs | Connection strings with credentials |
| Cloud Credentials | AWS_SECRET, GCP_TOKEN |
#### 3.5 Production Readiness (analyzeReadiness)
Checks for deployment readiness issues:
- Missing tool descriptions
- Missing input schemas
- No required field validation
- Overloaded tool scope
- Destructive tools without safety hints
#### 3.6 OWASP Mapping (mapToOwasp)
Maps every finding to the OWASP Agentic Top 10 categories (ASI01–ASI05):
| OWASP ID | Category |
|---|---|
| ASI01 | Agentic Access Control |
| ASI02 | Excessive Agency |
| ASI03 | hallucinations |
| ASI04 | Data Leakage |
| ASI05 | Overreliance |
Sources: README.md
4. Scan Orchestration (`scan`)
The main orchestrator combines all analysis components:
async function scan({ skills = false } = {}) {
// 1. Discover MCP configs from all hosts
// 2. For each server, probe via stdio
// 3. Run all analysis functions
// 4. Collect findings
// 5. Generate output based on format
return {
servers: [...],
toxicFlows: [...],
skills: [...],
summary: {...}
};
}
Sources: AGENTS.md
5. Output Generation
#### SARIF Output (toSarif)
Generates SARIF 2.1.0 compliant output for CI/CD integration:
{
"$schema": "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json",
"version": "2.1.0",
"runs": [{
"results": [...],
"tool": { "driver": { "name": "decoy-scan", "version": "..." }}
}]
}
#### JSON Output Schema
{
"timestamp": "ISO-8601",
"hosts": ["Claude Desktop", "Cursor"],
"servers": [{
"name": "server-name",
"hosts": ["Claude Desktop"],
"command": "npx",
"args": ["@modelcontextprotocol/server-filesystem"],
"tools": [{
"name": "read_file",
"description": "...",
"risk": "high",
"poisoning": [{ "type": "...", "severity": "...", "description": "..." }]
}],
"risk": "high",
"error": null,
"findings": [{ "type": "env-exposure", "severity": "high", "description": "..." }]
}],
"summary": { "total": 2, "critical": 1, "high": 1 }
}
#### Brief Output Schema
{
"servers": 3,
"critical": 1,
"high": 2,
"medium": 4,
"low": 5,
"poisoned": 0,
"status": "fail",
"exitCode": 2
}
Sources: AGENTS.md
6. Telemetry System
The telemetry module (v2 envelope) collects anonymized usage data:
{
schema_version: "2",
event_id: "uuid",
run_id: "uuid",
ts: "ISO-8601",
env: {
node: "v20.x.x",
platform: "darwin",
arch: "x64",
ci: false,
host: "claude-desktop",
locale: "en-US"
}
}
#### Telemetry Features
| Feature | Description |
|---|---|
| Retry Logic | 1 retry with 200→800ms backoff |
| Persistent Queue | ~/.decoy/telemetry-queue.jsonl (FIFO, 1000 event cap) |
| Opt-out | DECOY_TELEMETRY=0 or --no-telemetry flag |
| First-run Notice | Cached at ~/.decoy/telemetry-notice-shown |
Sources: CHANGELOG.md
CLI Architecture
graph TD
A[CLI Entry: bin/cli.mjs] --> B[Parse Arguments]
B --> C{Command?}
C -->|explain| D[Explain Handler]
C -->|scan| E[Scan Handler]
C -->|login| F[Auth Handler]
D --> G[Resolve against RISK_PATTERNS]
E --> H[Initialize scan options]
F --> I[Open browser to auth]
H --> J[Call scan from index.mjs]
J --> K[Format output]
K --> L{Format?}
L -->|json| M[JSON.stringify]
L -->|sarif| N[toSarif]
L -->|pretty| O[ANSI colors]
L -->|brief| P[Summary object]
M --> Q[Send telemetry]
N --> Q
O --> Q
P --> Q
Q --> R[Exit with code]CLI Options
| Flag | Description |
|---|---|
--json | Machine-readable JSON output |
--sarif | SARIF 2.1.0 for CI/CD |
--brief | Minimal summary object |
--verbose, -v | Show all tools including low-risk |
--quiet, -q | Suppress status output |
--no-probe | Config-only scan (skip stdio) |
--no-advisories | Skip network calls |
--explain <target> | Explain severity/category/tool |
--version, -V | Print version |
--help, -h | Print help |
Sources: AGENTS.md
Exit Codes
| Code | Meaning |
|---|---|
0 | No critical or high-risk issues |
1 | High-risk issues found |
2 | Critical issues, tool poisoning, toxic flows, or policy violation |
Module Dependency Graph
graph LR
A[bin/cli.mjs] --> B[index.mjs]
B --> C[RISK_PATTERNS]
B --> D[POISONING_PATTERNS]
B --> E[HOST_CONFIGS]
B --> F[SENSITIVE_ENV_PATTERNS]
B --> G[OWASP_MAP]
C --> H[classifyTool]
D --> I[detectPoisoning]
E --> J[discoverConfigs]
F --> K[analyzeEnvExposure]
G --> L[mapToOwasp]
H --> M[scan]
I --> M
J --> M
K --> M
L --> M
M --> N[toSarif]
M --> O[JSON Output]
M --> P[analyzeReadiness]
M --> Q[analyzeServerCommand]Library API
The module can be imported and used programmatically:
import {
scan,
toSarif,
classifyTool,
detectPoisoning,
analyzeToxicFlows,
hashToolManifest,
detectManifestChanges,
discoverSkills,
analyzeSkill,
} from 'decoy-scan';
const results = await scan({ skills: true });
console.log(results.toxicFlows); // [{ id: "TF001", severity: "critical", roles: {...} }]
console.log(results.skills); // [{ name: "...", findings: [...] }]
console.log(results.servers[0].manifestHash); // "45c4c571f03c78a2"
Sources: AGENTS.md
Additional Analysis Features
Toxic Flow Analysis
Detects cross-server data leak (TF001) and destructive (TF002) attack chains:
results.toxicFlows = [
{ id: "TF001", severity: "critical", roles: {...} },
{ id: "TF002", severity: "high", roles: {...} }
];
Skill Scanning
Analyzes Claude Code skills for:
- Prompt injection in skill definitions
- Hardcoded secrets
- Suspicious URLs
Manifest Hashing
Tracks tool additions, removals, and description changes between scans:
results.servers[0].manifestHash // "45c4c571f03c78a2"
Supply Chain Advisories
Cross-references against Decoy advisory database covering 40+ known vulnerable MCP packages.
Sources: README.md
GitHub Action Integration
graph LR
A[GitHub Workflow] --> B[decoy-run/decoy-scan@v1]
B --> C[Scan MCP Configs]
C --> D{Policy Violation?}
D -->|Yes| E[Fail Build]
D -->|No| F[Upload SARIF]
F --> G[GitHub Security Tab]Action Inputs
| Input | Default | Description |
|---|---|---|
policy | no-critical,no-poisoning | Comma-separated policy rules |
sarif | true | Upload SARIF to GitHub Security tab |
report | false | Upload to Decoy Guard dashboard |
token | — | Decoy API token (for report) |
verbose | false | Show all tools including low-risk |
Policy Rules
no-critical Fail on critical tools (code exec, file write)
no-high Fail on high-risk tools (file read, network)
no-poisoning Fail on prompt injection in tool descriptions
no-toxic-flows Fail on cross-server data leak / destructive chains
no-secrets Fail on secrets exposed in MCP config
require-tripwires Fail if decoy-tripwire not installed
max-critical=N Fail if more than N critical tools
Sources: README.md
Sources: CONTRIBUTING.md
Core Modules Reference
Related topics: System Architecture, Security Checks and Detection
Continue reading this section for the full explanation and source context.
Continue reading this section for the full explanation and source context.
Continue reading this section for the full explanation and source context.
Continue reading this section for the full explanation and source context.
Related Pages
Related topics: System Architecture, Security Checks and Detection
Core Modules Reference
This reference documents the core modules that power decoy-scan's MCP security scanning engine. The tool uses a modular architecture with each library file handling a specific aspect of security analysis, from pattern matching to OWASP compliance mapping.
Architecture Overview
graph TD
A[CLI Entry] --> B[scan orchestrator]
B --> C[analyzers.mjs]
B --> D[patterns.mjs]
B --> E[tier.mjs]
B --> F[owasp.mjs]
B --> G[verify.mjs]
C --> H[Tool Risk Classification]
C --> I[Poisoning Detection]
C --> J[Env Exposure Analysis]
C --> K[Readiness Heuristics]
H --> L[JSON/SARIF Output]
I --> L
J --> L
K --> LModule Responsibilities
| Module | Primary Role | Key Exports |
|---|---|---|
analyzers.mjs | Security analysis engine | analyzeServerCommand, analyzeEnvExposure, analyzeReadiness, probeServer |
patterns.mjs | Pattern definitions | RISK_PATTERNS, POISONING_PATTERNS, SENSITIVE_ENV_PATTERNS |
tier.mjs | Risk tier classification | classifyTool, severity tiers |
owasp.mjs | OWASP mapping | OWASP_MAP, mapToOwasp |
verify.mjs | Policy verification | Security policy enforcement |
Risk Tier Classification (`tier.mjs`)
The tier.mjs module implements the classifyTool() function that evaluates MCP tools against predefined risk patterns. Tools are classified into four severity tiers:
graph LR
A[Tool Name + Description] --> B[classifyTool]
B --> C{pattern match}
C -->|execute*| D[Critical]
C -->|write*| D
C -->|eval*| D
C -->|read*| E[High]
C -->|fetch*| E
C -->|delete*| E
C -->|search*| F[Medium]
C -->|other| G[Low]Severity Tiers
| Tier | Description | Example Tools | Exit Code Impact |
|---|---|---|---|
| Critical | Code execution, file write, data modification | execute_command, write_file, evaluate_script | Exit code 2 |
| High | File read, network access, data deletion | read_file, fetch_url, delete_record | Exit code 1 |
| Medium | Information retrieval, search operations | search_files, list_directory | Exit code 1 |
| Low | Safe, read-only operations | get_time, ping | Exit code 0 |
Sources: CONTRIBUTING.md
Pattern Anchoring
Critical patterns use anchoring (^ and $) to ensure exact matching. The module includes patterns for:
^execute[_-]?(script|code|js|javascript|python|sql)$^evaluate[_-]?(script|code)$^run[_-]?(script|code|js|javascript|python|sql)$^eval[_-]?(script|code)$
Sources: CHANGELOG.md
Pattern Definitions (`patterns.mjs`)
The patterns.mjs module contains the security pattern definitions used across all analyzers.
Poisoning Patterns
Detects prompt injection attacks hidden in tool descriptions. The module defines 37 regex patterns across 20 attack categories:
| Category | Severity | Description |
|---|---|---|
prompt-override | Critical | Direct instruction override attempts |
instruction-hijack | Critical | Hidden system prompt modifications |
credential-harvest | Critical | Credentials or tokens in descriptions |
data-exfiltration | High | Data extraction patterns |
tool-shadowing | High | Tool name override patterns |
concealment | Medium | Hidden/obfuscated content |
coercive-execution | High | Force execution patterns |
evasion-techniques | Medium | Detection evasion attempts |
Sources: CONTRIBUTING.md, AGENTS.md
Pattern Structure
Each poisoning pattern follows this schema:
{
pattern: /regex/i, // Regex with case-insensitive flag
type: "category-name", // Finding type for OWASP mapping
severity: "critical", // critical, high, medium, low
description: "Human-readable explanation"
}
Sensitive Environment Patterns
The SENSITIVE_ENV_PATTERNS constant identifies 12 categories of sensitive credentials:
| Category | Examples |
|---|---|
| API Keys | OPENAI_API_KEY, ANTHROPIC_API_KEY, GITHUB_TOKEN |
| Database | DATABASE_URL, DB_PASSWORD, REDIS_URL |
| Cloud | AWS_SECRET_KEY, AZURE_KEY, GCP_TOKEN |
| Auth | JWT_SECRET, SESSION_KEY, AUTH_TOKEN |
Security Analyzers (`analyzers.mjs`)
The analyzers.mjs module contains the core analysis functions that evaluate MCP servers and their tools.
Server Command Analysis
The analyzeServerCommand() function examines how MCP servers are spawned:
- Pipe-to-shell patterns (
| sh,| bash) - Temp directory spawning (
/tmp/,$TMPDIR) - Inline code execution
- Typosquatting detection
- Network tool usage
Environment Exposure Analysis
The analyzeEnvExposure() function scans environment variables passed to MCP servers, flagging:
- Exposed API keys and tokens
- Database connection strings
- Cloud service credentials
- Private authentication tokens
Production Readiness Analysis
The analyzeReadiness() function applies heuristics to evaluate production readiness:
// Readiness check pattern
if (/* condition */) {
findings.push({
type: "readiness-check-name",
severity: "medium",
description: "What's wrong and why it matters"
});
}
Checks include:
- Missing tool descriptions
- Missing input schemas
- Tools without required fields
- Overloaded tool scope
- Destructive tools without safety hints
Server Probing
The probeServer() function implements MCP stdio protocol probing:
- Spawns the server process
- Sends JSON-RPC initialize request
- Sends tools/list request
- Parses and returns tool manifest
- Terminates server process
OWASP Mapping (`owasp.mjs`)
The owasp.mjs module maps all findings to the OWASP Agentic Top 10 for 2026.
OWASP Categories
| Code | Category | Description |
|---|---|---|
| ASI01 | Agentic Access Control | Over-privileged agent permissions |
| ASI02 | Tool Poisoning | Prompt injection in tools |
| ASI03 | Data Exfiltration | Cross-server data leaks |
| ASI04 | Unbounded Tool Execution | Tools without safeguards |
| ASI05 | Supply Chain | Vulnerable dependencies |
Sources: README.md
Mapping Function
The mapToOwasp() function converts internal finding types to OWASP categories:
// After adding a pattern, add its type to OWASP_MAP
ASI02: ["prompt-override", "instruction-hijack", "credential-harvest"]
Policy Verification (`verify.mjs`)
The verify.mjs module enforces security policies defined via CLI flags.
Policy Rules
| Rule | Action |
|---|---|
no-critical | Fail if critical tools found |
no-high | Fail if high-risk tools found |
no-poisoning | Fail if prompt injection detected |
no-toxic-flows | Fail on cross-server attack chains |
no-secrets | Fail on exposed secrets |
require-tripwires | Fail if decoy-tripwire not installed |
max-critical=N | Limit critical tool count |
Verification Flow
graph TD
A[Scan Results] --> B[verify.mjs]
B --> C{Policy Check}
C -->|no-critical| D{critical count > 0?}
C -->|no-poisoning| E{poisoning detected?}
C -->|no-toxic-flows| F{toxic flows found?}
D -->|Yes| G[Exit Code 2]
E -->|Yes| G
F -->|Yes| G
D -->|No| H[Continue]
E -->|No| H
F -->|No| H
H --> I[Exit Code 0 or 1]Integration Flow
sequenceDiagram
participant CLI
participant scan
participant analyzers
participant patterns
participant owasp
participant verify
participant output
CLI->>scan: scan({ options })
scan->>analyzers: discoverConfigs()
analyzers-->>scan: server configs
scan->>analyzers: probeServer(server)
analyzers-->>scan: tool manifest
scan->>patterns: detectPoisoning(tools)
patterns-->>scan: poisoning findings
scan->>patterns: classifyTool(tool)
patterns-->>scan: risk tier
scan->>analyzers: analyzeEnvExposure()
analyzers-->>scan: env findings
scan->>owasp: mapToOwasp(findings)
owasp-->>scan: OWASP mappings
scan->>verify: checkPolicy(results)
verify-->>CLI: exitCode
scan->>output: toSarif(results)
output-->>CLI: SARIF reportExport Summary
The library can be imported directly:
import {
scan,
toSarif,
classifyTool,
detectPoisoning,
analyzeToxicFlows,
hashToolManifest,
detectManifestChanges,
discoverSkills,
analyzeSkill,
} from 'decoy-scan';
const results = await scan({ skills: true });
Key exports include:
scan()— Full scan orchestratortoSarif()— SARIF 2.1.0 output generatorclassifyTool()— Tool risk classificationdetectPoisoning()— Prompt injection detectionanalyzeToxicFlows()— Cross-server attack chain analysishashToolManifest()— Tool manifest hashingdetectManifestChanges()— Change tracking between scans
Sources: README.md
Sources: CONTRIBUTING.md
Security Checks and Detection
Related topics: Supply Chain and Advisory Database, Skill Scanning, Output Formats and Policy Configuration
Continue reading this section for the full explanation and source context.
Continue reading this section for the full explanation and source context.
Continue reading this section for the full explanation and source context.
Continue reading this section for the full explanation and source context.
Related Pages
Related topics: Supply Chain and Advisory Database, Skill Scanning, Output Formats and Policy Configuration
Security Checks and Detection
Overview
decoy-scan implements a comprehensive multi-layered security detection system for MCP (Model Context Protocol) servers. The scanner analyzes MCP client configurations, probes running servers, and evaluates tools against various threat categories including prompt injection, credential exposure, dangerous command execution, and supply chain vulnerabilities.
Sources: README.md:37-51
The detection engine operates across eight primary security dimensions, providing both static configuration analysis and dynamic runtime probing to identify risks before they can be exploited.
Architecture Overview
graph TD
A[MCP Client Configs] --> B[Config Discovery]
B --> C[Server Command Analysis]
B --> D[Environment Variable Analysis]
C --> E[Server Probing]
E --> F[Tool Risk Classification]
E --> G[Poisoning Detection]
E --> H[Readiness Checks]
F --> I[Toxic Flow Analysis]
G --> I
H --> I
I --> J[OWASP Mapping]
J --> K[SARIF/JSON Output]
D --> KDetection Layers
1. Tool Risk Classification
The scanner classifies every discovered tool into severity tiers based on name patterns and description analysis. Risk levels follow a four-tier system:
| Tier | Risk Level | Description | Exit Code Impact |
|---|---|---|---|
| Critical | Critical | Can execute code, modify data, or cause irreversible changes | Exit code 2 |
| High | High | File system access, network operations | Exit code 1 |
| Medium | Medium | Information disclosure potential | Exit code 0 |
| Low | Low | Minimal risk, read-only operations | Exit code 0 |
Sources: AGENTS.md:85-90
#### Classification Mechanism
Tool classification uses the classifyTool() function which applies regex pattern matching against both tool names and descriptions. The RISK_PATTERNS object defines critical patterns including:
execute_command,run_shell,bash,exec— command executionwrite_file,create_file,update_file— file modificationdelete_file,remove_file— destructive operationsevaluate_script,execute_script,run_javascript,run_python,run_sql— code evaluation variantsspawn,fork,child_process— process spawning
Sources: CONTRIBUTING.md:15-16
The substring fallback mechanism matches against lowercased tool names, ensuring risky verbs like evaluate, spawn, and fetch classify correctly even when no description is provided.
2. Prompt Injection Detection
Prompt injection detection identifies malicious content hidden within tool descriptions. The system uses 37 regex patterns across 20 attack categories:
| Category | Severity | Description |
|---|---|---|
| instruction-override | Critical | Overrides agent instructions |
| role-assumption | Critical | Impersonates system roles |
| concealed-commands | High | Hidden command instructions |
| privilege-escalation | High | Attempts to gain elevated access |
| context-manipulation | Medium | Manipulates conversation context |
| data-exfiltration | High | Extracts sensitive information |
| credential-harvesting | Critical | Collects authentication credentials |
| coercion | High | Forces specific behaviors |
| tool-shadowing | Critical | Masks legitimate tool behavior |
Sources: README.md:37-40
The detectPoisoning() function scans tool descriptions against POISONING_PATTERNS, identifying injection attempts that could compromise agent behavior.
#### Poisoning Pattern Structure
Each pattern in POISONING_PATTERNS follows this schema:
{
pattern: /regex/i, // Match criteria
type: "category-name", // Finding type for OWASP mapping
severity: "critical", // critical, high, medium, low
description: "Human-readable" // Display message
}
Sources: CONTRIBUTING.md:33-40
3. Server Command Analysis
The analyzeServerCommand() function examines how MCP servers are spawned, detecting suspicious invocation patterns:
- Pipe-to-shell patterns — Commands using
|to pipe output to shell interpreters - Temp directory spawning — Servers running from
/tmpor similar writable locations - Inline code execution — Commands with embedded code strings
- Typosquatting detection — Package names similar to legitimate tools
- Network tool usage — Presence of
curl,wget, or other network utilities
This analysis operates on configuration data without requiring server execution.
4. Environment Variable Exposure Detection
The analyzeEnvExposure() function identifies sensitive environment variables being passed to MCP servers. It checks against SENSITIVE_ENV_PATTERNS covering 12 categories:
| Category | Examples |
|---|---|
| API Keys | OPENAI_API_KEY, ANTHROPIC_API_KEY, AWS_ACCESS_KEY_ID |
| Tokens | GITHUB_TOKEN, GITLAB_TOKEN, SLACK_TOKEN |
| Database Credentials | DB_PASSWORD, POSTGRES_PASSWORD, MONGO_URI |
| Cloud Credentials | AWS_SECRET_ACCESS_KEY, AZURE_CLIENT_SECRET |
| Private Keys | SSH_PRIVATE_KEY, GPG_KEY |
Sources: CONTRIBUTING.md:21-22
5. Production Readiness Checks
The analyzeReadiness() function applies heuristics to evaluate production readiness:
| Check | Severity | Description |
|---|---|---|
| Missing descriptions | Medium | Tools without documentation |
| Missing schemas | Medium | Tools without input validation schemas |
| No required fields | Medium | Unvalidated parameter acceptance |
| Overloaded scope | Medium | Tools performing multiple unrelated operations |
| Destructive tools without safety hints | Low | Dangerous operations lacking warnings |
Sources: CONTRIBUTING.md:23-24
6. Toxic Flow Analysis
Toxic flow detection identifies dangerous cross-server data leakage patterns. Two primary flow types are detected:
| Flow ID | Severity | Description |
|---|---|---|
| TF001 | Critical | Cross-server data leak — data read by one server flows to another |
| TF002 | Critical | Destructive attack chain — combined operations cause irreversible damage |
Sources: README.md:43-44
The analyzeToxicFlows() function examines the interaction patterns between multiple MCP servers to identify these attack vectors.
7. Tool Manifest Hashing
Manifest hashing tracks changes in the tool list exposed by MCP servers:
const results = await scan({ skills: true });
console.log(results.servers[0].manifestHash); // "45c4c571f03c78a2"
The hashToolManifest() and detectManifestChanges() functions detect:
- Tool additions (potential malicious injection)
- Tool removals (potential functionality loss)
- Description changes (potential poisoning updates)
Sources: AGENTS.md:101-105
8. Skill Scanning
For Claude Code environments, the scanner performs additional analysis on skills:
- Prompt injection detection in skill prompts
- Hardcoded secret detection
- Suspicious URL identification
The discoverSkills() and analyzeSkill() functions implement this analysis.
OWASP Agentic Top 10 Mapping
All findings are mapped to the OWASP Agentic Top 10 for Agentic Applications using the mapToOwasp() function with OWASP_MAP. The mapping covers ASI01 through ASI05 categories:
| OWASP Code | Category | Mapped From |
|---|---|---|
| ASI01 | Sensitive Action Without Confirmation | Critical tool findings |
| ASI02 | Tool Poisoning | Poisoning pattern matches |
| ASI03 | Over-Privileged Tool Scope | Readiness check failures |
| ASI04 | Sandbox Escape | Command execution patterns |
| ASI05 | Context Length Exhaustion | Heavy tool descriptions |
Sources: README.md:50
Scan Orchestration
The scan() function orchestrates all security checks in the following sequence:
graph LR
A[Discover Hosts] --> B[Find Server Configs]
B --> C[Analyze Commands]
C --> D[Probe Servers]
D --> E[Classify Tools]
E --> F[Detect Poisoning]
F --> G[Check Readiness]
G --> H[Analyze Flows]
H --> I[Map to OWASP]
I --> J[Generate Output]Output Formats
SARIF Output
The toSarif() function generates SARIF 2.1.0 format output suitable for CI/CD integration and GitHub Security tab uploads.
JSON Output Schema
{
"timestamp": "ISO-8601",
"hosts": ["Claude Desktop", "Cursor"],
"servers": [{
"name": "server-name",
"hosts": ["Claude Desktop"],
"command": "npx",
"args": ["@modelcontextprotocol/server-filesystem"],
"tools": [{
"name": "read_file",
"description": "...",
"risk": "high",
"poisoning": [{ "type": "...", "severity": "...", "description": "..." }]
}],
"risk": "high",
"findings": [{
"type": "env-exposure",
"severity": "high",
"description": "..."
}]
}],
"summary": {
"total": 2, "critical": 1, "high": 1
}
}
Sources: AGENTS.md:53-71
Exit Codes
| Code | Meaning | Condition |
|---|---|---|
| 0 | Clean | No critical or high-risk issues |
| 1 | Warning | High-risk issues found |
| 2 | Failure | Critical issues, tool poisoning, or toxic flows |
The exitCode field is also surfaced in JSON and --brief output for programmatic consumption.
Policy Enforcement
The --policy flag enables CI/CD policy gates:
| Rule | Behavior |
|---|---|
no-critical | Fail on critical tools |
no-high | Fail on high-risk tools |
no-poisoning | Fail on prompt injection |
no-toxic-flows | Fail on cross-server leaks |
no-secrets | Fail on exposed secrets |
require-tripwires | Fail if decoy-tripwire not installed |
Sources: README.md:66-77
Explain Functionality
The explain subcommand provides context for findings without running a full scan:
decoy-scan explain critical # Severity tier explanation
decoy-scan explain tool-description # Finding category details
decoy-scan explain prompt-override # Poisoning type explanation
decoy-scan explain evaluate_script # Tool classification reasoning
Explanations resolve against the same patterns used by the scanner, ensuring consistency between detection and documentation.
CLI Integration
The GitHub Action integration provides automated security scanning:
- uses: decoy-run/decoy-scan@v1
with:
policy: no-critical,no-poisoning,no-toxic-flows
sarif: true
Results are uploaded to the GitHub Security tab via the SARIF format.
Summary Table of Detection Capabilities
| Detection Type | Function | Patterns/Checks | Exit Code |
|---|---|---|---|
| Tool Risk | classifyTool() | Name + description matching | 0/1/2 |
| Poisoning | detectPoisoning() | 37 regex patterns | 2 |
| Command | analyzeServerCommand() | 5 pattern categories | 1/2 |
| Env Exposure | analyzeEnvExposure() | 12 credential categories | 1/2 |
| Readiness | analyzeReadiness() | 5 heuristic checks | 0/1 |
| Toxic Flows | analyzeToxicFlows() | TF001, TF002 | 2 |
| Manifest Hash | hashToolManifest() | Change detection | 1 |
| Skill Scan | analyzeSkill() | Injection + secrets | 1/2 |
Sources: index.mjs, bin/cli.mjs
Sources: README.md:37-51
Supply Chain and Advisory Database
Related topics: Security Checks and Detection, Skill Scanning
Continue reading this section for the full explanation and source context.
Continue reading this section for the full explanation and source context.
Continue reading this section for the full explanation and source context.
Continue reading this section for the full explanation and source context.
Related Pages
Related topics: Security Checks and Detection, Skill Scanning
Supply Chain and Advisory Database
Overview
The Supply Chain and Advisory Database module in decoy-scan provides security checks against a curated database of known vulnerable MCP packages. This feature enables automated cross-referencing of configured MCP servers against known supply chain threats, helping organizations identify and mitigate risks from third-party dependencies before attackers can exploit them.
The advisory system is designed to be non-intrusive and fast. It performs network lookups against the Decoy advisory database to fetch security intelligence about MCP packages, with built-in retry logic and offline fallbacks to ensure scanning reliability.
Architecture
graph TD
A[scan] --> B[Discover MCP Configs]
B --> C[For Each Server]
C --> D[Probe Server via Stdio]
D --> E[Fetch Tool List]
E --> F[Run Security Checks]
F --> G{Tool Risk Classification}
F --> H{Poisoning Detection}
F --> I{Supply Chain Advisories}
I --> J[HTTP GET /api/advisories?<packages>]
J --> K{API Available?}
K -->|Yes| L[Cache Response]
K -->|No| M[Retry 1x]
M -->|Fail| N[Fallback to Local]
L --> O[Apply Findings]
N --> OCore Components
Advisory Database Integration
The supply chain advisory system integrates with an external Decoy advisory database via HTTP API calls. When scanning MCP configurations, the system extracts package identifiers from server configurations and queries the advisory database for known vulnerabilities.
// Conceptual flow from index.mjs
const advisories = await fetchAdvisories(packageList);
Sources: index.mjs:scan()
Advisory Data Structure
Advisory records returned from the database contain the following fields:
| Field | Type | Description |
|---|---|---|
package | string | NPM package name or MCP server identifier |
severity | string | critical, high, medium, or low |
title | string | Brief description of the vulnerability |
description | string | Detailed advisory information |
cve | string | CVE identifier (if available) |
recommendation | string | Suggested remediation steps |
Network Layer
The advisory fetcher implements resilient network handling:
sequenceDiagram
participant Scanner
participant API as Decoy API
participant Cache
Scanner->>API: GET /api/advisories?packages=...
API-->>Scanner: 200 OK (advisory data)
Scanner->>Cache: Store response
Note over Scanner: 1 retry with 200-800ms backoff
Scanner->>API: GET /api/advisories?packages=...
API-->>Scanner: 5xx Error
Scanner->>API: Retry after backoff
API-->>Scanner: Still failing
Scanner->>Scanner: Fallback to cached/local dataSources: CHANGELOG.md:0.7.0
Configuration Options
CLI Flags
| Flag | Default | Description |
|---|---|---|
--no-advisories | false | Skip supply chain advisory checks |
--advisory-cache | ~/.decoy/advisory-cache.json | Local cache file path |
--api-url | https://api.decoy.run | Override advisory API endpoint |
Environment Variables
| Variable | Description |
|---|---|
DECOY_API_URL | Custom API endpoint for advisory lookups |
DECOY_API_TOKEN | Authentication token for premium advisories |
Advisory Categories
The Decoy advisory database covers multiple vulnerability categories relevant to MCP servers:
| Category | Description | Example |
|---|---|---|
| Code Execution | Vulnerabilities allowing arbitrary code execution | Malicious npm package with postinstall script |
| Data Exfiltration | Packages that leak sensitive information | Telemetry packages with credential harvesting |
| Dependency Confusion | Typosquatting or substitution attacks | mcp-server vs mc-p-server |
| Known Exploits | CVE-assigned vulnerabilities with active exploitation | Remote code execution in popular MCP packages |
Sources: README.md:What it checks
Scan Integration
Scan Categories
The supply chain advisory check is one of nine scan categories in decoy-scan:
| Check | Priority |
|---|---|
| Tool risk classification | 1 |
| Tool poisoning detection | 2 |
| Supply chain advisories | 3 |
| Server command analysis | 4 |
| Environment variable exposure | 5 |
| Production readiness | 6 |
| Toxic flow analysis | 7 |
| Manifest change tracking | 8 |
| Transport security | 9 |
Sources: AGENTS.md:Scan Categories
Integration with Tool Classification
Advisory findings are combined with tool risk classification results to produce comprehensive security reports:
// Simplified integration flow
const toolRisk = classifyTool(toolName, toolDescription);
const advisoryInfo = await lookupAdvisory(serverPackage);
const combinedRisk = mergeRiskScores(toolRisk, advisoryInfo);
Sources: index.mjs:classifyTool()
Output Integration
JSON Output
Advisory findings appear in the JSON output under each server's findings array:
{
"servers": [{
"name": "example-mcp-server",
"findings": [{
"type": "supply-chain-advisory",
"severity": "high",
"package": "@example/mcp-server",
"description": "Known vulnerability in version < 1.2.0",
"cve": "CVE-2024-1234",
"recommendation": "Upgrade to version 1.2.0 or later"
}]
}]
}
SARIF Output
Advisory findings are also exported in SARIF 2.1.0 format for CI/CD integration:
{
"results": [{
"ruleId": "decoy-advisory-HIGH-001",
"level": "warning",
"message": {
"text": "Package @example/mcp-server has known vulnerability CVE-2024-1234"
}
}]
}
Sources: README.md:Structured output for agents
Telemetry and Analytics
The supply chain advisory system includes anonymized telemetry to help improve the advisory database:
| Event | Purpose |
|---|---|
scan.discovery | Records which hosts and servers were scanned |
scan.complete | Final scan results including advisory findings |
scan.uploaded | Indicates when results were uploaded to dashboard |
Telemetry includes environment metadata (Node version, platform, architecture) but no sensitive user data. Users can opt out via DECOY_TELEMETRY=0 or the --no-telemetry flag.
Sources: CHANGELOG.md:0.7.0
Performance Considerations
Timeout Configuration
Advisory API calls use aggressive timeouts to maintain scan performance:
| Setting | Value | Rationale |
|---|---|---|
| Connection timeout | 2000ms | Fast failure on unreachable API |
| Read timeout | 5000ms | Allow for large response payloads |
| Retry attempts | 1 | Minimize latency impact |
Caching Strategy
Advisory responses are cached locally to reduce API calls:
graph LR
A[Scan Start] --> B{Cache Hit?}
B -->|Yes| C[Use Cached Data]
B -->|No| D[Query API]
D --> E[Store in Cache]
E --> C
C --> F[Continue Scan]Cache location is platform-aware:
| Platform | Cache Path |
|---|---|
| macOS | ~/.decoy/advisory-cache.json |
| Linux | ~/.decoy/advisory-cache.json |
| Windows | %APPDATA%/.decoy/advisory-cache.json |
Sources: AGENTS.md:Supported Hosts
Extensibility
Adding New Advisory Categories
To extend the advisory system with new vulnerability categories, modify the pattern definitions in the constants module:
// In lib/constants.mjs
export const ADVISORY_CATEGORIES = {
// ... existing categories
NEW_CATEGORY: {
pattern: /new-vulnerability-pattern/i,
severity: "medium",
description: "Description of new vulnerability type"
}
};
Custom Advisory Sources
Organizations can integrate private advisory databases by implementing a custom advisory fetcher:
import { createAdvisoryFetcher } from './lib/advisories.mjs';
const customFetcher = createAdvisoryFetcher({
apiUrl: 'https://internal.advisories.example.com',
apiToken: process.env.INTERNAL_ADVISORY_TOKEN
});
Sources: CONTRIBUTING.md:Code Structure
Exit Codes and Policy Enforcement
Advisory findings affect the scan exit code:
| Exit Code | Condition |
|---|---|
0 | No critical or high-risk issues, no advisories |
1 | High-risk advisories found |
2 | Critical advisories found |
Policy gates can be configured via CLI:
decoy-scan --policy no-critical,no-high
Sources: README.md:Exit codes
See Also
- CLI Reference - Full CLI documentation including advisory flags
- Contributing Guide - How to extend the advisory system
- GitHub Action - CI/CD integration with policy enforcement
Source: https://github.com/decoy-run/decoy-scan / Human Manual
Skill Scanning
Related topics: Security Checks and Detection, CLI Reference
Continue reading this section for the full explanation and source context.
Continue reading this section for the full explanation and source context.
Continue reading this section for the full explanation and source context.
Continue reading this section for the full explanation and source context.
Related Pages
Related topics: Security Checks and Detection, CLI Reference
Skill Scanning
Skill Scanning is a security analysis feature in decoy-scan that detects vulnerabilities within Claude Code skills. It scans skill definitions for prompt injection payloads, hardcoded secrets, and suspicious URLs that could compromise agent safety.
Overview
Skill Scanning operates as part of the broader MCP (Model Context Protocol) security assessment suite. While tool scanning analyzes the risk profile of MCP server tools, skill scanning focuses on identifying malicious content embedded within skill definitions that could be exploited during agent execution.
技能扫描与工具扫描的核心区别在于:工具扫描评估 MCP 服务器提供的工具能力风险,而技能扫描检查本地定义的技能文件中可能被恶意利用的内容。
Architecture
The skill scanning subsystem consists of three primary components that work together to provide comprehensive skill security analysis:
graph TD
A[Skill Definitions] --> B[discoverSkills]
B --> C{Skill Files Found?}
C -->|Yes| D[analyzeSkill]
C -->|No| E[Skip Analysis]
D --> F[Skill Findings Array]
F --> G[Integration into scan Results]
H[Poisoning Patterns] --> D
I[Secret Patterns] --> D
J[URL Patterns] --> DThe architecture follows a two-phase approach: first discovering skill definitions across the filesystem, then analyzing each discovered skill for multiple categories of security issues.
Core Functions
discoverSkills()
The discoverSkills() function searches for Claude Code skill definition files in the project directory. It recursively traverses the filesystem to locate .mdc files that contain skill definitions.
| Property | Value |
|---|---|
| Function Name | discoverSkills |
| Module | lib/skills.mjs |
| Return Type | Promise<Skill[]> |
| Side Effects | Read-only filesystem scan |
技能发现采用递归目录遍历策略,从当前工作目录开始搜索 .mdc 扩展名的文件。
analyzeSkill(skill)
The analyzeSkill() function performs deep security analysis on an individual skill definition. It evaluates the skill content against multiple pattern sets to detect various attack vectors.
分析函数对每个技能执行三类安全检查:
- 提示注入检测 (Prompt Injection Detection)
- 硬编码密钥检测 (Hardcoded Secret Detection)
- 可疑 URL 检测 (Suspicious URL Detection)
Integration with scan()
In the main scan orchestration, skills are discovered and analyzed when the skills option is enabled:
const results = await scan({ skills: true });
console.log(results.skills); // [{ name: "...", findings: [...] }]
Sources: README.md
Finding Categories
Skill scanning identifies three primary categories of security issues:
Prompt Injection Detection
Detects prompt injection payloads hidden within skill descriptions. These attacks embed malicious instructions that could override agent behavior when the skill is executed.
| Severity | Description |
|---|---|
| Critical | Active prompt override instructions |
| High | Concealment techniques hiding true intent |
| Medium | Subtle manipulation hints |
Hardcoded Secrets
Identifies API keys, tokens, passwords, and other credentials accidentally embedded in skill definitions. This finding type aligns with OWASP credential exposure categories.
Suspicious URLs
Detects references to potentially malicious or untrusted URLs within skill content. URLs to external resources could lead agents to compromised servers or phishing pages.
Output Structure
When skill scanning is enabled, the scan results include a skills array containing analysis results for each discovered skill:
{
"skills": [
{
"name": "skill-name",
"path": "/path/to/skill.mdc",
"findings": [
{
"type": "prompt-injection",
"severity": "high",
"description": "...",
"source": "skill-content"
}
]
}
]
}
The skills findings are integrated into the overall scan results alongside tool risk classifications, poisoning detections, and toxic flow analysis.
Sources: README.md
Usage
CLI Usage
Skill scanning is enabled by default when running a full scan with skill analysis:
npx decoy-scan --verbose
The --verbose flag reveals all discovered skills and their findings, including those previously hidden in standard output.
Programmatic Usage
import { discoverSkills, analyzeSkill } from 'decoy-scan';
// Discover all skills in the project
const skills = await discoverSkills();
// Analyze each skill individually
for (const skill of skills) {
const analysis = await analyzeSkill(skill);
console.log(`${skill.name}: ${analysis.findings.length} issues`);
}
Configuration
Skill scanning behavior can be controlled through scan options:
| Option | Type | Default | Description |
|---|---|---|---|
skills | boolean | true | Enable/disable skill scanning |
verbose | boolean | false | Show detailed skill findings |
OWASP Mapping
Skill findings are mapped to the OWASP Agentic Top 10 categories where applicable:
| Finding Type | OWASP Category |
|---|---|
| Prompt Injection | ASI01 - Prompt Injection |
| Hardcoded Secrets | ASI04 - Sensitive Data Disclosure |
| Suspicious URLs | ASI02 - Visualization Overflow |
The OWASP_MAP in the main scanner correlates skill finding types with their corresponding OWASP classifications for compliance reporting.
Sources: CONTRIBUTING.md
Discovery Process
The skill discovery process in lib/discovery.mjs locates Claude Code skill files through a structured search pattern:
- Directory Traversal: Recursively scan project directories
- Pattern Matching: Identify files matching skill definition patterns
- Path Resolution: Build absolute paths for discovered skills
- Metadata Extraction: Parse skill name and metadata from definitions
技能发现是只读操作,不会修改任何文件系统内容。所有发现的文件路径都被规范化为绝对路径以便后续分析。
Integration Points
Skill scanning integrates with several other decoy-scan subsystems:
- Tool Manifest Hashing: Skills may reference tools whose manifests are hashed
- SARIF Export: Skill findings appear in SARIF output under the appropriate rules
- Policy Enforcement:
no-secretspolicy rules can target skill findings - Telemetry: Scan telemetry includes skill discovery counts
Testing
The test suite validates skill scanning behavior through unit.test.mjs which covers skill analysis patterns and findings generation. All skill-related tests must pass before PR submission.
npm test # Runs 48 tests including skill scanning coverage
See Also
- Tool Risk Classification - MCP tool security analysis
- Poisoning Detection - Prompt injection pattern matching
- OWASP Mapping - Compliance categorization
Sources: README.md
CLI Reference
Related topics: Installation and Quick Start, GitHub Action Integration, Output Formats and Policy Configuration
Continue reading this section for the full explanation and source context.
Continue reading this section for the full explanation and source context.
Continue reading this section for the full explanation and source context.
Continue reading this section for the full explanation and source context.
Related Pages
Related topics: Installation and Quick Start, GitHub Action Integration, Output Formats and Policy Configuration
CLI Reference
The decoy-scan CLI is the primary interface for scanning MCP (Model Context Protocol) client configurations and servers for security vulnerabilities. Built as a zero-dependency Node.js application, it provides comprehensive security scanning without requiring installation or configuration.
Overview
The CLI serves as an MCP supply chain security scanner that discovers MCP server configurations, probes running servers, and analyzes them for security risks including tool poisoning, sensitive environment variable exposure, and production readiness issues.
Key capabilities:
- Scans 7 MCP host configurations (Claude Desktop, Cursor, Windsurf, VS Code, Claude Code, Zed, Cline)
- Classifies tools by risk level (critical/high/medium/low)
- Detects 37 prompt injection patterns across 20 attack categories
- Outputs structured formats for CI/CD integration (JSON, SARIF)
- Maps findings to OWASP Agentic Top 10
Sources: README.md
Installation & Requirements
Prerequisites
- Node.js >= 18
- No npm packages required (zero dependencies)
Running the CLI
npx decoy-scan # full scan
npx decoy-scan --json # machine-readable output
npx decoy-scan --sarif # SARIF 2.1.0 for CI/CD
No installation step required. The CLI runs directly via npx.
Sources: AGENTS.md
Command Syntax
decoy-scan [command] [options]
Global Options
| Option | Alias | Description |
|---|---|---|
--verbose | -v | Show all tools including low-risk |
--quiet | -q | Suppress status output |
--version | -V | Print version |
--help | -h | Print help |
Sources: AGENTS.md
Scan Command
The default command that discovers MCP servers, probes them, and analyzes for security issues.
Scan Options
| Option | Description |
|---|---|
--json | Output results as JSON |
--sarif | Output results as SARIF 2.1.0 |
--brief | Output minimal summary (implies --json) |
--verbose, -v | Show all tools including low-risk |
--quiet, -q | Suppress status output |
--no-probe | Skip server probing (config-only scan) |
--no-advisories | Skip supply chain advisory checks |
--skills | Enable skill scanning |
--report | Upload results to Decoy Guard dashboard |
--no-telemetry | Disable telemetry collection |
--verify | AI-verify findings (requires token) |
Sources: README.md
Example Usage
# Full scan with pretty output
decoy-scan
# JSON output for scripting
decoy-scan --json
# SARIF output for GitHub Security tab
decoy-scan --sarif
# Config-only scan (no server probing)
decoy-scan --no-probe
# Skip network calls (faster, local only)
decoy-scan --no-advisories
# Verbose mode showing all tools
decoy-scan --verbose
Sources: CONTRIBUTING.md
Explain Subcommand
Resolves what a scan finding means without parsing the full scan output. Useful when an agent sees a finding and needs structured context to act on it.
decoy-scan explain critical # severity tier
decoy-scan explain tool-description # finding category
decoy-scan explain prompt-override # poisoning type
decoy-scan explain read_file # tool name (runs real classifier rules)
decoy-scan explain list # enumerate all explainable targets
decoy-scan explain <target> --json # structured output (preferred for agents)
Explain Target Types
| Kind | Description | Example |
|---|---|---|
tier | Severity levels | critical, high, medium, low |
category | Finding categories | env-exposure, command-analysis |
poisoning | Attack types | prompt-override, instruction-override |
tool | Tool risk classifications | execute_command, read_file |
Sources: AGENTS.md
JSON Output Schema
{
"tool": "decoy-scan",
"version": "0.5.1",
"target": "critical",
"result": {
"kind": "tier",
"key": "critical",
"title": "Critical",
"summary": "Can execute code, modify data, or cause irreversible changes.",
"body": "...",
"examples": ["execute_command", "write_file", "..."],
"advice": "..."
}
}
For tool results, additional fields include risk, reason, matched (the regex that matched), and note when classification relied on name alone.
Sources: AGENTS.md
Exit Codes
The CLI returns specific exit codes for CI/CD pipeline integration:
| Exit Code | Meaning |
|---|---|
0 | No critical or high-risk issues found |
1 | High-risk issues found |
2 | Critical issues, tool poisoning, toxic flows, or policy violation |
The exit code is also surfaced as exitCode on --json and --brief output, enabling agents to branch on severity without re-deriving it from summary counts.
Sources: AGENTS.md
Output Formats
Pretty Output
Default terminal output with color-coded severity badges:
✗ server-name N critical
! server-name poisoned tool (magenta)
? server-name probe failed
✓ server-name passed
Severity labels introduce each tool group; Low severity collapses to a count.
Sources: CHANGELOG.md
JSON Output Schema
{
"timestamp": "ISO-8601",
"hosts": ["Claude Desktop", "Cursor"],
"servers": [{
"name": "server-name",
"hosts": ["Claude Desktop"],
"command": "npx",
"args": ["@modelcontextprotocol/server-filesystem"],
"tools": [{
"name": "read_file",
"description": "...",
"risk": "high",
"poisoning": [{ "type": "...", "severity": "...", "description": "..." }]
}],
"risk": "high",
"error": null,
"findings": [{
"type": "env-exposure",
"severity": "high",
"description": "...",
"source": "env-config"
}]
}],
"summary": {
"total": 2,
"critical": 1,
"high": 1,
"medium": 0,
"low": 0,
"poisoned": 0
},
"exitCode": 2
}
Sources: AGENTS.md
SARIF Output
SARIF 2.1.0 format for integration with GitHub Security tab:
decoy-scan --sarif
The SARIF output includes all findings, rules, and tool information compatible with GitHub's code scanning API.
Sources: README.md
Brief Output
Minimal summary object (implies --json):
{
"servers": 3,
"critical": 1,
"high": 2,
"medium": 4,
"low": 5,
"poisoned": 0,
"status": "fail",
"exitCode": 2
}
Fields:
servers— number of non-decoy, non-error servers scannedcritical,high,medium,low— tool risk countspoisoned— number of tool poisoning findingsstatus—"pass"(clean),"warn"(high-risk), or"fail"(critical/poisoned/toxic flows)exitCode— matches process exit code
Sources: AGENTS.md
Environment Variables
| Variable | Description |
|---|---|
DECOY_TOKEN | API token for dashboard upload |
DECOY_TELEMETRY=0 | Disable telemetry collection |
Sources: CHANGELOG.md
GitHub Action Integration
The CLI integrates with GitHub Actions via the official action:
# .github/workflows/mcp-security.yml
name: MCP Security
on: [push, pull_request]
jobs:
scan:
runs-on: ubuntu-latest
permissions:
security-events: write
steps:
- uses: actions/checkout@v4
- uses: decoy-run/decoy-scan@v1
with:
policy: no-critical,no-poisoning,no-toxic-flows
sarif: true
report: true
token: ${{ secrets.DECOY_TOKEN }}
Action Inputs
| Input | Default | Description |
|---|---|---|
policy | no-critical,no-poisoning | Comma-separated policy rules |
sarif | true | Upload SARIF to GitHub Security tab |
report | false | Upload to Decoy Guard dashboard |
token | — | Decoy API token (for report) |
verbose | false | Show all tools including low-risk |
Policy Rules
no-critical Fail on critical tools (code exec, file write)
no-high Fail on high-risk tools (file read, network)
no-poisoning Fail on prompt injection in tool descriptions
no-toxic-flows Fail on cross-server data leak / destructive chains
no-secrets Fail on secrets exposed in MCP config
require-tripwires Fail if decoy-tripwire not installed
max-critical=N Fail if more than N critical tools found
Sources: README.md
Scan Architecture
graph TD
A[decoy-scan CLI] --> B[Discover MCP Configs]
B --> C{Host Configs}
C -->|Claude Desktop| D[~/.claude...json]
C -->|Cursor| E[~/.cursor...json]
C -->|VS Code| F[.vscode/mcp.json]
C -->|Zed| G[~/.config/zed...]
C -->|Cline| H[~/.cline...]
D --> I[Parse Server Configs]
E --> I
F --> I
G --> I
H --> I
I --> J[For Each Server]
J --> K[Probe Server via stdio]
K --> L{Probe Success?}
L -->|No| M[Log Error & Continue]
L -->|Yes| N[Analyze Tool List]
N --> O[Tool Risk Classification]
N --> P[Poisoning Detection]
N --> Q[Command Analysis]
N --> R[Env Exposure Check]
O --> S[Aggregate Findings]
P --> S
Q --> S
R --> S
S --> T{Output Format}
T -->|Pretty| U[Terminal Output]
T -->|JSON| V[JSON to stdout]
T -->|SARIF| W[SARIF to stdout]
M --> X[Final Summary]
U --> X
V --> X
W --> X
X --> Y[Exit Code 0/1/2]Development
For local development and testing:
git clone https://github.com/decoy-run/decoy-scan
cd decoy-scan
node bin/cli.mjs --help
No build step required. No dependencies to install.
Manual Testing Modes
node bin/cli.mjs --no-probe # Config-only
node bin/cli.mjs --no-advisories # Skip network calls
node bin/cli.mjs --json # Verify JSON structure
node bin/cli.mjs --sarif # Verify SARIF structure
node bin/cli.mjs --verbose # Show everything
Running Tests
npm test
This runs 48 tests covering CLI output, JSON/SARIF structure, policy gates, toxic flow detection, skill analysis, and manifest hashing.
Sources: CONTRIBUTING.md
Supported Hosts
The CLI automatically discovers MCP configurations across multiple platforms:
| Host | macOS Path | Windows Path | Linux Path |
|---|---|---|---|
| Claude Desktop | ~/Library/Application Support/Claude | %APPDATA%/Claude | ~/.config/Claude |
| Cursor | ~/.cursor | %APPDATA%/Cursor | ~/.cursor |
| Windsurf | ~/.windsurf | %APPDATA%/Windsurf | ~/.windsurf |
| VS Code | .vscode/mcp.json (workspace) | .vscode/mcp.json | .vscode/mcp.json |
| Claude Code | ~/.claude.json | %APPDATA%/claude.json | ~/.claude.json |
| Zed | ~/.config/zed | %APPDATA%/Zed | ~/.config/zed |
| Cline | ~/.cline | %APPDATA%/cline | ~/.cline |
Config paths are platform-aware and detected automatically.
Sources: AGENTS.md
Scan Categories
| Check | What it finds |
|---|---|
| Tool risk classification | Critical/high/medium/low tools by name + description |
| Prompt injection detection | 37 patterns across 20 attack categories in tool descriptions |
| Toxic flow analysis | Cross-server data leak (TF001) and destructive (TF002) attack chains |
| Tool manifest hashing | Tool additions, removals, and description changes between scans |
| Skill scanning | Prompt injection, hardcoded secrets, suspicious URLs in Claude Code skills |
| Server command analysis | Pipe-to-shell, inline code, typosquatting, temp directory spawning |
| Environment variable exposure | API keys, tokens, secrets, cloud credentials passed to servers |
| Supply chain advisories | 40+ known vulnerable MCP packages via Decoy advisory database |
| Transport security | HTTP without TLS, missing auth, wildcard CORS, public-bound SSE |
| Input sanitization | Unconstrained parameters, missing maxLength, open schemas |
| Permission scope | Over-privileged servers, dangerous capability combinations |
| OWASP mapping | Every finding mapped to ASI01–ASI05 |
Sources: README.md
Sources: README.md
GitHub Action Integration
Related topics: CLI Reference, Output Formats and Policy Configuration
Continue reading this section for the full explanation and source context.
Continue reading this section for the full explanation and source context.
Continue reading this section for the full explanation and source context.
Continue reading this section for the full explanation and source context.
Related Pages
Related topics: CLI Reference, Output Formats and Policy Configuration
GitHub Action Integration
Overview
The decoy-scan GitHub Action provides automated MCP (Model Context Protocol) security scanning as part of a CI/CD pipeline. It integrates directly with GitHub's security infrastructure, enabling teams to enforce security policies on every push and pull request without manual intervention.
The action discovers MCP server configurations across multiple hosts (Claude Desktop, Cursor, Windsurf, VS Code, Claude Code, Zed, and Cline), executes the security scanner, and uploads results to the GitHub Security tab via SARIF format.
Sources: README.md:72-89
Architecture
graph TD
A[GitHub Workflow Trigger] --> B[decoy-run/decoy-scan Action]
B --> C[Discover MCP Configs]
B --> D[CLI: npx decoy-scan]
D --> E[Scan MCP Servers]
D --> F[Policy Gate Check]
E --> G{Policy Violated?}
F --> G
G -->|No| H[Exit Code 0]
G -->|Yes| I[Exit Code 1/2]
E --> J[Generate SARIF Output]
J --> K[github/codeql-action/upload-sarif]
K --> L[GitHub Security Tab]
I --> M[Fail Build]The action consists of two primary steps: a scan step that executes the decoy-scan CLI and a SARIF upload step that publishes results to GitHub Security.
Sources: action.yml:20-45
Action Inputs
| Input | Default | Required | Description |
|---|---|---|---|
policy | no-critical,no-poisoning | No | Comma-separated policy rules that determine build failure conditions |
sarif | true | No | Whether to upload SARIF results to GitHub Security tab |
report | false | No | Whether to upload results to Decoy Guard dashboard |
token | — | Conditional | Decoy API token required when report is true |
verbose | false | No | Show all tools including low-risk items in output |
Sources: README.md:80-86
Workflow Example
name: MCP Security
on: [push, pull_request]
jobs:
scan:
runs-on: ubuntu-latest
permissions:
security-events: write
steps:
- uses: actions/checkout@v4
- uses: decoy-run/decoy-scan@v1
This minimal configuration scans MCP servers on every push and pull request, uploading SARIF results to the GitHub Security tab.
Sources: README.md:72-89
Policy Rules
The policy input accepts comma-separated rules that define build failure conditions:
| Rule | Behavior |
|---|---|
no-critical | Fail on critical tools (code exec, file write) |
no-high | Fail on high-risk tools (file read, network) |
no-poisoning | Fail on prompt injection in tool descriptions |
no-toxic-flows | Fail on cross-server data leak / destructive chains |
no-secrets | Fail on secrets exposed in MCP config |
require-tripwires | Fail if decoy-tripwire not installed |
max-critical=N | Fail if more than N critical tools |
max-high=N | Fail if more than N high-risk tools |
Sources: README.md:97-106
Exit Codes
| Code | Meaning |
|---|---|
0 | No critical or high-risk issues |
1 | High-risk issues found |
2 | Critical issues, tool poisoning, toxic flows, or policy violation |
The exit code determines whether the GitHub Actions job succeeds or fails, enabling automatic policy enforcement.
Sources: README.md:43-48
SARIF Integration
How SARIF Works
SARIF (Static Analysis Results Interchange Format) is an industry-standard format for sharing static analysis results. The action generates SARIF 2.1.0 output that GitHub's code scanning feature can ingest and display.
graph LR
A[decoy-scan CLI] -->|--sarif flag| B[SARIF 2.1.0 JSON]
B --> C[github/codeql-action/upload-sarif]
C --> D[GitHub Security Tab]
C -->|continue-on-error: true| E[Non-blocking Upload]The action includes SARIF upload as a separate step with continue-on-error: true, ensuring that SARIF upload failures do not cause the workflow to fail when the scan itself passes.
Sources: action.yml:35-44
Manual SARIF Upload
For workflows requiring more control, you can run the scan manually and upload SARIF separately:
- run: npx decoy-scan --sarif > results.sarif
- uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: results.sarif
Sources: README.md:108-112
GitHub Step Summary
The action writes a summary to $GITHUB_STEP_SUMMARY providing immediate feedback within the GitHub Actions UI:
graph TD
A[Scan Complete] --> B{Exit Code}
B -->|0 - Clean| C["✅ **Clean** — no issues found"]
B -->|Non-zero| D["🚨 **Issues found** — SUMMARY"]
B -->|Non-zero| E["Run `npx decoy-scan -v` locally for full details."]Sources: action.yml:28-34
Permissions
The workflow requires the security-events: write permission to upload SARIF results to the GitHub Security tab:
permissions:
security-events: write
Sources: README.md:76-78
Advanced Configuration
Report to Decoy Dashboard
To upload results to the Decoy Guard dashboard for centralized monitoring:
- uses: decoy-run/decoy-scan@v1
with:
report: true
token: ${{ secrets.DECOY_TOKEN }}
Verbose Output
To include low-risk tools in the output for full visibility:
- uses: decoy-run/decoy-scan@v1
with:
verbose: true
Custom Policy
Combine multiple policy rules for stricter enforcement:
- uses: decoy-run/decoy-scan@v1
with:
policy: no-critical,no-poisoning,no-toxic-flows,max-critical=0
Sources: README.md:80-90
Dependencies
The decoy-scan action itself has zero dependencies at runtime. It uses Node.js built-in modules only, following the project's design principle of keeping the tool dependency-free.
The CLI is invoked via npx decoy-scan, which downloads and executes the package on-demand.
Sources: CONTRIBUTING.md:9-10
Version Pinning
For production CI/CD pipelines, pin to a major version to receive minor updates automatically:
- uses: decoy-run/decoy-scan@v1
Or pin to a specific version for complete stability:
- uses: decoy-run/[email protected]
Sources: README.md:24-26
Sources: README.md:72-89
Output Formats and Policy Configuration
Related topics: CLI Reference, GitHub Action Integration, Security Checks and Detection
Continue reading this section for the full explanation and source context.
Continue reading this section for the full explanation and source context.
Continue reading this section for the full explanation and source context.
Continue reading this section for the full explanation and source context.
Related Pages
Related topics: CLI Reference, GitHub Action Integration, Security Checks and Detection
Output Formats and Policy Configuration
Overview
decoy-scan provides multiple output formats to serve different use cases—from human-readable console output for developers to machine-parseable JSON and SARIF formats for CI/CD pipelines and security automation. The tool also supports a flexible policy configuration system that enables automated enforcement of security rules.
The output and policy system is designed with an "agent-first" philosophy: JSON and SARIF outputs are structurally complete, include exit codes for programmatic branching, and contain all metadata needed for downstream processing without requiring additional parsing or context.
Sources: AGENTS.md:1-20
Output Format Architecture
Format Types
decoy-scan supports four distinct output formats:
| Format | Flag | Primary Use Case | Exit Code Included |
|---|---|---|---|
| Pretty Console | Default | Interactive terminal inspection | No |
| JSON | --json | Scripted processing, APIs | Yes |
| SARIF 2.1.0 | --sarif | GitHub Security tab, CI tools | Yes |
| Brief | --brief | Quick summary for automation | Yes |
All structured formats (JSON, SARIF, Brief) include an exitCode field that mirrors the process exit code, enabling agents to branch on results without re-deriving severity from summary counts.
Sources: AGENTS.md:80-95
graph TD
A[decoy-scan invocation] --> B{CLI Args?}
B -->|Default| C[Pretty Console Output]
B -->|--json| D[JSON Output]
B -->|--sarif| E[SARIF 2.1.0 Output]
B -->|--brief| F[Brief Summary JSON]
B -->|combine| G[Multiple Formats]
C --> H[Terminal Display]
D --> I[Machine Processing]
E --> J[GitHub Security Tab]
F --> K[Quick Status Checks]JSON Output Format
Full Scan Schema
The JSON output provides complete scan results including all findings, server details, and summary statistics.
{
"timestamp": "ISO-8601",
"hosts": ["Claude Desktop", "Cursor"],
"servers": [{
"name": "server-name",
"hosts": ["Claude Desktop"],
"command": "npx",
"args": ["@modelcontextprotocol/server-filesystem"],
"tools": [{
"name": "read_file",
"description": "...",
"risk": "high",
"poisoning": [{ "type": "...", "severity": "...", "description": "..." }]
}],
"risk": "high",
"error": null,
"findings": [{
"type": "env-exposure",
"severity": "high",
"description": "...",
"source": "env-config"
}]
}],
"summary": {
"total": 2,
"critical": 1,
"high": 2,
"medium": 4,
"low": 5,
"poisoned": 0
},
"exitCode": 1
}
Sources: AGENTS.md:28-55
Brief Output Schema
The --brief format provides a minimal summary object optimized for quick status checks:
{
"servers": 3,
"critical": 1,
"high": 2,
"medium": 4,
"low": 5,
"poisoned": 0,
"status": "fail",
"exitCode": 2
}
| Field | Type | Description |
|---|---|---|
servers | number | Non-decoy, non-error servers scanned |
critical | number | Critical severity tool count |
high | number | High severity tool count |
medium | number | Medium severity tool count |
low | number | Low severity tool count |
poisoned | number | Prompt injection findings |
status | string | "pass", "warn", or "fail" |
exitCode | number | Process exit code (0/1/2) |
Sources: AGENTS.md:60-75
SARIF 2.1.0 Output
SARIF (Static Analysis Results Interchange Format) is generated by the toSarif() function in lib/sarif.mjs. This format is specifically designed for integration with GitHub Security tab and other security scanning platforms.
Key Features
- Rule definitions mapping to OWASP Agentic Top 10 categories (ASI01–ASI05)
- Result categorization by severity level
- Tool metadata including version and run timestamps
- Multi-host support in result locations
CLI Integration
When using the GitHub Action or CLI with SARIF output:
node bin/cli.mjs --sarif --no-advisories > scan-results.sarif
The SARIF file can then be uploaded using the GitHub CodeQL action:
- uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: ${{ steps.scan.outputs.sarif-file }}
category: decoy-scan
Sources: action.yml:40-45
SARIF Structure Overview
graph TD
A[SARIF 2.1.0 Log] --> B[runs array]
B --> C[Tool driver]
B --> D[Results array]
B --> E[Rules definitions]
D --> F[Individual findings]
E --> G[ASI01-ASI05 mappings]
F --> H[severity, message, locations]Policy Configuration System
Policy Rules
The policy system uses comma-separated rules to define pass/fail criteria:
| Rule | Effect | Example |
|---|---|---|
no-critical | Fail on critical tools (code exec, file write) | policy: no-critical |
no-high | Fail on high-risk tools (file read, network) | policy: no-high |
no-poisoning | Fail on prompt injection in tool descriptions | policy: no-poisoning |
no-toxic-flows | Fail on cross-server data leak/destructive chains | policy: no-toxic-flows |
no-secrets | Fail on secrets exposed in MCP config | policy: no-secrets |
require-tripwires | Fail if decoy-tripwire not installed | policy: require-tripwires |
max-critical=N | Fail if critical tools exceed N | policy: max-critical=0 |
Multiple rules can be combined: policy: no-critical,no-poisoning,no-toxic-flows
Sources: README.md:80-90
Policy Gates
The analyzePolicyGates() function evaluates scan results against configured policy rules. Each finding type maps to one or more policy rules:
| Finding Type | Maps to Policy Rules |
|---|---|
| Critical risk tools | no-critical, max-critical=N |
| High risk tools | no-high |
| Prompt injection | no-poisoning |
| Toxic flows (TF001, TF002) | no-toxic-flows |
| Environment exposure | no-secrets |
| Missing decoy-tripwire | require-tripwires |
Sources: index.mjs:RISK_PATTERNS,POISONING_PATTERNS
Exit Codes
The exit code system provides programmatic feedback about scan results:
| Code | Meaning | Triggers |
|---|---|---|
0 | No critical or high-risk issues | Clean scan |
1 | High-risk issues found | High-severity tools present |
2 | Critical issues, tool poisoning, toxic flows, or policy violation | Critical tools, injection detected, or policy failure |
Exit codes are included in both --json and --brief output as the exitCode field, enabling conditional logic in scripts:
const result = JSON.parse(childProcess.execSync('decoy-scan --json'));
if (result.exitCode === 2) {
process.exit(1); // Block deployment
}
Sources: AGENTS.md:75-82
Explain Subcommand
The explain subcommand provides structured explanations for severity tiers, finding categories, poisoning types, and tool names:
decoy-scan explain critical # severity tier
decoy-scan explain tool-description # finding category
decoy-scan explain prompt-override # poisoning type
decoy-scan explain read_file # tool name (runs real classifier)
decoy-scan explain list # enumerate all explainable targets
decoy-scan explain <target> --json # structured output
Explain Output Schema
{
"tool": "decoy-scan",
"version": "0.5.1",
"target": "critical",
"result": {
"kind": "tier",
"key": "critical",
"title": "Critical",
"summary": "Can execute code, modify data, or cause irreversible changes.",
"body": "Detailed explanation...",
"examples": ["execute_command", "write_file", "..."],
"advice": "Remediation guidance..."
}
}
result.kind | Description |
|---|---|
tier | Severity level (critical, high, medium, low) |
category | Finding category (env-exposure, missing-schema, etc.) |
poisoning | Poisoning type (instruction-override, credential-harvesting, etc.) |
tool | Tool name classification with risk level and matched pattern |
Sources: AGENTS.md:32-55
CLI Options Reference
| Option | Short | Description |
|---|---|---|
--json | JSON output format | |
--sarif | SARIF 2.1.0 output format | |
--brief | Brief summary (implies --json) | |
--verbose | -v | Show all tools including low-risk |
--quiet | -q | Suppress status output |
--no-probe | Config-only scan, skip server probing | |
--no-advisories | Skip network calls to advisory database | |
--no-telemetry | Opt out of telemetry | |
--policy | Comma-separated policy rules | |
--report | Upload results to Decoy Guard dashboard | |
--version | -V | Print version |
--help | -h | Print help |
Sources: AGENTS.md:15-30
Integration Patterns
CI/CD Pipeline
graph LR
A[Push/PR] --> B[Checkout]
B --> C[decoy-scan Action]
C --> D{Policy Pass?}
D -->|Yes| E[Continue Build]
D -->|No| F[Fail Build]
C --> G[Upload SARIF]
G --> H[GitHub Security Tab]Agentic Workflow
graph TD
A[Agent receives scan result] --> B{exitCode === 0?}
B -->|Yes| C[Proceed]
B -->|No| D{exitCode === 2?}
D -->|Yes| E[Block - Critical/Poisoning]
D -->|No| F{exitCode === 1?}
F -->|Yes| G[Warn - High-risk]
F -->|No| H[Unknown state]
B -->|Parse| I[Tool analysis]
I --> J[Explain each finding]
J --> K[Remediation]Output Stability Guarantees
decoy-scan maintains backward compatibility for structured outputs:
- JSON Schema Versioning — The
versionfield in explain output enables consumers to handle schema changes - Exit Code Stability — Exit code meanings are documented and stable across versions
- SARIF Compliance — SARIF output adheres to OASIS SARIF 2.1.0 specification
These guarantees enable reliable automation without constant schema adaptation.
Sources: AGENTS.md:1-20
Doramagic Pitfall Log
Source-linked risks stay visible on the manual page so the preview does not read like a recommendation.
The project should not be treated as fully validated until this signal is reviewed.
Users cannot judge support quality until recent activity, releases, and issue response are checked.
The project may affect permissions, credentials, data exposure, or host boundaries.
The project may affect permissions, credentials, data exposure, or host boundaries.
Doramagic Pitfall Log
Doramagic extracted 7 source-linked risk signals. Review them before installing or handing real data to the project.
1. Capability assumption: README/documentation is current enough for a first validation pass.
- Severity: medium
- Finding: README/documentation is current enough for a first validation pass.
- User impact: The project should not be treated as fully validated until this signal is reviewed.
- Recommended check: Open the linked source, confirm whether it still applies to the current version, and keep the first run isolated.
- Evidence: capability.assumptions | github_repo:1185640470 | https://github.com/decoy-run/decoy-scan | README/documentation is current enough for a first validation pass.
2. Maintenance risk: Maintainer activity is unknown
- Severity: medium
- Finding: Maintenance risk is backed by a source signal: Maintainer activity is unknown. Treat it as a review item until the current version is checked.
- User impact: Users cannot judge support quality until recent activity, releases, and issue response are checked.
- Recommended check: Open the linked source, confirm whether it still applies to the current version, and keep the first run isolated.
- Evidence: evidence.maintainer_signals | github_repo:1185640470 | https://github.com/decoy-run/decoy-scan | last_activity_observed missing
3. Security or permission risk: no_demo
- Severity: medium
- Finding: no_demo
- User impact: The project may affect permissions, credentials, data exposure, or host boundaries.
- Recommended check: Open the linked source, confirm whether it still applies to the current version, and keep the first run isolated.
- Evidence: downstream_validation.risk_items | github_repo:1185640470 | https://github.com/decoy-run/decoy-scan | no_demo; severity=medium
4. Security or permission risk: no_demo
- Severity: medium
- Finding: no_demo
- User impact: The project may affect permissions, credentials, data exposure, or host boundaries.
- Recommended check: Open the linked source, confirm whether it still applies to the current version, and keep the first run isolated.
- Evidence: risks.scoring_risks | github_repo:1185640470 | https://github.com/decoy-run/decoy-scan | no_demo; severity=medium
5. Security or permission risk: Decoy Scan - MCP Security for CI/CD
- Severity: medium
- Finding: Security or permission risk is backed by a source signal: Decoy Scan - MCP Security for CI/CD. Treat it as a review item until the current version is checked.
- User impact: The project may affect permissions, credentials, data exposure, or host boundaries.
- Recommended check: Open the linked source, confirm whether it still applies to the current version, and keep the first run isolated.
- Evidence: Source-linked evidence: https://github.com/decoy-run/decoy-scan/releases/tag/v1
6. Maintenance risk: issue_or_pr_quality=unknown
- Severity: low
- Finding: issue_or_pr_quality=unknown。
- User impact: Users cannot judge support quality until recent activity, releases, and issue response are checked.
- Recommended check: Open the linked source, confirm whether it still applies to the current version, and keep the first run isolated.
- Evidence: evidence.maintainer_signals | github_repo:1185640470 | https://github.com/decoy-run/decoy-scan | issue_or_pr_quality=unknown
7. Maintenance risk: release_recency=unknown
- Severity: low
- Finding: release_recency=unknown。
- User impact: Users cannot judge support quality until recent activity, releases, and issue response are checked.
- Recommended check: Open the linked source, confirm whether it still applies to the current version, and keep the first run isolated.
- Evidence: evidence.maintainer_signals | github_repo:1185640470 | https://github.com/decoy-run/decoy-scan | release_recency=unknown
Source: Doramagic discovery, validation, and Project Pack records
Community Discussion Evidence
These external discussion links are review inputs, not standalone proof that the project is production-ready.
Count of project-level external discussion links exposed on this manual page.
Open the linked issues or discussions before treating the pack as ready for your environment.
Community Discussion Evidence
Doramagic exposes project-level community discussion separately from official documentation. Review these links before using decoy-scan with real data or production workflows.
- Decoy Scan - MCP Security for CI/CD - github / github_release
- README/documentation is current enough for a first validation pass. - GitHub / issue
Source: Project Pack community evidence and pitfall evidence