const fs = require('fs'); const path = require('path'); // A simple key regex to prevent path traversal const validKeyRegex = /^[a-zA-Z0-9]+$/; // handles saving and retrieving all documents class FileDocumentStore { constructor(options) { this.basePath = options.path || './data'; this.logger = options.logger; this.logger.info('Path to data: ' + this.basePath); // Create directory if it does not exist if (!fs.existsSync(this.basePath)) { fs.mkdirSync(this.basePath, { recursive: true, mode: '700' }); } } // saves a new file to the filesystem set(key, data, callback) { if (!validKeyRegex.test(key)) { this.logger.warn(`Invalid key provided for set: ${key}`); return callback(false); } const resolvedBasePath = path.resolve(this.basePath); const filePath = path.resolve(resolvedBasePath, key); // Security check to ensure we are not writing outside of the data directory if (path.dirname(filePath) !== resolvedBasePath) { this.logger.error(`Path traversal attempt detected for key: ${key}`); return callback(false); } fs.writeFile(filePath, data, 'utf8', (err) => { if (err) { this.logger.error('Error writing file:', err); callback(false); } else { callback(true); } }); } // gets an existing file from the filesystem get(key, callback) { if (!validKeyRegex.test(key)) { this.logger.warn(`Invalid key provided for get: ${key}`); return callback(false); } const resolvedBasePath = path.resolve(this.basePath); const filePath = path.resolve(resolvedBasePath, key); // Security check to ensure we are not reading outside of the data directory if (path.dirname(filePath) !== resolvedBasePath) { this.logger.error(`Path traversal attempt detected for key: ${key}`); return callback(false); } fs.readFile(filePath, 'utf8', (err, data) => { if (err) { // Don't log error if file just doesn't exist if (err.code !== 'ENOENT') { this.logger.error('Error reading file:', err); } callback(false); } else { callback(data); } }); } } module.exports = FileDocumentStore;