ctrlshiftleft
Version:
AI-powered toolkit for embedding QA and security testing into development workflows
238 lines (231 loc) • 7.88 kB
JavaScript
;
/**
* Config Loader
*
* Loads custom validation patterns and configuration from the .ctrlshiftleft/config.js file
* allowing users to customize the security analyzer behavior.
*/
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.loadConfig = loadConfig;
exports.createSampleConfig = createSampleConfig;
const fs = __importStar(require("fs"));
const path = __importStar(require("path"));
// Default configuration
const DEFAULT_CONFIG = {
security: {
customPatterns: [],
disabledPatterns: [],
frameworks: {
react: { enabled: true },
nextjs: { enabled: true },
express: { enabled: true }
},
output: {
format: 'markdown',
directory: './security-reports'
}
},
testing: {
performance: {
enabled: true,
reportDir: './performance-reports'
},
generation: {
framework: 'playwright',
outputDir: './tests'
}
},
global: {
lineEndings: 'auto',
createDirs: true
}
};
/**
* Loads the configuration from .ctrlshiftleft/config.js
* Falls back to defaults if configuration doesn't exist
*/
function loadConfig(projectRoot = process.cwd()) {
const configPath = path.join(projectRoot, '.ctrlshiftleft', 'config.js');
try {
if (fs.existsSync(configPath)) {
// Delete require cache to ensure we get the latest version
delete require.cache[require.resolve(configPath)];
// Load the user's configuration
const userConfig = require(configPath);
// Convert string patterns to RegExp
if (userConfig.security?.customPatterns) {
userConfig.security.customPatterns = userConfig.security.customPatterns.map((pattern) => ({
...pattern,
pattern: typeof pattern.pattern === 'string' ? new RegExp(pattern.pattern) : pattern.pattern
}));
}
Object.keys(userConfig.security?.frameworks || {}).forEach(framework => {
if (userConfig.security?.frameworks?.[framework]?.customPatterns) {
userConfig.security.frameworks[framework].customPatterns =
userConfig.security.frameworks[framework].customPatterns.map((pattern) => ({
...pattern,
pattern: typeof pattern.pattern === 'string' ? new RegExp(pattern.pattern) : pattern.pattern
}));
}
});
// Merge with default configuration
return deepMerge(DEFAULT_CONFIG, userConfig);
}
}
catch (error) {
const errorMessage = error instanceof Error ? error.message : String(error);
console.warn(`Error loading configuration: ${errorMessage}`);
console.warn('Using default configuration instead');
}
return { ...DEFAULT_CONFIG };
}
/**
* Creates a sample configuration file if one doesn't exist
*/
function createSampleConfig(projectRoot = process.cwd()) {
const configDir = path.join(projectRoot, '.ctrlshiftleft');
const configPath = path.join(configDir, 'config.js');
// Create the .ctrlshiftleft directory if it doesn't exist
if (!fs.existsSync(configDir)) {
fs.mkdirSync(configDir, { recursive: true });
}
// Only create the sample config if it doesn't already exist
if (!fs.existsSync(configPath)) {
const sampleConfig = `/**
* ctrl.shift.left Configuration
*
* This file configures the behavior of ctrl.shift.left tools
* including security analysis, test generation, and performance tracking.
*/
module.exports = {
// Security analyzer configuration
security: {
// Add your custom security patterns
customPatterns: [
{
id: 'custom-react-error',
pattern: /componentDidCatch\\s*\\(\\s*error\\s*\\)\\s*{[^}]*console\\.log/,
severity: 'HIGH',
title: 'Unhandled Error in componentDidCatch',
description: 'Errors should be properly handled in componentDidCatch, not just logged',
remediation: 'Implement proper error handling and user feedback in componentDidCatch',
category: 'react-errors',
frameworks: ['react']
}
],
// Disable specific built-in patterns by ID if needed
disabledPatterns: [
// 'react-keys', 'unsafe-innerhtml'
],
// Framework-specific settings
frameworks: {
react: {
enabled: true,
// Add React-specific patterns here
customPatterns: []
},
nextjs: {
enabled: true,
// Add Next.js-specific patterns here
customPatterns: []
},
express: {
enabled: true,
// Add Express-specific patterns here
customPatterns: []
}
},
// Output configuration
output: {
format: 'markdown',
directory: './security-reports'
}
},
// Test generation configuration
testing: {
// Performance tracking settings
performance: {
enabled: true,
reportDir: './performance-reports'
},
// Test generation settings
generation: {
framework: 'playwright',
outputDir: './tests'
}
},
// Global settings
global: {
// Line endings to use in generated files (auto, lf, crlf)
lineEndings: 'auto',
// Whether to create directories if they don't exist
createDirs: true
}
};
`;
fs.writeFileSync(configPath, sampleConfig);
console.log(`Created sample configuration at ${configPath}`);
}
}
/**
* Helper function to deeply merge objects
*/
function deepMerge(target, source) {
const output = { ...target };
if (isObject(target) && isObject(source)) {
Object.keys(source).forEach(key => {
if (isObject(source[key])) {
if (!(key in target)) {
Object.assign(output, { [key]: source[key] });
}
else {
output[key] = deepMerge(target[key], source[key]);
}
}
else {
Object.assign(output, { [key]: source[key] });
}
});
}
return output;
}
/**
* Helper function to check if a value is an object
*/
function isObject(item) {
return (item && typeof item === 'object' && !Array.isArray(item));
}
//# sourceMappingURL=configLoader.js.map