aboutsummaryrefslogtreecommitdiff
path: root/src/hydrilla/proxy/policies
diff options
context:
space:
mode:
Diffstat (limited to 'src/hydrilla/proxy/policies')
-rw-r--r--src/hydrilla/proxy/policies/__init__.py2
-rw-r--r--src/hydrilla/proxy/policies/base.py95
-rw-r--r--src/hydrilla/proxy/policies/info_pages_templates/info_base.html.jinja89
-rw-r--r--src/hydrilla/proxy/policies/info_pages_templates/js_error_blocked_info.html.jinja22
-rw-r--r--src/hydrilla/proxy/policies/info_pages_templates/js_fallback_allowed_info.html.jinja14
-rw-r--r--src/hydrilla/proxy/policies/info_pages_templates/js_fallback_blocked_info.html.jinja14
-rw-r--r--src/hydrilla/proxy/policies/info_pages_templates/js_rule_allowed_info.html.jinja14
-rw-r--r--src/hydrilla/proxy/policies/info_pages_templates/js_rule_blocked_info.html.jinja14
-rw-r--r--src/hydrilla/proxy/policies/info_pages_templates/js_rule_info.html.jinja37
-rw-r--r--src/hydrilla/proxy/policies/info_pages_templates/payload_info.html.jinja48
-rw-r--r--src/hydrilla/proxy/policies/info_pages_templates/special_page_info.html.jinja17
-rw-r--r--src/hydrilla/proxy/policies/injectable_scripts/popup.js.jinja173
-rw-r--r--src/hydrilla/proxy/policies/misc.py28
-rw-r--r--src/hydrilla/proxy/policies/payload.py7
-rw-r--r--src/hydrilla/proxy/policies/payload_resource.py22
-rw-r--r--src/hydrilla/proxy/policies/rule.py14
16 files changed, 576 insertions, 34 deletions
diff --git a/src/hydrilla/proxy/policies/__init__.py b/src/hydrilla/proxy/policies/__init__.py
index 2276177..93c3d4f 100644
--- a/src/hydrilla/proxy/policies/__init__.py
+++ b/src/hydrilla/proxy/policies/__init__.py
@@ -4,7 +4,7 @@
#
# Available under the terms of Creative Commons Zero v1.0 Universal.
-from .base import *
+from .base import PolicyPriority, Policy, PolicyFactory, response_work_data
from .payload import PayloadPolicyFactory
diff --git a/src/hydrilla/proxy/policies/base.py b/src/hydrilla/proxy/policies/base.py
index 7ce5105..1626b5c 100644
--- a/src/hydrilla/proxy/policies/base.py
+++ b/src/hydrilla/proxy/policies/base.py
@@ -31,10 +31,10 @@
import enum
import re
+import threading
import dataclasses as dc
import typing as t
-from threading import Lock
from abc import ABC, abstractmethod
from hashlib import sha256
from base64 import b64encode
@@ -43,24 +43,60 @@ import jinja2
from immutables import Map
-from ... url_patterns import ParsedUrl
+from ...translations import translation as make_translation
+from ... import url_patterns
+from ... import common_jinja_templates
from .. import state
from .. import http_messages
from .. import csp
-loader = jinja2.PackageLoader(__package__, package_path='injectable_scripts')
-jinja_env = jinja2.Environment(
- loader = loader,
+_info_loader = jinja2.PackageLoader(
+ __package__,
+ package_path = 'info_pages_templates'
+)
+_combined_loader = common_jinja_templates.combine_with_loaders([_info_loader])
+_jinja_info_env = jinja2.Environment(
+ loader = _combined_loader,
+ autoescape = jinja2.select_autoescape(['html.jinja']),
+ lstrip_blocks = True,
+ extensions = ['jinja2.ext.i18n', 'jinja2.ext.do']
+)
+_jinja_info_env.install_gettext_translations(make_translation()) # type: ignore
+_jinja_info_env.globals['url_patterns'] = url_patterns
+_jinja_info_lock = threading.Lock()
+
+def get_info_template(template_file_name: str) -> jinja2.Template:
+ with _jinja_info_lock:
+ return _jinja_info_env.get_template(template_file_name)
+
+
+_jinja_script_loader = jinja2.PackageLoader(
+ __package__,
+ package_path = 'injectable_scripts'
+)
+_jinja_script_env = jinja2.Environment(
+ loader = _jinja_script_loader,
+ autoescape = False,
lstrip_blocks = True,
- autoescape = False
+ extensions = ['jinja2.ext.do']
)
-jinja_lock = Lock()
+_jinja_script_lock = threading.Lock()
+def get_script_template(template_file_name: str) -> jinja2.Template:
+ with _jinja_script_lock:
+ return _jinja_script_env.get_template(template_file_name)
-popup_script = jinja_env.get_template('popup.js.jinja').render()
-popup_script_sha256_bytes = sha256(popup_script.encode()).digest()
-popup_script_sha256_b64 = b64encode(popup_script_sha256_bytes).decode()
+
+response_work_data = threading.local()
+
+def response_nonce() -> str:
+ """
+ When called multiple times during consume_response(), each time returns the
+ same unpredictable string unique to this response. The string is used as a
+ nonce for script elements.
+ """
+ return response_work_data.nonce
class PolicyPriority(int, enum.Enum):
@@ -140,7 +176,9 @@ class Policy(ABC):
-> t.Mapping[str, t.Sequence[str]]:
if (self.current_popup_settings.popup_enabled and
http_info.is_likely_a_page):
- return {'script-src': [f"'sha256-{popup_script_sha256_b64}'"]}
+ nonce_source = f"'nonce-{response_nonce()}'"
+ directives = ('script-src', 'style-src', 'frame-src')
+ return dict((directive, [nonce_source]) for directive in directives)
else:
return Map()
@@ -167,8 +205,26 @@ class Policy(ABC):
) -> t.Union[str, bytes]:
popup_settings = self.current_popup_settings
- if (popup_settings.popup_enabled and
- http_info.is_likely_a_page):
+ if popup_settings.popup_enabled:
+ nonce = response_nonce()
+
+ popup_page = self.make_info_page(http_info)
+ if popup_page is None:
+ template = get_info_template('special_page_info.html.jinja')
+ popup_page = template.render(
+ url = http_info.request_info.url.orig_url
+ )
+
+ template = get_script_template('popup.js.jinja')
+ popup_script = template.render(
+ popup_page_b64 = b64encode(popup_page.encode()).decode(),
+ nonce_b64 = b64encode(nonce.encode()).decode(),
+ # TODO: add an option to configure popup style in the web UI.
+ # Then start passing the real style value.
+ #popup_style = popup_settings.style.value
+ popup_style = 'D'
+ )
+
if encoding is None:
encoding = 'utf-8'
@@ -180,16 +236,15 @@ class Policy(ABC):
dotype_decl = body[0:doctype_decl_len]
doc_rest = body[doctype_decl_len:]
- return f'{dotype_decl}<script>{popup_script}</script>{doc_rest}'
+ script_tag = f'<script nonce="{nonce}">{popup_script}</script>'
+
+ return dotype_decl + script_tag + doc_rest
else:
return http_info.response_info.body
def _modify_response_body(self, http_info: http_messages.FullHTTPInfo) \
-> bytes:
- if not http_messages.is_likely_a_page(
- request_info = http_info.request_info,
- response_info = http_info.response_info
- ):
+ if not http_info.is_likely_a_page:
return http_info.response_info.body
data = http_info.response_info.body
@@ -252,6 +307,10 @@ class Policy(ABC):
body = new_body
)
+ def make_info_page(self, http_info: http_messages.FullHTTPInfo) \
+ -> t.Optional[str]:
+ return None
+
@dc.dataclass(frozen=True, unsafe_hash=True) # type: ignore[misc]
class PolicyFactory(ABC):
diff --git a/src/hydrilla/proxy/policies/info_pages_templates/info_base.html.jinja b/src/hydrilla/proxy/policies/info_pages_templates/info_base.html.jinja
new file mode 100644
index 0000000..0785039
--- /dev/null
+++ b/src/hydrilla/proxy/policies/info_pages_templates/info_base.html.jinja
@@ -0,0 +1,89 @@
+{#
+SPDX-License-Identifier: GPL-3.0-or-later OR CC-BY-SA-4.0
+
+Proxy info page with information about other page - base template.
+
+This file is part of Hydrilla&Haketilo.
+
+Copyright (C) 2022 Wojtek Kosior <koszko@koszko.org>
+
+Dual licensed under
+* GNU General Public License v3.0 or later and
+* Creative Commons Attribution Share Alike 4.0 International.
+
+You can choose to use either of these licenses or both.
+
+
+I, Wojtek Kosior, thereby promise not to sue for violation of this
+file's licenses. Although I request that you do not make use of this
+code in a proprietary work, I am not going to enforce this in court.
+#}
+{% extends "base.html.jinja" %}
+
+{% block style %}
+ {{ super() }}
+
+ #main {
+ padding: 0 10px;
+ }
+{% endblock %}
+
+{% block head %}
+ {{ super() }}
+
+ <title>{{ _('info.base.title') }}</title>
+{% endblock head %}
+
+{% block main %}
+ <h3>
+ {{ _('info.base.heading.page_info') }}
+ </h3>
+
+ {{ label(_('info.base.page_url_label')) }}
+
+ <p>
+ {{ url }}
+ </p>
+
+ <div class="horizontal-separator"></div>
+
+ {{ label(_('info.base.page_policy_label')) }}
+
+ <p class="has-colored-links">
+ {% block site_policy required %}{% endblock %}
+ </p>
+
+ {% block main_rest %}
+ {% endblock %}
+
+ {% block options %}
+ <div class="horizontal-separator"></div>
+
+ {{ label(_('info.base.more_config_options_label')) }}
+
+ {% set site_pattern = url_patterns.pattern_for_domain(url)|urlencode %}
+ {% set page_pattern = url_patterns.normalize_pattern(url)|urlencode %}
+
+ {%
+ for pattern, hkt_url_fmt, but_text in [
+ (site_pattern, 'https://hkt.mitm.it/rules/viewbypattern?pattern={}',
+ _('info.base.this_site_script_blocking_button')),
+
+ (site_pattern, 'https://hkt.mitm.it/import?pattern={}',
+ _('info.base.this_site_payload_button')),
+
+ (page_pattern, 'https://hkt.mitm.it/rules/viewbypattern?pattern={}',
+ _('info.base.this_page_script_blocking_button')),
+
+ (page_pattern, 'https://hkt.mitm.it/import?pattern={}',
+ _('info.base.this_page_payload_button'))
+ ]
+ %}
+ {% set hkt_url = hkt_url_fmt.format(pattern) %}
+ {% set classes = "green-button block-with-bottom-margin" %}
+ <a class="{{classes}}" href="{{ hkt_url }}" target="_blank">
+ {{ but_text }}
+ </a>
+ {% endfor %}
+ {% endblock options %}
+{% endblock main %}
diff --git a/src/hydrilla/proxy/policies/info_pages_templates/js_error_blocked_info.html.jinja b/src/hydrilla/proxy/policies/info_pages_templates/js_error_blocked_info.html.jinja
new file mode 100644
index 0000000..c76d42b
--- /dev/null
+++ b/src/hydrilla/proxy/policies/info_pages_templates/js_error_blocked_info.html.jinja
@@ -0,0 +1,22 @@
+{#
+SPDX-License-Identifier: CC0-1.0
+
+Proxy info page with information about page with JS blocked after an error.
+
+This file is part of Hydrilla&Haketilo.
+
+Copyright (C) 2022 Wojtek Kosior <koszko@koszko.org>
+#}
+{% extends "info_base.html.jinja" %}
+
+{% block site_policy %}
+ {{ _('info.js_error_blocked.html')|safe }}
+{% endblock %}
+
+{% block main_rest %}
+ {% if settings.advanced_user %}
+ {{ label(_('info.js_error_blocked.stacktrace')) }}
+
+ {{ verbatim(traceback) }}
+ {% endif %}
+{% endblock %}
diff --git a/src/hydrilla/proxy/policies/info_pages_templates/js_fallback_allowed_info.html.jinja b/src/hydrilla/proxy/policies/info_pages_templates/js_fallback_allowed_info.html.jinja
new file mode 100644
index 0000000..71f3151
--- /dev/null
+++ b/src/hydrilla/proxy/policies/info_pages_templates/js_fallback_allowed_info.html.jinja
@@ -0,0 +1,14 @@
+{#
+SPDX-License-Identifier: CC0-1.0
+
+Proxy info page with information about page with JS allowed by default policy.
+
+This file is part of Hydrilla&Haketilo.
+
+Copyright (C) 2022 Wojtek Kosior <koszko@koszko.org>
+#}
+{% extends "info_base.html.jinja" %}
+
+{% block site_policy %}
+ {{ _('info.js_fallback_allowed') }}
+{% endblock %}
diff --git a/src/hydrilla/proxy/policies/info_pages_templates/js_fallback_blocked_info.html.jinja b/src/hydrilla/proxy/policies/info_pages_templates/js_fallback_blocked_info.html.jinja
new file mode 100644
index 0000000..3e8719a
--- /dev/null
+++ b/src/hydrilla/proxy/policies/info_pages_templates/js_fallback_blocked_info.html.jinja
@@ -0,0 +1,14 @@
+{#
+SPDX-License-Identifier: CC0-1.0
+
+Proxy info page with information about page with JS blocked by default policy.
+
+This file is part of Hydrilla&Haketilo.
+
+Copyright (C) 2022 Wojtek Kosior <koszko@koszko.org>
+#}
+{% extends "info_base.html.jinja" %}
+
+{% block site_policy %}
+ {{ _('info.js_fallback_blocked') }}
+{% endblock %}
diff --git a/src/hydrilla/proxy/policies/info_pages_templates/js_rule_allowed_info.html.jinja b/src/hydrilla/proxy/policies/info_pages_templates/js_rule_allowed_info.html.jinja
new file mode 100644
index 0000000..fe74602
--- /dev/null
+++ b/src/hydrilla/proxy/policies/info_pages_templates/js_rule_allowed_info.html.jinja
@@ -0,0 +1,14 @@
+{#
+SPDX-License-Identifier: CC0-1.0
+
+Proxy info page with information about page with JS allowed by a rule.
+
+This file is part of Hydrilla&Haketilo.
+
+Copyright (C) 2022 Wojtek Kosior <koszko@koszko.org>
+#}
+{% extends "js_rule_info.html.jinja" %}
+
+{% block site_policy %}
+ {{ format_html_with_rule_url(_('info.js_allowed.html.rule{url}_is_used')) }}
+{% endblock %}
diff --git a/src/hydrilla/proxy/policies/info_pages_templates/js_rule_blocked_info.html.jinja b/src/hydrilla/proxy/policies/info_pages_templates/js_rule_blocked_info.html.jinja
new file mode 100644
index 0000000..e84d371
--- /dev/null
+++ b/src/hydrilla/proxy/policies/info_pages_templates/js_rule_blocked_info.html.jinja
@@ -0,0 +1,14 @@
+{#
+SPDX-License-Identifier: CC0-1.0
+
+Proxy info page with information about page with JS blocked by a rule.
+
+This file is part of Hydrilla&Haketilo.
+
+Copyright (C) 2022 Wojtek Kosior <koszko@koszko.org>
+#}
+{% extends "js_rule_info.html.jinja" %}
+
+{% block site_policy %}
+ {{ format_html_with_rule_url(_('info.js_blocked.html.rule{url}_is_used')) }}
+{% endblock %}
diff --git a/src/hydrilla/proxy/policies/info_pages_templates/js_rule_info.html.jinja b/src/hydrilla/proxy/policies/info_pages_templates/js_rule_info.html.jinja
new file mode 100644
index 0000000..b808827
--- /dev/null
+++ b/src/hydrilla/proxy/policies/info_pages_templates/js_rule_info.html.jinja
@@ -0,0 +1,37 @@
+{#
+SPDX-License-Identifier: GPL-3.0-or-later OR CC-BY-SA-4.0
+
+Proxy info page with information about page with JS blocked or allowed by a
+rule - template for firther extending.
+
+This file is part of Hydrilla&Haketilo.
+
+Copyright (C) 2022 Wojtek Kosior <koszko@koszko.org>
+
+Dual licensed under
+* GNU General Public License v3.0 or later and
+* Creative Commons Attribution Share Alike 4.0 International.
+
+You can choose to use either of these licenses or both.
+
+
+I, Wojtek Kosior, thereby promise not to sue for violation of this
+file's licenses. Although I request that you do not make use of this
+code in a proprietary work, I am not going to enforce this in court.
+#}
+{% extends "info_base.html.jinja" %}
+
+{% macro format_html_with_rule_url(msg_fmt) %}
+ {% set url_fmt = 'https://hkt.mitm.it/rules/viewbypattern?pattern={pattern}' %}
+ {{ msg_fmt.format(url=url_fmt.format(pattern=pattern)|e)|safe }}
+{% endmacro %}
+
+{% block main_rest %}
+ <div class="horizontal-separator"></div>
+
+ {{ label(_('info.rule.matched_pattern_label')) }}
+
+ <p>
+ {{ pattern }}
+ </p>
+{% endblock %}
diff --git a/src/hydrilla/proxy/policies/info_pages_templates/payload_info.html.jinja b/src/hydrilla/proxy/policies/info_pages_templates/payload_info.html.jinja
new file mode 100644
index 0000000..a71ca25
--- /dev/null
+++ b/src/hydrilla/proxy/policies/info_pages_templates/payload_info.html.jinja
@@ -0,0 +1,48 @@
+{#
+SPDX-License-Identifier: GPL-3.0-or-later OR CC-BY-SA-4.0
+
+Proxy info page with information about page with payload.
+
+This file is part of Hydrilla&Haketilo.
+
+Copyright (C) 2022 Wojtek Kosior <koszko@koszko.org>
+
+Dual licensed under
+* GNU General Public License v3.0 or later and
+* Creative Commons Attribution Share Alike 4.0 International.
+
+You can choose to use either of these licenses or both.
+
+
+I, Wojtek Kosior, thereby promise not to sue for violation of this
+file's licenses. Although I request that you do not make use of this
+code in a proprietary work, I am not going to enforce this in court.
+#}
+{% extends "info_base.html.jinja" %}
+
+{% macro format_html_with_package_identifier_and_url(msg_fmt) %}
+ {% set package_identifier = payload_data.mapping_identifier|e %}
+ {% set url_fmt = 'https://hkt.mitm.it/package/viewbypayload/{payload_id}/{package_identifier}' %}
+ {%
+ set url = url_fmt.format(
+ payload_id = payload_data.ref.id,
+ package_identifier = package_identifier
+ )
+ %}
+ {{ msg_fmt.format(identifier=package_identifier, url=url|e)|safe }}
+{% endmacro %}
+
+{% block site_policy %}
+ {% set fmt = _('info.payload.html.package_{identifier}{url}_is_used') %}
+ {{ format_html_with_package_identifier_and_url(fmt) }}
+{% endblock %}
+
+{% block main_rest %}
+ <div class="horizontal-separator"></div>
+
+ {{ label(_('info.payload.matched_pattern_label')) }}
+
+ <p>
+ {{ payload_data.pattern }}
+ </p>
+{% endblock %}
diff --git a/src/hydrilla/proxy/policies/info_pages_templates/special_page_info.html.jinja b/src/hydrilla/proxy/policies/info_pages_templates/special_page_info.html.jinja
new file mode 100644
index 0000000..2f7a9d3
--- /dev/null
+++ b/src/hydrilla/proxy/policies/info_pages_templates/special_page_info.html.jinja
@@ -0,0 +1,17 @@
+{#
+SPDX-License-Identifier: CC0-1.0
+
+Proxy info page with information about page handled by special policy.
+
+This file is part of Hydrilla&Haketilo.
+
+Copyright (C) 2022 Wojtek Kosior <koszko@koszko.org>
+#}
+{% extends "info_base.html.jinja" %}
+
+{% block site_policy %}
+ {{ _('info.special_page') }}
+{% endblock %}
+
+{% block options %}
+{% endblock %}
diff --git a/src/hydrilla/proxy/policies/injectable_scripts/popup.js.jinja b/src/hydrilla/proxy/policies/injectable_scripts/popup.js.jinja
index 653b7df..593673b 100644
--- a/src/hydrilla/proxy/policies/injectable_scripts/popup.js.jinja
+++ b/src/hydrilla/proxy/policies/injectable_scripts/popup.js.jinja
@@ -45,6 +45,177 @@ code in a proprietary program, I am not going to enforce this in court.
#}
(function(){
- console.log('TODO: make Haketilo able to actually display a popup')
document.currentScript.remove();
+
+ /*
+ * To slightly decrease the chance of accidental popup breakage we snapshot
+ * methods that other code might redefine.
+ */
+ function get_setter(obj, name) {
+ return Object.getOwnPropertyDescriptor(obj, name).set;
+ }
+
+ const ElementPrototype = [0, 0, 0]
+ .reduce(n => Object.getPrototypeOf(n), document.documentElement);
+
+ const prepend_fun = ElementPrototype.prepend;
+ const setattr_fun = ElementPrototype.setAttribute;
+ const remove_fun = ElementPrototype.remove;
+ const setinner_fun = get_setter(ElementPrototype, "innerHTML");
+ const open_fun = window.open;
+
+ const shortcut = "HKT";
+ const nonce = atob("{{nonce_b64}}");
+ const popup_style = "{{popup_style}}";
+ const popup_html = atob("{{popup_page_b64}}");
+ const popup_container = document.createElement("div");
+ const popup_frame = document.createElement("iframe");
+
+ function make_style(styles_obj) {
+ return Object.entries(styles_obj)
+ .map(([key, val]) => `${key}: ${val} !important`)
+ .join(';');
+ }
+
+ const frame_style = make_style({
+ "position": "absolute",
+ "left": "50%",
+ "top": "50%",
+ "transform": "translate(-50%, -50%)",
+ "display": "block",
+ "visibility": "visible",
+ "min-width": "initial",
+ "width": "600px",
+ "max-width": "calc(100vw - 20px)",
+ "min-height": "initial",
+ "height": "700px",
+ "max-height": "calc(100vh - 20px)",
+ "background-color": "#fff",
+ "opacity": "100%",
+ "margin": 0,
+ "padding": 0,
+ "border": "none",
+ "border-radius": "5px"
+ });
+
+ const container_style = make_style({
+ "position": "fixed",
+ "left": "0",
+ "top": "0",
+ "transform": "initial",
+ "z-index": 2147483647,
+ "display": "block",
+ "visibility": "visible",
+ "min-width": "100vw",
+ "max-width": "100vw",
+ "min-height": "100vh",
+ "max-height": "100vh",
+ "background-color": "#0008",
+ "opacity": "100%",
+ "margin": 0,
+ "padding": 0,
+ "border": "none",
+ "border-radius": 0
+ });
+
+ const popup_blob_opts = {type: "text/html;charset=UTF-8"};
+ const popup_blob = new Blob([popup_html], popup_blob_opts);
+ const popup_url = URL.createObjectURL(popup_blob);
+
+ function show_popup_dialog() {
+ setattr_fun.call(popup_frame, "srcdoc", popup_html);
+ setattr_fun.call(popup_frame, "nonce", nonce);
+ setattr_fun.call(popup_frame, "style", frame_style);
+
+ setattr_fun.call(popup_container, "style", container_style);
+ setinner_fun.call(popup_container, "");
+ prepend_fun.call(popup_container, popup_frame);
+
+ prepend_fun.call(document.body, popup_container);
+ }
+
+ let popup_newtab_wanted = false;
+
+ function show_popup_newtab() {
+ /*
+ * We cannot open popup directly here because browsers block window
+ * creation attempts from "keypress" event handlers. Instead, we set a
+ * flag to have "click" event handler open the popup.
+ */
+ popup_newtab_wanted = true;
+ console.info(`You typed "${shortcut}". Please click anywhere on the page to show Haketilo page information.`);
+ }
+
+ function show_popup() {
+ if (popup_style === "T") {
+ show_popup_newtab();
+ } else {
+ /* popup_syle === "D" */
+ show_popup_dialog();
+ }
+ }
+
+ function hide_popup_dialog() {
+ remove_fun.call(popup_container);
+ }
+
+ let letters_matched = 0;
+
+ function matches_previous(letter) {
+ return letters_matched > 0 && letter === shortcut[letters_matched - 1];
+ }
+
+ function match_letter(letter) {
+ if (letter !== shortcut[letters_matched] && !matches_previous(letter))
+ letters_matched = 0;
+
+ if (letter === shortcut[letters_matched]) {
+ if (++letters_matched === shortcut.length) {
+ letters_matched = 0;
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ function consume_keypress(event) {
+ if (!event.isTrusted)
+ return;
+
+ if (match_letter(event.key))
+ show_popup();
+ }
+
+ function cancel_event(event) {
+ event.stopImmediatePropagation();
+ event.stopPropagation();
+ event.preventDefault();
+ }
+
+ function consume_click(event) {
+ if (!event.isTrusted)
+ return;
+
+ if (popup_style === "T") {
+ if (popup_newtab_wanted) {
+ popup_newtab_wanted = false;
+ cancel_event(event);
+ window.open(
+ popup_url,
+ "_blank",
+ "popup,width=600px,height=700px"
+ );
+ }
+ } else {
+ /* popup_syle === "D" */
+ if (event.target === popup_container) {
+ hide_popup_dialog();
+ cancel_event(event);
+ }
+ }
+ }
+
+ document.addEventListener("keypress", consume_keypress, {capture: true});
+ document.addEventListener("click", consume_click, {capture: true});
})();
diff --git a/src/hydrilla/proxy/policies/misc.py b/src/hydrilla/proxy/policies/misc.py
index 350f3dc..0ff4596 100644
--- a/src/hydrilla/proxy/policies/misc.py
+++ b/src/hydrilla/proxy/policies/misc.py
@@ -29,9 +29,10 @@
.....
"""
+import enum
+import traceback as tb
import dataclasses as dc
import typing as t
-import enum
from abc import ABC, abstractmethod
@@ -44,16 +45,39 @@ from .rule import AllowPolicy, BlockPolicy
class FallbackAllowPolicy(AllowPolicy):
priority = base.PolicyPriority._ONE
+ def make_info_page(self, http_info: http_messages.FullHTTPInfo) \
+ -> t.Optional[str]:
+ template = base.get_info_template('js_fallback_allowed_info.html.jinja')
+ return template.render(url=http_info.request_info.url.orig_url)
+
class FallbackBlockPolicy(BlockPolicy):
priority = base.PolicyPriority._ONE
+ def make_info_page(self, http_info: http_messages.FullHTTPInfo) \
+ -> t.Optional[str]:
+ template = base.get_info_template('js_fallback_blocked_info.html.jinja')
+ return template.render(url=http_info.request_info.url.orig_url)
+
@dc.dataclass(frozen=True)
class ErrorBlockPolicy(BlockPolicy):
- """...."""
error: Exception
+ @property
+ def traceback(self) -> str:
+ lines = tb.format_exception(None, self.error, self.error.__traceback__)
+ return ''.join(lines)
+
+ def make_info_page(self, http_info: http_messages.FullHTTPInfo) \
+ -> t.Optional[str]:
+ template = base.get_info_template('js_error_blocked_info.html.jinja')
+ return template.render(
+ url = http_info.request_info.url.orig_url,
+ settings = self.haketilo_settings,
+ traceback = self.traceback
+ )
+
class MitmItPagePolicy(base.Policy):
"""
diff --git a/src/hydrilla/proxy/policies/payload.py b/src/hydrilla/proxy/policies/payload.py
index 3252c6a..55851cc 100644
--- a/src/hydrilla/proxy/policies/payload.py
+++ b/src/hydrilla/proxy/policies/payload.py
@@ -173,6 +173,13 @@ class PayloadInjectPolicy(PayloadAwarePolicy):
return soup.decode()
+ def make_info_page(self, http_info: http_messages.FullHTTPInfo) \
+ -> t.Optional[str]:
+ return base.get_info_template('payload_info.html.jinja').render(
+ url = http_info.request_info.url.orig_url,
+ payload_data = self.payload_data
+ )
+
class _PayloadHasProblemsError(HaketiloException):
pass
diff --git a/src/hydrilla/proxy/policies/payload_resource.py b/src/hydrilla/proxy/policies/payload_resource.py
index 38cfd21..0d73242 100644
--- a/src/hydrilla/proxy/policies/payload_resource.py
+++ b/src/hydrilla/proxy/policies/payload_resource.py
@@ -261,18 +261,16 @@ class PayloadResourcePolicy(PayloadAwarePolicy):
request_info: http_messages.RequestInfo
) -> MessageInfo:
if path[0] == 'page_init_script.js':
- with base.jinja_lock:
- template = base.jinja_env.get_template(
- 'page_init_script.js.jinja'
- )
- token = self.payload_data.unique_token
- base_url = self._assets_base_url(request_info.url)
- ver_str = json.dumps(haketilo_version)
- js = template.render(
- unique_token_encoded = encode_string_for_js(token),
- assets_base_url_encoded = encode_string_for_js(base_url),
- haketilo_version = encode_string_for_js(ver_str)
- )
+ template = base.get_script_template('page_init_script.js.jinja')
+
+ token = self.payload_data.unique_token
+ base_url = self._assets_base_url(request_info.url)
+ ver_str = json.dumps(haketilo_version)
+ js = template.render(
+ unique_token_encoded = encode_string_for_js(token),
+ assets_base_url_encoded = encode_string_for_js(base_url),
+ haketilo_version = encode_string_for_js(ver_str)
+ )
return http_messages.ResponseInfo.make(
status_code = 200,
diff --git a/src/hydrilla/proxy/policies/rule.py b/src/hydrilla/proxy/policies/rule.py
index 8c5e69b..1f39295 100644
--- a/src/hydrilla/proxy/policies/rule.py
+++ b/src/hydrilla/proxy/policies/rule.py
@@ -67,11 +67,25 @@ class BlockPolicy(base.Policy):
class RuleAllowPolicy(AllowPolicy):
pattern: ParsedPattern
+ def make_info_page(self, http_info: http_messages.FullHTTPInfo) \
+ -> t.Optional[str]:
+ return base.get_info_template('js_rule_allowed_info.html.jinja').render(
+ url = http_info.request_info.url.orig_url,
+ pattern = self.pattern.orig_url
+ )
+
@dc.dataclass(frozen=True)
class RuleBlockPolicy(BlockPolicy):
pattern: ParsedPattern
+ def make_info_page(self, http_info: http_messages.FullHTTPInfo) \
+ -> t.Optional[str]:
+ return base.get_info_template('js_rule_blocked_info.html.jinja').render(
+ url = http_info.request_info.url.orig_url,
+ pattern = self.pattern.orig_url
+ )
+
@dc.dataclass(frozen=True, unsafe_hash=True) # type: ignore[misc]
class RulePolicyFactory(base.PolicyFactory):