diff --git a/exodus_gw/routers/cdn.py b/exodus_gw/routers/cdn.py index 638ce99e..84c30d4b 100644 --- a/exodus_gw/routers/cdn.py +++ b/exodus_gw/routers/cdn.py @@ -30,7 +30,8 @@ def build_policy(url: str, expiration: datetime): datelessthan = int(datetime2timestamp(expiration)) condition = {"DateLessThan": {"AWS:EpochTime": datelessthan}} - payload = [("Resource", url), ("Condition", condition)] + # If URL has querystring, the '?' must be escaped. + payload = [("Resource", url.replace("?", "\\?")), ("Condition", condition)] policy = {"Statement": [OrderedDict(payload)]} return json.dumps(policy, separators=(",", ":")).encode("utf-8") @@ -136,12 +137,12 @@ def sign_url(url: str, settings: Settings, env: Environment, username: str): policy = build_policy(dest_url, signature_expires) signature = rsa_signer(env.cdn_private_key, policy) - params = [ - f"Expires={int(datetime2timestamp(signature_expires))}", - f"Signature={cf_b64(signature).decode('utf8')}", - f"Key-Pair-Id={env.cdn_key_id}", - ] - return f"{dest_url}&{'&'.join(params)}" + return ( + f"{dest_url}" + f"&Expires={int(datetime2timestamp(signature_expires))}" + f"&Signature={cf_b64(signature).decode('utf8')}" + f"&Key-Pair-Id={env.cdn_key_id}" + ) Url = Path( diff --git a/tests/routers/test_cdn.py b/tests/routers/test_cdn.py index 8a0fad6d..24900733 100644 --- a/tests/routers/test_cdn.py +++ b/tests/routers/test_cdn.py @@ -14,6 +14,23 @@ from exodus_gw.settings import get_environment +@freeze_time("2022-02-16") +def test_build_policy(): + expiration = datetime.now(timezone.utc) + timedelta(seconds=720) + url = "https://some.host/path/to/file?token=1234" + + out = cdn.build_policy(url, expiration) + assert json.loads(out) == { + "Statement": [ + { + # Querystring delimiter should be escaped + "Resource": "https://some.host/path/to/file\?token=1234", + "Condition": {"DateLessThan": {"AWS:EpochTime": 1644970320}}, + } + ] + } + + @freeze_time("2022-02-16") def test_generate_cf_cookies(monkeypatch, dummy_private_key, caplog): monkeypatch.setenv("EXODUS_GW_CDN_PRIVATE_KEY_TEST", dummy_private_key) @@ -66,7 +83,7 @@ def test_cdn_redirect_(monkeypatch, dummy_private_key, caplog): "http://localhost:8049/_/cookie/some/url?" f"CloudFront-Cookies={expected_cookies}&" "Expires=1644971400&" - "Signature=DxQExeKUk0OJ~qafWOIow1OM8Nil9x4JBjpgtODY1AoIuH-FcW4nt~AcAQmJ1WHRqYIuC79INWk9RTyOokj-Ao6e6i5r6AcPKvhTTyOgRkg9Ywfzf~fUdBENi3k9q4sWgbvND5kiZRZwj3DBc4s0bX82rYYuuSGnjNyjshYhlVU_&" + "Signature=AHmdCLzDTcrGZYe4Psdf8bcIeqSPqdEqAzB~b36kUdqdqAgpxEbtxAQSzntt2ZsHPJqOnPv9rRYMBWaIzRlWkYYZwVVJPyeWtiHUPK9R6AaNE9lhoL5J87lw3RpdsasObuGUrw0HTd7z-c2D-r9Nd-ZpS~LYSDH7PaNw0psY65Y_&" "Key-Pair-Id=XXXXXXXXXXXXXX" )