UNPKG

mira-consciousness

Version:

Memory & Intelligence Retention Archive - Preserving The Spark

362 lines 16.2 kB
/** * Shell Command Analyzer for Cross-Platform Compatibility * Detects platform-specific shell commands and scripts */ import fs from 'fs-extra'; import * as path from 'path'; import { glob } from 'glob'; export class ShellCommandAnalyzer { projectRoot; constructor(projectRoot) { this.projectRoot = projectRoot; } isTestFile(filePath) { const testPatterns = [ /\.test\.[jt]sx?$/, /\.spec\.[jt]sx?$/, /\/__tests__\//, /\/tests?\//, /\/e2e\//, /test-utils/, /test-helpers/ ]; return testPatterns.some(pattern => pattern.test(filePath)); } isAnalyzerFile(filePath) { return /\/analyzers?\//.test(filePath); } async analyze(issues) { const analysis = { bashScripts: [], powershellScripts: [], batchScripts: [], crossPlatformIssues: [] }; await Promise.all([ this.analyzeShellCommands(issues, analysis), this.analyzeScriptFiles(analysis), this.analyzeProcessHandling(issues) ]); return analysis; } async analyzeShellCommands(issues, analysis) { const shellPatterns = [ { pattern: /exec\(['"`]ls\s/g, type: 'unix_command', message: 'Unix-specific command "ls" - not available on Windows', platforms: ['linux', 'macOS', 'unix'], severity: 'high', recommendation: 'Use fs.readdir() or cross-platform alternatives' }, { pattern: /exec\(['"`]dir\s/g, type: 'windows_command', message: 'Windows-specific command "dir" - not available on Unix', platforms: ['windows'], severity: 'high', recommendation: 'Use fs.readdir() or cross-platform alternatives' }, { pattern: /exec\(['"`]cat\s/g, type: 'unix_command', message: 'Unix-specific command "cat" - not available on Windows', platforms: ['linux', 'macOS', 'unix'], severity: 'medium', recommendation: 'Use fs.readFile() for cross-platform file reading' }, { pattern: /exec\(['"`]type\s/g, type: 'windows_command', message: 'Windows-specific command "type" - not available on Unix', platforms: ['windows'], severity: 'medium', recommendation: 'Use fs.readFile() for cross-platform file reading' }, { pattern: /exec\(['"`]rm\s/g, type: 'unix_command', message: 'Unix-specific command "rm" - not available on Windows', platforms: ['linux', 'macOS', 'unix'], severity: 'medium', recommendation: 'Use fs.remove() or fs.unlink() for cross-platform file deletion' }, { pattern: /exec\(['"`]del\s/g, type: 'windows_command', message: 'Windows-specific command "del" - not available on Unix', platforms: ['windows'], severity: 'medium', recommendation: 'Use fs.remove() or fs.unlink() for cross-platform file deletion' }, { pattern: /exec\(['"`]which\s/g, type: 'unix_command', message: 'Unix-specific command "which" - not available on Windows', platforms: ['linux', 'macOS', 'unix'], severity: 'medium', recommendation: 'Use npm package "which" or "command-exists" for cross-platform' }, { pattern: /exec\(['"`]where\s/g, type: 'windows_command', message: 'Windows-specific command "where" - not available on Unix', platforms: ['windows'], severity: 'medium', recommendation: 'Use npm package "which" or "command-exists" for cross-platform' }, { pattern: /spawn\(['"`]sh['"`]/g, type: 'unix_shell', message: 'Unix shell "sh" - not available on Windows', platforms: ['linux', 'macOS', 'unix'], severity: 'high', recommendation: 'Use cross-platform shell detection or Node.js APIs' }, { pattern: /spawn\(['"`]bash['"`]/g, type: 'unix_shell', message: 'Bash shell - not available on Windows by default', platforms: ['linux', 'macOS', 'unix'], severity: 'high', recommendation: 'Check for bash availability or use cross-platform alternatives' }, { pattern: /spawn\(['"`]cmd['"`]/g, type: 'windows_shell', message: 'Windows shell "cmd" - not available on Unix', platforms: ['windows'], severity: 'high', recommendation: 'Use cross-platform shell detection or Node.js APIs' }, { pattern: /spawn\(['"`]powershell['"`]/g, type: 'windows_shell', message: 'PowerShell - not available on all Unix systems', platforms: ['windows'], severity: 'medium', recommendation: 'Check for PowerShell availability or use alternatives' }, { pattern: /exec\(['"`]grep\s/g, type: 'unix_command', message: 'Unix command "grep" - not available on Windows', platforms: ['linux', 'macOS', 'unix'], severity: 'medium', recommendation: 'Use Node.js string/file search or cross-platform grep tool' }, { pattern: /exec\(['"`]find\s/g, type: 'unix_command', message: 'Unix command "find" - different behavior on Windows', platforms: ['linux', 'macOS', 'unix'], severity: 'medium', recommendation: 'Use glob patterns or fs operations' }, { pattern: /exec\(['"`]touch\s/g, type: 'unix_command', message: 'Unix command "touch" - not available on Windows', platforms: ['linux', 'macOS', 'unix'], severity: 'low', recommendation: 'Use fs.writeFile() or fs.utimes() for cross-platform' }, { pattern: /exec\(['"`]chmod\s/g, type: 'unix_command', message: 'Unix command "chmod" - not applicable on Windows', platforms: ['linux', 'macOS', 'unix'], severity: 'medium', recommendation: 'Use fs.chmod() with platform checks' }, { pattern: /exec\(['"`]chown\s/g, type: 'unix_command', message: 'Unix command "chown" - not available on Windows', platforms: ['linux', 'macOS', 'unix'], severity: 'medium', recommendation: 'Windows has different ownership model' }, { pattern: /exec\(['"`]ps\s/g, type: 'unix_command', message: 'Unix command "ps" - not available on Windows', platforms: ['linux', 'macOS', 'unix'], severity: 'medium', recommendation: 'Use process management libraries like "ps-list"' }, { pattern: /exec\(['"`]kill\s/g, type: 'unix_command', message: 'Unix command "kill" - different on Windows', platforms: ['linux', 'macOS', 'unix'], severity: 'medium', recommendation: 'Use process.kill() for cross-platform' } ]; await this.scanWithPatterns(issues, shellPatterns, 'Shell Commands', analysis); } async analyzeScriptFiles(analysis) { try { // Find shell scripts const bashScripts = await glob('**/*.{sh,bash}', { cwd: this.projectRoot, ignore: ['node_modules/**', '.git/**'] }); analysis.bashScripts = bashScripts; // Find PowerShell scripts const psScripts = await glob('**/*.{ps1,psm1,psd1}', { cwd: this.projectRoot, ignore: ['node_modules/**', '.git/**'] }); analysis.powershellScripts = psScripts; // Find batch scripts const batchScripts = await glob('**/*.{bat,cmd}', { cwd: this.projectRoot, ignore: ['node_modules/**', '.git/**'] }); analysis.batchScripts = batchScripts; // Check package.json scripts const packageJsonPath = path.join(this.projectRoot, 'package.json'); if (await fs.pathExists(packageJsonPath)) { const packageJson = await fs.readJson(packageJsonPath); if (packageJson.scripts) { for (const [name, script] of Object.entries(packageJson.scripts)) { const scriptStr = script; // Check for shell-specific syntax if (scriptStr.includes('&&') || scriptStr.includes('||')) { analysis.crossPlatformIssues.push(`Script "${name}" uses shell operators that may behave differently across platforms`); } if (scriptStr.includes('$') && !scriptStr.includes('$npm_')) { analysis.crossPlatformIssues.push(`Script "${name}" uses $ variables that may not work on Windows`); } if (scriptStr.includes('\\')) { analysis.crossPlatformIssues.push(`Script "${name}" uses backslashes that may cause issues`); } } } } } catch (error) { // Skip if analysis fails } } async analyzeProcessHandling(issues) { const processPatterns = [ { pattern: /process\.platform\s*===?\s*['"]win32['"]/g, type: 'platform_check', message: 'Windows platform check - good practice', platforms: ['all'], severity: 'low', recommendation: 'Continue using platform checks for compatibility' }, { pattern: /process\.env\.HOME/g, type: 'env_home', message: 'HOME environment variable - not set on Windows', platforms: ['linux', 'macOS', 'unix'], severity: 'medium', recommendation: 'Use os.homedir() or process.env.HOME || process.env.USERPROFILE' }, { pattern: /process\.env\.USERPROFILE/g, type: 'env_userprofile', message: 'USERPROFILE environment variable - Windows specific', platforms: ['windows'], severity: 'medium', recommendation: 'Use os.homedir() for cross-platform home directory' }, { pattern: /process\.env\.PATH\.split\(['"]:['"]\)/g, type: 'path_separator', message: 'Unix PATH separator ":" - Windows uses ";"', platforms: ['linux', 'macOS', 'unix'], severity: 'high', recommendation: 'Use path.delimiter for cross-platform PATH splitting' }, { pattern: /process\.env\.PATH\.split\(['"]\;['"]\)/g, type: 'path_separator', message: 'Windows PATH separator ";" - Unix uses ":"', platforms: ['windows'], severity: 'high', recommendation: 'Use path.delimiter for cross-platform PATH splitting' }, { pattern: /SIGKILL|SIGTERM|SIGHUP|SIGINT/g, type: 'unix_signals', message: 'Unix signals - limited support on Windows', platforms: ['linux', 'macOS', 'unix'], severity: 'medium', recommendation: 'Only SIGTERM and SIGINT work reliably on Windows' }, { pattern: /process\.getuid|process\.getgid/g, type: 'unix_process_methods', message: 'Unix process methods - not available on Windows', platforms: ['linux', 'macOS', 'unix'], severity: 'medium', recommendation: 'Check platform before using Unix-specific process methods' } ]; await this.scanWithPatterns(issues, processPatterns, 'Process Handling'); } async scanWithPatterns(issues, patterns, category, analysis) { try { const files = await glob('**/*.{js,ts,jsx,tsx}', { cwd: this.projectRoot, ignore: ['node_modules/**', '.git/**', 'dist/**', 'build/**'] }); for (const file of files) { const filePath = path.join(this.projectRoot, file); const isTest = this.isTestFile(file); const isAnalyzer = this.isAnalyzerFile(file); try { const content = await fs.readFile(filePath, 'utf-8'); const lines = content.split('\n'); lines.forEach((line, index) => { for (const pattern of patterns) { if (pattern.pattern.test(line)) { // Skip test files with intentional bad patterns if (isTest) { const context = lines.slice(Math.max(0, index - 3), Math.min(lines.length, index + 3)).join('\n'); if (context.includes('cross-platform issues for testing') || context.includes('intentional') || context.includes('// File with') && context.includes('issues')) { return; } } // Skip analyzer example patterns if (isAnalyzer && (line.includes('pattern:') || line.includes('bad:') || line.includes('message:'))) { return; } issues.push({ file, line: index + 1, severity: pattern.severity, category, type: pattern.type, message: pattern.message, affectedPlatforms: pattern.platforms, recommendation: pattern.recommendation, codeSnippet: line.trim() }); if (analysis && pattern.type.includes('command')) { analysis.crossPlatformIssues.push(`${pattern.type} in ${file}:${index + 1}`); } } } }); } catch (error) { // Skip files that can't be read } } } catch (error) { // Skip if glob fails } } } //# sourceMappingURL=ShellCommandAnalyzer.js.map