Revision 7f0b5ded
Added by koszko over 1 year ago
test/haketilo_test/unit/test_webrequest.py | ||
---|---|---|
24 | 24 |
from ..script_loader import load_script |
25 | 25 |
from .utils import are_scripts_allowed |
26 | 26 |
|
27 |
allowed_url = 'https://site.with.scripts.allow.ed/' |
|
28 |
blocked_url = 'https://site.with.scripts.block.ed/' |
|
29 |
payload_url = 'https://site.with.paylo.ad/' |
|
30 |
|
|
27 | 31 |
def webrequest_js(): |
28 | 32 |
return (load_script('background/webrequest.js', |
29 | 33 |
'#IMPORT common/patterns_query_tree.js AS pqt') + |
... | ... | |
34 | 38 |
default_allow = {name: "default_allow", value: true}; |
35 | 39 |
|
36 | 40 |
// Rule to block scripts. |
37 |
pqt.register(tree, "https://site.with.scripts.block.ed/***",
|
|
41 |
pqt.register(tree, "%(blocked)s***",
|
|
38 | 42 |
"~allow", 0); |
39 | 43 |
|
40 | 44 |
// Rule to allow scripts, but overridden by payload assignment. |
41 |
pqt.register(tree, "https://site.with.paylo.ad/***", "~allow", 1);
|
|
42 |
pqt.register(tree, "https://site.with.paylo.ad/***",
|
|
43 |
"somemapping", {identifier: "someresource"});
|
|
45 |
pqt.register(tree, "%(payload)s***", "~allow", 1);
|
|
46 |
pqt.register(tree, "%(payload)s***", "somemapping",
|
|
47 |
{identifier: "someresource"}); |
|
44 | 48 |
|
45 | 49 |
// Mock stream_filter. |
46 | 50 |
stream_filter.apply = (details, headers, policy) => headers; |
51 |
''' % {'blocked': blocked_url, 'payload': payload_url}) |
|
52 |
|
|
53 |
def webrequest_js_start_called(): |
|
54 |
return webrequest_js() + ';\nstart("somesecret");' |
|
55 |
|
|
56 |
ext_url = 'moz-extension://49de6ce9-49fc-49e1-8102-7ef35286389c/html/settings.html' |
|
57 |
prefix = 'X-Haketilo-' + sha256(ext_url.encode()).digest().hex() |
|
58 |
|
|
59 |
# Prepare a list of headers as could be sent by a website. |
|
60 |
sample_csp_header = { |
|
61 |
'name': 'Content-Security-Policy', |
|
62 |
'value': "script-src 'self';" |
|
63 |
} |
|
64 |
sample_csp_header_idx = 7 |
|
65 |
|
|
66 |
sample_headers = [ |
|
67 |
{'name': 'Content-Type', 'value': 'text/html;charset=utf-8'}, |
|
68 |
{'name': 'Content-Length', 'value': '61954'}, |
|
69 |
{'name': 'Content-Language', 'value': 'en'}, |
|
70 |
{'name': 'Expires', 'value': 'Mon, 12 Mar 2012 11:04...'}, |
|
71 |
{'name': 'Last-Modified', 'value': 'Fri, 26 Jul 2013 22:50...'}, |
|
72 |
{'name': 'Cache-Control', 'value': 'max-age=0, s-maxage=86...'}, |
|
73 |
{'name': 'Age', 'value': '224'}, |
|
74 |
{'name': 'Server', 'value': 'nginx/1.1.19'}, |
|
75 |
{'name': 'Date', 'value': 'Thu, 10 Mar 2022 12:09...'} |
|
76 |
] |
|
77 |
|
|
78 |
sample_headers.insert(sample_csp_header_idx, sample_csp_header) |
|
79 |
|
|
80 |
# Prepare a list of headers as would be crafted by Haketilo when there is a |
|
81 |
# payload to inject. |
|
82 |
nonce_source = f'somemapping:someresource:{payload_url}:somesecret'.encode() |
|
83 |
nonce = f'nonce-{sha256(nonce_source).digest().hex()}' |
|
84 |
|
|
85 |
payload_csp_header = { |
|
86 |
'name': f'Content-Security-Policy', |
|
87 |
'value': ("prefetch-src 'none'; script-src-attr 'none'; " |
|
88 |
f"script-src '{nonce}'; script-src-elem '{nonce}';") |
|
89 |
} |
|
90 |
|
|
91 |
sample_payload_headers = [ |
|
92 |
*sample_headers, |
|
93 |
{'name': prefix, 'value': ':)'}, |
|
94 |
payload_csp_header |
|
95 |
] |
|
96 |
|
|
97 |
sample_payload_headers[sample_csp_header_idx] = { |
|
98 |
**sample_csp_header, |
|
99 |
'name': f'{prefix}-{sample_csp_header["name"]}', |
|
100 |
} |
|
101 |
|
|
102 |
# Prepare a list of headers as would be crafted by Haketilo when scripts are |
|
103 |
# blocked. |
|
104 |
sample_blocked_headers = [*sample_payload_headers] |
|
105 |
sample_blocked_headers.pop() |
|
106 |
sample_blocked_headers.append(sample_csp_header) |
|
107 |
sample_blocked_headers.append({ |
|
108 |
'name': f'Content-Security-Policy', |
|
109 |
'value': ("prefetch-src 'none'; script-src-attr 'none'; " |
|
110 |
f"script-src 'none'; script-src-elem 'none';") |
|
111 |
}) |
|
112 |
|
|
113 |
@pytest.mark.get_page('https://gotmyowndoma.in') |
|
114 |
@pytest.mark.parametrize('params', [ |
|
115 |
(sample_headers, allowed_url), |
|
116 |
(sample_blocked_headers, blocked_url), |
|
117 |
(sample_payload_headers, payload_url), |
|
118 |
]) |
|
119 |
def test_webrequest_on_headers_received(driver, execute_in_page, params): |
|
120 |
"""Unit-test the on_headers_received() function.""" |
|
121 |
headers_out, url = params |
|
122 |
|
|
123 |
execute_in_page( |
|
124 |
'''{ |
|
125 |
// Mock browser object. |
|
126 |
const url = arguments[0]; |
|
127 |
this.browser = {runtime: {getURL: () => url}}; |
|
128 |
}''', |
|
129 |
ext_url) |
|
130 |
|
|
131 |
execute_in_page(webrequest_js()) |
|
132 |
|
|
133 |
execute_in_page('secret = "somesecret";') |
|
134 |
|
|
135 |
for headers_in in [ |
|
136 |
sample_headers, |
|
137 |
sample_blocked_headers, |
|
138 |
sample_payload_headers |
|
139 |
]: |
|
140 |
details = {'url': url, 'responseHeaders': headers_in, 'fromCache': True} |
|
141 |
res = execute_in_page('returnval(on_headers_received(arguments[0]));', |
|
142 |
details) |
|
47 | 143 |
|
48 |
// Mock secret and start webrequest operations. |
|
49 |
start("somesecret"); |
|
50 |
''') |
|
144 |
assert res == {'responseHeaders': headers_out} |
|
51 | 145 |
|
52 |
@pytest.mark.ext_data({'background_script': webrequest_js}) |
|
146 |
@pytest.mark.ext_data({'background_script': webrequest_js_start_called})
|
|
53 | 147 |
@pytest.mark.usefixtures('webextension') |
54 |
def test_on_headers_received(driver, execute_in_page): |
|
148 |
def test_webrequest_real_pages(driver, execute_in_page): |
|
149 |
""" |
|
150 |
Test webRequest-based header modifications by loading actual pages and |
|
151 |
attempting to run scripts within them. |
|
152 |
""" |
|
55 | 153 |
for attempt in range(10): |
56 | 154 |
driver.get('https://site.with.scripts.block.ed/') |
57 | 155 |
|
... | ... | |
59 | 157 |
break |
60 | 158 |
assert attempt != 9 |
61 | 159 |
|
62 |
driver.get('https://site.with.scripts.allow.ed/')
|
|
160 |
driver.get(allowed_url)
|
|
63 | 161 |
assert are_scripts_allowed(driver) |
64 | 162 |
|
65 |
driver.get('https://site.with.paylo.ad/')
|
|
163 |
driver.get(payload_url)
|
|
66 | 164 |
assert not are_scripts_allowed(driver) |
67 | 165 |
source = 'somemapping:someresource:https://site.with.paylo.ad/index.html:somesecret' |
68 | 166 |
assert are_scripts_allowed(driver, sha256(source.encode()).digest().hex()) |
Also available in: Unified diff
don't double-modify response headers retrieved from cache