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.

57 lines 7.07 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() .replace(/[-_\s]+/g, ' ') // Convert dashes, underscores to spaces .replace(/\.md$/, '') // Remove .md extension .trim(); } /** * 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 = [ /[\x00-\x1F\x7F]/, // Control characters /[<>]/, // Potential HTML/XML injection ]; for (const pattern of dangerousPatterns) { if (pattern.test(cleanQuery)) { throw new Error('Search query contains invalid characters'); } } return true; } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2VhcmNoVXRpbHMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvdXRpbHMvc2VhcmNoVXRpbHMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7O0dBRUc7QUFFSCxPQUFPLEVBQUUsZ0JBQWdCLEVBQUUsTUFBTSw0Q0FBNEMsQ0FBQztBQUU5RTs7Ozs7Ozs7R0FRRztBQUNILE1BQU0sVUFBVSxtQkFBbUIsQ0FBQyxJQUFZLEVBQUUsWUFBb0IsSUFBSTtJQUN4RSwrREFBK0Q7SUFDL0QsTUFBTSxVQUFVLEdBQUcsZ0JBQWdCLENBQUMsU0FBUyxDQUFDLElBQUksQ0FBQyxDQUFDO0lBQ3BELE1BQU0sU0FBUyxHQUFHLFVBQVUsQ0FBQyxpQkFBaUIsQ0FBQztJQUUvQyxzREFBc0Q7SUFDdEQsSUFBSSxTQUFTLENBQUMsTUFBTSxHQUFHLFNBQVMsRUFBRSxDQUFDO1FBQ2pDLE1BQU0sSUFBSSxLQUFLLENBQUMseUNBQXlDLFNBQVMsYUFBYSxDQUFDLENBQUM7SUFDbkYsQ0FBQztJQUVELE9BQU8sU0FBUyxDQUFDLFdBQVcsRUFBRTtTQUMzQixPQUFPLENBQUMsVUFBVSxFQUFFLEdBQUcsQ0FBQyxDQUFFLHdDQUF3QztTQUNsRSxPQUFPLENBQUMsT0FBTyxFQUFFLEVBQUUsQ0FBQyxDQUFPLHVCQUF1QjtTQUNsRCxJQUFJLEVBQUUsQ0FBQztBQUNaLENBQUM7QUFFRDs7Ozs7OztHQU9HO0FBQ0gsTUFBTSxVQUFVLG1CQUFtQixDQUFDLEtBQWEsRUFBRSxZQUFvQixJQUFJO0lBQ3pFLElBQUksQ0FBQyxLQUFLLElBQUksS0FBSyxDQUFDLElBQUksRUFBRSxDQUFDLE1BQU0sS0FBSyxDQUFDLEVBQUUsQ0FBQztRQUN4QyxNQUFNLElBQUksS0FBSyxDQUFDLDhCQUE4QixDQUFDLENBQUM7SUFDbEQsQ0FBQztJQUVELG9EQUFvRDtJQUNwRCxNQUFNLFVBQVUsR0FBRyxnQkFBZ0IsQ0FBQyxTQUFTLENBQUMsS0FBSyxDQUFDLENBQUM7SUFDckQsTUFBTSxVQUFVLEdBQUcsVUFBVSxDQUFDLGlCQUFpQixDQUFDO0lBRWhELElBQUksVUFBVSxDQUFDLE1BQU0sR0FBRyxTQUFTLEVBQUUsQ0FBQztRQUNsQyxNQUFNLElBQUksS0FBSyxDQUFDLDBDQUEwQyxTQUFTLGFBQWEsQ0FBQyxDQUFDO0lBQ3BGLENBQUM7SUFFRCw0REFBNEQ7SUFDNUQsTUFBTSxpQkFBaUIsR0FBRztRQUN4QixpQkFBaUIsRUFBRyxxQkFBcUI7UUFDekMsTUFBTSxFQUFjLCtCQUErQjtLQUNwRCxDQUFDO0lBRUYsS0FBSyxNQUFNLE9BQU8sSUFBSSxpQkFBaUIsRUFBRSxDQUFDO1FBQ3hDLElBQUksT0FBTyxDQUFDLElBQUksQ0FBQyxVQUFVLENBQUMsRUFBRSxDQUFDO1lBQzdCLE1BQU0sSUFBSSxLQUFLLENBQUMsMENBQTBDLENBQUMsQ0FBQztRQUM5RCxDQUFDO0lBQ0gsQ0FBQztJQUVELE9BQU8sSUFBSSxDQUFDO0FBQ2QsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbIi8qKlxuICogU2hhcmVkIHV0aWxpdGllcyBmb3Igc2VhcmNoIGZ1bmN0aW9uYWxpdHlcbiAqL1xuXG5pbXBvcnQgeyBVbmljb2RlVmFsaWRhdG9yIH0gZnJvbSAnLi4vc2VjdXJpdHkvdmFsaWRhdG9ycy91bmljb2RlVmFsaWRhdG9yLmpzJztcblxuLyoqXG4gKiBOb3JtYWxpemUgc2VhcmNoIHRlcm1zIGZvciBiZXR0ZXIgbWF0Y2hpbmdcbiAqIEhhbmRsZXMgc3BhY2VzLCBkYXNoZXMsIHVuZGVyc2NvcmVzLCBhbmQgZmlsZSBleHRlbnNpb25zXG4gKiBcbiAqIEBwYXJhbSB0ZXJtIC0gVGhlIHNlYXJjaCB0ZXJtIHRvIG5vcm1hbGl6ZVxuICogQHBhcmFtIG1heExlbmd0aCAtIE1heGltdW0gYWxsb3dlZCBsZW5ndGggZm9yIHRoZSBzZWFyY2ggdGVybSAoZGVmYXVsdDogMTAwMClcbiAqIEByZXR1cm5zIE5vcm1hbGl6ZWQgc2VhcmNoIHRlcm1cbiAqIEB0aHJvd3MgRXJyb3IgaWYgdGVybSBleGNlZWRzIG1heExlbmd0aFxuICovXG5leHBvcnQgZnVuY3Rpb24gbm9ybWFsaXplU2VhcmNoVGVybSh0ZXJtOiBzdHJpbmcsIG1heExlbmd0aDogbnVtYmVyID0gMTAwMCk6IHN0cmluZyB7XG4gIC8vIFNFQ1VSSVRZIEZJWDogTm9ybWFsaXplIFVuaWNvZGUgdG8gcHJldmVudCBob21vZ3JhcGggYXR0YWNrc1xuICBjb25zdCBub3JtYWxpemVkID0gVW5pY29kZVZhbGlkYXRvci5ub3JtYWxpemUodGVybSk7XG4gIGNvbnN0IGNsZWFuVGVybSA9IG5vcm1hbGl6ZWQubm9ybWFsaXplZENvbnRlbnQ7XG4gIFxuICAvLyBTZWN1cml0eTogTGltaXQgaW5wdXQgbGVuZ3RoIHRvIHByZXZlbnQgRG9TIGF0dGFja3NcbiAgaWYgKGNsZWFuVGVybS5sZW5ndGggPiBtYXhMZW5ndGgpIHtcbiAgICB0aHJvdyBuZXcgRXJyb3IoYFNlYXJjaCB0ZXJtIGV4Y2VlZHMgbWF4aW11bSBsZW5ndGggb2YgJHttYXhMZW5ndGh9IGNoYXJhY3RlcnNgKTtcbiAgfVxuICBcbiAgcmV0dXJuIGNsZWFuVGVybS50b0xvd2VyQ2FzZSgpXG4gICAgLnJlcGxhY2UoL1stX1xcc10rL2csICcgJykgIC8vIENvbnZlcnQgZGFzaGVzLCB1bmRlcnNjb3JlcyB0byBzcGFjZXNcbiAgICAucmVwbGFjZSgvXFwubWQkLywgJycpICAgICAgIC8vIFJlbW92ZSAubWQgZXh0ZW5zaW9uXG4gICAgLnRyaW0oKTtcbn1cblxuLyoqXG4gKiBWYWxpZGF0ZSBzZWFyY2ggcXVlcnkgbGVuZ3RoIGFuZCBjb250ZW50XG4gKiBcbiAqIEBwYXJhbSBxdWVyeSAtIFRoZSBzZWFyY2ggcXVlcnkgdG8gdmFsaWRhdGVcbiAqIEBwYXJhbSBtYXhMZW5ndGggLSBNYXhpbXVtIGFsbG93ZWQgbGVuZ3RoIChkZWZhdWx0OiAxMDAwKVxuICogQHJldHVybnMgdHJ1ZSBpZiB2YWxpZFxuICogQHRocm93cyBFcnJvciB3aXRoIGRlc2NyaXB0aXZlIG1lc3NhZ2UgaWYgaW52YWxpZFxuICovXG5leHBvcnQgZnVuY3Rpb24gdmFsaWRhdGVTZWFyY2hRdWVyeShxdWVyeTogc3RyaW5nLCBtYXhMZW5ndGg6IG51bWJlciA9IDEwMDApOiBib29sZWFuIHtcbiAgaWYgKCFxdWVyeSB8fCBxdWVyeS50cmltKCkubGVuZ3RoID09PSAwKSB7XG4gICAgdGhyb3cgbmV3IEVycm9yKCdTZWFyY2ggcXVlcnkgY2Fubm90IGJlIGVtcHR5Jyk7XG4gIH1cbiAgXG4gIC8vIFNFQ1VSSVRZIEZJWDogTm9ybWFsaXplIFVuaWNvZGUgYmVmb3JlIHZhbGlkYXRpb25cbiAgY29uc3Qgbm9ybWFsaXplZCA9IFVuaWNvZGVWYWxpZGF0b3Iubm9ybWFsaXplKHF1ZXJ5KTtcbiAgY29uc3QgY2xlYW5RdWVyeSA9IG5vcm1hbGl6ZWQubm9ybWFsaXplZENvbnRlbnQ7XG4gIFxuICBpZiAoY2xlYW5RdWVyeS5sZW5ndGggPiBtYXhMZW5ndGgpIHtcbiAgICB0aHJvdyBuZXcgRXJyb3IoYFNlYXJjaCBxdWVyeSBleGNlZWRzIG1heGltdW0gbGVuZ3RoIG9mICR7bWF4TGVuZ3RofSBjaGFyYWN0ZXJzYCk7XG4gIH1cbiAgXG4gIC8vIENoZWNrIGZvciBwb3RlbnRpYWwgaW5qZWN0aW9uIHBhdHRlcm5zIChiYXNpYyB2YWxpZGF0aW9uKVxuICBjb25zdCBkYW5nZXJvdXNQYXR0ZXJucyA9IFtcbiAgICAvW1xceDAwLVxceDFGXFx4N0ZdLywgIC8vIENvbnRyb2wgY2hhcmFjdGVyc1xuICAgIC9bPD5dLywgICAgICAgICAgICAgLy8gUG90ZW50aWFsIEhUTUwvWE1MIGluamVjdGlvblxuICBdO1xuICBcbiAgZm9yIChjb25zdCBwYXR0ZXJuIG9mIGRhbmdlcm91c1BhdHRlcm5zKSB7XG4gICAgaWYgKHBhdHRlcm4udGVzdChjbGVhblF1ZXJ5KSkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKCdTZWFyY2ggcXVlcnkgY29udGFpbnMgaW52YWxpZCBjaGFyYWN0ZXJzJyk7XG4gICAgfVxuICB9XG4gIFxuICByZXR1cm4gdHJ1ZTtcbn0iXX0=