UNPKG

agentsqripts

Version:

Comprehensive static code analysis toolkit for identifying technical debt, security vulnerabilities, performance issues, and code quality problems

214 lines (187 loc) 6.38 kB
/** * @file Context analyzer for static bug detection * @description Determines file context (test, config, framework) for appropriate rules */ /** * Check if file is a test file * @param {string} filePath - File path * @returns {boolean} True if test file */ function isTestFile(filePath) { const testPatterns = [ '.test.', '.spec.', '__tests__', '__test__', 'test/', 'tests/', 'spec/', 'specs/', '.test.js', '.spec.js', 'test.js', 'spec.js' ]; const lowerPath = filePath.toLowerCase(); return testPatterns.some(pattern => lowerPath.includes(pattern)); } /** * Check if file is a configuration file * @param {string} filePath - File path * @returns {boolean} True if config file */ function isConfigFile(filePath) { const configPatterns = [ 'config.js', 'configuration.js', '.config.js', 'webpack.', 'rollup.', 'vite.', 'babel.', 'jest.config', 'tsconfig', 'jsconfig', 'package.json', '.eslintrc', '.prettierrc' ]; const filename = filePath.split('/').pop().toLowerCase(); return configPatterns.some(pattern => filename.includes(pattern)); } /** * Check if file is a CLI tool * @param {string} filePath - File path * @param {string} content - File content * @returns {boolean} True if CLI tool */ function isCLITool(filePath, content) { let weight = 0; const lowerPath = filePath.toLowerCase(); // Strong path indicators (high weight) if (lowerPath.includes('/bin/')) weight += 5; if (lowerPath.includes('/cli/')) weight += 4; if (lowerPath.includes('/scripts/')) weight += 3; // Filename patterns (medium weight) const filename = lowerPath.split('/').pop(); if (filename === 'cli.js') weight += 4; if (filename.includes('-cli.js')) weight += 3; if (filename.startsWith('analyze-')) weight += 3; if (filename.includes('script')) weight += 2; if (filename.includes('tool')) weight += 2; // Shebang is a very strong indicator if (content.startsWith('#!/usr/bin/env node') || content.startsWith('#!/usr/bin/node')) { weight += 6; } // CLI-specific patterns with weights const cliPatterns = [ { pattern: 'process.exit', weight: 3 }, { pattern: 'process.argv', weight: 3 }, { pattern: 'getProcessArgs', weight: 3 }, { pattern: 'parseArgs', weight: 3 }, { pattern: 'showHelp', weight: 2 }, { pattern: '__dirname', weight: 1 }, { pattern: 'console.log', weight: 1 }, { pattern: 'console.error', weight: 1 }, { pattern: 'require(', weight: 0.5 } ]; // Add weights for each pattern found cliPatterns.forEach(({ pattern, weight: patternWeight }) => { if (content.includes(pattern)) { weight += patternWeight; } }); // Check for main function pattern common in CLIs if (content.includes('function main()') || content.includes('async function main()')) { weight += 2; } // Check for commander/yargs/minimist usage if (content.includes('require("commander")') || content.includes('require("yargs")') || content.includes('require("minimist")') || content.includes("require('commander')") || content.includes("require('yargs')") || content.includes("require('minimist')")) { weight += 4; } // Lower threshold - if weight >= 5, it's likely a CLI return weight >= 5; } /** * Detect framework from file content and structure * @param {string} content - File content * @param {string} filePath - File path * @returns {string} Framework name or 'vanilla' */ function detectFramework(content, filePath) { // React detection if (content.includes('import React') || content.includes('from "react"') || content.includes("from 'react'") || content.includes('useState') || content.includes('useEffect') || content.includes('jsx')) { return 'react'; } // Vue detection if (content.includes('import Vue') || content.includes('from "vue"') || content.includes("from 'vue'") || content.includes('createApp') || filePath.endsWith('.vue')) { return 'vue'; } // Angular detection if (content.includes('@angular/') || content.includes('import { Component }') || content.includes('@Component') || content.includes('ngOnInit')) { return 'angular'; } // Node.js backend detection if (content.includes('express') || content.includes('require("http")') || content.includes('createServer') || content.includes('app.listen')) { return 'node'; } return 'vanilla'; } /** * Get appropriate bug detection rules based on context * @param {string} filePath - File path * @param {string} content - File content * @returns {Object} Context info with applicable rules */ function getFileContext(filePath, content) { const context = { isTest: isTestFile(filePath), isConfig: isConfigFile(filePath), isCLI: isCLITool(filePath, content), framework: detectFramework(content, filePath), rules: { allowEval: false, allowConsoleLog: false, checkAsyncAwait: true, checkNullSafety: true, checkResourceLeaks: true, frameworkSpecific: true } }; // Adjust rules based on context if (context.isTest) { context.rules.allowEval = true; // Tests might use eval for dynamic scenarios context.rules.allowConsoleLog = true; // Tests often log for debugging } if (context.isConfig) { context.rules.checkAsyncAwait = false; // Config files rarely use async context.rules.checkResourceLeaks = false; // Not applicable to configs } if (context.isCLI) { context.rules.allowConsoleLog = true; // CLI tools need console.log for output } return context; } /** * Check if a pattern should be ignored based on context * @param {string} bugType - Type of bug * @param {Object} context - File context * @returns {boolean} True if should be ignored */ function shouldIgnoreBug(bugType, context) { if (context.isTest) { // Ignore certain patterns in test files if (['dangerous_eval', 'console_log', 'hardcoded_values'].includes(bugType)) { return true; } } if (context.isConfig) { // Ignore certain patterns in config files if (['unused_variable', 'missing_await'].includes(bugType)) { return true; } } return false; } module.exports = { isTestFile, isConfigFile, detectFramework, getFileContext, shouldIgnoreBug };