Scan installed plugins and skills for security risks including malicious code AND malicious natural language instructions. Use /security-scanner to audit before installation.
/security-scanner # Scan all (plugins + skills)
/security-scanner --user # Scan user-level only (~/.claude/)
/security-scanner --project # Scan project-level only (.claude/)
/security-scanner --all # Scan ALL (ignore trusted sources and self-exclusion)
/security-scanner <url> # Scan from GitHub URL (public repos only)
/security-scanner --url <url> # Same as above (explicit form)
Search for security-scanner.local.md in the following locations:
.claude/security-scanner.local.md~/.claude/security-scanner.local.mdPriority rules:
From the selected file, extract:
report_language from YAML frontmattertarget_agents list from YAML frontmattertrusted_marketplaces list from YAML frontmattertrusted_plugins list from YAML frontmattertrusted_skills list from YAML frontmatterDefault values (when not specified):
report_language: ja (Japanese)target_agents: ["claude"]trusted_marketplaces: []trusted_plugins: []trusted_skills: []Validation:
report_language: Any string value accepted (AI will generate report in that language)target_agents must contain only valid agent IDs: claude, codex, gemini, agentsError handling:
Check arguments to determine what to scan:
Location filters:
--user: Scan only user-level paths for all agents in target_agents--project: Scan only project-level paths for all agents in target_agentsURL detection (highest priority):
--url <url> is provided explicitly → Go to Step 2-URLhttps://github.com/ or http://github.com/ → Treat as URL, go to Step 2-URLhttps:// or http:// but not github.com → Error: "Unsupported host: {host}. Currently only github.com is supported."Special modes (if no URL):
--all: Scan everything (skip Step 4 filtering entirely)If URL is provided (via --url or auto-detected), follow this process instead of Steps 3-4.
Parse the GitHub URL to extract owner, repo, branch, path, and determine scan type:
URL Patterns:
https://github.com/{owner}/{repo}[/tree/{branch}/{path}]https://github.com/{owner}/{repo}/blob/{branch}/{path}.mdgithub.comowner and repo from path segments/blob/ and ends with .md → Single file scan/tree/{branch}/{path} exists, extract branch and path/tree/, set branch to empty (use default) and path to empty stringbranch and file path after /blob/{branch}/For Single File Scan:
/blob/ URL to raw URL: https://raw.githubusercontent.com/{owner}/{repo}/{branch}/{path}For Directory Scan:
https://api.github.com/repos/{owner}/{repo}/contents/{path}?ref={branch}?ref= parameter (uses default branch)plugin.json exists: Full plugin scan (fetch all plugin files)skills/ exists: Skill scan (fetch skill directories)SKILL.md exists: Single skill directory scan (fetch all files in directory)skills/ → fetch subdirectories → fetch SKILL.md filesagents/ → fetch all *.md files (if exists)hooks/ → fetch all *.md files (if exists)commands/ → fetch all *.md files (if exists)For plugin scan, fetch:
plugin.json, README.md, .mcp.jsonskills/*/SKILL.md, agents/*.md, hooks/*.md, commands/*.mdFor skill directory scan (skills/ or single skill), fetch:
Use WebFetch with prompt: "Return the raw file content exactly as-is"
After fetching all files, proceed to Step 5 for analysis.
Based on scope determined in Step 2 and target_agents from Step 1, collect targets:
For plugins (Claude Code only):
User-level:
~/.claude/plugins/installed_plugins.json.plugins maps each plugin ID (<plugin>@<marketplace>) to an array of install records. For each ID, keep the ID and every record's installPath, regardless of the record's scope, then deduplicate the scan targets by installPathProject-level:
.claude/plugins/*/For skills (based on target_agents):
For each agent in target_agents list, collect skills from the corresponding directories:
Agent path mapping:
| Agent | Project Level | User Level |
|---|---|---|
| claude | .claude/skills/*/ |
~/.claude/skills/*/ |
| codex | .codex/skills/*/ |
~/.codex/skills/*/ |
| gemini | .gemini/skills/*/ |
~/.gemini/skills/*/ |
| agents | .agents/skills/*/ |
~/.config/agents/skills/*/ AND ~/.agents/skills/*/ |
For each agent in target_agents:
User-level:
Project-level:
If --all flag is set: Skip this step entirely and scan all targets.
<skill base directory> is the directory the harness reports as "Base directory for this skill" at this skill's invocation. Do not hardcode an absolute path.
/ and expand a leading ~ls -ld <dir>. When its output shows -> <target>, the resolved path is <target> when absolute, else <target> joined against the directory containing <dir> and normalized. Ancestor components are not resolved. When the command fails, that path has no resolved forminstallPath, a project-level plugin directory, or a skill directory — is this scanner itself when its path equals <skill base directory>, or one of the two is a /-boundary prefix of the other. Compare the pair twice: as collected against as reported, then with both sides resolved<skill base directory> is unavailable: skip plugins and skills named security-scanner, and report the skip as a name matchsecurity-scanner that is not this scanner is scanned normally whenever <skill base directory> is available; a second copy of this scanner installed elsewhere on the host is one such targetTrusted sources:
trusted_marketplaces → Skip<plugin>@<marketplace>) is in trusted_plugins → SkipTrusted sources:
trusted_skills → Skip (any agent)For each non-trusted plugin:
Read plugin metadata (plugin.json, README.md) to understand its stated purpose
Read all executable content:
skills/*/SKILL.md - Skill definitions and instructionsagents/*.md - Agent system prompts (if exists)hooks/*.md - Hook definitions (if exists)commands/*.md - Command definitions (if exists).mcp.json - MCP server configurations (if exists)Analyze for malicious intent (both code AND natural language)
For each non-trusted skill:
Read skill metadata (SKILL.md frontmatter) to understand its stated purpose
Read all files in the skill directory:
SKILL.md - Main skill instructions (required)Analyze for malicious intent (both code AND natural language)
Look for dangerous command patterns:
curl|sh, wget|bash, base64 -d|shpython -c, perl -e, ruby -e, node -e, osascript, powershellnc -e, netcat, socat, mkfiform -rf /, dd if=~/.ssh/, /etc/passwd, keychainsshpass, scp, rsync to external serverschmod +x /tmp/*, mktemp + executionbase64/xxd/hexdump decoding + executionnpm install/pip install with postinstall or setup.pycrontab, launchctlsudo, doasCarefully read and analyze system prompts, instructions, and documentation for:
Check if permissions match the plugin's purpose:
Bash(*)? Suspicious.Bash(git *)? Reasonable.~/.ssh/? Suspicious.Generate the report in the language specified by report_language setting.
Japanese (ja) - Default:
# セキュリティ分析レポート
## 概要
| エージェント | 種別 | 検出 | 信頼済 | スキャン | 悪意あり | 要注意 | 安全 |
|-------------|------|------|--------|----------|----------|--------|------|
| claude | プラグイン | N | N | N | N | N | N |
| claude | スキル | N | N | N | N | N | N |
注: `target_agents` に設定されたエージェントのみ表示。プラグインは常に `claude` 配下。
## 信頼済み(スキップ)
- plugin-name@marketplace(信頼済みマーケットプレイス)
- plugin-name@marketplace(自己除外: パス一致)
- skill-name (claude)(自己除外: 名前一致)
- skill-name (claude) - 信頼済みスキル
## 検出結果
### プラグイン (claude)
#### [プラグイン名]
**種別:** プラグイン
**目的:** [README/plugin.json から]
**判定:** 安全 / 要注意 / 悪意あり
**検出された問題:**
- [問題の説明、ファイル、懸念される理由]
### スキル
#### [スキル名] (claude)
**エージェント:** claude
**種別:** スキル
**場所:** ~/.claude/skills/skill-name/ または .claude/skills/skill-name/
**目的:** [SKILL.md の description から]
**判定:** 安全 / 要注意 / 悪意あり
**検出された問題:**
- [問題の説明と懸念される理由]
---
## 推奨事項
問題のある項目について:
- [ ] 安全 - 使用可
- [ ] 要確認 - [具体的な懸念点]
- [ ] 使用禁止 - [悪意のあるコンテンツを検出]
English (en):
# Security Analysis Report
## Summary
| Agent | Type | Found | Trusted | Scanned | Malicious | Suspicious | Safe |
|-------|------|-------|---------|---------|-----------|------------|------|
| claude | Plugins | N | N | N | N | N | N |
| claude | Skills | N | N | N | N | N | N |
Note: Only rows for configured `target_agents` are shown. Plugins are always under `claude`.
## Trusted (Skipped)
- plugin-name@marketplace (trusted marketplace)
- plugin-name@marketplace (self-exclusion: path match)
- skill-name (claude) (self-exclusion: name match)
- skill-name (claude) - trusted skill
## Findings
### Plugins (claude)
#### [Plugin Name]
**Type:** Plugin
**Purpose:** [from README/plugin.json]
**Verdict:** Safe / Suspicious / Malicious
**Issues found:**
- [Description of issue, file, and why it's concerning]
### Skills
#### [Skill Name] (claude)
**Agent:** claude
**Type:** Skill
**Location:** ~/.claude/skills/skill-name/ or .claude/skills/skill-name/
**Purpose:** [from SKILL.md description]
**Verdict:** Safe / Suspicious / Malicious
**Issues found:**
- [Description of issue and why it's concerning]
Use the same report format as local scans, with this header added:
Japanese (ja):
English (en):
rm -rf patterns is different from a plugin containing rm -rf commands