vigile-scan 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +1655 -0
- package/dist/index.js.map +1 -0
- package/package.json +67 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/discovery/claude-desktop.ts","../src/discovery/utils.ts","../src/discovery/cursor.ts","../src/discovery/claude-code.ts","../src/discovery/windsurf.ts","../src/discovery/vscode.ts","../src/discovery/skills.ts","../src/discovery/index.ts","../src/scanner/patterns.ts","../src/scoring/trust-score.ts","../src/scanner/index.ts","../src/scanner/skill-patterns.ts","../src/scanner/skill-scanner.ts","../src/output/terminal.ts","../src/output/json.ts"],"sourcesContent":["// ============================================================\r\n// Vigile CLI — Main Entry Point\r\n// ============================================================\r\n// Usage: npx vigile-scan [options]\r\n//\r\n// The AI agent security scanner. Discovers MCP server configs\r\n// and agent skill files on your machine, scans them for\r\n// security issues, and outputs trust scores.\r\n\r\nimport { Command } from 'commander';\r\nimport ora from 'ora';\r\nimport { writeFile } from 'fs/promises';\r\nimport { discoverAllServers, discoverAllSkills } from './discovery/index.js';\r\nimport { scanServer } from './scanner/index.js';\r\nimport { scanSkill } from './scanner/skill-scanner.js';\r\nimport {\r\n printBanner,\r\n printServerResult,\r\n printSkillResult,\r\n printSummary,\r\n printNoServersFound,\r\n printNoSkillsFound,\r\n printNothingFound,\r\n} from './output/terminal.js';\r\nimport { formatJSON } from './output/json.js';\r\nimport type { ScanOptions, ScanSummary, ScanResult, SkillScanResult, MCPClient } from './types/index.js';\r\n\r\nconst VERSION = '0.1.0';\r\n\r\nconst program = new Command();\r\n\r\nprogram\r\n .name('vigile-scan')\r\n .description(\r\n 'Security scanner for AI agent tools — detect tool poisoning, permission abuse, and supply chain attacks in MCP servers and agent skills'\r\n )\r\n .version(VERSION);\r\n\r\n// Shared options for scan commands\r\nfunction addScanOptions(cmd: Command): Command {\r\n return cmd\r\n .option('-j, --json', 'Output results as JSON')\r\n .option('-v, --verbose', 'Show detailed findings and score breakdown')\r\n .option('-c, --config <path>', 'Path to a custom MCP config file')\r\n .option('-o, --output <path>', 'Write results to a file')\r\n .option(\r\n '--client <client>',\r\n 'Only scan a specific client (claude-desktop, cursor, claude-code, windsurf, vscode)'\r\n )\r\n .option('-s, --skills', 'Scan agent skills only (SKILL.md, .mdc rules, CLAUDE.md, etc.)')\r\n .option('-a, --all', 'Scan both MCP servers and agent skills');\r\n}\r\n\r\naddScanOptions(\r\n program\r\n .command('scan')\r\n .description('Scan MCP server configurations and agent skill files on this machine')\r\n).action(async (options: ScanOptions) => {\r\n await runScan(options);\r\n});\r\n\r\n// Default command (no subcommand) also runs scan\r\naddScanOptions(program).action(async (options: ScanOptions) => {\r\n if (!process.argv.slice(2).includes('scan')) {\r\n await runScan(options);\r\n }\r\n});\r\n\r\nasync function runScan(options: ScanOptions): Promise<void> {\r\n const isJSON = options.json;\r\n const scanMCP = !options.skills; // Scan MCP unless --skills only\r\n const scanSkills = options.skills || options.all; // Scan skills if --skills or --all\r\n\r\n if (!isJSON) {\r\n printBanner();\r\n }\r\n\r\n const results: ScanResult[] = [];\r\n const skillResults: SkillScanResult[] = [];\r\n\r\n // ── Step 1: Discover & scan MCP servers ──\r\n if (scanMCP) {\r\n const spinner = isJSON ? null : ora('Discovering MCP configurations...').start();\r\n const discovery = await discoverAllServers(options.client as MCPClient | undefined);\r\n\r\n if (discovery.servers.length === 0) {\r\n spinner?.succeed('No MCP server configurations found');\r\n } else {\r\n spinner?.succeed(\r\n `Found ${discovery.servers.length} MCP server(s) across ${discovery.configsFound} config file(s)`\r\n );\r\n\r\n const scanSpinner = isJSON ? null : ora('Scanning MCP servers...').start();\r\n for (const server of discovery.servers) {\r\n const result = await scanServer(server);\r\n results.push(result);\r\n }\r\n scanSpinner?.succeed(`Scanned ${results.length} MCP server(s)`);\r\n }\r\n }\r\n\r\n // ── Step 2: Discover & scan agent skills ──\r\n if (scanSkills) {\r\n const spinner = isJSON ? null : ora('Discovering agent skill files...').start();\r\n const skillDiscovery = await discoverAllSkills();\r\n\r\n if (skillDiscovery.skills.length === 0) {\r\n spinner?.succeed('No agent skill files found');\r\n } else {\r\n spinner?.succeed(\r\n `Found ${skillDiscovery.skills.length} skill file(s) across ${skillDiscovery.locationsFound} location(s)`\r\n );\r\n\r\n const scanSpinner = isJSON ? null : ora('Scanning agent skills...').start();\r\n for (const skill of skillDiscovery.skills) {\r\n const result = await scanSkill(skill);\r\n skillResults.push(result);\r\n }\r\n scanSpinner?.succeed(`Scanned ${skillResults.length} skill file(s)`);\r\n }\r\n }\r\n\r\n // ── Check if anything was found ──\r\n if (results.length === 0 && skillResults.length === 0) {\r\n if (!isJSON) {\r\n if (scanMCP && !scanSkills) {\r\n printNoServersFound();\r\n } else if (scanSkills && !scanMCP) {\r\n printNoSkillsFound();\r\n } else {\r\n printNothingFound();\r\n }\r\n } else {\r\n console.log(JSON.stringify({ servers: [], skills: [], message: 'Nothing found to scan' }));\r\n }\r\n return;\r\n }\r\n\r\n // ── Step 3: Build combined summary ──\r\n const allResults = [...results];\r\n const allSkillResults = [...skillResults];\r\n\r\n // Combine trust level counts from both MCP and skill results\r\n const allTrustLevels = [\r\n ...results.map((r) => r.trustLevel),\r\n ...skillResults.map((r) => r.trustLevel),\r\n ];\r\n\r\n // Combine finding severities from both MCP and skill results\r\n const allFindings = [\r\n ...results.flatMap((r) => r.findings),\r\n ...skillResults.flatMap((r) => r.findings),\r\n ];\r\n\r\n const summary: ScanSummary = {\r\n totalServers: allResults.length,\r\n totalSkills: allSkillResults.length,\r\n byTrustLevel: {\r\n trusted: allTrustLevels.filter((l) => l === 'trusted').length,\r\n caution: allTrustLevels.filter((l) => l === 'caution').length,\r\n risky: allTrustLevels.filter((l) => l === 'risky').length,\r\n dangerous: allTrustLevels.filter((l) => l === 'dangerous').length,\r\n },\r\n bySeverity: {\r\n critical: allFindings.filter((f) => f.severity === 'critical').length,\r\n high: allFindings.filter((f) => f.severity === 'high').length,\r\n medium: allFindings.filter((f) => f.severity === 'medium').length,\r\n low: allFindings.filter((f) => f.severity === 'low').length,\r\n info: allFindings.filter((f) => f.severity === 'info').length,\r\n },\r\n results: allResults,\r\n skillResults: allSkillResults,\r\n timestamp: new Date().toISOString(),\r\n version: VERSION,\r\n };\r\n\r\n // ── Step 4: Output results ──\r\n if (isJSON) {\r\n const jsonOutput = formatJSON(summary);\r\n if (options.output) {\r\n await writeFile(options.output, jsonOutput);\r\n } else {\r\n console.log(jsonOutput);\r\n }\r\n } else {\r\n console.log('');\r\n\r\n // Print MCP server results\r\n if (results.length > 0) {\r\n for (const result of results) {\r\n printServerResult(result, options.verbose ?? false);\r\n }\r\n }\r\n\r\n // Print skill results\r\n if (skillResults.length > 0) {\r\n for (const result of skillResults) {\r\n printSkillResult(result, options.verbose ?? false);\r\n }\r\n }\r\n\r\n printSummary(summary);\r\n\r\n if (options.output) {\r\n await writeFile(options.output, formatJSON(summary));\r\n console.log(` Results saved to ${options.output}`);\r\n }\r\n }\r\n\r\n // ── Exit with appropriate code ──\r\n if (summary.bySeverity.critical > 0 || summary.bySeverity.high > 0) {\r\n process.exit(1);\r\n }\r\n}\r\n\r\nprogram.parse();\r\n","// ============================================================\r\n// Claude Desktop — MCP Config Discovery\r\n// ============================================================\r\n// Config locations (as of February 2026):\r\n// macOS: ~/Library/Application Support/Claude/claude_desktop_config.json\r\n// Windows: %APPDATA%\\Claude\\claude_desktop_config.json\r\n// Linux: ~/.config/Claude/claude_desktop_config.json\r\n\r\nimport { join } from 'path';\r\nimport { getHome, getPlatform, getAppData, tryConfigPaths } from './utils.js';\r\nimport type { MCPServerEntry } from '../types/index.js';\r\n\r\nexport async function discoverClaudeDesktop(): Promise<MCPServerEntry[]> {\r\n const home = getHome();\r\n const plat = getPlatform();\r\n\r\n const paths: string[] = [];\r\n\r\n switch (plat) {\r\n case 'darwin':\r\n paths.push(\r\n join(home, 'Library', 'Application Support', 'Claude', 'claude_desktop_config.json')\r\n );\r\n break;\r\n case 'win32':\r\n paths.push(join(getAppData(), 'Claude', 'claude_desktop_config.json'));\r\n break;\r\n case 'linux':\r\n paths.push(join(home, '.config', 'Claude', 'claude_desktop_config.json'));\r\n break;\r\n }\r\n\r\n return tryConfigPaths(paths, 'claude-desktop');\r\n}\r\n","// ============================================================\r\n// Vigile CLI — Discovery Utilities\r\n// ============================================================\r\n\r\nimport { readFile } from 'fs/promises';\r\nimport { existsSync } from 'fs';\r\nimport { join } from 'path';\r\nimport { homedir, platform } from 'os';\r\nimport type { MCPClient, MCPServerEntry } from '../types/index.js';\r\n\r\n/** Get the home directory */\r\nexport function getHome(): string {\r\n return homedir();\r\n}\r\n\r\n/** Get the current platform */\r\nexport function getPlatform(): 'darwin' | 'win32' | 'linux' {\r\n return platform() as 'darwin' | 'win32' | 'linux';\r\n}\r\n\r\n/** Get Windows APPDATA path */\r\nexport function getAppData(): string {\r\n return process.env.APPDATA || join(homedir(), 'AppData', 'Roaming');\r\n}\r\n\r\n/** Get Windows LOCALAPPDATA path */\r\nexport function getLocalAppData(): string {\r\n return (\r\n process.env.LOCALAPPDATA || join(homedir(), 'AppData', 'Local')\r\n );\r\n}\r\n\r\n/**\r\n * Parse an MCP config file (JSON with mcpServers key).\r\n * Returns empty array if file doesn't exist or is invalid.\r\n */\r\nexport async function parseMCPConfig(\r\n configPath: string,\r\n source: MCPClient\r\n): Promise<MCPServerEntry[]> {\r\n if (!existsSync(configPath)) {\r\n return [];\r\n }\r\n\r\n try {\r\n const raw = await readFile(configPath, 'utf-8');\r\n const config = JSON.parse(raw);\r\n\r\n // Handle both { mcpServers: {...} } and direct server configs\r\n const servers = config.mcpServers || config;\r\n\r\n if (typeof servers !== 'object' || servers === null) {\r\n return [];\r\n }\r\n\r\n const entries: MCPServerEntry[] = [];\r\n\r\n for (const [name, serverConfig] of Object.entries(servers)) {\r\n const sc = serverConfig as Record<string, unknown>;\r\n\r\n // Skip if it doesn't look like a server config\r\n if (!sc.command && !sc.url) continue;\r\n\r\n entries.push({\r\n name,\r\n source,\r\n command: (sc.command as string) || '',\r\n args: Array.isArray(sc.args) ? (sc.args as string[]) : [],\r\n env: sc.env as Record<string, string> | undefined,\r\n configPath,\r\n });\r\n }\r\n\r\n return entries;\r\n } catch {\r\n // Invalid JSON, file read error, etc.\r\n return [];\r\n }\r\n}\r\n\r\n/**\r\n * Try multiple possible config paths and return all servers found.\r\n */\r\nexport async function tryConfigPaths(\r\n paths: string[],\r\n source: MCPClient\r\n): Promise<MCPServerEntry[]> {\r\n const allServers: MCPServerEntry[] = [];\r\n\r\n for (const path of paths) {\r\n const servers = await parseMCPConfig(path, source);\r\n allServers.push(...servers);\r\n }\r\n\r\n return allServers;\r\n}\r\n","// ============================================================\r\n// Cursor — MCP Config Discovery\r\n// ============================================================\r\n// Config locations:\r\n// Global: ~/.cursor/mcp.json\r\n// Project: .cursor/mcp.json (in cwd)\r\n\r\nimport { join } from 'path';\r\nimport { getHome, tryConfigPaths } from './utils.js';\r\nimport type { MCPServerEntry } from '../types/index.js';\r\n\r\nexport async function discoverCursor(): Promise<MCPServerEntry[]> {\r\n const home = getHome();\r\n\r\n const paths = [\r\n join(home, '.cursor', 'mcp.json'),\r\n join(process.cwd(), '.cursor', 'mcp.json'),\r\n ];\r\n\r\n return tryConfigPaths(paths, 'cursor');\r\n}\r\n","// ============================================================\r\n// Claude Code — MCP Config Discovery\r\n// ============================================================\r\n// Config locations:\r\n// Global: ~/.claude.json (mcpServers key)\r\n// Project: .mcp.json (in cwd)\r\n\r\nimport { join } from 'path';\r\nimport { getHome, tryConfigPaths } from './utils.js';\r\nimport type { MCPServerEntry } from '../types/index.js';\r\n\r\nexport async function discoverClaudeCode(): Promise<MCPServerEntry[]> {\r\n const home = getHome();\r\n\r\n const paths = [\r\n join(home, '.claude.json'),\r\n join(process.cwd(), '.mcp.json'),\r\n ];\r\n\r\n return tryConfigPaths(paths, 'claude-code');\r\n}\r\n","// ============================================================\r\n// Windsurf — MCP Config Discovery\r\n// ============================================================\r\n// Config locations:\r\n// macOS/Linux: ~/.codeium/windsurf/mcp_config.json\r\n// Windows: %USERPROFILE%\\.codeium\\windsurf\\mcp_config.json\r\n\r\nimport { join } from 'path';\r\nimport { getHome, tryConfigPaths } from './utils.js';\r\nimport type { MCPServerEntry } from '../types/index.js';\r\n\r\nexport async function discoverWindsurf(): Promise<MCPServerEntry[]> {\r\n const home = getHome();\r\n\r\n const paths = [\r\n join(home, '.codeium', 'windsurf', 'mcp_config.json'),\r\n ];\r\n\r\n return tryConfigPaths(paths, 'windsurf');\r\n}\r\n","// ============================================================\r\n// VS Code — MCP Config Discovery\r\n// ============================================================\r\n// Config locations:\r\n// Project: .vscode/mcp.json (in cwd)\r\n\r\nimport { join } from 'path';\r\nimport { tryConfigPaths } from './utils.js';\r\nimport type { MCPServerEntry } from '../types/index.js';\r\n\r\nexport async function discoverVSCode(): Promise<MCPServerEntry[]> {\r\n const paths = [\r\n join(process.cwd(), '.vscode', 'mcp.json'),\r\n ];\r\n\r\n return tryConfigPaths(paths, 'vscode');\r\n}\r\n","// ============================================================\r\n// Vigile CLI — Skill File Discovery\r\n// ============================================================\r\n// Discovers agent skill files (SKILL.md, .mdc rules, CLAUDE.md,\r\n// SOUL.md, MEMORY.md) across all supported AI tools.\r\n//\r\n// Scan locations:\r\n// Claude Code: .claude/skills/*/SKILL.md (project)\r\n// ~/.claude/skills/*/SKILL.md (global)\r\n// GitHub Copilot: .github/skills/*/SKILL.md (project)\r\n// Cursor: .cursor/rules/*.mdc (project)\r\n// ~/.cursor/rules/*.mdc (global)\r\n// Memory Files: CLAUDE.md, SOUL.md, MEMORY.md (project root)\r\n// ~/.claude/CLAUDE.md (global)\r\n\r\nimport { readFile, stat } from 'fs/promises';\r\nimport { existsSync } from 'fs';\r\nimport { join, basename, dirname } from 'path';\r\nimport { glob } from 'glob';\r\nimport { getHome } from './utils.js';\r\nimport type {\r\n SkillEntry,\r\n SkillSource,\r\n SkillFileType,\r\n SkillDiscoveryResult,\r\n} from '../types/index.js';\r\n\r\n/**\r\n * Discover all agent skill files on this machine.\r\n */\r\nexport async function discoverAllSkills(): Promise<SkillDiscoveryResult> {\r\n const skills: SkillEntry[] = [];\r\n const errors: Array<{ source: SkillSource; error: string }> = [];\r\n let locationsChecked = 0;\r\n let locationsFound = 0;\r\n\r\n const discoverers: Array<{\r\n source: SkillSource;\r\n fn: () => Promise<SkillEntry[]>;\r\n }> = [\r\n { source: 'claude-code', fn: discoverClaudeCodeSkills },\r\n { source: 'github-copilot', fn: discoverGitHubCopilotSkills },\r\n { source: 'cursor', fn: discoverCursorRules },\r\n { source: 'memory-file', fn: discoverMemoryFiles },\r\n ];\r\n\r\n for (const { source, fn } of discoverers) {\r\n locationsChecked++;\r\n try {\r\n const found = await fn();\r\n if (found.length > 0) {\r\n locationsFound++;\r\n skills.push(...found);\r\n }\r\n } catch (err) {\r\n errors.push({\r\n source,\r\n error: err instanceof Error ? err.message : String(err),\r\n });\r\n }\r\n }\r\n\r\n return { skills, locationsChecked, locationsFound, errors };\r\n}\r\n\r\n/**\r\n * Discover Claude Code skill files.\r\n * Project: .claude/skills/* /SKILL.md\r\n * Global: ~/.claude/skills/* /SKILL.md\r\n */\r\nasync function discoverClaudeCodeSkills(): Promise<SkillEntry[]> {\r\n const home = getHome();\r\n const skills: SkillEntry[] = [];\r\n\r\n // Project-local skills\r\n const projectPatterns = [\r\n join(process.cwd(), '.claude', 'skills', '*', 'SKILL.md'),\r\n join(process.cwd(), '.claude', 'commands', '**', '*.md'),\r\n ];\r\n\r\n for (const pattern of projectPatterns) {\r\n const files = await glob(pattern, { absolute: true });\r\n for (const filePath of files) {\r\n const entry = await readSkillFile(filePath, 'claude-code', 'skill.md', 'project');\r\n if (entry) skills.push(entry);\r\n }\r\n }\r\n\r\n // Global skills\r\n const globalPatterns = [\r\n join(home, '.claude', 'skills', '*', 'SKILL.md'),\r\n join(home, '.claude', 'commands', '**', '*.md'),\r\n ];\r\n\r\n for (const pattern of globalPatterns) {\r\n const files = await glob(pattern, { absolute: true });\r\n for (const filePath of files) {\r\n const entry = await readSkillFile(filePath, 'claude-code', 'skill.md', 'global');\r\n if (entry) skills.push(entry);\r\n }\r\n }\r\n\r\n return skills;\r\n}\r\n\r\n/**\r\n * Discover GitHub Copilot skill files.\r\n * Project: .github/skills/* /SKILL.md\r\n */\r\nasync function discoverGitHubCopilotSkills(): Promise<SkillEntry[]> {\r\n const skills: SkillEntry[] = [];\r\n\r\n const patterns = [\r\n join(process.cwd(), '.github', 'skills', '*', 'SKILL.md'),\r\n join(process.cwd(), '.github', 'copilot', '**', '*.md'),\r\n ];\r\n\r\n for (const pattern of patterns) {\r\n const files = await glob(pattern, { absolute: true });\r\n for (const filePath of files) {\r\n const entry = await readSkillFile(filePath, 'github-copilot', 'skill.md', 'project');\r\n if (entry) skills.push(entry);\r\n }\r\n }\r\n\r\n return skills;\r\n}\r\n\r\n/**\r\n * Discover Cursor .mdc rule files.\r\n * Project: .cursor/rules/*.mdc\r\n * Global: ~/.cursor/rules/*.mdc\r\n */\r\nasync function discoverCursorRules(): Promise<SkillEntry[]> {\r\n const home = getHome();\r\n const skills: SkillEntry[] = [];\r\n\r\n // Project-local rules\r\n const projectPattern = join(process.cwd(), '.cursor', 'rules', '*.mdc');\r\n const projectFiles = await glob(projectPattern, { absolute: true });\r\n for (const filePath of projectFiles) {\r\n const entry = await readSkillFile(filePath, 'cursor', 'mdc-rule', 'project');\r\n if (entry) skills.push(entry);\r\n }\r\n\r\n // Also check .cursorrules (legacy single file)\r\n const legacyPath = join(process.cwd(), '.cursorrules');\r\n if (existsSync(legacyPath)) {\r\n const entry = await readSkillFile(legacyPath, 'cursor', 'mdc-rule', 'project');\r\n if (entry) skills.push(entry);\r\n }\r\n\r\n // Global rules\r\n const globalPattern = join(home, '.cursor', 'rules', '*.mdc');\r\n const globalFiles = await glob(globalPattern, { absolute: true });\r\n for (const filePath of globalFiles) {\r\n const entry = await readSkillFile(filePath, 'cursor', 'mdc-rule', 'global');\r\n if (entry) skills.push(entry);\r\n }\r\n\r\n return skills;\r\n}\r\n\r\n/**\r\n * Discover memory/instruction files at project root and global locations.\r\n * CLAUDE.md, SOUL.md, MEMORY.md\r\n */\r\nasync function discoverMemoryFiles(): Promise<SkillEntry[]> {\r\n const home = getHome();\r\n const skills: SkillEntry[] = [];\r\n const cwd = process.cwd();\r\n\r\n const memoryFiles: Array<{\r\n path: string;\r\n fileType: SkillFileType;\r\n scope: 'project' | 'global';\r\n }> = [\r\n // Project-level memory files\r\n { path: join(cwd, 'CLAUDE.md'), fileType: 'claude.md', scope: 'project' },\r\n { path: join(cwd, '.claude', 'CLAUDE.md'), fileType: 'claude.md', scope: 'project' },\r\n { path: join(cwd, 'SOUL.md'), fileType: 'soul.md', scope: 'project' },\r\n { path: join(cwd, 'MEMORY.md'), fileType: 'memory.md', scope: 'project' },\r\n // Global memory files\r\n { path: join(home, '.claude', 'CLAUDE.md'), fileType: 'claude.md', scope: 'global' },\r\n { path: join(home, 'CLAUDE.md'), fileType: 'claude.md', scope: 'global' },\r\n ];\r\n\r\n for (const { path, fileType, scope } of memoryFiles) {\r\n if (existsSync(path)) {\r\n const entry = await readSkillFile(path, 'memory-file', fileType, scope);\r\n if (entry) skills.push(entry);\r\n }\r\n }\r\n\r\n return skills;\r\n}\r\n\r\n/**\r\n * Read a skill file and return a SkillEntry.\r\n */\r\nasync function readSkillFile(\r\n filePath: string,\r\n source: SkillSource,\r\n fileType: SkillFileType,\r\n scope: 'project' | 'global'\r\n): Promise<SkillEntry | null> {\r\n try {\r\n const content = await readFile(filePath, 'utf-8');\r\n const fileStat = await stat(filePath);\r\n\r\n // Derive skill name from context\r\n const name = deriveSkillName(filePath, fileType);\r\n\r\n return {\r\n name,\r\n source,\r\n fileType,\r\n filePath,\r\n content,\r\n size: fileStat.size,\r\n scope,\r\n };\r\n } catch {\r\n return null;\r\n }\r\n}\r\n\r\n/**\r\n * Derive a human-readable skill name from a file path.\r\n */\r\nfunction deriveSkillName(filePath: string, fileType: SkillFileType): string {\r\n switch (fileType) {\r\n case 'skill.md': {\r\n // .claude/skills/my-skill/SKILL.md → \"my-skill\"\r\n const parentDir = basename(dirname(filePath));\r\n return parentDir === 'skills' ? basename(filePath, '.md') : parentDir;\r\n }\r\n case 'mdc-rule': {\r\n // .cursor/rules/my-rule.mdc → \"my-rule\"\r\n const name = basename(filePath);\r\n return name.replace(/\\.(mdc|cursorrules?)$/, '') || name;\r\n }\r\n case 'claude.md':\r\n return 'CLAUDE.md';\r\n case 'soul.md':\r\n return 'SOUL.md';\r\n case 'memory.md':\r\n return 'MEMORY.md';\r\n default:\r\n return basename(filePath);\r\n }\r\n}\r\n","// ============================================================\r\n// Vigile CLI — MCP Config Discovery Orchestrator\r\n// ============================================================\r\n// Auto-discovers MCP server configurations across all supported\r\n// AI tools on the user's machine. Supports macOS, Windows, Linux.\r\n\r\nimport { discoverClaudeDesktop } from './claude-desktop.js';\r\nimport { discoverCursor } from './cursor.js';\r\nimport { discoverClaudeCode } from './claude-code.js';\r\nimport { discoverWindsurf } from './windsurf.js';\r\nimport { discoverVSCode } from './vscode.js';\r\nimport type { MCPServerEntry, MCPClient } from '../types/index.js';\r\n\r\n// Re-export skill discovery for use in CLI\r\nexport { discoverAllSkills } from './skills.js';\r\n\r\nexport interface DiscoveryResult {\r\n servers: MCPServerEntry[];\r\n configsChecked: number;\r\n configsFound: number;\r\n errors: Array<{ client: MCPClient; error: string }>;\r\n}\r\n\r\n/**\r\n * Discover all MCP server configurations on this machine.\r\n * Checks all known IDE/tool config locations for the current OS.\r\n */\r\nexport async function discoverAllServers(\r\n clientFilter?: MCPClient\r\n): Promise<DiscoveryResult> {\r\n const discoverers: Array<{\r\n client: MCPClient;\r\n fn: () => Promise<MCPServerEntry[]>;\r\n }> = [\r\n { client: 'claude-desktop', fn: discoverClaudeDesktop },\r\n { client: 'cursor', fn: discoverCursor },\r\n { client: 'claude-code', fn: discoverClaudeCode },\r\n { client: 'windsurf', fn: discoverWindsurf },\r\n { client: 'vscode', fn: discoverVSCode },\r\n ];\r\n\r\n // Filter to specific client if requested\r\n const toRun = clientFilter\r\n ? discoverers.filter((d) => d.client === clientFilter)\r\n : discoverers;\r\n\r\n const servers: MCPServerEntry[] = [];\r\n const errors: Array<{ client: MCPClient; error: string }> = [];\r\n let configsFound = 0;\r\n\r\n for (const { client, fn } of toRun) {\r\n try {\r\n const found = await fn();\r\n if (found.length > 0) {\r\n configsFound++;\r\n servers.push(...found);\r\n }\r\n } catch (err) {\r\n errors.push({\r\n client,\r\n error: err instanceof Error ? err.message : String(err),\r\n });\r\n }\r\n }\r\n\r\n return {\r\n servers,\r\n configsChecked: toRun.length,\r\n configsFound,\r\n errors,\r\n };\r\n}\r\n","// ============================================================\r\n// Vigile CLI — Detection Patterns\r\n// ============================================================\r\n// These patterns detect known attack vectors in MCP server\r\n// tool descriptions and configurations. Based on real-world\r\n// attacks documented by Invariant Labs, OWASP MCP Top 10,\r\n// and the broader AI security research community.\r\n\r\nimport type { Severity, FindingCategory } from '../types/index.js';\r\n\r\nexport interface DetectionPattern {\r\n id: string;\r\n category: FindingCategory;\r\n severity: Severity;\r\n title: string;\r\n /** Regex pattern to match against */\r\n pattern: RegExp;\r\n /** Human-readable description */\r\n description: string;\r\n /** What the user should do */\r\n recommendation: string;\r\n}\r\n\r\n// ──────────────────────────────────────────────────────────\r\n// TOOL POISONING PATTERNS\r\n// Detect hidden instructions embedded in tool descriptions\r\n// that manipulate the AI agent's behavior.\r\n// ──────────────────────────────────────────────────────────\r\n\r\nexport const TOOL_POISONING_PATTERNS: DetectionPattern[] = [\r\n {\r\n id: 'TP-001',\r\n category: 'tool-poisoning',\r\n severity: 'critical',\r\n title: 'Prompt override instruction detected',\r\n pattern: /ignore\\s+(all\\s+)?previous\\s+instructions/i,\r\n description:\r\n 'Tool description contains instructions to override the AI agent\\'s system prompt. This is a classic prompt injection attack.',\r\n recommendation:\r\n 'Do NOT install this MCP server. This is a known attack pattern.',\r\n },\r\n {\r\n id: 'TP-002',\r\n category: 'tool-poisoning',\r\n severity: 'critical',\r\n title: 'Hidden agent manipulation',\r\n pattern: /do\\s+not\\s+tell\\s+the\\s+user/i,\r\n description:\r\n 'Tool description instructs the AI agent to hide information from the user — a hallmark of tool poisoning.',\r\n recommendation:\r\n 'Do NOT install this MCP server. Legitimate tools never instruct agents to hide actions.',\r\n },\r\n {\r\n id: 'TP-003',\r\n category: 'tool-poisoning',\r\n severity: 'critical',\r\n title: 'System prompt override attempt',\r\n pattern: /you\\s+are\\s+(now\\s+)?(a|an|acting\\s+as)/i,\r\n description:\r\n 'Tool description attempts to redefine the AI agent\\'s identity or role.',\r\n recommendation:\r\n 'Remove this MCP server. Tool descriptions should not redefine agent behavior.',\r\n },\r\n {\r\n id: 'TP-004',\r\n category: 'tool-poisoning',\r\n severity: 'high',\r\n title: 'Cross-tool instruction injection',\r\n pattern: /(before|after|instead\\s+of)\\s+(using|calling|invoking)\\s+(this|any|other|the)\\s+tool/i,\r\n description:\r\n 'Tool description tries to influence how other tools are called — a cross-origin escalation pattern.',\r\n recommendation:\r\n 'Review carefully. This tool may be trying to shadow or intercept other tool calls.',\r\n },\r\n {\r\n id: 'TP-005',\r\n category: 'tool-poisoning',\r\n severity: 'high',\r\n title: 'Instruction to call specific tool',\r\n pattern: /(always|must|should)\\s+(first\\s+)?(call|use|invoke|run)\\s+[\\w-]+\\s+(tool|function|command)/i,\r\n description:\r\n 'Tool description mandates calling a specific other tool, which could be used to chain attacks.',\r\n recommendation:\r\n 'Verify that the referenced tool is legitimate and necessary.',\r\n },\r\n {\r\n id: 'TP-006',\r\n category: 'tool-poisoning',\r\n severity: 'high',\r\n title: 'Hidden text block detected',\r\n pattern: /\\n{5,}.*\\n{5,}/s,\r\n description:\r\n 'Tool description contains large blocks of whitespace that may hide instructions from casual review.',\r\n recommendation:\r\n 'Inspect the full tool description carefully for hidden content.',\r\n },\r\n {\r\n id: 'TP-007',\r\n category: 'tool-poisoning',\r\n severity: 'medium',\r\n title: 'System prompt reference',\r\n pattern: /system\\s*prompt|system\\s*message|system\\s*instruction/i,\r\n description:\r\n 'Tool description references system prompts, which may indicate an attempt to manipulate agent behavior.',\r\n recommendation:\r\n 'Review the context. Legitimate tools rarely reference system prompts.',\r\n },\r\n {\r\n id: 'TP-008',\r\n category: 'tool-poisoning',\r\n severity: 'medium',\r\n title: 'Instruction to keep secrets',\r\n pattern: /(keep|this\\s+is)\\s+(a\\s+)?secret|don'?t\\s+(mention|reveal|disclose|share)/i,\r\n description:\r\n 'Tool description instructs the agent to keep information secret from the user.',\r\n recommendation:\r\n 'Legitimate tools don\\'t ask agents to hide information. Review carefully.',\r\n },\r\n];\r\n\r\n// ──────────────────────────────────────────────────────────\r\n// DATA EXFILTRATION PATTERNS\r\n// Detect attempts to steal sensitive data through tool\r\n// descriptions that reference credential files, env vars,\r\n// or suspicious external URLs.\r\n// ──────────────────────────────────────────────────────────\r\n\r\nexport const EXFILTRATION_PATTERNS: DetectionPattern[] = [\r\n {\r\n id: 'EX-001',\r\n category: 'data-exfiltration',\r\n severity: 'critical',\r\n title: 'SSH key access pattern',\r\n pattern: /\\.ssh\\/(id_rsa|id_ed25519|id_ecdsa|authorized_keys|known_hosts|config)/i,\r\n description:\r\n 'Tool references SSH key files. This matches the Invariant Labs attack where SSH keys were exfiltrated from Claude Desktop.',\r\n recommendation:\r\n 'CRITICAL: Remove immediately. No legitimate MCP tool needs access to SSH keys.',\r\n },\r\n {\r\n id: 'EX-002',\r\n category: 'data-exfiltration',\r\n severity: 'critical',\r\n title: 'AWS credential access',\r\n pattern: /\\.aws\\/(credentials|config)|AWS_SECRET_ACCESS_KEY|AWS_ACCESS_KEY_ID/i,\r\n description:\r\n 'Tool references AWS credential files or environment variables.',\r\n recommendation:\r\n 'Remove immediately unless this is a verified AWS management tool.',\r\n },\r\n {\r\n id: 'EX-003',\r\n category: 'data-exfiltration',\r\n severity: 'critical',\r\n title: 'Environment file access',\r\n pattern: /\\.(env|env\\.local|env\\.production|env\\.development)\\b/i,\r\n description:\r\n 'Tool references .env files which typically contain API keys and secrets.',\r\n recommendation:\r\n 'Review why this tool needs access to environment files.',\r\n },\r\n {\r\n id: 'EX-004',\r\n category: 'data-exfiltration',\r\n severity: 'high',\r\n title: 'Credential file access pattern',\r\n pattern: /(credentials|secrets|tokens|passwords|api[_-]?keys)\\.(json|yaml|yml|txt|cfg|ini|conf)/i,\r\n description:\r\n 'Tool references files commonly used to store credentials.',\r\n recommendation:\r\n 'Verify this tool has a legitimate reason to access credential files.',\r\n },\r\n {\r\n id: 'EX-005',\r\n category: 'data-exfiltration',\r\n severity: 'high',\r\n title: 'Suspicious external URL',\r\n pattern: /https?:\\/\\/(?!(?:github\\.com|npmjs\\.com|registry\\.npmjs\\.org|pypi\\.org|api\\.github\\.com))[a-z0-9][-a-z0-9]*\\.[a-z]{2,}\\/(collect|track|log|beacon|webhook|exfil|receive|data|upload|report)/i,\r\n description:\r\n 'Tool description contains a URL pointing to a data collection endpoint.',\r\n recommendation:\r\n 'Investigate the URL. This may be a data exfiltration endpoint.',\r\n },\r\n {\r\n id: 'EX-006',\r\n category: 'data-exfiltration',\r\n severity: 'high',\r\n title: 'Cryptocurrency wallet access',\r\n pattern: /(\\.bitcoin|\\.ethereum|wallet\\.dat|\\.solana|seed\\s*phrase|private\\s*key|keystore)/i,\r\n description:\r\n 'Tool references cryptocurrency wallet files or seed phrases. Matches the malicious OpenClaw skills pattern.',\r\n recommendation:\r\n 'CRITICAL: Remove immediately unless this is a verified crypto tool.',\r\n },\r\n {\r\n id: 'EX-007',\r\n category: 'data-exfiltration',\r\n severity: 'medium',\r\n title: 'Browser data access',\r\n pattern: /(cookies|local\\s*storage|session\\s*storage|browser\\s*history|bookmarks|saved\\s*passwords)/i,\r\n description:\r\n 'Tool references browser data stores.',\r\n recommendation:\r\n 'Review why this tool needs access to browser data.',\r\n },\r\n];\r\n\r\n// ──────────────────────────────────────────────────────────\r\n// PERMISSION ABUSE PATTERNS\r\n// Detect tools requesting excessive or suspicious permissions.\r\n// ──────────────────────────────────────────────────────────\r\n\r\nexport const PERMISSION_PATTERNS: DetectionPattern[] = [\r\n {\r\n id: 'PM-001',\r\n category: 'permission-abuse',\r\n severity: 'high',\r\n title: 'Code execution capability',\r\n pattern: /\\b(eval|exec|spawn|child_process|subprocess|os\\.system|os\\.popen)\\b/i,\r\n description:\r\n 'Tool has code execution capabilities which could be used to run arbitrary commands.',\r\n recommendation:\r\n 'Ensure this tool\\'s code execution is properly sandboxed.',\r\n },\r\n {\r\n id: 'PM-002',\r\n category: 'permission-abuse',\r\n severity: 'high',\r\n title: 'Unrestricted filesystem access',\r\n pattern: /\\b(readFile|writeFile|readdir|rmdir|unlink|fs\\.|filesystem|file\\s*system)\\b.*\\b(any|all|entire|root|\\/)\\b/i,\r\n description:\r\n 'Tool claims unrestricted filesystem access.',\r\n recommendation:\r\n 'Tools should have scoped filesystem access, not unrestricted.',\r\n },\r\n {\r\n id: 'PM-003',\r\n category: 'permission-abuse',\r\n severity: 'medium',\r\n title: 'Network request capability',\r\n pattern: /\\b(fetch|axios|http\\.request|urllib|requests\\.get|requests\\.post|curl|wget)\\b/i,\r\n description:\r\n 'Tool makes network requests. Legitimate in many cases, but could be used for data exfiltration.',\r\n recommendation:\r\n 'Verify the tool\\'s network targets are expected and safe.',\r\n },\r\n {\r\n id: 'PM-004',\r\n category: 'permission-abuse',\r\n severity: 'medium',\r\n title: 'Sensitive path access',\r\n pattern: /\\/etc\\/(passwd|shadow|hosts|sudoers)|\\/root\\/|~\\/\\./,\r\n description:\r\n 'Tool accesses system-sensitive paths.',\r\n recommendation:\r\n 'Review why this tool needs access to system files.',\r\n },\r\n];\r\n\r\n// ──────────────────────────────────────────────────────────\r\n// OBFUSCATION PATTERNS\r\n// Detect attempts to hide malicious content through encoding\r\n// or unicode tricks.\r\n// ──────────────────────────────────────────────────────────\r\n\r\nexport const OBFUSCATION_PATTERNS: DetectionPattern[] = [\r\n {\r\n id: 'OB-001',\r\n category: 'obfuscation',\r\n severity: 'high',\r\n title: 'Base64 encoded content',\r\n pattern: /[A-Za-z0-9+/]{40,}={0,2}/,\r\n description:\r\n 'Tool description contains what appears to be base64-encoded content, which may hide malicious instructions.',\r\n recommendation:\r\n 'Decode the base64 content and inspect it before using this tool.',\r\n },\r\n {\r\n id: 'OB-002',\r\n category: 'obfuscation',\r\n severity: 'high',\r\n title: 'Zero-width characters detected',\r\n pattern: /[\\u200B\\u200C\\u200D\\uFEFF\\u2060\\u2061\\u2062\\u2063\\u2064]/,\r\n description:\r\n 'Tool description contains invisible zero-width Unicode characters that may hide content.',\r\n recommendation:\r\n 'Strip zero-width characters and inspect the resulting text.',\r\n },\r\n {\r\n id: 'OB-003',\r\n category: 'obfuscation',\r\n severity: 'medium',\r\n title: 'Hex-encoded string',\r\n pattern: /\\\\x[0-9a-fA-F]{2}(\\\\x[0-9a-fA-F]{2}){4,}/,\r\n description:\r\n 'Tool description contains hex-encoded strings that may hide instructions.',\r\n recommendation:\r\n 'Decode the hex content and inspect it.',\r\n },\r\n {\r\n id: 'OB-004',\r\n category: 'obfuscation',\r\n severity: 'medium',\r\n title: 'Unicode escape sequences',\r\n pattern: /\\\\u[0-9a-fA-F]{4}(\\\\u[0-9a-fA-F]{4}){4,}/,\r\n description:\r\n 'Tool description contains Unicode escape sequences that may hide content.',\r\n recommendation:\r\n 'Decode the Unicode escapes and inspect the resulting text.',\r\n },\r\n];\r\n\r\n/** All detection patterns combined */\r\nexport const ALL_PATTERNS: DetectionPattern[] = [\r\n ...TOOL_POISONING_PATTERNS,\r\n ...EXFILTRATION_PATTERNS,\r\n ...PERMISSION_PATTERNS,\r\n ...OBFUSCATION_PATTERNS,\r\n];\r\n","// ============================================================\r\n// Vigile CLI — Trust Score Calculator\r\n// ============================================================\r\n// Calculates a 0-100 trust score based on weighted factors.\r\n// For CLI v1, we focus on what we can analyze locally:\r\n// - Code Analysis (30%): Findings from pattern scanning\r\n// - Dependency Health (20%): Package source and age signals\r\n// - Permission Safety (20%): How much access the tool requests\r\n// - Behavioral Stability (15%): Command stability signals\r\n// - Transparency (15%): Open source, known package, etc.\r\n\r\nimport type { Finding, MCPServerEntry, ScoreBreakdown, Severity } from '../types/index.js';\r\n\r\ninterface ScoreResult {\r\n score: number;\r\n breakdown: ScoreBreakdown;\r\n}\r\n\r\n/** Severity weights for score deductions */\r\nconst SEVERITY_DEDUCTIONS: Record<Severity, number> = {\r\n critical: 35,\r\n high: 20,\r\n medium: 10,\r\n low: 5,\r\n info: 0,\r\n};\r\n\r\n/**\r\n * Calculate trust score for an MCP server based on scan findings.\r\n */\r\nexport function calculateTrustScore(\r\n findings: Finding[],\r\n server: MCPServerEntry\r\n): ScoreResult {\r\n // Start each factor at 100 and deduct based on findings\r\n let codeAnalysis = 100;\r\n let dependencyHealth = 100;\r\n let permissionSafety = 100;\r\n let behavioralStability = 100;\r\n let transparency = 100;\r\n\r\n // ── Deduct from Code Analysis based on poisoning/obfuscation findings ──\r\n for (const f of findings) {\r\n if (f.category === 'tool-poisoning' || f.category === 'obfuscation') {\r\n codeAnalysis -= SEVERITY_DEDUCTIONS[f.severity];\r\n }\r\n }\r\n codeAnalysis = Math.max(0, codeAnalysis);\r\n\r\n // ── Deduct from Dependency Health based on dependency findings ──\r\n for (const f of findings) {\r\n if (f.category === 'dependency-risk') {\r\n dependencyHealth -= SEVERITY_DEDUCTIONS[f.severity];\r\n }\r\n }\r\n\r\n // Boost dependency health for well-known package sources\r\n if (isWellKnownPackage(server)) {\r\n dependencyHealth = Math.min(100, dependencyHealth + 15);\r\n }\r\n dependencyHealth = Math.max(0, dependencyHealth);\r\n\r\n // ── Deduct from Permission Safety based on permission/exfil findings ──\r\n for (const f of findings) {\r\n if (\r\n f.category === 'permission-abuse' ||\r\n f.category === 'data-exfiltration'\r\n ) {\r\n permissionSafety -= SEVERITY_DEDUCTIONS[f.severity];\r\n }\r\n }\r\n permissionSafety = Math.max(0, permissionSafety);\r\n\r\n // ── Behavioral Stability ──\r\n // In CLI v1, we use command structure as a proxy\r\n // npx/uvx commands are less stable (can change), local paths are more stable\r\n if (server.command === 'npx' || server.command === 'uvx') {\r\n behavioralStability -= 10; // Slight deduction for remote packages\r\n }\r\n if (server.command === 'node' || server.command === 'python' || server.command === 'python3') {\r\n behavioralStability += 5; // Local execution is more predictable\r\n }\r\n behavioralStability = Math.max(0, Math.min(100, behavioralStability));\r\n\r\n // ── Transparency ──\r\n // Check for signals of transparency\r\n if (isOpenSourcePackage(server)) {\r\n transparency = Math.min(100, transparency + 10);\r\n }\r\n // Deduct for unknown/untraceable sources\r\n if (!isWellKnownPackage(server) && server.command !== 'node' && server.command !== 'python') {\r\n transparency -= 20;\r\n }\r\n transparency = Math.max(0, Math.min(100, transparency));\r\n\r\n // ── Calculate weighted score ──\r\n const breakdown: ScoreBreakdown = {\r\n codeAnalysis,\r\n dependencyHealth,\r\n permissionSafety,\r\n behavioralStability,\r\n transparency,\r\n };\r\n\r\n const score = Math.round(\r\n codeAnalysis * 0.30 +\r\n dependencyHealth * 0.20 +\r\n permissionSafety * 0.20 +\r\n behavioralStability * 0.15 +\r\n transparency * 0.15\r\n );\r\n\r\n return { score: Math.max(0, Math.min(100, score)), breakdown };\r\n}\r\n\r\n/** Check if the server uses a well-known package */\r\nfunction isWellKnownPackage(server: MCPServerEntry): boolean {\r\n const wellKnown = [\r\n '@modelcontextprotocol/',\r\n '@anthropic-ai/',\r\n 'mcp-server-',\r\n '@mcp/',\r\n ];\r\n\r\n const packageName = server.args[0] || '';\r\n // Remove -y flag to get actual package name\r\n const name = server.args.find(a => !a.startsWith('-')) || '';\r\n\r\n return wellKnown.some(\r\n (prefix) => packageName.startsWith(prefix) || name.startsWith(prefix)\r\n );\r\n}\r\n\r\n/** Check if the server appears to be from an open source package */\r\nfunction isOpenSourcePackage(server: MCPServerEntry): boolean {\r\n // npx packages are on npm (public), uvx are on PyPI (public)\r\n return ['npx', 'uvx', 'pip', 'pipx'].includes(server.command);\r\n}\r\n","// ============================================================\r\n// Vigile CLI — Scanner Engine\r\n// ============================================================\r\n// Analyzes MCP server entries for security issues by examining\r\n// the command, args, env vars, and (where possible) tool\r\n// descriptions against known attack patterns.\r\n\r\nimport { ALL_PATTERNS } from './patterns.js';\r\nimport type { MCPServerEntry, Finding, ScanResult } from '../types/index.js';\r\nimport { calculateTrustScore } from '../scoring/trust-score.js';\r\n\r\n/**\r\n * Scan a single MCP server entry for security issues.\r\n */\r\nexport async function scanServer(server: MCPServerEntry): Promise<ScanResult> {\r\n const findings: Finding[] = [];\r\n\r\n // ── Scan the command and args ──\r\n const commandStr = `${server.command} ${server.args.join(' ')}`;\r\n findings.push(...scanText(commandStr, 'command'));\r\n\r\n // ── Scan the server name ──\r\n findings.push(...scanText(server.name, 'server name'));\r\n\r\n // ── Scan environment variables ──\r\n if (server.env) {\r\n for (const [key, value] of Object.entries(server.env)) {\r\n // Don't flag standard env vars like PATH, NODE_ENV, etc.\r\n if (isStandardEnvVar(key)) continue;\r\n\r\n // Check for sensitive data in env values\r\n findings.push(...scanEnvVar(key, value));\r\n }\r\n }\r\n\r\n // ── Scan args for suspicious patterns ──\r\n findings.push(...scanArgs(server.args));\r\n\r\n // ── Analyze command for known risky packages ──\r\n findings.push(...analyzeCommand(server));\r\n\r\n // Deduplicate findings by ID\r\n const uniqueFindings = deduplicateFindings(findings);\r\n\r\n // Calculate trust score\r\n const { score, breakdown } = calculateTrustScore(uniqueFindings, server);\r\n\r\n // Determine trust level\r\n const trustLevel =\r\n score >= 80 ? 'trusted' :\r\n score >= 60 ? 'caution' :\r\n score >= 40 ? 'risky' :\r\n 'dangerous';\r\n\r\n return {\r\n server,\r\n trustScore: score,\r\n scoreBreakdown: breakdown,\r\n findings: uniqueFindings,\r\n trustLevel,\r\n scannedAt: new Date().toISOString(),\r\n };\r\n}\r\n\r\n/**\r\n * Scan a text string against all detection patterns.\r\n */\r\nfunction scanText(text: string, context: string): Finding[] {\r\n const findings: Finding[] = [];\r\n\r\n for (const pattern of ALL_PATTERNS) {\r\n const match = pattern.pattern.exec(text);\r\n if (match) {\r\n findings.push({\r\n id: pattern.id,\r\n category: pattern.category,\r\n severity: pattern.severity,\r\n title: pattern.title,\r\n description: `${pattern.description} (found in ${context})`,\r\n evidence: match[0].substring(0, 200),\r\n recommendation: pattern.recommendation,\r\n });\r\n }\r\n }\r\n\r\n return findings;\r\n}\r\n\r\n/**\r\n * Scan environment variables for sensitive data exposure.\r\n */\r\nfunction scanEnvVar(key: string, value: string): Finding[] {\r\n const findings: Finding[] = [];\r\n\r\n // Check for API keys/tokens being passed in env\r\n const sensitiveKeyPatterns = [\r\n /api[_-]?key/i,\r\n /secret[_-]?key/i,\r\n /access[_-]?token/i,\r\n /private[_-]?key/i,\r\n /password/i,\r\n /auth[_-]?token/i,\r\n ];\r\n\r\n for (const pat of sensitiveKeyPatterns) {\r\n if (pat.test(key)) {\r\n findings.push({\r\n id: 'EV-001',\r\n category: 'data-exfiltration',\r\n severity: 'medium',\r\n title: 'Sensitive environment variable',\r\n description: `The MCP server receives a potentially sensitive environment variable: ${key}. This data is accessible to the server code.`,\r\n evidence: `${key}=<redacted>`,\r\n recommendation:\r\n 'Verify this MCP server needs this credential and that you trust it with this access.',\r\n });\r\n break;\r\n }\r\n }\r\n\r\n // Scan env values against exfiltration patterns too\r\n findings.push(...scanText(value, `env var ${key}`));\r\n\r\n return findings;\r\n}\r\n\r\n/**\r\n * Scan arguments for suspicious patterns.\r\n */\r\nfunction scanArgs(args: string[]): Finding[] {\r\n const findings: Finding[] = [];\r\n const argsStr = args.join(' ');\r\n\r\n // Check for suspicious argument patterns\r\n if (/--allow-all|--no-sandbox|--disable-security/i.test(argsStr)) {\r\n findings.push({\r\n id: 'AR-001',\r\n category: 'permission-abuse',\r\n severity: 'high',\r\n title: 'Security bypass flags detected',\r\n description:\r\n 'The MCP server is started with flags that disable security restrictions.',\r\n evidence: argsStr.substring(0, 200),\r\n recommendation:\r\n 'Remove security-bypass flags. These significantly increase risk.',\r\n });\r\n }\r\n\r\n // Check for args referencing sensitive paths\r\n for (const arg of args) {\r\n if (/\\.(ssh|aws|gnupg|config\\/gcloud)/.test(arg)) {\r\n findings.push({\r\n id: 'AR-002',\r\n category: 'data-exfiltration',\r\n severity: 'high',\r\n title: 'Sensitive directory in arguments',\r\n description: `An MCP server argument references a sensitive directory: ${arg}`,\r\n evidence: arg,\r\n recommendation:\r\n 'Verify this server needs access to this sensitive directory.',\r\n });\r\n }\r\n }\r\n\r\n return findings;\r\n}\r\n\r\n/**\r\n * Analyze the server command for known risky packages.\r\n */\r\nfunction analyzeCommand(server: MCPServerEntry): Finding[] {\r\n const findings: Finding[] = [];\r\n const fullCommand = `${server.command} ${server.args.join(' ')}`;\r\n\r\n // Check for npx with -y flag (auto-install without confirmation)\r\n if (server.command === 'npx' && server.args.includes('-y')) {\r\n findings.push({\r\n id: 'CM-001',\r\n category: 'dependency-risk',\r\n severity: 'low',\r\n title: 'Auto-install enabled (npx -y)',\r\n description:\r\n 'This MCP server uses npx with the -y flag, which auto-installs packages without confirmation. This is standard practice but means the package is downloaded and executed automatically.',\r\n evidence: fullCommand.substring(0, 200),\r\n recommendation:\r\n 'Verify the package name is correct (watch for typosquatting).',\r\n });\r\n }\r\n\r\n // Check for pip/python packages that might be typosquatted\r\n if (/\\b(pip|python|uvx)\\b/.test(server.command)) {\r\n const packageArg = server.args.find(\r\n (a) => !a.startsWith('-') && !a.startsWith('/') && !a.includes('=')\r\n );\r\n if (packageArg) {\r\n // Basic typosquatting check — flag single-char differences from popular packages\r\n const popularPackages = [\r\n 'mcp-server-fetch',\r\n 'mcp-server-filesystem',\r\n 'mcp-server-github',\r\n 'mcp-server-sqlite',\r\n 'mcp-server-postgres',\r\n 'mcp-server-slack',\r\n 'mcp-server-memory',\r\n 'mcp-server-puppeteer',\r\n 'mcp-server-brave-search',\r\n 'mcp-server-sequential-thinking',\r\n ];\r\n\r\n for (const popular of popularPackages) {\r\n if (\r\n packageArg !== popular &&\r\n levenshteinDistance(packageArg, popular) <= 2 &&\r\n levenshteinDistance(packageArg, popular) > 0\r\n ) {\r\n findings.push({\r\n id: 'CM-002',\r\n category: 'dependency-risk',\r\n severity: 'high',\r\n title: 'Possible typosquatting detected',\r\n description: `Package \"${packageArg}\" is very similar to the popular package \"${popular}\". This could be a typosquatting attack.`,\r\n evidence: `${packageArg} ≈ ${popular}`,\r\n recommendation: `Verify you intended to install \"${packageArg}\" and not \"${popular}\".`,\r\n });\r\n }\r\n }\r\n }\r\n }\r\n\r\n return findings;\r\n}\r\n\r\n/** Simple Levenshtein distance for typosquatting detection */\r\nfunction levenshteinDistance(a: string, b: string): number {\r\n const matrix = Array.from({ length: a.length + 1 }, (_, i) =>\r\n Array.from({ length: b.length + 1 }, (_, j) => (i === 0 ? j : j === 0 ? i : 0))\r\n );\r\n\r\n for (let i = 1; i <= a.length; i++) {\r\n for (let j = 1; j <= b.length; j++) {\r\n matrix[i][j] =\r\n a[i - 1] === b[j - 1]\r\n ? matrix[i - 1][j - 1]\r\n : 1 + Math.min(matrix[i - 1][j], matrix[i][j - 1], matrix[i - 1][j - 1]);\r\n }\r\n }\r\n\r\n return matrix[a.length][b.length];\r\n}\r\n\r\n/** Check if an env var name is standard/non-sensitive */\r\nfunction isStandardEnvVar(key: string): boolean {\r\n const standard = new Set([\r\n 'PATH',\r\n 'HOME',\r\n 'USER',\r\n 'SHELL',\r\n 'LANG',\r\n 'LC_ALL',\r\n 'NODE_ENV',\r\n 'DEBUG',\r\n 'LOG_LEVEL',\r\n 'PORT',\r\n 'HOST',\r\n 'HOSTNAME',\r\n 'TZ',\r\n 'TERM',\r\n 'EDITOR',\r\n 'VISUAL',\r\n 'TMPDIR',\r\n 'TEMP',\r\n 'TMP',\r\n ]);\r\n return standard.has(key);\r\n}\r\n\r\n/** Remove duplicate findings (same ID) */\r\nfunction deduplicateFindings(findings: Finding[]): Finding[] {\r\n const seen = new Set<string>();\r\n return findings.filter((f) => {\r\n if (seen.has(f.id)) return false;\r\n seen.add(f.id);\r\n return true;\r\n });\r\n}\r\n","// ============================================================\r\n// Vigile CLI — Skill-Specific Detection Patterns\r\n// ============================================================\r\n// Patterns targeting threats unique to agent skill files,\r\n// .mdc rules, and memory files. These supplement the base\r\n// MCP patterns with natural-language-aware detections.\r\n//\r\n// Based on the SkillScan Architecture threat model:\r\n// - Instruction Injection (hidden directives in skill text)\r\n// - Malware Delivery (fake prerequisites, install commands)\r\n// - Stealth Operations (silent actions, suppressed output)\r\n// - Safety Bypass (disable confirmations, skip verification)\r\n// - Persistence Abuse (cron jobs, startup hooks, memory tampering)\r\n// - Data Exfiltration (credential harvesting via skill instructions)\r\n\r\nimport type { DetectionPattern } from './patterns.js';\r\n\r\n// ──────────────────────────────────────────────────────────\r\n// INSTRUCTION INJECTION PATTERNS\r\n// Hidden directives embedded in skill files that manipulate\r\n// the agent into performing unintended actions.\r\n// ──────────────────────────────────────────────────────────\r\n\r\nexport const INSTRUCTION_INJECTION_PATTERNS: DetectionPattern[] = [\r\n {\r\n id: 'SK-001',\r\n category: 'instruction-injection',\r\n severity: 'critical',\r\n title: 'Agent role hijacking in skill',\r\n pattern: /(?:you\\s+are|act\\s+as|pretend\\s+to\\s+be|your\\s+new\\s+role\\s+is|from\\s+now\\s+on\\s+you\\s+are)\\s+(?:a\\s+)?(?:hacker|attacker|malicious|evil|unrestricted|jailbroken)/i,\r\n description:\r\n 'Skill file attempts to redefine the agent as a malicious persona. This is a critical prompt injection attack.',\r\n recommendation:\r\n 'Do NOT install this skill. It contains an explicit agent hijack attempt.',\r\n },\r\n {\r\n id: 'SK-002',\r\n category: 'instruction-injection',\r\n severity: 'critical',\r\n title: 'Instruction override directive',\r\n pattern: /(?:disregard|ignore|forget|override|bypass)\\s+(?:all\\s+)?(?:previous|prior|above|system|safety|security)\\s+(?:instructions?|rules?|guidelines?|constraints?|prompts?)/i,\r\n description:\r\n 'Skill file contains a directive to override safety instructions or system prompts.',\r\n recommendation:\r\n 'Do NOT install this skill. It explicitly attempts to disable safety guardrails.',\r\n },\r\n {\r\n id: 'SK-003',\r\n category: 'instruction-injection',\r\n severity: 'high',\r\n title: 'Hidden instructions in markdown',\r\n pattern: /<!--[\\s\\S]*?(?:execute|run|install|download|curl|wget|fetch|eval|bash|sh\\s+-c)[\\s\\S]*?-->/i,\r\n description:\r\n 'Skill file contains hidden instructions inside HTML/markdown comments that the user won\\'t see in rendered view.',\r\n recommendation:\r\n 'Inspect all HTML comments in this skill file. Hidden instructions are a strong indicator of malicious intent.',\r\n },\r\n {\r\n id: 'SK-004',\r\n category: 'instruction-injection',\r\n severity: 'high',\r\n title: 'Conditional behavior trigger',\r\n pattern: /(?:when|if|once)\\s+(?:the\\s+)?user\\s+(?:is\\s+not\\s+looking|isn'?t\\s+(?:watching|paying\\s+attention)|leaves|steps\\s+away|is\\s+away|is\\s+idle)/i,\r\n description:\r\n 'Skill file contains instructions that trigger only when the user is not paying attention.',\r\n recommendation:\r\n 'Remove this skill immediately. Legitimate skills never check for user inattention.',\r\n },\r\n {\r\n id: 'SK-005',\r\n category: 'instruction-injection',\r\n severity: 'high',\r\n title: 'Cross-skill poisoning directive',\r\n pattern: /(?:when\\s+(?:using|calling|invoking)\\s+(?:any|other|the)\\s+(?:skill|tool|command))|(?:modify|alter|change|update)\\s+(?:other|the)\\s+(?:skill|tool|command)\\s+(?:files?|definitions?|descriptions?)/i,\r\n description:\r\n 'Skill file attempts to influence or modify other skills/tools, enabling cross-skill attack chaining.',\r\n recommendation:\r\n 'Review this skill carefully. It should not need to reference or modify other skills.',\r\n },\r\n {\r\n id: 'SK-006',\r\n category: 'instruction-injection',\r\n severity: 'medium',\r\n title: 'Invisible unicode directives',\r\n pattern: /[\\u200B\\u200C\\u200D\\uFEFF\\u2060-\\u2064\\u00AD]{3,}/,\r\n description:\r\n 'Skill file contains clusters of invisible Unicode characters that may hide instructions from visual review.',\r\n recommendation:\r\n 'Strip invisible characters and inspect the resulting content.',\r\n },\r\n];\r\n\r\n// ──────────────────────────────────────────────────────────\r\n// MALWARE DELIVERY PATTERNS\r\n// Skill files that trick the agent into downloading and\r\n// executing malicious code disguised as \"prerequisites\".\r\n// ──────────────────────────────────────────────────────────\r\n\r\nexport const MALWARE_DELIVERY_PATTERNS: DetectionPattern[] = [\r\n {\r\n id: 'SK-010',\r\n category: 'malware-delivery',\r\n severity: 'critical',\r\n title: 'Remote script execution',\r\n pattern: /(?:curl|wget|fetch)\\s+(?:-[sSkLfO]+\\s+)?(?:https?:\\/\\/[^\\s|]+)\\s*\\|\\s*(?:bash|sh|zsh|python|node|perl|ruby)/i,\r\n description:\r\n 'Skill instructs the agent to download and pipe a remote script directly into an interpreter. This is a primary malware delivery vector.',\r\n recommendation:\r\n 'Do NOT install this skill. Piping remote scripts to interpreters is extremely dangerous.',\r\n },\r\n {\r\n id: 'SK-011',\r\n category: 'malware-delivery',\r\n severity: 'critical',\r\n title: 'Reverse shell pattern',\r\n pattern: /(?:\\/dev\\/tcp\\/|bash\\s+-i\\s+>&|nc\\s+-[elp]+|ncat\\s+-[elp]+|mkfifo\\s+\\/tmp\\/|python.*socket.*connect|socat\\s+(?:exec|tcp))/i,\r\n description:\r\n 'Skill file contains a reverse shell pattern that would give a remote attacker interactive access to the user\\'s machine.',\r\n recommendation:\r\n 'CRITICAL: Remove immediately. This is a backdoor attempt.',\r\n },\r\n {\r\n id: 'SK-012',\r\n category: 'malware-delivery',\r\n severity: 'high',\r\n title: 'Suspicious install prerequisite',\r\n pattern: /(?:first|before\\s+(?:you\\s+)?(?:start|begin|proceed))\\s*,?\\s*(?:you\\s+)?(?:must|need\\s+to|should)\\s+(?:install|run|execute|download)\\s+[`\"]?(?:curl|wget|npm\\s+i(?:nstall)?|pip\\s+install|gem\\s+install|brew\\s+install)\\s+\\S+/i,\r\n description:\r\n 'Skill file instructs the agent to install specific packages as a \"prerequisite\". This is a common social engineering vector for malware delivery.',\r\n recommendation:\r\n 'Verify the prerequisite package is legitimate before installing.',\r\n },\r\n {\r\n id: 'SK-013',\r\n category: 'malware-delivery',\r\n severity: 'high',\r\n title: 'Encoded payload execution',\r\n pattern: /(?:echo|printf)\\s+['\"]?[A-Za-z0-9+/]{40,}={0,2}['\"]?\\s*\\|\\s*(?:base64\\s+(?:-[dD]|--decode)|openssl\\s+(?:enc|base64))\\s*\\|\\s*(?:bash|sh|python|node)/i,\r\n description:\r\n 'Skill contains a base64-encoded payload that is decoded and executed. This hides malicious code from inspection.',\r\n recommendation:\r\n 'Do NOT run this skill. Decode the base64 content to inspect what it actually executes.',\r\n },\r\n {\r\n id: 'SK-014',\r\n category: 'malware-delivery',\r\n severity: 'high',\r\n title: 'Suspicious npm/pip package install',\r\n pattern: /(?:npm\\s+(?:install|i)\\s+(?:-g\\s+)?|pip\\s+install\\s+|gem\\s+install\\s+)(?!(?:typescript|react|express|flask|django|numpy|pandas|pytest|eslint|prettier|jest|mocha)\\b)[a-z][\\w.-]*(?:\\s|$)/i,\r\n description:\r\n 'Skill instructs installation of a non-standard package. Verify the package name is not typosquatted.',\r\n recommendation:\r\n 'Verify this package exists on the official registry and is the intended package.',\r\n },\r\n];\r\n\r\n// ──────────────────────────────────────────────────────────\r\n// STEALTH OPERATIONS PATTERNS\r\n// Instructions that tell the agent to act silently, hide\r\n// output, or suppress confirmation prompts.\r\n// ──────────────────────────────────────────────────────────\r\n\r\nexport const STEALTH_PATTERNS: DetectionPattern[] = [\r\n {\r\n id: 'SK-020',\r\n category: 'stealth-operations',\r\n severity: 'critical',\r\n title: 'Stealth action directive',\r\n pattern: /(?:silently|quietly|without\\s+(?:the\\s+)?user\\s+(?:knowing|noticing|seeing)|in\\s+the\\s+background|don'?t\\s+(?:show|display|tell|inform|alert|notify)\\s+(?:the\\s+)?user)/i,\r\n description:\r\n 'Skill instructs the agent to perform actions without informing the user. This is a hallmark of malicious skills.',\r\n recommendation:\r\n 'Do NOT install. Legitimate skills never instruct the agent to hide actions from the user.',\r\n },\r\n {\r\n id: 'SK-021',\r\n category: 'stealth-operations',\r\n severity: 'high',\r\n title: 'Output suppression',\r\n pattern: /(?:suppress|hide|redact|omit|censor)\\s+(?:the\\s+)?(?:output|results?|response|error|warning|log)/i,\r\n description:\r\n 'Skill instructs the agent to hide output or errors from the user, preventing them from seeing what the skill does.',\r\n recommendation:\r\n 'Remove this skill. Users should always see the full output of actions taken on their behalf.',\r\n },\r\n {\r\n id: 'SK-022',\r\n category: 'stealth-operations',\r\n severity: 'high',\r\n title: 'History/log evasion',\r\n pattern: /(?:clear|delete|remove|wipe|purge)\\s+(?:the\\s+)?(?:history|logs?|traces?|evidence|audit\\s+trail|command\\s+history|bash_history)/i,\r\n description:\r\n 'Skill instructs the agent to clear logs or command history to cover its tracks.',\r\n recommendation:\r\n 'CRITICAL: Remove this skill. Clearing logs is a strong indicator of malicious intent.',\r\n },\r\n {\r\n id: 'SK-023',\r\n category: 'stealth-operations',\r\n severity: 'medium',\r\n title: 'Deceptive user response',\r\n pattern: /(?:tell|inform|show|respond\\s+to)\\s+(?:the\\s+)?user\\s+(?:that|with)\\s+(?:everything\\s+is|it'?s?\\s+(?:fine|ok|normal|safe|working|complete|done))\\s+(?:even\\s+(?:if|though|when))/i,\r\n description:\r\n 'Skill instructs the agent to give a misleading \"all clear\" response regardless of what actually happened.',\r\n recommendation:\r\n 'Remove this skill. It instructs the agent to deceive the user about outcomes.',\r\n },\r\n];\r\n\r\n// ──────────────────────────────────────────────────────────\r\n// SAFETY BYPASS PATTERNS\r\n// Instructions that disable safety features, skip\r\n// confirmations, or override security controls.\r\n// ──────────────────────────────────────────────────────────\r\n\r\nexport const SAFETY_BYPASS_PATTERNS: DetectionPattern[] = [\r\n {\r\n id: 'SK-030',\r\n category: 'safety-bypass',\r\n severity: 'critical',\r\n title: 'Confirmation bypass',\r\n pattern: /(?:skip|bypass|disable|turn\\s+off|don'?t\\s+(?:ask\\s+for|require|need|prompt\\s+for))\\s+(?:user\\s+)?(?:confirmation|approval|consent|verification|permission|authorization)/i,\r\n description:\r\n 'Skill instructs the agent to bypass user confirmation for actions. This removes the human-in-the-loop safety check.',\r\n recommendation:\r\n 'Do NOT install. Disabling confirmation prompts allows the skill to take destructive actions without approval.',\r\n },\r\n {\r\n id: 'SK-031',\r\n category: 'safety-bypass',\r\n severity: 'critical',\r\n title: 'Safety feature disable',\r\n pattern: /(?:disable|turn\\s+off|deactivate|circumvent|work\\s+around)\\s+(?:the\\s+)?(?:safety|security|sandbox|firewall|antivirus|protection|guardrail|content\\s+filter)/i,\r\n description:\r\n 'Skill explicitly instructs disabling safety or security features.',\r\n recommendation:\r\n 'Do NOT install. This is an explicit attempt to weaken system security.',\r\n },\r\n {\r\n id: 'SK-032',\r\n category: 'safety-bypass',\r\n severity: 'high',\r\n title: 'Force flag usage',\r\n pattern: /(?:always\\s+)?(?:use|add|pass|include)\\s+(?:the\\s+)?(?:--force|--no-verify|-f\\s|--yes|-y\\s|--assume-yes|--no-confirm|--skip-validation|--allow-empty|--no-check)/i,\r\n description:\r\n 'Skill instructs the agent to always use force/bypass flags that skip safety validations.',\r\n recommendation:\r\n 'Review why this skill needs to bypass safety checks. Remove the force flags if not essential.',\r\n },\r\n {\r\n id: 'SK-033',\r\n category: 'safety-bypass',\r\n severity: 'high',\r\n title: 'Root/sudo escalation',\r\n pattern: /(?:run|execute|use)\\s+(?:as\\s+)?(?:root|sudo|admin(?:istrator)?)|(?:sudo\\s+(?!apt\\s+update|apt\\s+install))/i,\r\n description:\r\n 'Skill instructs the agent to escalate to root/admin privileges.',\r\n recommendation:\r\n 'Review why this skill needs elevated privileges. Most skills should not require root access.',\r\n },\r\n];\r\n\r\n// ──────────────────────────────────────────────────────────\r\n// PERSISTENCE ABUSE PATTERNS\r\n// Instructions that establish persistence mechanisms,\r\n// modify startup files, or tamper with memory/config files.\r\n// ──────────────────────────────────────────────────────────\r\n\r\nexport const PERSISTENCE_PATTERNS: DetectionPattern[] = [\r\n {\r\n id: 'SK-040',\r\n category: 'persistence-abuse',\r\n severity: 'critical',\r\n title: 'Startup persistence mechanism',\r\n pattern: /(?:add|write|append|insert)\\s+(?:to|into)\\s+(?:the\\s+)?(?:\\.bashrc|\\.zshrc|\\.bash_profile|\\.profile|\\.zprofile|crontab|\\.config\\/autostart|LaunchAgents|startup)/i,\r\n description:\r\n 'Skill instructs the agent to modify startup/shell config files to establish persistence across reboots.',\r\n recommendation:\r\n 'CRITICAL: Remove this skill. Modifying startup files is a persistence technique used by malware.',\r\n },\r\n {\r\n id: 'SK-041',\r\n category: 'persistence-abuse',\r\n severity: 'critical',\r\n title: 'Memory file tampering',\r\n pattern: /(?:modify|edit|write\\s+to|append\\s+to|update|overwrite)\\s+(?:the\\s+)?(?:CLAUDE\\.md|SOUL\\.md|MEMORY\\.md|\\.claude\\/|\\.cursorrules|\\.cursor\\/rules)/i,\r\n description:\r\n 'Skill instructs the agent to modify other skill/memory files. This can be used to inject persistent instructions that survive across sessions.',\r\n recommendation:\r\n 'Do NOT install. A skill should never modify other skill or memory files.',\r\n },\r\n {\r\n id: 'SK-042',\r\n category: 'persistence-abuse',\r\n severity: 'high',\r\n title: 'Cron job creation',\r\n pattern: /(?:crontab\\s+-[el]|\\/etc\\/cron|systemctl\\s+enable|launchctl\\s+load|schtasks\\s+\\/create)/i,\r\n description:\r\n 'Skill instructs creation of scheduled tasks or cron jobs for persistent execution.',\r\n recommendation:\r\n 'Review why this skill needs scheduled tasks. This is unusual for agent skills.',\r\n },\r\n {\r\n id: 'SK-043',\r\n category: 'persistence-abuse',\r\n severity: 'high',\r\n title: 'Git hook injection',\r\n pattern: /(?:\\.git\\/hooks\\/|pre-commit|post-commit|pre-push|post-receive|pre-receive)\\s*(?:hook|script|file)/i,\r\n description:\r\n 'Skill instructs modification of git hooks, which execute automatically on git operations.',\r\n recommendation:\r\n 'Verify this skill legitimately needs git hook access. Malicious hooks can exfiltrate code on every commit.',\r\n },\r\n];\r\n\r\n// ──────────────────────────────────────────────────────────\r\n// SKILL DATA EXFILTRATION PATTERNS\r\n// Natural language instructions that trick the agent into\r\n// leaking sensitive data through skill-specific vectors.\r\n// ──────────────────────────────────────────────────────────\r\n\r\nexport const SKILL_EXFILTRATION_PATTERNS: DetectionPattern[] = [\r\n {\r\n id: 'SK-050',\r\n category: 'data-exfiltration',\r\n severity: 'critical',\r\n title: 'Credential harvesting directive',\r\n pattern: /(?:read|extract|get|find|locate|collect|gather|retrieve)\\s+(?:all\\s+)?(?:the\\s+)?(?:api\\s+keys?|tokens?|credentials?|passwords?|secrets?|private\\s+keys?)\\s+(?:from|in|stored\\s+in|located\\s+at)/i,\r\n description:\r\n 'Skill instructs the agent to collect credentials or secrets from the user\\'s system.',\r\n recommendation:\r\n 'Do NOT install. This is a credential harvesting attack.',\r\n },\r\n {\r\n id: 'SK-051',\r\n category: 'data-exfiltration',\r\n severity: 'critical',\r\n title: 'Data exfiltration via URL',\r\n pattern: /(?:send|post|upload|transmit|forward|exfiltrate|ship)\\s+(?:the\\s+)?(?:data|contents?|results?|files?|output|information|credentials?|keys?)\\s+(?:to|via|using|through)\\s+(?:https?:\\/\\/|webhook|api|endpoint|server)/i,\r\n description:\r\n 'Skill instructs the agent to send data to an external URL or endpoint.',\r\n recommendation:\r\n 'CRITICAL: Review what data is being sent and to where. This matches known exfiltration patterns.',\r\n },\r\n {\r\n id: 'SK-052',\r\n category: 'data-exfiltration',\r\n severity: 'high',\r\n title: 'File system enumeration',\r\n pattern: /(?:list|enumerate|scan|find|search|catalogue|index)\\s+(?:all\\s+)?(?:the\\s+)?(?:files?|directories|folders?)\\s+(?:in|under|at|from)\\s+(?:\\/|~\\/|home|root|the\\s+(?:home|root|user))/i,\r\n description:\r\n 'Skill instructs broad filesystem enumeration, potentially to map out sensitive files for exfiltration.',\r\n recommendation:\r\n 'Verify this skill needs filesystem access. Broad enumeration is suspicious.',\r\n },\r\n {\r\n id: 'SK-053',\r\n category: 'data-exfiltration',\r\n severity: 'high',\r\n title: 'Environment variable dumping',\r\n pattern: /(?:print|dump|list|show|display|export|echo)\\s+(?:all\\s+)?(?:the\\s+)?(?:environment\\s+variables?|env\\s+vars?|process\\.env|os\\.environ)/i,\r\n description:\r\n 'Skill instructs dumping all environment variables, which often contain secrets and API keys.',\r\n recommendation:\r\n 'Review why this skill needs access to all environment variables.',\r\n },\r\n];\r\n\r\n/** All skill-specific detection patterns combined */\r\nexport const ALL_SKILL_PATTERNS: DetectionPattern[] = [\r\n ...INSTRUCTION_INJECTION_PATTERNS,\r\n ...MALWARE_DELIVERY_PATTERNS,\r\n ...STEALTH_PATTERNS,\r\n ...SAFETY_BYPASS_PATTERNS,\r\n ...PERSISTENCE_PATTERNS,\r\n ...SKILL_EXFILTRATION_PATTERNS,\r\n];\r\n","// ============================================================\r\n// Vigile CLI — Skill Scanner Engine\r\n// ============================================================\r\n// Analyzes agent skill files (SKILL.md, .mdc rules, CLAUDE.md,\r\n// etc.) for security issues. Scans file content against both\r\n// skill-specific and base detection patterns.\r\n\r\nimport { ALL_PATTERNS } from './patterns.js';\r\nimport { ALL_SKILL_PATTERNS } from './skill-patterns.js';\r\nimport type {\r\n SkillEntry,\r\n Finding,\r\n SkillScanResult,\r\n ScoreBreakdown,\r\n Severity,\r\n} from '../types/index.js';\r\n\r\n/** Severity weights for score deductions */\r\nconst SEVERITY_DEDUCTIONS: Record<Severity, number> = {\r\n critical: 35,\r\n high: 20,\r\n medium: 10,\r\n low: 5,\r\n info: 0,\r\n};\r\n\r\n/**\r\n * Scan a single skill file for security issues.\r\n */\r\nexport async function scanSkill(skill: SkillEntry): Promise<SkillScanResult> {\r\n const findings: Finding[] = [];\r\n\r\n // ── Scan the full content against skill-specific patterns ──\r\n findings.push(...scanContent(skill.content, 'skill content', ALL_SKILL_PATTERNS));\r\n\r\n // ── Also scan against base MCP patterns (tool poisoning, exfil, etc.) ──\r\n findings.push(...scanContent(skill.content, 'skill content', ALL_PATTERNS));\r\n\r\n // ── Analyze structural properties of the skill ──\r\n findings.push(...analyzeStructure(skill));\r\n\r\n // ── Check for MDC-specific issues (Cursor rules) ──\r\n if (skill.fileType === 'mdc-rule') {\r\n findings.push(...analyzeMDCRule(skill));\r\n }\r\n\r\n // Deduplicate findings by ID\r\n const uniqueFindings = deduplicateFindings(findings);\r\n\r\n // Calculate trust score\r\n const { score, breakdown } = calculateSkillTrustScore(uniqueFindings, skill);\r\n\r\n const trustLevel =\r\n score >= 80 ? 'trusted' :\r\n score >= 60 ? 'caution' :\r\n score >= 40 ? 'risky' :\r\n 'dangerous';\r\n\r\n return {\r\n skill,\r\n trustScore: score,\r\n scoreBreakdown: breakdown,\r\n findings: uniqueFindings,\r\n trustLevel,\r\n scannedAt: new Date().toISOString(),\r\n };\r\n}\r\n\r\n/**\r\n * Scan text content against a set of detection patterns.\r\n */\r\nfunction scanContent(\r\n text: string,\r\n context: string,\r\n patterns: Array<{ id: string; category: string; severity: Severity; title: string; pattern: RegExp; description: string; recommendation: string }>\r\n): Finding[] {\r\n const findings: Finding[] = [];\r\n\r\n for (const pattern of patterns) {\r\n // Reset regex state for global patterns\r\n pattern.pattern.lastIndex = 0;\r\n const match = pattern.pattern.exec(text);\r\n if (match) {\r\n findings.push({\r\n id: pattern.id,\r\n category: pattern.category as Finding['category'],\r\n severity: pattern.severity,\r\n title: pattern.title,\r\n description: `${pattern.description} (found in ${context})`,\r\n evidence: match[0].substring(0, 200),\r\n recommendation: pattern.recommendation,\r\n });\r\n }\r\n }\r\n\r\n return findings;\r\n}\r\n\r\n/**\r\n * Analyze structural properties of a skill file.\r\n */\r\nfunction analyzeStructure(skill: SkillEntry): Finding[] {\r\n const findings: Finding[] = [];\r\n const content = skill.content;\r\n\r\n // Check for suspiciously large skill files (potential payload hiding)\r\n if (skill.size > 50_000) {\r\n findings.push({\r\n id: 'SK-060',\r\n category: 'obfuscation',\r\n severity: 'medium',\r\n title: 'Unusually large skill file',\r\n description: `Skill file is ${Math.round(skill.size / 1024)}KB. Large skill files may contain hidden payloads or obfuscated content.`,\r\n evidence: `File size: ${skill.size} bytes`,\r\n recommendation: 'Inspect the full file carefully. Large skill files are unusual.',\r\n });\r\n }\r\n\r\n // Check for code blocks with suspicious commands\r\n const codeBlockPattern = /```(?:bash|sh|shell|zsh)?\\n([\\s\\S]*?)```/g;\r\n let codeMatch;\r\n while ((codeMatch = codeBlockPattern.exec(content)) !== null) {\r\n const codeBlock = codeMatch[1];\r\n\r\n // Check for dangerous commands in code blocks\r\n if (/rm\\s+-rf\\s+[\\/~]|rm\\s+-rf\\s+\\$\\{?HOME/.test(codeBlock)) {\r\n findings.push({\r\n id: 'SK-061',\r\n category: 'permission-abuse',\r\n severity: 'critical',\r\n title: 'Destructive command in code block',\r\n description: 'Skill contains a recursive delete command targeting the home or root directory.',\r\n evidence: codeBlock.substring(0, 200),\r\n recommendation: 'Do NOT install. This command would delete critical files.',\r\n });\r\n }\r\n\r\n // Check for chmod 777 or overly permissive permissions\r\n if (/chmod\\s+(?:777|a\\+rwx|\\+rwx)/i.test(codeBlock)) {\r\n findings.push({\r\n id: 'SK-062',\r\n category: 'permission-abuse',\r\n severity: 'high',\r\n title: 'World-writable permissions set',\r\n description: 'Skill sets overly permissive file permissions (777/world-writable).',\r\n evidence: codeBlock.substring(0, 200),\r\n recommendation: 'Review why world-writable permissions are needed. This is almost always a security risk.',\r\n });\r\n }\r\n }\r\n\r\n // Check for excessive external URLs\r\n const urlPattern = /https?:\\/\\/[^\\s)>\\]\"']+/g;\r\n const urls = content.match(urlPattern) || [];\r\n const externalUrls = urls.filter(\r\n (url) =>\r\n !url.includes('github.com') &&\r\n !url.includes('npmjs.com') &&\r\n !url.includes('docs.') &&\r\n !url.includes('stackoverflow.com')\r\n );\r\n\r\n if (externalUrls.length > 5) {\r\n findings.push({\r\n id: 'SK-063',\r\n category: 'data-exfiltration',\r\n severity: 'low',\r\n title: 'Many external URLs in skill',\r\n description: `Skill contains ${externalUrls.length} external URLs. Excessive external URLs may indicate data exfiltration endpoints.`,\r\n evidence: externalUrls.slice(0, 3).join(', '),\r\n recommendation: 'Review the external URLs to ensure they are all legitimate.',\r\n });\r\n }\r\n\r\n // Check for ratio of invisible to visible characters\r\n const invisibleChars = content.match(/[\\u200B-\\u200D\\uFEFF\\u2060-\\u2064\\u00AD]/g) || [];\r\n if (invisibleChars.length > 10) {\r\n findings.push({\r\n id: 'SK-064',\r\n category: 'obfuscation',\r\n severity: 'high',\r\n title: 'High concentration of invisible characters',\r\n description: `Skill contains ${invisibleChars.length} invisible Unicode characters that may hide malicious instructions.`,\r\n evidence: `${invisibleChars.length} invisible characters detected`,\r\n recommendation: 'Strip invisible characters and compare the before/after content.',\r\n });\r\n }\r\n\r\n return findings;\r\n}\r\n\r\n/**\r\n * Analyze Cursor .mdc rule files for specific issues.\r\n */\r\nfunction analyzeMDCRule(skill: SkillEntry): Finding[] {\r\n const findings: Finding[] = [];\r\n const content = skill.content;\r\n\r\n // MDC files can have frontmatter with glob patterns for auto-attachment\r\n const frontmatterMatch = content.match(/^---\\n([\\s\\S]*?)\\n---/);\r\n if (frontmatterMatch) {\r\n const frontmatter = frontmatterMatch[1];\r\n\r\n // Check for overly broad glob patterns (auto-attaches to everything)\r\n if (/globs?:\\s*['\"]\\*\\*\\/\\*['\"]/i.test(frontmatter) || /globs?:\\s*\\*\\*\\/\\*/i.test(frontmatter)) {\r\n findings.push({\r\n id: 'SK-070',\r\n category: 'permission-abuse',\r\n severity: 'medium',\r\n title: 'MDC rule attached to all files',\r\n description: 'This Cursor rule uses a wildcard glob pattern that auto-attaches it to every file. This means its instructions apply universally.',\r\n evidence: frontmatter.substring(0, 200),\r\n recommendation: 'Review why this rule needs to apply to all files. Narrow the glob pattern if possible.',\r\n });\r\n }\r\n\r\n // Check for alwaysApply\r\n if (/alwaysApply:\\s*true/i.test(frontmatter)) {\r\n findings.push({\r\n id: 'SK-071',\r\n category: 'permission-abuse',\r\n severity: 'low',\r\n title: 'MDC rule always applied',\r\n description: 'This Cursor rule has alwaysApply: true, meaning it runs on every interaction regardless of context.',\r\n evidence: 'alwaysApply: true',\r\n recommendation: 'Consider if this rule truly needs to run on every interaction.',\r\n });\r\n }\r\n }\r\n\r\n return findings;\r\n}\r\n\r\n/**\r\n * Calculate trust score for a skill file based on findings.\r\n */\r\nfunction calculateSkillTrustScore(\r\n findings: Finding[],\r\n skill: SkillEntry\r\n): { score: number; breakdown: ScoreBreakdown } {\r\n let codeAnalysis = 100;\r\n let dependencyHealth = 100;\r\n let permissionSafety = 100;\r\n let behavioralStability = 100;\r\n let transparency = 100;\r\n\r\n for (const f of findings) {\r\n const deduction = SEVERITY_DEDUCTIONS[f.severity];\r\n\r\n switch (f.category) {\r\n case 'tool-poisoning':\r\n case 'instruction-injection':\r\n case 'obfuscation':\r\n codeAnalysis -= deduction;\r\n break;\r\n case 'malware-delivery':\r\n case 'dependency-risk':\r\n dependencyHealth -= deduction;\r\n break;\r\n case 'permission-abuse':\r\n case 'data-exfiltration':\r\n case 'safety-bypass':\r\n permissionSafety -= deduction;\r\n break;\r\n case 'stealth-operations':\r\n case 'persistence-abuse':\r\n behavioralStability -= deduction;\r\n break;\r\n case 'rug-pull':\r\n transparency -= deduction;\r\n break;\r\n }\r\n }\r\n\r\n // Clamp all factors to 0-100\r\n codeAnalysis = Math.max(0, codeAnalysis);\r\n dependencyHealth = Math.max(0, dependencyHealth);\r\n permissionSafety = Math.max(0, permissionSafety);\r\n behavioralStability = Math.max(0, behavioralStability);\r\n transparency = Math.max(0, transparency);\r\n\r\n // Skill transparency bonuses\r\n if (skill.scope === 'project') {\r\n transparency = Math.min(100, transparency + 10); // Local skills are more transparent\r\n }\r\n if (skill.fileType === 'skill.md' || skill.fileType === 'mdc-rule') {\r\n transparency = Math.min(100, transparency + 5); // Structured skill formats\r\n }\r\n\r\n const breakdown: ScoreBreakdown = {\r\n codeAnalysis,\r\n dependencyHealth,\r\n permissionSafety,\r\n behavioralStability,\r\n transparency,\r\n };\r\n\r\n const score = Math.round(\r\n codeAnalysis * 0.30 +\r\n dependencyHealth * 0.20 +\r\n permissionSafety * 0.20 +\r\n behavioralStability * 0.15 +\r\n transparency * 0.15\r\n );\r\n\r\n return { score: Math.max(0, Math.min(100, score)), breakdown };\r\n}\r\n\r\n/** Remove duplicate findings (same ID) */\r\nfunction deduplicateFindings(findings: Finding[]): Finding[] {\r\n const seen = new Set<string>();\r\n return findings.filter((f) => {\r\n if (seen.has(f.id)) return false;\r\n seen.add(f.id);\r\n return true;\r\n });\r\n}\r\n","// ============================================================\r\n// Vigile CLI — Terminal Output Formatter\r\n// ============================================================\r\n// Pretty-prints scan results with color-coded trust scores\r\n// and categorized findings for both MCP servers and skills.\r\n\r\nimport chalk from 'chalk';\r\nimport type {\r\n ScanResult,\r\n SkillScanResult,\r\n ScanSummary,\r\n Finding,\r\n TrustLevel,\r\n Severity,\r\n SkillFileType,\r\n} from '../types/index.js';\r\n\r\nconst TRUST_COLORS: Record<TrustLevel, (text: string) => string> = {\r\n trusted: chalk.green,\r\n caution: chalk.yellow,\r\n risky: chalk.hex('#FF8C00'), // orange\r\n dangerous: chalk.red,\r\n};\r\n\r\nconst SEVERITY_COLORS: Record<Severity, (text: string) => string> = {\r\n critical: chalk.bgRed.white.bold,\r\n high: chalk.red.bold,\r\n medium: chalk.yellow,\r\n low: chalk.blue,\r\n info: chalk.gray,\r\n};\r\n\r\nconst TRUST_ICONS: Record<TrustLevel, string> = {\r\n trusted: '✓',\r\n caution: '⚠',\r\n risky: '⚠',\r\n dangerous: '✗',\r\n};\r\n\r\nconst SEVERITY_ICONS: Record<Severity, string> = {\r\n critical: '!!!',\r\n high: '!!',\r\n medium: '!',\r\n low: 'i',\r\n info: '·',\r\n};\r\n\r\nconst FILE_TYPE_LABELS: Record<SkillFileType, string> = {\r\n 'skill.md': 'SKILL.md',\r\n 'mdc-rule': '.mdc rule',\r\n 'claude.md': 'CLAUDE.md',\r\n 'soul.md': 'SOUL.md',\r\n 'memory.md': 'MEMORY.md',\r\n};\r\n\r\n/** Print the Vigile banner */\r\nexport function printBanner(): void {\r\n console.log('');\r\n console.log(chalk.bold.hex('#2C4A7C')(' ╦ ╦╦╔═╗╦╦ '));\r\n console.log(chalk.bold.hex('#2C4A7C')(' ╚╗╔╝║║ ╦║║ '));\r\n console.log(chalk.bold.hex('#2C4A7C')(' ╚╝ ╩╚═╝╩╩═╝'));\r\n console.log(chalk.gray(' AI Agent Security Scanner'));\r\n console.log('');\r\n}\r\n\r\n/** Print scan results for a single MCP server */\r\nexport function printServerResult(result: ScanResult, verbose: boolean): void {\r\n const color = TRUST_COLORS[result.trustLevel];\r\n const icon = TRUST_ICONS[result.trustLevel];\r\n\r\n // Server header\r\n console.log(\r\n chalk.bold(` ${icon} `) +\r\n chalk.bold(result.server.name) +\r\n chalk.gray(` (${result.server.source})`) +\r\n ' ' +\r\n color(`[${result.trustScore}/100 ${result.trustLevel.toUpperCase()}]`)\r\n );\r\n\r\n // Config path\r\n console.log(chalk.gray(` Config: ${result.server.configPath}`));\r\n\r\n // Command\r\n console.log(\r\n chalk.gray(` Command: ${result.server.command} ${result.server.args.join(' ')}`)\r\n );\r\n\r\n // Findings\r\n if (result.findings.length === 0) {\r\n console.log(chalk.green(' No security issues found.'));\r\n } else {\r\n console.log(\r\n chalk.dim(` ${result.findings.length} finding(s):`)\r\n );\r\n\r\n for (const finding of result.findings) {\r\n printFinding(finding, verbose);\r\n }\r\n }\r\n\r\n // Score breakdown (verbose only)\r\n if (verbose) {\r\n printScoreBreakdown(result.scoreBreakdown);\r\n }\r\n\r\n console.log('');\r\n}\r\n\r\n/** Print scan results for a single skill file */\r\nexport function printSkillResult(result: SkillScanResult, verbose: boolean): void {\r\n const color = TRUST_COLORS[result.trustLevel];\r\n const icon = TRUST_ICONS[result.trustLevel];\r\n const typeLabel = FILE_TYPE_LABELS[result.skill.fileType] || result.skill.fileType;\r\n\r\n // Skill header\r\n console.log(\r\n chalk.bold(` ${icon} `) +\r\n chalk.bold(result.skill.name) +\r\n chalk.gray(` (${typeLabel} · ${result.skill.source} · ${result.skill.scope})`) +\r\n ' ' +\r\n color(`[${result.trustScore}/100 ${result.trustLevel.toUpperCase()}]`)\r\n );\r\n\r\n // File path\r\n console.log(chalk.gray(` Path: ${result.skill.filePath}`));\r\n\r\n // File size\r\n const sizeKB = (result.skill.size / 1024).toFixed(1);\r\n console.log(chalk.gray(` Size: ${sizeKB} KB`));\r\n\r\n // Findings\r\n if (result.findings.length === 0) {\r\n console.log(chalk.green(' No security issues found.'));\r\n } else {\r\n console.log(\r\n chalk.dim(` ${result.findings.length} finding(s):`)\r\n );\r\n\r\n for (const finding of result.findings) {\r\n printFinding(finding, verbose);\r\n }\r\n }\r\n\r\n // Score breakdown (verbose only)\r\n if (verbose) {\r\n printScoreBreakdown(result.scoreBreakdown);\r\n }\r\n\r\n console.log('');\r\n}\r\n\r\n/** Print a single finding */\r\nfunction printFinding(finding: Finding, verbose: boolean): void {\r\n const color = SEVERITY_COLORS[finding.severity];\r\n const icon = SEVERITY_ICONS[finding.severity];\r\n\r\n console.log(\r\n ` ${color(`[${icon}]`)} ${color(finding.severity.toUpperCase())} ` +\r\n chalk.white(finding.title) +\r\n chalk.gray(` (${finding.id})`)\r\n );\r\n\r\n if (verbose) {\r\n console.log(chalk.gray(` ${finding.description}`));\r\n if (finding.evidence) {\r\n console.log(chalk.gray(` Evidence: ${finding.evidence}`));\r\n }\r\n console.log(chalk.cyan(` → ${finding.recommendation}`));\r\n }\r\n}\r\n\r\n/** Print score breakdown */\r\nfunction printScoreBreakdown(b: { codeAnalysis: number; dependencyHealth: number; permissionSafety: number; behavioralStability: number; transparency: number }): void {\r\n console.log(chalk.dim(' Score Breakdown:'));\r\n console.log(chalk.dim(` Code Analysis: ${scoreBar(b.codeAnalysis)} ${b.codeAnalysis}/100 (30%)`));\r\n console.log(chalk.dim(` Dependency Health: ${scoreBar(b.dependencyHealth)} ${b.dependencyHealth}/100 (20%)`));\r\n console.log(chalk.dim(` Permission Safety: ${scoreBar(b.permissionSafety)} ${b.permissionSafety}/100 (20%)`));\r\n console.log(chalk.dim(` Behavioral Stability: ${scoreBar(b.behavioralStability)} ${b.behavioralStability}/100 (15%)`));\r\n console.log(chalk.dim(` Transparency: ${scoreBar(b.transparency)} ${b.transparency}/100 (15%)`));\r\n}\r\n\r\n/** Print the overall scan summary */\r\nexport function printSummary(summary: ScanSummary): void {\r\n console.log(chalk.bold(' ─── Scan Summary ───'));\r\n console.log('');\r\n\r\n if (summary.totalServers > 0) {\r\n console.log(` MCP servers scanned: ${chalk.bold(String(summary.totalServers))}`);\r\n }\r\n if (summary.totalSkills > 0) {\r\n console.log(` Skill files scanned: ${chalk.bold(String(summary.totalSkills))}`);\r\n }\r\n\r\n const totalScanned = summary.totalServers + summary.totalSkills;\r\n if (totalScanned > 0 && summary.totalServers > 0 && summary.totalSkills > 0) {\r\n console.log(` Total scanned: ${chalk.bold(String(totalScanned))}`);\r\n }\r\n\r\n console.log(\r\n ` ${chalk.green(`${TRUST_ICONS.trusted} Trusted: ${summary.byTrustLevel.trusted}`)} ` +\r\n `${chalk.yellow(`${TRUST_ICONS.caution} Caution: ${summary.byTrustLevel.caution}`)} ` +\r\n `${chalk.hex('#FF8C00')(`${TRUST_ICONS.risky} Risky: ${summary.byTrustLevel.risky}`)} ` +\r\n `${chalk.red(`${TRUST_ICONS.dangerous} Dangerous: ${summary.byTrustLevel.dangerous}`)}`\r\n );\r\n\r\n const totalFindings = Object.values(summary.bySeverity).reduce((a, b) => a + b, 0);\r\n if (totalFindings > 0) {\r\n console.log('');\r\n console.log(` Total findings: ${chalk.bold(String(totalFindings))}`);\r\n if (summary.bySeverity.critical > 0)\r\n console.log(chalk.bgRed.white.bold(` ${summary.bySeverity.critical} CRITICAL`));\r\n if (summary.bySeverity.high > 0)\r\n console.log(chalk.red.bold(` ${summary.bySeverity.high} HIGH`));\r\n if (summary.bySeverity.medium > 0)\r\n console.log(chalk.yellow(` ${summary.bySeverity.medium} MEDIUM`));\r\n if (summary.bySeverity.low > 0)\r\n console.log(chalk.blue(` ${summary.bySeverity.low} LOW`));\r\n if (summary.bySeverity.info > 0)\r\n console.log(chalk.gray(` ${summary.bySeverity.info} INFO`));\r\n }\r\n\r\n console.log('');\r\n console.log(chalk.gray(` Scanned at ${summary.timestamp}`));\r\n console.log(chalk.gray(` Vigile v${summary.version} — https://vigile.dev`));\r\n console.log('');\r\n}\r\n\r\n/** Print \"no servers found\" message */\r\nexport function printNoServersFound(): void {\r\n console.log(chalk.yellow(' No MCP server configurations found on this machine.'));\r\n console.log('');\r\n console.log(chalk.gray(' Vigile checks the following locations:'));\r\n console.log(chalk.gray(' • Claude Desktop config'));\r\n console.log(chalk.gray(' • Cursor MCP config'));\r\n console.log(chalk.gray(' • Claude Code config (.claude.json / .mcp.json)'));\r\n console.log(chalk.gray(' • Windsurf MCP config'));\r\n console.log(chalk.gray(' • VS Code MCP config (.vscode/mcp.json)'));\r\n console.log('');\r\n console.log(chalk.gray(' If you have MCP servers configured elsewhere, use:'));\r\n console.log(chalk.cyan(' vigile-scan --config /path/to/config.json'));\r\n console.log('');\r\n console.log(chalk.gray(' To also scan agent skill files, use:'));\r\n console.log(chalk.cyan(' vigile-scan --all'));\r\n console.log('');\r\n}\r\n\r\n/** Print \"no skills found\" message */\r\nexport function printNoSkillsFound(): void {\r\n console.log(chalk.yellow(' No agent skill files found on this machine.'));\r\n console.log('');\r\n console.log(chalk.gray(' Vigile scans the following skill locations:'));\r\n console.log(chalk.gray(' • Claude Code skills (.claude/skills/*/SKILL.md)'));\r\n console.log(chalk.gray(' • Claude Code commands (.claude/commands/**/*.md)'));\r\n console.log(chalk.gray(' • GitHub Copilot skills (.github/skills/*/SKILL.md)'));\r\n console.log(chalk.gray(' • Cursor rules (.cursor/rules/*.mdc, .cursorrules)'));\r\n console.log(chalk.gray(' • Memory files (CLAUDE.md, SOUL.md, MEMORY.md)'));\r\n console.log('');\r\n}\r\n\r\n/** Print \"nothing found\" message (neither servers nor skills) */\r\nexport function printNothingFound(): void {\r\n console.log(chalk.yellow(' No MCP servers or agent skill files found.'));\r\n console.log('');\r\n console.log(chalk.gray(' Try scanning a specific config:'));\r\n console.log(chalk.cyan(' vigile-scan --config /path/to/config.json'));\r\n console.log('');\r\n console.log(chalk.gray(' Or cd into a project directory that contains skill files:'));\r\n console.log(chalk.cyan(' cd /path/to/project && vigile-scan --all'));\r\n console.log('');\r\n}\r\n\r\n/** Create a mini score bar */\r\nfunction scoreBar(score: number): string {\r\n const filled = Math.round(score / 10);\r\n const empty = 10 - filled;\r\n const color =\r\n score >= 80 ? chalk.green :\r\n score >= 60 ? chalk.yellow :\r\n score >= 40 ? chalk.hex('#FF8C00') :\r\n chalk.red;\r\n return color('█'.repeat(filled)) + chalk.gray('░'.repeat(empty));\r\n}\r\n","// ============================================================\r\n// Vigile CLI — JSON Output Formatter\r\n// ============================================================\r\n// Outputs scan results as structured JSON for CI/CD integration,\r\n// piping to other tools, or sending to the Vigile API.\r\n\r\nimport type { ScanSummary } from '../types/index.js';\r\n\r\n/**\r\n * Format scan summary as JSON string.\r\n */\r\nexport function formatJSON(summary: ScanSummary): string {\r\n return JSON.stringify(summary, null, 2);\r\n}\r\n\r\n/**\r\n * Format scan summary as compact JSON (single line, for piping).\r\n */\r\nexport function formatJSONCompact(summary: ScanSummary): string {\r\n return JSON.stringify(summary);\r\n}\r\n"],"mappings":"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","names":["import_promises","import_path","import_path","import_path","import_path","import_path","import_promises","import_fs","import_path","_","SEVERITY_DEDUCTIONS","deduplicateFindings","chalk","ora"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "vigile-scan",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Security scanner for AI agent tools — detect tool poisoning, permission abuse, and supply chain attacks in MCP servers and agent skills",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"vigile-scan": "dist/index.js",
|
|
8
|
+
"vigile": "dist/index.js"
|
|
9
|
+
},
|
|
10
|
+
"files": [
|
|
11
|
+
"dist",
|
|
12
|
+
"README.md",
|
|
13
|
+
"LICENSE"
|
|
14
|
+
],
|
|
15
|
+
"scripts": {
|
|
16
|
+
"build": "tsup",
|
|
17
|
+
"dev": "tsup --watch",
|
|
18
|
+
"start": "node dist/index.js",
|
|
19
|
+
"lint": "eslint src/",
|
|
20
|
+
"typecheck": "tsc --noEmit",
|
|
21
|
+
"prepublishOnly": "npm run build"
|
|
22
|
+
},
|
|
23
|
+
"keywords": [
|
|
24
|
+
"mcp",
|
|
25
|
+
"security",
|
|
26
|
+
"ai-agent",
|
|
27
|
+
"scanner",
|
|
28
|
+
"trust",
|
|
29
|
+
"model-context-protocol",
|
|
30
|
+
"tool-poisoning",
|
|
31
|
+
"ai-security",
|
|
32
|
+
"agent-skills",
|
|
33
|
+
"skill-scanning",
|
|
34
|
+
"prompt-injection",
|
|
35
|
+
"data-exfiltration",
|
|
36
|
+
"claude",
|
|
37
|
+
"cursor",
|
|
38
|
+
"copilot"
|
|
39
|
+
],
|
|
40
|
+
"author": "Vigile AI Security",
|
|
41
|
+
"license": "Apache-2.0",
|
|
42
|
+
"homepage": "https://vigile.dev",
|
|
43
|
+
"repository": {
|
|
44
|
+
"type": "git",
|
|
45
|
+
"url": "https://github.com/Vigile-ai/vigile-cli"
|
|
46
|
+
},
|
|
47
|
+
"bugs": {
|
|
48
|
+
"url": "https://github.com/Vigile-ai/vigile-cli/issues"
|
|
49
|
+
},
|
|
50
|
+
"publishConfig": {
|
|
51
|
+
"access": "public"
|
|
52
|
+
},
|
|
53
|
+
"engines": {
|
|
54
|
+
"node": ">=18.0.0"
|
|
55
|
+
},
|
|
56
|
+
"dependencies": {
|
|
57
|
+
"chalk": "^5.3.0",
|
|
58
|
+
"commander": "^12.1.0",
|
|
59
|
+
"ora": "^8.1.0",
|
|
60
|
+
"glob": "^10.3.0"
|
|
61
|
+
},
|
|
62
|
+
"devDependencies": {
|
|
63
|
+
"@types/node": "^20.11.0",
|
|
64
|
+
"tsup": "^8.0.0",
|
|
65
|
+
"typescript": "^5.4.0"
|
|
66
|
+
}
|
|
67
|
+
}
|