From 00dfd2d47a103106156f59f19b9d0205d2298baf Mon Sep 17 00:00:00 2001 From: red person Date: Mon, 29 Jun 2026 10:54:44 -0700 Subject: [PATCH] Keep snap helper safe without context (#1828) --- static/js/editor/snap.js | 6 ++++-- tests/test_snap_other_layers_nonarray_js.py | 22 +++++++++++++++++++++ 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/static/js/editor/snap.js b/static/js/editor/snap.js index 42489765c..eebfaba82 100644 --- a/static/js/editor/snap.js +++ b/static/js/editor/snap.js @@ -23,8 +23,10 @@ * @returns {{x: number, y: number, guides: Array}} */ export function computeSnap(layer, nx, ny, ctx) { - const SNAP_PX = 6 / Math.max(ctx.zoom, 0.0001); - const cw = ctx.canvasW, ch = ctx.canvasH; + if (!layer || !layer.canvas || !ctx) return { x: nx, y: ny, guides: [] }; + const zoom = Number.isFinite(Number(ctx.zoom)) ? Number(ctx.zoom) : 1; + const SNAP_PX = 6 / Math.max(zoom, 0.0001); + const cw = Number(ctx.canvasW) || 0, ch = Number(ctx.canvasH) || 0; const w = layer.canvas.width, h = layer.canvas.height; const vTargets = [ diff --git a/tests/test_snap_other_layers_nonarray_js.py b/tests/test_snap_other_layers_nonarray_js.py index f99e10163..c2925d330 100644 --- a/tests/test_snap_other_layers_nonarray_js.py +++ b/tests/test_snap_other_layers_nonarray_js.py @@ -36,6 +36,28 @@ def test_compute_snap_tolerates_non_array_other_layers(): assert r["x"] == 10 and r["y"] == 10 and r["guides"] == [] +@pytest.mark.skipif(not _HAS_NODE, reason="node binary not on PATH") +def test_compute_snap_tolerates_missing_layer_or_context(): + js = f""" + import {{ computeSnap }} from '{_HELPER.as_posix()}'; + console.log(JSON.stringify([ + computeSnap(null, 10, 20, {{ zoom: 1, canvasW: 800, canvasH: 600 }}), + computeSnap({{ id: 'L1' }}, 11, 21, {{ zoom: 1, canvasW: 800, canvasH: 600 }}), + computeSnap({{ id: 'L1', canvas: {{ width: 100, height: 50 }} }}, 12, 22, null) + ])); + """ + proc = subprocess.run( + ["node", "--input-type=module"], + input=js, capture_output=True, text=True, cwd=str(_REPO), timeout=30, + ) + assert proc.returncode == 0, proc.stderr + assert json.loads(proc.stdout.strip()) == [ + {"x": 10, "y": 20, "guides": []}, + {"x": 11, "y": 21, "guides": []}, + {"x": 12, "y": 22, "guides": []}, + ] + + @pytest.mark.skipif(not _HAS_NODE, reason="node binary not on PATH") def test_compute_snap_still_snaps_to_a_layer_edge(): other = [{"id": "L2", "visible": True, "offset": {"x": 12, "y": 300},