|
1 | 1 | import os |
2 | 2 | import sys |
| 3 | +from unittest.mock import MagicMock |
| 4 | + |
3 | 5 | import pytest |
4 | 6 |
|
5 | 7 | import matplotlib.backends.backend_webagg_core |
| 8 | +from matplotlib.backends.backend_webagg_core import ( |
| 9 | + FigureCanvasWebAggCore, NavigationToolbar2WebAgg, |
| 10 | +) |
6 | 11 | from matplotlib.testing import subprocess_run_for_testing |
7 | 12 |
|
8 | 13 |
|
@@ -30,3 +35,46 @@ def test_webagg_fallback(backend): |
30 | 35 | def test_webagg_core_no_toolbar(): |
31 | 36 | fm = matplotlib.backends.backend_webagg_core.FigureManagerWebAgg |
32 | 37 | assert fm._toolbar2_class is None |
| 38 | + |
| 39 | + |
| 40 | +def test_toolbar_button_dispatch_allowlist(): |
| 41 | + """Only declared toolbar items should be dispatched.""" |
| 42 | + fig = MagicMock() |
| 43 | + canvas = FigureCanvasWebAggCore(fig) |
| 44 | + canvas.toolbar = MagicMock(spec=NavigationToolbar2WebAgg) |
| 45 | + canvas.toolbar.toolitems = NavigationToolbar2WebAgg.toolitems |
| 46 | + |
| 47 | + # Valid toolbar action should be dispatched. |
| 48 | + canvas.handle_toolbar_button({'name': 'home'}) |
| 49 | + canvas.toolbar.home.assert_called_once() |
| 50 | + |
| 51 | + # Invalid names should be silently ignored. |
| 52 | + canvas.toolbar.reset_mock() |
| 53 | + canvas.handle_toolbar_button({'name': '__init__'}) |
| 54 | + canvas.handle_toolbar_button({'name': 'not_a_real_button'}) |
| 55 | + # No methods should have been called. |
| 56 | + assert canvas.toolbar.method_calls == [] |
| 57 | + |
| 58 | + |
| 59 | +@pytest.mark.parametrize("host, origin, allowed", [ |
| 60 | + ("localhost:8988", "http://localhost:8988", True), |
| 61 | + ("localhost:8988", "http://evil.com", False), |
| 62 | + ("localhost:8988", "http://127.0.0.1:8988", False), |
| 63 | + ("localhost:8988", "http://[::1]:8988", False), |
| 64 | + ("127.0.0.1:8988", "http://127.0.0.1:8988", True), |
| 65 | + ("127.0.0.1:8988", "http://localhost:8988", False), |
| 66 | + ("127.0.0.1:8988", "http://[::1]:8988", False), |
| 67 | + ("[::1]:8988", "http://[::1]:8988", True), |
| 68 | + ("[::1]:8988", "http://[::2]:8988", False), |
| 69 | + ("[::1]:8988", "http://localhost:8988", False), |
| 70 | + ("[::1]:8988", "http://evil.com", False), |
| 71 | +]) |
| 72 | +def test_websocket_rejects_cross_origin(host, origin, allowed): |
| 73 | + """Verify Tornado's default check_origin rejects cross-origin requests.""" |
| 74 | + pytest.importorskip("tornado") |
| 75 | + from matplotlib.backends.backend_webagg import WebAggApplication |
| 76 | + |
| 77 | + ws = WebAggApplication.WebSocket.__new__(WebAggApplication.WebSocket) |
| 78 | + ws.request = MagicMock() |
| 79 | + ws.request.headers = {"Host": host} |
| 80 | + assert ws.check_origin(origin) is allowed |
0 commit comments