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.

126 lines 12 kB
/** * Context Tracking for LLM Request Detection * * Part of Issue #1321 Phase 2: Memory Security Architecture * * PURPOSE: * Tracks execution context to detect if code is running within an LLM request * handler. Uses AsyncLocalStorage to maintain context across async operations. * * SECURITY: * - Prevents pattern decryption in LLM contexts * - Ensures patterns never leak to LLM responses * - Provides audit trail for context checks * * REFACTOR NOTE: * Converted from static class to instance-based for DI architecture compatibility. * ContextTracker now uses instance methods instead of static methods, allowing * proper lifecycle management and testability within DI container. * * @module ContextTracker */ import { AsyncLocalStorage } from 'node:async_hooks'; import { randomBytes } from 'node:crypto'; import { logger } from '../../utils/logger.js'; /** * Context tracker using AsyncLocalStorage * * Maintains execution context across async operations to detect * LLM request handling and prevent pattern decryption in those contexts. * * DI-COMPATIBLE: Instance-based service for dependency injection. */ export class ContextTracker { storage = new AsyncLocalStorage(); /** * Create a new ContextTracker instance */ constructor() { logger.debug('ContextTracker initialized'); } /** * Run a function within a specific execution context * * @param context - Execution context to set * @param fn - Function to run within the context * @returns Result of the function */ run(context, fn) { return this.storage.run(context, fn); } /** * Run an async function within a specific execution context * * @param context - Execution context to set * @param fn - Async function to run within the context * @returns Promise resolving to the function result */ async runAsync(context, fn) { return this.storage.run(context, fn); } /** * Get the current execution context * * @returns Current context or undefined if no context is set */ getContext() { return this.storage.getStore(); } /** * Check if currently in an LLM request context * * @returns true if in LLM request context, false otherwise */ isLLMContext() { const context = this.getContext(); return context?.type === 'llm-request'; } /** * Create a new execution context object * * @param type - Context type * @param metadata - Optional metadata * @returns New execution context */ createContext(type, metadata) { return { type, requestId: this.generateRequestId(), timestamp: Date.now(), metadata, }; } /** * Generate a unique request ID using cryptographically secure random bytes * * @returns Unique request ID */ generateRequestId() { // Use cryptographically secure random bytes instead of Math.random() const randomId = randomBytes(4).toString('hex'); return `${Date.now()}-${randomId}`; } /** * Get the current correlation ID (request-level identifier). * Returns undefined when no context is active (e.g. background timers). */ getCorrelationId() { return this.getContext()?.requestId; } /** * Clear the current context (useful for testing) */ clearContext() { // AsyncLocalStorage doesn't have a direct clear method // Context is automatically cleared when execution exits } /** * Dispose of the context tracker and clean up resources * Implements cleanup for proper DI lifecycle management */ async dispose() { this.clearContext(); logger.debug('ContextTracker disposed'); } } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiQ29udGV4dFRyYWNrZXIuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi9zcmMvc2VjdXJpdHkvZW5jcnlwdGlvbi9Db250ZXh0VHJhY2tlci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7R0FvQkc7QUFFSCxPQUFPLEVBQUUsaUJBQWlCLEVBQUUsTUFBTSxrQkFBa0IsQ0FBQztBQUNyRCxPQUFPLEVBQUUsV0FBVyxFQUFFLE1BQU0sYUFBYSxDQUFDO0FBQzFDLE9BQU8sRUFBRSxNQUFNLEVBQUUsTUFBTSx1QkFBdUIsQ0FBQztBQW1CL0M7Ozs7Ozs7R0FPRztBQUNILE1BQU0sT0FBTyxjQUFjO0lBQ1IsT0FBTyxHQUFHLElBQUksaUJBQWlCLEVBQW9CLENBQUM7SUFFckU7O09BRUc7SUFDSDtRQUNFLE1BQU0sQ0FBQyxLQUFLLENBQUMsNEJBQTRCLENBQUMsQ0FBQztJQUM3QyxDQUFDO0lBRUQ7Ozs7OztPQU1HO0lBQ0gsR0FBRyxDQUFJLE9BQXlCLEVBQUUsRUFBVztRQUMzQyxPQUFPLElBQUksQ0FBQyxPQUFPLENBQUMsR0FBRyxDQUFDLE9BQU8sRUFBRSxFQUFFLENBQUMsQ0FBQztJQUN2QyxDQUFDO0lBRUQ7Ozs7OztPQU1HO0lBQ0gsS0FBSyxDQUFDLFFBQVEsQ0FDWixPQUF5QixFQUN6QixFQUFvQjtRQUVwQixPQUFPLElBQUksQ0FBQyxPQUFPLENBQUMsR0FBRyxDQUFDLE9BQU8sRUFBRSxFQUFFLENBQUMsQ0FBQztJQUN2QyxDQUFDO0lBRUQ7Ozs7T0FJRztJQUNILFVBQVU7UUFDUixPQUFPLElBQUksQ0FBQyxPQUFPLENBQUMsUUFBUSxFQUFFLENBQUM7SUFDakMsQ0FBQztJQUVEOzs7O09BSUc7SUFDSCxZQUFZO1FBQ1YsTUFBTSxPQUFPLEdBQUcsSUFBSSxDQUFDLFVBQVUsRUFBRSxDQUFDO1FBQ2xDLE9BQU8sT0FBTyxFQUFFLElBQUksS0FBSyxhQUFhLENBQUM7SUFDekMsQ0FBQztJQUVEOzs7Ozs7T0FNRztJQUNILGFBQWEsQ0FDWCxJQUE4QixFQUM5QixRQUFrQztRQUVsQyxPQUFPO1lBQ0wsSUFBSTtZQUNKLFNBQVMsRUFBRSxJQUFJLENBQUMsaUJBQWlCLEVBQUU7WUFDbkMsU0FBUyxFQUFFLElBQUksQ0FBQyxHQUFHLEVBQUU7WUFDckIsUUFBUTtTQUNULENBQUM7SUFDSixDQUFDO0lBRUQ7Ozs7T0FJRztJQUNLLGlCQUFpQjtRQUN2QixxRUFBcUU7UUFDckUsTUFBTSxRQUFRLEdBQUcsV0FBVyxDQUFDLENBQUMsQ0FBQyxDQUFDLFFBQVEsQ0FBQyxLQUFLLENBQUMsQ0FBQztRQUNoRCxPQUFPLEdBQUcsSUFBSSxDQUFDLEdBQUcsRUFBRSxJQUFJLFFBQVEsRUFBRSxDQUFDO0lBQ3JDLENBQUM7SUFFRDs7O09BR0c7SUFDSCxnQkFBZ0I7UUFDZCxPQUFPLElBQUksQ0FBQyxVQUFVLEVBQUUsRUFBRSxTQUFTLENBQUM7SUFDdEMsQ0FBQztJQUVEOztPQUVHO0lBQ0gsWUFBWTtRQUNWLHVEQUF1RDtRQUN2RCx3REFBd0Q7SUFDMUQsQ0FBQztJQUVEOzs7T0FHRztJQUNILEtBQUssQ0FBQyxPQUFPO1FBQ1gsSUFBSSxDQUFDLFlBQVksRUFBRSxDQUFDO1FBQ3BCLE1BQU0sQ0FBQyxLQUFLLENBQUMseUJBQXlCLENBQUMsQ0FBQztJQUMxQyxDQUFDO0NBQ0YiLCJzb3VyY2VzQ29udGVudCI6WyIvKipcbiAqIENvbnRleHQgVHJhY2tpbmcgZm9yIExMTSBSZXF1ZXN0IERldGVjdGlvblxuICpcbiAqIFBhcnQgb2YgSXNzdWUgIzEzMjEgUGhhc2UgMjogTWVtb3J5IFNlY3VyaXR5IEFyY2hpdGVjdHVyZVxuICpcbiAqIFBVUlBPU0U6XG4gKiBUcmFja3MgZXhlY3V0aW9uIGNvbnRleHQgdG8gZGV0ZWN0IGlmIGNvZGUgaXMgcnVubmluZyB3aXRoaW4gYW4gTExNIHJlcXVlc3RcbiAqIGhhbmRsZXIuIFVzZXMgQXN5bmNMb2NhbFN0b3JhZ2UgdG8gbWFpbnRhaW4gY29udGV4dCBhY3Jvc3MgYXN5bmMgb3BlcmF0aW9ucy5cbiAqXG4gKiBTRUNVUklUWTpcbiAqIC0gUHJldmVudHMgcGF0dGVybiBkZWNyeXB0aW9uIGluIExMTSBjb250ZXh0c1xuICogLSBFbnN1cmVzIHBhdHRlcm5zIG5ldmVyIGxlYWsgdG8gTExNIHJlc3BvbnNlc1xuICogLSBQcm92aWRlcyBhdWRpdCB0cmFpbCBmb3IgY29udGV4dCBjaGVja3NcbiAqXG4gKiBSRUZBQ1RPUiBOT1RFOlxuICogQ29udmVydGVkIGZyb20gc3RhdGljIGNsYXNzIHRvIGluc3RhbmNlLWJhc2VkIGZvciBESSBhcmNoaXRlY3R1cmUgY29tcGF0aWJpbGl0eS5cbiAqIENvbnRleHRUcmFja2VyIG5vdyB1c2VzIGluc3RhbmNlIG1ldGhvZHMgaW5zdGVhZCBvZiBzdGF0aWMgbWV0aG9kcywgYWxsb3dpbmdcbiAqIHByb3BlciBsaWZlY3ljbGUgbWFuYWdlbWVudCBhbmQgdGVzdGFiaWxpdHkgd2l0aGluIERJIGNvbnRhaW5lci5cbiAqXG4gKiBAbW9kdWxlIENvbnRleHRUcmFja2VyXG4gKi9cblxuaW1wb3J0IHsgQXN5bmNMb2NhbFN0b3JhZ2UgfSBmcm9tICdub2RlOmFzeW5jX2hvb2tzJztcbmltcG9ydCB7IHJhbmRvbUJ5dGVzIH0gZnJvbSAnbm9kZTpjcnlwdG8nO1xuaW1wb3J0IHsgbG9nZ2VyIH0gZnJvbSAnLi4vLi4vdXRpbHMvbG9nZ2VyLmpzJztcblxuLyoqXG4gKiBFeGVjdXRpb24gY29udGV4dCBpbmZvcm1hdGlvblxuICovXG5leHBvcnQgaW50ZXJmYWNlIEV4ZWN1dGlvbkNvbnRleHQge1xuICAvKiogQ29udGV4dCB0eXBlIChsbG0tcmVxdWVzdCwgYmFja2dyb3VuZC10YXNrLCB0ZXN0LCBldGMuKSAqL1xuICB0eXBlOiAnbGxtLXJlcXVlc3QnIHwgJ2JhY2tncm91bmQtdGFzaycgfCAndGVzdCcgfCAndW5rbm93bic7XG5cbiAgLyoqIFJlcXVlc3QgSUQgZm9yIGNvcnJlbGF0aW9uICovXG4gIHJlcXVlc3RJZD86IHN0cmluZztcblxuICAvKiogVGltZXN0YW1wIHdoZW4gY29udGV4dCB3YXMgY3JlYXRlZCAqL1xuICB0aW1lc3RhbXA6IG51bWJlcjtcblxuICAvKiogQWRkaXRpb25hbCBjb250ZXh0IG1ldGFkYXRhICovXG4gIG1ldGFkYXRhPzogUmVjb3JkPHN0cmluZywgdW5rbm93bj47XG59XG5cbi8qKlxuICogQ29udGV4dCB0cmFja2VyIHVzaW5nIEFzeW5jTG9jYWxTdG9yYWdlXG4gKlxuICogTWFpbnRhaW5zIGV4ZWN1dGlvbiBjb250ZXh0IGFjcm9zcyBhc3luYyBvcGVyYXRpb25zIHRvIGRldGVjdFxuICogTExNIHJlcXVlc3QgaGFuZGxpbmcgYW5kIHByZXZlbnQgcGF0dGVybiBkZWNyeXB0aW9uIGluIHRob3NlIGNvbnRleHRzLlxuICpcbiAqIERJLUNPTVBBVElCTEU6IEluc3RhbmNlLWJhc2VkIHNlcnZpY2UgZm9yIGRlcGVuZGVuY3kgaW5qZWN0aW9uLlxuICovXG5leHBvcnQgY2xhc3MgQ29udGV4dFRyYWNrZXIge1xuICBwcml2YXRlIHJlYWRvbmx5IHN0b3JhZ2UgPSBuZXcgQXN5bmNMb2NhbFN0b3JhZ2U8RXhlY3V0aW9uQ29udGV4dD4oKTtcblxuICAvKipcbiAgICogQ3JlYXRlIGEgbmV3IENvbnRleHRUcmFja2VyIGluc3RhbmNlXG4gICAqL1xuICBjb25zdHJ1Y3RvcigpIHtcbiAgICBsb2dnZXIuZGVidWcoJ0NvbnRleHRUcmFja2VyIGluaXRpYWxpemVkJyk7XG4gIH1cblxuICAvKipcbiAgICogUnVuIGEgZnVuY3Rpb24gd2l0aGluIGEgc3BlY2lmaWMgZXhlY3V0aW9uIGNvbnRleHRcbiAgICpcbiAgICogQHBhcmFtIGNvbnRleHQgLSBFeGVjdXRpb24gY29udGV4dCB0byBzZXRcbiAgICogQHBhcmFtIGZuIC0gRnVuY3Rpb24gdG8gcnVuIHdpdGhpbiB0aGUgY29udGV4dFxuICAgKiBAcmV0dXJucyBSZXN1bHQgb2YgdGhlIGZ1bmN0aW9uXG4gICAqL1xuICBydW48VD4oY29udGV4dDogRXhlY3V0aW9uQ29udGV4dCwgZm46ICgpID0+IFQpOiBUIHtcbiAgICByZXR1cm4gdGhpcy5zdG9yYWdlLnJ1bihjb250ZXh0LCBmbik7XG4gIH1cblxuICAvKipcbiAgICogUnVuIGFuIGFzeW5jIGZ1bmN0aW9uIHdpdGhpbiBhIHNwZWNpZmljIGV4ZWN1dGlvbiBjb250ZXh0XG4gICAqXG4gICAqIEBwYXJhbSBjb250ZXh0IC0gRXhlY3V0aW9uIGNvbnRleHQgdG8gc2V0XG4gICAqIEBwYXJhbSBmbiAtIEFzeW5jIGZ1bmN0aW9uIHRvIHJ1biB3aXRoaW4gdGhlIGNvbnRleHRcbiAgICogQHJldHVybnMgUHJvbWlzZSByZXNvbHZpbmcgdG8gdGhlIGZ1bmN0aW9uIHJlc3VsdFxuICAgKi9cbiAgYXN5bmMgcnVuQXN5bmM8VD4oXG4gICAgY29udGV4dDogRXhlY3V0aW9uQ29udGV4dCxcbiAgICBmbjogKCkgPT4gUHJvbWlzZTxUPlxuICApOiBQcm9taXNlPFQ+IHtcbiAgICByZXR1cm4gdGhpcy5zdG9yYWdlLnJ1bihjb250ZXh0LCBmbik7XG4gIH1cblxuICAvKipcbiAgICogR2V0IHRoZSBjdXJyZW50IGV4ZWN1dGlvbiBjb250ZXh0XG4gICAqXG4gICAqIEByZXR1cm5zIEN1cnJlbnQgY29udGV4dCBvciB1bmRlZmluZWQgaWYgbm8gY29udGV4dCBpcyBzZXRcbiAgICovXG4gIGdldENvbnRleHQoKTogRXhlY3V0aW9uQ29udGV4dCB8IHVuZGVmaW5lZCB7XG4gICAgcmV0dXJuIHRoaXMuc3RvcmFnZS5nZXRTdG9yZSgpO1xuICB9XG5cbiAgLyoqXG4gICAqIENoZWNrIGlmIGN1cnJlbnRseSBpbiBhbiBMTE0gcmVxdWVzdCBjb250ZXh0XG4gICAqXG4gICAqIEByZXR1cm5zIHRydWUgaWYgaW4gTExNIHJlcXVlc3QgY29udGV4dCwgZmFsc2Ugb3RoZXJ3aXNlXG4gICAqL1xuICBpc0xMTUNvbnRleHQoKTogYm9vbGVhbiB7XG4gICAgY29uc3QgY29udGV4dCA9IHRoaXMuZ2V0Q29udGV4dCgpO1xuICAgIHJldHVybiBjb250ZXh0Py50eXBlID09PSAnbGxtLXJlcXVlc3QnO1xuICB9XG5cbiAgLyoqXG4gICAqIENyZWF0ZSBhIG5ldyBleGVjdXRpb24gY29udGV4dCBvYmplY3RcbiAgICpcbiAgICogQHBhcmFtIHR5cGUgLSBDb250ZXh0IHR5cGVcbiAgICogQHBhcmFtIG1ldGFkYXRhIC0gT3B0aW9uYWwgbWV0YWRhdGFcbiAgICogQHJldHVybnMgTmV3IGV4ZWN1dGlvbiBjb250ZXh0XG4gICAqL1xuICBjcmVhdGVDb250ZXh0KFxuICAgIHR5cGU6IEV4ZWN1dGlvbkNvbnRleHRbJ3R5cGUnXSxcbiAgICBtZXRhZGF0YT86IFJlY29yZDxzdHJpbmcsIHVua25vd24+XG4gICk6IEV4ZWN1dGlvbkNvbnRleHQge1xuICAgIHJldHVybiB7XG4gICAgICB0eXBlLFxuICAgICAgcmVxdWVzdElkOiB0aGlzLmdlbmVyYXRlUmVxdWVzdElkKCksXG4gICAgICB0aW1lc3RhbXA6IERhdGUubm93KCksXG4gICAgICBtZXRhZGF0YSxcbiAgICB9O1xuICB9XG5cbiAgLyoqXG4gICAqIEdlbmVyYXRlIGEgdW5pcXVlIHJlcXVlc3QgSUQgdXNpbmcgY3J5cHRvZ3JhcGhpY2FsbHkgc2VjdXJlIHJhbmRvbSBieXRlc1xuICAgKlxuICAgKiBAcmV0dXJucyBVbmlxdWUgcmVxdWVzdCBJRFxuICAgKi9cbiAgcHJpdmF0ZSBnZW5lcmF0ZVJlcXVlc3RJZCgpOiBzdHJpbmcge1xuICAgIC8vIFVzZSBjcnlwdG9ncmFwaGljYWxseSBzZWN1cmUgcmFuZG9tIGJ5dGVzIGluc3RlYWQgb2YgTWF0aC5yYW5kb20oKVxuICAgIGNvbnN0IHJhbmRvbUlkID0gcmFuZG9tQnl0ZXMoNCkudG9TdHJpbmcoJ2hleCcpO1xuICAgIHJldHVybiBgJHtEYXRlLm5vdygpfS0ke3JhbmRvbUlkfWA7XG4gIH1cblxuICAvKipcbiAgICogR2V0IHRoZSBjdXJyZW50IGNvcnJlbGF0aW9uIElEIChyZXF1ZXN0LWxldmVsIGlkZW50aWZpZXIpLlxuICAgKiBSZXR1cm5zIHVuZGVmaW5lZCB3aGVuIG5vIGNvbnRleHQgaXMgYWN0aXZlIChlLmcuIGJhY2tncm91bmQgdGltZXJzKS5cbiAgICovXG4gIGdldENvcnJlbGF0aW9uSWQoKTogc3RyaW5nIHwgdW5kZWZpbmVkIHtcbiAgICByZXR1cm4gdGhpcy5nZXRDb250ZXh0KCk/LnJlcXVlc3RJZDtcbiAgfVxuXG4gIC8qKlxuICAgKiBDbGVhciB0aGUgY3VycmVudCBjb250ZXh0ICh1c2VmdWwgZm9yIHRlc3RpbmcpXG4gICAqL1xuICBjbGVhckNvbnRleHQoKTogdm9pZCB7XG4gICAgLy8gQXN5bmNMb2NhbFN0b3JhZ2UgZG9lc24ndCBoYXZlIGEgZGlyZWN0IGNsZWFyIG1ldGhvZFxuICAgIC8vIENvbnRleHQgaXMgYXV0b21hdGljYWxseSBjbGVhcmVkIHdoZW4gZXhlY3V0aW9uIGV4aXRzXG4gIH1cblxuICAvKipcbiAgICogRGlzcG9zZSBvZiB0aGUgY29udGV4dCB0cmFja2VyIGFuZCBjbGVhbiB1cCByZXNvdXJjZXNcbiAgICogSW1wbGVtZW50cyBjbGVhbnVwIGZvciBwcm9wZXIgREkgbGlmZWN5Y2xlIG1hbmFnZW1lbnRcbiAgICovXG4gIGFzeW5jIGRpc3Bvc2UoKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgdGhpcy5jbGVhckNvbnRleHQoKTtcbiAgICBsb2dnZXIuZGVidWcoJ0NvbnRleHRUcmFja2VyIGRpc3Bvc2VkJyk7XG4gIH1cbn1cbiJdfQ==