Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 42 additions & 2 deletions server/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,22 @@
/**
* SECURITY NOTES for this server:
*
* 1. The /api/execute endpoint runs user-submitted code. It is the highest-risk
* surface. See server/routes/execute.ts for sandboxing details.
*
* 2. CORS is restricted to the configured ALLOWED_ORIGIN (defaults to
* localhost during development). For production, set the CS_ALLOWED_ORIGIN
* environment variable to the actual frontend origin.
*
* 3. Request body size is limited to prevent memory exhaustion attacks.
*
* 4. TODO(security): Add authentication before allowing code execution.
* Currently any client can execute arbitrary code on the server.
*
* 5. TODO(security): Add HTTPS termination (via reverse proxy or directly)
* for production deployments.
*/

import { Database } from 'bun:sqlite';
import { contentRoutes } from './routes/content';
import { progressRoutes } from './routes/progress';
Expand All @@ -6,11 +25,18 @@ import { initializeDatabase } from './db/schema';

const PORT = process.env['PORT'] ?? 3000;

// SECURITY: Restrict CORS to a specific origin in production.
// Set CS_ALLOWED_ORIGIN env var to your frontend's URL.
const ALLOWED_ORIGIN = process.env['CS_ALLOWED_ORIGIN'] ?? `http://localhost:4200`;

// SECURITY: Maximum request body size (256 KB) to prevent memory exhaustion
const MAX_REQUEST_BODY_BYTES = 256 * 1024;

const db = initializeDatabase();

const corsHeaders = {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS',
'Access-Control-Allow-Origin': ALLOWED_ORIGIN,
'Access-Control-Allow-Methods': 'GET, POST, OPTIONS',
'Access-Control-Allow-Headers': 'Content-Type',
};

Expand All @@ -24,6 +50,20 @@ Bun.serve({
return new Response(null, { headers: corsHeaders });
}

// SECURITY: Enforce request body size limit on POST requests
if (req.method === 'POST') {
const contentLength = req.headers.get('content-length');
if (contentLength && parseInt(contentLength, 10) > MAX_REQUEST_BODY_BYTES) {
return new Response(
JSON.stringify({ error: 'Request body too large' }),
{
status: 413,
headers: { ...corsHeaders, 'Content-Type': 'application/json' },
}
);
}
}

if (path.startsWith('/api/content')) {
return contentRoutes(req, url, corsHeaders);
}
Expand Down
Loading