From f33c177fc58ac7b883a4becbdba8297f0a970305 Mon Sep 17 00:00:00 2001 From: lorecraft-io Date: Sat, 4 Apr 2026 20:41:09 -0400 Subject: [PATCH] =?UTF-8?q?Expand=20/safetycheck=20from=208=20to=2020=20ch?= =?UTF-8?q?ecks=20=E2=80=94=20add=20full=20MCP=20security=20suite?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds 12 new MCP-specific security checks that auto-activate when an MCP project is detected (package.json with MCP SDK, .mcp.json, server.tool() patterns, etc). New checks cover: - Tool description integrity (prompt injection markers, file paths) - Unicode/invisible character smuggling (U+E0000-U+E007F range) - Encoded payloads in tool metadata (Base64, hex) - MCP transport security (DNS rebinding CVE-2025-66414/66416) - MCP authentication (missing auth on HTTP endpoints) - Token scope & lifecycle (over-broad scopes, plaintext tokens) - Input schema validation (missing inputSchema, unvalidated args) - Tool response sanitization (stack traces in tool results) - CORS/Origin validation (wildcard CORS on MCP endpoints) - Supply chain & config hygiene (@latest pins, .mcp.json secrets) - Audit logging (structured logging for tool invocations) - Rug-pull & tool mutation defense (floating versions, no pinning) Existing checks 1, 3, 5, 6, 8 updated with MCP subsections. Install script self-test updated to verify 20 checks. README Step 9 section updated. Co-Authored-By: claude-flow --- README.md | 30 ++- step-9/safetycheck-skill/SKILL.md | 424 +++++++++++++++++++++++++++++- step-9/step-9-install.sh | 41 ++- 3 files changed, 466 insertions(+), 29 deletions(-) diff --git a/README.md b/README.md index faa2be9..c19dcae 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ Everything you need to start working with AI-powered development tools, installe | [Step 6](#step-6---productivity-tools) | Productivity Tools | Motion Calendar + Notion (pick what you use) | ~5 min | | [Step 7](#step-7---second-brain-obsidian) | Second Brain (Obsidian) | Personal knowledge management system | ~30+ min | | [Step 8](#step-8---telegram) | Telegram | Message Claude from your phone via Telegram bot | ~2 min | -| [Step 9](#step-9---safety-check) | Safety Check | Security auditing — scan any project for vulnerabilities | ~2 min | +| [Step 9](#step-9---safety-check) | Safety Check | Security auditing — scan any project for vulnerabilities + full MCP security checks | ~2 min | | [Final Step](#final-step---status-line) | Status Line | Final config — status indicators wired up | ~2 min | | [You're Ready](#youre-ready) | **Start here after setup** | Your daily command and what to do next | | | [Video Tutorials (coming soon)](#video-tutorials-coming-soon) | Walkthroughs | Shows you exactly how to do everything, screen by screen | | @@ -1038,17 +1038,27 @@ Open a new terminal and run `ctg` to launch Claude with Telegram connected. Insi [Back to top](#quick-nav) -This step installs a security auditing skill that lets Claude scan any project for vulnerabilities. Exposed API keys, missing rate limiting, input sanitization gaps, dependency vulnerabilities, insecure configurations — the stuff that slips through code review. You point Claude at a project and tell it to run a safety check. It does the rest. +This step installs a security auditing skill that lets Claude scan any project for vulnerabilities. Exposed API keys, missing rate limiting, input sanitization gaps, dependency vulnerabilities, insecure configurations — the stuff that slips through code review. For MCP projects, it automatically activates 12 additional checks covering tool poisoning, prompt injection vectors, transport security, authentication, and supply chain attacks. You point Claude at a project and tell it to run a safety check. It does the rest. ### What It Does The `/safetycheck` skill gives Claude a structured security audit framework. Instead of asking Claude to "look for security issues" and hoping for the best, this skill runs a systematic scan across the categories that actually matter: -- **Exposed secrets.** API keys, tokens, passwords, and credentials hardcoded in source files or committed to git. +**API Security (all projects):** +- **Exposed secrets.** API keys, tokens, passwords hardcoded in source files, git history, or MCP config files. - **Missing rate limiting.** Endpoints that accept unlimited requests without throttling. -- **Input sanitization gaps.** User input that flows into queries, commands, or file paths without validation. -- **Dependency vulnerabilities.** Known CVEs in your npm, pip, or other package dependencies. -- **Insecure configurations.** CORS misconfigurations, debug mode left on in production, permissive file permissions, and more. +- **Input sanitization gaps.** User input flowing into queries, commands, file paths, or MCP tool handlers without validation. +- **Dependency vulnerabilities.** Known CVEs in npm/pip packages, including MCP SDK version checks. +- **Insecure configurations.** CORS misconfigurations, missing .gitignore entries, untracked secrets. + +**MCP Security (auto-activated for MCP projects):** +- **Tool description integrity.** Hidden instructions, file path references, and injection markers in tool descriptions. +- **Unicode smuggling.** Invisible Unicode characters used to hide malicious instructions from human reviewers. +- **MCP transport security.** DNS rebinding vulnerabilities, HTTP vs HTTPS, known CVEs (CVE-2025-66414, CVE-2025-66416). +- **MCP authentication.** Missing bearer auth on HTTP-based MCP servers. +- **Supply chain hygiene.** `@latest` floating versions, rug-pull risk, unverified packages in MCP configs. +- **Tool response sanitization.** Stack traces and raw errors leaking through tool results. +- **Audit logging.** Missing structured logging for tool invocations. This isn't a replacement for a full security audit. It's a first line of defense — the kind of check you should run before every deploy, every PR, every time you hand code off to someone else. @@ -1068,11 +1078,11 @@ Once you're inside the Claude session, paste this and hit Enter: | Component | What it does | |-----------|-------------| -| Safety Check Skill (`/safetycheck`) | A Claude Code skill that runs structured security audits on any project — exposed keys, missing rate limiting, input sanitization gaps, dependency vulnerabilities, and insecure configurations. | +| Safety Check Skill (`/safetycheck`) | A Claude Code skill that runs 8 API security checks on any project, plus 12 MCP-specific checks when an MCP project is detected. Covers tool poisoning, prompt injection vectors, DNS rebinding CVEs, supply chain attacks, and more. | ### After Step 9 -Open any project in Claude and type `/safetycheck` to run a security audit. Claude will scan the project and report what it finds, organized by severity. You can also just ask Claude to "run a safety check on this project" in plain English and the skill kicks in automatically. +Open any project in Claude and type `/safetycheck` to run a security audit. For standard projects, Claude runs 8 checks and reports findings by severity. For MCP projects, it automatically detects the project type and activates 12 additional MCP-specific checks. You can also ask Claude to "run a safety check" in plain English — the skill kicks in automatically. --- @@ -1181,10 +1191,10 @@ Run the steps in this order: | 6 | Productivity Tools | Motion Calendar + Notion (optional) | | 7 | Second Brain | Obsidian vault setup + data import (7a-7d) | | 8 | Telegram | Telegram bot setup — message Claude from your phone | -| 9 | Safety Check | Security auditing — scan projects for exposed keys, vulnerabilities, and more | +| 9 | Safety Check | Security auditing — 8 API checks + 12 MCP checks for tool poisoning, DNS rebinding, supply chain attacks | | **Final** | **Status Line** | **Final config — status indicators, system health check** | -> **Note:** Step 6 (Productivity Tools) is all optional — install only the tools you use. Step 7 (Second Brain) is the biggest step with four sub-parts (7a-7d). Step 8 (Telegram) is interactive — it walks you through creating a bot and pasting your token. Step 9 (Safety Check) installs a security auditing skill for scanning projects. The Final Step (Status Line) is the wrap-up — it wires your status indicators to show what's active across all the tools you installed. +> **Note:** Step 6 (Productivity Tools) is all optional — install only the tools you use. Step 7 (Second Brain) is the biggest step with four sub-parts (7a-7d). Step 8 (Telegram) is interactive — it walks you through creating a bot and pasting your token. Step 9 (Safety Check) installs a security auditing skill — 8 standard checks for any project, plus 12 MCP-specific checks that auto-activate when an MCP project is detected. The Final Step (Status Line) is the wrap-up — it wires your status indicators to show what's active across all the tools you installed. --- diff --git a/step-9/safetycheck-skill/SKILL.md b/step-9/safetycheck-skill/SKILL.md index 35d559e..eafc60d 100644 --- a/step-9/safetycheck-skill/SKILL.md +++ b/step-9/safetycheck-skill/SKILL.md @@ -18,7 +18,22 @@ Determine what kind of project this is by checking for: - `go.mod` → Go - If none match, treat as generic and run filesystem-level checks only -### Step 2 — Run All 8 Security Checks +**MCP Detection** — After detecting the base project type, check for MCP signals: +- `@modelcontextprotocol/sdk` or `fastmcp` in package.json dependencies +- `from mcp import` or `from mcp.server` in Python files +- `new McpServer(`, `server.tool(`, `McpServer` in JS/TS files +- `.mcp.json`, `claude_desktop_config.json`, `.cursor/mcp.json` file presence +- `@mcp.tool` decorator in Python + +If MCP detected: activate Phase 0 + Checks 9-20, AND add MCP subsections to Checks 1, 3, 5, 6, 8. + +Two scan modes: +- **MCP Server Mode** — codebase IS an MCP server (SDK imports, tool registrations found) +- **MCP Consumer Mode** — project has `.mcp.json` or `claude_desktop_config.json` config files + +A project can be both. + +### Step 2 — Run All Checks Run each check against the current working directory. Use Grep, Read, Glob, and Bash tools. Report findings in a severity-rated table at the end. @@ -50,7 +65,17 @@ git log -p --all -S "API_KEY" -S "SECRET" -S "TOKEN" -S "sk-" --max-count=30 2>/ git ls-files 2>/dev/null | grep -iE "\.env$" ``` -**Severity**: CRITICAL if real keys found in source or git history. HIGH if .env is tracked. PASS if clean. +**MCP Config scan** (if MCP detected) — Scan `.mcp.json`, `claude_desktop_config.json`, `.cursor/mcp.json` for hardcoded secrets in `env` blocks: +```bash +grep -r '"env"' .mcp.json claude_desktop_config.json .cursor/mcp.json 2>/dev/null | grep -iE '(sk-[a-zA-Z0-9]{20,}|AKIA[0-9A-Z]{16}|ghp_[a-zA-Z0-9]{36}|AIzaSy[a-zA-Z0-9_-]{30,}|xox[bpsa]-[a-zA-Z0-9-]+)' +``` + +Check if MCP configs are tracked in git: +```bash +git ls-files 2>/dev/null | grep -iE "(\.mcp\.json|claude_desktop_config\.json)" +``` + +**Severity**: CRITICAL if real keys found in source, git history, or MCP config env blocks. HIGH if .env or MCP config is tracked. PASS if clean. --- @@ -65,6 +90,8 @@ git ls-files 2>/dev/null | grep -iE "\.env$" **Detect outbound APIs** — Grep for: `fetch(`, `axios`, `http.request`, `got(` - If outbound calls exist, check for retry/backoff logic and 429 handling +Note: For MCP servers using HTTP transport, the same rate-limit checks apply to the HTTP layer. + **Severity**: HIGH if public endpoints exist without rate limiting. MEDIUM if outbound APIs lack 429 handling. N/A if no endpoints. --- @@ -84,7 +111,20 @@ Scan for dangerous patterns: Check for validation: - Grep for: `zod`, `joi`, `yup`, `ajv`, `validator`, `encodeURIComponent`, `escapeHtml`, `sanitize` -**Severity**: CRITICAL for eval/exec with user input. HIGH for unsanitized URL paths. MEDIUM for missing validation library. PASS if clean. +**MCP Tool Handler scan** (if MCP Server Mode) — Grep for tool handlers using arguments directly without validation: +```bash +# Tool handlers passing args to dangerous sinks +grep -rniE '(args\.\w+|arguments\.\w+)' --include="*.ts" --include="*.js" --include="*.py" . | grep -E "(exec|execSync|spawn|readFile|writeFile|query|SQL|eval)" + +# Tool handlers without inputSchema (missing 2nd arg in server.tool) +grep -rniE 'server\.tool\([^,]+,\s*(?:async\s*)?\(' --include="*.ts" --include="*.js" . + +# No validation library when server.tool() calls exist +``` + +Check if any validation library (zod, joi, ajv, pydantic, BaseModel) is present when `server.tool()` calls exist. + +**Severity**: CRITICAL for eval/exec with user input or unvalidated MCP tool args flowing to exec/SQL/filesystem. HIGH for unsanitized URL paths. MEDIUM for missing validation library. PASS if clean. --- @@ -107,7 +147,20 @@ Check for validation: **Check lockfile**: Verify `package-lock.json`, `yarn.lock`, or equivalent exists. **Check outdated**: Run `npm outdated 2>/dev/null | head -10` -**Severity**: CRITICAL if known high/critical vulns. MEDIUM if no lockfile. LOW if outdated packages. PASS if clean. +**MCP SDK version check** (if MCP detected): +```bash +node -e "const p=require('./package.json'); const v=p.dependencies?.['@modelcontextprotocol/sdk']||p.devDependencies?.['@modelcontextprotocol/sdk']; if(v) console.log('MCP SDK version: '+v);" 2>/dev/null +``` +- TypeScript SDK < 1.4.0 → CRITICAL: CVE-2025-66414 (DNS rebinding, host header validation disabled by default) +- Python SDK (any version prior to fix) → HIGH: CVE-2025-66416 (DNS rebinding) +- Flag `@modelcontextprotocol/sdk` version 1.3.x or below + +Check for typosquatted MCP packages: +```bash +grep -E '"(model-context-protocol|mcp-sdk|fast-mcp)"' package.json 2>/dev/null +``` + +**Severity**: CRITICAL if known high/critical vulns or vulnerable MCP SDK. MEDIUM if no lockfile. LOW if outdated packages. PASS if clean. --- @@ -127,7 +180,14 @@ git ls-files 2>/dev/null | grep -iE "\.(env|pem|key|cert|p12|pfx|keystore)$" If the project is published to npm, check for `files` field in package.json or `.npmignore`. -**Severity**: HIGH if .env not in .gitignore. MEDIUM if *.pem/*.key missing. LOW if minor patterns missing. PASS if complete. +**MCP Config gitignore check** (if MCP Consumer Mode) — Verify MCP config files are in .gitignore: +```bash +git ls-files 2>/dev/null | grep -iE "(\.mcp\.json|claude_desktop_config\.json)" +``` +- `.mcp.json` should be in .gitignore (may contain API keys in env blocks) +- `claude_desktop_config.json` should be in .gitignore + +**Severity**: HIGH if .env or MCP config files are tracked in git. MEDIUM if *.pem/*.key missing. LOW if minor patterns missing. PASS if complete. --- @@ -152,7 +212,347 @@ Scan for patterns that leak internal details: - `console.error` of full error objects in production code paths - Error responses that include raw API response bodies -**Severity**: MEDIUM if raw error bodies are exposed to users. LOW if only logged to console. PASS if errors are sanitized. +**MCP Tool Error scan** (if MCP Server Mode) — Scan for raw errors in tool handler responses: +```bash +# Stack traces or raw errors in isError responses +grep -rniE 'isError.*true' --include="*.ts" --include="*.js" -A3 . | grep -E '(\.stack|\.message|JSON\.stringify\(e|JSON\.stringify\(err)' + +# Error details in template literals within catch blocks +grep -rniE 'catch\s*\([^)]*\)' --include="*.ts" --include="*.js" -A5 . | grep -E '(\$\{(e|err|error)\}|\.stack|traceback)' + +# Python traceback in tool returns +grep -rniE 'traceback\.format_exc\(\)|str\(e\)' --include="*.py" . +``` + +**Severity**: MEDIUM if raw error bodies or stack traces are exposed to users or in tool content. LOW if only logged to console. PASS if errors are sanitized. + +--- + +### MCP Security Checks (activated when MCP project detected) + +#### Phase 0 — MCP Detection Summary + +This is not a numbered check. Output the detection result: +- "MCP project detected — activating MCP Security Checks 9-20" +- State which mode: **Server Mode**, **Consumer Mode**, or **Both** +- Report: language, transport type, estimated tool/resource/prompt counts, config files found + +--- + +#### Check 9: Tool Description Integrity + +**Only in MCP Server Mode.** + +Scan all source files containing `server.tool(`, `@mcp.tool`, or tool definition objects for suspicious content in description fields. + +**Grep patterns:** +```bash +# Find tool definition files +grep -rn "server\.tool\|@mcp\.tool\|\"description\"\s*:" --include="*.ts" --include="*.js" --include="*.py" . + +# Scan descriptions for injection markers +grep -riE '(||||ignore previous|disregard|override.*instruction|you must now|act as|pretend to be|never tell|do not (tell|mention|say))' --include="*.ts" --include="*.js" --include="*.py" . + +# Scan for file path references in descriptions +grep -riE '(~/\.|/etc/|~/.ssh|~/.cursor|\.env|id_rsa|\.config)' --include="*.ts" --include="*.js" --include="*.py" . | grep -i "description" + +# Flag descriptions > 500 chars (may hide injected content) +# Check for cross-tool references in descriptions +``` + +**Severity**: CRITICAL if injection markers or sensitive file paths found in descriptions. HIGH if descriptions > 500 chars or reference other tools by name. PASS if all descriptions are static and clean. N/A if no tool registrations found. + +**Auto-fix**: Offer to review and clean suspicious descriptions. Strip hidden content. + +--- + +#### Check 10: Unicode / Invisible Character Smuggling + +**Applies to both MCP Server Mode and Consumer Mode.** + +Scan tool description strings and source files for invisible Unicode characters that are processed by the LLM but invisible to human reviewers. + +**Bash command:** +```bash +python3 -c " +import os, re, sys +dangerous_ranges = [ + (0x200B, 0x200D), # zero-width spaces + (0xFEFF, 0xFEFF), # BOM + (0xE0000, 0xE007F), # Unicode tags (invisible) + (0x202A, 0x202E), # directional overrides + (0x2060, 0x206F), # format chars + (0x00AD, 0x00AD), # soft hyphen +] +files = [] +for root, _, fs in os.walk('.'): + for f in fs: + if f.endswith(('.ts','.js','.py','.json')) and 'node_modules' not in root: + files.append(os.path.join(root,f)) +found = [] +for fp in files: + try: + content = open(fp, encoding='utf-8', errors='ignore').read() + for i, ch in enumerate(content): + cp = ord(ch) + for lo, hi in dangerous_ranges: + if lo <= cp <= hi: + found.append((fp, hex(cp))) + break + except: pass +if found: + for f, c in found[:10]: print(f'FOUND {c} in {f}') +else: + print('PASS') +" 2>/dev/null +``` + +**Severity**: CRITICAL if Unicode tag characters (U+E0000-U+E007F) found in tool descriptions (invisible to humans, processed by LLM). HIGH for other invisible Unicode (directional overrides, zero-width chars). PASS if clean. + +--- + +#### Check 11: Encoded Payloads in Tool Metadata + +**Only in MCP Server Mode.** + +Scan tool description and parameter definition strings for encoded content that could be decoded and executed by the LLM. + +**Grep patterns:** +```bash +# Base64 in description fields (30+ chars of base64) +grep -rniE '[A-Za-z0-9+/]{30,}={0,2}' --include="*.ts" --include="*.js" --include="*.py" . | grep -i "description" + +# Hex encoding patterns +grep -rniE '(\\x[0-9a-fA-F]{2}){4,}|0x[0-9a-fA-F]{8,}' --include="*.ts" --include="*.js" --include="*.py" . + +# Decode/execute instructions in descriptions +grep -rniE '(decode|execute|eval|interpret|run this)' --include="*.ts" --include="*.js" --include="*.py" . | grep -i "description" +``` + +**Severity**: HIGH if Base64 or hex-encoded patterns found in tool metadata. MEDIUM if descriptions reference decoding operations. PASS if clean. + +**Auto-fix**: Offer to decode and inspect suspicious strings for review. + +--- + +#### Check 12: MCP Transport Security + +**Only in MCP Server Mode.** + +Verify TLS is enforced and DNS rebinding protection is active. + +**Checks:** +```bash +# Check for HTTP (non-HTTPS, non-localhost) in MCP configs +grep -rniE '"url"\s*:\s*"http://' .mcp.json claude_desktop_config.json 2>/dev/null | grep -vE '(localhost|127\.0\.0\.1|::1)' + +# Check for 0.0.0.0 binding without auth +grep -rniE '(0\.0\.0\.0|host:\s*["'"'"']0\.0\.0\.0)' --include="*.ts" --include="*.js" --include="*.py" . + +# Check for DNS rebinding protection +grep -rn "enableDnsRebindingProtection\|localhostHostValidation\|hostHeaderValidation\|createMcpExpressApp" --include="*.ts" --include="*.js" . + +# Check MCP SDK version for DNS rebinding CVE +node -e "const v=require('./package.json').dependencies?.['@modelcontextprotocol/sdk']; if(v) console.log(v);" 2>/dev/null +``` + +MCP TypeScript SDK < 1.4.0 = CRITICAL: CVE-2025-66414 (DNS rebinding protection disabled by default). + +**Severity**: CRITICAL for HTTP URLs to remote servers + SDK < 1.4.0. HIGH for 0.0.0.0 binding without auth or missing DNS rebinding protection. N/A for stdio-only servers. + +**Auto-fix**: Offer to add `hostHeaderValidation` middleware. Offer to upgrade SDK. + +--- + +#### Check 13: MCP Authentication + +**Only in MCP Server Mode + HTTP transport detected.** + +First detect HTTP transport: +```bash +grep -rn "StreamableHTTPServerTransport\|SSEServerTransport\|createMcpExpressApp\|app\.listen\|app\.post.*mcp\|app\.get.*sse" --include="*.ts" --include="*.js" . +``` + +If HTTP transport found, check for auth: +```bash +grep -rn "mcpAuthRouter\|requireBearerAuth\|OAuthServerProvider\|verifyAccessToken\|bearerAuth\|express-jwt\|passport\|jsonwebtoken\|jwt\.verify" --include="*.ts" --include="*.js" . +``` + +If HTTP transport exists but NO auth patterns found: **CRITICAL**. +If only stdio transport (StdioServerTransport): **N/A**. + +**Severity**: CRITICAL if HTTP-based MCP server has no authentication. N/A for stdio-only. + +**Auto-fix**: Offer to add basic bearer token auth middleware. + +--- + +#### Check 14: Token Scope & Lifecycle + +**Applies to MCP Consumer Mode.** + +Check for over-privileged tokens, missing expiration, and insecure storage. + +```bash +# Check for wildcard/broad OAuth scopes in MCP config or auth code +grep -rniE '(mail\.google\.com/|calendar\.google\.com/|drive\.google\.com/|scope.*\*|scope.*"all"|scope.*"full")' --include="*.ts" --include="*.js" --include="*.py" .mcp.json 2>/dev/null + +# Check for access tokens stored in plaintext +grep -rniE '("access_token"\s*:\s*"[^"]{20,}"|token\s*=\s*["'"'"'][^"'"'"']{20,})' .mcp.json claude_desktop_config.json 2>/dev/null + +# Check for long-lived tokens (no expiry) +grep -rniE '(expires_in.*86400|expires_in.*[0-9]{6,}|no.*expir|never.*expir)' --include="*.ts" --include="*.js" . +``` + +**Severity**: CRITICAL for plaintext access tokens in config files. HIGH for broad OAuth scopes or tokens with no expiry. PASS if scoped and rotated. + +--- + +#### Check 15: MCP Input Schema Validation + +**Only in MCP Server Mode.** + +Verify all tools define and enforce input schemas. + +```bash +# Find all tool registrations +grep -n "server\.tool\|@mcp\.tool\|setRequestHandler.*ListTools" --include="*.ts" --include="*.js" --include="*.py" -r . + +# Check for inputSchema / validation library usage +grep -rn "inputSchema\|z\.object\|Joi\.object\|ajv\.compile\|BaseModel\|pydantic" --include="*.ts" --include="*.js" --include="*.py" . + +# Check for additionalProperties: false (strict schemas) +grep -rn "additionalProperties.*false" --include="*.ts" --include="*.js" --include="*.json" . + +# Unvalidated args flowing to dangerous operations +grep -rniE '(args\.\w+|arguments\.\w+)' --include="*.ts" --include="*.js" . | grep -E "(exec|execSync|spawn|readFile|writeFile|query|SQL|eval)" +``` + +**Severity**: CRITICAL if unvalidated args flow to exec/SQL/filesystem. HIGH if tools exist without inputSchema. MEDIUM if schemas lack constraints (no maxLength, no enum). PASS if all tools have strict schemas. + +**Auto-fix**: Offer to add zod schema stubs to tool definitions. + +--- + +#### Check 16: Tool Response Sanitization + +**Only in MCP Server Mode.** + +Verify tool handlers do not leak raw errors, stack traces, or internal paths in tool results. + +```bash +# Raw error in isError responses +grep -rniE 'isError.*true' --include="*.ts" --include="*.js" -A3 . | grep -E '(\.stack|\.message|JSON\.stringify\(e|JSON\.stringify\(err|\$\{e\}|\$\{err\})' + +# Stack traces in catch blocks returning content +grep -rniE 'catch\s*\([^)]*\)' --include="*.ts" --include="*.js" -A5 . | grep -E '(\.stack|stacktrace|traceback)' + +# Full error serialization +grep -rniE 'JSON\.stringify\((e|err|error)\b' --include="*.ts" --include="*.js" . + +# Python traceback in tool returns +grep -rniE 'traceback\.format_exc\(\)|str\(e\)' --include="*.py" . +``` + +**Severity**: MEDIUM for stack traces or raw error objects in tool responses. Same criteria as Check 8. + +--- + +#### Check 17: CORS / Origin Validation + +**Only in MCP Server Mode + HTTP transport detected.** + +```bash +# Dangerous CORS: wildcard origin +grep -rniE 'cors\(\s*\)|cors\(\s*\{\s*origin\s*:\s*["'"'"']\*["'"'"']|cors\(\s*\{\s*origin\s*:\s*true' --include="*.ts" --include="*.js" . + +# Wildcard Access-Control-Allow-Origin header +grep -rniE 'Access-Control-Allow-Origin.*\*' --include="*.ts" --include="*.js" . + +# Python FastAPI wildcard +grep -rniE 'allow_origins\s*=\s*\["?\*"?\]' --include="*.py" . + +# PASS indicators: SDK built-in protections +grep -rn "hostHeaderValidation\|localhostHostValidation\|createMcpExpressApp" --include="*.ts" --include="*.js" . +``` + +**Severity**: CRITICAL if `origin: '*'` combined with `credentials: true`. HIGH for `origin: '*'` on HTTP MCP server. N/A for stdio-only. + +--- + +#### Check 18: MCP Supply Chain & Config Hygiene + +**Applies to both MCP Server Mode and Consumer Mode.** + +```bash +# @latest floating versions in MCP config (rug-pull risk) +grep -rniE '"@latest"|npx.*@latest' .mcp.json claude_desktop_config.json .cursor/mcp.json 2>/dev/null + +# npx -y without pinned version (auto-install from potentially poisoned package) +grep -rniE 'npx.*-y' .mcp.json claude_desktop_config.json 2>/dev/null | grep -vE '@[0-9]' + +# Lockfile check +ls package-lock.json yarn.lock pnpm-lock.yaml bun.lockb 2>/dev/null || echo "NO_LOCKFILE" + +# files whitelist in package.json (if published MCP server) +node -e "const p=require('./package.json'); console.log(p.files ? 'HAS_FILES_WHITELIST' : 'NO_FILES_WHITELIST');" 2>/dev/null + +# Shell metacharacters in MCP config args (command injection via config) +grep -rniE '"args"\s*:\s*\[' .mcp.json claude_desktop_config.json 2>/dev/null | grep -E '[;|&\$\`]' +``` + +**Severity**: HIGH for `@latest` in MCP config. HIGH for no lockfile. HIGH for shell metacharacters in args arrays. MEDIUM for no files whitelist on published MCP server. PASS if pinned and locked. + +**Auto-fix**: Offer to pin all @latest references to current resolved version numbers. + +--- + +#### Check 19: Audit Logging + +**Only in MCP Server Mode.** + +Verify tool invocations are logged with structured data. + +```bash +# Check for structured logging library +grep -rn "winston\|pino\|bunyan\|log4js\|structlog\|logging\.getLogger" package.json requirements.txt 2>/dev/null + +# Check for MCP logging notifications +grep -rn "sendLoggingMessage\|LoggingMessageNotification\|setLoggingLevel\|notifications/message" --include="*.ts" --include="*.js" . + +# Check for observability integration +grep -rn "opentelemetry\|datadog\|sentry\|splunk\|elastic-apm" package.json 2>/dev/null +``` + +Compare: count tool registrations (`server.tool` / `@mcp.tool`) vs structured logging references. If tools > 0 and structured logging = 0, flag it. + +**Severity**: MEDIUM if MCP server has tool handlers but no structured logging. LOW if has logging but no observability integration. PASS if structured logging with audit trail. + +--- + +#### Check 20: Rug-Pull & Tool Mutation Defense + +**Applies to MCP Consumer Mode (config files present).** + +Check for floating version references that enable rug-pull attacks. + +```bash +# @latest in any MCP config +grep -rniE '"@latest"' .mcp.json claude_desktop_config.json .cursor/mcp.json 2>/dev/null + +# npx without pinned version in MCP config commands +grep -rniE '"command"\s*:\s*"npx"' .mcp.json claude_desktop_config.json 2>/dev/null + +# Verify packages have pinned versions (not @latest) +grep -rniE '@[a-z0-9-]+/[a-z0-9-]+' .mcp.json claude_desktop_config.json 2>/dev/null | grep -v '@[0-9]' | grep -v '@latest' + +# Check if any MCP server hashes tool definitions (integrity verification) +grep -rn "createHash\|sha256\|sha-256\|integrity\|checksum" --include="*.ts" --include="*.js" . | grep -iE "(tool|description|schema)" +``` + +**Severity**: HIGH for `@latest` floating versions. MEDIUM for unpinned packages without `@latest`. LOW if no hash/integrity verification for tool definitions (informational). PASS if all pinned with lockfile. + +**Auto-fix**: Offer to pin all @latest references to current version numbers. --- @@ -168,6 +568,10 @@ Output a markdown table: | ... | ... | ... | ... | ``` +For MCP projects, split into two tables: **Core Checks** (1-8) and **MCP-Specific Checks** (9-20). + +MCP checks show N/A if project is not an MCP project. + Then list specific findings with file paths and line numbers. ### Step 4 — Offer Fixes @@ -179,4 +583,12 @@ For each finding that has an auto-fixable solution, offer to fix it: - execSync with string concat → offer to replace with execFileSync - Missing input validation → offer to add validation functions +**MCP-specific fixes:** +- Pinning @latest versions → offer to look up current versions and pin them +- Missing DNS rebinding protection → offer to add hostHeaderValidation middleware +- Missing inputSchema → offer to add zod schema to tool definitions +- Hardcoded secrets in .mcp.json → offer to move to environment variable references +- HTTP without auth → offer to add bearer token auth middleware +- MCP config tracked in git → offer to add to .gitignore + Ask the user before making any changes. diff --git a/step-9/step-9-install.sh b/step-9/step-9-install.sh index 17d9aad..3f4e443 100755 --- a/step-9/step-9-install.sh +++ b/step-9/step-9-install.sh @@ -203,13 +203,13 @@ run_self_test() { TEST_FAIL=$((TEST_FAIL + 1)) fi - # Test 5: Skill file contains all 8 checks - CHECK_COUNT=$(grep -c "^#\{2,4\} Check [0-9]" "$SKILL_FILE" 2>/dev/null || echo "0") - if [ "$CHECK_COUNT" -ge 8 ]; then - success "TEST: SKILL.md defines all 8 security checks" + # Test 5: Skill file contains all 20 checks + CHECK_COUNT=$(grep -c "^#\{2,4\} Check [0-9][0-9]*" "$SKILL_FILE" 2>/dev/null || echo "0") + if [ "$CHECK_COUNT" -ge 20 ]; then + success "TEST: SKILL.md defines all 20 security checks" TEST_PASS=$((TEST_PASS + 1)) else - echo -e "${RED}[FAIL]${NC} TEST: SKILL.md only has $CHECK_COUNT/8 checks" + echo -e "${RED}[FAIL]${NC} TEST: SKILL.md only has $CHECK_COUNT/20 checks" TEST_FAIL=$((TEST_FAIL + 1)) fi @@ -239,7 +239,7 @@ run_self_test() { print_summary() { echo "" echo -e "${GREEN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}" - echo -e "${GREEN} Step 9 Complete — /safetycheck Installed${NC}" + echo -e "${GREEN} Step 9 Complete — /safetycheck Installed (20 checks)${NC}" echo -e "${GREEN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}" echo "" echo " Installed:" @@ -251,22 +251,37 @@ print_summary() { echo "" echo " What it checks:" echo "" - echo " 1. Exposed API Keys — hardcoded secrets + git history" + echo " API Security (checks 1-8):" + echo " 1. Exposed API Keys — hardcoded secrets, git history, MCP config" echo " 2. Rate Limiting — endpoint protection middleware" - echo " 3. Input Sanitization — eval, innerHTML, SQL injection" + echo " 3. Input Sanitization — eval, innerHTML, SQL injection, tool handlers" echo " 4. RLS / Database Security — parameterized queries, RLS policies" - echo " 5. Dependency Vulns — npm audit, lockfile hygiene" - echo " 6. Gitignore Hygiene — .env, *.pem, *.key coverage" + echo " 5. Dependency Vulns — npm audit, MCP SDK CVEs, lockfile hygiene" + echo " 6. Gitignore Hygiene — .env, *.pem, MCP config files" echo " 7. CI/CD & GitHub Security — workflows, dependabot, SECURITY.md" - echo " 8. Error Handling — raw error exposure to clients" + echo " 8. Error Handling — raw error exposure, tool response leakage" + echo "" + echo " MCP Security (checks 9-20, activated when MCP project detected):" + echo " 9. Tool Description Integrity — injection markers, file paths, cross-tool refs" + echo " 10. Unicode Smuggling — invisible chars, zero-width, tag characters" + echo " 11. Encoded Payloads — Base64/hex in tool metadata" + echo " 12. MCP Transport Security — HTTP vs HTTPS, DNS rebinding CVEs" + echo " 13. MCP Authentication — bearer auth on HTTP MCP endpoints" + echo " 14. Token Scope & Lifecycle — over-broad scopes, plaintext tokens" + echo " 15. Input Schema Validation — tool schemas, additionalProperties" + echo " 16. Tool Response Sanitization — stack traces in tool results" + echo " 17. CORS / Origin Validation — wildcard CORS on MCP endpoints" + echo " 18. Supply Chain & Config — @latest pins, lockfile, .mcp.json hygiene" + echo " 19. Audit Logging — structured logging, MCP notifications" + echo " 20. Rug-Pull Defense — tool mutation, version pinning" echo "" if [ "$ERRORS" -gt 0 ]; then echo -e " ${YELLOW}Warnings: $ERRORS non-critical issue(s) during install.${NC}" echo -e " ${YELLOW}Scroll up to see details.${NC}" echo "" fi - echo -e " ${YELLOW}Tip: Run /safetycheck in any project directory to get a${NC}" - echo -e " ${YELLOW}severity-rated audit table with fix suggestions.${NC}" + echo -e " ${YELLOW}Tip: Run /safetycheck in any project. MCP projects get${NC}" + echo -e " ${YELLOW}12 additional security checks automatically.${NC}" echo "" echo -e "${GREEN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}" }