Files
FrenoCorp/server/websocket/index.ts
Michael Freno 754fce269f fix: implement critical security remediation for authentication and authorization
- 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).
2026-04-25 08:24:45 -04:00

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);
});
}