diff --git a/static/skin/viewer.js b/static/skin/viewer.js index ca49833fa..ea47e532f 100644 --- a/static/skin/viewer.js +++ b/static/skin/viewer.js @@ -54,6 +54,22 @@ function gotoRandomPage() { gotoUrl(`/random?content=${currentBook}`); } +// URI-encodes only the specified special symbols (note, however, that '%' is +// always considered a special symbol). +function quasiUriEncode(s, specialSymbols) { + if ( specialSymbols.match(/[A-Za-z0-9]/) ) { + throw "Alphanumeric symbols cannot be special"; + } + + // Set's guarantee of iterating in insertion order ensures that + // all %s in s will be encoded first. + for ( const c of new Set('%' + specialSymbols) ) { + s = s.replaceAll(c, encodeURIComponent(c)); + } + + return s; +} + function performSearch() { const searchbox = document.getElementById('kiwixsearchbox'); const q = encodeURIComponent(searchbox.value); @@ -386,8 +402,13 @@ function setupSuggestions() { const uriEncodedBookName = encodeURIComponent(currentBook); let url; if (data.value.kind == "path") { - const path = encodeURIComponent(htmlDecode(data.value.path)); - url = `/content/${uriEncodedBookName}/${path}`; + // The double quote and backslash symbols are included in the list + // of special symbols to URI-encode so that the resulting URL can + // be safely quoted inside a dynamically executed piece of + // Javascript code a few lines later. + const path = htmlDecode(data.value.path); + const quasiUriEncodedPath = quasiUriEncode(path, '#?"\\'); + url = `/content/${uriEncodedBookName}/${quasiUriEncodedPath}`; } else { const pattern = encodeURIComponent(htmlDecode(data.value.value)); url = `/search?content=${uriEncodedBookName}&pattern=${pattern}`; diff --git a/test/server.cpp b/test/server.cpp index 53ee47df1..4e2086704 100644 --- a/test/server.cpp +++ b/test/server.cpp @@ -73,7 +73,7 @@ const ResourceCollection resources200Compressible{ { DYNAMIC_CONTENT, "/ROOT%23%3F/skin/taskbar.css" }, { STATIC_CONTENT, "/ROOT%23%3F/skin/taskbar.css?cacheid=bbdaf425" }, { DYNAMIC_CONTENT, "/ROOT%23%3F/skin/viewer.js" }, - { STATIC_CONTENT, "/ROOT%23%3F/skin/viewer.js?cacheid=cb9b1f75" }, + { STATIC_CONTENT, "/ROOT%23%3F/skin/viewer.js?cacheid=bb748367" }, { DYNAMIC_CONTENT, "/ROOT%23%3F/skin/fonts/Poppins.ttf" }, { STATIC_CONTENT, "/ROOT%23%3F/skin/fonts/Poppins.ttf?cacheid=af705837" }, { DYNAMIC_CONTENT, "/ROOT%23%3F/skin/fonts/Roboto.ttf" }, @@ -312,7 +312,7 @@ R"EXPECTEDRESULT( - + const blankPageUrl = root + "/skin/blank.html?cacheid=6b1fa032";