UNPKG

endpoint-sentinel

Version:

User-friendly security scanner with interactive setup that scales from beginner to expert

234 lines 7.52 kB
"use strict"; /** * Configuration Management System * Handles domain-specific configurations for easy scanning setup */ var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.ConfigManager = void 0; const fs_1 = require("fs"); const path_1 = require("path"); const os_1 = require("os"); const joi_1 = __importDefault(require("joi")); class ConfigManager { configDir; configFile; store; constructor() { this.configDir = (0, path_1.join)((0, os_1.homedir)(), '.endpoint-sentinel'); this.configFile = (0, path_1.join)(this.configDir, 'config.json'); this.store = this.loadOrCreateStore(); } /** * Normalizes URL by adding protocol if missing */ normalizeUrl(target) { if (!target || typeof target !== 'string') { return target; } // If URL already has a protocol, return as-is if (/^https?:\/\//i.test(target)) { return target; } // If it looks like a domain/hostname, prepend https:// if (/^[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}(\/.*)?$/.test(target)) { return `https://${target}`; } // Return as-is for other cases (will likely fail validation) return target; } /** * Get domain from URL */ extractDomain(url) { try { const normalizedUrl = this.normalizeUrl(url); const urlObj = new URL(normalizedUrl); return urlObj.hostname; } catch { throw new Error(`Invalid URL: ${url}`); } } /** * Load existing config store or create new one */ loadOrCreateStore() { if (!(0, fs_1.existsSync)(this.configDir)) { (0, fs_1.mkdirSync)(this.configDir, { recursive: true }); } if ((0, fs_1.existsSync)(this.configFile)) { try { const data = (0, fs_1.readFileSync)(this.configFile, 'utf-8'); const parsed = JSON.parse(data); // Convert date strings back to Date objects Object.values(parsed.configs).forEach((config) => { config.createdAt = new Date(config.createdAt); config.updatedAt = new Date(config.updatedAt); }); return parsed; } catch (error) { console.warn('Config file corrupted, creating new one'); } } return { version: '1.0.0', configs: {}, globalDefaults: { rateLimit: 2, timeout: 30000, maxRedirects: 5, concurrent: 5 } }; } /** * Save config store to disk */ saveStore() { (0, fs_1.writeFileSync)(this.configFile, JSON.stringify(this.store, null, 2)); } /** * Validate domain config */ validateDomainConfig(config) { const schema = joi_1.default.object({ domain: joi_1.default.string().required(), name: joi_1.default.string().optional(), authType: joi_1.default.string().valid(...['none', 'basic', 'bearer_token', 'jwt', 'oauth2', 'session_cookie', 'api_key', 'custom']).required(), token: joi_1.default.string().optional(), cookie: joi_1.default.string().optional(), defaultKeywords: joi_1.default.array().items(joi_1.default.string()).optional(), rateLimit: joi_1.default.number().min(0.1).max(100).optional(), customHeaders: joi_1.default.object().optional(), notes: joi_1.default.string().optional(), createdAt: joi_1.default.date().optional(), updatedAt: joi_1.default.date().optional() }); const { error, value } = schema.validate(config); if (error) { throw new Error(`Config validation failed: ${error.message}`); } const now = new Date(); return { ...value, createdAt: value.createdAt || now, updatedAt: now }; } /** * Check if config exists for domain */ hasConfig(url) { const domain = this.extractDomain(url); return domain in this.store.configs; } /** * Get config for domain */ getConfig(url) { const domain = this.extractDomain(url); return this.store.configs[domain] || null; } /** * Save config for domain */ saveConfig(url, config) { const domain = this.extractDomain(url); const validatedConfig = this.validateDomainConfig({ ...config, domain }); this.store.configs[domain] = validatedConfig; this.saveStore(); return validatedConfig; } /** * Delete config for domain */ deleteConfig(url) { const domain = this.extractDomain(url); if (domain in this.store.configs) { delete this.store.configs[domain]; this.saveStore(); return true; } return false; } /** * List all saved configs */ listConfigs() { return Object.values(this.store.configs); } /** * Convert domain config to scan config */ buildScanConfig(url, overrides = {}) { const normalizedUrl = this.normalizeUrl(url); const domain = this.extractDomain(url); const domainConfig = this.store.configs[domain]; const baseConfig = { target: normalizedUrl, consent: true, ...this.store.globalDefaults }; if (domainConfig) { // Apply domain-specific settings if (domainConfig.defaultKeywords) { baseConfig.keywords = domainConfig.defaultKeywords; } if (domainConfig.rateLimit) { baseConfig.rateLimit = domainConfig.rateLimit; } // Handle authentication if (domainConfig.authType !== 'none') { if (domainConfig.cookie) { baseConfig.cookie = domainConfig.cookie; } if (domainConfig.token) { // For JWT/Bearer tokens, add to cookie or custom header if (domainConfig.authType === 'jwt' || domainConfig.authType === 'bearer_token') { baseConfig.cookie = `token=${domainConfig.token}`; } } } } // Apply any overrides return { ...baseConfig, ...overrides }; } /** * Update global defaults */ updateGlobalDefaults(defaults) { this.store.globalDefaults = { ...this.store.globalDefaults, ...defaults }; this.saveStore(); } /** * Get config file path for manual editing */ getConfigPath() { return this.configFile; } /** * Reset all configurations */ reset() { this.store = { version: '1.0.0', configs: {}, globalDefaults: { rateLimit: 2, timeout: 30000, maxRedirects: 5, concurrent: 5 } }; this.saveStore(); } } exports.ConfigManager = ConfigManager; //# sourceMappingURL=config-manager.js.map