Skip to content

Commit baf359e

Browse files
wlachjayaddisonAA-Turner
authored
Don't include HTML content in title search index (#13356)
Co-authored-by: James Addison <55152140+jayaddison@users.noreply.github.com> Co-authored-by: Adam Turner <9087854+AA-Turner@users.noreply.github.com>
1 parent b47fa78 commit baf359e

7 files changed

Lines changed: 35 additions & 10 deletions

File tree

CHANGES.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ Incompatible changes
2020
The :confval:`autodoc_use_legacy_class_based` option has been added to
2121
use the legacy (pre-9.0) implementation of autodoc.
2222
Patches by Adam Turner.
23+
* #13355: Don't include escaped title content in the search index.
24+
Patch by Will Lachance.
2325

2426
Deprecated
2527
----------

sphinx/builders/html/__init__.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -668,8 +668,9 @@ def write_doc(self, docname: str, doctree: nodes.document) -> None:
668668
def write_doc_serialized(self, docname: str, doctree: nodes.document) -> None:
669669
self.imgpath = relative_uri(self.get_target_uri(docname), self.imagedir)
670670
self.post_process_images(doctree)
671+
# get title as plain text
671672
title_node = self.env.longtitles.get(docname)
672-
title = self.render_partial(title_node)['title'] if title_node else ''
673+
title = title_node.astext() if title_node else ''
673674
self.index_page(docname, doctree, title)
674675

675676
def finish(self) -> None:

sphinx/themes/basic/static/searchtools.js

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,15 @@ const _removeChildren = (element) => {
5959
const _escapeRegExp = (string) =>
6060
string.replace(/[.*+\-?^${}()|[\]\\]/g, "\\$&"); // $& means the whole matched string
6161

62+
const _escapeHTML = (text) => {
63+
return text
64+
.replaceAll("&", "&amp;")
65+
.replaceAll("<", "&lt;")
66+
.replaceAll(">", "&gt;")
67+
.replaceAll('"', "&quot;")
68+
.replaceAll("'", "&apos;");
69+
};
70+
6271
const _displayItem = (item, searchTerms, highlightTerms) => {
6372
const docBuilder = DOCUMENTATION_OPTIONS.BUILDER;
6473
const docFileSuffix = DOCUMENTATION_OPTIONS.FILE_SUFFIX;
@@ -91,10 +100,10 @@ const _displayItem = (item, searchTerms, highlightTerms) => {
91100
let linkEl = listItem.appendChild(document.createElement("a"));
92101
linkEl.href = linkUrl + anchor;
93102
linkEl.dataset.score = score;
94-
linkEl.innerHTML = title;
103+
linkEl.innerHTML = _escapeHTML(title);
95104
if (descr) {
96105
listItem.appendChild(document.createElement("span")).innerHTML =
97-
" (" + descr + ")";
106+
` (${_escapeHTML(descr)})`;
98107
// highlight search terms in the description
99108
if (SPHINX_HIGHLIGHT_ENABLED)
100109
// SPHINX_HIGHLIGHT_ENABLED is set in sphinx_highlight.js

tests/js/fixtures/cpp/searchindex.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

tests/js/searchtools.spec.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ describe("Basic html theme search", function () {
3838
// prettier-ignore
3939
hits = [[
4040
"index",
41-
"&lt;no title&gt;",
41+
"<no title>",
4242
"",
4343
null,
4444
5,
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
`escaped` title with < and > in it
2+
==================================
3+
4+
this document has escaped content in the title but also the characters < and > in it

tests/test_search.py

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -154,8 +154,17 @@ def test_term_in_heading_and_section(app: SphinxTestApp) -> None:
154154
# if search term is in the title of one doc and in the text of another
155155
# both documents should be a hit in the search index as a title,
156156
# respectively text hit
157-
assert '"textinhead":2' in searchindex
158-
assert '"textinhead":0' in searchindex
157+
assert '"textinhead":3' in searchindex
158+
assert '"textinhead":1' in searchindex
159+
160+
161+
@pytest.mark.sphinx('html', testroot='search')
162+
def test_escaped_title(app: SphinxTestApp) -> None:
163+
app.build(force_all=True)
164+
searchindex = load_searchindex(app.outdir / 'searchindex.js')
165+
print(searchindex)
166+
assert 'escapedtitle' in searchindex['docnames']
167+
assert 'escaped title with < and > in it' in searchindex['titles']
159168

160169

161170
@pytest.mark.sphinx('html', testroot='search')
@@ -388,15 +397,15 @@ def test_search_index_gen_zh(app: SphinxTestApp) -> None:
388397
def test_nosearch(app: SphinxTestApp) -> None:
389398
app.build()
390399
index = load_searchindex(app.outdir / 'searchindex.js')
391-
assert index['docnames'] == ['index', 'nosearch', 'tocitem']
400+
assert index['docnames'] == ['escapedtitle', 'index', 'nosearch', 'tocitem']
392401
# latex is in 'nosearch.rst', and nowhere else
393402
assert 'latex' not in index['terms']
394403
# cat is in 'index.rst' but is marked with the 'no-search' class
395404
assert 'cat' not in index['terms']
396405
# bat is indexed from 'index.rst' and 'tocitem.rst' (document IDs 0, 2), and
397406
# not from 'nosearch.rst' (document ID 1)
398407
assert 'bat' in index['terms']
399-
assert index['terms']['bat'] == [0, 2]
408+
assert index['terms']['bat'] == [1, 3]
400409

401410

402411
@pytest.mark.sphinx(
@@ -408,7 +417,7 @@ def test_nosearch(app: SphinxTestApp) -> None:
408417
def test_parallel(app: SphinxTestApp) -> None:
409418
app.build()
410419
index = load_searchindex(app.outdir / 'searchindex.js')
411-
assert index['docnames'] == ['index', 'nosearch', 'tocitem']
420+
assert index['docnames'] == ['escapedtitle', 'index', 'nosearch', 'tocitem']
412421

413422

414423
@pytest.mark.sphinx('html', testroot='search')

0 commit comments

Comments
 (0)