From 30b3f05497704b95d044115a5c894ae3b953d89f Mon Sep 17 00:00:00 2001 From: Veloman Yunkan Date: Wed, 17 Jan 2024 18:35:52 +0400 Subject: [PATCH] All kiwix-serve errors are now frontend-translatable But the question is do we need all of them to be translatable in the frontend? Maybe only responses to /random, /content and /search endpoints (that are displayed in the viewer) should be translatable? Also, the test cases against vulnerabilities in kiwix-serve seem to suggest that KIWIX_RESPONSE_DATA should be HTML-encoded too. --- src/server/internalServer.cpp | 3 +- src/server/response.cpp | 14 ++++---- src/server/response.h | 6 ++-- test/server.cpp | 63 ++++++++++++++++++++++++++++++----- test/server_search.cpp | 5 ++- 5 files changed, 70 insertions(+), 21 deletions(-) diff --git a/src/server/internalServer.cpp b/src/server/internalServer.cpp index dcf6f170b..2c1ea518c 100644 --- a/src/server/internalServer.cpp +++ b/src/server/internalServer.cpp @@ -942,7 +942,8 @@ std::unique_ptr InternalServer::handle_search_request(const RequestCon HTTPErrorResponse response(request, MHD_HTTP_NOT_FOUND, "fulltext-search-unavailable", "404-page-heading", - cssUrl); + cssUrl, + /*includeKiwixResponseData=*/true); response += nonParameterizedMessage("no-search-results"); // XXX: Now this has to be handled by the iframe-based viewer which // XXX: has to resolve if the book selection resulted in a single book. diff --git a/src/server/response.cpp b/src/server/response.cpp index 8e4d9c71d..8ccf8c52a 100644 --- a/src/server/response.cpp +++ b/src/server/response.cpp @@ -381,20 +381,18 @@ HTTPErrorResponse::HTTPErrorResponse(const RequestContext& request, }); } -HTTP404Response::HTTP404Response(const RequestContext& request, - bool includeKiwixResponseData) +HTTP404Response::HTTP404Response(const RequestContext& request) : HTTPErrorResponse(request, MHD_HTTP_NOT_FOUND, "404-page-title", "404-page-heading", std::string(), - includeKiwixResponseData) + /*includeKiwixResponseData=*/true) { } -UrlNotFoundResponse::UrlNotFoundResponse(const RequestContext& request, - bool includeKiwixResponseData) - : HTTP404Response(request, includeKiwixResponseData) +UrlNotFoundResponse::UrlNotFoundResponse(const RequestContext& request) + : HTTP404Response(request) { const std::string requestUrl = urlDecode(m_request.get_full_url(), false); *this += ParameterizedMessage("url-not-found", {{"url", requestUrl}}); @@ -417,7 +415,9 @@ HTTP400Response::HTTP400Response(const RequestContext& request) : HTTPErrorResponse(request, MHD_HTTP_BAD_REQUEST, "400-page-title", - "400-page-heading") + "400-page-heading", + std::string(), + /*includeKiwixResponseData=*/true) { std::string requestUrl = urlDecode(m_request.get_full_url(), false); const auto query = m_request.get_query(); diff --git a/src/server/response.h b/src/server/response.h index a695efa84..11808f0da 100644 --- a/src/server/response.h +++ b/src/server/response.h @@ -160,14 +160,12 @@ struct HTTPErrorResponse : ContentResponseBlueprint struct HTTP404Response : HTTPErrorResponse { - explicit HTTP404Response(const RequestContext& request, - bool includeKiwixResponseData = false); + explicit HTTP404Response(const RequestContext& request); }; struct UrlNotFoundResponse : HTTP404Response { - explicit UrlNotFoundResponse(const RequestContext& request, - bool includeKiwixResponseData = false); + explicit UrlNotFoundResponse(const RequestContext& request); }; struct HTTP400Response : HTTPErrorResponse diff --git a/test/server.cpp b/test/server.cpp index fa57aee4e..cf7e0a46f 100644 --- a/test/server.cpp +++ b/test/server.cpp @@ -337,6 +337,7 @@ R"EXPECTEDRESULT( + window.KIWIX_RESPONSE_DATA = { "CSS_URL" : "/ROOT%23%3F/skin/search_results.css?cacheid=76d39c84", "PAGE_HEADING" : { "msgid" : "404-page-heading", "params" : { } }, "PAGE_TITLE" : { "msgid" : "fulltext-search-unavailable", "params" : { } }, "details" : [ { "p" : { "msgid" : "no-search-results", "params" : { } } } ] }; )EXPECTEDRESULT" }, }; @@ -535,6 +536,7 @@ struct ExpectedResponseData { const std::string expectedPageTitle; const std::string expectedCssUrl; + const std::string expectedKiwixResponseData; const std::string bookName; const std::string bookTitle; const std::string expectedBody; @@ -544,6 +546,7 @@ enum ExpectedResponseDataType { expected_page_title, expected_css_url, + expected_kiwix_response_data, book_name, book_title, expected_body @@ -556,11 +559,13 @@ ExpectedResponseData operator==(ExpectedResponseDataType t, std::string s) { switch (t) { - case expected_page_title: return ExpectedResponseData{s, "", "", "", ""}; - case expected_css_url: return ExpectedResponseData{"", s, "", "", ""}; - case book_name: return ExpectedResponseData{"", "", s, "", ""}; - case book_title: return ExpectedResponseData{"", "", "", s, ""}; - case expected_body: return ExpectedResponseData{"", "", "", "", s}; + case expected_page_title: return ExpectedResponseData{s, "", "", "", "", ""}; + case expected_css_url: return ExpectedResponseData{"", s, "", "", "", ""}; + case expected_kiwix_response_data: + return ExpectedResponseData{"", "", s, "", "", ""}; + case book_name: return ExpectedResponseData{"", "", "", s, "", ""}; + case book_title: return ExpectedResponseData{"", "", "", "", s, ""}; + case expected_body: return ExpectedResponseData{"", "", "", "", "", s}; default: assert(false); return ExpectedResponseData{}; } } @@ -579,6 +584,7 @@ ExpectedResponseData operator&&(const ExpectedResponseData& a, return ExpectedResponseData{ selectNonEmpty(a.expectedPageTitle, b.expectedPageTitle), selectNonEmpty(a.expectedCssUrl, b.expectedCssUrl), + selectNonEmpty(a.expectedKiwixResponseData, b.expectedKiwixResponseData), selectNonEmpty(a.bookName, b.bookName), selectNonEmpty(a.bookTitle, b.bookTitle), selectNonEmpty(a.expectedBody, b.expectedBody) @@ -607,19 +613,29 @@ private: std::string TestContentIn404HtmlResponse::expectedResponse() const { const std::string frag[] = { + // frag[0] R"FRAG( )FRAG", + // frag[1] R"FRAG( )FRAG", - R"FRAG( + // frag[2] + R"( )FRAG", + // frag[4] R"FRAG( )FRAG" @@ -630,8 +646,10 @@ std::string TestContentIn404HtmlResponse::expectedResponse() const + frag[1] + pageCssLink() + frag[2] + + expectedKiwixResponseData + + frag[3] + expectedBody - + frag[3]; + + frag[4]; } std::string TestContentIn404HtmlResponse::pageTitle() const @@ -648,7 +666,8 @@ std::string TestContentIn404HtmlResponse::pageCssLink() const return R"( )"; + + R"(" rel="Stylesheet" />)" + + "\n"; } class TestContentIn400HtmlResponse : public TestContentIn404HtmlResponse @@ -676,6 +695,7 @@ TEST_F(ServerTest, Http404HtmlError) using namespace TestingOfHtmlResponses; const std::vector testData{ { /* url */ "/ROOT%23%3F/random?content=non-existent-book", + expected_kiwix_response_data==R"({ "CSS_URL" : false, "PAGE_HEADING" : { "msgid" : "404-page-heading", "params" : { } }, "PAGE_TITLE" : { "msgid" : "404-page-title", "params" : { } }, "details" : [ { "p" : { "msgid" : "no-such-book", "params" : { "BOOK_NAME" : "non-existent-book" } } } ] })" && expected_body==R"(

Not Found

@@ -685,6 +705,7 @@ TEST_F(ServerTest, Http404HtmlError) { /* url */ "/ROOT%23%3F/random?content=non-existent-book&userlang=test", expected_page_title=="[I18N TESTING] Not Found - Try Again" && + expected_kiwix_response_data==R"({ "CSS_URL" : false, "PAGE_HEADING" : { "msgid" : "404-page-heading", "params" : { } }, "PAGE_TITLE" : { "msgid" : "404-page-title", "params" : { } }, "details" : [ { "p" : { "msgid" : "no-such-book", "params" : { "BOOK_NAME" : "non-existent-book" } } } ] })" && expected_body==R"(

[I18N TESTING] Content not found, but at least the server is alive

@@ -693,6 +714,7 @@ TEST_F(ServerTest, Http404HtmlError) )" }, { /* url */ "/ROOT%23%3F/suggest?content=no-such-book&term=whatever", + expected_kiwix_response_data==R"({ "CSS_URL" : false, "PAGE_HEADING" : { "msgid" : "404-page-heading", "params" : { } }, "PAGE_TITLE" : { "msgid" : "404-page-title", "params" : { } }, "details" : [ { "p" : { "msgid" : "no-such-book", "params" : { "BOOK_NAME" : "no-such-book" } } } ] })" && expected_body==R"(

Not Found

@@ -701,6 +723,7 @@ TEST_F(ServerTest, Http404HtmlError) )" }, { /* url */ "/ROOT%23%3F/catalog/", + expected_kiwix_response_data==R"({ "CSS_URL" : false, "PAGE_HEADING" : { "msgid" : "404-page-heading", "params" : { } }, "PAGE_TITLE" : { "msgid" : "404-page-title", "params" : { } }, "details" : [ { "p" : { "msgid" : "url-not-found", "params" : { "url" : "/ROOT%23%3F/catalog/" } } } ] })" && expected_body==R"(

Not Found

@@ -710,6 +733,7 @@ TEST_F(ServerTest, Http404HtmlError) { /* url */ "/ROOT%23%3F/catalog/?userlang=test", expected_page_title=="[I18N TESTING] Not Found - Try Again" && + expected_kiwix_response_data==R"({ "CSS_URL" : false, "PAGE_HEADING" : { "msgid" : "404-page-heading", "params" : { } }, "PAGE_TITLE" : { "msgid" : "404-page-title", "params" : { } }, "details" : [ { "p" : { "msgid" : "url-not-found", "params" : { "url" : "/ROOT%23%3F/catalog/" } } } ] })" && expected_body==R"(

[I18N TESTING] Content not found, but at least the server is alive

@@ -718,6 +742,7 @@ TEST_F(ServerTest, Http404HtmlError) )" }, { /* url */ "/ROOT%23%3F/catalog/invalid_endpoint", + expected_kiwix_response_data==R"({ "CSS_URL" : false, "PAGE_HEADING" : { "msgid" : "404-page-heading", "params" : { } }, "PAGE_TITLE" : { "msgid" : "404-page-title", "params" : { } }, "details" : [ { "p" : { "msgid" : "url-not-found", "params" : { "url" : "/ROOT%23%3F/catalog/invalid_endpoint" } } } ] })" && expected_body==R"(

Not Found

@@ -727,6 +752,7 @@ TEST_F(ServerTest, Http404HtmlError) { /* url */ "/ROOT%23%3F/catalog/invalid_endpoint?userlang=test", expected_page_title=="[I18N TESTING] Not Found - Try Again" && + expected_kiwix_response_data==R"({ "CSS_URL" : false, "PAGE_HEADING" : { "msgid" : "404-page-heading", "params" : { } }, "PAGE_TITLE" : { "msgid" : "404-page-title", "params" : { } }, "details" : [ { "p" : { "msgid" : "url-not-found", "params" : { "url" : "/ROOT%23%3F/catalog/invalid_endpoint" } } } ] })" && expected_body==R"(

[I18N TESTING] Content not found, but at least the server is alive

@@ -735,6 +761,7 @@ TEST_F(ServerTest, Http404HtmlError) )" }, { /* url */ "/ROOT%23%3F/content/invalid-book/whatever", + expected_kiwix_response_data==R"({ "CSS_URL" : false, "PAGE_HEADING" : { "msgid" : "404-page-heading", "params" : { } }, "PAGE_TITLE" : { "msgid" : "404-page-title", "params" : { } }, "details" : [ { "p" : { "msgid" : "url-not-found", "params" : { "url" : "/ROOT%23%3F/content/invalid-book/whatever" } } }, { "p" : { "msgid" : "suggest-search", "params" : { "PATTERN" : "whatever", "SEARCH_URL" : "/ROOT%23%3F/search?pattern=whatever" } } } ] })" && expected_body==R"(

Not Found

@@ -748,6 +775,7 @@ TEST_F(ServerTest, Http404HtmlError) { /* url */ "/ROOT%23%3F/content/zimfile/invalid-article", book_name=="zimfile" && book_title=="Ray Charles" && + expected_kiwix_response_data==R"({ "CSS_URL" : false, "PAGE_HEADING" : { "msgid" : "404-page-heading", "params" : { } }, "PAGE_TITLE" : { "msgid" : "404-page-title", "params" : { } }, "details" : [ { "p" : { "msgid" : "url-not-found", "params" : { "url" : "/ROOT%23%3F/content/zimfile/invalid-article" } } }, { "p" : { "msgid" : "suggest-search", "params" : { "PATTERN" : "invalid-article", "SEARCH_URL" : "/ROOT%23%3F/search?content=zimfile&pattern=invalid-article" } } } ] })" && expected_body==R"(

Not Found

@@ -759,6 +787,9 @@ TEST_F(ServerTest, Http404HtmlError) )" }, { /* url */ R"(/ROOT%23%3F/content/">)", + // XXX: This test case suggests that KIWIX_RESPONSE_DATA + // XXX: must be HTML-encoded, too + expected_kiwix_response_data==R"({ "CSS_URL" : false, "PAGE_HEADING" : { "msgid" : "404-page-heading", "params" : { } }, "PAGE_TITLE" : { "msgid" : "404-page-title", "params" : { } }, "details" : [ { "p" : { "msgid" : "url-not-found", "params" : { "url" : "/ROOT%23%3F/content/\">" } } }, { "p" : { "msgid" : "suggest-search", "params" : { "PATTERN" : "\">", "SEARCH_URL" : "/ROOT%23%3F/search?pattern=%22%3E%3Csvg%20onload%3Dalert(1)%3E" } } } ] })" && expected_body==R"(

Not Found

@@ -772,6 +803,9 @@ TEST_F(ServerTest, Http404HtmlError) { /* url */ R"(/ROOT%23%3F/content/zimfile/">)", book_name=="zimfile" && book_title=="Ray Charles" && + // XXX: This test case suggests that KIWIX_RESPONSE_DATA + // XXX: must be HTML-encoded, too + expected_kiwix_response_data==R"({ "CSS_URL" : false, "PAGE_HEADING" : { "msgid" : "404-page-heading", "params" : { } }, "PAGE_TITLE" : { "msgid" : "404-page-title", "params" : { } }, "details" : [ { "p" : { "msgid" : "url-not-found", "params" : { "url" : "/ROOT%23%3F/content/zimfile/\">" } } }, { "p" : { "msgid" : "suggest-search", "params" : { "PATTERN" : "\">", "SEARCH_URL" : "/ROOT%23%3F/search?content=zimfile&pattern=%22%3E%3Csvg%20onload%3Dalert(1)%3E" } } } ] })" && expected_body==R"(

Not Found

@@ -786,6 +820,7 @@ TEST_F(ServerTest, Http404HtmlError) expected_page_title=="[I18N TESTING] Not Found - Try Again" && book_name=="zimfile" && book_title=="Ray Charles" && + expected_kiwix_response_data==R"({ "CSS_URL" : false, "PAGE_HEADING" : { "msgid" : "404-page-heading", "params" : { } }, "PAGE_TITLE" : { "msgid" : "404-page-title", "params" : { } }, "details" : [ { "p" : { "msgid" : "url-not-found", "params" : { "url" : "/ROOT%23%3F/content/zimfile/invalid-article" } } }, { "p" : { "msgid" : "suggest-search", "params" : { "PATTERN" : "invalid-article", "SEARCH_URL" : "/ROOT%23%3F/search?content=zimfile&pattern=invalid-article" } } } ] })" && expected_body==R"(

[I18N TESTING] Content not found, but at least the server is alive

@@ -797,6 +832,7 @@ TEST_F(ServerTest, Http404HtmlError) )" }, { /* url */ "/ROOT%23%3F/raw/no-such-book/meta/Title", + expected_kiwix_response_data==R"({ "CSS_URL" : false, "PAGE_HEADING" : { "msgid" : "404-page-heading", "params" : { } }, "PAGE_TITLE" : { "msgid" : "404-page-title", "params" : { } }, "details" : [ { "p" : { "msgid" : "url-not-found", "params" : { "url" : "/ROOT%23%3F/raw/no-such-book/meta/Title" } } }, { "p" : { "msgid" : "no-such-book", "params" : { "BOOK_NAME" : "no-such-book" } } } ] })" && expected_body==R"(

Not Found

@@ -808,6 +844,7 @@ TEST_F(ServerTest, Http404HtmlError) )" }, { /* url */ "/ROOT%23%3F/raw/zimfile/XYZ", + expected_kiwix_response_data==R"({ "CSS_URL" : false, "PAGE_HEADING" : { "msgid" : "404-page-heading", "params" : { } }, "PAGE_TITLE" : { "msgid" : "404-page-title", "params" : { } }, "details" : [ { "p" : { "msgid" : "url-not-found", "params" : { "url" : "/ROOT%23%3F/raw/zimfile/XYZ" } } }, { "p" : { "msgid" : "invalid-raw-data-type", "params" : { "DATATYPE" : "XYZ" } } } ] })" && expected_body==R"(

Not Found

@@ -819,6 +856,7 @@ TEST_F(ServerTest, Http404HtmlError) )" }, { /* url */ "/ROOT%23%3F/raw/zimfile/meta/invalid-metadata", + expected_kiwix_response_data==R"({ "CSS_URL" : false, "PAGE_HEADING" : { "msgid" : "404-page-heading", "params" : { } }, "PAGE_TITLE" : { "msgid" : "404-page-title", "params" : { } }, "details" : [ { "p" : { "msgid" : "url-not-found", "params" : { "url" : "/ROOT%23%3F/raw/zimfile/meta/invalid-metadata" } } }, { "p" : { "msgid" : "raw-entry-not-found", "params" : { "DATATYPE" : "meta", "ENTRY" : "invalid-metadata" } } } ] })" && expected_body==R"(

Not Found

@@ -830,6 +868,7 @@ TEST_F(ServerTest, Http404HtmlError) )" }, { /* url */ "/ROOT%23%3F/raw/zimfile/content/invalid-article", + expected_kiwix_response_data==R"({ "CSS_URL" : false, "PAGE_HEADING" : { "msgid" : "404-page-heading", "params" : { } }, "PAGE_TITLE" : { "msgid" : "404-page-title", "params" : { } }, "details" : [ { "p" : { "msgid" : "url-not-found", "params" : { "url" : "/ROOT%23%3F/raw/zimfile/content/invalid-article" } } }, { "p" : { "msgid" : "raw-entry-not-found", "params" : { "DATATYPE" : "content", "ENTRY" : "invalid-article" } } } ] })" && expected_body==R"(

Not Found

@@ -845,6 +884,7 @@ TEST_F(ServerTest, Http404HtmlError) expected_css_url=="/ROOT%23%3F/skin/search_results.css?cacheid=76d39c84" && book_name=="poor" && book_title=="poor" && + expected_kiwix_response_data==R"({ "CSS_URL" : "/ROOT%23%3F/skin/search_results.css?cacheid=76d39c84", "PAGE_HEADING" : { "msgid" : "404-page-heading", "params" : { } }, "PAGE_TITLE" : { "msgid" : "fulltext-search-unavailable", "params" : { } }, "details" : [ { "p" : { "msgid" : "no-search-results", "params" : { } } } ] })" && expected_body==R"(

Not Found

@@ -866,6 +906,7 @@ TEST_F(ServerTest, Http400HtmlError) using namespace TestingOfHtmlResponses; const std::vector testData{ { /* url */ "/ROOT%23%3F/search", + expected_kiwix_response_data==R"({ "CSS_URL" : false, "PAGE_HEADING" : { "msgid" : "400-page-heading", "params" : { } }, "PAGE_TITLE" : { "msgid" : "400-page-title", "params" : { } }, "details" : [ { "p" : { "msgid" : "invalid-request", "params" : { "url" : "/ROOT%23%3F/search" } } }, { "p" : { "msgid" : "too-many-books", "params" : { "LIMIT" : "3", "NB_BOOKS" : "4" } } } ] })" && expected_body== R"(

Invalid request

@@ -876,6 +917,7 @@ TEST_F(ServerTest, Http400HtmlError)

)" }, { /* url */ "/ROOT%23%3F/search?content=zimfile", + expected_kiwix_response_data==R"({ "CSS_URL" : false, "PAGE_HEADING" : { "msgid" : "400-page-heading", "params" : { } }, "PAGE_TITLE" : { "msgid" : "400-page-title", "params" : { } }, "details" : [ { "p" : { "msgid" : "invalid-request", "params" : { "url" : "/ROOT%23%3F/search?content=zimfile" } } }, { "p" : { "msgid" : "no-query", "params" : { } } } ] })" && expected_body==R"(

Invalid request

@@ -886,6 +928,7 @@ TEST_F(ServerTest, Http400HtmlError)

)" }, { /* url */ "/ROOT%23%3F/search?content=non-existing-book&pattern=asdfqwerty", + expected_kiwix_response_data==R"({ "CSS_URL" : false, "PAGE_HEADING" : { "msgid" : "400-page-heading", "params" : { } }, "PAGE_TITLE" : { "msgid" : "400-page-title", "params" : { } }, "details" : [ { "p" : { "msgid" : "invalid-request", "params" : { "url" : "/ROOT%23%3F/search?content=non-existing-book&pattern=asdfqwerty" } } }, { "p" : { "msgid" : "no-such-book", "params" : { "BOOK_NAME" : "non-existing-book" } } } ] })" && expected_body==R"(

Invalid request

@@ -896,6 +939,7 @@ TEST_F(ServerTest, Http400HtmlError)

)" }, { /* url */ "/ROOT%23%3F/search?content=non-existing-book&pattern=a\"

Invalid request