summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMingli Yu <mingli.yu@windriver.com>2022-03-29 14:13:44 +0800
committerRichard Purdie <richard.purdie@linuxfoundation.org>2022-04-03 20:49:03 +0100
commit9fc229578cc213be4cbb8bcebed653bd03cda244 (patch)
treec7837e90876b40bc78ec8cde207d3507fb3e5843
parent6e1ca0e922e7aee3eb945c0f04a2ace1c8b36100 (diff)
downloadpoky-9fc229578cc213be4cbb8bcebed653bd03cda244.tar.gz
epiphany: fix CVEs
Backport patch [1] to fix below CVEs: - CVE-2021-45085 - CVE-2021-45086 - CVE-2021-45087 - CVE-2021-45088 [1] https://sources.debian.org/data/main/e/epiphany-browser/3.38.2-1+deb11u2/debian/patches/encode-untrusted-data.patch (From OE-Core rev: 125c6f5770542c3b509336b92d6c45c0c955027e) Signed-off-by: Mingli Yu <mingli.yu@windriver.com> Signed-off-by: Anuj Mittal <anuj.mittal@intel.com> Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
-rw-r--r--meta/recipes-gnome/epiphany/epiphany_3.38.2.bb1
-rw-r--r--meta/recipes-gnome/epiphany/files/encode-untrusted-data.patch707
2 files changed, 708 insertions, 0 deletions
diff --git a/meta/recipes-gnome/epiphany/epiphany_3.38.2.bb b/meta/recipes-gnome/epiphany/epiphany_3.38.2.bb
index 04f340f133..72d116da69 100644
--- a/meta/recipes-gnome/epiphany/epiphany_3.38.2.bb
+++ b/meta/recipes-gnome/epiphany/epiphany_3.38.2.bb
@@ -18,6 +18,7 @@ SRC_URI = "${GNOME_MIRROR}/${GNOMEBN}/${@gnome_verdir("${PV}")}/${GNOMEBN}-${PV}
18 file://0002-help-meson.build-disable-the-use-of-yelp.patch \ 18 file://0002-help-meson.build-disable-the-use-of-yelp.patch \
19 file://migrator.patch \ 19 file://migrator.patch \
20 file://distributor.patch \ 20 file://distributor.patch \
21 file://encode-untrusted-data.patch \
21 " 22 "
22SRC_URI[archive.sha256sum] = "8b05f2bcc1e80ecf4a10f6f01b3285087eb4cbdf5741dffb8c0355715ef5116d" 23SRC_URI[archive.sha256sum] = "8b05f2bcc1e80ecf4a10f6f01b3285087eb4cbdf5741dffb8c0355715ef5116d"
23 24
diff --git a/meta/recipes-gnome/epiphany/files/encode-untrusted-data.patch b/meta/recipes-gnome/epiphany/files/encode-untrusted-data.patch
new file mode 100644
index 0000000000..4805ee4e6b
--- /dev/null
+++ b/meta/recipes-gnome/epiphany/files/encode-untrusted-data.patch
@@ -0,0 +1,707 @@
1From: Michael Catanzaro <mcatanzaro@redhat.com>
2Subject: Properly encode untrusted data when injecting into trusted pages
3
4CVE: CVE-2021-45085 CVE-2021-45086 CVE-2021-45087 CVE-2021-45088
5
6Upstream-Status: Backport [https://gitlab.gnome.org/GNOME/epiphany/-/compare/c27a8180e12e6ec92292dcf53b9243815ad9aa2e...abac58c5191b7d653fbefa8d44e5c2bd4d002825?from_project_id=1906]
7
8Signed-off-by: Mingli Yu <mingli.yu@windriver.com>
9Index: epiphany-browser/embed/ephy-about-handler.c
10===================================================================
11--- epiphany-browser.orig/embed/ephy-about-handler.c
12+++ epiphany-browser/embed/ephy-about-handler.c
13@@ -27,6 +27,7 @@
14 #include "ephy-file-helpers.h"
15 #include "ephy-flatpak-utils.h"
16 #include "ephy-history-service.h"
17+#include "ephy-output-encoding.h"
18 #include "ephy-prefs.h"
19 #include "ephy-settings.h"
20 #include "ephy-smaps.h"
21@@ -263,16 +264,34 @@ handle_applications_finished_cb (EphyAbo
22
23 for (p = applications; p; p = p->next) {
24 EphyWebApplication *app = (EphyWebApplication *)p->data;
25-
26+ g_autofree char *html_encoded_id = NULL;
27+ g_autofree char *encoded_icon_url = NULL;
28+ g_autofree char *encoded_name = NULL;
29+ g_autofree char *encoded_url = NULL;
30+ g_autofree char *js_encoded_id = NULL;
31+ g_autofree char *encoded_install_date = NULL;
32+
33+ /* Most of these fields are untrusted. The web app suggests its own title,
34+ * which gets used in the app ID and icon URL. The main URL could contain
35+ * anything. Install date is the only trusted field here in that it's
36+ * constructed by Epiphany, but it's a freeform string and we're encoding
37+ * everything else here anyway, so might as well encode this too.
38+ */
39+ html_encoded_id = ephy_encode_for_html_attribute (app->id);
40+ encoded_icon_url = ephy_encode_for_html_attribute (app->icon_url);
41+ encoded_name = ephy_encode_for_html_entity (app->name);
42+ encoded_url = ephy_encode_for_html_entity (app->url);
43+ js_encoded_id = ephy_encode_for_javascript (app->id);
44+ encoded_install_date = ephy_encode_for_html_entity (app->install_date);
45 g_string_append_printf (data_str,
46 "<tbody><tr id =\"%s\">"
47 "<td class=\"icon\"><img width=64 height=64 src=\"file://%s\"></img></td>"
48 "<td class=\"data\"><div class=\"appname\">%s</div><div class=\"appurl\">%s</div></td>"
49 "<td class=\"input\"><input type=\"button\" value=\"%s\" onclick=\"deleteWebApp('%s');\"></td>"
50 "<td class=\"date\">%s <br /> %s</td></tr></tbody>",
51- app->id, app->icon_url, app->name, app->url, _("Delete"), app->id,
52+ html_encoded_id, encoded_icon_url, encoded_name, encoded_url, _("Delete"), js_encoded_id,
53 /* Note for translators: this refers to the installation date. */
54- _("Installed on:"), app->install_date);
55+ _("Installed on:"), encoded_install_date);
56 }
57
58 g_string_append (data_str, "</table></div></body></html>");
59@@ -407,7 +426,9 @@ history_service_query_urls_cb (EphyHisto
60 EphyHistoryURL *url = (EphyHistoryURL *)l->data;
61 const char *snapshot;
62 g_autofree char *thumbnail_style = NULL;
63- g_autofree char *markup = NULL;
64+ g_autofree char *entity_encoded_title = NULL;
65+ g_autofree char *attribute_encoded_title = NULL;
66+ g_autofree char *encoded_url = NULL;
67
68 snapshot = ephy_snapshot_service_lookup_cached_snapshot_path (snapshot_service, url->url);
69 if (snapshot)
70@@ -415,15 +436,19 @@ history_service_query_urls_cb (EphyHisto
71 else
72 ephy_embed_shell_schedule_thumbnail_update (shell, url);
73
74- markup = g_markup_escape_text (url->title, -1);
75+ /* Title and URL are controlled by web content and could be malicious. */
76+ entity_encoded_title = ephy_encode_for_html_entity (url->title);
77+ attribute_encoded_title = ephy_encode_for_html_attribute (url->title);
78+ encoded_url = ephy_encode_for_html_attribute (url->url);
79 g_string_append_printf (data_str,
80 "<a class=\"overview-item\" title=\"%s\" href=\"%s\">"
81 " <div class=\"overview-close-button\" title=\"%s\"></div>"
82 " <span class=\"overview-thumbnail\"%s></span>"
83 " <span class=\"overview-title\">%s</span>"
84 "</a>",
85- markup, url->url, _("Remove from overview"),
86- thumbnail_style ? thumbnail_style : "", url->title);
87+ attribute_encoded_title, encoded_url, _("Remove from overview"),
88+ thumbnail_style ? thumbnail_style : "",
89+ entity_encoded_title);
90 }
91
92 data_str = g_string_append (data_str,
93Index: epiphany-browser/embed/ephy-pdf-handler.c
94===================================================================
95--- epiphany-browser.orig/embed/ephy-pdf-handler.c
96+++ epiphany-browser/embed/ephy-pdf-handler.c
97@@ -23,6 +23,7 @@
98
99 #include "ephy-embed-container.h"
100 #include "ephy-embed-shell.h"
101+#include "ephy-output-encoding.h"
102 #include "ephy-web-view.h"
103
104 #include <gio/gio.h>
105@@ -124,8 +125,9 @@ pdf_file_loaded (GObject *source,
106 GBytes *html_file;
107 g_autoptr (GError) error = NULL;
108 g_autoptr (GString) html = NULL;
109- g_autofree gchar *b64 = NULL;
110 g_autofree char *file_data = NULL;
111+ g_autofree char *encoded_file_data = NULL;
112+ g_autofree char *encoded_filename = NULL;
113 gsize len = 0;
114
115 if (!g_file_load_contents_finish (G_FILE (source), res, &file_data, &len, NULL, &error)) {
116@@ -134,13 +136,13 @@ pdf_file_loaded (GObject *source,
117 return;
118 }
119
120- html_file = g_resources_lookup_data ("/org/gnome/epiphany/pdfjs/web/viewer.html", 0, NULL);
121-
122- b64 = g_base64_encode ((const guchar *)file_data, len);
123 g_file_delete_async (G_FILE (source), G_PRIORITY_DEFAULT, NULL, pdf_file_deleted, NULL);
124
125- html = g_string_new ("");
126- g_string_printf (html, g_bytes_get_data (html_file, NULL), b64, self->file_name ? self->file_name : "");
127+ html = g_string_new (NULL);
128+ html_file = g_resources_lookup_data ("/org/gnome/epiphany/pdfjs/web/viewer.html", 0, NULL);
129+ encoded_file_data = g_base64_encode ((const guchar *)file_data, len);
130+ encoded_filename = self->file_name ? ephy_encode_for_html_attribute (self->file_name) : g_strdup ("");
131+ g_string_printf (html, g_bytes_get_data (html_file, NULL), encoded_file_data, encoded_filename);
132
133 finish_uri_scheme_request (self, g_strdup (html->str), NULL);
134 }
135Index: epiphany-browser/embed/ephy-reader-handler.c
136===================================================================
137--- epiphany-browser.orig/embed/ephy-reader-handler.c
138+++ epiphany-browser/embed/ephy-reader-handler.c
139@@ -24,6 +24,7 @@
140 #include "ephy-embed-container.h"
141 #include "ephy-embed-shell.h"
142 #include "ephy-lib-type-builtins.h"
143+#include "ephy-output-encoding.h"
144 #include "ephy-settings.h"
145 #include "ephy-web-view.h"
146
147@@ -156,7 +157,9 @@ readability_js_finish_cb (GObject *
148 g_autoptr (WebKitJavascriptResult) js_result = NULL;
149 g_autoptr (GError) error = NULL;
150 g_autofree gchar *byline = NULL;
151+ g_autofree gchar *encoded_byline = NULL;
152 g_autofree gchar *content = NULL;
153+ g_autofree gchar *encoded_title = NULL;
154 g_autoptr (GString) html = NULL;
155 g_autoptr (GBytes) style_css = NULL;
156 const gchar *title;
157@@ -173,10 +176,14 @@ readability_js_finish_cb (GObject *
158
159 byline = readability_get_property_string (js_result, "byline");
160 content = readability_get_property_string (js_result, "content");
161+ title = webkit_web_view_get_title (web_view);
162+
163+ encoded_byline = byline ? ephy_encode_for_html_entity (byline) : g_strdup ("");
164+ encoded_title = ephy_encode_for_html_entity (title);
165
166- html = g_string_new ("");
167+ html = g_string_new (NULL);
168 style_css = g_resources_lookup_data ("/org/gnome/epiphany/readability/reader.css", G_RESOURCE_LOOKUP_FLAGS_NONE, NULL);
169- title = webkit_web_view_get_title (web_view);
170+
171 font_style = enum_nick (EPHY_TYPE_PREFS_READER_FONT_STYLE,
172 g_settings_get_enum (EPHY_SETTINGS_READER,
173 EPHY_PREFS_READER_FONT_STYLE));
174@@ -186,7 +193,8 @@ readability_js_finish_cb (GObject *
175
176 g_string_append_printf (html, "<style>%s</style>"
177 "<title>%s</title>"
178- "<meta http-equiv=\"Content-Type\" content=\"text/html;\" charset=\"UTF-8\">" \
179+ "<meta http-equiv='Content-Type' content='text/html;' charset='UTF-8'>" \
180+ "<meta http-equiv='Content-Security-Policy' content=\"script-src 'none'\">" \
181 "<body class='%s %s'>"
182 "<article>"
183 "<h2>"
184@@ -197,13 +205,27 @@ readability_js_finish_cb (GObject *
185 "</i>"
186 "<hr>",
187 (gchar *)g_bytes_get_data (style_css, NULL),
188- title,
189+ encoded_title,
190 font_style,
191 color_scheme,
192- title,
193- byline != NULL ? byline : "");
194+ encoded_title,
195+ encoded_byline);
196+
197+ /* We cannot encode the page content because it contains HTML tags inserted by
198+ * Readability.js. Upstream recommends that we use an XSS sanitizer like
199+ * DOMPurify plus Content-Security-Policy, but I'm not keen on adding more
200+ * bundled JS dependencies, and we have an advantage over Firefox in that we
201+ * don't need scripts to work at this point. So instead the above CSP
202+ * completely blocks all scripts, which should hopefully obviate the need for
203+ * a DOM purifier.
204+ *
205+ * Note the encoding for page title and byline is still required, as they're
206+ * not supposed to contain markup, and Readability.js unescapes them before
207+ * returning them to us.
208+ */
209 g_string_append (html, content);
210 g_string_append (html, "</article>");
211+ g_string_append (html, "</body>");
212
213 finish_uri_scheme_request (request, g_strdup (html->str), NULL);
214 }
215Index: epiphany-browser/embed/ephy-view-source-handler.c
216===================================================================
217--- epiphany-browser.orig/embed/ephy-view-source-handler.c
218+++ epiphany-browser/embed/ephy-view-source-handler.c
219@@ -23,6 +23,7 @@
220
221 #include "ephy-embed-container.h"
222 #include "ephy-embed-shell.h"
223+#include "ephy-output-encoding.h"
224 #include "ephy-web-view.h"
225
226 #include <gio/gio.h>
227@@ -109,7 +110,9 @@ web_resource_data_cb (WebKitWebResource
228 EphyViewSourceRequest *request)
229 {
230 g_autofree guchar *data = NULL;
231- g_autofree char *escaped_str = NULL;
232+ g_autofree char *data_str = NULL;
233+ g_autofree char *encoded_str = NULL;
234+ g_autofree char *encoded_uri = NULL;
235 g_autoptr (GError) error = NULL;
236 g_autofree char *html = NULL;
237 gsize length;
238@@ -120,8 +123,13 @@ web_resource_data_cb (WebKitWebResource
239 return;
240 }
241
242- /* Warning: data is not a string, so we pass length here because it's not NUL-terminated. */
243- escaped_str = g_markup_escape_text ((const char *)data, length);
244+ /* Convert data to a string */
245+ data_str = g_malloc (length + 1);
246+ memcpy (data_str, data, length);
247+ data_str[length] = '\0';
248+
249+ encoded_str = ephy_encode_for_html_entity (data_str);
250+ encoded_uri = ephy_encode_for_html_entity (webkit_web_resource_get_uri (resource));
251
252 html = g_strdup_printf ("<head>"
253 " <link rel='stylesheet' href='ephy-resource:///org/gnome/epiphany/highlightjs/nnfx.css' media='(prefers-color-scheme: no-preference), (prefers-color-scheme: light)'>"
254@@ -136,8 +144,8 @@ web_resource_data_cb (WebKitWebResource
255 " hljs.initLineNumbersOnLoad();</script>"
256 " <pre><code class='html'>%s</code></pre>"
257 "</body>",
258- webkit_web_resource_get_uri (resource),
259- escaped_str);
260+ encoded_uri,
261+ encoded_str);
262
263 finish_uri_scheme_request (request, g_steal_pointer (&html), NULL);
264 }
265Index: epiphany-browser/embed/ephy-web-view.c
266===================================================================
267--- epiphany-browser.orig/embed/ephy-web-view.c
268+++ epiphany-browser/embed/ephy-web-view.c
269@@ -38,6 +38,7 @@
270 #include "ephy-gsb-utils.h"
271 #include "ephy-history-service.h"
272 #include "ephy-lib-type-builtins.h"
273+#include "ephy-output-encoding.h"
274 #include "ephy-permissions-manager.h"
275 #include "ephy-prefs.h"
276 #include "ephy-reader-handler.h"
277@@ -1772,9 +1773,11 @@ format_network_error_page (const char *
278 const char **icon_name,
279 const char **style)
280 {
281- char *formatted_origin;
282- char *formatted_reason;
283- char *first_paragraph;
284+ g_autofree char *encoded_uri = NULL;
285+ g_autofree char *encoded_origin = NULL;
286+ g_autofree char *formatted_origin = NULL;
287+ g_autofree char *formatted_reason = NULL;
288+ g_autofree char *first_paragraph = NULL;
289 const char *second_paragraph;
290
291 /* Page title when a site cannot be loaded due to a network error. */
292@@ -1783,7 +1786,8 @@ format_network_error_page (const char *
293 /* Message title when a site cannot be loaded due to a network error. */
294 *message_title = g_strdup (_("Unable to display this website"));
295
296- formatted_origin = g_strdup_printf ("<strong>%s</strong>", origin);
297+ encoded_origin = ephy_encode_for_html_entity (origin);
298+ formatted_origin = g_strdup_printf ("<strong>%s</strong>", encoded_origin);
299 /* Error details when a site cannot be loaded due to a network error. */
300 first_paragraph = g_strdup_printf (_("The site at %s seems to be "
301 "unavailable."),
302@@ -1805,16 +1809,13 @@ format_network_error_page (const char *
303
304 /* The button on the network error page. DO NOT ADD MNEMONICS HERE. */
305 *button_label = g_strdup (_("Reload"));
306- *button_action = g_strdup_printf ("window.location = '%s';", uri);
307+ encoded_uri = ephy_encode_for_javascript (uri);
308+ *button_action = g_strdup_printf ("window.location = '%s';", encoded_uri);
309 /* Mnemonic for the Reload button on browser error pages. */
310 *button_accesskey = C_("reload-access-key", "R");
311
312 *icon_name = "network-error-symbolic.svg";
313 *style = "default";
314-
315- g_free (formatted_origin);
316- g_free (formatted_reason);
317- g_free (first_paragraph);
318 }
319
320 static void
321@@ -1828,10 +1829,12 @@ format_crash_error_page (const char *ur
322 const char **icon_name,
323 const char **style)
324 {
325- char *formatted_uri;
326- char *formatted_distributor;
327- char *first_paragraph;
328- char *second_paragraph;
329+ g_autofree char *html_encoded_uri = NULL;
330+ g_autofree char *js_encoded_uri = NULL;
331+ g_autofree char *formatted_uri = NULL;
332+ g_autofree char *formatted_distributor = NULL;
333+ g_autofree char *first_paragraph = NULL;
334+ g_autofree char *second_paragraph = NULL;
335
336 /* Page title when a site cannot be loaded due to a page crash error. */
337 *page_title = g_strdup_printf (_("Problem Loading Page"));
338@@ -1839,7 +1842,8 @@ format_crash_error_page (const char *ur
339 /* Message title when a site cannot be loaded due to a page crash error. */
340 *message_title = g_strdup (_("Oops! There may be a problem"));
341
342- formatted_uri = g_strdup_printf ("<strong>%s</strong>", uri);
343+ html_encoded_uri = ephy_encode_for_html_entity (uri);
344+ formatted_uri = g_strdup_printf ("<strong>%s</strong>", html_encoded_uri);
345 /* Error details when a site cannot be loaded due to a page crash error. */
346 first_paragraph = g_strdup_printf (_("The page %s may have caused Web to "
347 "close unexpectedly."),
348@@ -1858,17 +1862,13 @@ format_crash_error_page (const char *ur
349
350 /* The button on the page crash error page. DO NOT ADD MNEMONICS HERE. */
351 *button_label = g_strdup (_("Reload"));
352- *button_action = g_strdup_printf ("window.location = '%s';", uri);
353+ js_encoded_uri = ephy_encode_for_javascript (uri);
354+ *button_action = g_strdup_printf ("window.location = '%s';", js_encoded_uri);
355 /* Mnemonic for the Reload button on browser error pages. */
356 *button_accesskey = C_("reload-access-key", "R");
357
358 *icon_name = "computer-fail-symbolic.svg";
359 *style = "default";
360-
361- g_free (formatted_uri);
362- g_free (formatted_distributor);
363- g_free (first_paragraph);
364- g_free (second_paragraph);
365 }
366
367 static void
368@@ -1882,6 +1882,7 @@ format_process_crash_error_page (const c
369 const char **icon_name,
370 const char **style)
371 {
372+ g_autofree char *encoded_uri = NULL;
373 const char *first_paragraph;
374
375 /* Page title when a site cannot be loaded due to a process crash error. */
376@@ -1897,7 +1898,8 @@ format_process_crash_error_page (const c
377
378 /* The button on the process crash error page. DO NOT ADD MNEMONICS HERE. */
379 *button_label = g_strdup (_("Reload"));
380- *button_action = g_strdup_printf ("window.location = '%s';", uri);
381+ encoded_uri = ephy_encode_for_javascript (uri);
382+ *button_action = g_strdup_printf ("window.location = '%s';", encoded_uri);
383 /* Mnemonic for the Reload button on browser error pages. */
384 *button_accesskey = C_("reload-access-key", "R");
385
386@@ -1921,8 +1923,9 @@ format_tls_error_page (EphyWebView *vie
387 const char **icon_name,
388 const char **style)
389 {
390- char *formatted_origin;
391- char *first_paragraph;
392+ g_autofree char *encoded_origin = NULL;
393+ g_autofree char *formatted_origin = NULL;
394+ g_autofree char *first_paragraph = NULL;
395
396 /* Page title when a site is not loaded due to an invalid TLS certificate. */
397 *page_title = g_strdup_printf (_("Security Violation"));
398@@ -1930,7 +1933,8 @@ format_tls_error_page (EphyWebView *vie
399 /* Message title when a site is not loaded due to an invalid TLS certificate. */
400 *message_title = g_strdup (_("This Connection is Not Secure"));
401
402- formatted_origin = g_strdup_printf ("<strong>%s</strong>", origin);
403+ encoded_origin = ephy_encode_for_html_entity (origin);
404+ formatted_origin = g_strdup_printf ("<strong>%s</strong>", encoded_origin);
405 /* Error details when a site is not loaded due to an invalid TLS certificate. */
406 first_paragraph = g_strdup_printf (_("This does not look like the real %s. "
407 "Attackers might be trying to steal or "
408@@ -1956,9 +1960,6 @@ format_tls_error_page (EphyWebView *vie
409
410 *icon_name = "channel-insecure-symbolic.svg";
411 *style = "danger";
412-
413- g_free (formatted_origin);
414- g_free (first_paragraph);
415 }
416
417 static void
418@@ -1978,8 +1979,9 @@ format_unsafe_browsing_error_page (EphyW
419 const char **icon_name,
420 const char **style)
421 {
422- char *formatted_origin;
423- char *first_paragraph;
424+ g_autofree char *encoded_origin = NULL;
425+ g_autofree char *formatted_origin = NULL;
426+ g_autofree char *first_paragraph = NULL;
427
428 /* Page title when a site is flagged by Google Safe Browsing verification. */
429 *page_title = g_strdup_printf (_("Security Warning"));
430@@ -1987,7 +1989,8 @@ format_unsafe_browsing_error_page (EphyW
431 /* Message title on the unsafe browsing error page. */
432 *message_title = g_strdup (_("Unsafe website detected!"));
433
434- formatted_origin = g_strdup_printf ("<strong>%s</strong>", origin);
435+ encoded_origin = ephy_encode_for_html_entity (origin);
436+ formatted_origin = g_strdup_printf ("<strong>%s</strong>", encoded_origin);
437 /* Error details on the unsafe browsing error page.
438 * https://developers.google.com/safe-browsing/v4/usage-limits#UserWarnings
439 */
440@@ -2045,9 +2048,6 @@ format_unsafe_browsing_error_page (EphyW
441
442 *icon_name = "security-high-symbolic.svg";
443 *style = "danger";
444-
445- g_free (formatted_origin);
446- g_free (first_paragraph);
447 }
448
449 static void
450@@ -2061,7 +2061,8 @@ format_no_such_file_error_page (EphyWebV
451 const char **icon_name,
452 const char **style)
453 {
454- g_autofree gchar *formatted_origin = NULL;
455+ g_autofree gchar *encoded_address = NULL;
456+ g_autofree gchar *formatted_address = NULL;
457 g_autofree gchar *first_paragraph = NULL;
458 g_autofree gchar *second_paragraph = NULL;
459
460@@ -2071,10 +2072,11 @@ format_no_such_file_error_page (EphyWebV
461 /* Message title on the no such file error page. */
462 *message_title = g_strdup (_("File not found"));
463
464- formatted_origin = g_strdup_printf ("<strong>%s</strong>", view->address);
465+ encoded_address = ephy_encode_for_html_entity (view->address);
466+ formatted_address = g_strdup_printf ("<strong>%s</strong>", encoded_address);
467
468 first_paragraph = g_strdup_printf (_("%s could not be found."),
469- formatted_origin);
470+ formatted_address);
471 second_paragraph = g_strdup_printf (_("Please check the file name for "
472 "capitalization or other typing errors. Also check if "
473 "it has been moved, renamed, or deleted."));
474@@ -2109,19 +2111,19 @@ ephy_web_view_load_error_page (EphyWebVi
475 GError *error,
476 gpointer user_data)
477 {
478- GBytes *html_file;
479- GString *html = g_string_new ("");
480- char *origin = NULL;
481- char *lang = NULL;
482- char *page_title = NULL;
483- char *msg_title = NULL;
484- char *msg_body = NULL;
485- char *msg_details = NULL;
486- char *button_label = NULL;
487- char *hidden_button_label = NULL;
488- char *button_action = NULL;
489- char *hidden_button_action = NULL;
490- char *style_sheet = NULL;
491+ g_autoptr (GBytes) html_file = NULL;
492+ g_autoptr (GString) html = g_string_new (NULL);
493+ g_autofree char *origin = NULL;
494+ g_autofree char *lang = NULL;
495+ g_autofree char *page_title = NULL;
496+ g_autofree char *msg_title = NULL;
497+ g_autofree char *msg_body = NULL;
498+ g_autofree char *msg_details = NULL;
499+ g_autofree char *button_label = NULL;
500+ g_autofree char *hidden_button_label = NULL;
501+ g_autofree char *button_action = NULL;
502+ g_autofree char *hidden_button_action = NULL;
503+ g_autofree char *style_sheet = NULL;
504 const char *button_accesskey = NULL;
505 const char *hidden_button_accesskey = NULL;
506 const char *icon_name = NULL;
507@@ -2261,23 +2263,9 @@ ephy_web_view_load_error_page (EphyWebVi
508 button_accesskey, button_label);
509 #pragma GCC diagnostic pop
510
511- g_bytes_unref (html_file);
512- g_free (origin);
513- g_free (lang);
514- g_free (page_title);
515- g_free (msg_title);
516- g_free (msg_body);
517- g_free (msg_details);
518- g_free (button_label);
519- g_free (button_action);
520- g_free (hidden_button_label);
521- g_free (hidden_button_action);
522- g_free (style_sheet);
523-
524 /* Make our history backend ignore the next page load, since it will be an error page. */
525 ephy_web_view_freeze_history (view);
526 webkit_web_view_load_alternate_html (WEBKIT_WEB_VIEW (view), html->str, uri, 0);
527- g_string_free (html, TRUE);
528 }
529
530 static gboolean
531Index: epiphany-browser/lib/ephy-output-encoding.c
532===================================================================
533--- /dev/null
534+++ epiphany-browser/lib/ephy-output-encoding.c
535@@ -0,0 +1,117 @@
536+/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
537+/*
538+ * Copyright © Red Hat Inc.
539+ *
540+ * This file is part of Epiphany.
541+ *
542+ * Epiphany is free software: you can redistribute it and/or modify
543+ * it under the terms of the GNU General Public License as published by
544+ * the Free Software Foundation, either version 3 of the License, or
545+ * (at your option) any later version.
546+ *
547+ * Epiphany is distributed in the hope that it will be useful,
548+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
549+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
550+ * GNU General Public License for more details.
551+ *
552+ * You should have received a copy of the GNU General Public License
553+ * along with Epiphany. If not, see <http://www.gnu.org/licenses/>.
554+ */
555+
556+#include "config.h"
557+#include "ephy-output-encoding.h"
558+
559+#include <glib.h>
560+
561+#if !GLIB_CHECK_VERSION(2, 68, 0)
562+static guint
563+g_string_replace (GString *string,
564+ const gchar *find,
565+ const gchar *replace,
566+ guint limit)
567+{
568+ gsize f_len, r_len, pos;
569+ gchar *cur, *next;
570+ guint n = 0;
571+
572+ g_return_val_if_fail (string != NULL, 0);
573+ g_return_val_if_fail (find != NULL, 0);
574+ g_return_val_if_fail (replace != NULL, 0);
575+
576+ f_len = strlen (find);
577+ r_len = strlen (replace);
578+ cur = string->str;
579+
580+ while ((next = strstr (cur, find)) != NULL)
581+ {
582+ pos = next - string->str;
583+ g_string_erase (string, pos, f_len);
584+ g_string_insert (string, pos, replace);
585+ cur = string->str + pos + r_len;
586+ n++;
587+ /* Only match the empty string once at any given position, to
588+ * avoid infinite loops */
589+ if (f_len == 0)
590+ {
591+ if (cur[0] == '\0')
592+ break;
593+ else
594+ cur++;
595+ }
596+ if (n == limit)
597+ break;
598+ }
599+
600+ return n;
601+}
602+#endif
603+
604+char *
605+ephy_encode_for_html_entity (const char *input)
606+{
607+ GString *str = g_string_new (input);
608+
609+ g_string_replace (str, "&", "&amp;", 0);
610+ g_string_replace (str, "<", "&lt;", 0);
611+ g_string_replace (str, ">", "&gt;", 0);
612+ g_string_replace (str, "\"", "&quot;", 0);
613+ g_string_replace (str, "'", "&#x27;", 0);
614+ g_string_replace (str, "/", "&#x2F;", 0);
615+
616+ return g_string_free (str, FALSE);
617+}
618+
619+static char *
620+encode_all_except_alnum (const char *input,
621+ const char *format)
622+{
623+ GString *str;
624+ const char *c = input;
625+
626+ if (!g_utf8_validate (input, -1, NULL))
627+ return g_strdup ("");
628+
629+ str = g_string_new (NULL);
630+ do {
631+ gunichar u = g_utf8_get_char (c);
632+ if (g_unichar_isalnum (u))
633+ g_string_append_unichar (str, u);
634+ else
635+ g_string_append_printf (str, format, u);
636+ c = g_utf8_next_char (c);
637+ } while (*c);
638+
639+ return g_string_free (str, FALSE);
640+}
641+
642+char *
643+ephy_encode_for_html_attribute (const char *input)
644+{
645+ return encode_all_except_alnum (input, "&#x%02x;");
646+}
647+
648+char *
649+ephy_encode_for_javascript (const char *input)
650+{
651+ return encode_all_except_alnum (input, "\\u%04u;");
652+}
653Index: epiphany-browser/lib/ephy-output-encoding.h
654===================================================================
655--- /dev/null
656+++ epiphany-browser/lib/ephy-output-encoding.h
657@@ -0,0 +1,38 @@
658+/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
659+/*
660+ * Copyright © 2021 Red Hat Inc.
661+ *
662+ * This file is part of Epiphany.
663+ *
664+ * Epiphany is free software: you can redistribute it and/or modify
665+ * it under the terms of the GNU General Public License as published by
666+ * the Free Software Foundation, either version 3 of the License, or
667+ * (at your option) any later version.
668+ *
669+ * Epiphany is distributed in the hope that it will be useful,
670+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
671+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
672+ * GNU General Public License for more details.
673+ *
674+ * You should have received a copy of the GNU General Public License
675+ * along with Epiphany. If not, see <http://www.gnu.org/licenses/>.
676+ */
677+
678+#pragma once
679+
680+#include <glib.h>
681+
682+G_BEGIN_DECLS
683+
684+/* These functions implement the OWASP XSS prevention output encoding rules:
685+ * https://cheatsheetseries.owasp.org/cheatsheets/Cross_Site_Scripting_Prevention_Cheat_Sheet.html#output-encoding-rules-summary
686+ *
687+ * You must *carefully* read that document to safely inject untrusted data into
688+ * web content. Here be dragons.
689+ */
690+
691+char *ephy_encode_for_html_entity (const char *input);
692+char *ephy_encode_for_html_attribute (const char *input);
693+char *ephy_encode_for_javascript (const char *input);
694+
695+G_END_DECLS
696Index: epiphany-browser/lib/meson.build
697===================================================================
698--- epiphany-browser.orig/lib/meson.build
699+++ epiphany-browser/lib/meson.build
700@@ -21,6 +21,7 @@ libephymisc_sources = [
701 'ephy-langs.c',
702 'ephy-notification.c',
703 'ephy-notification-container.c',
704+ 'ephy-output-encoding.c',
705 'ephy-permissions-manager.c',
706 'ephy-profile-utils.c',
707 'ephy-search-engine-manager.c',