tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

clear-site-data-cache.py (7922B)


      1 """
      2 Loaded in Step 2/4/Optional 6 (/clear-site-data/clear-cache.https.html)
      3 Sending Message for Step 3/5/Optional 7 (/clear-site-data/clear-cache.https.html)
      4 """
      5 import uuid
      6 import random
      7 
      8 def generate_png(width, height, color=(0, 0, 0)):
      9    import zlib
     10    import struct
     11    def chunk(chunk_type, data):
     12        return (
     13            struct.pack(">I", len(data)) +
     14            chunk_type +
     15            data +
     16            struct.pack(">I", zlib.crc32(chunk_type + data) & 0xffffffff)
     17        )
     18 
     19    # PNG signature
     20    png = b"\x89PNG\r\n\x1a\n"
     21 
     22    # IHDR chunk
     23    ihdr = struct.pack(">IIBBBBB", width, height, 8, 2, 0, 0, 0)
     24    png += chunk(b'IHDR', ihdr)
     25 
     26    # IDAT chunk: RGB pixels
     27    row = b'\x00' + bytes(color * width)
     28    raw = row * height
     29    idat = zlib.compress(raw)
     30    png += chunk(b'IDAT', idat)
     31 
     32    # IEND chunk
     33    png += chunk(b'IEND', b'')
     34    return png
     35 
     36 def main(request, response):
     37    # type of response:
     38    # - General purpose: returns random uuid or forwards uuid from iframe
     39    #   - "single_html": Main page html file with a different postMessage uuid on each response
     40    # - Pages for simple testing normal http cache:
     41    #   - "json": Json that always responds with a different uuid in a single-element array
     42    #   - "html_embed_json": Main page html that embeds a cachable version of the above json
     43    # - Pages for testing specialized caches
     44    #   - "image": Image that returns random dimensions up to 1024x1024 each time
     45    #   - "html_embed_image": Main page html that embeds a cachable version of the above image
     46    #   - "css": Style sheet with random uuid variable
     47    #   - "html_embed_css": Main page html that embeds a cachable version of the above css
     48    #   - "js": Script that returns a different uuid each time
     49    #   - "html_embed_js": Main page html that embeds a cachable version of the above js file
     50    response_type = request.GET.first(b"response")
     51 
     52    cache_helper = request.GET.first(b"cache_helper")
     53 
     54    # force enable caching when present or force disable if not
     55    cache = b"cache" in request.GET
     56    clear = None
     57    if b"clear" in request.GET:
     58        clear = request.GET.first(b"clear")
     59    if b"clear_first" in request.GET:
     60        if request.server.stash.take(cache_helper) is None:
     61            clear = request.GET.first(b"clear_first")
     62            request.server.stash.put(cache_helper, ())
     63 
     64    headers = []
     65    if response_type == b"json":
     66        headers += [(b"Content-Type", b"application/json")]
     67    elif response_type == b"image":
     68        headers += [(b"Content-Type", b"image/png")]
     69    elif response_type == b"css":
     70        headers += [(b"Content-Type", b"text/css")]
     71    elif response_type == b"js":
     72        headers += [(b"Content-Type", b"text/javascript")]
     73    else:
     74        headers += [(b"Content-Type", b"text/html")]
     75 
     76    if cache:
     77        headers += [(b"cache-control", b"public, max-age=31536000, immutable")]
     78    else:
     79        headers += [(b"cache-control", b"no-store")]
     80 
     81    if clear is not None:
     82        if clear == b"all":
     83            headers += [(b"Clear-Site-Data", b'"*"')]
     84        else:
     85            headers += [(b"Clear-Site-Data", b'"' + clear + b'"')]
     86 
     87    if response_type == b"single_html":
     88        iframe = ""
     89        if b"iframe" in request.GET:
     90            # forward message from iframe to opener
     91            iframe_url = request.GET.first(b"iframe").decode()
     92            content = f'''
     93                <script>
     94                    // forward iframe uuid to opener
     95                    window.addEventListener('message', function(event) {{
     96                        if(window.opener) {{
     97                            window.opener.postMessage(event.data, "*");
     98                        }} else {{
     99                            window.parent.postMessage(event.data, "*");
    100                        }}
    101                        window.close();
    102                    }});
    103                </script>
    104                <br>
    105                {request.url}<br>
    106                {iframe_url}<br>
    107                <iframe src="{iframe_url}"></iframe>
    108                </body>
    109            '''
    110        else:
    111            # send unique UUID. Cache got cleared when uuids don't match.
    112            u = uuid.uuid4()
    113            content = f'''
    114                <script>
    115                    if(window.opener) {{
    116                        window.opener.postMessage("{u}", "*");
    117                    }} else {{
    118                        window.parent.postMessage("{u}", "*");
    119                    }}
    120                    window.close();
    121                </script>
    122                <body>
    123                    {request.url}
    124                </body>'''
    125    elif response_type == b"json":
    126        # send unique UUID. helper for below "html_embed_json"
    127        content = f'''["{uuid.uuid4()}"]'''
    128    elif response_type == b"html_embed_json":
    129        url = request.url_parts.path + "?response=json&cache&cache_helper=" + cache_helper.decode()
    130        content = f'''
    131            <script>
    132                fetch("{url}")
    133                    .then(response => response.json())
    134                    .then(uuid => {{
    135                        window.opener.postMessage(uuid[0], "*");
    136                        window.close();
    137                    }});
    138            </script>
    139            <body>
    140                {request.url}<br>
    141                {url}
    142            </body>'''
    143    elif response_type == b"image":
    144        # send uniquely sized images, because that info can be retrived from html and definitly using the image cache
    145        # helper for below "html_embed_image"
    146        content = generate_png(random.randint(1, 1024), random.randint(1, 1024))
    147    elif response_type == b"html_embed_image":
    148        urls = [request.url_parts.path + "?response=image&cache&cache_helper=" + cache_helper.decode() + "&img=" + str(i) for i in range(2)]
    149        content = f'''
    150            <!DOCTYPE html>
    151            <script>
    152                addEventListener("load", () => {{
    153                    let img1 = document.getElementById("randomess1");
    154                    let img2 = document.getElementById("randomess2");
    155                    let id = img1.naturalWidth + "x" + img1.naturalHeight;
    156                    id += "-" + img2.naturalWidth + "x" + img2.naturalHeight
    157                    window.opener.postMessage(id, "*");
    158                    window.close();
    159                }})
    160            </script>
    161            <body>
    162                {request.url}<br>
    163                <img id="randomess1" src="{urls[0]}"></img><br>
    164                <img id="randomess2" src="{urls[1]}"></img><br>
    165            </body>'''
    166    elif response_type == b"css":
    167        # send unique UUID. helper for below "html_embed_css"
    168        content = f'''
    169        :root {{
    170            --uuid: "{uuid.uuid4()}"
    171        }}'''
    172    elif response_type == b"html_embed_css":
    173        url = request.url_parts.path + "?response=css&cache&cache_helper=" + cache_helper.decode()
    174        content = f'''
    175            <!DOCTYPE html>
    176            <link rel="stylesheet" href="{url}">
    177            <script>
    178                let computed = getComputedStyle(document.documentElement);
    179                let uuid = computed.getPropertyValue("--uuid").trim().replaceAll('"', '');
    180                window.opener.postMessage(uuid, "*");
    181                window.close();
    182            </script>
    183            <body>
    184                {request.url}<br>
    185                {url}
    186            </body>'''
    187    elif response_type == b"js":
    188        # send unique UUID. helper for below "html_embed_js"
    189        content = f'''
    190            window.opener.postMessage("{uuid.uuid4()}", "*");
    191            window.close();
    192        '''
    193    elif response_type == b"html_embed_js":
    194        url = request.url_parts.path + "?response=js&cache&cache_helper=" + cache_helper.decode()
    195        content = f'''
    196            <script src="{url}"></script>
    197            <body>
    198                {request.url}<br>
    199                {url}
    200            </body>'''
    201 
    202    return 200, headers, content