Files
unknownbin/lib/file_storage.js

75 lines
2.5 KiB
JavaScript

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;