From e00d4be6d57ca8282a668f9aec2c835d367c6f69 Mon Sep 17 00:00:00 2001 From: Anders Kaseorg Date: Thu, 25 Jul 2019 13:06:59 -0700 Subject: [PATCH] events: Check last_event_id for validity. This verifies that the client passed a last_event_id that actually came from the queue instead of making up an ID from the future. It turns out one of our tests was making up such an ID, but legitimate clients are expected not to do so. Signed-off-by: Anders Kaseorg --- zerver/tests/test_tornado.py | 2 +- zerver/tornado/event_queue.py | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/zerver/tests/test_tornado.py b/zerver/tests/test_tornado.py index e9bdcb6125..8a0e88fcbb 100644 --- a/zerver/tests/test_tornado.py +++ b/zerver/tests/test_tornado.py @@ -117,7 +117,7 @@ class EventsTestCase(TornadoWebTestCase): event_queue_id = self.create_queue() data = { 'queue_id': event_queue_id, - 'last_event_id': 0, + 'last_event_id': -1, } path = '/json/events?{}'.format(urllib.parse.urlencode(data)) diff --git a/zerver/tornado/event_queue.py b/zerver/tornado/event_queue.py index b7421a0148..969cdd3263 100644 --- a/zerver/tornado/event_queue.py +++ b/zerver/tornado/event_queue.py @@ -240,6 +240,7 @@ class EventQueue: def __init__(self, id: str) -> None: self.queue = deque() # type: ignore # Should be Deque[Dict[str, Any]], but Deque isn't available in Python 3.4 self.next_event_id = 0 # type: int + self.newest_pruned_id = -1 # type: int self.id = id # type: str self.virtual_events = {} # type: Dict[str, Dict[str, Any]] @@ -295,6 +296,7 @@ class EventQueue: # See the comment on pop; that applies here as well def prune(self, through_id: int) -> None: while len(self.queue) != 0 and self.queue[0]['id'] <= through_id: + self.newest_pruned_id = self.queue[0]['id'] self.pop() def contents(self) -> List[Dict[str, Any]]: @@ -522,7 +524,11 @@ def fetch_events(query: Mapping[str, Any]) -> Dict[str, Any]: raise BadEventQueueIdError(queue_id) if user_profile_id != client.user_profile_id: raise JsonableError(_("You are not authorized to get events from this queue")) + if last_event_id < client.event_queue.newest_pruned_id: + raise JsonableError(_("An event newer than %s has already been pruned!") % (last_event_id,)) client.event_queue.prune(last_event_id) + if last_event_id != client.event_queue.newest_pruned_id: + raise JsonableError(_("Event %s was not in this queue") % (last_event_id,)) was_connected = client.finish_current_handler() if not client.event_queue.empty() or dont_block: