diff --git a/frigate/comms/webpush.py b/frigate/comms/webpush.py index 34a0e57508..45a6edba64 100644 --- a/frigate/comms/webpush.py +++ b/frigate/comms/webpush.py @@ -41,6 +41,20 @@ class PushNotification: ttl: int = 0 +def _build_web_pushers(user: str, subs: list[dict[str, Any]]) -> list[WebPusher]: + """Build pushers for a user's stored subscriptions, skipping unusable ones.""" + pushers: list[WebPusher] = [] + + for sub in subs: + # WebPusher decodes the stored keys and raises on malformed ones + try: + pushers.append(WebPusher(sub)) + except Exception: + logger.warning("Skipping invalid notification subscription for %s", user) + + return pushers + + class WebPushClient(Communicator): """Frigate wrapper for webpush client.""" @@ -82,9 +96,9 @@ class WebPushClient(Communicator): User.select(User.username, User.notification_tokens).dicts().iterator() ) for user in users: - self.web_pushers[user["username"]] = [] - for sub in user["notification_tokens"]: - self.web_pushers[user["username"]].append(WebPusher(sub)) + self.web_pushers[user["username"]] = _build_web_pushers( + user["username"], user["notification_tokens"] + ) # notification and auth config updater self.global_config_subscriber = ConfigSubscriber("config/") @@ -142,10 +156,7 @@ class WebPushClient(Communicator): User.username == user ).execute() - self.web_pushers[user] = [] - - for sub in user_subs: - self.web_pushers[user].append(WebPusher(sub)) + self.web_pushers[user] = _build_web_pushers(user, user_subs) logger.info( f"Cleaned up {len(expired)} notification subscriptions for {user}" diff --git a/frigate/test/test_webpush_registration.py b/frigate/test/test_webpush_registration.py index 16e0129152..733659489a 100644 --- a/frigate/test/test_webpush_registration.py +++ b/frigate/test/test_webpush_registration.py @@ -1,8 +1,10 @@ """Tests for push notification subscription validation.""" import unittest +from base64 import urlsafe_b64encode from frigate.api.notification import _validate_push_endpoint, _validate_subscription +from frigate.comms.webpush import _build_web_pushers VALID_ENDPOINTS = [ "https://fcm.googleapis.com/fcm/send/dGhpcy1pcy1hLXRva2Vu", @@ -148,3 +150,16 @@ class TestValidateSubscription(unittest.TestCase): if __name__ == "__main__": unittest.main() + + +class TestBuildWebPushers(unittest.TestCase): + def test_skips_subscriptions_with_malformed_keys(self): + bad = _subscription(VALID_ENDPOINTS[0]) + good = _subscription(VALID_ENDPOINTS[0]) + good["keys"]["p256dh"] = urlsafe_b64encode(b"\x04" + bytes(64)).decode() + + with self.assertLogs("frigate.comms.webpush", level="WARNING"): + pushers = _build_web_pushers("viewer", [bad, good]) + + self.assertEqual(len(pushers), 1) + self.assertEqual(pushers[0].receiver_key, b"\x04" + bytes(64))