mcp-quickbase
Version:
Work with Quickbase via Model Context Protocol
243 lines • 8.21 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