mirror of
https://github.com/blakeblackshear/frigate.git
synced 2026-10-10 08:42:49 +03:00
skip malformed webpush subscriptions instead of failing startup (#24604)
Registration only checked that the p256dh and auth keys were non-empty strings, so any authenticated user could store a subscription that WebPusher refuses to construct. WebPushClient built one WebPusher per stored subscription with no error handling during init_dispatcher, so a single bad row raised before the camera processes and API started, and it did so again on every restart because the row was reloaded from the database. Building the pushers now logs and skips a subscription that fails to construct, both at startup and when expired subscriptions are cleaned up.
This commit is contained in:
@@ -41,6 +41,20 @@ class PushNotification:
|
|||||||
ttl: int = 0
|
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):
|
class WebPushClient(Communicator):
|
||||||
"""Frigate wrapper for webpush client."""
|
"""Frigate wrapper for webpush client."""
|
||||||
|
|
||||||
@@ -82,9 +96,9 @@ class WebPushClient(Communicator):
|
|||||||
User.select(User.username, User.notification_tokens).dicts().iterator()
|
User.select(User.username, User.notification_tokens).dicts().iterator()
|
||||||
)
|
)
|
||||||
for user in users:
|
for user in users:
|
||||||
self.web_pushers[user["username"]] = []
|
self.web_pushers[user["username"]] = _build_web_pushers(
|
||||||
for sub in user["notification_tokens"]:
|
user["username"], user["notification_tokens"]
|
||||||
self.web_pushers[user["username"]].append(WebPusher(sub))
|
)
|
||||||
|
|
||||||
# notification and auth config updater
|
# notification and auth config updater
|
||||||
self.global_config_subscriber = ConfigSubscriber("config/")
|
self.global_config_subscriber = ConfigSubscriber("config/")
|
||||||
@@ -142,10 +156,7 @@ class WebPushClient(Communicator):
|
|||||||
User.username == user
|
User.username == user
|
||||||
).execute()
|
).execute()
|
||||||
|
|
||||||
self.web_pushers[user] = []
|
self.web_pushers[user] = _build_web_pushers(user, user_subs)
|
||||||
|
|
||||||
for sub in user_subs:
|
|
||||||
self.web_pushers[user].append(WebPusher(sub))
|
|
||||||
|
|
||||||
logger.info(
|
logger.info(
|
||||||
f"Cleaned up {len(expired)} notification subscriptions for {user}"
|
f"Cleaned up {len(expired)} notification subscriptions for {user}"
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
"""Tests for push notification subscription validation."""
|
"""Tests for push notification subscription validation."""
|
||||||
|
|
||||||
import unittest
|
import unittest
|
||||||
|
from base64 import urlsafe_b64encode
|
||||||
|
|
||||||
from frigate.api.notification import _validate_push_endpoint, _validate_subscription
|
from frigate.api.notification import _validate_push_endpoint, _validate_subscription
|
||||||
|
from frigate.comms.webpush import _build_web_pushers
|
||||||
|
|
||||||
VALID_ENDPOINTS = [
|
VALID_ENDPOINTS = [
|
||||||
"https://fcm.googleapis.com/fcm/send/dGhpcy1pcy1hLXRva2Vu",
|
"https://fcm.googleapis.com/fcm/send/dGhpcy1pcy1hLXRva2Vu",
|
||||||
@@ -148,3 +150,16 @@ class TestValidateSubscription(unittest.TestCase):
|
|||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
unittest.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))
|
||||||
|
|||||||
Reference in New Issue
Block a user