summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xconfigure3
-rw-r--r--content/policy_enforcing.js40
-rw-r--r--pytest.ini1
-rw-r--r--test/conftest.py26
-rw-r--r--test/unit/test_popup.py6
5 files changed, 66 insertions, 10 deletions
diff --git a/configure b/configure
index 50f75dc..80516fa 100755
--- a/configure
+++ b/configure
@@ -91,6 +91,9 @@ if [ "x$BROWSER_BINARY" = x ]; then
elif [ "x$TARGET" = xlibrewolf ]; then
# Debian's path to Librewolf
BROWSER_BINARY=/usr/share/librewolf/librewolf
+ elif [ "x$TARGET" = xiceweasel ]; then
+ # Parabola's path to Iceweasel
+ BROWSER_BINARY=/usr/lib/iceweasel/iceweasel
elif [ "x$TARGET" = xicecat ]; then
# Parabola's path to IceCat
BROWSER_BINARY=/usr/lib/icecat/icecat
diff --git a/content/policy_enforcing.js b/content/policy_enforcing.js
index 320b6d0..45529ea 100644
--- a/content/policy_enforcing.js
+++ b/content/policy_enforcing.js
@@ -159,6 +159,12 @@ function desanitize_script(script) {
delete script.haketilo_blocked_type;
}
+/*
+ * Blocking certain attributes that might allow 'javascript:' URLs. Some of
+ * these are: <iframe>'s 'src' attributes (would normally execute js in URL upon
+ * frame's load), <object>'s 'data' attribute (would also execute upon load) and
+ * <a>'s 'href' attribute (would execute upon link click).
+ */
const bad_url_reg = /^data:([^,;]*ml|unknown-content-type)|^javascript:/i;
function sanitize_element_urls(element) {
if (element.haketilo_sanitized_urls)
@@ -166,13 +172,37 @@ function sanitize_element_urls(element) {
element.haketilo_sanitized_urls = true;
+ let some_attr_blocked = false;
+
for (const attr of [...element.attributes || []]
.filter(attr => /^(href|src|data)$/i.test(attr.localName))
.filter(attr => bad_url_reg.test(attr.value))) {
+ /*
+ * Under some browsers (Mozilla) removing attributes doesn't stop their
+ * javascript from executing, but replacing them does. For 'src' and
+ * 'data' I chose to replace the attribute with a 'data:' URL and have
+ * it replace bad <iframe>'s/<object>'s contents with a "blocked"
+ * string. For 'href' (which appears on <a>'s) I chose to use a
+ * 'javascript:' URL to avoid having the page reloaded upon a link
+ * click.
+ */
const replacement_value = /^href$/i.test(attr.localName) ?
- "javascript:void('blocked');" : "data:text/plain,blocked";
+ "javascript:void('blocked');" : "data:text/plain,blocked";
+ some_attr_blocked = true;
block_attribute(element, attr.localName, attr.namespaceURI,
- replacement_value);
+ replacement_value);
+ }
+
+ /*
+ * Trial and error shows that under certain browsers additional element
+ * removal and re-addition might be necessary to prevent execution of a
+ * 'javascript:' URL (Parabola's Iceweasel 75 requires it for 'src' URL of
+ * an <iframe>).
+ */
+ if (some_attr_blocked) {
+ const replacement_elem = document.createElement("a");
+ element.replaceWith(replacement_elem);
+ replacement_elem.replaceWith(element);
}
}
@@ -189,8 +219,10 @@ function sanitize_element_onevent(element) {
continue;
/*
- * Guard against redefined getter on DOM object property. This should
- * not be an issue */
+ * Guard against redefined getter on DOM object property. This is a
+ * supplemental security measure since page's own scripts should be
+ * blocked and unable to redefine properties, anyway.
+ */
if (Object.getOwnPropertyDescriptor(element.wrappedJSObject, attr)) {
console.error("Redefined property on a DOM object! The page might have bypassed our script blocking measures!");
continue;
diff --git a/pytest.ini b/pytest.ini
index 6c8afcc..1057a9a 100644
--- a/pytest.ini
+++ b/pytest.ini
@@ -17,3 +17,4 @@
markers =
ext_data: define a custom testing extension for `webextension` fixture.
get_page: define a url the `driver` fixture should navigate the browser to.
+ second_driver: tell `driver` fixture to spawn a separate browser instance fr this test.
diff --git a/test/conftest.py b/test/conftest.py
index b9c46b0..dec6d91 100644
--- a/test/conftest.py
+++ b/test/conftest.py
@@ -59,10 +59,20 @@ def close_all_but_one_window(driver):
@pytest.fixture()
def driver(_driver, request):
nav_target = request.node.get_closest_marker('get_page')
- close_all_but_one_window(_driver)
- _driver.get(nav_target.args[0] if nav_target else 'about:blank')
- _driver.implicitly_wait(0)
- yield _driver
+ nav_target = nav_target.args[0] if nav_target else 'about:blank'
+
+ second_driver = request.node.get_closest_marker('second_driver')
+
+ if second_driver:
+ with firefox_safe_mode() as _driver:
+ _driver.get(nav_target)
+ yield _driver
+ _driver.quit()
+ else:
+ close_all_but_one_window(_driver)
+ _driver.get(nav_target)
+ _driver.implicitly_wait(0)
+ yield _driver
@pytest.fixture()
def webextension(driver, request):
@@ -87,8 +97,12 @@ def webextension(driver, request):
yield
- close_all_but_one_window(driver)
- driver.get('https://gotmyowndoma.in/')
+ # Unloading an extension might cause its windows to vanish. Make sure
+ # there's at least one window navigated to some other page before
+ # uninstalling the addon. Otherwise, we could be left with a windowless
+ # browser :c
+ driver.switch_to.window(driver.window_handles[-1])
+ driver.get('about:blank')
driver.uninstall_addon(addon_id)
ext_path.unlink()
diff --git a/test/unit/test_popup.py b/test/unit/test_popup.py
index 3163adb..1fc262c 100644
--- a/test/unit/test_popup.py
+++ b/test/unit/test_popup.py
@@ -235,6 +235,12 @@ def test_popup_repo_query(driver, execute_in_page):
@pytest.mark.ext_data(popup_ext_data)
@pytest.mark.usefixtures('webextension')
+# Under Parabola's Iceweasel 75 the settings page's window opened during this
+# test is impossible to close using driver.close() - it raises an exception with
+# message 'closeTab() not supported in iceweasel'. To avoid such error during
+# test cleanup, we use the mark below to tell our driver fixture to span a
+# separate browser instance for this test.
+@pytest.mark.second_driver()
def test_popup_settings_opening(driver, execute_in_page):
"""
Test opening the settings page from popup through button click.