Project

General

Profile

« Previous | Next » 

Revision 7f0b5ded

Added by koszko over 1 year ago

don't double-modify response headers retrieved from cache

View differences:

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