@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.
656 lines • 86 kB
JavaScript
/**
* Element Formatter/Cleaner Tool
*
* Fixes common issues with malformed DollhouseMCP elements:
* - Escaped newlines (\n instead of actual line breaks)
* - Malformed metadata (embedded in content instead of top-level)
* - Broken YAML structure
* - Makes elements human-readable and editable
*
* FIXES IMPLEMENTED (Issue #1190):
* 1. CRITICAL: Unescapes newline characters for readability
* 2. HIGH: Extracts embedded metadata to proper YAML structure
* 3. ENHANCEMENT: Formats YAML for consistency and readability
*/
import { promises as fs } from 'node:fs';
import * as path from 'node:path';
import * as yaml from 'js-yaml';
import { logger } from '../utils/logger.js';
import { ElementType } from '../portfolio/types.js';
import { SecureYamlParser } from '../security/secureYamlParser.js';
import { SecurityMonitor } from '../security/securityMonitor.js';
// Security: Maximum file size for processing (10MB)
const MAX_FILE_SIZE = 10 * 1024 * 1024;
export class ElementFormatter {
options;
fileOperations;
constructor(options = {}, fileOperations) {
this.options = {
backup: options.backup ?? true,
inPlace: options.inPlace ?? false,
validate: options.validate ?? true,
outputDir: options.outputDir ?? '',
maxFileSize: options.maxFileSize ?? MAX_FILE_SIZE
};
this.fileOperations = fileOperations;
}
// Note: validateYamlContent removed - SecureYamlParser handles all validation internally
/**
* Normalize Unicode in user input
*
* FIX: MEDIUM PRIORITY - Normalizes Unicode to prevent homograph attacks
*/
normalizeUnicode(input) {
// Use NFC (Canonical Decomposition, followed by Canonical Composition)
return input.normalize('NFC');
}
/**
* Format a single element file
* Refactored to reduce cognitive complexity by extracting methods
*/
async formatFile(filePath) {
// FIX: MEDIUM - Normalize Unicode in file path
filePath = this.normalizeUnicode(filePath);
const result = {
success: false,
filePath,
issues: [],
fixed: []
};
try {
// Check file size
const stats = await this.validateFileSize(filePath, result);
if (stats === null)
return result;
// Read and normalize content
const content = await this.readAndNormalizeFile(filePath, stats);
// Format content
const formatted = await this.formatContent(filePath, content, result);
// Validate if needed
if (!await this.validateFormattedContent(formatted, filePath, result)) {
return result;
}
// Create backup if requested
await this.createBackupIfNeeded(filePath, result);
// Write formatted content
await this.writeFormattedFile(filePath, formatted, result);
result.success = true;
result.fixed.push(`Formatted file written to ${this.getOutputPath(filePath)}`);
}
catch (error) {
this.handleFormatError(error, result, filePath);
}
return result;
}
/**
* Validate file size
*/
async validateFileSize(filePath, result) {
const stats = await this.fileOperations.stat(filePath);
if (stats.size > this.options.maxFileSize) {
result.error = `File size (${stats.size} bytes) exceeds maximum allowed (${this.options.maxFileSize} bytes)`;
result.issues.push('File too large for processing');
return null;
}
return stats;
}
/**
* Read and normalize file content
*/
async readAndNormalizeFile(filePath, stats) {
let content = await this.fileOperations.readFile(filePath, { source: 'ElementFormatter.readAndNormalizeFile' });
content = this.normalizeUnicode(content);
SecurityMonitor.logSecurityEvent({
type: 'FILE_COPIED',
severity: 'LOW',
source: 'ElementFormatter',
details: `Processing file: ${filePath} (${stats.size} bytes)`
});
return content;
}
/**
* Format content based on element type
*/
async formatContent(filePath, content, result) {
const elementType = this.detectElementType(filePath);
if (elementType === ElementType.MEMORY) {
return await this.formatMemory(content, filePath, result);
}
else {
return await this.formatStandardElement(content, result);
}
}
/**
* Validate formatted content if validation is enabled
*/
async validateFormattedContent(formatted, filePath, result) {
if (!this.options.validate)
return true;
try {
const elementType = this.detectElementType(filePath);
const yamlToValidate = elementType === ElementType.MEMORY
? `---\n${formatted}\n---\n`
: formatted;
// FIX (Issue #1211): Local files are pre-trusted (same as MemoryManager PR #1207)
SecureYamlParser.parse(yamlToValidate, {
validateContent: false,
validateFields: false
});
result.fixed.push('YAML validation passed');
return true;
}
catch (error) {
result.issues.push(`YAML validation failed: ${error}`);
result.success = false;
SecurityMonitor.logSecurityEvent({
type: 'YAML_PARSING_WARNING',
severity: 'MEDIUM',
source: 'ElementFormatter',
details: `YAML validation failed for ${filePath}`,
additionalData: {
error: error instanceof Error ? error.message : String(error)
}
});
return false;
}
}
/**
* Create backup if requested
*/
async createBackupIfNeeded(filePath, result) {
if (!this.options.backup)
return;
const backupPath = filePath + '.backup';
await this.fileOperations.copyFile(filePath, backupPath, { source: 'ElementFormatter.createBackupIfNeeded' });
result.backupPath = backupPath;
result.fixed.push(`Created backup at ${backupPath}`);
SecurityMonitor.logSecurityEvent({
type: 'FILE_COPIED',
severity: 'LOW',
source: 'ElementFormatter',
details: `Backup created: ${backupPath}`,
additionalData: {
originalFile: filePath,
backupFile: backupPath
}
});
}
/**
* Write formatted content to file
*/
async writeFormattedFile(filePath, formatted, result) {
const outputPath = this.getOutputPath(filePath);
await this.fileOperations.writeFile(outputPath, formatted, { source: 'ElementFormatter.writeFormattedFile' });
SecurityMonitor.logSecurityEvent({
type: 'FILE_COPIED',
severity: 'LOW',
source: 'ElementFormatter',
details: `File formatted successfully: ${outputPath}`,
additionalData: {
inputPath: filePath,
outputPath,
backup: result.backupPath || 'none'
}
});
}
/**
* Handle formatting errors
*/
handleFormatError(error, result, filePath) {
if (error instanceof Error) {
result.error = error.message;
if (error.message.includes('ENOENT')) {
result.issues.push('File not found');
}
else if (error.message.includes('EACCES')) {
result.issues.push('Permission denied');
}
else if (error.message.includes('Path traversal')) {
result.issues.push('Security: Path traversal attempt blocked');
}
}
else {
result.error = String(error);
}
logger.error('Failed to format file', {
filePath,
error: result.error,
errorType: error instanceof Error ? error.constructor.name : typeof error
});
}
/**
* Format multiple files
*
* FIX: Added parallel processing with concurrency limit for better performance
*/
async formatFiles(filePaths, concurrencyLimit = 5) {
const results = [];
// Process files in batches for controlled parallelism
for (let i = 0; i < filePaths.length; i += concurrencyLimit) {
const batch = filePaths.slice(i, i + concurrencyLimit);
const batchResults = await Promise.all(batch.map(filePath => this.formatFile(filePath)));
results.push(...batchResults);
}
return results;
}
/**
* Format all elements of a specific type
*/
async formatElementType(elementType, portfolioDir) {
const results = [];
const elementDir = path.join(portfolioDir, elementType);
try {
if (elementType === ElementType.MEMORY) {
// Handle memory-specific structure (date folders)
results.push(...await this.formatMemoryDirectory(elementDir));
}
else {
// Handle standard element structure (.md files in root)
results.push(...await this.formatStandardDirectory(elementDir));
}
}
catch (error) {
logger.error(`Failed to format element type: ${elementType}`, { error });
}
return results;
}
/**
* Format memory elements in date folder structure
*/
async formatMemoryDirectory(memoryDir) {
const results = [];
try {
const entries = await this.fileOperations.listDirectoryWithTypes(memoryDir);
// Process root .yaml files
for (const entry of entries) {
if (!entry.isDirectory && entry.name.endsWith('.yaml')) {
const filePath = path.join(memoryDir, entry.name);
results.push(await this.formatFile(filePath));
}
}
// Process date folders
// Use RegExp.test() directly as per SonarCloud S6594
const datePattern = /^\d{4}-\d{2}-\d{2}$/;
const dateFolders = entries.filter(e => e.isDirectory && datePattern.test(e.name));
for (const folder of dateFolders) {
const folderPath = path.join(memoryDir, folder.name);
const files = await fs.readdir(folderPath);
for (const file of files.filter(f => f.endsWith('.yaml'))) {
const filePath = path.join(folderPath, file);
results.push(await this.formatFile(filePath));
}
}
}
catch (error) {
logger.error('Failed to format memory directory', { memoryDir, error });
}
return results;
}
/**
* Format standard elements (.md files)
*/
async formatStandardDirectory(elementDir) {
const results = [];
try {
const files = await this.fileOperations.listDirectory(elementDir);
const mdFiles = files.filter(f => f.endsWith('.md'));
for (const file of mdFiles) {
const filePath = path.join(elementDir, file);
results.push(await this.formatFile(filePath));
}
}
catch (error) {
logger.error('Failed to format standard directory', { elementDir, error });
}
return results;
}
/**
* Format a memory YAML file
* Refactored to reduce cognitive complexity
*/
async formatMemory(content, filePath, result) {
try {
const data = await this.parseMemoryContent(content);
// Process entries if they exist
if (data.entries && Array.isArray(data.entries)) {
await this.processMemoryEntries(data.entries, data, result);
}
// Ensure proper structure
this.ensureMemoryStructure(data, filePath, result);
// Format as clean YAML
return this.formatAsYaml(data);
}
catch (error) {
result.issues.push(`Failed to parse YAML: ${error}`);
return content; // Return original content if we can't parse it
}
}
/**
* Parse memory content using SecureYamlParser
*/
async parseMemoryContent(content) {
const wrappedContent = `---\n${content}\n---\n`;
// FIX (Issue #1211): Local files are pre-trusted (same as MemoryManager PR #1207)
const parsed = SecureYamlParser.parse(wrappedContent, {
validateContent: false,
validateFields: false
});
return parsed.data;
}
/**
* Process memory entries to fix issues
*/
async processMemoryEntries(entries, data, result) {
for (const entry of entries) {
if (typeof entry.content !== 'string')
continue;
// Normalize Unicode
entry.content = this.normalizeUnicode(entry.content);
// Handle embedded metadata
if (this.hasEmbeddedMetadata(entry.content)) {
this.handleEmbeddedMetadata(entry, data, result);
}
else {
this.unescapeEntryContent(entry, result);
}
}
}
/**
* Check if content has embedded metadata
*/
hasEmbeddedMetadata(content) {
// Check for both actual newlines and escaped newlines
// Using String.raw to properly handle escape sequences (SonarCloud compliance)
const actualNewline = '---\n';
const escapedNewline = String.raw `---\n`; // This represents the literal string "---\n"
return content.includes(actualNewline) ||
content.includes(escapedNewline);
}
/**
* Handle embedded metadata extraction
*/
handleEmbeddedMetadata(entry, data, result) {
result.issues.push('Found embedded metadata in content');
// First unescape the content to make it parseable
const unescapedContent = this.unescapeNewlines(entry.content);
// Then try to extract metadata from the unescaped content
const extracted = this.extractEmbeddedMetadata(unescapedContent);
if (extracted.metadata) {
// Merge extracted metadata to top level
Object.assign(data, extracted.metadata);
// Update entry with clean content
entry.content = extracted.content;
result.fixed.push('Extracted embedded metadata to top level', 'Unescaped newlines in content');
}
else {
// Just unescape if no metadata found
entry.content = unescapedContent;
result.fixed.push('Unescaped newlines in content');
}
}
/**
* Unescape entry content
*/
unescapeEntryContent(entry, result) {
const original = entry.content;
entry.content = this.unescapeNewlines(entry.content);
if (original !== entry.content) {
result.fixed.push('Unescaped newlines in content');
}
}
/**
* Ensure memory has proper structure
* FIX (Issue #1211): Derive name from filename instead of auto-generated entry ID
*/
ensureMemoryStructure(data, filePath, result) {
if (!data.name) {
// Derive name from filename, removing extension and normalizing
const filename = path.basename(filePath, path.extname(filePath));
data.name = filename;
result.fixed.push(`Added name field from filename: ${filename}`);
}
}
/**
* Format data as clean YAML
* FIX: Improved YAML formatting for consistency and special character handling
*/
formatAsYaml(data) {
return yaml.dump(data, {
lineWidth: 120,
noRefs: true,
sortKeys: false,
quotingType: '"',
forceQuotes: false,
// Use block scalar style for strings containing newlines
// This preserves formatting while keeping tabs/returns readable
styles: {
'!!str': (str) => {
// Use block scalar for multiline strings
if (typeof str === 'string' && str.includes('\n')) {
return 'literal';
}
// Use default (quoted) for other strings, including those with tabs/returns
return 'plain';
}
}
});
}
/**
* Format a standard element (markdown with frontmatter)
*/
async formatStandardElement(content, result) {
try {
// Split frontmatter and content using RegExp.exec() as per SonarCloud S6594
const frontmatterRegex = /^---\n([\s\S]*?)\n---\n([\s\S]*)$/;
const match = frontmatterRegex.exec(content);
if (!match) {
result.issues.push('No frontmatter found');
return content;
}
const [, frontmatterStr, body] = match;
// FIX: HIGH - Use SecureYamlParser for frontmatter
// FIX (Issue #1211): Local files are pre-trusted (same as MemoryManager PR #1207)
const tempDoc = `---\n${frontmatterStr}\n---\n`;
const parsed = SecureYamlParser.parse(tempDoc, {
validateContent: false,
validateFields: false
});
const frontmatter = parsed.data;
// Clean frontmatter
if (frontmatter.content && typeof frontmatter.content === 'string') {
frontmatter.content = this.unescapeNewlines(frontmatter.content);
result.fixed.push('Unescaped newlines in frontmatter content');
}
// Clean body
const cleanBody = this.unescapeNewlines(body);
if (body !== cleanBody) {
result.fixed.push('Unescaped newlines in body');
}
// Reconstruct with clean YAML
const cleanFrontmatter = yaml.dump(frontmatter, {
lineWidth: 120,
noRefs: true,
sortKeys: false
});
return `---\n${cleanFrontmatter}---\n${cleanBody}`;
}
catch (error) {
result.issues.push(`Failed to parse element: ${error}`);
return content;
}
}
/**
* Extract embedded metadata from content string
*
* FIX: Security hotspot - Replaced regex vulnerable to catastrophic backtracking
* with a linear-time string parsing approach to prevent ReDoS attacks
*/
extractEmbeddedMetadata(content) {
// Content should already be unescaped by the time we get here
const unescaped = content;
// Use indexOf for linear-time parsing instead of regex to prevent ReDoS
const startMarker = '---';
const endMarker = '\n---\n';
// Try to find the start marker anywhere in the content
const trimmed = unescaped.trim();
const startIdx = trimmed.indexOf(startMarker);
if (startIdx === -1) {
return { metadata: null, content };
}
// Find the starting position after first marker
const startPos = startIdx + startMarker.length;
// Look for the end marker
const endPos = trimmed.indexOf(endMarker, startPos);
if (endPos === -1) {
// Try alternative end marker for edge cases
const altEndMarker = '---\n';
const altEndPos = trimmed.indexOf(altEndMarker, startPos + 1);
if (altEndPos === -1) {
return { metadata: null, content };
}
// Use alternative end position
const metadataStr = trimmed.slice(startPos, altEndPos).trim();
const cleanContent = trimmed.slice(altEndPos + altEndMarker.length).trim();
try {
// FIX (Issue #1211): Local files are pre-trusted (same as MemoryManager PR #1207)
const tempDoc = `---\n${metadataStr}\n---\n`;
const parsed = SecureYamlParser.parse(tempDoc, {
validateContent: false,
validateFields: false
});
return { metadata: parsed.data, content: cleanContent };
}
catch {
return { metadata: null, content };
}
}
// Extract metadata and content sections
const metadataStr = unescaped.slice(startPos, endPos).trim();
const cleanContent = unescaped.slice(endPos + endMarker.length).trim();
try {
// FIX: HIGH - Use SecureYamlParser for metadata extraction
// FIX (Issue #1211): Local files are pre-trusted (same as MemoryManager PR #1207)
const tempDoc = `---\n${metadataStr}\n---\n`;
const parsed = SecureYamlParser.parse(tempDoc, {
validateContent: false,
validateFields: false
});
const metadata = parsed.data;
return { metadata, content: cleanContent };
}
catch {
// If YAML parsing fails, return as-is
return { metadata: null, content };
}
}
/**
* Unescape content (public static method - Issue #874)
* Handles escaped newlines, tabs, and other escape sequences
*/
static unescapeContent(text) {
// Input validation: Handle edge cases
if (text === null || text === undefined) {
return '';
}
if (typeof text !== 'string') {
return String(text);
}
if (text.length === 0) {
return text;
}
// CRITICAL: Use placeholder approach to handle \\ correctly
// We must protect \\ from being processed as \t, \n, etc. after conversion
const BACKSLASH_PLACEHOLDER = '\x00BACKSLASH\x00'; // Use null bytes as delimiter
// Step 1: Replace \\ with placeholder to protect it
let result = text.replaceAll(String.raw `\\`, BACKSLASH_PLACEHOLDER);
// Step 2: Replace escape sequences with their actual characters
result = result.replaceAll(String.raw `\n`, '\n'); // Newline
result = result.replaceAll(String.raw `\r`, '\r'); // Carriage return
result = result.replaceAll(String.raw `\t`, '\t'); // Tab
// Step 3: Replace placeholder with single backslash
result = result.replaceAll(BACKSLASH_PLACEHOLDER, '\\');
return result;
}
/**
* Unescape newline characters (private instance method)
* Using replaceAll as per SonarCloud S7781
* Using character map to avoid escape sequence issues
*/
unescapeNewlines(text) {
return ElementFormatter.unescapeContent(text);
}
/**
* Detect element type from file path
* Enhanced with more explicit and robust path matching
*/
detectElementType(filePath) {
// Normalize path separators for cross-platform compatibility
const normalizedPath = filePath.replaceAll('\\', '/');
// Map of element type directory names to ElementType enum values
// More explicit than iterating through enum values
const elementTypeMap = {
'personas': ElementType.PERSONA,
'skills': ElementType.SKILL,
'templates': ElementType.TEMPLATE,
'agents': ElementType.AGENT,
'memories': ElementType.MEMORY,
'ensembles': ElementType.ENSEMBLE
};
// Split into path segments for accurate matching
const segments = normalizedPath.split('/').filter(s => s.length > 0);
// Find the element type by checking each segment against our explicit map
for (const segment of segments) {
const elementType = elementTypeMap[segment.toLowerCase()];
if (elementType) {
return elementType;
}
}
// Fallback: Use file extension as a hint
// .yaml files are typically memories, .md files are standard elements
if (filePath.endsWith('.yaml') || filePath.endsWith('.yml')) {
return ElementType.MEMORY;
}
// Default to PERSONA for .md files or unknown types
return ElementType.PERSONA;
}
/**
* Get output path for formatted file
*
* FIX: Added path traversal protection to prevent directory escape attacks
*/
getOutputPath(filePath) {
if (this.options.inPlace) {
return filePath;
}
if (this.options.outputDir) {
// Security: Validate output directory to prevent path traversal
// FIX: MEDIUM - Normalize Unicode in filename
const filename = this.normalizeUnicode(path.basename(filePath));
const safePath = path.resolve(this.options.outputDir, filename);
const expectedDir = path.resolve(this.options.outputDir);
// Ensure the resolved path is within the expected directory
if (!safePath.startsWith(expectedDir)) {
// FIX: LOW - Use SecurityMonitor for audit logging
SecurityMonitor.logSecurityEvent({
type: 'PATH_TRAVERSAL_ATTEMPT',
severity: 'HIGH',
source: 'ElementFormatter',
details: `Path traversal blocked: ${filename}`,
additionalData: {
attemptedPath: filename,
expectedDir,
resolvedPath: safePath
}
});
throw new Error(`Path traversal attempt detected: ${filename}`);
}
return safePath;
}
// Default: add .formatted before extension
const dir = path.dirname(filePath);
const ext = path.extname(filePath);
const base = path.basename(filePath, ext);
return path.join(dir, `${base}.formatted${ext}`);
}
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiRWxlbWVudEZvcm1hdHRlci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy91dGlscy9FbGVtZW50Rm9ybWF0dGVyLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOzs7Ozs7Ozs7Ozs7O0dBYUc7QUFFSCxPQUFPLEVBQUUsUUFBUSxJQUFJLEVBQUUsRUFBRSxNQUFNLFNBQVMsQ0FBQztBQUN6QyxPQUFPLEtBQUssSUFBSSxNQUFNLFdBQVcsQ0FBQztBQUNsQyxPQUFPLEtBQUssSUFBSSxNQUFNLFNBQVMsQ0FBQztBQUNoQyxPQUFPLEVBQUUsTUFBTSxFQUFFLE1BQU0sb0JBQW9CLENBQUM7QUFDNUMsT0FBTyxFQUFFLFdBQVcsRUFBRSxNQUFNLHVCQUF1QixDQUFDO0FBQ3BELE9BQU8sRUFBRSxnQkFBZ0IsRUFBRSxNQUFNLGlDQUFpQyxDQUFDO0FBQ25FLE9BQU8sRUFBRSxlQUFlLEVBQUUsTUFBTSxnQ0FBZ0MsQ0FBQztBQUdqRSxvREFBb0Q7QUFDcEQsTUFBTSxhQUFhLEdBQUcsRUFBRSxHQUFHLElBQUksR0FBRyxJQUFJLENBQUM7QUEwQnZDLE1BQU0sT0FBTyxnQkFBZ0I7SUFDVixPQUFPLENBQW9DO0lBQzNDLGNBQWMsQ0FBeUI7SUFFeEQsWUFBWSxVQUFtQyxFQUFFLEVBQUUsY0FBc0M7UUFDdkYsSUFBSSxDQUFDLE9BQU8sR0FBRztZQUNiLE1BQU0sRUFBRSxPQUFPLENBQUMsTUFBTSxJQUFJLElBQUk7WUFDOUIsT0FBTyxFQUFFLE9BQU8sQ0FBQyxPQUFPLElBQUksS0FBSztZQUNqQyxRQUFRLEVBQUUsT0FBTyxDQUFDLFFBQVEsSUFBSSxJQUFJO1lBQ2xDLFNBQVMsRUFBRSxPQUFPLENBQUMsU0FBUyxJQUFJLEVBQUU7WUFDbEMsV0FBVyxFQUFFLE9BQU8sQ0FBQyxXQUFXLElBQUksYUFBYTtTQUNsRCxDQUFDO1FBQ0YsSUFBSSxDQUFDLGNBQWMsR0FBRyxjQUFjLENBQUM7SUFDdkMsQ0FBQztJQUVELHlGQUF5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