Prechádzať zdrojové kódy

Push: send Content-Encoding aes128gcm on every push POST

RFC 8291 section 4 requires the Content-Encoding: aes128gcm header
on every aes128gcm-encrypted body. The push POST in
PushService.deliver_once set TTL, Urgency and Authorization but never
Content-Encoding, so real push services (Mozilla autopush, FCM)
rejected every notification with HTTP 400 - visible to users as
'Test notification failed: HTTP 400'.

Adds the header next to the existing appends and makes it
unregressable: the push bundle's captured-POST protocol checks now
assert Content-Encoding: aes128gcm (plus Urgency and the VAPID
Authorization header) on the global-block push, and the
test-notification round-trip asserts the same header plus TTL on its
own POST.

Verified: ninja build clean; full sequential web_stack_test suite
967 passed / 0 failed (964 on this branch before the three new
checks).
saddle-Clanker-Saddle 1 mesiac pred
rodič
commit
de55b3a745
2 zmenil súbory, kde vykonal 18 pridanie a 1 odobranie
  1. 3 0
      src/Services/PushService.vala
  2. 15 1
      test/bundles/push.py

+ 3 - 0
src/Services/PushService.vala

@@ -656,6 +656,9 @@ namespace ClankerSaddle.Services {
                 new Bytes(body));
             message.request_headers.append("TTL", "60");
             message.request_headers.append("Urgency", "high");
+            // RFC 8291 section 4: every aes128gcm body MUST carry
+            // Content-Encoding or the push service rejects with 400.
+            message.request_headers.append("Content-Encoding", "aes128gcm");
             message.request_headers.append("Authorization",
                 @"vapid t=$(vapid_jwt(job.endpoint)), k=$(vapid_public_key)");
             try {

+ 15 - 1
test/bundles/push.py

@@ -219,6 +219,15 @@ def run(ctx):
     check(block_hit["headers"].get("ttl") == "60"
           and block_hit["headers"].get("content-type") == "application/octet-stream",
           "the push POST carries TTL and the encrypted content type")
+    # RFC 8291 section 4: the aes128gcm body MUST announce its encoding
+    # — push services answer HTTP 400 without it (the test-notification
+    # 400 regression).
+    check(block_hit["headers"].get("content-encoding") == "aes128gcm",
+          "the push POST carries Content-Encoding: aes128gcm (RFC 8291)")
+    check(block_hit["headers"].get("urgency") == "high"
+          and block_hit["headers"].get("authorization", "")
+          .startswith("vapid t="),
+          "the push POST carries Urgency and the VAPID Authorization")
 
     # Task 2: only the OTHER user opted in — start + failure reach them
     # alone. Baseline again before the enqueue (first start fires at
@@ -449,12 +458,17 @@ def run(ctx):
                    and len(push2.hits("/b")) > b_before, timeout=15,
                    what="test notification POSTs") is not None,
           "push_test fires a REAL push to both of the user's endpoints")
-    _, test_payload = decrypt_webpush(push2.hits("/a")[-1], browser_a,
+    test_hit = push2.hits("/a")[-1]
+    _, test_payload = decrypt_webpush(test_hit, browser_a,
                                       browser_a_public, browser_a_auth)
     check(test_payload["title"] == "Test notification"
           and test_payload["url"] == "/",
           "the test payload decrypts with title + url (goes to the dashboard)"
           " (%s)" % test_payload)
+    check(test_hit["headers"].get("content-encoding") == "aes128gcm"
+          and test_hit["headers"].get("ttl") == "60",
+          "the test-notification POST carries Content-Encoding: aes128gcm"
+          " + TTL (the HTTP 400 fix)")
     check("2 browser" in message,
           "the action response reports both deliveries (%s)" % message)
     set_types = [d.get("type") for d in result if d.get("type") == "set"]