UNPKG

@dollhousemcp/mcp-server

Version:

DollhouseMCP - A Model Context Protocol (MCP) server that enables dynamic AI persona management from markdown files, allowing Claude and other compatible AI assistants to activate and switch between different behavioral personas.

137 lines 15.4 kB
/** * Pattern Matching Utilities * * Provides glob-like pattern matching for autonomy configuration, * tool filtering, and other pattern-based matching needs. * * Supports: * - * matches any sequence of characters * - ? matches any single character * * @example * ```typescript * matchesPattern('deploy_prod', 'deploy_*'); // true * matchesPattern('deploy_prod', 'deploy_???'); // false (only 3 chars after _) * patternsConflict('deploy_*', 'deploy_prod'); // true * ``` */ import { logger } from './logger.js'; /** Maximum allowed glob pattern length to prevent ReDoS (Issue #388) */ export const MAX_GLOB_PATTERN_LENGTH = 500; /** Maximum allowed text length for pattern matching (Issue #388) */ export const MAX_PATTERN_MATCH_TEXT_LENGTH = 10_000; /** LRU cache for compiled glob→regex conversions (Issue #625 Phase 4 review) */ const REGEX_CACHE_MAX = 256; const regexCache = new Map(); /** * Convert a glob pattern to a RegExp (cached). * * Compiled regexes are cached in an LRU map (max 256 entries) to avoid * recompilation on repeated pattern matching calls. * * @param pattern - Glob pattern with * and ? wildcards * @returns RegExp that matches the pattern (never-matching if pattern exceeds length limit) */ export function globToRegex(pattern) { if (pattern.length > MAX_GLOB_PATTERN_LENGTH) { logger.warn('Glob pattern exceeds maximum length, returning never-matching regex', { patternLength: pattern.length, maxLength: MAX_GLOB_PATTERN_LENGTH, }); return /(?!)/; } const cached = regexCache.get(pattern); if (cached) return cached; const patternLower = pattern.toLowerCase(); // Convert glob pattern to regex const regexPattern = patternLower .replace(/[.+^${}()|[\]\\]/g, '\\$&') // Escape special regex chars .replace(/\*/g, '.*') // * becomes .* .replace(/\?/g, '.'); // ? becomes . const compiled = new RegExp(`^${regexPattern}$`, 'i'); // LRU eviction: drop oldest entry when at capacity if (regexCache.size >= REGEX_CACHE_MAX) { const oldestKey = regexCache.keys().next().value; if (oldestKey) regexCache.delete(oldestKey); } regexCache.set(pattern, compiled); return compiled; } /** * Check if text matches a glob-like pattern * * @param text - The text to match against * @param pattern - Glob pattern with * and ? wildcards * @returns true if text matches the pattern */ export function matchesPattern(text, pattern) { if (text.length > MAX_PATTERN_MATCH_TEXT_LENGTH) { logger.warn('Text exceeds maximum length for pattern matching, returning false', { textLength: text.length, maxLength: MAX_PATTERN_MATCH_TEXT_LENGTH, }); return false; } const regex = globToRegex(pattern); return regex.test(text); } /** * Check if two patterns could potentially conflict * * Two patterns conflict if: * 1. They are identical (exact match) * 2. One pattern could match strings that the other pattern would also match * * This is used to detect configuration conflicts like: * - requiresApproval: ['deploy_*'] with autoApprove: ['deploy_prod'] * → 'deploy_prod' matches 'deploy_*' → conflict * * @param patternA - First pattern * @param patternB - Second pattern * @returns Object with conflict status and details */ export function detectPatternConflict(patternA, patternB) { // Exact match is always a conflict if (patternA.toLowerCase() === patternB.toLowerCase()) { return { conflicts: true, reason: 'exact match' }; } // Check if patternB matches patternA (B is more specific than A) // e.g., patternA = 'deploy_*', patternB = 'deploy_prod' if (matchesPattern(patternB, patternA)) { return { conflicts: true, reason: `'${patternB}' matches pattern '${patternA}'` }; } // Check if patternA matches patternB (A is more specific than B) // e.g., patternA = 'deploy_prod', patternB = 'deploy_*' if (matchesPattern(patternA, patternB)) { return { conflicts: true, reason: `'${patternA}' matches pattern '${patternB}'` }; } return { conflicts: false }; } /** * Find all conflicts between two sets of patterns * * @param patternsA - First set of patterns (e.g., requiresApproval) * @param patternsB - Second set of patterns (e.g., autoApprove) * @returns Array of conflict descriptions */ export function findPatternConflicts(patternsA, patternsB) { const conflicts = []; for (const patternA of patternsA) { for (const patternB of patternsB) { const result = detectPatternConflict(patternA, patternB); if (result.conflicts) { conflicts.push(`Conflict between '${patternA}' and '${patternB}': ${result.reason}`); } } } return conflicts; } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicGF0dGVybk1hdGNoZXIuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvdXRpbHMvcGF0dGVybk1hdGNoZXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7Ozs7Ozs7Ozs7Ozs7Ozs7R0FnQkc7QUFFSCxPQUFPLEVBQUUsTUFBTSxFQUFFLE1BQU0sYUFBYSxDQUFDO0FBRXJDLHdFQUF3RTtBQUN4RSxNQUFNLENBQUMsTUFBTSx1QkFBdUIsR0FBRyxHQUFHLENBQUM7QUFFM0Msb0VBQW9FO0FBQ3BFLE1BQU0sQ0FBQyxNQUFNLDZCQUE2QixHQUFHLE1BQU0sQ0FBQztBQUVwRCxnRkFBZ0Y7QUFDaEYsTUFBTSxlQUFlLEdBQUcsR0FBRyxDQUFDO0FBQzVCLE1BQU0sVUFBVSxHQUFHLElBQUksR0FBRyxFQUFrQixDQUFDO0FBRTdDOzs7Ozs7OztHQVFHO0FBQ0gsTUFBTSxVQUFVLFdBQVcsQ0FBQyxPQUFlO0lBQ3pDLElBQUksT0FBTyxDQUFDLE1BQU0sR0FBRyx1QkFBdUIsRUFBRSxDQUFDO1FBQzdDLE1BQU0sQ0FBQyxJQUFJLENBQUMscUVBQXFFLEVBQUU7WUFDakYsYUFBYSxFQUFFLE9BQU8sQ0FBQyxNQUFNO1lBQzdCLFNBQVMsRUFBRSx1QkFBdUI7U0FDbkMsQ0FBQyxDQUFDO1FBQ0gsT0FBTyxNQUFNLENBQUM7SUFDaEIsQ0FBQztJQUVELE1BQU0sTUFBTSxHQUFHLFVBQVUsQ0FBQyxHQUFHLENBQUMsT0FBTyxDQUFDLENBQUM7SUFDdkMsSUFBSSxNQUFNO1FBQUUsT0FBTyxNQUFNLENBQUM7SUFFMUIsTUFBTSxZQUFZLEdBQUcsT0FBTyxDQUFDLFdBQVcsRUFBRSxDQUFDO0lBRTNDLGdDQUFnQztJQUNoQyxNQUFNLFlBQVksR0FBRyxZQUFZO1NBQzlCLE9BQU8sQ0FBQyxtQkFBbUIsRUFBRSxNQUFNLENBQUMsQ0FBQyw2QkFBNkI7U0FDbEUsT0FBTyxDQUFDLEtBQUssRUFBRSxJQUFJLENBQUMsQ0FBQyxlQUFlO1NB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cm4gTWF0Y2hpbmcgVXRpbGl0aWVzXG4gKlxuICogUHJvdmlkZXMgZ2xvYi1saWtlIHBhdHRlcm4gbWF0Y2hpbmcgZm9yIGF1dG9ub215IGNvbmZpZ3VyYXRpb24sXG4gKiB0b29sIGZpbHRlcmluZywgYW5kIG90aGVyIHBhdHRlcm4tYmFzZWQgbWF0Y2hpbmcgbmVlZHMuXG4gKlxuICogU3VwcG9ydHM6XG4gKiAtICogbWF0Y2hlcyBhbnkgc2VxdWVuY2Ugb2YgY2hhcmFjdGVyc1xuICogLSA/IG1hdGNoZXMgYW55IHNpbmdsZSBjaGFyYWN0ZXJcbiAqXG4gKiBAZXhhbXBsZVxuICogYGBgdHlwZXNjcmlwdFxuICogbWF0Y2hlc1BhdHRlcm4oJ2RlcGxveV9wcm9kJywgJ2RlcGxveV8qJyk7IC8vIHRydWVcbiAqIG1hdGNoZXNQYXR0ZXJuKCdkZXBsb3lfcHJvZCcsICdkZXBsb3lfPz8/Jyk7IC8vIGZhbHNlIChvbmx5IDMgY2hhcnMgYWZ0ZXIgXylcbiAqIHBhdHRlcm5zQ29uZmxpY3QoJ2RlcGxveV8qJywgJ2RlcGxveV9wcm9kJyk7IC8vIHRydWVcbiAqIGBgYFxuICovXG5cbmltcG9ydCB7IGxvZ2dlciB9IGZyb20gJy4vbG9nZ2VyLmpzJztcblxuLyoqIE1heGltdW0gYWxsb3dlZCBnbG9iIHBhdHRlcm4gbGVuZ3RoIHRvIHByZXZlbnQgUmVEb1MgKElzc3VlICMzODgpICovXG5leHBvcnQgY29uc3QgTUFYX0dMT0JfUEFUVEVSTl9MRU5HVEggPSA1MDA7XG5cbi8qKiBNYXhpbXVtIGFsbG93ZWQgdGV4dCBsZW5ndGggZm9yIHBhdHRlcm4gbWF0Y2hpbmcgKElzc3VlICMzODgpICovXG5leHBvcnQgY29uc3QgTUFYX1BBVFRFUk5fTUFUQ0hfVEVYVF9MRU5HVEggPSAxMF8wMDA7XG5cbi8qKiBMUlUgY2FjaGUgZm9yIGNvbXBpbGVkIGdsb2LihpJyZWdleCBjb252ZXJzaW9ucyAoSXNzdWUgIzYyNSBQaGFzZSA0IHJldmlldykgKi9cbmNvbnN0IFJFR0VYX0NBQ0hFX01BWCA9IDI1NjtcbmNvbnN0IHJlZ2V4Q2FjaGUgPSBuZXcgTWFwPHN0cmluZywgUmVnRXhwPigpO1xuXG4vKipcbiAqIENvbnZlcnQgYSBnbG9iIHBhdHRlcm4gdG8gYSBSZWdFeHAgKGNhY2hlZCkuXG4gKlxuICogQ29tcGlsZWQgcmVnZXhlcyBhcmUgY2FjaGVkIGluIGFuIExSVSBtYXAgKG1heCAyNTYgZW50cmllcykgdG8gYXZvaWRcbiAqIHJlY29tcGlsYXRpb24gb24gcmVwZWF0ZWQgcGF0dGVybiBtYXRjaGluZyBjYWxscy5cbiAqXG4gKiBAcGFyYW0gcGF0dGVybiAtIEdsb2IgcGF0dGVybiB3aXRoICogYW5kID8gd2lsZGNhcmRzXG4gKiBAcmV0dXJucyBSZWdFeHAgdGhhdCBtYXRjaGVzIHRoZSBwYXR0ZXJuIChuZXZlci1tYXRjaGluZyBpZiBwYXR0ZXJuIGV4Y2VlZHMgbGVuZ3RoIGxpbWl0KVxuICovXG5leHBvcnQgZnVuY3Rpb24gZ2xvYlRvUmVnZXgocGF0dGVybjogc3RyaW5nKTogUmVnRXhwIHtcbiAgaWYgKHBhdHRlcm4ubGVuZ3RoID4gTUFYX0dMT0JfUEFUVEVSTl9MRU5HVEgpIHtcbiAgICBsb2dnZXIud2FybignR2xvYiBwYXR0ZXJuIGV4Y2VlZHMgbWF4aW11bSBsZW5ndGgsIHJldHVybmluZyBuZXZlci1tYXRjaGluZyByZWdleCcsIHtcbiAgICAgIHBhdHRlcm5MZW5ndGg6IHBhdHRlcm4ubGVuZ3RoLFxuICAgICAgbWF4TGVuZ3RoOiBNQVhfR0xPQl9QQVRURVJOX0xFTkdUSCxcbiAgICB9KTtcbiAgICByZXR1cm4gLyg/ISkvO1xuICB9XG5cbiAgY29uc3QgY2FjaGVkID0gcmVnZXhDYWNoZS5nZXQocGF0dGVybik7XG4gIGlmIChjYWNoZWQpIHJldHVybiBjYWNoZWQ7XG5cbiAgY29uc3QgcGF0dGVybkxvd2VyID0gcGF0dGVybi50b0xvd2VyQ2FzZSgpO1xuXG4gIC8vIENvbnZlcnQgZ2xvYiBwYXR0ZXJuIHRvIHJlZ2V4XG4gIGNvbnN0IHJlZ2V4UGF0dGVybiA9IHBhdHRlcm5Mb3dlclxuICAgIC5yZXBsYWNlKC9bLiteJHt9KCl8W1xcXVxcXFxdL2csICdcXFxcJCYnKSAvLyBFc2NhcGUgc3BlY2lhbCByZWdleCBjaGFyc1xuICAgIC5yZXBsYWNlKC9cXCovZywgJy4qJykgLy8gKiBiZWNvbWVzIC4qXG4gICAgLnJlcGxhY2UoL1xcPy9nLCAnLicpOyAvLyA/IGJlY29tZXMgLlxuXG4gIGNvbnN0IGNvbXBpbGVkID0gbmV3IFJlZ0V4cChgXiR7cmVnZXhQYXR0ZXJufSRgLCAnaScpO1xuXG4gIC8vIExSVSBldmljdGlvbjogZHJvcCBvbGRlc3QgZW50cnkgd2hlbiBhdCBjYXBhY2l0eVxuICBpZiAocmVnZXhDYWNoZS5zaXplID49IFJFR0VYX0NBQ0hFX01BWCkge1xuICAgIGNvbnN0IG9sZGVzdEtleSA9IHJlZ2V4Q2FjaGUua2V5cygpLm5leHQoKS52YWx1ZTtcbiAgICBpZiAob2xkZXN0S2V5KSByZWdleENhY2hlLmRlbGV0ZShvbGRlc3RLZXkpO1xuICB9XG4gIHJlZ2V4Q2FjaGUuc2V0KHBhdHRlcm4sIGNvbXBpbGVkKTtcblxuICByZXR1cm4gY29tcGlsZWQ7XG59XG5cbi8qKlxuICogQ2hlY2sgaWYgdGV4dCBtYXRjaGVzIGEgZ2xvYi1saWtlIHBhdHRlcm5cbiAqXG4gKiBAcGFyYW0gdGV4dCAtIFRoZSB0ZXh0IHRvIG1hdGNoIGFnYWluc3RcbiAqIEBwYXJhbSBwYXR0ZXJuIC0gR2xvYiBwYXR0ZXJuIHdpdGggKiBhbmQgPyB3aWxkY2FyZHNcbiAqIEByZXR1cm5zIHRydWUgaWYgdGV4dCBtYXRjaGVzIHRoZSBwYXR0ZXJuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBtYXRjaGVzUGF0dGVybih0ZXh0OiBzdHJpbmcsIHBhdHRlcm46IHN0cmluZyk6IGJvb2xlYW4ge1xuICBpZiAodGV4dC5sZW5ndGggPiBNQVhfUEFUVEVSTl9NQVRDSF9URVhUX0xFTkdUSCkge1xuICAgIGxvZ2dlci53YXJuKCdUZXh0IGV4Y2VlZHMgbWF4aW11bSBsZW5ndGggZm9yIHBhdHRlcm4gbWF0Y2hpbmcsIHJldHVybmluZyBmYWxzZScsIHtcbiAgICAgIHRleHRMZW5ndGg6IHRleHQubGVuZ3RoLFxuICAgICAgbWF4TGVuZ3RoOiBNQVhfUEFUVEVSTl9NQVRDSF9URVhUX0xFTkdUSCxcbiAgICB9KTtcbiAgICByZXR1cm4gZmFsc2U7XG4gIH1cblxuICBjb25zdCByZWdleCA9IGdsb2JUb1JlZ2V4KHBhdHRlcm4pO1xuICByZXR1cm4gcmVnZXgudGVzdCh0ZXh0KTtcbn1cblxuLyoqXG4gKiBDaGVjayBpZiB0d28gcGF0dGVybnMgY291bGQgcG90ZW50aWFsbHkgY29uZmxpY3RcbiAqXG4gKiBUd28gcGF0dGVybnMgY29uZmxpY3QgaWY6XG4gKiAxLiBUaGV5IGFyZSBpZGVudGljYWwgKGV4YWN0IG1hdGNoKVxuICogMi4gT25lIHBhdHRlcm4gY291bGQgbWF0Y2ggc3RyaW5ncyB0aGF0IHRoZSBvdGhlciBwYXR0ZXJuIHdvdWxkIGFsc28gbWF0Y2hcbiAqXG4gKiBUaGlzIGlzIHVzZWQgdG8gZGV0ZWN0IGNvbmZpZ3VyYXRpb24gY29uZmxpY3RzIGxpa2U6XG4gKiAtIHJlcXVpcmVzQXBwcm92YWw6IFsnZGVwbG95XyonXSB3aXRoIGF1dG9BcHByb3ZlOiBbJ2RlcGxveV9wcm9kJ11cbiAqICAg4oaSICdkZXBsb3lfcHJvZCcgbWF0Y2hlcyAnZGVwbG95XyonIOKGkiBjb25mbGljdFxuICpcbiAqIEBwYXJhbSBwYXR0ZXJuQSAtIEZpcnN0IHBhdHRlcm5cbiAqIEBwYXJhbSBwYXR0ZXJuQiAtIFNlY29uZCBwYXR0ZXJuXG4gKiBAcmV0dXJucyBPYmplY3Qgd2l0aCBjb25mbGljdCBzdGF0dXMgYW5kIGRldGFpbHNcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGRldGVjdFBhdHRlcm5Db25mbGljdChcbiAgcGF0dGVybkE6IHN0cmluZyxcbiAgcGF0dGVybkI6IHN0cmluZ1xuKTogeyBjb25mbGljdHM6IGJvb2xlYW47IHJlYXNvbj86IHN0cmluZyB9IHtcbiAgLy8gRXhhY3QgbWF0Y2ggaXMgYWx3YXlzIGEgY29uZmxpY3RcbiAgaWYgKHBhdHRlcm5BLnRvTG93ZXJDYXNlKCkgPT09IHBhdHRlcm5CLnRvTG93ZXJDYXNlKCkpIHtcbiAgICByZXR1cm4geyBjb25mbGljdHM6IHRydWUsIHJlYXNvbjogJ2V4YWN0IG1hdGNoJyB9O1xuICB9XG5cbiAgLy8gQ2hlY2sgaWYgcGF0dGVybkIgbWF0Y2hlcyBwYXR0ZXJuQSAoQiBpcyBtb3JlIHNwZWNpZmljIHRoYW4gQSlcbiAgLy8gZS5nLiwgcGF0dGVybkEgPSAnZGVwbG95XyonLCBwYXR0ZXJuQiA9ICdkZXBsb3lfcHJvZCdcbiAgaWYgKG1hdGNoZXNQYXR0ZXJuKHBhdHRlcm5CLCBwYXR0ZXJuQSkpIHtcbiAgICByZXR1cm4ge1xuICAgICAgY29uZmxpY3RzOiB0cnVlLFxuICAgICAgcmVhc29uOiBgJyR7cGF0dGVybkJ9JyBtYXRjaGVzIHBhdHRlcm4gJyR7cGF0dGVybkF9J2BcbiAgICB9O1xuICB9XG5cbiAgLy8gQ2hlY2sgaWYgcGF0dGVybkEgbWF0Y2hlcyBwYXR0ZXJuQiAoQSBpcyBtb3JlIHNwZWNpZmljIHRoYW4gQilcbiAgLy8gZS5nLiwgcGF0dGVybkEgPSAnZGVwbG95X3Byb2QnLCBwYXR0ZXJuQiA9ICdkZXBsb3lfKidcbiAgaWYgKG1hdGNoZXNQYXR0ZXJuKHBhdHRlcm5BLCBwYXR0ZXJuQikpIHtcbiAgICByZXR1cm4ge1xuICAgICAgY29uZmxpY3RzOiB0cnVlLFxuICAgICAgcmVhc29uOiBgJyR7cGF0dGVybkF9JyBtYXRjaGVzIHBhdHRlcm4gJyR7cGF0dGVybkJ9J2BcbiAgICB9O1xuICB9XG5cbiAgcmV0dXJuIHsgY29uZmxpY3RzOiBmYWxzZSB9O1xufVxuXG4vKipcbiAqIEZpbmQgYWxsIGNvbmZsaWN0cyBiZXR3ZWVuIHR3byBzZXRzIG9mIHBhdHRlcm5zXG4gKlxuICogQHBhcmFtIHBhdHRlcm5zQSAtIEZpcnN0IHNldCBvZiBwYXR0ZXJucyAoZS5nLiwgcmVxdWlyZXNBcHByb3ZhbClcbiAqIEBwYXJhbSBwYXR0ZXJuc0IgLSBTZWNvbmQgc2V0IG9mIHBhdHRlcm5zIChlLmcuLCBhdXRvQXBwcm92ZSlcbiAqIEByZXR1cm5zIEFycmF5IG9mIGNvbmZsaWN0IGRlc2NyaXB0aW9uc1xuICovXG5leHBvcnQgZnVuY3Rpb24gZmluZFBhdHRlcm5Db25mbGljdHMoXG4gIHBhdHRlcm5zQTogc3RyaW5nW10sXG4gIHBhdHRlcm5zQjogc3RyaW5nW11cbik6IHN0cmluZ1tdIHtcbiAgY29uc3QgY29uZmxpY3RzOiBzdHJpbmdbXSA9IFtdO1xuXG4gIGZvciAoY29uc3QgcGF0dGVybkEgb2YgcGF0dGVybnNBKSB7XG4gICAgZm9yIChjb25zdCBwYXR0ZXJuQiBvZiBwYXR0ZXJuc0IpIHtcbiAgICAgIGNvbnN0IHJlc3VsdCA9IGRldGVjdFBhdHRlcm5Db25mbGljdChwYXR0ZXJuQSwgcGF0dGVybkIpO1xuICAgICAgaWYgKHJlc3VsdC5jb25mbGljdHMpIHtcbiAgICAgICAgY29uZmxpY3RzLnB1c2goXG4gICAgICAgICAgYENvbmZsaWN0IGJldHdlZW4gJyR7cGF0dGVybkF9JyBhbmQgJyR7cGF0dGVybkJ9JzogJHtyZXN1bHQucmVhc29ufWBcbiAgICAgICAgKTtcbiAgICAgIH1cbiAgICB9XG4gIH1cblxuICByZXR1cm4gY29uZmxpY3RzO1xufVxuIl19