mira-consciousness
Version:
Memory & Intelligence Retention Archive - Preserving The Spark
362 lines • 16.2 kB
JavaScript
/**
* 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