UNPKG

ctrlshiftleft

Version:

AI-powered toolkit for embedding QA and security testing into development workflows

343 lines (291 loc) 11.3 kB
#!/usr/bin/env node /** * Ctrl+Shift+Left Checklist Generator * Automatically creates QA and security checklists for code components */ 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 = process.argv[3] || '/Users/johngaspar/CascadeProjects/ctrlshiftleft/vscode-ext-test/checklist.json'; const COMPONENT_NAME = path.basename(TARGET_FILE, path.extname(TARGET_FILE)); // Ensure all required directories exist function ensureDirectoriesExist() { // Ensure output directory for JSON and MD files exists const outputDir = path.dirname(OUTPUT_FILE); if (!fs.existsSync(outputDir)) { console.log(`Creating output directory: ${outputDir}`); fs.mkdirSync(outputDir, { recursive: true }); } // Create other potential directories that might be needed const dirs = [ './security-reports', './checklists', path.join(path.dirname(TARGET_FILE), 'checklists'), '/Users/johngaspar/CascadeProjects/ctrlshiftleft/reports', '/Users/johngaspar/CascadeProjects/ctrlshiftleft/checklists' ]; dirs.forEach(dir => { if (!fs.existsSync(dir)) { console.log(`Creating directory: ${dir}`); fs.mkdirSync(dir, { recursive: true }); } }); } // Function to analyze a file and generate checklist items function analyzeFileForChecklist(filePath) { console.log(`Analyzing component: ${filePath}`); if (!fs.existsSync(filePath)) { console.error(`File not found: ${filePath}`); return null; } const content = fs.readFileSync(filePath, 'utf8'); // Initialize checklist structure const checklist = { component: COMPONENT_NAME, timestamp: new Date().toISOString(), categories: [ { name: "Functionality", items: [] }, { name: "Security", items: [] }, { name: "Accessibility", items: [] }, { name: "Performance", items: [] }, { name: "Testing", items: [] } ] }; // Analyze file content and generate checklist items // Functionality checks checklist.categories[0].items.push({ id: "func-1", description: "All required form fields have validation", status: content.includes('validate') || content.includes('validation') ? "PASS" : "REVIEW", details: content.includes('validate') || content.includes('validation') ? "Validation functions detected in the code" : "No validation functions detected, please verify form validation manually" }); checklist.categories[0].items.push({ id: "func-2", description: "Error messages are displayed for invalid inputs", status: content.includes('error') && content.includes('message') ? "PASS" : "REVIEW", details: content.includes('error') && content.includes('message') ? "Error message handling detected" : "Could not detect error message handling, verify manually" }); // Security checks checklist.categories[1].items.push({ id: "sec-1", description: "Input sanitization before processing", status: content.includes('sanitize') || content.includes('purify') || content.includes('escape') ? "PASS" : "FAIL", details: content.includes('sanitize') || content.includes('purify') || content.includes('escape') ? "Input sanitization detected" : "No input sanitization detected, please implement to prevent XSS and injection attacks" }); checklist.categories[1].items.push({ id: "sec-2", description: "Sensitive data handling (credit cards, PII)", status: (content.includes('password') || content.includes('credit') || content.includes('card')) && !content.includes('encryption') ? "FAIL" : "PASS", details: (content.includes('password') || content.includes('credit') || content.includes('card')) && !content.includes('encryption') ? "Sensitive data detected without proper encryption" : "No unprotected sensitive data detected" }); checklist.categories[1].items.push({ id: "sec-3", description: "CSRF protection mechanisms", status: content.includes('csrf') || content.includes('token') ? "PASS" : "REVIEW", details: content.includes('csrf') || content.includes('token') ? "CSRF token handling detected" : "No CSRF protection detected, verify if needed for this component" }); checklist.categories[1].items.push({ id: "sec-4", description: "No client-side storage of sensitive data", status: content.includes('localStorage') || content.includes('sessionStorage') ? "FAIL" : "PASS", details: content.includes('localStorage') || content.includes('sessionStorage') ? "Client-side storage detected, verify no sensitive data is stored" : "No client-side storage detected" }); // Accessibility checks checklist.categories[2].items.push({ id: "a11y-1", description: "Form elements have associated labels", status: content.includes('<label') || content.includes('aria-label') ? "PASS" : "FAIL", details: content.includes('<label') || content.includes('aria-label') ? "Label elements detected" : "No label elements detected, please add for accessibility" }); checklist.categories[2].items.push({ id: "a11y-2", description: "Color contrast is sufficient", status: "REVIEW", details: "Manual verification required for color contrast" }); checklist.categories[2].items.push({ id: "a11y-3", description: "Keyboard navigation is supported", status: content.includes('onKeyDown') || content.includes('onKeyPress') ? "PASS" : "REVIEW", details: content.includes('onKeyDown') || content.includes('onKeyPress') ? "Keyboard event handlers detected" : "No keyboard event handlers detected, verify keyboard navigation manually" }); // Performance checks checklist.categories[3].items.push({ id: "perf-1", description: "No expensive operations in render method", status: "REVIEW", details: "Manual code review required to identify expensive operations" }); checklist.categories[3].items.push({ id: "perf-2", description: "Memoization for expensive calculations", status: content.includes('useMemo') || content.includes('useCallback') ? "PASS" : "REVIEW", details: content.includes('useMemo') || content.includes('useCallback') ? "Memoization hooks detected" : "No memoization detected, verify if needed for performance optimization" }); // Testing checks checklist.categories[4].items.push({ id: "test-1", description: "Unit tests cover component logic", status: "REVIEW", details: "Verify that unit tests exist and cover component logic" }); checklist.categories[4].items.push({ id: "test-2", description: "Integration tests for form submission", status: "REVIEW", details: "Verify that integration tests exist for form submission flow" }); checklist.categories[4].items.push({ id: "test-3", description: "End-to-end tests for critical paths", status: "REVIEW", details: "E2E tests are essential for payment forms, check existence" }); return checklist; } // Function to generate a human-readable markdown report function generateMarkdownReport(checklist) { const markdownPath = OUTPUT_FILE.replace('.json', '.md'); let markdown = `# QA & Security Checklist for ${checklist.component}\n\n`; markdown += `*Generated by Ctrl+Shift+Left on ${new Date().toLocaleString()}*\n\n`; // Summary statistics const stats = { pass: 0, fail: 0, review: 0 }; checklist.categories.forEach(category => { category.items.forEach(item => { if (item.status === "PASS") stats.pass++; else if (item.status === "FAIL") stats.fail++; else if (item.status === "REVIEW") stats.review++; }); }); markdown += `## Summary\n\n`; markdown += `- ✅ Passed: ${stats.pass}\n`; markdown += `- ❌ Failed: ${stats.fail}\n`; markdown += `- ⚠️ Needs Review: ${stats.review}\n\n`; // Results by category checklist.categories.forEach(category => { markdown += `## ${category.name}\n\n`; category.items.forEach(item => { const statusIcon = item.status === "PASS" ? "✅" : (item.status === "FAIL" ? "❌" : "⚠️"); markdown += `### ${statusIcon} ${item.description}\n\n`; markdown += `**Status:** ${item.status}\n\n`; markdown += `**Details:** ${item.details}\n\n`; }); }); // Add remediation advice markdown += `## Remediation Steps\n\n`; if (stats.fail > 0) { markdown += `### Critical Issues to Fix\n\n`; checklist.categories.forEach(category => { category.items.forEach(item => { if (item.status === "FAIL") { markdown += `- **${item.description}**: ${item.details}\n`; } }); }); markdown += `\n`; } if (stats.review > 0) { markdown += `### Items Needing Manual Review\n\n`; checklist.categories.forEach(category => { category.items.forEach(item => { if (item.status === "REVIEW") { markdown += `- **${item.description}**: ${item.details}\n`; } }); }); } fs.writeFileSync(markdownPath, markdown); console.log(`Markdown report written to: ${markdownPath}`); return markdownPath; } // Main function function main() { console.log('Ctrl+Shift+Left Checklist Generator'); console.log('=================================='); // Ensure all directories exist first ensureDirectoriesExist(); // Analyze file and generate checklist const checklist = analyzeFileForChecklist(TARGET_FILE); if (!checklist) { console.error('Failed to generate checklist'); process.exit(1); } // Write checklist to JSON file fs.writeFileSync(OUTPUT_FILE, JSON.stringify(checklist, null, 2)); console.log(`Checklist written to: ${OUTPUT_FILE}`); // Generate human-readable report const markdownPath = generateMarkdownReport(checklist); // Print summary console.log('\nChecklist Generation Summary:'); let passCount = 0; let failCount = 0; let reviewCount = 0; checklist.categories.forEach(category => { category.items.forEach(item => { if (item.status === "PASS") passCount++; else if (item.status === "FAIL") failCount++; else if (item.status === "REVIEW") reviewCount++; }); }); console.log(`Component: ${checklist.component}`); console.log(`Passed: ${passCount} items`); console.log(`Failed: ${failCount} items`); console.log(`Needs review: ${reviewCount} items`); if (failCount > 0) { console.log('\n⚠️ ATTENTION: Critical issues were found that need to be addressed!'); } try { if (process.platform === 'darwin') { // macOS execSync(`open "${markdownPath}"`); } else if (process.platform === 'win32') { // Windows execSync(`start "" "${markdownPath}"`); } else { // Linux execSync(`xdg-open "${markdownPath}"`); } } catch (error) { console.log('Could not automatically open the report.'); } } // Run the checklist generator main();