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.

119 lines 15.1 kB
/** * Shared utilities for search functionality */ import { UnicodeValidator } from '../security/validators/unicodeValidator.js'; /** * Normalize search terms for better matching * Handles spaces, dashes, underscores, and file extensions * * @param term - The search term to normalize * @param maxLength - Maximum allowed length for the search term (default: 1000) * @returns Normalized search term * @throws Error if term exceeds maxLength */ export function normalizeSearchTerm(term, maxLength = 1000) { // SECURITY FIX: Normalize Unicode to prevent homograph attacks const normalized = UnicodeValidator.normalize(term); const cleanTerm = normalized.normalizedContent; // Security: Limit input length to prevent DoS attacks if (cleanTerm.length > maxLength) { throw new Error(`Search term exceeds maximum length of ${maxLength} characters`); } return cleanTerm.toLowerCase() .replaceAll(/[-_\s]+/g, ' ') // Convert dashes, underscores to spaces .replace(/\.md$/, '') // Remove .md extension .trim(); } /** * Preserve original structure for partial matching * Less aggressive normalization that keeps original separators for partial searches * * @param term - The search term to normalize * @param maxLength - Maximum allowed length for the search term (default: 1000) * @returns Normalized search term with preserved structure * @throws Error if term exceeds maxLength */ export function normalizeForPartialMatch(term, maxLength = 1000) { // SECURITY FIX: Normalize Unicode to prevent homograph attacks const normalized = UnicodeValidator.normalize(term); const cleanTerm = normalized.normalizedContent; // Security: Limit input length to prevent DoS attacks if (cleanTerm.length > maxLength) { throw new Error(`Search term exceeds maximum length of ${maxLength} characters`); } return cleanTerm.toLowerCase() .replace(/\.md$/, '') // Remove .md extension only .trim(); } /** * Check if a search term matches a target string with multiple strategies * Uses both normalized matching and partial matching for better results * * @param searchTerm - The term being searched for * @param targetString - The string to search in * @returns true if there's a match */ export function isSearchMatch(searchTerm, targetString) { // Strategy 1: Exact normalized matching const normalizedSearch = normalizeSearchTerm(searchTerm); const normalizedTarget = normalizeSearchTerm(targetString); if (normalizedTarget.includes(normalizedSearch)) { return true; } // Strategy 2: Partial matching with preserved structure const partialSearch = normalizeForPartialMatch(searchTerm); const partialTarget = normalizeForPartialMatch(targetString); if (partialTarget.includes(partialSearch)) { return true; } // Strategy 3: Word-boundary matching const searchWords = normalizedSearch.split(/\s+/); const targetWords = normalizedTarget.split(/\s+/); return searchWords.some(searchWord => targetWords.some(targetWord => targetWord.includes(searchWord))); } /** * Validate search query length and content * * @param query - The search query to validate * @param maxLength - Maximum allowed length (default: 1000) * @returns true if valid * @throws Error with descriptive message if invalid */ export function validateSearchQuery(query, maxLength = 1000) { if (!query || query.trim().length === 0) { throw new Error('Search query cannot be empty'); } // SECURITY FIX: Normalize Unicode before validation const normalized = UnicodeValidator.normalize(query); const cleanQuery = normalized.normalizedContent; if (cleanQuery.length > maxLength) { throw new Error(`Search query exceeds maximum length of ${maxLength} characters`); } // Check for potential injection patterns (basic validation) const dangerousPatterns = [ // eslint-disable-next-line no-control-regex -- Intentionally checking for control chars for security /[\u0000-\u001F\u007F]/, // NOSONAR - Control characters check for security /[<>]/, // Potential HTML/XML injection ]; for (const pattern of dangerousPatterns) { if (pattern.test(cleanQuery)) { throw new Error('Search query contains invalid characters'); } } return true; } /** * Debug function to show how search terms are normalized * Helps troubleshoot search matching issues * * @param originalTerm - The original search term * @returns Object with different normalization results */ export function debugNormalization(originalTerm) { return { original: originalTerm, normalized: normalizeSearchTerm(originalTerm), partialMatch: normalizeForPartialMatch(originalTerm) }; } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2VhcmNoVXRpbHMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvdXRpbHMvc2VhcmNoVXRpbHMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7O0dBRUc7QUFFSCxPQUFPLEVBQUUsZ0JBQWdCLEVBQUUsTUFBTSw0Q0FBNEMsQ0FBQztBQUU5RTs7Ozs7Ozs7R0FRRztBQUNILE1BQU0sVUFBVSxtQkFBbUIsQ0FBQyxJQUFZLEVBQUUsWUFBb0IsSUFBSTtJQUN4RSwrREFBK0Q7SUFDL0QsTUFBTSxVQUFVLEdBQUcsZ0JBQWdCLENBQUMsU0FBUyxDQUFDLElBQUksQ0FBQyxDQUFDO0lBQ3BELE1BQU0sU0FBUyxHQUFHLFVBQVUsQ0FBQyxpQkFBaUIsQ0FBQztJQUUvQyxzREFBc0Q7SUFDdEQsSUFBSSxTQUFTLENBQUMsTUFBTSxHQUFHLFNBQVMsRUFBRSxDQUFDO1FBQ2pDLE1BQU0sSUFBSSxLQUFLLENBQUMseUNBQXlDLFNBQVMsYUFBYSxDQUFDLENBQUM7SUFDbkYsQ0FBQztJQUVELE9BQU8sU0FBUyxDQUFDLFdBQVcsRUFBRTtTQUMzQixVQUFVLENBQUMsVUFBVSxFQUFFLEdBQUcsQ0FBQyxDQUFFLHdDQUF3QztTQUNyRSxPQUFPLENBQUMsT0FBTyxFQUFFLEVBQUUsQ0FBQyxDQUFPLHVCQUF1QjtTQUNsRCxJQUFJLEVBQUUsQ0FBQztBQUNaLENBQUM7QUFFRDs7Ozs7Ozs7R0FRRztBQUNILE1BQU0sVUFBVSx3QkFBd0IsQ0FBQyxJQUFZLEVBQUUsWUFBb0IsSUFBSTtJQUM3RSwrREFBK0Q7SUFDL0QsTUFBTSxVQUFVLEdBQUcsZ0JBQWdCLENBQUMsU0FBUyxDQUFDLElBQUksQ0FBQyxDQUFDO0lBQ3BELE1BQU0sU0FBUyxHQUFHLFVBQVUsQ0FBQyxpQkFBaUIsQ0FBQztJQUUvQyxzREFBc0Q7SUFDdEQsSUFBSSxTQUFTLENBQUMsTUFBTSxHQUFHLFNBQVMsRUFBRSxDQUFDO1FBQ2pDLE1BQU0sSUFBSSxLQUFLLENBQUMs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cyBmb3Igc2VhcmNoIGZ1bmN0aW9uYWxpdHlcbiAqL1xuXG5pbXBvcnQgeyBVbmljb2RlVmFsaWRhdG9yIH0gZnJvbSAnLi4vc2VjdXJpdHkvdmFsaWRhdG9ycy91bmljb2RlVmFsaWRhdG9yLmpzJztcblxuLyoqXG4gKiBOb3JtYWxpemUgc2VhcmNoIHRlcm1zIGZvciBiZXR0ZXIgbWF0Y2hpbmdcbiAqIEhhbmRsZXMgc3BhY2VzLCBkYXNoZXMsIHVuZGVyc2NvcmVzLCBhbmQgZmlsZSBleHRlbnNpb25zXG4gKiBcbiAqIEBwYXJhbSB0ZXJtIC0gVGhlIHNlYXJjaCB0ZXJtIHRvIG5vcm1hbGl6ZVxuICogQHBhcmFtIG1heExlbmd0aCAtIE1heGltdW0gYWxsb3dlZCBsZW5ndGggZm9yIHRoZSBzZWFyY2ggdGVybSAoZGVmYXVsdDogMTAwMClcbiAqIEByZXR1cm5zIE5vcm1hbGl6ZWQgc2VhcmNoIHRlcm1cbiAqIEB0aHJvd3MgRXJyb3IgaWYgdGVybSBleGNlZWRzIG1heExlbmd0aFxuICovXG5leHBvcnQgZnVuY3Rpb24gbm9ybWFsaXplU2VhcmNoVGVybSh0ZXJtOiBzdHJpbmcsIG1heExlbmd0aDogbnVtYmVyID0gMTAwMCk6IHN0cmluZyB7XG4gIC8vIFNFQ1VSSVRZIEZJWDogTm9ybWFsaXplIFVuaWNvZGUgdG8gcHJldmVudCBob21vZ3JhcGggYXR0YWNrc1xuICBjb25zdCBub3JtYWxpemVkID0gVW5pY29kZVZhbGlkYXRvci5ub3JtYWxpemUodGVybSk7XG4gIGNvbnN0IGNsZWFuVGVybSA9IG5vcm1hbGl6ZWQubm9ybWFsaXplZENvbnRlbnQ7XG4gIFxuICAvLyBTZWN1cml0eTogTGltaXQgaW5wdXQgbGVuZ3RoIHRvIHByZXZlbnQgRG9TIGF0dGFja3NcbiAgaWYgKGNsZWFuVGVybS5sZW5ndGggPiBtYXhMZW5ndGgpIHtcbiAgICB0aHJvdyBuZXcgRXJyb3IoYFNlYXJjaCB0ZXJtIGV4Y2VlZHMgbWF4aW11bSBsZW5ndGggb2YgJHttYXhMZW5ndGh9IGNoYXJhY3RlcnNgKTtcbiAgfVxuICBcbiAgcmV0dXJuIGNsZWFuVGVybS50b0xvd2VyQ2FzZSgpXG4gICAgLnJlcGxhY2VBbGwoL1stX1xcc10rL2csICcgJykgIC8vIENvbnZlcnQgZGFzaGVzLCB1bmRlcnNjb3JlcyB0byBzcGFjZXNcbiAgICAucmVwbGFjZSgvXFwubWQkLywgJycpICAgICAgIC8vIFJlbW92ZSAubWQgZXh0ZW5zaW9uXG4gICAgLnRyaW0oKTtcbn1cblxuLyoqXG4gKiBQcmVzZXJ2ZSBvcmlnaW5hbCBzdHJ1Y3R1cmUgZm9yIHBhcnRpYWwgbWF0Y2hpbmdcbiAqIExlc3MgYWdncmVzc2l2ZSBub3JtYWxpemF0aW9uIHRoYXQga2VlcHMgb3JpZ2luYWwgc2VwYXJhdG9ycyBmb3IgcGFydGlhbCBzZWFyY2hlc1xuICogXG4gKiBAcGFyYW0gdGVybSAtIFRoZSBzZWFyY2ggdGVybSB0byBub3JtYWxpemVcbiAqIEBwYXJhbSBtYXhMZW5ndGggLSBNYXhpbXVtIGFsbG93ZWQgbGVuZ3RoIGZvciB0aGUgc2VhcmNoIHRlcm0gKGRlZmF1bHQ6IDEwMDApXG4gKiBAcmV0dXJucyBOb3JtYWxpemVkIHNlYXJjaCB0ZXJtIHdpdGggcHJlc2VydmVkIHN0cnVjdHVyZVxuICogQHRocm93cyBFcnJvciBpZiB0ZXJtIGV4Y2VlZHMgbWF4TGVuZ3RoXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBub3JtYWxpemVGb3JQYXJ0aWFsTWF0Y2godGVybTogc3RyaW5nLCBtYXhMZW5ndGg6IG51bWJlciA9IDEwMDApOiBzdHJpbmcge1xuICAvLyBTRUNVUklUWSBGSVg6IE5vcm1hbGl6ZSBVbmljb2RlIHRvIHByZXZlbnQgaG9tb2dyYXBoIGF0dGFja3NcbiAgY29uc3Qgbm9ybWFsaXplZCA9IFVuaWNvZGVWYWxpZGF0b3Iubm9ybWFsaXplKHRlcm0pO1xuICBjb25zdCBjbGVhblRlcm0gPSBub3JtYWxpemVkLm5vcm1hbGl6ZWRDb250ZW50O1xuICBcbiAgLy8gU2VjdXJpdHk6IExpbWl0IGlucHV0IGxlbmd0aCB0byBwcmV2ZW50IERvUyBhdHRhY2tzXG4gIGlmIChjbGVhblRlcm0ubGVuZ3RoID4gbWF4TGVuZ3RoKSB7XG4gICAgdGhyb3cgbmV3IEVycm9yKGBTZWFyY2ggdGVybSBleGNlZWRzIG1heGltdW0gbGVuZ3RoIG9mICR7bWF4TGVuZ3RofSBjaGFyYWN0ZXJzYCk7XG4gIH1cbiAgXG4gIHJldHVybiBjbGVhblRlcm0udG9Mb3dlckNhc2UoKVxuICAgIC5yZXBsYWNlKC9cXC5tZCQvLCAnJykgICAgICAgLy8gUmVtb3ZlIC5tZCBleHRlbnNpb24gb25seVxuICAgIC50cmltKCk7XG59XG5cbi8qKlxuICogQ2hlY2sgaWYgYSBzZWFyY2ggdGVybSBtYXRjaGVzIGEgdGFyZ2V0IHN0cmluZyB3aXRoIG11bHRpcGxlIHN0cmF0ZWdpZXNcbiAqIFVzZXMgYm90aCBub3JtYWxpemVkIG1hdGNoaW5nIGFuZCBwYXJ0aWFsIG1hdGNoaW5nIGZvciBiZXR0ZXIgcmVzdWx0c1xuICogXG4gKiBAcGFyYW0gc2VhcmNoVGVybSAtIFRoZSB0ZXJtIGJlaW5nIHNlYXJjaGVkIGZvclxuICogQHBhcmFtIHRhcmdldFN0cmluZyAtIFRoZSBzdHJpbmcgdG8gc2VhcmNoIGluXG4gKiBAcmV0dXJucyB0cnVlIGlmIHRoZXJlJ3MgYSBtYXRjaFxuICovXG5leHBvcnQgZnVuY3Rpb24gaXNTZWFyY2hNYXRjaChzZWFyY2hUZXJtOiBzdHJpbmcsIHRhcmdldFN0cmluZzogc3RyaW5nKTogYm9vbGVhbiB7XG4gIC8vIFN0cmF0ZWd5IDE6IEV4YWN0IG5vcm1hbGl6ZWQgbWF0Y2hpbmdcbiAgY29uc3Qgbm9ybWFsaXplZFNlYXJjaCA9IG5vcm1hbGl6ZVNlYXJjaFRlcm0oc2VhcmNoVGVybSk7XG4gIGNvbnN0IG5vcm1hbGl6ZWRUYXJnZXQgPSBub3JtYWxpemVTZWFyY2hUZXJtKHRhcmdldFN0cmluZyk7XG4gIGlmIChub3JtYWxpemVkVGFyZ2V0LmluY2x1ZGVzKG5vcm1hbGl6ZWRTZWFyY2gpKSB7XG4gICAgcmV0dXJuIHRydWU7XG4gIH1cbiAgXG4gIC8vIFN0cmF0ZWd5IDI6IFBhcnRpYWwgbWF0Y2hpbmcgd2l0aCBwcmVzZXJ2ZWQgc3RydWN0dXJlXG4gIGNvbnN0IHBhcnRpYWxTZWFyY2ggPSBub3JtYWxpemVGb3JQYXJ0aWFsTWF0Y2goc2VhcmNoVGVybSk7XG4gIGNvbnN0IHBhcnRpYWxUYXJnZXQgPSBub3JtYWxpemVGb3JQYXJ0aWFsTWF0Y2godGFyZ2V0U3RyaW5nKTtcbiAgaWYgKHBhcnRpYWxUYXJnZXQuaW5jbHVkZXMocGFydGlhbFNlYXJjaCkpIHtcbiAgICByZXR1cm4gdHJ1ZTtcbiAgfVxuICBcbiAgLy8gU3RyYXRlZ3kgMzogV29yZC1ib3VuZGFyeSBtYXRjaGluZ1xuICBjb25zdCBzZWFyY2hXb3JkcyA9IG5vcm1hbGl6ZWRTZWFyY2guc3BsaXQoL1xccysvKTtcbiAgY29uc3QgdGFyZ2V0V29yZHMgPSBub3JtYWxpemVkVGFyZ2V0LnNwbGl0KC9cXHMrLyk7XG4gIHJldHVybiBzZWFyY2hXb3Jkcy5zb21lKHNlYXJjaFdvcmQgPT4gXG4gICAgdGFyZ2V0V29yZHMuc29tZSh0YXJnZXRXb3JkID0+IHRhcmdldFdvcmQuaW5jbHVkZXMoc2VhcmNoV29yZCkpXG4gICk7XG59XG5cbi8qKlxuICogVmFsaWRhdGUgc2VhcmNoIHF1ZXJ5IGxlbmd0aCBhbmQgY29udGVudFxuICogXG4gKiBAcGFyYW0gcXVlcnkgLSBUaGUgc2VhcmNoIHF1ZXJ5IHRvIHZhbGlkYXRlXG4gKiBAcGFyYW0gbWF4TGVuZ3RoIC0gTWF4aW11bSBhbGxvd2VkIGxlbmd0aCAoZGVmYXVsdDogMTAwMClcbiAqIEByZXR1cm5zIHRydWUgaWYgdmFsaWRcbiAqIEB0aHJvd3MgRXJyb3Igd2l0aCBkZXNjcmlwdGl2ZSBtZXNzYWdlIGlmIGludmFsaWRcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHZhbGlkYXRlU2VhcmNoUXVlcnkocXVlcnk6IHN0cmluZywgbWF4TGVuZ3RoOiBudW1iZXIgPSAxMDAwKTogYm9vbGVhbiB7XG4gIGlmICghcXVlcnkgfHwgcXVlcnkudHJpbSgpLmxlbmd0aCA9PT0gMCkge1xuICAgIHRocm93IG5ldyBFcnJvcignU2VhcmNoIHF1ZXJ5IGNhbm5vdCBiZSBlbXB0eScpO1xuICB9XG4gIFxuICAvLyBTRUNVUklUWSBGSVg6IE5vcm1hbGl6ZSBVbmljb2RlIGJlZm9yZSB2YWxpZGF0aW9uXG4gIGNvbnN0IG5vcm1hbGl6ZWQgPSBVbmljb2RlVmFsaWRhdG9yLm5vcm1hbGl6ZShxdWVyeSk7XG4gIGNvbnN0IGNsZWFuUXVlcnkgPSBub3JtYWxpemVkLm5vcm1hbGl6ZWRDb250ZW50O1xuICBcbiAgaWYgKGNsZWFuUXVlcnkubGVuZ3RoID4gbWF4TGVuZ3RoKSB7XG4gICAgdGhyb3cgbmV3IEVycm9yKGBTZWFyY2ggcXVlcnkgZXhjZWVkcyBtYXhpbXVtIGxlbmd0aCBvZiAke21heExlbmd0aH0gY2hhcmFjdGVyc2ApO1xuICB9XG4gIFxuICAvLyBDaGVjayBmb3IgcG90ZW50aWFsIGluamVjdGlvbiBwYXR0ZXJucyAoYmFzaWMgdmFsaWRhdGlvbilcbiAgY29uc3QgZGFuZ2Vyb3VzUGF0dGVybnMgPSBbXG4gICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIG5vLWNvbnRyb2wtcmVnZXggLS0gSW50ZW50aW9uYWxseSBjaGVja2luZyBmb3IgY29udHJvbCBjaGFycyBmb3Igc2VjdXJpdHlcbiAgICAvW1xcdTAwMDAtXFx1MDAxRlxcdTAwN0ZdLywgIC8vIE5PU09OQVIgLSBDb250cm9sIGNoYXJhY3RlcnMgY2hlY2sgZm9yIHNlY3VyaXR5XG4gICAgL1s8Pl0vLCAgICAgICAgICAgICAvLyBQb3RlbnRpYWwgSFRNTC9YTUwgaW5qZWN0aW9uXG4gIF07XG4gIFxuICBmb3IgKGNvbnN0IHBhdHRlcm4gb2YgZGFuZ2Vyb3VzUGF0dGVybnMpIHtcbiAgICBpZiAocGF0dGVybi50ZXN0KGNsZWFuUXVlcnkpKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoJ1NlYXJjaCBxdWVyeSBjb250YWlucyBpbnZhbGlkIGNoYXJhY3RlcnMnKTtcbiAgICB9XG4gIH1cbiAgXG4gIHJldHVybiB0cnVlO1xufVxuXG4vKipcbiAqIERlYnVnIGZ1bmN0aW9uIHRvIHNob3cgaG93IHNlYXJjaCB0ZXJtcyBhcmUgbm9ybWFsaXplZFxuICogSGVscHMgdHJvdWJsZXNob290IHNlYXJjaCBtYXRjaGluZyBpc3N1ZXNcbiAqIFxuICogQHBhcmFtIG9yaWdpbmFsVGVybSAtIFRoZSBvcmlnaW5hbCBzZWFyY2ggdGVybVxuICogQHJldHVybnMgT2JqZWN0IHdpdGggZGlmZmVyZW50IG5vcm1hbGl6YXRpb24gcmVzdWx0c1xuICovXG5leHBvcnQgZnVuY3Rpb24gZGVidWdOb3JtYWxpemF0aW9uKG9yaWdpbmFsVGVybTogc3RyaW5nKSB7XG4gIHJldHVybiB7XG4gICAgb3JpZ2luYWw6IG9yaWdpbmFsVGVybSxcbiAgICBub3JtYWxpemVkOiBub3JtYWxpemVTZWFyY2hUZXJtKG9yaWdpbmFsVGVybSksXG4gICAgcGFydGlhbE1hdGNoOiBub3JtYWxpemVGb3JQYXJ0aWFsTWF0Y2gob3JpZ2luYWxUZXJtKVxuICB9O1xufSJdfQ==