[epiphany/pgriffis/web-extension-beastify] WebExtensions: Initial Implementation of tabs permissions
- From: Patrick Griffis <pgriffis src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [epiphany/pgriffis/web-extension-beastify] WebExtensions: Initial Implementation of tabs permissions
- Date: Mon, 23 May 2022 01:46:10 +0000 (UTC)
commit 377f0b7697eb328a9fdf3e2d3038c180b50ebf5e
Author: Patrick Griffis <pgriffis igalia com>
Date: Sun May 22 20:19:12 2022 -0500
WebExtensions: Initial Implementation of tabs permissions
One part remains incomplete, the activeTab permission should be
only allowed on user interactions.
src/webextension/api/tabs.c | 27 ++++--
src/webextension/ephy-web-extension.c | 160 ++++++++++++++++++++++++++++++++++
src/webextension/ephy-web-extension.h | 8 ++
3 files changed, 190 insertions(+), 5 deletions(-)
---
diff --git a/src/webextension/api/tabs.c b/src/webextension/api/tabs.c
index 1804c5501..d84402f33 100644
--- a/src/webextension/api/tabs.c
+++ b/src/webextension/api/tabs.c
@@ -66,11 +66,14 @@ get_web_view_for_tab_id (EphyShell *shell,
static void
add_web_view_to_json (JsonBuilder *builder,
EphyWindow *window,
- EphyWebView *web_view)
+ EphyWebView *web_view,
+ gboolean has_tab_permission)
{
json_builder_begin_object (builder);
- json_builder_set_member_name (builder, "url");
- json_builder_add_string_value (builder, ephy_web_view_get_address (web_view));
+ if (has_tab_permission) {
+ json_builder_set_member_name (builder, "url");
+ json_builder_add_string_value (builder, ephy_web_view_get_address (web_view));
+ }
json_builder_set_member_name (builder, "id");
json_builder_add_int_value (builder, ephy_web_view_get_uid (web_view));
json_builder_set_member_name (builder, "windowId");
@@ -169,7 +172,8 @@ tabs_handler_query (EphyWebExtension *self,
else if (active == TAB_QUERY_DONT_MATCH && web_view == active_web_view)
continue;
- add_web_view_to_json (builder, window, web_view);
+ add_web_view_to_json (builder, window, web_view,
+ ephy_web_extension_has_tab_or_host_permission (self, web_view, TRUE));
}
}
@@ -212,6 +216,9 @@ tabs_handler_insert_css (EphyWebExtension *self,
if (!target_web_view)
return NULL;
+ if (!ephy_web_extension_has_host_permission (self, EPHY_WEB_VIEW (target_web_view), TRUE))
+ return NULL;
+
ucm = webkit_web_view_get_user_content_manager (target_web_view);
code = jsc_value_object_get_property (obj, "code");
@@ -256,6 +263,9 @@ tabs_handler_remove_css (EphyWebExtension *self,
if (!target_web_view)
return NULL;
+ if (!ephy_web_extension_has_host_permission (self, EPHY_WEB_VIEW (target_web_view), TRUE))
+ return NULL;
+
ucm = webkit_web_view_get_user_content_manager (target_web_view);
code = jsc_value_object_get_property (obj, "code");
@@ -286,7 +296,8 @@ tabs_handler_get (EphyWebExtension *self,
if (!target_web_view)
return NULL;
- add_web_view_to_json (builder, parent_window, target_web_view);
+ add_web_view_to_json (builder, parent_window, target_web_view,
+ ephy_web_extension_has_tab_or_host_permission (self, target_web_view, TRUE));
root = json_builder_get_root (builder);
return json_to_string (root, FALSE);
@@ -336,6 +347,9 @@ tabs_handler_execute_script (EphyWebExtension *self,
target_web_view = get_web_view_for_tab_id (shell, jsc_value_to_int32 (tab_id_value), NULL);
if (code && target_web_view) {
+ if (!ephy_web_extension_has_host_permission (self, EPHY_WEB_VIEW (target_web_view), TRUE))
+ return NULL;
+
webkit_web_view_run_javascript_in_world (target_web_view,
code,
ephy_web_extension_get_guid (self),
@@ -376,6 +390,9 @@ tabs_handler_send_message (EphyWebExtension *self,
target_web_view = get_web_view_for_tab_id (shell, jsc_value_to_int32 (tab_id_value), NULL);
if (target_web_view) {
+ if (!ephy_web_extension_has_host_permission (self, EPHY_WEB_VIEW (target_web_view), TRUE))
+ return NULL;
+
webkit_web_view_run_javascript_in_world (target_web_view,
code,
ephy_web_extension_get_guid (self),
diff --git a/src/webextension/ephy-web-extension.c b/src/webextension/ephy-web-extension.c
index 28d70d5b9..cf1c27c5d 100644
--- a/src/webextension/ephy-web-extension.c
+++ b/src/webextension/ephy-web-extension.c
@@ -1207,3 +1207,163 @@ ephy_web_extension_get_permissions (EphyWebExtension *self)
{
return self->permissions;
}
+
+static gboolean
+is_supported_scheme (const char *scheme)
+{
+ static const char * const supported_schemes[] = {
+ "http", "https", "ws", "wss", "data", "file"
+ };
+
+ for (uint i = 0; i < G_N_ELEMENTS (supported_schemes); i++) {
+ if (strcmp (supported_schemes[i], scheme) == 0)
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static gboolean
+scheme_matches (const char *permission_scheme,
+ const char *uri_scheme)
+{
+ /* https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/Match_patterns#scheme */
+ /* NOTE: Firefox matches "wss" and "ws" here, Safari and Chrome do not. */
+ if (strcmp (permission_scheme, "*") == 0)
+ return strcmp (uri_scheme, "https") == 0 || strcmp (uri_scheme, "http") == 0;
+
+ return is_supported_scheme (permission_scheme) && strcmp (permission_scheme, uri_scheme) == 0;
+}
+
+static gboolean
+host_matches (const char *permission_host,
+ const char *uri_host)
+{
+ /* https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/Match_patterns#host */
+ if (strcmp (permission_host, "*") == 0)
+ return TRUE;
+
+ if (g_str_has_prefix (permission_host, "*."))
+ return g_str_has_suffix (uri_host, permission_host + 1); /* Skip '*' but NOT '.'. */
+
+ return strcmp (permission_host, uri_host) == 0;
+}
+
+static gboolean
+path_matches (const char *permission_path,
+ const char *uri_path)
+{
+ g_autofree char *permission_path_escaped = NULL;
+ g_autoptr (GString) permission_path_regex = NULL;
+
+ /* https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/Match_patterns#path */
+ if (strcmp (permission_path, "*") == 0)
+ return TRUE;
+
+ /* We need to do more complicated pattern matching so convert it to regex replacing '*' with '.*'
+ * and making sure to match the entire string. */
+ permission_path_escaped = g_regex_escape_string (permission_path, -1);
+ permission_path_regex = g_string_new (permission_path_escaped);
+ g_string_replace (permission_path_regex, "\\*", ".*", -1);
+
+ return g_regex_match_simple (permission_path_regex->str, uri_path, G_REGEX_ANCHORED,
G_REGEX_MATCH_ANCHORED | G_REGEX_MATCH_NOTEMPTY);
+}
+
+static char *
+join_path_and_query (GUri *uri)
+{
+ const char *path = g_uri_get_path (uri);
+ const char *query = g_uri_get_query (uri);
+ if (!query)
+ return g_strdup (path);
+
+ return g_strjoin ("?", path, query, NULL);
+}
+
+static gboolean
+permission_matches_uri (const char *permission,
+ GUri *uri)
+{
+ g_autoptr (GUri) permission_uri = NULL;
+ g_autoptr (GError) error = NULL;
+ g_autofree char *permission_path_and_query = NULL;
+ g_autofree char *uri_path_and_query = NULL;
+
+ /* https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/Match_patterns#all_urls */
+ if (strcmp (permission, "<all_urls>") == 0)
+ return is_supported_scheme (g_uri_get_scheme (uri));
+
+ permission_uri = g_uri_parse (permission, G_URI_FLAGS_ENCODED_PATH | G_URI_FLAGS_SCHEME_NORMALIZE |
G_URI_FLAGS_PARSE_RELAXED, &error);
+ if (error) {
+ g_message ("Failed to parse permission '%s' as URI: %s", permission, error->message);
+ return FALSE;
+ }
+
+ /* Ports are forbidden. */
+ if (g_uri_get_port (permission_uri) != -1)
+ return FALSE;
+
+ /* Empty paths are forbidden. */
+ if (strcmp (g_uri_get_path (permission_uri), "") == 0)
+ return FALSE;
+
+ if (!scheme_matches (g_uri_get_scheme (permission_uri), g_uri_get_scheme (uri)))
+ return FALSE;
+
+ if (!host_matches (g_uri_get_host (permission_uri), g_uri_get_host (uri)))
+ return FALSE;
+
+ permission_path_and_query = join_path_and_query (permission_uri);
+ uri_path_and_query = join_path_and_query (uri);
+
+ if (!path_matches (permission_path_and_query, uri_path_and_query))
+ return FALSE;
+
+ return TRUE;
+}
+
+static gboolean
+ephy_web_extension_has_permission_internal (EphyWebExtension *self,
+ EphyWebView *web_view,
+ gboolean is_user_interaction,
+ gboolean allow_tabs)
+{
+ EphyWebView *active_web_view = ephy_shell_get_active_web_view (ephy_shell_get_default ());
+ gboolean is_active_tab = active_web_view == web_view;
+ GUri *host = g_uri_parse (ephy_web_view_get_address (web_view), G_URI_FLAGS_ENCODED_PATH |
G_URI_FLAGS_SCHEME_NORMALIZE | G_URI_FLAGS_PARSE_RELAXED, NULL);
+
+ g_assert (host);
+
+ /* https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/Match_patterns */
+
+ for (uint i = 0; i < self->permissions->len; i++) {
+ const char *permission = g_ptr_array_index (self->permissions, i);
+
+ if (is_user_interaction && is_active_tab && strcmp (permission, "activeTab") == 0)
+ return TRUE;
+
+ if (allow_tabs && strcmp (permission, "tab") == 0)
+ return TRUE;
+
+ if (permission_matches_uri (permission, host))
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+gboolean
+ephy_web_extension_has_host_permission (EphyWebExtension *self,
+ EphyWebView *web_view,
+ gboolean is_user_interaction)
+{
+ return ephy_web_extension_has_permission_internal (self, web_view, is_user_interaction, FALSE);
+}
+
+gboolean
+ephy_web_extension_has_tab_or_host_permission (EphyWebExtension *self,
+ EphyWebView *web_view,
+ gboolean is_user_interaction)
+{
+ return ephy_web_extension_has_permission_internal (self, web_view, is_user_interaction, TRUE);
+}
diff --git a/src/webextension/ephy-web-extension.h b/src/webextension/ephy-web-extension.h
index 57d78a331..1a7584c46 100644
--- a/src/webextension/ephy-web-extension.h
+++ b/src/webextension/ephy-web-extension.h
@@ -125,5 +125,13 @@ const char *ephy_web_extension_get_guid (EphyW
GPtrArray *ephy_web_extension_get_permissions (EphyWebExtension *self);
+gboolean ephy_web_extension_has_tab_or_host_permission (EphyWebExtension *self,
+ EphyWebView *web_view,
+ gboolean
is_user_interaction);
+
+gboolean ephy_web_extension_has_host_permission (EphyWebExtension *self,
+ EphyWebView *web_view,
+ gboolean
is_user_interaction);
+
G_END_DECLS
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]