'use strict'; // src/utils.ts Object.defineProperty(exports, '__esModule', { value: true }); exports.Logger = void 0; exports.validateOllamaUrl = validateOllamaUrl; exports.validateModelName = validateModelName; exports.validatePluginSettings = validatePluginSettings; exports.safeParseJson = safeParseJson; exports.sanitizeFilePath = sanitizeFilePath; // ==================== Logger ==================== var LogLevel; (function (LogLevel) { LogLevel[(LogLevel['DEBUG'] = 0)] = 'DEBUG'; LogLevel[(LogLevel['INFO'] = 1)] = 'INFO'; LogLevel[(LogLevel['WARN'] = 2)] = 'WARN'; LogLevel[(LogLevel['ERROR'] = 3)] = 'ERROR'; })(LogLevel || (LogLevel = {})); const SEVERITY_ORDER = { debug: LogLevel.DEBUG, info: LogLevel.INFO, warn: LogLevel.WARN, error: LogLevel.ERROR, }; class Logger { static setLevel(level) { if (typeof level === 'string') { const lowerLevel = level.toLowerCase(); Logger.minLevel = SEVERITY_ORDER[lowerLevel] ?? LogLevel.DEBUG; } else { Logger.minLevel = level; } } static debug(message, category = 'general') { if (LogLevel.DEBUG >= Logger.minLevel) { console.debug(`[${category}] DEBUG: ${message}`); } } static info(message, category = 'general') { if (LogLevel.INFO >= Logger.minLevel) { console.info(`[${category}] INFO: ${message}`); } } static warn(message, category = 'general') { if (LogLevel.WARN >= Logger.minLevel) { console.warn(`[${category}] WARN: ${message}`); } } static error(message, category = 'general') { if (LogLevel.ERROR >= Logger.minLevel) { console.error(`[${category}] ERROR: ${message}`); } } } exports.Logger = Logger; Logger.minLevel = LogLevel.DEBUG; // ==================== URL & Model Validation ==================== function validateOllamaUrl(url) { if (typeof url !== 'string' || !url.trim()) { return { valid: false, error: 'URL cannot be empty' }; } const trimmedUrl = url.trim(); if (trimmedUrl.endsWith('/')) { return { valid: false, error: 'URL should not end with a slash' }; } try { const parsed = new URL(trimmedUrl); if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:') { return { valid: false, error: 'Must be a valid HTTP or HTTPS URL' }; } return { valid: true }; } catch { return { valid: false, error: 'Must be a valid HTTP or HTTPS URL' }; } } function validateModelName(model) { if (typeof model !== 'string') { return { valid: false, error: 'Model name must be a string' }; } const trimmedModel = model.trim(); // Explicit check for empty string after trimming if (!trimmedModel || trimmedModel.length === 0) { return { valid: false, error: 'Model name cannot be empty' }; } if (trimmedModel.length < 2) { return { valid: false, error: 'Model name must be at least 2 characters long' }; } if (trimmedModel.length > 100) { return { valid: false, error: 'Model name must be less than 100 characters long' }; } if (!/^[a-zA-Z0-9._:-]+$/.test(trimmedModel)) { return { valid: false, error: 'Model name can only contain letters, numbers, dots, dashes, underscores, and colons', }; } return { valid: true }; } function validatePluginSettings(settings) { const errors = []; const urlValidation = validateOllamaUrl(settings.ollamaUrl); if (!urlValidation.valid) { errors.push(`Invalid Ollama URL: ${urlValidation.error}`); } const modelValidation = validateModelName(settings.model); if (!modelValidation.valid) { errors.push(`Invalid Model Name: ${modelValidation.error}`); } return errors; } // ==================== Safe JSON Parsing ==================== const MAX_JSON_SIZE = 1000000; const MAX_JSON_NESTING = 24; function countNestingDepth(value, depth = 0) { if (depth > MAX_JSON_NESTING) { return depth; } if (Array.isArray(value)) { return Math.max(...value.map((item) => countNestingDepth(item, depth + 1)), depth); } if (value !== null && typeof value === 'object') { const entries = Object.values(value); if (entries.length === 0) return depth; return Math.max(...entries.map((item) => countNestingDepth(item, depth + 1)), depth); } return depth; } function safeParseJson(jsonString) { if (typeof jsonString !== 'string') { throw new Error('Input must be a string'); } if (jsonString.length > MAX_JSON_SIZE) { throw new Error('JSON input too large'); } let parsed; try { parsed = JSON.parse(jsonString); } catch { throw new Error('Invalid JSON'); } // Check for dangerous prototype pollution patterns const reStringified = JSON.stringify(parsed); if ( reStringified.includes('constructor') || reStringified.includes('prototype') || reStringified.includes('__proto__') || reStringified.includes('function') ) { throw new Error('dangerous code pattern detected'); } // Check nesting depth if (countNestingDepth(parsed) > MAX_JSON_NESTING) { throw new Error('JSON nesting too deep'); } return parsed; } // ==================== Path & File Utilities ==================== function sanitizeFilePath(path) { if (path.includes('..')) { throw new Error('Invalid path - cannot contain .. segments'); } return path; } // ==================== Markdown Utilities ====================