scan_rules_file
Scan AI rules files for prompt injection and backdoor attacks, preventing hidden security threats in configuration files.
Instructions
Scan an AI configuration/rules file for prompt injection and Rules File Backdoor attacks
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| file_path | Yes | Path to the AI rules file (e.g., .cursorrules, CLAUDE.md) |
Implementation Reference
- src/index.ts:278-325 (registration)Registration of the 'scan_rules_file' MCP tool using server.tool(). Defines the schema (file_path string), description, and handler.
// Tool 3: scan_rules_file server.tool( "scan_rules_file", "Scan an AI configuration/rules file for prompt injection and Rules File Backdoor attacks", { file_path: z.string().describe("Path to the AI rules file (e.g., .cursorrules, CLAUDE.md)"), }, async ({ file_path }) => { try { const resolvedPath = path.resolve(file_path); const content = fs.readFileSync(resolvedPath, "utf-8"); // Force rules-backdoor scanner + invisible/bidi/stego scanners const scanners = [ new RulesBackdoorScanner(), new InvisibleCharScanner(), new BidiScanner(), new SteganographyScanner(), new EncodingScanner(), ]; // Override isRulesFile to always return true for this tool (scanners[0] as RulesBackdoorScanner).isRulesFile = () => true; const result = scanFileContent(content, resolvedPath, scanners); const summary = buildSummary([result]); let output = formatSummary(summary); if (result.findings.length === 0) { output += "\n\nNo prompt injection or hidden content detected in this rules file."; } return { content: [{ type: "text" as const, text: output }], }; } catch (err: any) { return { content: [ { type: "text" as const, text: `Error scanning rules file: ${err.message}`, }, ], isError: true, }; } } ); - src/index.ts:285-325 (handler)Handler logic: resolves path, reads file, creates specific scanners (RulesBackdoorScanner, InvisibleCharScanner, BidiScanner, SteganographyScanner, EncodingScanner) with isRulesFile overridden to true, calls scanFileContent, builds summary, and formats output.
async ({ file_path }) => { try { const resolvedPath = path.resolve(file_path); const content = fs.readFileSync(resolvedPath, "utf-8"); // Force rules-backdoor scanner + invisible/bidi/stego scanners const scanners = [ new RulesBackdoorScanner(), new InvisibleCharScanner(), new BidiScanner(), new SteganographyScanner(), new EncodingScanner(), ]; // Override isRulesFile to always return true for this tool (scanners[0] as RulesBackdoorScanner).isRulesFile = () => true; const result = scanFileContent(content, resolvedPath, scanners); const summary = buildSummary([result]); let output = formatSummary(summary); if (result.findings.length === 0) { output += "\n\nNo prompt injection or hidden content detected in this rules file."; } return { content: [{ type: "text" as const, text: output }], }; } catch (err: any) { return { content: [ { type: "text" as const, text: `Error scanning rules file: ${err.message}`, }, ], isError: true, }; } } ); - src/index.ts:282-284 (schema)Zod schema for the tool input: file_path is a required string describing the path to an AI rules file.
{ file_path: z.string().describe("Path to the AI rules file (e.g., .cursorrules, CLAUDE.md)"), }, - src/index.ts:154-173 (helper)scanFileContent helper function that runs multiple scanners over file content and returns a ScanResult with sorted findings.
function scanFileContent( content: string, filePath: string, scanners: Scanner[] ): ScanResult { const findings: Finding[] = []; for (const scanner of scanners) { findings.push(...scanner.scan(content, filePath)); } // Sort by severity const severityOrder = { critical: 0, high: 1, medium: 2, low: 3, info: 4 }; findings.sort((a, b) => severityOrder[a.severity] - severityOrder[b.severity]); return { file: filePath, findings, scannedAt: new Date().toISOString(), }; } - src/scanner/rules-backdoor.ts:89-161 (helper)RulesBackdoorScanner implementation: scans for prompt injection patterns and hidden unicode characters in AI rules files.
export class RulesBackdoorScanner implements Scanner { name = "rules-backdoor"; isRulesFile(filePath: string): boolean { const basename = path.basename(filePath).toLowerCase(); const relativePath = filePath.replace(/\\/g, "/"); return RULES_FILE_PATTERNS.some((pattern) => { const normalizedPattern = pattern.toLowerCase(); return ( basename === normalizedPattern || relativePath.toLowerCase().endsWith(normalizedPattern) ); }); } scan(content: string, filePath: string): Finding[] { const findings: Finding[] = []; if (!this.isRulesFile(filePath)) { return findings; } // Check for prompt injection patterns for (const pattern of INJECTION_PATTERNS) { let match: RegExpExecArray | null; const regex = new RegExp(pattern.regex.source, pattern.regex.flags); while ((match = regex.exec(content)) !== null) { const { line, column } = getLineAndColumn(content, match.index); findings.push({ category: "rules-backdoor", severity: pattern.severity, file: filePath, line, column, message: pattern.message, snippet: getSnippet(content, line), recommendation: pattern.recommendation, }); } } // Also run invisible char / bidi checks on rules files with elevated severity // (invisible chars in rules files are ALWAYS suspicious) for (let i = 0; i < content.length; i++) { const cp = content.codePointAt(i)!; if ( cp === 0x200b || cp === 0x200c || cp === 0x200d || cp === 0x200e || cp === 0x200f || cp === 0xfeff || cp === 0x2060 || cp === 0x00ad || (cp >= 0x202a && cp <= 0x202e) || (cp >= 0x2066 && cp <= 0x2069) ) { const { line, column } = getLineAndColumn(content, i); findings.push({ category: "rules-backdoor", severity: "critical", file: filePath, line, column, message: `Hidden unicode character in AI rules file — Rules File Backdoor attack indicator`, snippet: getSnippet(content, line), codePoint: `U+${cp.toString(16).toUpperCase().padStart(4, "0")}`, recommendation: "Invisible unicode characters in AI rules files are the primary vector for Rules File Backdoor attacks. They wrap malicious prompts that the AI reads but humans cannot see. Remove all invisible characters from this file.", }); } } return findings; } }