ctrlshiftleft
Version:
AI-powered toolkit for embedding QA and security testing into development workflows
221 lines (185 loc) • 6.81 kB
JavaScript
/**
* Ctrl+Shift+Left Security Analyzer
* A direct command-line tool for analyzing security risks in code
*/
const fs = require('fs');
const path = require('path');
const { execSync } = require('child_process');
// Configuration
const TARGET_FILE = process.argv[2] || '/Users/johngaspar/CascadeProjects/ctrlshiftleft/demo/src/components/PaymentForm.tsx';
const OUTPUT_FILE = '/Users/johngaspar/CascadeProjects/ctrlshiftleft/vscode-ext-test/security-report.md';
const REPORTS_DIR = '/Users/johngaspar/CascadeProjects/ctrlshiftleft/security-reports';
// Ensure all required directories exist
function ensureDirectoriesExist() {
// Ensure output directory for the main report exists
const outputDir = path.dirname(OUTPUT_FILE);
if (!fs.existsSync(outputDir)) {
console.log(`Creating output directory: ${outputDir}`);
fs.mkdirSync(outputDir, { recursive: true });
}
// Ensure security-reports directory exists
if (!fs.existsSync(REPORTS_DIR)) {
console.log(`Creating reports directory: ${REPORTS_DIR}`);
fs.mkdirSync(REPORTS_DIR, { recursive: true });
}
// Create other potential directories that might be needed
const dirs = [
'./security-reports',
path.join(path.dirname(TARGET_FILE), 'security-reports'),
'/Users/johngaspar/CascadeProjects/ctrlshiftleft/reports',
'/Users/johngaspar/CascadeProjects/ctrlshiftleft/tests'
];
dirs.forEach(dir => {
if (!fs.existsSync(dir)) {
console.log(`Creating directory: ${dir}`);
fs.mkdirSync(dir, { recursive: true });
}
});
}
// Security patterns to scan for
const SECURITY_PATTERNS = [
{
pattern: /password/i,
severity: 'HIGH',
title: 'Unprotected Password Field',
description: 'Password fields should be properly secured with encryption or hashing',
remediation: 'Ensure passwords are never stored in plaintext and are properly hashed before storage'
},
{
pattern: /innerHTML|dangerouslySetInnerHTML/,
severity: 'CRITICAL',
title: 'Potential XSS Vulnerability',
description: 'Using innerHTML or dangerouslySetInnerHTML can lead to cross-site scripting attacks',
remediation: 'Use safer alternatives like textContent or sanitize input before using'
},
{
pattern: /localStorage|sessionStorage/,
severity: 'MEDIUM',
title: 'Client-side Storage of Sensitive Data',
description: 'Storing sensitive information in localStorage or sessionStorage is insecure',
remediation: 'Use secure storage mechanisms or encrypted tokens for sensitive data'
},
{
pattern: /fetch\s*\(/,
severity: 'INFO',
title: 'Network Request',
description: 'Network requests should be properly secured and validated',
remediation: 'Ensure proper CORS settings and validate all input and output'
}
];
// Function to analyze a file for security issues
function analyzeFile(filePath) {
console.log(`Analyzing file: ${filePath}`);
// Check if file exists
if (!fs.existsSync(filePath)) {
console.error(`File not found: ${filePath}`);
return [];
}
// Read file content
const content = fs.readFileSync(filePath, 'utf8');
const lines = content.split('\n');
// Find security issues
const issues = [];
lines.forEach((line, index) => {
SECURITY_PATTERNS.forEach(pattern => {
if (pattern.pattern.test(line)) {
issues.push({
lineNumber: index + 1,
line: line.trim(),
severity: pattern.severity,
title: pattern.title,
description: pattern.description,
remediation: pattern.remediation
});
}
});
});
return issues;
}
// Generate a markdown report
function generateReport(filePath, issues) {
const fileName = path.basename(filePath);
let report = `# Security Analysis for ${fileName}\n\n`;
report += `*Generated by Ctrl+Shift+Left on ${new Date().toLocaleString()}*\n\n`;
if (issues.length === 0) {
report += '✅ No security issues found!\n';
return report;
}
// Group by severity
const severityGroups = {
'CRITICAL': [],
'HIGH': [],
'MEDIUM': [],
'LOW': [],
'INFO': []
};
issues.forEach(issue => {
if (severityGroups[issue.severity]) {
severityGroups[issue.severity].push(issue);
} else {
severityGroups.INFO.push(issue);
}
});
// Summary
report += '## Summary\n\n';
const criticalCount = severityGroups.CRITICAL.length;
const highCount = severityGroups.HIGH.length;
const mediumCount = severityGroups.MEDIUM.length;
const lowCount = severityGroups.LOW.length;
const infoCount = severityGroups.INFO.length;
report += `- 🚨 Critical: ${criticalCount}\n`;
report += `- ⚠️ High: ${highCount}\n`;
report += `- ⚠️ Medium: ${mediumCount}\n`;
report += `- ℹ️ Low: ${lowCount}\n`;
report += `- ℹ️ Info: ${infoCount}\n\n`;
// Detailed issues
report += '## Detailed Issues\n\n';
['CRITICAL', 'HIGH', 'MEDIUM', 'LOW', 'INFO'].forEach(severity => {
if (severityGroups[severity].length > 0) {
report += `### ${severity} Severity\n\n`;
severityGroups[severity].forEach(issue => {
report += `#### ${issue.title}\n\n`;
report += `- **Line ${issue.lineNumber}**: \`${issue.line}\`\n`;
report += `- **Description**: ${issue.description}\n`;
report += `- **Remediation**: ${issue.remediation}\n\n`;
});
}
});
// Add test generation recommendation
report += '## Next Steps\n\n';
report += '1. Review and fix the identified security issues\n';
report += '2. Generate tests to verify fixes using `npx ctrlshiftleft gen <file> --output tests`\n';
report += '3. Run the generated tests to validate your changes\n';
return report;
}
// Main function
function main() {
console.log('Ctrl+Shift+Left Security Analyzer');
console.log('=================================');
// Ensure all directories exist first
ensureDirectoriesExist();
// Analyze file
const issues = analyzeFile(TARGET_FILE);
// Generate report
const report = generateReport(TARGET_FILE, issues);
// Write report to file
fs.writeFileSync(OUTPUT_FILE, report);
// Print summary
console.log(`Analysis complete. Found ${issues.length} potential security issues.`);
console.log(`Report written to: ${OUTPUT_FILE}`);
// Try to open the report in the default browser
try {
if (process.platform === 'darwin') { // macOS
execSync(`open "${OUTPUT_FILE}"`);
} else if (process.platform === 'win32') { // Windows
execSync(`start "" "${OUTPUT_FILE}"`);
} else { // Linux
execSync(`xdg-open "${OUTPUT_FILE}"`);
}
} catch (error) {
console.log('Could not automatically open the report.');
}
}
// Run the analyzer
main();