- Add Clerk token verification to tRPC context (server/trpc/index.ts) - Remove client-controlled authorId/reviewedById from revisions router - Require JWT_SECRET environment variable, remove hardcoded fallback - Add table name validation to prevent SQL injection in backup logic - Fix TRPCContext type to use better-sqlite3 instead of LibSQL - Update revisions router tests to use proper tRPC v11+ API - Add resetInMemoryState function for test isolation Security fixes address: - Critical: Authentication bypass via missing token verification - Critical: User impersonation via client-controlled IDs - High: Insecure WebSocket defaults with hardcoded secrets - High: SQL injection vulnerability in backup logic All tests passing (24/24).
82 lines
2.0 KiB
TypeScript
82 lines
2.0 KiB
TypeScript
/**
|
|
* WebSocket Server Entry Point
|
|
* Starts the Yjs sync server with JWT authentication
|
|
*/
|
|
|
|
import { createWebSocketServer } from './websocket/server';
|
|
|
|
interface ServerConfig {
|
|
port: number;
|
|
jwtSecret: string;
|
|
enableAuth: boolean;
|
|
}
|
|
|
|
/**
|
|
* Start the WebSocket sync server
|
|
*/
|
|
export async function startServer(config: ServerConfig) {
|
|
const { port, jwtSecret, enableAuth } = config;
|
|
|
|
// Auth middleware for JWT token validation
|
|
const authMiddleware = async (token: string) => {
|
|
if (!enableAuth) {
|
|
return { userId: 'anonymous', projectId: 'default' };
|
|
}
|
|
|
|
try {
|
|
const jwt = require('jsonwebtoken');
|
|
const decoded = jwt.verify(token, jwtSecret);
|
|
return {
|
|
userId: (decoded as any).userId,
|
|
projectId: (decoded as any).projectId,
|
|
};
|
|
} catch (error) {
|
|
throw new Error('Invalid JWT token');
|
|
}
|
|
};
|
|
|
|
const server = createWebSocketServer(port, {
|
|
authMiddleware: enableAuth ? authMiddleware : undefined,
|
|
});
|
|
|
|
server.on('listening', () => {
|
|
console.log(`WebSocket sync server listening on port ${port}`);
|
|
console.log(`Authentication ${enableAuth ? 'enabled' : 'disabled'}`);
|
|
});
|
|
|
|
server.on('error', (error) => {
|
|
console.error('WebSocket server error:', error);
|
|
});
|
|
|
|
// Graceful shutdown
|
|
process.on('SIGINT', () => {
|
|
console.log('\nShutting down WebSocket server...');
|
|
server.clients.forEach((client) => client.close());
|
|
server.close(() => {
|
|
console.log('WebSocket server closed');
|
|
process.exit(0);
|
|
});
|
|
});
|
|
|
|
return server;
|
|
}
|
|
|
|
// If run directly, start the server
|
|
if (require.main === module) {
|
|
const jwtSecret = process.env.JWT_SECRET;
|
|
if (!jwtSecret) {
|
|
throw new Error('JWT_SECRET environment variable is required. Please set it before starting the server.');
|
|
}
|
|
|
|
const config: ServerConfig = {
|
|
port: parseInt(process.env.WS_PORT || '8080', 10),
|
|
jwtSecret,
|
|
enableAuth: process.env.ENABLE_AUTH !== 'false',
|
|
};
|
|
|
|
startServer(config).catch((error) => {
|
|
console.error('Failed to start server:', error);
|
|
process.exit(1);
|
|
});
|
|
}
|