Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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
7 changes: 5 additions & 2 deletions lib/matplotlib/backends/backend_webagg_core.py
Original file line number Diff line number Diff line change
Expand Up @@ -349,8 +349,11 @@ def _handle_key(self, event):
handle_key_press = handle_key_release = _handle_key

def handle_toolbar_button(self, event):
# TODO: Be more suspicious of the input
getattr(self.toolbar, event['name'])()
name = event['name']
allowed = {item[3] for item in self.toolbar.toolitems if item[3] is not None}
if name not in allowed:
return
getattr(self.toolbar, name)()
Comment thread
scottshambaugh marked this conversation as resolved.
Outdated

def handle_refresh(self, event):
if self.manager:
Expand Down
47 changes: 47 additions & 0 deletions lib/matplotlib/tests/test_backend_webagg.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
import os
import sys
from unittest.mock import MagicMock
import pytest
Comment thread
scottshambaugh marked this conversation as resolved.

import matplotlib.backends.backend_webagg_core
from matplotlib.backends.backend_webagg_core import (
FigureCanvasWebAggCore, NavigationToolbar2WebAgg,
)
from matplotlib.testing import subprocess_run_for_testing


Expand Down Expand Up @@ -30,3 +34,46 @@ def test_webagg_fallback(backend):
def test_webagg_core_no_toolbar():
fm = matplotlib.backends.backend_webagg_core.FigureManagerWebAgg
assert fm._toolbar2_class is None


def test_toolbar_button_dispatch_allowlist():
"""Only declared toolbar items should be dispatched."""
fig = MagicMock()
canvas = FigureCanvasWebAggCore(fig)
canvas.toolbar = MagicMock(spec=NavigationToolbar2WebAgg)
canvas.toolbar.toolitems = NavigationToolbar2WebAgg.toolitems

# Valid toolbar action should be dispatched.
canvas.handle_toolbar_button({'name': 'home'})
canvas.toolbar.home.assert_called_once()

# Invalid names should be silently ignored.
canvas.toolbar.reset_mock()
canvas.handle_toolbar_button({'name': '__init__'})
canvas.handle_toolbar_button({'name': 'not_a_real_button'})
# No methods should have been called.
assert canvas.toolbar.method_calls == []


@pytest.mark.parametrize("host, origin, allowed", [
("localhost:8988", "http://localhost:8988", True),
("localhost:8988", "http://evil.com", False),
("localhost:8988", "http://127.0.0.1:8988", False),
("localhost:8988", "http://[::1]:8988", False),
("127.0.0.1:8988", "http://127.0.0.1:8988", True),
("127.0.0.1:8988", "http://localhost:8988", False),
("127.0.0.1:8988", "http://[::1]:8988", False),
("[::1]:8988", "http://[::1]:8988", True),
("[::1]:8988", "http://[::2]:8988", False),
("[::1]:8988", "http://localhost:8988", False),
("[::1]:8988", "http://evil.com", False),
])
def test_websocket_rejects_cross_origin(host, origin, allowed):
"""Verify Tornado's default check_origin rejects cross-origin requests."""
pytest.importorskip("tornado")
from matplotlib.backends.backend_webagg import WebAggApplication

ws = WebAggApplication.WebSocket.__new__(WebAggApplication.WebSocket)
ws.request = MagicMock()
ws.request.headers = {"Host": host}
assert ws.check_origin(origin) is allowed
Loading