Skip to content

Commit 0307391

Browse files
andrewboldiclaude
andcommitted
fix: add null guards to DrawingCanvas and restrict image proxy to AoPS domains
DrawingCanvas: guard ctx in draw(), handleMouseDown(), resizeCanvas(), and effect to prevent crashes when canvas events fire before initialization. Worker: restrict image proxy to artofproblemsolving.com domains only, preventing SSRF via the open proxy endpoint. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent e0b6327 commit 0307391

2 files changed

Lines changed: 14 additions & 2 deletions

File tree

src/lib/components/DrawingCanvas.svelte

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@
3333
];
3434
3535
function resizeCanvas() {
36-
if (!canvas || !visible) return;
36+
if (!canvas || !visible || !ctx) return;
3737
const body = document.body;
3838
const html = document.documentElement;
3939
height = Math.max(body.scrollHeight, body.offsetHeight, html.clientHeight, html.scrollHeight, html.offsetHeight);
@@ -50,13 +50,14 @@
5050
if (visible && canvas) {
5151
ctx = canvas.getContext('2d')!;
5252
resizeCanvas();
53-
if (undoStack.length === 0) {
53+
if (ctx && undoStack.length === 0) {
5454
undoStack.push(ctx.getImageData(0, 0, width, height));
5555
}
5656
}
5757
});
5858
5959
function draw() {
60+
if (!ctx) return;
6061
ctx.beginPath();
6162
ctx.moveTo(prevX, prevY);
6263
if (tool === 'eraser') {
@@ -78,6 +79,7 @@
7879
}
7980
8081
function handleMouseDown(e: MouseEvent) {
82+
if (!ctx) return;
8183
prevX = currX;
8284
prevY = currY;
8385
currX = e.clientX - canvas.offsetLeft;

worker/src/worker.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -255,11 +255,21 @@ async function handleIngest(request: Request, env: Env): Promise<Response> {
255255
}
256256

257257
/** Legacy image proxy — forwards requests to external URLs (AoPS diagrams, etc.) */
258+
const ALLOWED_PROXY_HOSTS = ['latex.artofproblemsolving.com', 'artofproblemsolving.com', 'wiki-images.artofproblemsolving.com'];
259+
258260
async function handleImageProxy(url: URL, request: Request): Promise<Response> {
259261
const targetUrl = decodeURIComponent(url.search.slice(1));
260262
if (!targetUrl.startsWith('https://')) {
261263
return error('Invalid proxy URL', 400);
262264
}
265+
try {
266+
const parsed = new URL(targetUrl);
267+
if (!ALLOWED_PROXY_HOSTS.includes(parsed.hostname)) {
268+
return error('Proxy domain not allowed', 403);
269+
}
270+
} catch {
271+
return error('Invalid proxy URL', 400);
272+
}
263273

264274
const response = await fetch(targetUrl, {
265275
headers: { 'User-Agent': 'AMC-Trainer-Proxy/2.0' },

0 commit comments

Comments
 (0)