This commit is contained in:
2025-09-10 13:19:45 -07:00
parent 89449947a6
commit 6e2eb6d6ac

View File

@@ -1,78 +1,32 @@
// Helper function for CORS headers
const corsHeaders = {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'POST, OPTIONS',
'Access-Control-Allow-Headers': 'Content-Type',
};
// Handler for CORS preflight OPTIONS requests
function handleOptions(request) {
if (
request.headers.get('Origin') !== null &&
request.headers.get('Access-Control-Request-Method') !== null &&
request.headers.get('Access-Control-Request-Headers') !== null
) {
// Handle CORS pre-flight request.
return new Response(null, {
headers: {
...corsHeaders,
'Access-Control-Max-Age': '86400',
}
});
} else {
// Handle standard OPTIONS request.
return new Response(null, { headers: { 'Allow': 'POST, OPTIONS' } });
}
}
// Define shared headers for responses.
const headers = { 'Access-Control-Allow-Origin': '*', 'Access-Control-Allow-Headers': 'Content-Type' };
export default {
async fetch(request, env, ctx) {
if (request.method === 'OPTIONS') {
return handleOptions(request);
}
async fetch(request, env) {
// Handle CORS preflight requests.
if (request.method === 'OPTIONS') return new Response(null, { headers: { ...headers, 'Access-Control-Allow-Methods': 'POST', 'Access-Control-Max-Age': '86400' }});
if (request.method !== 'POST') {
return new Response('Method Not Allowed. This proxy only accepts POST requests.', { status: 405, headers: corsHeaders });
}
if (request.headers.get('content-type') !== 'application/json') {
return new Response(JSON.stringify({ error: 'Request must be application/json' }), { status: 400, headers: corsHeaders });
}
// Enforce POST and JSON content-type.
if (request.method !== 'POST') return new Response('Method Not Allowed', { status: 405, headers });
if (!request.headers.get('content-type')?.includes('application/json')) return new Response(JSON.stringify({ error: 'Request must be application/json' }), { status: 400, headers });
try {
const { query, params = [] } = await request.json();
if (!query || typeof query !== 'string') {
return new Response(JSON.stringify({ error: 'Invalid request: "query" property must be a non-empty string.' }), { status: 400, headers: corsHeaders });
}
// Basic query validation and security checks.
if (!query || typeof query !== 'string' || query.trim().includes(';')) return new Response(JSON.stringify({ error: 'Invalid or forbidden query provided.' }), { status: 400, headers });
if (!['select', 'insert', 'explain'].some(verb => query.trim().toLowerCase().startsWith(verb))) return new Response(JSON.stringify({ error: 'Forbidden: Only SELECT, INSERT, and EXPLAIN are permitted.' }), { status: 403, headers });
// --- CRITICAL: Sanitize and validate the query ---
const sanitizedQuery = query.trim().toLowerCase();
// Execute the prepared statement against D1.
const result = await env.D1_SUNE.prepare(query).bind(...params).all();
// 1. Disallow multiple statements
if (query.trim().includes(';')) {
return new Response(JSON.stringify({ error: 'Forbidden: Multiple SQL statements are not allowed.' }), { status: 403, headers: corsHeaders });
}
// 2. Only allow approved starting verbs
const allowedVerbs = ['select', 'insert', 'update', 'explain'];
const isAllowedVerb = allowedVerbs.some(verb => sanitizedQuery.startsWith(verb));
if (!isAllowedVerb) {
return new Response(JSON.stringify({ error: 'Forbidden: Operation not allowed. Only SELECT, INSERT, UPDATE, and EXPLAIN are permitted.' }), { status: 403, headers: corsHeaders });
}
// --- Execute the query ---
const stmt = env.DB.prepare(query).bind(...params);
const result = await stmt.all();
return new Response(JSON.stringify(result, null, 2), { headers: corsHeaders });
// Return results with correct content-type.
return new Response(JSON.stringify(result, null, 2), { headers: { ...headers, 'Content-Type': 'application/json' } });
} catch (e) {
// Catch D1 errors and other exceptions
const errorMsg = e.cause ? e.cause.message : e.message;
return new Response(JSON.stringify({ error: 'Database Error', details: errorMsg }), { status: 500, headers: corsHeaders });
// Catch and format any runtime errors.
const errorMsg = e.cause?.message ?? e.message;
return new Response(JSON.stringify({ error: 'Database Error', details: errorMsg }), { status: 500, headers });
}
},
};