diff options
Diffstat (limited to 'src/hydrilla/proxy/policies')
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): |