mcp-quickbase
Version:
Work with Quickbase via Model Context Protocol
239 lines • 8.16 kB
JavaScript
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.fileExists = fileExists;
exports.ensureDirectoryExists = ensureDirectoryExists;
exports.getFileInfo = getFileInfo;
exports.readFileAsBuffer = readFileAsBuffer;
exports.writeFile = writeFile;
const fs = __importStar(require("fs"));
const path = __importStar(require("path"));
const logger_1 = require("./logger");
const logger = (0, logger_1.createLogger)('FileUtil');
/**
* Utility functions for file operations with security hardening
*/
// Maximum file size for reads (10MB)
const MAX_FILE_SIZE = 10 * 1024 * 1024;
// Get the working directory (can be overridden by environment variable)
const WORKING_DIR = process.env.QUICKBASE_WORKING_DIR || process.cwd();
/**
* Validate and sanitize a file path to prevent directory traversal
* @param filePath The file path to validate
* @returns Sanitized absolute path or null if invalid
*/
function sanitizePath(filePath) {
try {
// Resolve to absolute path
const absolutePath = path.resolve(WORKING_DIR, filePath);
// Ensure the path is within the working directory
const relative = path.relative(WORKING_DIR, absolutePath);
// Check for directory traversal attempts
if (relative.startsWith('..') || path.isAbsolute(relative)) {
logger.error('Path traversal attempt detected', {
filePath,
absolutePath,
relative,
workingDir: WORKING_DIR
});
return null;
}
return absolutePath;
}
catch (error) {
logger.error('Error sanitizing path', { filePath, error });
return null;
}
}
/**
* Check if a file exists
* @param filePath File path to check
* @returns True if the file exists
*/
function fileExists(filePath) {
try {
const safePath = sanitizePath(filePath);
if (!safePath) {
return false;
}
return fs.existsSync(safePath) && fs.statSync(safePath).isFile();
}
catch (error) {
logger.error('Error checking if file exists', { filePath, error });
return false;
}
}
/**
* Ensure a directory exists, creating it if necessary
* @param dirPath Directory path to ensure
* @returns True if the directory exists or was created
*/
function ensureDirectoryExists(dirPath) {
try {
const safePath = sanitizePath(dirPath);
if (!safePath) {
return false;
}
if (fs.existsSync(safePath)) {
return fs.statSync(safePath).isDirectory();
}
// Create the directory
fs.mkdirSync(safePath, { recursive: true });
return true;
}
catch (error) {
logger.error('Error ensuring directory exists', { dirPath, error });
return false;
}
}
/**
* Get information about a file
* @param filePath File path
* @returns File information or null if the file doesn't exist
*/
function getFileInfo(filePath) {
try {
const safePath = sanitizePath(filePath);
if (!safePath || !fileExists(filePath)) {
return null;
}
const stats = fs.statSync(safePath);
const ext = path.extname(filePath).toLowerCase();
// Simple mime type mapping
const mimeTypes = {
'.txt': 'text/plain',
'.html': 'text/html',
'.css': 'text/css',
'.js': 'application/javascript',
'.json': 'application/json',
'.xml': 'application/xml',
'.pdf': 'application/pdf',
'.zip': 'application/zip',
'.png': 'image/png',
'.jpg': 'image/jpeg',
'.jpeg': 'image/jpeg',
'.gif': 'image/gif',
'.svg': 'image/svg+xml',
'.doc': 'application/msword',
'.docx': 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
'.xls': 'application/vnd.ms-excel',
'.xlsx': 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
'.ppt': 'application/vnd.ms-powerpoint',
'.pptx': 'application/vnd.openxmlformats-officedocument.presentationml.presentation'
};
return {
name: path.basename(filePath),
size: stats.size,
extension: ext,
mimeType: mimeTypes[ext] || 'application/octet-stream',
lastModified: stats.mtime
};
}
catch (error) {
logger.error('Error getting file info', { filePath, error });
return null;
}
}
/**
* Read a file as a Buffer
* @param filePath File path
* @returns File contents as Buffer or null if an error occurs
*/
function readFileAsBuffer(filePath) {
try {
const safePath = sanitizePath(filePath);
if (!safePath) {
logger.error('Invalid file path', { filePath });
return null;
}
if (!fileExists(filePath)) {
logger.error('File does not exist', { filePath });
return null;
}
// Check file size before reading
const stats = fs.statSync(safePath);
if (stats.size > MAX_FILE_SIZE) {
logger.error('File too large', {
filePath,
size: stats.size,
maxSize: MAX_FILE_SIZE
});
return null;
}
return fs.readFileSync(safePath);
}
catch (error) {
logger.error('Error reading file', { filePath, error });
return null;
}
}
/**
* Write data to a file
* @param filePath File path to write to
* @param data Data to write
* @returns True if the file was written successfully
*/
function writeFile(filePath, data) {
try {
const safePath = sanitizePath(filePath);
if (!safePath) {
logger.error('Invalid file path', { filePath });
return false;
}
const dirPath = path.dirname(safePath);
const safeDirPath = sanitizePath(dirPath);
if (!safeDirPath || !ensureDirectoryExists(safeDirPath)) {
logger.error('Could not create directory for file', { dirPath: safeDirPath });
return false;
}
// Check data size limit
const dataSize = Buffer.isBuffer(data) ? data.length : Buffer.byteLength(data);
if (dataSize > MAX_FILE_SIZE) {
logger.error('Data too large to write', {
filePath,
size: dataSize,
maxSize: MAX_FILE_SIZE
});
return false;
}
fs.writeFileSync(safePath, data);
return true;
}
catch (error) {
logger.error('Error writing file', { filePath, error });
return false;
}
}
//# sourceMappingURL=file.js.map