Files
SuperCharged-Claude-Code-Up…/middleware/validation.js
uroma 55aafbae9a Fix project isolation: Make loadChatHistory respect active project sessions
- Modified loadChatHistory() to check for active project before fetching all sessions
- When active project exists, use project.sessions instead of fetching from API
- Added detailed console logging to debug session filtering
- This prevents ALL sessions from appearing in every project's sidebar

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-22 14:43:05 +00:00

293 lines
6.8 KiB
JavaScript

/**
* Validation Middleware
*
* Provides validation middleware for route parameters and request bodies.
*/
// Session ID validation pattern
// 10-64 characters, alphanumeric, underscore, hyphen
// Updated to support timestamp-based session IDs like: session-1769029589191-gjqeg0i0i
const SESSION_ID_PATTERN = /^[a-zA-Z0-9_-]{10,64}$/;
// Terminal ID validation pattern
const TERMINAL_ID_PATTERN = /^term-[0-9]{13,}-[a-zA-Z0-9]{4,}$/;
/**
* Validate session ID format
* @param {string} sessionId - Session ID to validate
* @returns {boolean} True if valid
*/
function validateSessionIdFormat(sessionId) {
if (!sessionId) {
return false;
}
return SESSION_ID_PATTERN.test(sessionId);
}
/**
* Validate terminal ID format
* @param {string} terminalId - Terminal ID to validate
* @returns {boolean} True if valid
*/
function validateTerminalIdFormat(terminalId) {
if (!terminalId) {
return false;
}
return TERMINAL_ID_PATTERN.test(terminalId);
}
/**
* Middleware to validate session ID parameter
*/
function validateSessionId(req, res, next) {
const { sessionId } = req.params;
if (!sessionId) {
return res.status(400).json({
error: 'Session ID is required',
statusCode: 400
});
}
if (!validateSessionIdFormat(sessionId)) {
return res.status(400).json({
error: 'Invalid session ID format',
sessionId,
expected: '10-64 characters, alphanumeric, underscore, hyphen',
statusCode: 400
});
}
// Check session exists
// Note: We access the global claudeService instance from app.locals
const claudeService = req.app.locals.claudeService;
if (!claudeService) {
console.error('[ValidationError] claudeService not found in app.locals');
return res.status(500).json({
error: 'Service configuration error',
statusCode: 500
});
}
const session = claudeService.getSession(sessionId);
if (!session) {
return res.status(404).json({
error: 'Session not found',
sessionId,
hint: 'The session may have been deleted or never existed',
statusCode: 404
});
}
// Attach session context to request
req.sessionContext = session;
next();
}
/**
* Middleware to validate terminal ID parameter
*/
function validateTerminalId(req, res, next) {
const { terminalId } = req.params;
if (!terminalId) {
return res.status(400).json({
error: 'Terminal ID is required',
statusCode: 400
});
}
if (!validateTerminalIdFormat(terminalId)) {
return res.status(400).json({
error: 'Invalid terminal ID format',
terminalId,
expected: 'Format: term-{timestamp}-{random}',
statusCode: 400
});
}
// Check terminal exists
// Note: We access the global terminalService instance from app.locals
const terminalService = req.app.locals.terminalService;
if (!terminalService) {
console.error('[ValidationError] terminalService not found in app.locals');
return res.status(500).json({
error: 'Service configuration error',
statusCode: 500
});
}
const result = terminalService.getTerminal(terminalId);
if (!result.success) {
return res.status(404).json({
error: 'Terminal not found',
terminalId,
hint: 'The terminal may have been closed',
statusCode: 404
});
}
// Attach terminal context to request
req.terminalContext = result.terminal;
next();
}
/**
* Middleware to validate command/prompt body
*/
function validateCommand(req, res, next) {
const { command } = req.body;
if (!command || typeof command !== 'string') {
return res.status(400).json({
error: 'Command is required and must be a string',
statusCode: 400
});
}
if (command.trim().length === 0) {
return res.status(400).json({
error: 'Command cannot be empty',
statusCode: 400
});
}
if (command.length > 100000) {
return res.status(400).json({
error: 'Command too large (max 100KB)',
statusCode: 400
});
}
next();
}
/**
* Middleware to validate operations array
*/
function validateOperations(req, res, next) {
const { operations } = req.body;
if (!operations || !Array.isArray(operations)) {
return res.status(400).json({
error: 'Operations array is required',
statusCode: 400
});
}
if (operations.length === 0) {
return res.status(400).json({
error: 'Operations array cannot be empty',
statusCode: 400
});
}
if (operations.length > 1000) {
return res.status(400).json({
error: 'Too many operations (max 1000)',
statusCode: 400
});
}
// Validate each operation
for (let i = 0; i < operations.length; i++) {
const op = operations[i];
if (!op.type) {
return res.status(400).json({
error: `Operation at index ${i} missing 'type' field`,
operation: op,
statusCode: 400
});
}
const validTypes = ['read', 'write', 'delete', 'list', 'search'];
if (!validTypes.includes(op.type)) {
return res.status(400).json({
error: `Invalid operation type at index ${i}: ${op.type}`,
validTypes,
statusCode: 400
});
}
}
next();
}
/**
* Middleware to validate response for operations preview
*/
function validateResponse(req, res, next) {
const { response } = req.body;
if (!response || typeof response !== 'string') {
return res.status(400).json({
error: 'Response is required and must be a string',
statusCode: 400
});
}
if (response.trim().length === 0) {
return res.status(400).json({
error: 'Response cannot be empty',
statusCode: 400
});
}
if (response.length > 10000000) {
return res.status(400).json({
error: 'Response too large (max 10MB)',
statusCode: 400
});
}
next();
}
/**
* Error handler middleware
*/
function errorHandler(err, req, res, next) {
console.error('[ErrorHandler]', err);
// Handle validation errors
if (err.name === 'ValidationError') {
return res.status(400).json({
error: 'Validation error',
details: err.message,
statusCode: 400
});
}
// Handle not found errors
if (err.name === 'NotFoundError') {
return res.status(404).json({
error: 'Resource not found',
details: err.message,
statusCode: 404
});
}
// Default error response
res.status(500).json({
error: 'Internal server error',
message: err.message,
statusCode: 500
});
}
module.exports = {
validateSessionIdFormat,
validateTerminalIdFormat,
validateSessionId,
validateTerminalId,
validateCommand,
validateOperations,
validateResponse,
errorHandler
};