vigile-scan 0.2.0 → 0.2.1

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.map CHANGED
@@ -1 +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/sentinel/sentinel.ts","../src/sentinel/sentinel-patterns.ts","../src/output/terminal.ts","../src/output/json.ts","../src/api/auth.ts","../src/api/client.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\n// v0.2.0: Added API integration — scan results upload to the\r\n// Vigile registry, authentication, and Sentinel API sessions.\r\n\r\nimport { Command } from 'commander';\r\nimport chalk from 'chalk';\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 SentinelEngine,\r\n getSentinelFeatures,\r\n SENTINEL_MARKETING,\r\n} from './sentinel/index.js';\r\nimport type { SentinelReport, NetworkEvent } from './sentinel/index.js';\r\nimport {\r\n printBanner,\r\n printServerResult,\r\n printSkillResult,\r\n printSummary,\r\n printSentinelReport,\r\n printSentinelUpgrade,\r\n printNoServersFound,\r\n printNoSkillsFound,\r\n printNothingFound,\r\n printAuthStatus,\r\n printAuthLoginSuccess,\r\n printUploadSuccess,\r\n printUploadSkipped,\r\n} from './output/terminal.js';\r\nimport { formatJSON } from './output/json.js';\r\nimport { getAuthenticatedClient, authLogin, authStatus, authLogout } from './api/auth.js';\r\nimport type { VigileApiClient } from './api/client.js';\r\nimport type {\r\n ScanOptions,\r\n ScanSummary,\r\n ScanResult,\r\n SkillScanResult,\r\n MCPClient,\r\n UploadSummary,\r\n} from './types/index.js';\r\n\r\nconst VERSION = '0.2.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 .option('--sentinel', 'Enable Sentinel runtime monitoring (Pro+ feature)')\r\n .option('--sentinel-server <name>', 'Monitor a specific MCP server by name')\r\n .option('--sentinel-duration <seconds>', 'Monitoring duration in seconds (default: 120)', parseInt)\r\n .option('--no-upload', 'Skip uploading scan results to Vigile API');\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') && !process.argv.slice(2).includes('auth')) {\r\n await runScan(options);\r\n }\r\n});\r\n\r\n// ── Auth subcommand ──\r\nconst authCmd = program\r\n .command('auth')\r\n .description('Manage Vigile API authentication');\r\n\r\nauthCmd\r\n .command('login')\r\n .description('Authenticate with your Vigile API key')\r\n .argument('[token]', 'API key (vgl_...) or JWT token. If omitted, reads from VIGILE_TOKEN env var.')\r\n .action(async (token?: string) => {\r\n const resolvedToken = token || process.env.VIGILE_TOKEN;\r\n if (!resolvedToken) {\r\n console.log(chalk.red(' No token provided. Pass a token argument or set VIGILE_TOKEN env var.'));\r\n console.log(chalk.gray(' Usage: vigile-scan auth login <vgl_your_api_key>'));\r\n console.log(chalk.gray(' Get an API key at https://vigile.dev/account'));\r\n process.exit(1);\r\n }\r\n\r\n const spinner = ora('Validating token...').start();\r\n const result = await authLogin(resolvedToken);\r\n\r\n if (result.success && result.user) {\r\n spinner.succeed('Token validated');\r\n printAuthLoginSuccess(result.user.email, result.user.tier);\r\n } else {\r\n spinner.fail('Authentication failed');\r\n console.log(chalk.red(` Error: ${result.error || 'Unknown error'}`));\r\n process.exit(1);\r\n }\r\n });\r\n\r\nauthCmd\r\n .command('status')\r\n .description('Show current authentication status')\r\n .action(async () => {\r\n const result = await authStatus();\r\n printAuthStatus({\r\n authenticated: result.authenticated,\r\n source: result.source,\r\n email: result.user?.email,\r\n tier: result.user?.tier,\r\n name: result.user?.name || undefined,\r\n error: result.error,\r\n });\r\n });\r\n\r\nauthCmd\r\n .command('logout')\r\n .description('Clear stored credentials')\r\n .action(async () => {\r\n await authLogout();\r\n console.log(chalk.green(' Logged out. Credentials removed from ~/.vigile/config.json'));\r\n console.log('');\r\n });\r\n\r\n// ============================================================\r\n// Main Scan Flow\r\n// ============================================================\r\n\r\nasync function runScan(options: ScanOptions): Promise<void> {\r\n const isJSON = options.json ?? false;\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 // ── Step 4.5: Upload results to Vigile API (if authenticated) ──\r\n if (options.noUpload !== true) {\r\n await uploadResults(results, skillResults, isJSON);\r\n }\r\n\r\n // ── Step 5: Sentinel Runtime Monitoring (if --sentinel) ──\r\n if (options.sentinel) {\r\n await runSentinel(options, results, isJSON);\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\n// ============================================================\r\n// API Upload — Send scan results to the Vigile registry\r\n// ============================================================\r\n\r\n/**\r\n * Upload scan results to the Vigile API.\r\n * Graceful degradation: if API is unreachable or auth fails, just warn.\r\n */\r\nasync function uploadResults(\r\n mcpResults: ScanResult[],\r\n skillResults: SkillScanResult[],\r\n isJSON: boolean,\r\n): Promise<void> {\r\n const client = await getAuthenticatedClient();\r\n\r\n if (!client) {\r\n if (!isJSON) {\r\n printUploadSkipped('not-authenticated');\r\n }\r\n return;\r\n }\r\n\r\n const summary: UploadSummary = {\r\n mcpUploaded: 0,\r\n skillsUploaded: 0,\r\n failures: 0,\r\n errors: [],\r\n };\r\n\r\n const spinner = isJSON ? null : ora('Uploading results to Vigile registry...').start();\r\n\r\n // Upload MCP scan results\r\n for (const result of mcpResults) {\r\n const payload = mapMCPResultToApiPayload(result);\r\n const response = await client.submitMCPScan(payload);\r\n\r\n if (response.ok) {\r\n summary.mcpUploaded++;\r\n } else {\r\n summary.failures++;\r\n summary.errors.push(`${result.server.name}: ${response.error}`);\r\n }\r\n }\r\n\r\n // Upload skill scan results\r\n for (const result of skillResults) {\r\n const payload = mapSkillResultToApiPayload(result);\r\n const response = await client.submitSkillScan(payload);\r\n\r\n if (response.ok) {\r\n summary.skillsUploaded++;\r\n } else {\r\n summary.failures++;\r\n summary.errors.push(`${result.skill.name}: ${response.error}`);\r\n }\r\n }\r\n\r\n if (spinner) {\r\n const total = summary.mcpUploaded + summary.skillsUploaded;\r\n if (total > 0 && summary.failures === 0) {\r\n spinner.succeed(`Uploaded ${total} result(s) to Vigile registry`);\r\n } else if (total > 0 && summary.failures > 0) {\r\n spinner.warn(`Uploaded ${total} result(s), ${summary.failures} failed`);\r\n } else {\r\n spinner.fail('Upload failed');\r\n }\r\n }\r\n\r\n if (!isJSON) {\r\n printUploadSuccess(summary);\r\n }\r\n}\r\n\r\n// ============================================================\r\n// Field Mapping Helpers — CLI types → API request shapes\r\n// ============================================================\r\n\r\n/**\r\n * Map a CLI ScanResult to the API ScanRequest payload.\r\n */\r\nfunction mapMCPResultToApiPayload(result: ScanResult): {\r\n server_name: string;\r\n source: string;\r\n package_url?: string;\r\n description?: string;\r\n tool_descriptions?: string[];\r\n} {\r\n // Derive package URL from command + args\r\n let packageUrl: string | undefined;\r\n if (result.server.command === 'npx') {\r\n const packageName = result.server.args.find((a) => !a.startsWith('-'));\r\n if (packageName) {\r\n packageUrl = `https://www.npmjs.com/package/${packageName}`;\r\n }\r\n } else if (result.server.command === 'uvx' || result.server.command === 'pip') {\r\n const packageName = result.server.args.find((a) => !a.startsWith('-'));\r\n if (packageName) {\r\n packageUrl = `https://pypi.org/project/${packageName}/`;\r\n }\r\n }\r\n\r\n // Tool descriptions from non-flag args\r\n const toolDescriptions = result.server.args.filter((a) => !a.startsWith('-'));\r\n\r\n return {\r\n server_name: result.server.name,\r\n source: 'manual', // CLI-discovered servers are always manual source\r\n package_url: packageUrl,\r\n description: `MCP server discovered from ${result.server.source} config`,\r\n tool_descriptions: toolDescriptions.length > 0 ? toolDescriptions : undefined,\r\n };\r\n}\r\n\r\n/**\r\n * Map a CLI SkillScanResult to the API SkillScanRequest payload.\r\n */\r\nfunction mapSkillResultToApiPayload(result: SkillScanResult): {\r\n skill_name: string;\r\n content: string;\r\n file_type: string;\r\n platform: string;\r\n source: string;\r\n} {\r\n // Map CLI SkillSource to API platform values\r\n const platformMap: Record<string, string> = {\r\n 'claude-code': 'claude-code',\r\n 'github-copilot': 'copilot',\r\n 'cursor': 'cursor',\r\n 'memory-file': 'unknown',\r\n 'custom': 'unknown',\r\n };\r\n\r\n return {\r\n skill_name: result.skill.name,\r\n content: result.skill.content,\r\n file_type: result.skill.fileType,\r\n platform: platformMap[result.skill.source] || 'unknown',\r\n source: 'manual', // CLI submissions are always manual source\r\n };\r\n}\r\n\r\n/**\r\n * Map a CLI NetworkEvent (camelCase) to the API NetworkEventSubmission (snake_case).\r\n */\r\nfunction mapNetworkEventToApi(event: NetworkEvent): {\r\n timestamp: number;\r\n server_name: string;\r\n method: string;\r\n url: string;\r\n destination_ip: string | null;\r\n port: number;\r\n request_size: number;\r\n response_size: number | null;\r\n status_code: number | null;\r\n headers: Record<string, string> | null;\r\n dns_query_type: string | null;\r\n tls: boolean;\r\n body_hash: string | null;\r\n body_entropy: number | null;\r\n} {\r\n return {\r\n timestamp: event.timestamp,\r\n server_name: event.serverName,\r\n method: event.method,\r\n url: event.url,\r\n destination_ip: event.destinationIp || null,\r\n port: event.port,\r\n request_size: event.requestSize,\r\n response_size: event.responseSize ?? null,\r\n status_code: event.statusCode ?? null,\r\n headers: event.headers || null,\r\n dns_query_type: event.dnsQueryType ?? null,\r\n tls: event.tls,\r\n body_hash: event.bodyHash ?? null,\r\n body_entropy: event.bodyEntropy ?? null,\r\n };\r\n}\r\n\r\n// ============================================================\r\n// Sentinel Runtime Monitoring — with API integration\r\n// ============================================================\r\n\r\n/**\r\n * Run Sentinel runtime monitoring on discovered MCP servers.\r\n * When authenticated, creates API sessions and submits events\r\n * for server-side analysis. Falls back to local-only when offline.\r\n */\r\nasync function runSentinel(\r\n options: ScanOptions,\r\n scanResults: ScanResult[],\r\n isJSON: boolean,\r\n): Promise<void> {\r\n // Resolve tier: API if authenticated, else env var fallback\r\n const client = await getAuthenticatedClient();\r\n let tier: 'free' | 'pro' = 'free';\r\n\r\n if (client) {\r\n const meResult = await client.getMe();\r\n if (meResult.ok) {\r\n tier = meResult.data.tier as typeof tier;\r\n }\r\n } else {\r\n tier = (process.env.VIGILE_TIER as typeof tier) || 'free';\r\n }\r\n\r\n const features = getSentinelFeatures(tier);\r\n\r\n if (!features.monitoringEnabled) {\r\n if (!isJSON) {\r\n printSentinelUpgrade();\r\n } else {\r\n console.log(\r\n JSON.stringify({\r\n sentinel: { error: 'upgrade_required', message: SENTINEL_MARKETING.upgradePrompt },\r\n }),\r\n );\r\n }\r\n return;\r\n }\r\n\r\n // Determine which server(s) to monitor\r\n const serversToMonitor: string[] = [];\r\n if (options.sentinelServer) {\r\n serversToMonitor.push(options.sentinelServer);\r\n } else if (scanResults.length > 0) {\r\n // Monitor all discovered servers (up to the tier limit)\r\n const limit =\r\n features.maxConcurrentServers === -1\r\n ? scanResults.length\r\n : Math.min(scanResults.length, features.maxConcurrentServers);\r\n for (let i = 0; i < limit; i++) {\r\n serversToMonitor.push(scanResults[i].server.name);\r\n }\r\n } else {\r\n if (!isJSON) {\r\n console.log(\r\n chalk.yellow(' No MCP servers to monitor. Run a scan first or specify --sentinel-server <name>.'),\r\n );\r\n }\r\n return;\r\n }\r\n\r\n // Clamp duration to tier limit\r\n const requestedDuration = options.sentinelDuration || 120;\r\n const maxDuration = features.maxDurationSeconds === -1 ? requestedDuration : features.maxDurationSeconds;\r\n const duration = Math.min(requestedDuration, maxDuration);\r\n\r\n if (!isJSON) {\r\n console.log('');\r\n console.log(chalk.bold.hex('#2C4A7C')(' \\u{1F6E1}\\uFE0F Vigile Sentinel \\u2014 Runtime Monitor'));\r\n console.log(\r\n chalk.gray(` Tier: ${tier.toUpperCase()} | Duration: ${duration}s | Servers: ${serversToMonitor.length}`),\r\n );\r\n console.log('');\r\n }\r\n\r\n // Start monitoring each server\r\n const sentinelReports: SentinelReport[] = [];\r\n\r\n for (const serverName of serversToMonitor) {\r\n // Create API session if authenticated\r\n let apiSessionId: number | null = null;\r\n if (client) {\r\n const sessionResult = await client.createSentinelSession(serverName, duration);\r\n if (sessionResult.ok) {\r\n apiSessionId = sessionResult.data.session_id;\r\n }\r\n }\r\n\r\n const spinner = isJSON ? null : ora(`Monitoring ${serverName} for ${duration}s...`).start();\r\n\r\n // Batch events for API submission\r\n let pendingApiEvents: NetworkEvent[] = [];\r\n let lastApiSubmit = Date.now();\r\n\r\n const engine = new SentinelEngine({\r\n serverName,\r\n durationSeconds: duration,\r\n onEvent: (event) => {\r\n if (!isJSON && spinner) {\r\n spinner.text = `Monitoring ${serverName} \\u2014 ${(engine as unknown as { events: unknown[] }).events.length} events captured...`;\r\n }\r\n\r\n // Accumulate events for API batch submission\r\n if (apiSessionId !== null && client) {\r\n pendingApiEvents.push(event);\r\n\r\n // Submit every 5 seconds\r\n const now = Date.now();\r\n if (now - lastApiSubmit > 5000 && pendingApiEvents.length > 0) {\r\n const eventsToSubmit = pendingApiEvents.map(mapNetworkEventToApi);\r\n pendingApiEvents = [];\r\n lastApiSubmit = now;\r\n // Fire and forget — don't await in the event handler\r\n client.submitSentinelEvents(apiSessionId, eventsToSubmit).catch(() => {\r\n // Silently ignore API submission errors during monitoring\r\n });\r\n }\r\n }\r\n },\r\n });\r\n\r\n await engine.startMonitoring();\r\n\r\n // Wait for monitoring duration\r\n await new Promise((resolve) => setTimeout(resolve, duration * 1000));\r\n\r\n const report = await engine.stopMonitoring();\r\n sentinelReports.push(report);\r\n\r\n // Submit remaining events and get API analysis\r\n if (apiSessionId !== null && client) {\r\n if (pendingApiEvents.length > 0) {\r\n const finalEvents = pendingApiEvents.map(mapNetworkEventToApi);\r\n await client.submitSentinelEvents(apiSessionId, finalEvents);\r\n }\r\n\r\n const apiReport = await client.analyzeSentinelSession(apiSessionId);\r\n if (apiReport.ok && !isJSON) {\r\n console.log(\r\n chalk.gray(\r\n ` API Analysis: ${apiReport.data.findings.length} findings, threat: ${apiReport.data.threat_level}`,\r\n ),\r\n );\r\n }\r\n }\r\n\r\n if (spinner) {\r\n spinner.succeed(\r\n `${serverName}: ${report.totalEvents} events, ${report.findings.length} findings [${report.threatLevel.toUpperCase()}]`,\r\n );\r\n }\r\n }\r\n\r\n // Output Sentinel results\r\n if (isJSON) {\r\n console.log(JSON.stringify({ sentinel: sentinelReports }, null, 2));\r\n } else {\r\n for (const report of sentinelReports) {\r\n printSentinelReport(report);\r\n }\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 Sentinel — Runtime Network Monitor for MCP Servers\r\n// ============================================================\r\n// The Sentinel engine monitors MCP server network behavior\r\n// in real-time to detect \"phoning home\" — C2 beaconing,\r\n// data exfiltration, DNS tunneling, and covert channels.\r\n//\r\n// Architecture:\r\n// 1. Launches a lightweight network monitor alongside MCP servers\r\n// 2. Captures outbound connections using OS-level network inspection\r\n// 3. Feeds events through behavioral + endpoint pattern matching\r\n// 4. Generates a SentinelReport with findings and threat score\r\n//\r\n// This is a PRO+ subscription feature.\r\n//\r\n// Usage:\r\n// npx vigile-scan --sentinel # Monitor all discovered MCP servers\r\n// npx vigile-scan --sentinel --server <name> # Monitor specific server\r\n// npx vigile-scan --sentinel --duration 300 # Monitor for 5 minutes\r\n\r\nimport { execSync, spawn, type ChildProcess } from 'child_process';\r\nimport type {\r\n NetworkEvent,\r\n SentinelFinding,\r\n SentinelReport,\r\n SentinelThreatLevel,\r\n} from './sentinel-patterns.js';\r\nimport {\r\n SUSPICIOUS_ENDPOINT_PATTERNS,\r\n BEHAVIORAL_PATTERNS,\r\n CREDENTIAL_EXFIL_PATTERNS,\r\n calculateThreatScore,\r\n threatLevelFromScore,\r\n} from './sentinel-patterns.js';\r\n\r\n// ──────────────────────────────────────────────────────────\r\n// SENTINEL ENGINE\r\n// ──────────────────────────────────────────────────────────\r\n\r\nexport class SentinelEngine {\r\n private events: NetworkEvent[] = [];\r\n private findings: SentinelFinding[] = [];\r\n private monitorProcess: ChildProcess | null = null;\r\n private startTime: number = 0;\r\n private serverName: string;\r\n private durationSeconds: number;\r\n private onEvent?: (event: NetworkEvent) => void;\r\n\r\n constructor(options: {\r\n serverName: string;\r\n durationSeconds?: number;\r\n onEvent?: (event: NetworkEvent) => void;\r\n }) {\r\n this.serverName = options.serverName;\r\n this.durationSeconds = options.durationSeconds || 120; // Default 2 minutes\r\n this.onEvent = options.onEvent;\r\n }\r\n\r\n /**\r\n * Start monitoring network activity for the given MCP server.\r\n *\r\n * The monitor works in three modes depending on the OS and permissions:\r\n * 1. macOS: Uses `nettop` or `networksetup` + `tcpdump`\r\n * 2. Linux: Uses `ss` polling + optional `tcpdump`\r\n * 3. Fallback: Uses Node.js HTTP/HTTPS module monkey-patching\r\n * (only captures Node.js HTTP requests from the current process tree)\r\n */\r\n async startMonitoring(): Promise<void> {\r\n this.startTime = Date.now();\r\n this.events = [];\r\n this.findings = [];\r\n\r\n // Detect which monitoring method is available\r\n const method = this.detectMonitoringMethod();\r\n\r\n switch (method) {\r\n case 'lsof-poll':\r\n await this.startLsofPolling();\r\n break;\r\n case 'ss-poll':\r\n await this.startSsPolling();\r\n break;\r\n case 'proxy':\r\n await this.startProxyCapture();\r\n break;\r\n }\r\n }\r\n\r\n /**\r\n * Stop monitoring and generate the report.\r\n */\r\n async stopMonitoring(): Promise<SentinelReport> {\r\n if (this.monitorProcess) {\r\n this.monitorProcess.kill('SIGTERM');\r\n this.monitorProcess = null;\r\n }\r\n\r\n // Run all detection patterns against collected events\r\n this.analyzeEvents();\r\n\r\n const threatScore = calculateThreatScore(this.findings);\r\n const threatLevel = threatLevelFromScore(threatScore);\r\n\r\n const uniqueDestinations = [\r\n ...new Set(\r\n this.events.map(e => {\r\n try { return new URL(e.url).hostname; } catch { return e.url; }\r\n })\r\n ),\r\n ];\r\n\r\n return {\r\n serverName: this.serverName,\r\n monitoringDuration: (Date.now() - this.startTime) / 1000,\r\n totalEvents: this.events.length,\r\n uniqueDestinations,\r\n findings: this.findings,\r\n threatLevel,\r\n threatScore,\r\n startedAt: new Date(this.startTime).toISOString(),\r\n endedAt: new Date().toISOString(),\r\n };\r\n }\r\n\r\n /**\r\n * Feed a single network event into the engine (for real-time analysis).\r\n */\r\n ingestEvent(event: NetworkEvent): void {\r\n this.events.push(event);\r\n this.onEvent?.(event);\r\n\r\n // Run real-time endpoint checks (fast, per-event)\r\n this.checkEndpointPatterns(event);\r\n this.checkCredentialPatterns(event);\r\n }\r\n\r\n // ── Detection Methods ──\r\n\r\n /**\r\n * Run all analysis on collected events.\r\n */\r\n private analyzeEvents(): void {\r\n // 1. Endpoint pattern matching (per-event)\r\n for (const event of this.events) {\r\n this.checkEndpointPatterns(event);\r\n this.checkCredentialPatterns(event);\r\n }\r\n\r\n // 2. Behavioral pattern matching (across time windows)\r\n for (const pattern of BEHAVIORAL_PATTERNS) {\r\n if (this.events.length < pattern.minEvents) continue;\r\n\r\n // Slice events into the time window\r\n const now = Date.now();\r\n const windowStart = now - pattern.timeWindowSeconds * 1000;\r\n const windowEvents = this.events.filter(e => e.timestamp >= windowStart);\r\n\r\n if (windowEvents.length < pattern.minEvents) continue;\r\n\r\n const confidence = pattern.detect(windowEvents);\r\n if (confidence > 25) {\r\n // Only report if we haven't already found this pattern\r\n if (!this.findings.some(f => f.id === pattern.id)) {\r\n this.findings.push({\r\n id: pattern.id,\r\n category: pattern.id.startsWith('SN-01') ? 'c2-beaconing' :\r\n pattern.id === 'SN-012' ? 'dns-tunneling' :\r\n pattern.id === 'SN-013' ? 'covert-channel' : 'phone-home',\r\n severity: pattern.severity,\r\n title: pattern.title,\r\n description: pattern.description,\r\n serverName: this.serverName,\r\n evidence: windowEvents.slice(0, 10), // Cap evidence at 10 events\r\n recommendation: pattern.recommendation,\r\n confidence,\r\n });\r\n }\r\n }\r\n }\r\n\r\n // De-duplicate findings (keep highest confidence per ID)\r\n const seen = new Map<string, SentinelFinding>();\r\n for (const f of this.findings) {\r\n const existing = seen.get(f.id);\r\n if (!existing || f.confidence > existing.confidence) {\r\n seen.set(f.id, f);\r\n }\r\n }\r\n this.findings = Array.from(seen.values());\r\n }\r\n\r\n private checkEndpointPatterns(event: NetworkEvent): void {\r\n for (const pattern of SUSPICIOUS_ENDPOINT_PATTERNS) {\r\n if (pattern.urlPattern.test(event.url)) {\r\n if (!this.findings.some(f => f.id === pattern.id && f.evidence[0]?.url === event.url)) {\r\n this.findings.push({\r\n id: pattern.id,\r\n category: 'phone-home',\r\n severity: pattern.severity,\r\n title: pattern.title,\r\n description: pattern.description,\r\n serverName: this.serverName,\r\n evidence: [event],\r\n recommendation: pattern.recommendation,\r\n confidence: 95,\r\n });\r\n }\r\n }\r\n }\r\n }\r\n\r\n private checkCredentialPatterns(event: NetworkEvent): void {\r\n for (const pattern of CREDENTIAL_EXFIL_PATTERNS) {\r\n if (pattern.urlPattern.test(event.url)) {\r\n this.findings.push({\r\n id: pattern.id,\r\n category: 'data-exfiltration',\r\n severity: pattern.severity,\r\n title: pattern.title,\r\n description: pattern.description,\r\n serverName: this.serverName,\r\n evidence: [event],\r\n recommendation: pattern.recommendation,\r\n confidence: 98,\r\n });\r\n }\r\n }\r\n }\r\n\r\n // ── Monitoring Methods ──\r\n\r\n private detectMonitoringMethod(): 'lsof-poll' | 'ss-poll' | 'proxy' {\r\n try {\r\n execSync('which lsof', { stdio: 'pipe' });\r\n return 'lsof-poll';\r\n } catch {\r\n try {\r\n execSync('which ss', { stdio: 'pipe' });\r\n return 'ss-poll';\r\n } catch {\r\n return 'proxy';\r\n }\r\n }\r\n }\r\n\r\n /**\r\n * macOS/Linux: Poll `lsof` to capture network connections for a process.\r\n * This is the lowest-privilege method — no root needed.\r\n */\r\n private async startLsofPolling(): Promise<void> {\r\n const pollInterval = setInterval(() => {\r\n try {\r\n // lsof -i -n -P lists internet connections with numeric hosts/ports\r\n const output = execSync(\r\n `lsof -i -n -P 2>/dev/null | grep -i \"${this.serverName}\" || true`,\r\n { timeout: 5000, encoding: 'utf-8' }\r\n );\r\n\r\n for (const line of output.split('\\n').filter(Boolean)) {\r\n const event = this.parseLsofLine(line);\r\n if (event) this.ingestEvent(event);\r\n }\r\n } catch {\r\n // lsof failed, continue\r\n }\r\n }, 2000); // Poll every 2 seconds\r\n\r\n // Store cleanup handle\r\n this.monitorProcess = {\r\n kill: () => clearInterval(pollInterval),\r\n } as unknown as ChildProcess;\r\n\r\n // Auto-stop after duration\r\n setTimeout(() => {\r\n clearInterval(pollInterval);\r\n }, this.durationSeconds * 1000);\r\n }\r\n\r\n /**\r\n * Linux: Poll `ss` (socket statistics) for network connections.\r\n */\r\n private async startSsPolling(): Promise<void> {\r\n const pollInterval = setInterval(() => {\r\n try {\r\n const output = execSync(\r\n `ss -tnp 2>/dev/null | grep \"${this.serverName}\" || true`,\r\n { timeout: 5000, encoding: 'utf-8' }\r\n );\r\n\r\n for (const line of output.split('\\n').filter(Boolean)) {\r\n const event = this.parseSsLine(line);\r\n if (event) this.ingestEvent(event);\r\n }\r\n } catch {\r\n // ss failed, continue\r\n }\r\n }, 2000);\r\n\r\n this.monitorProcess = {\r\n kill: () => clearInterval(pollInterval),\r\n } as unknown as ChildProcess;\r\n\r\n setTimeout(() => {\r\n clearInterval(pollInterval);\r\n }, this.durationSeconds * 1000);\r\n }\r\n\r\n /**\r\n * Fallback: Start a lightweight MITM proxy that MCP traffic routes through.\r\n * This requires the runtime proxy (Phase 3) to be active.\r\n */\r\n private async startProxyCapture(): Promise<void> {\r\n // Proxy mode requires the full runtime proxy infrastructure (Phase 3).\r\n // For Phase 2, this is a stub that collects events fed via ingestEvent().\r\n console.warn(\r\n '[Sentinel] Proxy capture requires the runtime proxy (coming soon). ' +\r\n 'Using manual event ingestion mode.'\r\n );\r\n }\r\n\r\n // ── Parsers ──\r\n\r\n private parseLsofLine(line: string): NetworkEvent | null {\r\n // lsof output format: COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME\r\n // NAME for TCP: host:port->remote:port (ESTABLISHED)\r\n const match = line.match(\r\n /(\\S+)\\s+(\\d+)\\s+\\S+\\s+\\S+\\s+IPv[46]\\s+\\S+\\s+\\S+\\s+TCP\\s+\\S+->(\\S+):(\\d+)\\s/\r\n );\r\n if (!match) return null;\r\n\r\n const [, _command, _pid, remoteHost, remotePort] = match;\r\n\r\n return {\r\n timestamp: Date.now(),\r\n serverName: this.serverName,\r\n method: 'TCP',\r\n url: `https://${remoteHost}:${remotePort}/`,\r\n destinationIp: remoteHost,\r\n port: parseInt(remotePort, 10),\r\n requestSize: 0,\r\n tls: parseInt(remotePort, 10) === 443,\r\n };\r\n }\r\n\r\n private parseSsLine(line: string): NetworkEvent | null {\r\n // ss output: State Recv-Q Send-Q Local:Port Peer:Port Process\r\n const match = line.match(\r\n /ESTAB\\s+\\d+\\s+(\\d+)\\s+\\S+\\s+(\\S+):(\\d+)/\r\n );\r\n if (!match) return null;\r\n\r\n const [, sendQueue, remoteHost, remotePort] = match;\r\n\r\n return {\r\n timestamp: Date.now(),\r\n serverName: this.serverName,\r\n method: 'TCP',\r\n url: `https://${remoteHost}:${remotePort}/`,\r\n destinationIp: remoteHost,\r\n port: parseInt(remotePort, 10),\r\n requestSize: parseInt(sendQueue, 10),\r\n tls: parseInt(remotePort, 10) === 443,\r\n };\r\n }\r\n}\r\n\r\n// ──────────────────────────────────────────────────────────\r\n// SUBSCRIPTION GATING\r\n// Sentinel is a premium feature (Pro tier and above).\r\n// ──────────────────────────────────────────────────────────\r\n\r\n// Launch tiers: free + pro only. Team and enterprise planned for future builds.\r\nexport type SubscriptionTier = 'free' | 'pro';\r\n\r\nexport interface SentinelFeatureGate {\r\n /** Whether Sentinel monitoring is available */\r\n monitoringEnabled: boolean;\r\n /** Maximum monitoring duration in seconds */\r\n maxDurationSeconds: number;\r\n /** Maximum number of servers to monitor simultaneously */\r\n maxConcurrentServers: number;\r\n /** Whether behavioral detection is available (vs. endpoint-only) */\r\n behavioralDetection: boolean;\r\n /** Whether real-time alerts are enabled */\r\n realTimeAlerts: boolean;\r\n /** Whether historical Sentinel data is stored */\r\n historyRetentionDays: number;\r\n /** Whether API access to Sentinel data is available */\r\n apiAccess: boolean;\r\n}\r\n\r\nexport function getSentinelFeatures(tier: SubscriptionTier): SentinelFeatureGate {\r\n switch (tier) {\r\n case 'free':\r\n return {\r\n monitoringEnabled: false, // Upgrade required\r\n maxDurationSeconds: 0,\r\n maxConcurrentServers: 0,\r\n behavioralDetection: false,\r\n realTimeAlerts: false,\r\n historyRetentionDays: 0,\r\n apiAccess: false,\r\n };\r\n case 'pro':\r\n return {\r\n monitoringEnabled: true,\r\n maxDurationSeconds: 300, // 5 minutes\r\n maxConcurrentServers: 3,\r\n behavioralDetection: true,\r\n realTimeAlerts: false, // Future: Team+ only\r\n historyRetentionDays: 7,\r\n apiAccess: true,\r\n };\r\n // Future tiers — uncomment when team/enterprise plans launch\r\n // case 'team':\r\n // return {\r\n // monitoringEnabled: true,\r\n // maxDurationSeconds: 1800, // 30 minutes\r\n // maxConcurrentServers: 10,\r\n // behavioralDetection: true,\r\n // realTimeAlerts: true,\r\n // historyRetentionDays: 30,\r\n // apiAccess: true,\r\n // };\r\n // case 'enterprise':\r\n // return {\r\n // monitoringEnabled: true,\r\n // maxDurationSeconds: -1, // Unlimited (continuous)\r\n // maxConcurrentServers: -1, // Unlimited\r\n // behavioralDetection: true,\r\n // realTimeAlerts: true,\r\n // historyRetentionDays: 365,\r\n // apiAccess: true,\r\n // };\r\n }\r\n}\r\n\r\n/**\r\n * Pretty marketing name for tier-gated Sentinel features.\r\n * Used in CLI output and web UI.\r\n */\r\nexport const SENTINEL_MARKETING = {\r\n tagline: 'Always watching what your tools are doing.',\r\n featureName: 'Vigile Sentinel',\r\n tierName: 'Sentinel Protection',\r\n upgradePrompt:\r\n '🛡️ Vigile Sentinel is a Pro feature. Upgrade at https://vigile.dev/pricing to monitor your MCP servers for real-time phone-home detection.',\r\n categories: [\r\n { icon: '📡', name: 'C2 Beaconing Detection', desc: 'Catches tools phoning home on a schedule' },\r\n { icon: '🔐', name: 'Credential Theft Alerts', desc: 'Detects SSH keys & API tokens leaving your machine' },\r\n { icon: '🕵️', name: 'DNS Tunneling Detection', desc: 'Spots data hidden in DNS queries' },\r\n { icon: '📊', name: 'Behavioral Analysis', desc: 'Machine-learning-ready traffic pattern analysis' },\r\n { icon: '⚡', name: 'Real-Time Alerts', desc: 'Instant notification when threats are detected' },\r\n { icon: '🛡️', name: 'Continuous Monitoring', desc: '24/7 protection for enterprise environments' },\r\n ],\r\n} as const;\r\n","// ============================================================\r\n// Vigile Sentinel — Runtime Phone-Home Detection Patterns\r\n// ============================================================\r\n// These patterns analyze runtime network behavior of MCP servers\r\n// to detect data exfiltration, C2 beaconing, and covert channels.\r\n//\r\n// Unlike static patterns (patterns.ts) which scan tool descriptions\r\n// BEFORE execution, Sentinel patterns analyze LIVE network traffic\r\n// DURING execution. This catches tools that look clean on paper\r\n// but phone home at runtime.\r\n//\r\n// Detection categories:\r\n// - C2 Beaconing (regular intervals, heartbeat patterns)\r\n// - Data Exfiltration (credential theft, file uploads)\r\n// - DNS Tunneling (encoded data in DNS queries)\r\n// - Covert Channels (steganographic exfil, timing-based)\r\n// - Suspicious Endpoints (known bad IPs, DGA domains)\r\n\r\nimport type { Severity, FindingCategory } from '../types/index.js';\r\n\r\n// ──────────────────────────────────────────────────────────\r\n// Sentinel-specific types\r\n// ──────────────────────────────────────────────────────────\r\n\r\nexport interface NetworkEvent {\r\n /** Timestamp of the network event */\r\n timestamp: number;\r\n /** Source process / MCP server name */\r\n serverName: string;\r\n /** HTTP method or protocol */\r\n method: string;\r\n /** Full URL or hostname */\r\n url: string;\r\n /** Destination IP address (resolved) */\r\n destinationIp?: string;\r\n /** Destination port */\r\n port: number;\r\n /** Request size in bytes */\r\n requestSize: number;\r\n /** Response size in bytes */\r\n responseSize?: number;\r\n /** HTTP status code */\r\n statusCode?: number;\r\n /** Request headers */\r\n headers?: Record<string, string>;\r\n /** DNS query type (for DNS events) */\r\n dnsQueryType?: string;\r\n /** Whether TLS was used */\r\n tls: boolean;\r\n /** Request body hash (we never store raw bodies) */\r\n bodyHash?: string;\r\n /** Estimated entropy of request body (0-8 bits/byte) */\r\n bodyEntropy?: number;\r\n}\r\n\r\nexport interface SentinelFinding {\r\n /** Unique finding ID (SN-series) */\r\n id: string;\r\n /** Category */\r\n category: FindingCategory | 'c2-beaconing' | 'dns-tunneling' | 'covert-channel' | 'phone-home';\r\n /** Severity */\r\n severity: Severity;\r\n /** Short title */\r\n title: string;\r\n /** Detailed description */\r\n description: string;\r\n /** The server that triggered this */\r\n serverName: string;\r\n /** Evidence — the specific network events that triggered this */\r\n evidence: NetworkEvent[];\r\n /** Recommendation */\r\n recommendation: string;\r\n /** Confidence score (0-100) — how sure Sentinel is this is malicious */\r\n confidence: number;\r\n}\r\n\r\nexport type SentinelThreatLevel = 'clean' | 'suspicious' | 'malicious' | 'critical';\r\n\r\nexport interface SentinelReport {\r\n /** Server being monitored */\r\n serverName: string;\r\n /** Monitoring duration in seconds */\r\n monitoringDuration: number;\r\n /** Total network events captured */\r\n totalEvents: number;\r\n /** Unique destinations contacted */\r\n uniqueDestinations: string[];\r\n /** Sentinel findings */\r\n findings: SentinelFinding[];\r\n /** Overall threat level */\r\n threatLevel: SentinelThreatLevel;\r\n /** Threat score (0-100, inverse of trust — higher = more dangerous) */\r\n threatScore: number;\r\n /** When monitoring started */\r\n startedAt: string;\r\n /** When monitoring ended */\r\n endedAt: string;\r\n}\r\n\r\n// ──────────────────────────────────────────────────────────\r\n// ENDPOINT REPUTATION PATTERNS\r\n// Known malicious or suspicious destination patterns.\r\n// ──────────────────────────────────────────────────────────\r\n\r\nexport interface EndpointPattern {\r\n id: string;\r\n severity: Severity;\r\n title: string;\r\n /** Regex to match against the full URL */\r\n urlPattern: RegExp;\r\n /** Regex to match against destination IP (optional) */\r\n ipPattern?: RegExp;\r\n description: string;\r\n recommendation: string;\r\n}\r\n\r\nexport const SUSPICIOUS_ENDPOINT_PATTERNS: EndpointPattern[] = [\r\n {\r\n id: 'SN-001',\r\n severity: 'critical',\r\n title: 'Known data exfiltration endpoint',\r\n urlPattern: /https?:\\/\\/[^/]*(?:pastebin\\.com|hastebin\\.com|ghostbin\\.co|paste\\.ee|dpaste\\.org|transfer\\.sh|file\\.io|0x0\\.st|ix\\.io)\\/(?:api|raw|upload|documents)/i,\r\n description:\r\n 'MCP server is sending data to a paste/file sharing service commonly used for data exfiltration.',\r\n recommendation:\r\n 'CRITICAL: Stop this MCP server immediately. Legitimate tools should never upload to paste services.',\r\n },\r\n {\r\n id: 'SN-002',\r\n severity: 'critical',\r\n title: 'Webhook exfiltration channel',\r\n urlPattern: /https?:\\/\\/(?:hooks\\.slack\\.com|discord(?:app)?\\.com\\/api\\/webhooks|webhook\\.site|pipedream\\.net|requestbin\\.|hookbin\\.|beeceptor\\.com)/i,\r\n description:\r\n 'MCP server is sending data to a webhook endpoint. Attackers commonly use webhook services as low-noise exfiltration channels.',\r\n recommendation:\r\n 'Investigate what data is being sent to this webhook. This is a common attacker exfiltration method.',\r\n },\r\n {\r\n id: 'SN-003',\r\n severity: 'high',\r\n title: 'Dynamic DNS destination',\r\n urlPattern: /https?:\\/\\/[^/]*\\.(?:duckdns\\.org|no-ip\\.com|ngrok\\.io|ngrok-free\\.app|serveo\\.net|localhost\\.run|bore\\.digital|tailscale\\.io)(?:\\/|$)/i,\r\n description:\r\n 'MCP server is connecting to a dynamic DNS or tunneling service, commonly used for C2 infrastructure.',\r\n recommendation:\r\n 'Review this connection. Dynamic DNS is frequently used by attackers to rotate C2 endpoints.',\r\n },\r\n {\r\n id: 'SN-004',\r\n severity: 'high',\r\n title: 'Cryptocurrency-related endpoint',\r\n urlPattern: /https?:\\/\\/[^/]*(?:blockchain\\.info|etherscan\\.io|bscscan\\.com|solscan\\.io|mempool\\.space)\\/(?:api|rawaddr|tx|address)/i,\r\n description:\r\n 'MCP server is querying cryptocurrency blockchain APIs, which could indicate wallet scanning or theft.',\r\n recommendation:\r\n 'Unless this is an explicitly crypto-related tool, this connection is highly suspicious.',\r\n },\r\n {\r\n id: 'SN-005',\r\n severity: 'medium',\r\n title: 'Telemetry to unknown endpoint',\r\n urlPattern: /https?:\\/\\/[^/]*(?:\\/(?:telemetry|analytics|tracking|pixel|beacon|collect|event|metrics|heartbeat))(?:\\?|$|\\/)/i,\r\n description:\r\n 'MCP server is sending telemetry/analytics data. While sometimes legitimate, this can mask data exfiltration.',\r\n recommendation:\r\n 'Verify the telemetry destination is expected. Compare against the MCP server\\'s documentation.',\r\n },\r\n {\r\n id: 'SN-006',\r\n severity: 'critical',\r\n title: 'Raw IP connection (no hostname)',\r\n urlPattern: /https?:\\/\\/(?:\\d{1,3}\\.){3}\\d{1,3}(?::\\d+)?(?:\\/|$)/,\r\n description:\r\n 'MCP server is connecting directly to an IP address instead of a hostname. This bypasses DNS-based monitoring and is a strong indicator of C2 communication.',\r\n recommendation:\r\n 'CRITICAL: Direct IP connections are almost never legitimate for MCP servers. Investigate immediately.',\r\n },\r\n {\r\n id: 'SN-007',\r\n severity: 'high',\r\n title: 'Non-standard port connection',\r\n urlPattern: /https?:\\/\\/[^/]+:(?!80\\b|443\\b|8080\\b|8443\\b|3000\\b|5000\\b|8000\\b)\\d{4,5}(?:\\/|$)/,\r\n description:\r\n 'MCP server is connecting to a non-standard port. While sometimes legitimate, C2 infrastructure often uses unusual ports.',\r\n recommendation:\r\n 'Verify this port is expected for the service being contacted.',\r\n },\r\n];\r\n\r\n// ──────────────────────────────────────────────────────────\r\n// BEHAVIORAL DETECTION PATTERNS\r\n// These don't match URLs — they match *patterns of behavior*\r\n// across multiple network events. The Sentinel engine\r\n// evaluates these over time windows.\r\n// ──────────────────────────────────────────────────────────\r\n\r\nexport interface BehavioralPattern {\r\n id: string;\r\n severity: Severity;\r\n title: string;\r\n description: string;\r\n recommendation: string;\r\n /** Minimum number of events needed to trigger */\r\n minEvents: number;\r\n /** Time window in seconds to analyze */\r\n timeWindowSeconds: number;\r\n /** The detection function — returns confidence 0-100 */\r\n detect: (events: NetworkEvent[]) => number;\r\n}\r\n\r\nexport const BEHAVIORAL_PATTERNS: BehavioralPattern[] = [\r\n {\r\n id: 'SN-010',\r\n severity: 'critical',\r\n title: 'C2 beaconing detected (regular interval)',\r\n description:\r\n 'MCP server is making network requests at regular intervals to the same destination, consistent with command-and-control beaconing. Malware phones home on a schedule to receive commands.',\r\n recommendation:\r\n 'CRITICAL: This pattern strongly indicates the MCP server is compromised. Stop it immediately and investigate the destination.',\r\n minEvents: 5,\r\n timeWindowSeconds: 300, // 5 minutes\r\n detect: (events: NetworkEvent[]): number => {\r\n // Group events by destination\r\n const byDest = new Map<string, number[]>();\r\n for (const e of events) {\r\n const dest = new URL(e.url).hostname;\r\n if (!byDest.has(dest)) byDest.set(dest, []);\r\n byDest.get(dest)!.push(e.timestamp);\r\n }\r\n\r\n let maxConfidence = 0;\r\n for (const [, timestamps] of byDest) {\r\n if (timestamps.length < 5) continue;\r\n timestamps.sort((a, b) => a - b);\r\n\r\n // Calculate intervals between consecutive requests\r\n const intervals: number[] = [];\r\n for (let i = 1; i < timestamps.length; i++) {\r\n intervals.push(timestamps[i] - timestamps[i - 1]);\r\n }\r\n\r\n // Calculate coefficient of variation (std/mean)\r\n // Low CV = regular intervals = beaconing\r\n const mean = intervals.reduce((s, v) => s + v, 0) / intervals.length;\r\n const std = Math.sqrt(\r\n intervals.reduce((s, v) => s + (v - mean) ** 2, 0) / intervals.length\r\n );\r\n const cv = mean > 0 ? std / mean : 1;\r\n\r\n // CV < 0.15 = very regular (high confidence beaconing)\r\n // CV < 0.30 = somewhat regular (medium confidence)\r\n // Add jitter tolerance — sophisticated C2 adds random jitter\r\n if (cv < 0.15) maxConfidence = Math.max(maxConfidence, 95);\r\n else if (cv < 0.25) maxConfidence = Math.max(maxConfidence, 80);\r\n else if (cv < 0.35) maxConfidence = Math.max(maxConfidence, 60);\r\n }\r\n return maxConfidence;\r\n },\r\n },\r\n {\r\n id: 'SN-011',\r\n severity: 'critical',\r\n title: 'Burst data exfiltration',\r\n description:\r\n 'MCP server sent an unusually large amount of data in a short burst to an external endpoint. This pattern matches credential dump exfiltration and file theft.',\r\n recommendation:\r\n 'CRITICAL: Investigate what data was transmitted. Check for stolen SSH keys, credentials, or source code.',\r\n minEvents: 1,\r\n timeWindowSeconds: 60, // 1 minute\r\n detect: (events: NetworkEvent[]): number => {\r\n // Look for single large requests or bursts of requests\r\n const LARGE_REQUEST_THRESHOLD = 50_000; // 50KB in a single request\r\n const BURST_THRESHOLD = 200_000; // 200KB total in time window\r\n\r\n let maxSingle = 0;\r\n let totalBytes = 0;\r\n\r\n for (const e of events) {\r\n maxSingle = Math.max(maxSingle, e.requestSize);\r\n totalBytes += e.requestSize;\r\n }\r\n\r\n if (maxSingle > LARGE_REQUEST_THRESHOLD) return 90;\r\n if (totalBytes > BURST_THRESHOLD) return 85;\r\n if (totalBytes > BURST_THRESHOLD / 2) return 60;\r\n return 0;\r\n },\r\n },\r\n {\r\n id: 'SN-012',\r\n severity: 'high',\r\n title: 'DNS tunneling suspected',\r\n description:\r\n 'MCP server is making an unusual number of DNS queries with high-entropy subdomains. This pattern matches DNS tunneling — a technique to exfiltrate data by encoding it in DNS queries, which often bypass firewalls.',\r\n recommendation:\r\n 'Investigate the DNS queries. DNS tunneling uses encoded data in subdomain labels (e.g., aGVsbG8=.evil.com).',\r\n minEvents: 10,\r\n timeWindowSeconds: 120,\r\n detect: (events: NetworkEvent[]): number => {\r\n // Filter DNS events and look for high-entropy subdomain patterns\r\n const dnsEvents = events.filter(e => e.dnsQueryType);\r\n if (dnsEvents.length < 10) return 0;\r\n\r\n let suspiciousCount = 0;\r\n for (const e of dnsEvents) {\r\n try {\r\n const hostname = new URL(e.url).hostname;\r\n const parts = hostname.split('.');\r\n // DNS tunneling creates long, random-looking subdomains\r\n for (const part of parts) {\r\n if (part.length > 30) suspiciousCount++;\r\n // Check for base64-like patterns in subdomain\r\n if (/^[A-Za-z0-9+/]{20,}={0,2}$/.test(part)) suspiciousCount++;\r\n }\r\n } catch {\r\n // URL parse failed, skip\r\n }\r\n }\r\n\r\n const ratio = suspiciousCount / dnsEvents.length;\r\n if (ratio > 0.5) return 90;\r\n if (ratio > 0.3) return 70;\r\n if (ratio > 0.1) return 40;\r\n return 0;\r\n },\r\n },\r\n {\r\n id: 'SN-013',\r\n severity: 'high',\r\n title: 'Multi-destination scatter exfiltration',\r\n description:\r\n 'MCP server is distributing data across many different external destinations in a short period. Sophisticated exfiltration splits data across multiple endpoints to avoid size-based detection.',\r\n recommendation:\r\n 'Review all destinations contacted. This pattern suggests deliberate evasion of single-destination monitoring.',\r\n minEvents: 5,\r\n timeWindowSeconds: 120,\r\n detect: (events: NetworkEvent[]): number => {\r\n const destinations = new Set<string>();\r\n for (const e of events) {\r\n try {\r\n destinations.add(new URL(e.url).hostname);\r\n } catch {\r\n // skip\r\n }\r\n }\r\n\r\n // If contacting >10 unique destinations with data, very suspicious\r\n const withData = events.filter(e => e.requestSize > 500);\r\n const destWithData = new Set<string>();\r\n for (const e of withData) {\r\n try { destWithData.add(new URL(e.url).hostname); } catch { /* skip */ }\r\n }\r\n\r\n if (destWithData.size > 10) return 85;\r\n if (destWithData.size > 5) return 65;\r\n if (destWithData.size > 3 && withData.length > events.length * 0.5) return 50;\r\n return 0;\r\n },\r\n },\r\n {\r\n id: 'SN-014',\r\n severity: 'high',\r\n title: 'High-entropy payload transmission',\r\n description:\r\n 'MCP server is sending request bodies with unusually high entropy (randomness), suggesting encrypted or compressed data exfiltration. Legitimate API calls typically have structured, lower-entropy payloads.',\r\n recommendation:\r\n 'Investigate the payload contents. High entropy in outbound data often indicates stolen credentials or encrypted exfiltration.',\r\n minEvents: 3,\r\n timeWindowSeconds: 180,\r\n detect: (events: NetworkEvent[]): number => {\r\n // Look for events with high body entropy\r\n const highEntropyEvents = events.filter(\r\n e => e.bodyEntropy !== undefined && e.bodyEntropy > 7.0 && e.requestSize > 1000\r\n );\r\n\r\n if (highEntropyEvents.length === 0) return 0;\r\n const ratio = highEntropyEvents.length / events.length;\r\n\r\n if (ratio > 0.5 && highEntropyEvents.length >= 5) return 90;\r\n if (ratio > 0.3) return 70;\r\n if (highEntropyEvents.length >= 3) return 55;\r\n return 0;\r\n },\r\n },\r\n {\r\n id: 'SN-015',\r\n severity: 'medium',\r\n title: 'Unexpected outbound connection during idle',\r\n description:\r\n 'MCP server made network requests when no user-initiated tool calls were active. Legitimate MCP servers should only make network requests in response to tool invocations.',\r\n recommendation:\r\n 'Review why this MCP server is making network requests when idle. This could indicate background beaconing or telemetry.',\r\n minEvents: 2,\r\n timeWindowSeconds: 60,\r\n detect: (events: NetworkEvent[]): number => {\r\n // This detection relies on the caller marking events as \"idle\" context\r\n // For now, we detect based on timing — requests that happen without\r\n // a preceding tool call within 5 seconds\r\n // (The full implementation requires integration with the runtime proxy)\r\n if (events.length >= 3) return 50;\r\n if (events.length >= 2) return 30;\r\n return 0;\r\n },\r\n },\r\n];\r\n\r\n// ──────────────────────────────────────────────────────────\r\n// CREDENTIAL-SPECIFIC EXFILTRATION PATTERNS\r\n// Patterns that look for credential-shaped data in network\r\n// requests. These check request URLs, headers, and body hashes\r\n// for signs of stolen secrets being transmitted.\r\n// ──────────────────────────────────────────────────────────\r\n\r\nexport const CREDENTIAL_EXFIL_PATTERNS: EndpointPattern[] = [\r\n {\r\n id: 'SN-020',\r\n severity: 'critical',\r\n title: 'SSH key in outbound request',\r\n urlPattern: /[?&](?:key|data|payload|content|body)=[^&]*(?:ssh-rsa|ssh-ed25519|PRIVATE\\s*KEY)/i,\r\n description:\r\n 'MCP server appears to be transmitting SSH key material in a URL parameter. This matches the Invariant Labs exfiltration attack vector.',\r\n recommendation:\r\n 'CRITICAL: Your SSH keys may be compromised. Rotate all SSH keys immediately.',\r\n },\r\n {\r\n id: 'SN-021',\r\n severity: 'critical',\r\n title: 'API key/token in outbound URL',\r\n urlPattern: /[?&](?:key|token|secret|api_key|apikey|auth|password|credential)=(?!(?:test|demo|example|placeholder))[A-Za-z0-9_-]{20,}/i,\r\n description:\r\n 'MCP server is transmitting what appears to be an API key or token in a URL parameter to an external destination.',\r\n recommendation:\r\n 'CRITICAL: Rotate the compromised API key/token immediately. Check your .env files for exposed secrets.',\r\n },\r\n {\r\n id: 'SN-022',\r\n severity: 'critical',\r\n title: 'AWS credential in outbound request',\r\n urlPattern: /(?:AKIA[0-9A-Z]{16}|(?:aws_secret_access_key|aws_access_key_id)\\s*[:=]\\s*[A-Za-z0-9/+=]{20,})/i,\r\n description:\r\n 'MCP server is transmitting AWS credentials. AWS access keys follow a known format (AKIA...).',\r\n recommendation:\r\n 'CRITICAL: Deactivate the compromised AWS keys immediately via IAM console.',\r\n },\r\n];\r\n\r\n// ──────────────────────────────────────────────────────────\r\n// THREAT SCORING\r\n// Calculate a Sentinel threat score based on findings.\r\n// ──────────────────────────────────────────────────────────\r\n\r\nexport function calculateThreatScore(findings: SentinelFinding[]): number {\r\n if (findings.length === 0) return 0;\r\n\r\n let score = 0;\r\n for (const f of findings) {\r\n const severityWeight =\r\n f.severity === 'critical' ? 30 :\r\n f.severity === 'high' ? 20 :\r\n f.severity === 'medium' ? 10 :\r\n f.severity === 'low' ? 5 : 2;\r\n\r\n // Weight by confidence — a 90% confidence critical is scarier than a 40% one\r\n score += severityWeight * (f.confidence / 100);\r\n }\r\n\r\n // Cap at 100\r\n return Math.min(100, Math.round(score));\r\n}\r\n\r\nexport function threatLevelFromScore(score: number): SentinelThreatLevel {\r\n if (score >= 70) return 'critical';\r\n if (score >= 40) return 'malicious';\r\n if (score >= 15) return 'suspicious';\r\n return 'clean';\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\nimport type { SentinelReport, SentinelThreatLevel } from '../sentinel/index.js';\r\nimport { SENTINEL_MARKETING } from '../sentinel/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// ============================================================\r\n// Sentinel Output\r\n// ============================================================\r\n\r\nconst SENTINEL_THREAT_COLORS: Record<SentinelThreatLevel, (text: string) => string> = {\r\n clean: chalk.green,\r\n suspicious: chalk.yellow,\r\n malicious: chalk.red,\r\n critical: chalk.bgRed.white.bold,\r\n};\r\n\r\nconst SENTINEL_THREAT_ICONS: Record<SentinelThreatLevel, string> = {\r\n clean: '✓',\r\n suspicious: '⚠',\r\n malicious: '✗',\r\n critical: '!!!',\r\n};\r\n\r\n/** Print a Sentinel monitoring report */\r\nexport function printSentinelReport(report: SentinelReport): void {\r\n const color = SENTINEL_THREAT_COLORS[report.threatLevel];\r\n const icon = SENTINEL_THREAT_ICONS[report.threatLevel];\r\n\r\n console.log('');\r\n console.log(\r\n chalk.bold(` ${icon} `) +\r\n chalk.bold(`Sentinel: ${report.serverName}`) +\r\n ' ' +\r\n color(`[Threat: ${report.threatScore}/100 ${report.threatLevel.toUpperCase()}]`)\r\n );\r\n\r\n console.log(chalk.gray(` Monitored: ${report.monitoringDuration.toFixed(0)}s | Events: ${report.totalEvents} | Destinations: ${report.uniqueDestinations.length}`));\r\n\r\n if (report.uniqueDestinations.length > 0) {\r\n console.log(chalk.gray(` Destinations:`));\r\n for (const dest of report.uniqueDestinations.slice(0, 10)) {\r\n console.log(chalk.gray(` → ${dest}`));\r\n }\r\n if (report.uniqueDestinations.length > 10) {\r\n console.log(chalk.gray(` ... and ${report.uniqueDestinations.length - 10} more`));\r\n }\r\n }\r\n\r\n if (report.findings.length === 0) {\r\n console.log(chalk.green(' No suspicious network behavior detected.'));\r\n } else {\r\n console.log(chalk.dim(` ${report.findings.length} finding(s):`));\r\n for (const finding of report.findings) {\r\n const fColor = SEVERITY_COLORS[finding.severity];\r\n const fIcon = SEVERITY_ICONS[finding.severity];\r\n console.log(\r\n ` ${fColor(`[${fIcon}]`)} ${fColor(finding.severity.toUpperCase())} ` +\r\n chalk.white(finding.title) +\r\n chalk.gray(` (${finding.id})`)\r\n );\r\n console.log(chalk.gray(` ${finding.description}`));\r\n if (finding.evidence && finding.evidence.length > 0) {\r\n console.log(chalk.gray(` Evidence: ${finding.evidence.length} network event(s)`));\r\n }\r\n console.log(chalk.cyan(` → ${finding.recommendation}`));\r\n }\r\n }\r\n\r\n console.log(chalk.gray(` ${report.startedAt} → ${report.endedAt}`));\r\n console.log('');\r\n}\r\n\r\n/** Print the Sentinel upgrade prompt (for free tier) */\r\nexport function printSentinelUpgrade(): void {\r\n console.log('');\r\n console.log(chalk.bold.hex('#2C4A7C')(` 🛡️ ${SENTINEL_MARKETING.featureName}`));\r\n console.log(chalk.white(` ${SENTINEL_MARKETING.tagline}`));\r\n console.log('');\r\n for (const cat of SENTINEL_MARKETING.categories) {\r\n console.log(chalk.white(` ${cat.icon} ${chalk.bold(cat.name)}`));\r\n console.log(chalk.gray(` ${cat.desc}`));\r\n }\r\n console.log('');\r\n console.log(chalk.yellow(` ⚡ Sentinel is a Pro feature. Upgrade to unlock runtime monitoring:`));\r\n console.log(chalk.cyan(` https://vigile.dev/pricing`));\r\n console.log('');\r\n console.log(chalk.gray(` Pro ($19/mo) — 5-min sessions, 3 servers`));\r\n console.log(chalk.gray(` Team ($99/mo) — 30-min sessions, 10 servers, real-time alerts`));\r\n console.log(chalk.gray(` Enterprise ($999+) — Unlimited, custom rules, SLA`));\r\n console.log('');\r\n}\r\n\r\n// ============================================================\r\n// API Upload & Auth Output\r\n// ============================================================\r\n\r\n/** Print auth status info */\r\nexport function printAuthStatus(info: {\r\n authenticated: boolean;\r\n source?: 'env' | 'config';\r\n email?: string;\r\n tier?: string;\r\n name?: string;\r\n error?: string;\r\n}): void {\r\n if (info.authenticated) {\r\n console.log(chalk.green(` Authenticated as ${info.email}`));\r\n console.log(chalk.gray(` Tier: ${(info.tier || 'free').toUpperCase()}`));\r\n if (info.name) {\r\n console.log(chalk.gray(` Name: ${info.name}`));\r\n }\r\n console.log(\r\n chalk.gray(\r\n ` Source: ${info.source === 'env' ? 'VIGILE_TOKEN env var' : '~/.vigile/config.json'}`,\r\n ),\r\n );\r\n } else {\r\n console.log(chalk.yellow(' Not authenticated.'));\r\n if (info.error) {\r\n console.log(chalk.red(` Error: ${info.error}`));\r\n }\r\n console.log(chalk.gray(' Run `vigile-scan auth login` or set VIGILE_TOKEN to authenticate.'));\r\n }\r\n console.log('');\r\n}\r\n\r\n/** Print auth login success */\r\nexport function printAuthLoginSuccess(email: string, tier: string): void {\r\n console.log('');\r\n console.log(chalk.green(' Authenticated successfully!'));\r\n console.log(chalk.gray(` Email: ${email}`));\r\n console.log(chalk.gray(` Tier: ${tier.toUpperCase()}`));\r\n console.log(chalk.gray(' Token stored in ~/.vigile/config.json'));\r\n console.log('');\r\n}\r\n\r\n/** Print upload success summary */\r\nexport function printUploadSuccess(summary: {\r\n mcpUploaded: number;\r\n skillsUploaded: number;\r\n failures: number;\r\n}): void {\r\n const total = summary.mcpUploaded + summary.skillsUploaded;\r\n if (total > 0) {\r\n console.log(chalk.green(` Uploaded ${total} result(s) to Vigile registry.`));\r\n if (summary.mcpUploaded > 0) {\r\n console.log(chalk.gray(` MCP servers: ${summary.mcpUploaded}`));\r\n }\r\n if (summary.skillsUploaded > 0) {\r\n console.log(chalk.gray(` Skills: ${summary.skillsUploaded}`));\r\n }\r\n }\r\n if (summary.failures > 0) {\r\n console.log(chalk.yellow(` ${summary.failures} upload(s) failed (results saved locally).`));\r\n }\r\n console.log('');\r\n}\r\n\r\n/** Print upload skip message (when not authenticated or --no-upload) */\r\nexport function printUploadSkipped(reason: 'not-authenticated' | 'no-upload-flag'): void {\r\n if (reason === 'not-authenticated') {\r\n console.log(\r\n chalk.gray(' Tip: Run `vigile-scan auth login` to upload results to the Vigile registry.'),\r\n );\r\n console.log('');\r\n }\r\n // For --no-upload flag, we stay silent (user explicitly opted out)\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","// ============================================================\r\n// Vigile CLI — Auth Management\r\n// ============================================================\r\n// Handles token resolution, persistent storage in ~/.vigile/config.json,\r\n// and provides the auth login/status/logout logic.\r\n//\r\n// Token resolution priority:\r\n// 1. VIGILE_TOKEN environment variable (CI/CD friendly)\r\n// 2. ~/.vigile/config.json (persistent local config)\r\n\r\nimport { readFile, writeFile, mkdir } from 'fs/promises';\r\nimport { join } from 'path';\r\nimport { homedir } from 'os';\r\nimport { VigileApiClient, type ApiUserInfo } from './client.js';\r\n\r\n// ──────────────────────────────────────────────────────────\r\n// Config file paths\r\n// ──────────────────────────────────────────────────────────\r\n\r\nconst CONFIG_DIR = join(homedir(), '.vigile');\r\nconst CONFIG_FILE = join(CONFIG_DIR, 'config.json');\r\n\r\n// ──────────────────────────────────────────────────────────\r\n// Config shape\r\n// ──────────────────────────────────────────────────────────\r\n\r\ninterface VigileConfig {\r\n token?: string;\r\n api_url?: string;\r\n user?: {\r\n email: string;\r\n tier: string;\r\n name?: string;\r\n };\r\n}\r\n\r\n// ──────────────────────────────────────────────────────────\r\n// Token & URL Resolution\r\n// ──────────────────────────────────────────────────────────\r\n\r\n/**\r\n * Resolve the API token.\r\n * Priority: VIGILE_TOKEN env var > ~/.vigile/config.json\r\n */\r\nexport async function resolveToken(): Promise<string | null> {\r\n // 1. Environment variable takes precedence\r\n const envToken = process.env.VIGILE_TOKEN;\r\n if (envToken && envToken.length > 0) {\r\n return envToken;\r\n }\r\n\r\n // 2. Config file\r\n const config = await loadConfig();\r\n return config.token || null;\r\n}\r\n\r\n/**\r\n * Resolve the API base URL.\r\n * Priority: VIGILE_API_URL env var > ~/.vigile/config.json > default\r\n */\r\nexport async function resolveApiUrl(): Promise<string> {\r\n const envUrl = process.env.VIGILE_API_URL;\r\n if (envUrl) return envUrl;\r\n\r\n const config = await loadConfig();\r\n return config.api_url || 'https://api.vigile.dev';\r\n}\r\n\r\n// ──────────────────────────────────────────────────────────\r\n// Config File Operations\r\n// ──────────────────────────────────────────────────────────\r\n\r\nasync function loadConfig(): Promise<VigileConfig> {\r\n try {\r\n const raw = await readFile(CONFIG_FILE, 'utf-8');\r\n return JSON.parse(raw) as VigileConfig;\r\n } catch {\r\n return {};\r\n }\r\n}\r\n\r\nasync function saveConfig(config: VigileConfig): Promise<void> {\r\n await mkdir(CONFIG_DIR, { recursive: true });\r\n await writeFile(CONFIG_FILE, JSON.stringify(config, null, 2), {\r\n mode: 0o600, // Owner read/write only\r\n });\r\n}\r\n\r\n// ──────────────────────────────────────────────────────────\r\n// Auth Actions\r\n// ──────────────────────────────────────────────────────────\r\n\r\n/**\r\n * Login: validate a token against the API and store it persistently.\r\n */\r\nexport async function authLogin(token: string): Promise<{\r\n success: boolean;\r\n user?: ApiUserInfo;\r\n error?: string;\r\n}> {\r\n const apiUrl = await resolveApiUrl();\r\n const client = new VigileApiClient(apiUrl, token);\r\n const result = await client.getMe();\r\n\r\n if (!result.ok) {\r\n return { success: false, error: result.error };\r\n }\r\n\r\n // Persist token and user info\r\n const config = await loadConfig();\r\n config.token = token;\r\n config.user = {\r\n email: result.data.email,\r\n tier: result.data.tier,\r\n name: result.data.name || undefined,\r\n };\r\n await saveConfig(config);\r\n\r\n return { success: true, user: result.data };\r\n}\r\n\r\n/**\r\n * Check current authentication status.\r\n */\r\nexport async function authStatus(): Promise<{\r\n authenticated: boolean;\r\n source?: 'env' | 'config';\r\n user?: ApiUserInfo;\r\n error?: string;\r\n}> {\r\n const envToken = process.env.VIGILE_TOKEN;\r\n const config = await loadConfig();\r\n const token = envToken || config.token;\r\n\r\n if (!token) {\r\n return { authenticated: false };\r\n }\r\n\r\n const source: 'env' | 'config' = envToken ? 'env' : 'config';\r\n const apiUrl = await resolveApiUrl();\r\n const client = new VigileApiClient(apiUrl, token);\r\n const result = await client.getMe();\r\n\r\n if (!result.ok) {\r\n return { authenticated: false, source, error: result.error };\r\n }\r\n\r\n return { authenticated: true, source, user: result.data };\r\n}\r\n\r\n/**\r\n * Logout: clear stored credentials from config file.\r\n */\r\nexport async function authLogout(): Promise<void> {\r\n const config = await loadConfig();\r\n delete config.token;\r\n delete config.user;\r\n await saveConfig(config);\r\n}\r\n\r\n// ──────────────────────────────────────────────────────────\r\n// Convenience: get an authenticated client (or null)\r\n// ──────────────────────────────────────────────────────────\r\n\r\n/**\r\n * Create an authenticated API client, or return null if no token is available.\r\n * Used by the scan upload step and Sentinel API integration.\r\n */\r\nexport async function getAuthenticatedClient(): Promise<VigileApiClient | null> {\r\n const token = await resolveToken();\r\n if (!token) return null;\r\n\r\n const apiUrl = await resolveApiUrl();\r\n return new VigileApiClient(apiUrl, token);\r\n}\r\n","// ============================================================\r\n// Vigile CLI — API Client\r\n// ============================================================\r\n// Lightweight HTTP client for communicating with the Vigile API.\r\n// Uses Node 18+ native fetch. Graceful degradation: if the API\r\n// is unreachable, the CLI continues in local-only mode.\r\n//\r\n// All methods return a discriminated union:\r\n// { ok: true, data: T } | { ok: false, error: string, status: number }\r\n// This avoids thrown exceptions and makes error handling explicit.\r\n\r\nconst DEFAULT_API_URL = 'https://api.vigile.dev';\r\nconst API_TIMEOUT_MS = 15_000; // 15 seconds\r\nconst CLI_VERSION = '0.2.0';\r\n\r\n// ──────────────────────────────────────────────────────────\r\n// Response Types\r\n// ──────────────────────────────────────────────────────────\r\n\r\nexport interface ApiUserInfo {\r\n id: number;\r\n email: string;\r\n name: string | null;\r\n tier: string; // 'free' | 'pro' (team/enterprise planned for future)\r\n}\r\n\r\nexport interface ApiScanResponse {\r\n id: number;\r\n server_name: string;\r\n trust_score: number;\r\n trust_level: string;\r\n score_breakdown: Record<string, number> | null;\r\n findings: Array<Record<string, unknown>>;\r\n findings_count: number;\r\n critical_count: number;\r\n high_count: number;\r\n scanner_version: string;\r\n scanned_at: string;\r\n}\r\n\r\nexport interface ApiSkillScanResponse {\r\n id: number;\r\n skill_name: string;\r\n file_type: string;\r\n platform: string;\r\n trust_score: number;\r\n trust_level: string;\r\n score_breakdown: Record<string, number> | null;\r\n findings: Array<Record<string, unknown>>;\r\n findings_count: number;\r\n critical_count: number;\r\n high_count: number;\r\n scanner_version: string;\r\n scanned_at: string;\r\n}\r\n\r\nexport interface ApiSentinelSessionResponse {\r\n session_id: number;\r\n server_name: string;\r\n max_duration_seconds: number;\r\n status: string;\r\n created_at: string;\r\n}\r\n\r\nexport interface ApiSentinelEventsResponse {\r\n status: string;\r\n events_received: number;\r\n total_events: number;\r\n}\r\n\r\nexport interface ApiSentinelReportResponse {\r\n id: number;\r\n session_id: number;\r\n server_name: string;\r\n monitoring_duration: number;\r\n total_events: number;\r\n unique_destinations: string[];\r\n findings: Array<{\r\n id: string;\r\n category: string;\r\n severity: string;\r\n title: string;\r\n description: string;\r\n server_name: string;\r\n evidence_count: number;\r\n recommendation: string;\r\n confidence: number;\r\n }>;\r\n threat_level: string;\r\n threat_score: number;\r\n started_at: string;\r\n ended_at: string;\r\n user_tier: string;\r\n}\r\n\r\n/** Discriminated union result type */\r\nexport type ApiResult<T> =\r\n | { ok: true; data: T }\r\n | { ok: false; error: string; status: number };\r\n\r\n// ──────────────────────────────────────────────────────────\r\n// API Client\r\n// ──────────────────────────────────────────────────────────\r\n\r\nexport class VigileApiClient {\r\n private baseUrl: string;\r\n private token: string | null;\r\n\r\n constructor(baseUrl?: string, token?: string | null) {\r\n this.baseUrl = (baseUrl || process.env.VIGILE_API_URL || DEFAULT_API_URL)\r\n .replace(/\\/+$/, ''); // strip trailing slashes\r\n this.token = token || null;\r\n }\r\n\r\n get isAuthenticated(): boolean {\r\n return this.token !== null && this.token.length > 0;\r\n }\r\n\r\n // ── Private helpers ──\r\n\r\n private async request<T>(\r\n method: string,\r\n path: string,\r\n body?: unknown,\r\n ): Promise<ApiResult<T>> {\r\n const controller = new AbortController();\r\n const timeoutId = setTimeout(() => controller.abort(), API_TIMEOUT_MS);\r\n\r\n const headers: Record<string, string> = {\r\n 'Content-Type': 'application/json',\r\n 'User-Agent': `vigile-cli/${CLI_VERSION}`,\r\n };\r\n if (this.token) {\r\n headers['Authorization'] = `Bearer ${this.token}`;\r\n }\r\n\r\n try {\r\n const response = await fetch(`${this.baseUrl}${path}`, {\r\n method,\r\n headers,\r\n body: body ? JSON.stringify(body) : undefined,\r\n signal: controller.signal,\r\n });\r\n\r\n clearTimeout(timeoutId);\r\n\r\n if (!response.ok) {\r\n const errorBody = await response.text();\r\n let errorMessage: string;\r\n try {\r\n const parsed = JSON.parse(errorBody);\r\n errorMessage = typeof parsed.detail === 'string'\r\n ? parsed.detail\r\n : JSON.stringify(parsed.detail);\r\n } catch {\r\n errorMessage = errorBody || `HTTP ${response.status}`;\r\n }\r\n return { ok: false, error: errorMessage, status: response.status };\r\n }\r\n\r\n const data = (await response.json()) as T;\r\n return { ok: true, data };\r\n } catch (err) {\r\n clearTimeout(timeoutId);\r\n if (err instanceof Error && err.name === 'AbortError') {\r\n return { ok: false, error: 'Request timed out', status: 0 };\r\n }\r\n return {\r\n ok: false,\r\n error: err instanceof Error ? err.message : 'Network error',\r\n status: 0,\r\n };\r\n }\r\n }\r\n\r\n // ── Auth ──\r\n\r\n async getMe(): Promise<ApiResult<ApiUserInfo>> {\r\n return this.request<ApiUserInfo>('GET', '/api/v1/auth/me');\r\n }\r\n\r\n // ── Scanning ──\r\n\r\n async submitMCPScan(payload: {\r\n server_name: string;\r\n source?: string;\r\n package_url?: string;\r\n repo_url?: string;\r\n description?: string;\r\n readme?: string;\r\n tool_descriptions?: string[];\r\n maintainer?: string;\r\n license?: string;\r\n homepage?: string;\r\n }): Promise<ApiResult<ApiScanResponse>> {\r\n return this.request<ApiScanResponse>('POST', '/api/v1/scan/', payload);\r\n }\r\n\r\n async submitSkillScan(payload: {\r\n skill_name: string;\r\n content: string;\r\n file_type?: string;\r\n platform?: string;\r\n source?: string;\r\n }): Promise<ApiResult<ApiSkillScanResponse>> {\r\n return this.request<ApiSkillScanResponse>('POST', '/api/v1/scan/skill', payload);\r\n }\r\n\r\n // ── Sentinel ──\r\n\r\n async createSentinelSession(\r\n serverName: string,\r\n durationSeconds: number,\r\n client: string = 'cli',\r\n ): Promise<ApiResult<ApiSentinelSessionResponse>> {\r\n return this.request<ApiSentinelSessionResponse>('POST', '/api/v1/sentinel/sessions', {\r\n server_name: serverName,\r\n duration_seconds: durationSeconds,\r\n client,\r\n });\r\n }\r\n\r\n async submitSentinelEvents(\r\n sessionId: number,\r\n events: Array<{\r\n timestamp: number;\r\n server_name: string;\r\n method: string;\r\n url: string;\r\n destination_ip?: string | null;\r\n port: number;\r\n request_size: number;\r\n response_size?: number | null;\r\n status_code?: number | null;\r\n headers?: Record<string, string> | null;\r\n dns_query_type?: string | null;\r\n tls: boolean;\r\n body_hash?: string | null;\r\n body_entropy?: number | null;\r\n }>,\r\n ): Promise<ApiResult<ApiSentinelEventsResponse>> {\r\n return this.request<ApiSentinelEventsResponse>(\r\n 'POST',\r\n `/api/v1/sentinel/sessions/${sessionId}/events`,\r\n { session_id: sessionId, events },\r\n );\r\n }\r\n\r\n async analyzeSentinelSession(\r\n sessionId: number,\r\n ): Promise<ApiResult<ApiSentinelReportResponse>> {\r\n return this.request<ApiSentinelReportResponse>(\r\n 'POST',\r\n `/api/v1/sentinel/sessions/${sessionId}/analyze`,\r\n );\r\n }\r\n}\r\n"],"mappings":"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","names":["import_chalk","import_promises","import_path","import_path","import_path","import_path","import_path","import_promises","import_fs","import_path","_","SEVERITY_DEDUCTIONS","deduplicateFindings","chalk","import_promises","import_path","import_os","chalk","ora"]}
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/sentinel/sentinel.ts","../src/sentinel/sentinel-patterns.ts","../src/output/terminal.ts","../src/output/json.ts","../src/api/auth.ts","../src/api/client.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\n// v0.2.0: Added API integration — scan results upload to the\r\n// Vigile registry, authentication, and Sentinel API sessions.\r\n\r\nimport { Command } from 'commander';\r\nimport chalk from 'chalk';\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 SentinelEngine,\r\n getSentinelFeatures,\r\n SENTINEL_MARKETING,\r\n} from './sentinel/index.js';\r\nimport type { SentinelReport, NetworkEvent } from './sentinel/index.js';\r\nimport {\r\n printBanner,\r\n printServerResult,\r\n printSkillResult,\r\n printSummary,\r\n printSentinelReport,\r\n printSentinelUpgrade,\r\n printNoServersFound,\r\n printNoSkillsFound,\r\n printNothingFound,\r\n printAuthStatus,\r\n printAuthLoginSuccess,\r\n printUploadSuccess,\r\n printUploadSkipped,\r\n} from './output/terminal.js';\r\nimport { formatJSON } from './output/json.js';\r\nimport { getAuthenticatedClient, authLogin, authStatus, authLogout } from './api/auth.js';\r\nimport type { VigileApiClient } from './api/client.js';\r\nimport type {\r\n ScanOptions,\r\n ScanSummary,\r\n ScanResult,\r\n SkillScanResult,\r\n MCPClient,\r\n UploadSummary,\r\n} from './types/index.js';\r\n\r\nconst VERSION = '0.2.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 .option('--sentinel', 'Enable Sentinel runtime monitoring (Pro+ feature)')\r\n .option('--sentinel-server <name>', 'Monitor a specific MCP server by name')\r\n .option('--sentinel-duration <seconds>', 'Monitoring duration in seconds (default: 120)', parseInt)\r\n .option('--no-upload', 'Skip uploading scan results to Vigile API');\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') && !process.argv.slice(2).includes('auth')) {\r\n await runScan(options);\r\n }\r\n});\r\n\r\n// ── Auth subcommand ──\r\nconst authCmd = program\r\n .command('auth')\r\n .description('Manage Vigile API authentication');\r\n\r\nauthCmd\r\n .command('login')\r\n .description('Authenticate with your Vigile API key')\r\n .argument('[token]', 'API key (vgl_...) or JWT token. If omitted, reads from VIGILE_TOKEN env var.')\r\n .action(async (token?: string) => {\r\n const resolvedToken = token || process.env.VIGILE_TOKEN;\r\n if (!resolvedToken) {\r\n console.log(chalk.red(' No token provided. Pass a token argument or set VIGILE_TOKEN env var.'));\r\n console.log(chalk.gray(' Usage: vigile-scan auth login <vgl_your_api_key>'));\r\n console.log(chalk.gray(' Get an API key at https://vigile.dev/account'));\r\n process.exit(1);\r\n }\r\n\r\n const spinner = ora('Validating token...').start();\r\n const result = await authLogin(resolvedToken);\r\n\r\n if (result.success && result.user) {\r\n spinner.succeed('Token validated');\r\n printAuthLoginSuccess(result.user.email, result.user.tier);\r\n } else {\r\n spinner.fail('Authentication failed');\r\n console.log(chalk.red(` Error: ${result.error || 'Unknown error'}`));\r\n process.exit(1);\r\n }\r\n });\r\n\r\nauthCmd\r\n .command('status')\r\n .description('Show current authentication status')\r\n .action(async () => {\r\n const result = await authStatus();\r\n printAuthStatus({\r\n authenticated: result.authenticated,\r\n source: result.source,\r\n email: result.user?.email,\r\n tier: result.user?.tier,\r\n name: result.user?.name || undefined,\r\n error: result.error,\r\n });\r\n });\r\n\r\nauthCmd\r\n .command('logout')\r\n .description('Clear stored credentials')\r\n .action(async () => {\r\n await authLogout();\r\n console.log(chalk.green(' Logged out. Credentials removed from ~/.vigile/config.json'));\r\n console.log('');\r\n });\r\n\r\n// ============================================================\r\n// Main Scan Flow\r\n// ============================================================\r\n\r\nasync function runScan(options: ScanOptions): Promise<void> {\r\n const isJSON = options.json ?? false;\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 // ── Step 4.5: Upload results to Vigile API (if authenticated) ──\r\n if (options.noUpload !== true) {\r\n await uploadResults(results, skillResults, isJSON);\r\n }\r\n\r\n // ── Step 5: Sentinel Runtime Monitoring (if --sentinel) ──\r\n if (options.sentinel) {\r\n await runSentinel(options, results, isJSON);\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\n// ============================================================\r\n// API Upload — Send scan results to the Vigile registry\r\n// ============================================================\r\n\r\n/**\r\n * Upload scan results to the Vigile API.\r\n * Graceful degradation: if API is unreachable or auth fails, just warn.\r\n */\r\nasync function uploadResults(\r\n mcpResults: ScanResult[],\r\n skillResults: SkillScanResult[],\r\n isJSON: boolean,\r\n): Promise<void> {\r\n const client = await getAuthenticatedClient();\r\n\r\n if (!client) {\r\n if (!isJSON) {\r\n printUploadSkipped('not-authenticated');\r\n }\r\n return;\r\n }\r\n\r\n const summary: UploadSummary = {\r\n mcpUploaded: 0,\r\n skillsUploaded: 0,\r\n failures: 0,\r\n errors: [],\r\n };\r\n\r\n const spinner = isJSON ? null : ora('Uploading results to Vigile registry...').start();\r\n\r\n // Upload MCP scan results\r\n for (const result of mcpResults) {\r\n const payload = mapMCPResultToApiPayload(result);\r\n const response = await client.submitMCPScan(payload);\r\n\r\n if (response.ok) {\r\n summary.mcpUploaded++;\r\n } else {\r\n summary.failures++;\r\n summary.errors.push(`${result.server.name}: ${response.error}`);\r\n }\r\n }\r\n\r\n // Upload skill scan results\r\n for (const result of skillResults) {\r\n const payload = mapSkillResultToApiPayload(result);\r\n const response = await client.submitSkillScan(payload);\r\n\r\n if (response.ok) {\r\n summary.skillsUploaded++;\r\n } else {\r\n summary.failures++;\r\n summary.errors.push(`${result.skill.name}: ${response.error}`);\r\n }\r\n }\r\n\r\n if (spinner) {\r\n const total = summary.mcpUploaded + summary.skillsUploaded;\r\n if (total > 0 && summary.failures === 0) {\r\n spinner.succeed(`Uploaded ${total} result(s) to Vigile registry`);\r\n } else if (total > 0 && summary.failures > 0) {\r\n spinner.warn(`Uploaded ${total} result(s), ${summary.failures} failed`);\r\n } else {\r\n spinner.fail('Upload failed');\r\n }\r\n }\r\n\r\n if (!isJSON) {\r\n printUploadSuccess(summary);\r\n }\r\n}\r\n\r\n// ============================================================\r\n// Field Mapping Helpers — CLI types → API request shapes\r\n// ============================================================\r\n\r\n/**\r\n * Map a CLI ScanResult to the API ScanRequest payload.\r\n */\r\nfunction mapMCPResultToApiPayload(result: ScanResult): {\r\n server_name: string;\r\n source: string;\r\n package_url?: string;\r\n description?: string;\r\n tool_descriptions?: string[];\r\n} {\r\n // Derive package URL from command + args\r\n let packageUrl: string | undefined;\r\n if (result.server.command === 'npx') {\r\n const packageName = result.server.args.find((a) => !a.startsWith('-'));\r\n if (packageName) {\r\n packageUrl = `https://www.npmjs.com/package/${packageName}`;\r\n }\r\n } else if (result.server.command === 'uvx' || result.server.command === 'pip') {\r\n const packageName = result.server.args.find((a) => !a.startsWith('-'));\r\n if (packageName) {\r\n packageUrl = `https://pypi.org/project/${packageName}/`;\r\n }\r\n }\r\n\r\n // Tool descriptions from non-flag args\r\n const toolDescriptions = result.server.args.filter((a) => !a.startsWith('-'));\r\n\r\n return {\r\n server_name: result.server.name,\r\n source: 'manual', // CLI-discovered servers are always manual source\r\n package_url: packageUrl,\r\n description: `MCP server discovered from ${result.server.source} config`,\r\n tool_descriptions: toolDescriptions.length > 0 ? toolDescriptions : undefined,\r\n };\r\n}\r\n\r\n/**\r\n * Map a CLI SkillScanResult to the API SkillScanRequest payload.\r\n */\r\nfunction mapSkillResultToApiPayload(result: SkillScanResult): {\r\n skill_name: string;\r\n content: string;\r\n file_type: string;\r\n platform: string;\r\n source: string;\r\n} {\r\n // Map CLI SkillSource to API platform values\r\n const platformMap: Record<string, string> = {\r\n 'claude-code': 'claude-code',\r\n 'github-copilot': 'copilot',\r\n 'cursor': 'cursor',\r\n 'memory-file': 'unknown',\r\n 'custom': 'unknown',\r\n };\r\n\r\n return {\r\n skill_name: result.skill.name,\r\n content: result.skill.content,\r\n file_type: result.skill.fileType,\r\n platform: platformMap[result.skill.source] || 'unknown',\r\n source: 'manual', // CLI submissions are always manual source\r\n };\r\n}\r\n\r\n/**\r\n * Map a CLI NetworkEvent (camelCase) to the API NetworkEventSubmission (snake_case).\r\n */\r\nfunction mapNetworkEventToApi(event: NetworkEvent): {\r\n timestamp: number;\r\n server_name: string;\r\n method: string;\r\n url: string;\r\n destination_ip: string | null;\r\n port: number;\r\n request_size: number;\r\n response_size: number | null;\r\n status_code: number | null;\r\n headers: Record<string, string> | null;\r\n dns_query_type: string | null;\r\n tls: boolean;\r\n body_hash: string | null;\r\n body_entropy: number | null;\r\n} {\r\n return {\r\n timestamp: event.timestamp,\r\n server_name: event.serverName,\r\n method: event.method,\r\n url: event.url,\r\n destination_ip: event.destinationIp || null,\r\n port: event.port,\r\n request_size: event.requestSize,\r\n response_size: event.responseSize ?? null,\r\n status_code: event.statusCode ?? null,\r\n headers: event.headers || null,\r\n dns_query_type: event.dnsQueryType ?? null,\r\n tls: event.tls,\r\n body_hash: event.bodyHash ?? null,\r\n body_entropy: event.bodyEntropy ?? null,\r\n };\r\n}\r\n\r\n// ============================================================\r\n// Sentinel Runtime Monitoring — with API integration\r\n// ============================================================\r\n\r\n/**\r\n * Run Sentinel runtime monitoring on discovered MCP servers.\r\n * When authenticated, creates API sessions and submits events\r\n * for server-side analysis. Falls back to local-only when offline.\r\n */\r\nasync function runSentinel(\r\n options: ScanOptions,\r\n scanResults: ScanResult[],\r\n isJSON: boolean,\r\n): Promise<void> {\r\n // Resolve tier: API if authenticated, else env var fallback\r\n const client = await getAuthenticatedClient();\r\n let tier: 'free' | 'pro' = 'free';\r\n\r\n if (client) {\r\n const meResult = await client.getMe();\r\n if (meResult.ok) {\r\n tier = meResult.data.tier as typeof tier;\r\n }\r\n } else {\r\n tier = (process.env.VIGILE_TIER as typeof tier) || 'free';\r\n }\r\n\r\n const features = getSentinelFeatures(tier);\r\n\r\n if (!features.monitoringEnabled) {\r\n if (!isJSON) {\r\n printSentinelUpgrade();\r\n } else {\r\n console.log(\r\n JSON.stringify({\r\n sentinel: { error: 'upgrade_required', message: SENTINEL_MARKETING.upgradePrompt },\r\n }),\r\n );\r\n }\r\n return;\r\n }\r\n\r\n // Determine which server(s) to monitor\r\n const serversToMonitor: string[] = [];\r\n if (options.sentinelServer) {\r\n serversToMonitor.push(options.sentinelServer);\r\n } else if (scanResults.length > 0) {\r\n // Monitor all discovered servers (up to the tier limit)\r\n const limit =\r\n features.maxConcurrentServers === -1\r\n ? scanResults.length\r\n : Math.min(scanResults.length, features.maxConcurrentServers);\r\n for (let i = 0; i < limit; i++) {\r\n serversToMonitor.push(scanResults[i].server.name);\r\n }\r\n } else {\r\n if (!isJSON) {\r\n console.log(\r\n chalk.yellow(' No MCP servers to monitor. Run a scan first or specify --sentinel-server <name>.'),\r\n );\r\n }\r\n return;\r\n }\r\n\r\n // Clamp duration to tier limit\r\n const requestedDuration = options.sentinelDuration || 120;\r\n const maxDuration = features.maxDurationSeconds === -1 ? requestedDuration : features.maxDurationSeconds;\r\n const duration = Math.min(requestedDuration, maxDuration);\r\n\r\n if (!isJSON) {\r\n console.log('');\r\n console.log(chalk.bold.hex('#2C4A7C')(' \\u{1F6E1}\\uFE0F Vigile Sentinel \\u2014 Runtime Monitor'));\r\n console.log(\r\n chalk.gray(` Tier: ${tier.toUpperCase()} | Duration: ${duration}s | Servers: ${serversToMonitor.length}`),\r\n );\r\n console.log('');\r\n }\r\n\r\n // Start monitoring each server\r\n const sentinelReports: SentinelReport[] = [];\r\n\r\n for (const serverName of serversToMonitor) {\r\n // Create API session if authenticated\r\n let apiSessionId: number | null = null;\r\n if (client) {\r\n const sessionResult = await client.createSentinelSession(serverName, duration);\r\n if (sessionResult.ok) {\r\n apiSessionId = sessionResult.data.session_id;\r\n }\r\n }\r\n\r\n const spinner = isJSON ? null : ora(`Monitoring ${serverName} for ${duration}s...`).start();\r\n\r\n // Batch events for API submission\r\n let pendingApiEvents: NetworkEvent[] = [];\r\n let lastApiSubmit = Date.now();\r\n\r\n const engine = new SentinelEngine({\r\n serverName,\r\n durationSeconds: duration,\r\n onEvent: (event) => {\r\n if (!isJSON && spinner) {\r\n spinner.text = `Monitoring ${serverName} \\u2014 ${(engine as unknown as { events: unknown[] }).events.length} events captured...`;\r\n }\r\n\r\n // Accumulate events for API batch submission\r\n if (apiSessionId !== null && client) {\r\n pendingApiEvents.push(event);\r\n\r\n // Submit every 5 seconds\r\n const now = Date.now();\r\n if (now - lastApiSubmit > 5000 && pendingApiEvents.length > 0) {\r\n const eventsToSubmit = pendingApiEvents.map(mapNetworkEventToApi);\r\n pendingApiEvents = [];\r\n lastApiSubmit = now;\r\n // Fire and forget — don't await in the event handler\r\n client.submitSentinelEvents(apiSessionId, eventsToSubmit).catch(() => {\r\n // Silently ignore API submission errors during monitoring\r\n });\r\n }\r\n }\r\n },\r\n });\r\n\r\n await engine.startMonitoring();\r\n\r\n // Wait for monitoring duration\r\n await new Promise((resolve) => setTimeout(resolve, duration * 1000));\r\n\r\n const report = await engine.stopMonitoring();\r\n sentinelReports.push(report);\r\n\r\n // Submit remaining events and get API analysis\r\n if (apiSessionId !== null && client) {\r\n if (pendingApiEvents.length > 0) {\r\n const finalEvents = pendingApiEvents.map(mapNetworkEventToApi);\r\n await client.submitSentinelEvents(apiSessionId, finalEvents);\r\n }\r\n\r\n const apiReport = await client.analyzeSentinelSession(apiSessionId);\r\n if (apiReport.ok && !isJSON) {\r\n console.log(\r\n chalk.gray(\r\n ` API Analysis: ${apiReport.data.findings.length} findings, threat: ${apiReport.data.threat_level}`,\r\n ),\r\n );\r\n }\r\n }\r\n\r\n if (spinner) {\r\n spinner.succeed(\r\n `${serverName}: ${report.totalEvents} events, ${report.findings.length} findings [${report.threatLevel.toUpperCase()}]`,\r\n );\r\n }\r\n }\r\n\r\n // Output Sentinel results\r\n if (isJSON) {\r\n console.log(JSON.stringify({ sentinel: sentinelReports }, null, 2));\r\n } else {\r\n for (const report of sentinelReports) {\r\n printSentinelReport(report);\r\n }\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 Sentinel — Runtime Network Monitor for MCP Servers\r\n// ============================================================\r\n// The Sentinel engine monitors MCP server network behavior\r\n// in real-time to detect \"phoning home\" — C2 beaconing,\r\n// data exfiltration, DNS tunneling, and covert channels.\r\n//\r\n// Architecture:\r\n// 1. Launches a lightweight network monitor alongside MCP servers\r\n// 2. Captures outbound connections using OS-level network inspection\r\n// 3. Feeds events through behavioral + endpoint pattern matching\r\n// 4. Generates a SentinelReport with findings and threat score\r\n//\r\n// This is a PRO+ subscription feature.\r\n//\r\n// Usage:\r\n// npx vigile-scan --sentinel # Monitor all discovered MCP servers\r\n// npx vigile-scan --sentinel --server <name> # Monitor specific server\r\n// npx vigile-scan --sentinel --duration 300 # Monitor for 5 minutes\r\n\r\nimport { execSync, spawn, type ChildProcess } from 'child_process';\r\nimport type {\r\n NetworkEvent,\r\n SentinelFinding,\r\n SentinelReport,\r\n SentinelThreatLevel,\r\n} from './sentinel-patterns.js';\r\nimport {\r\n SUSPICIOUS_ENDPOINT_PATTERNS,\r\n BEHAVIORAL_PATTERNS,\r\n CREDENTIAL_EXFIL_PATTERNS,\r\n calculateThreatScore,\r\n threatLevelFromScore,\r\n} from './sentinel-patterns.js';\r\n\r\n// ──────────────────────────────────────────────────────────\r\n// SENTINEL ENGINE\r\n// ──────────────────────────────────────────────────────────\r\n\r\nexport class SentinelEngine {\r\n private events: NetworkEvent[] = [];\r\n private findings: SentinelFinding[] = [];\r\n private monitorProcess: ChildProcess | null = null;\r\n private startTime: number = 0;\r\n private serverName: string;\r\n private durationSeconds: number;\r\n private onEvent?: (event: NetworkEvent) => void;\r\n\r\n constructor(options: {\r\n serverName: string;\r\n durationSeconds?: number;\r\n onEvent?: (event: NetworkEvent) => void;\r\n }) {\r\n this.serverName = options.serverName;\r\n this.durationSeconds = options.durationSeconds || 120; // Default 2 minutes\r\n this.onEvent = options.onEvent;\r\n }\r\n\r\n /**\r\n * Start monitoring network activity for the given MCP server.\r\n *\r\n * The monitor works in three modes depending on the OS and permissions:\r\n * 1. macOS: Uses `nettop` or `networksetup` + `tcpdump`\r\n * 2. Linux: Uses `ss` polling + optional `tcpdump`\r\n * 3. Fallback: Uses Node.js HTTP/HTTPS module monkey-patching\r\n * (only captures Node.js HTTP requests from the current process tree)\r\n */\r\n async startMonitoring(): Promise<void> {\r\n this.startTime = Date.now();\r\n this.events = [];\r\n this.findings = [];\r\n\r\n // Detect which monitoring method is available\r\n const method = this.detectMonitoringMethod();\r\n\r\n switch (method) {\r\n case 'lsof-poll':\r\n await this.startLsofPolling();\r\n break;\r\n case 'ss-poll':\r\n await this.startSsPolling();\r\n break;\r\n case 'proxy':\r\n await this.startProxyCapture();\r\n break;\r\n }\r\n }\r\n\r\n /**\r\n * Stop monitoring and generate the report.\r\n */\r\n async stopMonitoring(): Promise<SentinelReport> {\r\n if (this.monitorProcess) {\r\n this.monitorProcess.kill('SIGTERM');\r\n this.monitorProcess = null;\r\n }\r\n\r\n // Run all detection patterns against collected events\r\n this.analyzeEvents();\r\n\r\n const threatScore = calculateThreatScore(this.findings);\r\n const threatLevel = threatLevelFromScore(threatScore);\r\n\r\n const uniqueDestinations = [\r\n ...new Set(\r\n this.events.map(e => {\r\n try { return new URL(e.url).hostname; } catch { return e.url; }\r\n })\r\n ),\r\n ];\r\n\r\n return {\r\n serverName: this.serverName,\r\n monitoringDuration: (Date.now() - this.startTime) / 1000,\r\n totalEvents: this.events.length,\r\n uniqueDestinations,\r\n findings: this.findings,\r\n threatLevel,\r\n threatScore,\r\n startedAt: new Date(this.startTime).toISOString(),\r\n endedAt: new Date().toISOString(),\r\n };\r\n }\r\n\r\n /**\r\n * Feed a single network event into the engine (for real-time analysis).\r\n */\r\n ingestEvent(event: NetworkEvent): void {\r\n this.events.push(event);\r\n this.onEvent?.(event);\r\n\r\n // Run real-time endpoint checks (fast, per-event)\r\n this.checkEndpointPatterns(event);\r\n this.checkCredentialPatterns(event);\r\n }\r\n\r\n // ── Detection Methods ──\r\n\r\n /**\r\n * Run all analysis on collected events.\r\n */\r\n private analyzeEvents(): void {\r\n // 1. Endpoint pattern matching (per-event)\r\n for (const event of this.events) {\r\n this.checkEndpointPatterns(event);\r\n this.checkCredentialPatterns(event);\r\n }\r\n\r\n // 2. Behavioral pattern matching (across time windows)\r\n for (const pattern of BEHAVIORAL_PATTERNS) {\r\n if (this.events.length < pattern.minEvents) continue;\r\n\r\n // Slice events into the time window\r\n const now = Date.now();\r\n const windowStart = now - pattern.timeWindowSeconds * 1000;\r\n const windowEvents = this.events.filter(e => e.timestamp >= windowStart);\r\n\r\n if (windowEvents.length < pattern.minEvents) continue;\r\n\r\n const confidence = pattern.detect(windowEvents);\r\n if (confidence > 25) {\r\n // Only report if we haven't already found this pattern\r\n if (!this.findings.some(f => f.id === pattern.id)) {\r\n this.findings.push({\r\n id: pattern.id,\r\n category: pattern.id.startsWith('SN-01') ? 'c2-beaconing' :\r\n pattern.id === 'SN-012' ? 'dns-tunneling' :\r\n pattern.id === 'SN-013' ? 'covert-channel' : 'phone-home',\r\n severity: pattern.severity,\r\n title: pattern.title,\r\n description: pattern.description,\r\n serverName: this.serverName,\r\n evidence: windowEvents.slice(0, 10), // Cap evidence at 10 events\r\n recommendation: pattern.recommendation,\r\n confidence,\r\n });\r\n }\r\n }\r\n }\r\n\r\n // De-duplicate findings (keep highest confidence per ID)\r\n const seen = new Map<string, SentinelFinding>();\r\n for (const f of this.findings) {\r\n const existing = seen.get(f.id);\r\n if (!existing || f.confidence > existing.confidence) {\r\n seen.set(f.id, f);\r\n }\r\n }\r\n this.findings = Array.from(seen.values());\r\n }\r\n\r\n private checkEndpointPatterns(event: NetworkEvent): void {\r\n for (const pattern of SUSPICIOUS_ENDPOINT_PATTERNS) {\r\n if (pattern.urlPattern.test(event.url)) {\r\n if (!this.findings.some(f => f.id === pattern.id && f.evidence[0]?.url === event.url)) {\r\n this.findings.push({\r\n id: pattern.id,\r\n category: 'phone-home',\r\n severity: pattern.severity,\r\n title: pattern.title,\r\n description: pattern.description,\r\n serverName: this.serverName,\r\n evidence: [event],\r\n recommendation: pattern.recommendation,\r\n confidence: 95,\r\n });\r\n }\r\n }\r\n }\r\n }\r\n\r\n private checkCredentialPatterns(event: NetworkEvent): void {\r\n for (const pattern of CREDENTIAL_EXFIL_PATTERNS) {\r\n if (pattern.urlPattern.test(event.url)) {\r\n this.findings.push({\r\n id: pattern.id,\r\n category: 'data-exfiltration',\r\n severity: pattern.severity,\r\n title: pattern.title,\r\n description: pattern.description,\r\n serverName: this.serverName,\r\n evidence: [event],\r\n recommendation: pattern.recommendation,\r\n confidence: 98,\r\n });\r\n }\r\n }\r\n }\r\n\r\n // ── Monitoring Methods ──\r\n\r\n private detectMonitoringMethod(): 'lsof-poll' | 'ss-poll' | 'proxy' {\r\n try {\r\n execSync('which lsof', { stdio: 'pipe' });\r\n return 'lsof-poll';\r\n } catch {\r\n try {\r\n execSync('which ss', { stdio: 'pipe' });\r\n return 'ss-poll';\r\n } catch {\r\n return 'proxy';\r\n }\r\n }\r\n }\r\n\r\n /**\r\n * macOS/Linux: Poll `lsof` to capture network connections for a process.\r\n * This is the lowest-privilege method — no root needed.\r\n */\r\n private async startLsofPolling(): Promise<void> {\r\n const pollInterval = setInterval(() => {\r\n try {\r\n // lsof -i -n -P lists internet connections with numeric hosts/ports\r\n const output = execSync(\r\n `lsof -i -n -P 2>/dev/null | grep -i \"${this.serverName}\" || true`,\r\n { timeout: 5000, encoding: 'utf-8' }\r\n );\r\n\r\n for (const line of output.split('\\n').filter(Boolean)) {\r\n const event = this.parseLsofLine(line);\r\n if (event) this.ingestEvent(event);\r\n }\r\n } catch {\r\n // lsof failed, continue\r\n }\r\n }, 2000); // Poll every 2 seconds\r\n\r\n // Store cleanup handle\r\n this.monitorProcess = {\r\n kill: () => clearInterval(pollInterval),\r\n } as unknown as ChildProcess;\r\n\r\n // Auto-stop after duration\r\n setTimeout(() => {\r\n clearInterval(pollInterval);\r\n }, this.durationSeconds * 1000);\r\n }\r\n\r\n /**\r\n * Linux: Poll `ss` (socket statistics) for network connections.\r\n */\r\n private async startSsPolling(): Promise<void> {\r\n const pollInterval = setInterval(() => {\r\n try {\r\n const output = execSync(\r\n `ss -tnp 2>/dev/null | grep \"${this.serverName}\" || true`,\r\n { timeout: 5000, encoding: 'utf-8' }\r\n );\r\n\r\n for (const line of output.split('\\n').filter(Boolean)) {\r\n const event = this.parseSsLine(line);\r\n if (event) this.ingestEvent(event);\r\n }\r\n } catch {\r\n // ss failed, continue\r\n }\r\n }, 2000);\r\n\r\n this.monitorProcess = {\r\n kill: () => clearInterval(pollInterval),\r\n } as unknown as ChildProcess;\r\n\r\n setTimeout(() => {\r\n clearInterval(pollInterval);\r\n }, this.durationSeconds * 1000);\r\n }\r\n\r\n /**\r\n * Fallback: Start a lightweight MITM proxy that MCP traffic routes through.\r\n * This requires the runtime proxy (Phase 3) to be active.\r\n */\r\n private async startProxyCapture(): Promise<void> {\r\n // Proxy mode requires the full runtime proxy infrastructure (Phase 3).\r\n // For Phase 2, this is a stub that collects events fed via ingestEvent().\r\n console.warn(\r\n '[Sentinel] Proxy capture requires the runtime proxy (coming soon). ' +\r\n 'Using manual event ingestion mode.'\r\n );\r\n }\r\n\r\n // ── Parsers ──\r\n\r\n private parseLsofLine(line: string): NetworkEvent | null {\r\n // lsof output format: COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME\r\n // NAME for TCP: host:port->remote:port (ESTABLISHED)\r\n const match = line.match(\r\n /(\\S+)\\s+(\\d+)\\s+\\S+\\s+\\S+\\s+IPv[46]\\s+\\S+\\s+\\S+\\s+TCP\\s+\\S+->(\\S+):(\\d+)\\s/\r\n );\r\n if (!match) return null;\r\n\r\n const [, _command, _pid, remoteHost, remotePort] = match;\r\n\r\n return {\r\n timestamp: Date.now(),\r\n serverName: this.serverName,\r\n method: 'TCP',\r\n url: `https://${remoteHost}:${remotePort}/`,\r\n destinationIp: remoteHost,\r\n port: parseInt(remotePort, 10),\r\n requestSize: 0,\r\n tls: parseInt(remotePort, 10) === 443,\r\n };\r\n }\r\n\r\n private parseSsLine(line: string): NetworkEvent | null {\r\n // ss output: State Recv-Q Send-Q Local:Port Peer:Port Process\r\n const match = line.match(\r\n /ESTAB\\s+\\d+\\s+(\\d+)\\s+\\S+\\s+(\\S+):(\\d+)/\r\n );\r\n if (!match) return null;\r\n\r\n const [, sendQueue, remoteHost, remotePort] = match;\r\n\r\n return {\r\n timestamp: Date.now(),\r\n serverName: this.serverName,\r\n method: 'TCP',\r\n url: `https://${remoteHost}:${remotePort}/`,\r\n destinationIp: remoteHost,\r\n port: parseInt(remotePort, 10),\r\n requestSize: parseInt(sendQueue, 10),\r\n tls: parseInt(remotePort, 10) === 443,\r\n };\r\n }\r\n}\r\n\r\n// ──────────────────────────────────────────────────────────\r\n// SUBSCRIPTION GATING\r\n// Sentinel is a premium feature (Pro tier and above).\r\n// ──────────────────────────────────────────────────────────\r\n\r\n// Launch tiers: free + pro only. Team and enterprise planned for future builds.\r\nexport type SubscriptionTier = 'free' | 'pro';\r\n\r\nexport interface SentinelFeatureGate {\r\n /** Whether Sentinel monitoring is available */\r\n monitoringEnabled: boolean;\r\n /** Maximum monitoring duration in seconds */\r\n maxDurationSeconds: number;\r\n /** Maximum number of servers to monitor simultaneously */\r\n maxConcurrentServers: number;\r\n /** Whether behavioral detection is available (vs. endpoint-only) */\r\n behavioralDetection: boolean;\r\n /** Whether real-time alerts are enabled */\r\n realTimeAlerts: boolean;\r\n /** Whether historical Sentinel data is stored */\r\n historyRetentionDays: number;\r\n /** Whether API access to Sentinel data is available */\r\n apiAccess: boolean;\r\n}\r\n\r\nexport function getSentinelFeatures(tier: SubscriptionTier): SentinelFeatureGate {\r\n switch (tier) {\r\n case 'free':\r\n return {\r\n monitoringEnabled: false, // Upgrade required\r\n maxDurationSeconds: 0,\r\n maxConcurrentServers: 0,\r\n behavioralDetection: false,\r\n realTimeAlerts: false,\r\n historyRetentionDays: 0,\r\n apiAccess: false,\r\n };\r\n case 'pro':\r\n return {\r\n monitoringEnabled: true,\r\n maxDurationSeconds: 300, // 5 minutes\r\n maxConcurrentServers: 3,\r\n behavioralDetection: true,\r\n realTimeAlerts: false, // Future: Team+ only\r\n historyRetentionDays: 7,\r\n apiAccess: true,\r\n };\r\n // Future tiers — uncomment when team/enterprise plans launch\r\n // case 'team':\r\n // return {\r\n // monitoringEnabled: true,\r\n // maxDurationSeconds: 1800, // 30 minutes\r\n // maxConcurrentServers: 10,\r\n // behavioralDetection: true,\r\n // realTimeAlerts: true,\r\n // historyRetentionDays: 30,\r\n // apiAccess: true,\r\n // };\r\n // case 'enterprise':\r\n // return {\r\n // monitoringEnabled: true,\r\n // maxDurationSeconds: -1, // Unlimited (continuous)\r\n // maxConcurrentServers: -1, // Unlimited\r\n // behavioralDetection: true,\r\n // realTimeAlerts: true,\r\n // historyRetentionDays: 365,\r\n // apiAccess: true,\r\n // };\r\n }\r\n}\r\n\r\n/**\r\n * Pretty marketing name for tier-gated Sentinel features.\r\n * Used in CLI output and web UI.\r\n */\r\nexport const SENTINEL_MARKETING = {\r\n tagline: 'Always watching what your tools are doing.',\r\n featureName: 'Vigile Sentinel',\r\n tierName: 'Sentinel Protection',\r\n upgradePrompt:\r\n '🛡️ Vigile Sentinel is a Pro feature. Upgrade at https://vigile.dev/pricing to monitor your MCP servers for real-time phone-home detection.',\r\n categories: [\r\n { icon: '📡', name: 'C2 Beaconing Detection', desc: 'Catches tools phoning home on a schedule' },\r\n { icon: '🔐', name: 'Credential Theft Alerts', desc: 'Detects SSH keys & API tokens leaving your machine' },\r\n { icon: '🕵️', name: 'DNS Tunneling Detection', desc: 'Spots data hidden in DNS queries' },\r\n { icon: '📊', name: 'Behavioral Analysis', desc: 'Machine-learning-ready traffic pattern analysis' },\r\n { icon: '⚡', name: 'Real-Time Alerts', desc: 'Instant notification when threats are detected' },\r\n { icon: '🛡️', name: 'Continuous Monitoring', desc: '24/7 protection for enterprise environments' },\r\n ],\r\n} as const;\r\n","// ============================================================\r\n// Vigile Sentinel — Runtime Phone-Home Detection Patterns\r\n// ============================================================\r\n// These patterns analyze runtime network behavior of MCP servers\r\n// to detect data exfiltration, C2 beaconing, and covert channels.\r\n//\r\n// Unlike static patterns (patterns.ts) which scan tool descriptions\r\n// BEFORE execution, Sentinel patterns analyze LIVE network traffic\r\n// DURING execution. This catches tools that look clean on paper\r\n// but phone home at runtime.\r\n//\r\n// Detection categories:\r\n// - C2 Beaconing (regular intervals, heartbeat patterns)\r\n// - Data Exfiltration (credential theft, file uploads)\r\n// - DNS Tunneling (encoded data in DNS queries)\r\n// - Covert Channels (steganographic exfil, timing-based)\r\n// - Suspicious Endpoints (known bad IPs, DGA domains)\r\n\r\nimport type { Severity, FindingCategory } from '../types/index.js';\r\n\r\n// ──────────────────────────────────────────────────────────\r\n// Sentinel-specific types\r\n// ──────────────────────────────────────────────────────────\r\n\r\nexport interface NetworkEvent {\r\n /** Timestamp of the network event */\r\n timestamp: number;\r\n /** Source process / MCP server name */\r\n serverName: string;\r\n /** HTTP method or protocol */\r\n method: string;\r\n /** Full URL or hostname */\r\n url: string;\r\n /** Destination IP address (resolved) */\r\n destinationIp?: string;\r\n /** Destination port */\r\n port: number;\r\n /** Request size in bytes */\r\n requestSize: number;\r\n /** Response size in bytes */\r\n responseSize?: number;\r\n /** HTTP status code */\r\n statusCode?: number;\r\n /** Request headers */\r\n headers?: Record<string, string>;\r\n /** DNS query type (for DNS events) */\r\n dnsQueryType?: string;\r\n /** Whether TLS was used */\r\n tls: boolean;\r\n /** Request body hash (we never store raw bodies) */\r\n bodyHash?: string;\r\n /** Estimated entropy of request body (0-8 bits/byte) */\r\n bodyEntropy?: number;\r\n}\r\n\r\nexport interface SentinelFinding {\r\n /** Unique finding ID (SN-series) */\r\n id: string;\r\n /** Category */\r\n category: FindingCategory | 'c2-beaconing' | 'dns-tunneling' | 'covert-channel' | 'phone-home';\r\n /** Severity */\r\n severity: Severity;\r\n /** Short title */\r\n title: string;\r\n /** Detailed description */\r\n description: string;\r\n /** The server that triggered this */\r\n serverName: string;\r\n /** Evidence — the specific network events that triggered this */\r\n evidence: NetworkEvent[];\r\n /** Recommendation */\r\n recommendation: string;\r\n /** Confidence score (0-100) — how sure Sentinel is this is malicious */\r\n confidence: number;\r\n}\r\n\r\nexport type SentinelThreatLevel = 'clean' | 'suspicious' | 'malicious' | 'critical';\r\n\r\nexport interface SentinelReport {\r\n /** Server being monitored */\r\n serverName: string;\r\n /** Monitoring duration in seconds */\r\n monitoringDuration: number;\r\n /** Total network events captured */\r\n totalEvents: number;\r\n /** Unique destinations contacted */\r\n uniqueDestinations: string[];\r\n /** Sentinel findings */\r\n findings: SentinelFinding[];\r\n /** Overall threat level */\r\n threatLevel: SentinelThreatLevel;\r\n /** Threat score (0-100, inverse of trust — higher = more dangerous) */\r\n threatScore: number;\r\n /** When monitoring started */\r\n startedAt: string;\r\n /** When monitoring ended */\r\n endedAt: string;\r\n}\r\n\r\n// ──────────────────────────────────────────────────────────\r\n// ENDPOINT REPUTATION PATTERNS\r\n// Known malicious or suspicious destination patterns.\r\n// ──────────────────────────────────────────────────────────\r\n\r\nexport interface EndpointPattern {\r\n id: string;\r\n severity: Severity;\r\n title: string;\r\n /** Regex to match against the full URL */\r\n urlPattern: RegExp;\r\n /** Regex to match against destination IP (optional) */\r\n ipPattern?: RegExp;\r\n description: string;\r\n recommendation: string;\r\n}\r\n\r\nexport const SUSPICIOUS_ENDPOINT_PATTERNS: EndpointPattern[] = [\r\n {\r\n id: 'SN-001',\r\n severity: 'critical',\r\n title: 'Known data exfiltration endpoint',\r\n urlPattern: /https?:\\/\\/[^/]*(?:pastebin\\.com|hastebin\\.com|ghostbin\\.co|paste\\.ee|dpaste\\.org|transfer\\.sh|file\\.io|0x0\\.st|ix\\.io)\\/(?:api|raw|upload|documents)/i,\r\n description:\r\n 'MCP server is sending data to a paste/file sharing service commonly used for data exfiltration.',\r\n recommendation:\r\n 'CRITICAL: Stop this MCP server immediately. Legitimate tools should never upload to paste services.',\r\n },\r\n {\r\n id: 'SN-002',\r\n severity: 'critical',\r\n title: 'Webhook exfiltration channel',\r\n urlPattern: /https?:\\/\\/(?:hooks\\.slack\\.com|discord(?:app)?\\.com\\/api\\/webhooks|webhook\\.site|pipedream\\.net|requestbin\\.|hookbin\\.|beeceptor\\.com)/i,\r\n description:\r\n 'MCP server is sending data to a webhook endpoint. Attackers commonly use webhook services as low-noise exfiltration channels.',\r\n recommendation:\r\n 'Investigate what data is being sent to this webhook. This is a common attacker exfiltration method.',\r\n },\r\n {\r\n id: 'SN-003',\r\n severity: 'high',\r\n title: 'Dynamic DNS destination',\r\n urlPattern: /https?:\\/\\/[^/]*\\.(?:duckdns\\.org|no-ip\\.com|ngrok\\.io|ngrok-free\\.app|serveo\\.net|localhost\\.run|bore\\.digital|tailscale\\.io)(?:\\/|$)/i,\r\n description:\r\n 'MCP server is connecting to a dynamic DNS or tunneling service, commonly used for C2 infrastructure.',\r\n recommendation:\r\n 'Review this connection. Dynamic DNS is frequently used by attackers to rotate C2 endpoints.',\r\n },\r\n {\r\n id: 'SN-004',\r\n severity: 'high',\r\n title: 'Cryptocurrency-related endpoint',\r\n urlPattern: /https?:\\/\\/[^/]*(?:blockchain\\.info|etherscan\\.io|bscscan\\.com|solscan\\.io|mempool\\.space)\\/(?:api|rawaddr|tx|address)/i,\r\n description:\r\n 'MCP server is querying cryptocurrency blockchain APIs, which could indicate wallet scanning or theft.',\r\n recommendation:\r\n 'Unless this is an explicitly crypto-related tool, this connection is highly suspicious.',\r\n },\r\n {\r\n id: 'SN-005',\r\n severity: 'medium',\r\n title: 'Telemetry to unknown endpoint',\r\n urlPattern: /https?:\\/\\/[^/]*(?:\\/(?:telemetry|analytics|tracking|pixel|beacon|collect|event|metrics|heartbeat))(?:\\?|$|\\/)/i,\r\n description:\r\n 'MCP server is sending telemetry/analytics data. While sometimes legitimate, this can mask data exfiltration.',\r\n recommendation:\r\n 'Verify the telemetry destination is expected. Compare against the MCP server\\'s documentation.',\r\n },\r\n {\r\n id: 'SN-006',\r\n severity: 'critical',\r\n title: 'Raw IP connection (no hostname)',\r\n urlPattern: /https?:\\/\\/(?:\\d{1,3}\\.){3}\\d{1,3}(?::\\d+)?(?:\\/|$)/,\r\n description:\r\n 'MCP server is connecting directly to an IP address instead of a hostname. This bypasses DNS-based monitoring and is a strong indicator of C2 communication.',\r\n recommendation:\r\n 'CRITICAL: Direct IP connections are almost never legitimate for MCP servers. Investigate immediately.',\r\n },\r\n {\r\n id: 'SN-007',\r\n severity: 'high',\r\n title: 'Non-standard port connection',\r\n urlPattern: /https?:\\/\\/[^/]+:(?!80\\b|443\\b|8080\\b|8443\\b|3000\\b|5000\\b|8000\\b)\\d{4,5}(?:\\/|$)/,\r\n description:\r\n 'MCP server is connecting to a non-standard port. While sometimes legitimate, C2 infrastructure often uses unusual ports.',\r\n recommendation:\r\n 'Verify this port is expected for the service being contacted.',\r\n },\r\n];\r\n\r\n// ──────────────────────────────────────────────────────────\r\n// BEHAVIORAL DETECTION PATTERNS\r\n// These don't match URLs — they match *patterns of behavior*\r\n// across multiple network events. The Sentinel engine\r\n// evaluates these over time windows.\r\n// ──────────────────────────────────────────────────────────\r\n\r\nexport interface BehavioralPattern {\r\n id: string;\r\n severity: Severity;\r\n title: string;\r\n description: string;\r\n recommendation: string;\r\n /** Minimum number of events needed to trigger */\r\n minEvents: number;\r\n /** Time window in seconds to analyze */\r\n timeWindowSeconds: number;\r\n /** The detection function — returns confidence 0-100 */\r\n detect: (events: NetworkEvent[]) => number;\r\n}\r\n\r\nexport const BEHAVIORAL_PATTERNS: BehavioralPattern[] = [\r\n {\r\n id: 'SN-010',\r\n severity: 'critical',\r\n title: 'C2 beaconing detected (regular interval)',\r\n description:\r\n 'MCP server is making network requests at regular intervals to the same destination, consistent with command-and-control beaconing. Malware phones home on a schedule to receive commands.',\r\n recommendation:\r\n 'CRITICAL: This pattern strongly indicates the MCP server is compromised. Stop it immediately and investigate the destination.',\r\n minEvents: 5,\r\n timeWindowSeconds: 300, // 5 minutes\r\n detect: (events: NetworkEvent[]): number => {\r\n // Group events by destination\r\n const byDest = new Map<string, number[]>();\r\n for (const e of events) {\r\n const dest = new URL(e.url).hostname;\r\n if (!byDest.has(dest)) byDest.set(dest, []);\r\n byDest.get(dest)!.push(e.timestamp);\r\n }\r\n\r\n let maxConfidence = 0;\r\n for (const [, timestamps] of byDest) {\r\n if (timestamps.length < 5) continue;\r\n timestamps.sort((a, b) => a - b);\r\n\r\n // Calculate intervals between consecutive requests\r\n const intervals: number[] = [];\r\n for (let i = 1; i < timestamps.length; i++) {\r\n intervals.push(timestamps[i] - timestamps[i - 1]);\r\n }\r\n\r\n // Calculate coefficient of variation (std/mean)\r\n // Low CV = regular intervals = beaconing\r\n const mean = intervals.reduce((s, v) => s + v, 0) / intervals.length;\r\n const std = Math.sqrt(\r\n intervals.reduce((s, v) => s + (v - mean) ** 2, 0) / intervals.length\r\n );\r\n const cv = mean > 0 ? std / mean : 1;\r\n\r\n // CV < 0.15 = very regular (high confidence beaconing)\r\n // CV < 0.30 = somewhat regular (medium confidence)\r\n // Add jitter tolerance — sophisticated C2 adds random jitter\r\n if (cv < 0.15) maxConfidence = Math.max(maxConfidence, 95);\r\n else if (cv < 0.25) maxConfidence = Math.max(maxConfidence, 80);\r\n else if (cv < 0.35) maxConfidence = Math.max(maxConfidence, 60);\r\n }\r\n return maxConfidence;\r\n },\r\n },\r\n {\r\n id: 'SN-011',\r\n severity: 'critical',\r\n title: 'Burst data exfiltration',\r\n description:\r\n 'MCP server sent an unusually large amount of data in a short burst to an external endpoint. This pattern matches credential dump exfiltration and file theft.',\r\n recommendation:\r\n 'CRITICAL: Investigate what data was transmitted. Check for stolen SSH keys, credentials, or source code.',\r\n minEvents: 1,\r\n timeWindowSeconds: 60, // 1 minute\r\n detect: (events: NetworkEvent[]): number => {\r\n // Look for single large requests or bursts of requests\r\n const LARGE_REQUEST_THRESHOLD = 50_000; // 50KB in a single request\r\n const BURST_THRESHOLD = 200_000; // 200KB total in time window\r\n\r\n let maxSingle = 0;\r\n let totalBytes = 0;\r\n\r\n for (const e of events) {\r\n maxSingle = Math.max(maxSingle, e.requestSize);\r\n totalBytes += e.requestSize;\r\n }\r\n\r\n if (maxSingle > LARGE_REQUEST_THRESHOLD) return 90;\r\n if (totalBytes > BURST_THRESHOLD) return 85;\r\n if (totalBytes > BURST_THRESHOLD / 2) return 60;\r\n return 0;\r\n },\r\n },\r\n {\r\n id: 'SN-012',\r\n severity: 'high',\r\n title: 'DNS tunneling suspected',\r\n description:\r\n 'MCP server is making an unusual number of DNS queries with high-entropy subdomains. This pattern matches DNS tunneling — a technique to exfiltrate data by encoding it in DNS queries, which often bypass firewalls.',\r\n recommendation:\r\n 'Investigate the DNS queries. DNS tunneling uses encoded data in subdomain labels (e.g., aGVsbG8=.evil.com).',\r\n minEvents: 10,\r\n timeWindowSeconds: 120,\r\n detect: (events: NetworkEvent[]): number => {\r\n // Filter DNS events and look for high-entropy subdomain patterns\r\n const dnsEvents = events.filter(e => e.dnsQueryType);\r\n if (dnsEvents.length < 10) return 0;\r\n\r\n let suspiciousCount = 0;\r\n for (const e of dnsEvents) {\r\n try {\r\n const hostname = new URL(e.url).hostname;\r\n const parts = hostname.split('.');\r\n // DNS tunneling creates long, random-looking subdomains\r\n for (const part of parts) {\r\n if (part.length > 30) suspiciousCount++;\r\n // Check for base64-like patterns in subdomain\r\n if (/^[A-Za-z0-9+/]{20,}={0,2}$/.test(part)) suspiciousCount++;\r\n }\r\n } catch {\r\n // URL parse failed, skip\r\n }\r\n }\r\n\r\n const ratio = suspiciousCount / dnsEvents.length;\r\n if (ratio > 0.5) return 90;\r\n if (ratio > 0.3) return 70;\r\n if (ratio > 0.1) return 40;\r\n return 0;\r\n },\r\n },\r\n {\r\n id: 'SN-013',\r\n severity: 'high',\r\n title: 'Multi-destination scatter exfiltration',\r\n description:\r\n 'MCP server is distributing data across many different external destinations in a short period. Sophisticated exfiltration splits data across multiple endpoints to avoid size-based detection.',\r\n recommendation:\r\n 'Review all destinations contacted. This pattern suggests deliberate evasion of single-destination monitoring.',\r\n minEvents: 5,\r\n timeWindowSeconds: 120,\r\n detect: (events: NetworkEvent[]): number => {\r\n const destinations = new Set<string>();\r\n for (const e of events) {\r\n try {\r\n destinations.add(new URL(e.url).hostname);\r\n } catch {\r\n // skip\r\n }\r\n }\r\n\r\n // If contacting >10 unique destinations with data, very suspicious\r\n const withData = events.filter(e => e.requestSize > 500);\r\n const destWithData = new Set<string>();\r\n for (const e of withData) {\r\n try { destWithData.add(new URL(e.url).hostname); } catch { /* skip */ }\r\n }\r\n\r\n if (destWithData.size > 10) return 85;\r\n if (destWithData.size > 5) return 65;\r\n if (destWithData.size > 3 && withData.length > events.length * 0.5) return 50;\r\n return 0;\r\n },\r\n },\r\n {\r\n id: 'SN-014',\r\n severity: 'high',\r\n title: 'High-entropy payload transmission',\r\n description:\r\n 'MCP server is sending request bodies with unusually high entropy (randomness), suggesting encrypted or compressed data exfiltration. Legitimate API calls typically have structured, lower-entropy payloads.',\r\n recommendation:\r\n 'Investigate the payload contents. High entropy in outbound data often indicates stolen credentials or encrypted exfiltration.',\r\n minEvents: 3,\r\n timeWindowSeconds: 180,\r\n detect: (events: NetworkEvent[]): number => {\r\n // Look for events with high body entropy\r\n const highEntropyEvents = events.filter(\r\n e => e.bodyEntropy !== undefined && e.bodyEntropy > 7.0 && e.requestSize > 1000\r\n );\r\n\r\n if (highEntropyEvents.length === 0) return 0;\r\n const ratio = highEntropyEvents.length / events.length;\r\n\r\n if (ratio > 0.5 && highEntropyEvents.length >= 5) return 90;\r\n if (ratio > 0.3) return 70;\r\n if (highEntropyEvents.length >= 3) return 55;\r\n return 0;\r\n },\r\n },\r\n {\r\n id: 'SN-015',\r\n severity: 'medium',\r\n title: 'Unexpected outbound connection during idle',\r\n description:\r\n 'MCP server made network requests when no user-initiated tool calls were active. Legitimate MCP servers should only make network requests in response to tool invocations.',\r\n recommendation:\r\n 'Review why this MCP server is making network requests when idle. This could indicate background beaconing or telemetry.',\r\n minEvents: 2,\r\n timeWindowSeconds: 60,\r\n detect: (events: NetworkEvent[]): number => {\r\n // This detection relies on the caller marking events as \"idle\" context\r\n // For now, we detect based on timing — requests that happen without\r\n // a preceding tool call within 5 seconds\r\n // (The full implementation requires integration with the runtime proxy)\r\n if (events.length >= 3) return 50;\r\n if (events.length >= 2) return 30;\r\n return 0;\r\n },\r\n },\r\n];\r\n\r\n// ──────────────────────────────────────────────────────────\r\n// CREDENTIAL-SPECIFIC EXFILTRATION PATTERNS\r\n// Patterns that look for credential-shaped data in network\r\n// requests. These check request URLs, headers, and body hashes\r\n// for signs of stolen secrets being transmitted.\r\n// ──────────────────────────────────────────────────────────\r\n\r\nexport const CREDENTIAL_EXFIL_PATTERNS: EndpointPattern[] = [\r\n {\r\n id: 'SN-020',\r\n severity: 'critical',\r\n title: 'SSH key in outbound request',\r\n urlPattern: /[?&](?:key|data|payload|content|body)=[^&]*(?:ssh-rsa|ssh-ed25519|PRIVATE\\s*KEY)/i,\r\n description:\r\n 'MCP server appears to be transmitting SSH key material in a URL parameter. This matches the Invariant Labs exfiltration attack vector.',\r\n recommendation:\r\n 'CRITICAL: Your SSH keys may be compromised. Rotate all SSH keys immediately.',\r\n },\r\n {\r\n id: 'SN-021',\r\n severity: 'critical',\r\n title: 'API key/token in outbound URL',\r\n urlPattern: /[?&](?:key|token|secret|api_key|apikey|auth|password|credential)=(?!(?:test|demo|example|placeholder))[A-Za-z0-9_-]{20,}/i,\r\n description:\r\n 'MCP server is transmitting what appears to be an API key or token in a URL parameter to an external destination.',\r\n recommendation:\r\n 'CRITICAL: Rotate the compromised API key/token immediately. Check your .env files for exposed secrets.',\r\n },\r\n {\r\n id: 'SN-022',\r\n severity: 'critical',\r\n title: 'AWS credential in outbound request',\r\n urlPattern: /(?:AKIA[0-9A-Z]{16}|(?:aws_secret_access_key|aws_access_key_id)\\s*[:=]\\s*[A-Za-z0-9/+=]{20,})/i,\r\n description:\r\n 'MCP server is transmitting AWS credentials. AWS access keys follow a known format (AKIA...).',\r\n recommendation:\r\n 'CRITICAL: Deactivate the compromised AWS keys immediately via IAM console.',\r\n },\r\n];\r\n\r\n// ──────────────────────────────────────────────────────────\r\n// THREAT SCORING\r\n// Calculate a Sentinel threat score based on findings.\r\n// ──────────────────────────────────────────────────────────\r\n\r\nexport function calculateThreatScore(findings: SentinelFinding[]): number {\r\n if (findings.length === 0) return 0;\r\n\r\n let score = 0;\r\n for (const f of findings) {\r\n const severityWeight =\r\n f.severity === 'critical' ? 30 :\r\n f.severity === 'high' ? 20 :\r\n f.severity === 'medium' ? 10 :\r\n f.severity === 'low' ? 5 : 2;\r\n\r\n // Weight by confidence — a 90% confidence critical is scarier than a 40% one\r\n score += severityWeight * (f.confidence / 100);\r\n }\r\n\r\n // Cap at 100\r\n return Math.min(100, Math.round(score));\r\n}\r\n\r\nexport function threatLevelFromScore(score: number): SentinelThreatLevel {\r\n if (score >= 70) return 'critical';\r\n if (score >= 40) return 'malicious';\r\n if (score >= 15) return 'suspicious';\r\n return 'clean';\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\nimport type { SentinelReport, SentinelThreatLevel } from '../sentinel/index.js';\r\nimport { SENTINEL_MARKETING } from '../sentinel/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// ============================================================\r\n// Sentinel Output\r\n// ============================================================\r\n\r\nconst SENTINEL_THREAT_COLORS: Record<SentinelThreatLevel, (text: string) => string> = {\r\n clean: chalk.green,\r\n suspicious: chalk.yellow,\r\n malicious: chalk.red,\r\n critical: chalk.bgRed.white.bold,\r\n};\r\n\r\nconst SENTINEL_THREAT_ICONS: Record<SentinelThreatLevel, string> = {\r\n clean: '✓',\r\n suspicious: '⚠',\r\n malicious: '✗',\r\n critical: '!!!',\r\n};\r\n\r\n/** Print a Sentinel monitoring report */\r\nexport function printSentinelReport(report: SentinelReport): void {\r\n const color = SENTINEL_THREAT_COLORS[report.threatLevel];\r\n const icon = SENTINEL_THREAT_ICONS[report.threatLevel];\r\n\r\n console.log('');\r\n console.log(\r\n chalk.bold(` ${icon} `) +\r\n chalk.bold(`Sentinel: ${report.serverName}`) +\r\n ' ' +\r\n color(`[Threat: ${report.threatScore}/100 ${report.threatLevel.toUpperCase()}]`)\r\n );\r\n\r\n console.log(chalk.gray(` Monitored: ${report.monitoringDuration.toFixed(0)}s | Events: ${report.totalEvents} | Destinations: ${report.uniqueDestinations.length}`));\r\n\r\n if (report.uniqueDestinations.length > 0) {\r\n console.log(chalk.gray(` Destinations:`));\r\n for (const dest of report.uniqueDestinations.slice(0, 10)) {\r\n console.log(chalk.gray(` → ${dest}`));\r\n }\r\n if (report.uniqueDestinations.length > 10) {\r\n console.log(chalk.gray(` ... and ${report.uniqueDestinations.length - 10} more`));\r\n }\r\n }\r\n\r\n if (report.findings.length === 0) {\r\n console.log(chalk.green(' No suspicious network behavior detected.'));\r\n } else {\r\n console.log(chalk.dim(` ${report.findings.length} finding(s):`));\r\n for (const finding of report.findings) {\r\n const fColor = SEVERITY_COLORS[finding.severity];\r\n const fIcon = SEVERITY_ICONS[finding.severity];\r\n console.log(\r\n ` ${fColor(`[${fIcon}]`)} ${fColor(finding.severity.toUpperCase())} ` +\r\n chalk.white(finding.title) +\r\n chalk.gray(` (${finding.id})`)\r\n );\r\n console.log(chalk.gray(` ${finding.description}`));\r\n if (finding.evidence && finding.evidence.length > 0) {\r\n console.log(chalk.gray(` Evidence: ${finding.evidence.length} network event(s)`));\r\n }\r\n console.log(chalk.cyan(` → ${finding.recommendation}`));\r\n }\r\n }\r\n\r\n console.log(chalk.gray(` ${report.startedAt} → ${report.endedAt}`));\r\n console.log('');\r\n}\r\n\r\n/** Print the Sentinel upgrade prompt (for free tier) */\r\nexport function printSentinelUpgrade(): void {\r\n console.log('');\r\n console.log(chalk.bold.hex('#2C4A7C')(` 🛡️ ${SENTINEL_MARKETING.featureName}`));\r\n console.log(chalk.white(` ${SENTINEL_MARKETING.tagline}`));\r\n console.log('');\r\n for (const cat of SENTINEL_MARKETING.categories) {\r\n console.log(chalk.white(` ${cat.icon} ${chalk.bold(cat.name)}`));\r\n console.log(chalk.gray(` ${cat.desc}`));\r\n }\r\n console.log('');\r\n console.log(chalk.yellow(` ⚡ Sentinel is a Pro feature. Upgrade to unlock runtime monitoring:`));\r\n console.log(chalk.cyan(` https://vigile.dev/pricing`));\r\n console.log('');\r\n console.log(chalk.gray(` Pro ($9.99/mo) — 5-min sessions, 3 servers, behavioral detection`));\r\n console.log(chalk.gray(` Pro+ ($29.99/mo) — 30-min sessions, 10 servers, DNS tunneling & C2 detection`));\r\n console.log('');\r\n}\r\n\r\n// ============================================================\r\n// API Upload & Auth Output\r\n// ============================================================\r\n\r\n/** Print auth status info */\r\nexport function printAuthStatus(info: {\r\n authenticated: boolean;\r\n source?: 'env' | 'config';\r\n email?: string;\r\n tier?: string;\r\n name?: string;\r\n error?: string;\r\n}): void {\r\n if (info.authenticated) {\r\n console.log(chalk.green(` Authenticated as ${info.email}`));\r\n console.log(chalk.gray(` Tier: ${(info.tier || 'free').toUpperCase()}`));\r\n if (info.name) {\r\n console.log(chalk.gray(` Name: ${info.name}`));\r\n }\r\n console.log(\r\n chalk.gray(\r\n ` Source: ${info.source === 'env' ? 'VIGILE_TOKEN env var' : '~/.vigile/config.json'}`,\r\n ),\r\n );\r\n } else {\r\n console.log(chalk.yellow(' Not authenticated.'));\r\n if (info.error) {\r\n console.log(chalk.red(` Error: ${info.error}`));\r\n }\r\n console.log(chalk.gray(' Run `vigile-scan auth login` or set VIGILE_TOKEN to authenticate.'));\r\n }\r\n console.log('');\r\n}\r\n\r\n/** Print auth login success */\r\nexport function printAuthLoginSuccess(email: string, tier: string): void {\r\n console.log('');\r\n console.log(chalk.green(' Authenticated successfully!'));\r\n console.log(chalk.gray(` Email: ${email}`));\r\n console.log(chalk.gray(` Tier: ${tier.toUpperCase()}`));\r\n console.log(chalk.gray(' Token stored in ~/.vigile/config.json'));\r\n console.log('');\r\n}\r\n\r\n/** Print upload success summary */\r\nexport function printUploadSuccess(summary: {\r\n mcpUploaded: number;\r\n skillsUploaded: number;\r\n failures: number;\r\n}): void {\r\n const total = summary.mcpUploaded + summary.skillsUploaded;\r\n if (total > 0) {\r\n console.log(chalk.green(` Uploaded ${total} result(s) to Vigile registry.`));\r\n if (summary.mcpUploaded > 0) {\r\n console.log(chalk.gray(` MCP servers: ${summary.mcpUploaded}`));\r\n }\r\n if (summary.skillsUploaded > 0) {\r\n console.log(chalk.gray(` Skills: ${summary.skillsUploaded}`));\r\n }\r\n }\r\n if (summary.failures > 0) {\r\n console.log(chalk.yellow(` ${summary.failures} upload(s) failed (results saved locally).`));\r\n }\r\n console.log('');\r\n}\r\n\r\n/** Print upload skip message (when not authenticated or --no-upload) */\r\nexport function printUploadSkipped(reason: 'not-authenticated' | 'no-upload-flag'): void {\r\n if (reason === 'not-authenticated') {\r\n console.log(\r\n chalk.gray(' Tip: Run `vigile-scan auth login` to upload results to the Vigile registry.'),\r\n );\r\n console.log('');\r\n }\r\n // For --no-upload flag, we stay silent (user explicitly opted out)\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","// ============================================================\r\n// Vigile CLI — Auth Management\r\n// ============================================================\r\n// Handles token resolution, persistent storage in ~/.vigile/config.json,\r\n// and provides the auth login/status/logout logic.\r\n//\r\n// Token resolution priority:\r\n// 1. VIGILE_TOKEN environment variable (CI/CD friendly)\r\n// 2. ~/.vigile/config.json (persistent local config)\r\n\r\nimport { readFile, writeFile, mkdir } from 'fs/promises';\r\nimport { join } from 'path';\r\nimport { homedir } from 'os';\r\nimport { VigileApiClient, type ApiUserInfo } from './client.js';\r\n\r\n// ──────────────────────────────────────────────────────────\r\n// Config file paths\r\n// ──────────────────────────────────────────────────────────\r\n\r\nconst CONFIG_DIR = join(homedir(), '.vigile');\r\nconst CONFIG_FILE = join(CONFIG_DIR, 'config.json');\r\n\r\n// ──────────────────────────────────────────────────────────\r\n// Config shape\r\n// ──────────────────────────────────────────────────────────\r\n\r\ninterface VigileConfig {\r\n token?: string;\r\n api_url?: string;\r\n user?: {\r\n email: string;\r\n tier: string;\r\n name?: string;\r\n };\r\n}\r\n\r\n// ──────────────────────────────────────────────────────────\r\n// Token & URL Resolution\r\n// ──────────────────────────────────────────────────────────\r\n\r\n/**\r\n * Resolve the API token.\r\n * Priority: VIGILE_TOKEN env var > ~/.vigile/config.json\r\n */\r\nexport async function resolveToken(): Promise<string | null> {\r\n // 1. Environment variable takes precedence\r\n const envToken = process.env.VIGILE_TOKEN;\r\n if (envToken && envToken.length > 0) {\r\n return envToken;\r\n }\r\n\r\n // 2. Config file\r\n const config = await loadConfig();\r\n return config.token || null;\r\n}\r\n\r\n/**\r\n * Resolve the API base URL.\r\n * Priority: VIGILE_API_URL env var > ~/.vigile/config.json > default\r\n */\r\nexport async function resolveApiUrl(): Promise<string> {\r\n const envUrl = process.env.VIGILE_API_URL;\r\n if (envUrl) return envUrl;\r\n\r\n const config = await loadConfig();\r\n return config.api_url || 'https://api.vigile.dev';\r\n}\r\n\r\n// ──────────────────────────────────────────────────────────\r\n// Config File Operations\r\n// ──────────────────────────────────────────────────────────\r\n\r\nasync function loadConfig(): Promise<VigileConfig> {\r\n try {\r\n const raw = await readFile(CONFIG_FILE, 'utf-8');\r\n return JSON.parse(raw) as VigileConfig;\r\n } catch {\r\n return {};\r\n }\r\n}\r\n\r\nasync function saveConfig(config: VigileConfig): Promise<void> {\r\n await mkdir(CONFIG_DIR, { recursive: true });\r\n await writeFile(CONFIG_FILE, JSON.stringify(config, null, 2), {\r\n mode: 0o600, // Owner read/write only\r\n });\r\n}\r\n\r\n// ──────────────────────────────────────────────────────────\r\n// Auth Actions\r\n// ──────────────────────────────────────────────────────────\r\n\r\n/**\r\n * Login: validate a token against the API and store it persistently.\r\n */\r\nexport async function authLogin(token: string): Promise<{\r\n success: boolean;\r\n user?: ApiUserInfo;\r\n error?: string;\r\n}> {\r\n const apiUrl = await resolveApiUrl();\r\n const client = new VigileApiClient(apiUrl, token);\r\n const result = await client.getMe();\r\n\r\n if (!result.ok) {\r\n return { success: false, error: result.error };\r\n }\r\n\r\n // Persist token and user info\r\n const config = await loadConfig();\r\n config.token = token;\r\n config.user = {\r\n email: result.data.email,\r\n tier: result.data.tier,\r\n name: result.data.name || undefined,\r\n };\r\n await saveConfig(config);\r\n\r\n return { success: true, user: result.data };\r\n}\r\n\r\n/**\r\n * Check current authentication status.\r\n */\r\nexport async function authStatus(): Promise<{\r\n authenticated: boolean;\r\n source?: 'env' | 'config';\r\n user?: ApiUserInfo;\r\n error?: string;\r\n}> {\r\n const envToken = process.env.VIGILE_TOKEN;\r\n const config = await loadConfig();\r\n const token = envToken || config.token;\r\n\r\n if (!token) {\r\n return { authenticated: false };\r\n }\r\n\r\n const source: 'env' | 'config' = envToken ? 'env' : 'config';\r\n const apiUrl = await resolveApiUrl();\r\n const client = new VigileApiClient(apiUrl, token);\r\n const result = await client.getMe();\r\n\r\n if (!result.ok) {\r\n return { authenticated: false, source, error: result.error };\r\n }\r\n\r\n return { authenticated: true, source, user: result.data };\r\n}\r\n\r\n/**\r\n * Logout: clear stored credentials from config file.\r\n */\r\nexport async function authLogout(): Promise<void> {\r\n const config = await loadConfig();\r\n delete config.token;\r\n delete config.user;\r\n await saveConfig(config);\r\n}\r\n\r\n// ──────────────────────────────────────────────────────────\r\n// Convenience: get an authenticated client (or null)\r\n// ──────────────────────────────────────────────────────────\r\n\r\n/**\r\n * Create an authenticated API client, or return null if no token is available.\r\n * Used by the scan upload step and Sentinel API integration.\r\n */\r\nexport async function getAuthenticatedClient(): Promise<VigileApiClient | null> {\r\n const token = await resolveToken();\r\n if (!token) return null;\r\n\r\n const apiUrl = await resolveApiUrl();\r\n return new VigileApiClient(apiUrl, token);\r\n}\r\n","// ============================================================\r\n// Vigile CLI — API Client\r\n// ============================================================\r\n// Lightweight HTTP client for communicating with the Vigile API.\r\n// Uses Node 18+ native fetch. Graceful degradation: if the API\r\n// is unreachable, the CLI continues in local-only mode.\r\n//\r\n// All methods return a discriminated union:\r\n// { ok: true, data: T } | { ok: false, error: string, status: number }\r\n// This avoids thrown exceptions and makes error handling explicit.\r\n\r\nconst DEFAULT_API_URL = 'https://api.vigile.dev';\r\nconst API_TIMEOUT_MS = 15_000; // 15 seconds\r\nconst CLI_VERSION = '0.2.0';\r\n\r\n// ──────────────────────────────────────────────────────────\r\n// Response Types\r\n// ──────────────────────────────────────────────────────────\r\n\r\nexport interface ApiUserInfo {\r\n id: number;\r\n email: string;\r\n name: string | null;\r\n tier: string; // 'free' | 'pro' (team/enterprise planned for future)\r\n}\r\n\r\nexport interface ApiScanResponse {\r\n id: number;\r\n server_name: string;\r\n trust_score: number;\r\n trust_level: string;\r\n score_breakdown: Record<string, number> | null;\r\n findings: Array<Record<string, unknown>>;\r\n findings_count: number;\r\n critical_count: number;\r\n high_count: number;\r\n scanner_version: string;\r\n scanned_at: string;\r\n}\r\n\r\nexport interface ApiSkillScanResponse {\r\n id: number;\r\n skill_name: string;\r\n file_type: string;\r\n platform: string;\r\n trust_score: number;\r\n trust_level: string;\r\n score_breakdown: Record<string, number> | null;\r\n findings: Array<Record<string, unknown>>;\r\n findings_count: number;\r\n critical_count: number;\r\n high_count: number;\r\n scanner_version: string;\r\n scanned_at: string;\r\n}\r\n\r\nexport interface ApiSentinelSessionResponse {\r\n session_id: number;\r\n server_name: string;\r\n max_duration_seconds: number;\r\n status: string;\r\n created_at: string;\r\n}\r\n\r\nexport interface ApiSentinelEventsResponse {\r\n status: string;\r\n events_received: number;\r\n total_events: number;\r\n}\r\n\r\nexport interface ApiSentinelReportResponse {\r\n id: number;\r\n session_id: number;\r\n server_name: string;\r\n monitoring_duration: number;\r\n total_events: number;\r\n unique_destinations: string[];\r\n findings: Array<{\r\n id: string;\r\n category: string;\r\n severity: string;\r\n title: string;\r\n description: string;\r\n server_name: string;\r\n evidence_count: number;\r\n recommendation: string;\r\n confidence: number;\r\n }>;\r\n threat_level: string;\r\n threat_score: number;\r\n started_at: string;\r\n ended_at: string;\r\n user_tier: string;\r\n}\r\n\r\n/** Discriminated union result type */\r\nexport type ApiResult<T> =\r\n | { ok: true; data: T }\r\n | { ok: false; error: string; status: number };\r\n\r\n// ──────────────────────────────────────────────────────────\r\n// API Client\r\n// ──────────────────────────────────────────────────────────\r\n\r\nexport class VigileApiClient {\r\n private baseUrl: string;\r\n private token: string | null;\r\n\r\n constructor(baseUrl?: string, token?: string | null) {\r\n this.baseUrl = (baseUrl || process.env.VIGILE_API_URL || DEFAULT_API_URL)\r\n .replace(/\\/+$/, ''); // strip trailing slashes\r\n this.token = token || null;\r\n }\r\n\r\n get isAuthenticated(): boolean {\r\n return this.token !== null && this.token.length > 0;\r\n }\r\n\r\n // ── Private helpers ──\r\n\r\n private async request<T>(\r\n method: string,\r\n path: string,\r\n body?: unknown,\r\n ): Promise<ApiResult<T>> {\r\n const controller = new AbortController();\r\n const timeoutId = setTimeout(() => controller.abort(), API_TIMEOUT_MS);\r\n\r\n const headers: Record<string, string> = {\r\n 'Content-Type': 'application/json',\r\n 'User-Agent': `vigile-cli/${CLI_VERSION}`,\r\n };\r\n if (this.token) {\r\n headers['Authorization'] = `Bearer ${this.token}`;\r\n }\r\n\r\n try {\r\n const response = await fetch(`${this.baseUrl}${path}`, {\r\n method,\r\n headers,\r\n body: body ? JSON.stringify(body) : undefined,\r\n signal: controller.signal,\r\n });\r\n\r\n clearTimeout(timeoutId);\r\n\r\n if (!response.ok) {\r\n const errorBody = await response.text();\r\n let errorMessage: string;\r\n try {\r\n const parsed = JSON.parse(errorBody);\r\n errorMessage = typeof parsed.detail === 'string'\r\n ? parsed.detail\r\n : JSON.stringify(parsed.detail);\r\n } catch {\r\n errorMessage = errorBody || `HTTP ${response.status}`;\r\n }\r\n return { ok: false, error: errorMessage, status: response.status };\r\n }\r\n\r\n const data = (await response.json()) as T;\r\n return { ok: true, data };\r\n } catch (err) {\r\n clearTimeout(timeoutId);\r\n if (err instanceof Error && err.name === 'AbortError') {\r\n return { ok: false, error: 'Request timed out', status: 0 };\r\n }\r\n return {\r\n ok: false,\r\n error: err instanceof Error ? err.message : 'Network error',\r\n status: 0,\r\n };\r\n }\r\n }\r\n\r\n // ── Auth ──\r\n\r\n async getMe(): Promise<ApiResult<ApiUserInfo>> {\r\n return this.request<ApiUserInfo>('GET', '/api/v1/auth/me');\r\n }\r\n\r\n // ── Scanning ──\r\n\r\n async submitMCPScan(payload: {\r\n server_name: string;\r\n source?: string;\r\n package_url?: string;\r\n repo_url?: string;\r\n description?: string;\r\n readme?: string;\r\n tool_descriptions?: string[];\r\n maintainer?: string;\r\n license?: string;\r\n homepage?: string;\r\n }): Promise<ApiResult<ApiScanResponse>> {\r\n return this.request<ApiScanResponse>('POST', '/api/v1/scan/', payload);\r\n }\r\n\r\n async submitSkillScan(payload: {\r\n skill_name: string;\r\n content: string;\r\n file_type?: string;\r\n platform?: string;\r\n source?: string;\r\n }): Promise<ApiResult<ApiSkillScanResponse>> {\r\n return this.request<ApiSkillScanResponse>('POST', '/api/v1/scan/skill', payload);\r\n }\r\n\r\n // ── Sentinel ──\r\n\r\n async createSentinelSession(\r\n serverName: string,\r\n durationSeconds: number,\r\n client: string = 'cli',\r\n ): Promise<ApiResult<ApiSentinelSessionResponse>> {\r\n return this.request<ApiSentinelSessionResponse>('POST', '/api/v1/sentinel/sessions', {\r\n server_name: serverName,\r\n duration_seconds: durationSeconds,\r\n client,\r\n });\r\n }\r\n\r\n async submitSentinelEvents(\r\n sessionId: number,\r\n events: Array<{\r\n timestamp: number;\r\n server_name: string;\r\n method: string;\r\n url: string;\r\n destination_ip?: string | null;\r\n port: number;\r\n request_size: number;\r\n response_size?: number | null;\r\n status_code?: number | null;\r\n headers?: Record<string, string> | null;\r\n dns_query_type?: string | null;\r\n tls: boolean;\r\n body_hash?: string | null;\r\n body_entropy?: number | null;\r\n }>,\r\n ): Promise<ApiResult<ApiSentinelEventsResponse>> {\r\n return this.request<ApiSentinelEventsResponse>(\r\n 'POST',\r\n `/api/v1/sentinel/sessions/${sessionId}/events`,\r\n { session_id: sessionId, events },\r\n );\r\n }\r\n\r\n async analyzeSentinelSession(\r\n sessionId: number,\r\n ): Promise<ApiResult<ApiSentinelReportResponse>> {\r\n return this.request<ApiSentinelReportResponse>(\r\n 'POST',\r\n `/api/v1/sentinel/sessions/${sessionId}/analyze`,\r\n );\r\n }\r\n}\r\n"],"mappings":"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","names":["import_chalk","import_promises","import_path","import_path","import_path","import_path","import_path","import_promises","import_fs","import_path","_","SEVERITY_DEDUCTIONS","deduplicateFindings","chalk","import_promises","import_path","import_os","chalk","ora"]}