diff --git a/src/inkwell/client_dist.py b/src/inkwell/client_dist.py index 465f9cc..185d13b 100644 --- a/src/inkwell/client_dist.py +++ b/src/inkwell/client_dist.py @@ -424,6 +424,10 @@ async def send_artifact(root: Path, platform: Platform, found: dict) -> Response root, platform.artifact, mimetype=platform.mimetype, add_etags=False, conditional=False ) response.set_etag(found["sha256"]) + # Only behind a login, so no shared cache may keep a copy (family idea #5105, + # practice 11). Quart marks every file it sends `public`. + response.cache_control.public = False + response.cache_control.private = True # Without this some browsers try to render it, and Android's download handler # wants a filename to hand to the package installer. response.headers["Content-Disposition"] = f'attachment; filename="{platform.artifact}"' diff --git a/tests/test_client_dist.py b/tests/test_client_dist.py index 8dc3095..f9c836e 100644 --- a/tests/test_client_dist.py +++ b/tests/test_client_dist.py @@ -448,6 +448,15 @@ async def test_the_etag_is_the_sha256_the_sidecar_records(app): assert body == PAYLOAD +async def test_signed_in_bytes_are_never_cached_by_a_shared_cache(app): + """Quart sends files `public`; behind a login that would let a proxy hand one + account's download to anyone.""" + resp, _ = await _send(app, {}) + directives = {d.strip().split("=")[0] for d in resp.headers["Cache-Control"].split(",")} + assert "private" in directives + assert "public" not in directives + + async def test_a_range_request_gets_just_that_range(app): resp, body = await _send(app, {"Range": "bytes=4-9"}) assert resp.status_code == 206