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 ---
// --- Secrets and Credentials ---
const secretPatterns = [
{
pattern:
/(api_?key|secret|password|auth_?token)[\s]*[:=][\s]*['"][a-zA-Z0-9_\-\.]{8,}['"]/i,
msg: "Hardcoded secret detected",
},
{ pattern: /BEGIN RSA PRIVATE KEY/, msg: "Private key found" },
{
pattern: /eyJ[a-zA-Z0-9\-_]+\.[a-zA-Z0-9\-_]+\.[a-zA-Z0-9\-_]+/,
msg: "Potential JWT token hardcoded",
},
{ pattern: /(aws_?access|aws_?secret)/i, msg: "AWS credentials detected" },
{ pattern: /stripe_?sk/i, msg: "Stripe secret key detected" },
];
if (focus === "all" || focus === "secrets") {
secretPatterns.forEach(({ pattern, msg }) => {
if (pattern.test(code)) {
const match = code.match(pattern)?.[0] || "pattern";
risks.push(
`🚨 **${msg}**: Found \`${match.substring(0, 15)}...\`. Use environment variables.`,
);
}
});
}
// --- Injection Risks ---
if (focus === "all" || focus === "injection") {
// SQL Injection
if (
/execute\s*\(\s*['"]SELECT.*?\+/i.test(code) ||
/query\s*\(\s*['"].*?\$\{/i.test(code)
) {
risks.push(
"🚨 **SQL Injection**: String concatenation in SQL query. Use parameterized queries/prepared statements.",
);
}
// Command Injection
if (/(exec|spawn|eval|system)\s*\(/i.test(code)) {
risks.push(
"⚠️ **RCE Risk**: Dangerous execution detected (`eval`, `exec`). Ensure strict input validation.",
);
}
// XSS in React/JS
if (/dangerouslySetInnerHTML/.test(code)) {
risks.push(
"⚠️ **XSS Risk**: `dangerouslySetInnerHTML` usage. Ensure content is sanitized.",
);
}
if (/javascript:/.test(code)) {
risks.push("⚠️ **XSS Risk**: `javascript:` protocol usage detected.");
}
}
// --- 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|sha1)\s*\(/.test(code)) {
risks.push(
"⚠️ **Weak Crypto**: MD5/SHA1 are obsolete. Use SHA-256 or bcrypt.",
);
}
if (
/Math\.random/.test(code) &&
(code.includes("token") || code.includes("auth"))
) {
risks.push(
"⚠️ **Weak Randomness**: `Math.random` is not cryptographically secure. Use `crypto.getRandomValues`.",
);
}
}
// --- General Security Best Practices ---
if (code.includes("http://")) {
risks.push(
"⚠️ **Insecure Transport**: `http://` found. Enforce `https://`.",
);
}
if (/disable-eslint|ts-ignore/.test(code)) {
risks.push(
"ℹ️ **Security Bypass**: Linter suppression detected. Verify if this is safe.",
);
}
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,
};