Skip to content

Commit 60afeb0

Browse files
committed
fix: patch website search suggestion vulnerability
1 parent ec48009 commit 60afeb0

2 files changed

Lines changed: 33 additions & 1 deletion

File tree

‎requirements-site.txt‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
# Website tooling only; independent of the student Binder environment.
22
mkdocs==1.6.1
3-
mkdocs-material==9.7.6
3+
mkdocs-material==9.7.7

‎scripts/test_site_browser.py‎

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,43 @@
44
import threading
55
import tempfile
66
from pathlib import Path
7+
from urllib.parse import quote
78

89
from playwright.sync_api import sync_playwright
910

1011
ROOT = Path(__file__).resolve().parents[1]
1112

1213

14+
def check_search_safety(page, base):
15+
"""Exercise URL queries through the real search worker and suggestion DOM."""
16+
for query in (
17+
'pyth',
18+
'-<b>probe</b> pyth',
19+
'-<img/src=x/onerror=window.__searchProbe=1> pyth',
20+
'-&lt;b&gt;probe&lt;/b&gt; pyth',
21+
):
22+
print('Checking suggestion:', query, flush=True)
23+
page.goto(base + '?q=' + quote(query, safe=''))
24+
search = page.get_by_role('textbox', name='Search')
25+
page.wait_for_function('!document.querySelector(".md-search__input").disabled')
26+
search.focus()
27+
search.press('End')
28+
search.press('Backspace')
29+
search.press('h')
30+
# A nonempty suggestion proves the vulnerable renderer was reached.
31+
page.wait_for_function('document.querySelector("[data-md-component=search-suggest]").textContent.length > 0', timeout=30000)
32+
suggestion = page.locator('[data-md-component="search-suggest"]')
33+
text = suggestion.text_content()
34+
assert text.startswith(query[:-4]), (query, text)
35+
assert suggestion.locator('*').count() == 0, query
36+
assert page.evaluate('window.__searchProbe') is None, query
37+
if query == 'pyth':
38+
assert text.startswith('python'), text
39+
search.press('ArrowRight')
40+
assert search.input_value() == text
41+
print('PASS: search suggestions render URL input as text, including markup and encoded entities; normal completion works.')
42+
43+
1344
class Handler(http.server.SimpleHTTPRequestHandler):
1445
def do_GET(self):
1546
if self.path.startswith('/python_foundation/'):
@@ -32,6 +63,7 @@ def check():
3263
page.set_default_timeout(10000)
3364
errors = []
3465
page.on('pageerror', lambda error: errors.append(str(error)))
66+
check_search_safety(page, base)
3567
page.goto(base)
3668
assert page.title() == 'Introduction to Python: A Guided Beginner Course'
3769
assert page.locator('.course-chip').inner_text() == 'Introduction to Python: A Guided Beginner Course'

0 commit comments

Comments
 (0)