import { z } from "zod";
/**
* Quality Tools - specific code quality and security analysis
*/
// ============================================
// Analyze Complexity
// ============================================
export const analyzeComplexitySchema = {
name: "analyze_complexity",
description: "Analyzes code complexity to identify refactoring candidates",
inputSchema: z.object({
code: z.string().describe("Source code to analyze"),
language: z.string().describe("Programming language"),
})
};
export function analyzeComplexityHandler(args: { code: string; language: string }) {
const { code, language } = args;
// 1. Cyclomatic Complexity (heuristic: count decision points)
const lines = code.split('\n');
let complexity = 1; // Base path
let maxNesting = 0;
let currentNesting = 0;
const branchPatterns = [
/\bif\b/, /\belse\b/, /\bfor\b/, /\bwhile\b/, /\bcase\b/, /\bcatch\b/,
/\?.*\:/, /\|\|/, /\&\&/
];
// 2. Halstead Volume (heuristic: count operands and operators)
let operators = 0;
let operands = 0;
const operatorPattern = /[\+\-\*\/\=\%\&\>\|\<\!\^]/g;
const operandPattern = /\b[a-zA-Z_]\w*\b/g;
lines.forEach(line => {
const trimmed = line.trim();
if (!trimmed || trimmed.startsWith('//') || trimmed.startsWith('*')) return;
// Nesting
if (trimmed.endsWith('{') || trimmed.endsWith(':')) currentNesting++;
if (trimmed.startsWith('}') || trimmed.startsWith('end')) currentNesting--;
maxNesting = Math.max(maxNesting, currentNesting);
// Branching
branchPatterns.forEach(p => { if (p.test(line)) complexity++; });
// Halstead
const ops = line.match(operatorPattern);
if (ops) operators += ops.length;
const oprs = line.match(operandPattern);
if (oprs) operands += oprs.length;
});
// Score Calculation
let rating = "Low";
let status = "✅ Good";
if (complexity > 20 || maxNesting > 4) {
rating = "High";
status = "⚠️ Refactor Needed";
} else if (complexity > 10 || maxNesting > 2) {
rating = "Medium";
status = "⚠️ Warning";
}
const simpleMetrics = `
- **Cyclomatic Complexity**: ~${complexity} (Target: < 10)
- **Nesting Depth**: ${maxNesting} (Target: < 3)
- **Estimated Operators**: ${operators}
- **Estimated Operands**: ${operands}
`;
const recommendations = [];
if (complexity > 10) recommendations.push("- Extract complex logic into helper functions.");
if (maxNesting > 3) recommendations.push("- Use guard clauses (early returns) to flatten nesting.");
if (lines.length > 200) recommendations.push("- Module is too large; consider splitting.");
return {
content: [{
type: "text",
text: `# Code Quality Report\n\n**Rating**: ${rating} - ${status}\n${simpleMetrics}\n\n## Recommendations\n${recommendations.length > 0 ? recommendations.join('\n') : "- Code structure looks healthy."}`
}]
};
}
// ============================================
// Security Scan
// ============================================
export const securityScanSchema = {
name: "security_scan",
description: "Scans code for common security vulnerabilities (OWASP Top 10)",
inputSchema: z.object({
code: z.string().describe("Source code to scan"),
language: z.string().describe("Programming language"),
focus: z.enum(["injection", "auth", "secrets", "all"]).optional().default("all")
})
};
export function securityScanHandler(args: { code: string; language: string; focus?: string }) {
const { code, language, focus } = args;
const risks: string[] = [];
// --- Secrets Detection ---
const secretPatterns = [
/(api_?key|secret|password|auth_?token)[\s]*[:=][\s]*['"][a-zA-Z0-9_\-\.]{8,}['"]/i, // Variable assignment
/BEGIN RSA PRIVATE KEY/, // Keys
/eyJ[a-zA-Z0-9\-_]+\.[a-zA-Z0-9\-_]+\.[a-zA-Z0-9\-_]+/ // JWT structure
];
if (focus === "all" || focus === "secrets") {
secretPatterns.forEach(p => {
if (p.test(code)) {
const match = code.match(p)?.[0] || "pattern";
risks.push(`🚨 **Hardcoded Secret**: Detected potential secret at \`${match.substring(0, 15)}...\`. Use environment variables.`);
}
});
}
// --- Injection Risks ---
if (focus === "all" || focus === "injection") {
if (/execute\s*\(\s*['"]SELECT.*?\+/i.test(code) || /query\s*\(\s*['"].*?\$\{/i.test(code)) {
risks.push("🚨 **SQL Injection**: String concatenation detected in SQL query. Use parameterized queries.");
}
if (/(exec|spawn|eval|system)\s*\(/i.test(code)) {
risks.push("⚠️ **RCE Risk**: Dangerous function execution detected (`eval`, `exec`). Ensure strict input validation.");
}
if (/dangerouslySetInnerHTML/.test(code)) {
risks.push("⚠️ **XSS Risk**: `dangerouslySetInnerHTML` used in React. Sanitize content.");
}
}
// --- Auth & Crypto ---
if (focus === "all" || focus === "auth") {
if (/password\s*==\s*|password\s*===/i.test(code)) {
risks.push("⚠️ **Timing Attack**: Insecure password comparison. Use `crypto.timingSafeEqual`.");
}
if (/md5\(/i.test(code) || /sha1\(/i.test(code)) {
risks.push("⚠️ **Weak Crypto**: MD5/SHA1 detected. Use SHA256 or bcrypt/argon2.");
}
}
// --- General Best Practices ---
if (code.includes("http://")) {
risks.push("⚠️ **Insecure Transport**: `http://` URL found. Use `https://`.");
}
if (/console\.log/.test(code) && !/debug/i.test(code) && code.length > 500) {
risks.push("ℹ️ **Cleanup**: `console.log` statements found in production-like code.");
}
return {
content: [{
type: "text",
text: `# Security Scan Report\n\n**Focus**: ${focus}\n\n## Findings\n${risks.length > 0 ? risks.join('\n\n') : "✅ No obvious issues detected via static analysis."}\n\n*Note: This scan checks for common patterns but is not a replacement for dedicated security tools like Snyk or SonarQube.*`
}]
};
}
// Export all
export const qualityTools = {
analyzeComplexitySchema, analyzeComplexityHandler,
securityScanSchema, securityScanHandler
};