on master help sign in
Fix 7 server.knot bugs: hex validation, poll authorization, gossip fromServer check, and message age calculation
commit75f4a12c66b92219c0b3413315d040dfd51a5809 authoriko <mail@iko.soy> dateThu 26 Mar 2026, 18:17 (7 months ago) parent0cabbfd
1 file changed+15−10
server.knot+15−10
server.knot+15−10
@@ -55,7 +55,7 @@ route AuthApi where
5555
5656route ClientApi where
5757 GET {cursor: Int} /poll headers {authorization: Text}
58− -> {events: [PollEvent], cursor: Int}
58+ -> {events: [PollEvent], cursor: Int, authorized: Bool}
5959 = Poll
6060 /messages
6161 POST {to: Text, encryptedBlob: Text, timestamp: Int} / headers {authorization: Text}
@@ -163,7 +163,7 @@ pruneExpired = \t -> do
163163 messages <- *messages
164164 set *messages = do
165165 m <- messages
166− where m.timestamp >= t - messageMaxAge
166+ where m.receivedAt >= t - messageMaxAge
167167 yield m
168168 yield {expiredLocal: (do
169169 p <- presenceBefore
@@ -198,7 +198,7 @@ broadcastOfflineGossip = \t peerServers expiredLocal -> do
198198serve = \req -> case req of
199199
200200 ReqChallenge {pubkey, respond} ->
201− if pubkey == "" || tooLong pubkey maxPubkeyLen
201+ if pubkey == "" || tooLong pubkey maxPubkeyLen || not (isValidHex pubkey)
202202 then respond {challenge: ""}
203203 else do
204204 challenge <- genToken
@@ -297,24 +297,27 @@ serve = \req -> case req of
297297 Just {value: s} -> do
298298 m <- messages
299299 where m.toKey == s.pubkey
300− where m.timestamp >= t - messageMaxAge
300+ where m.receivedAt >= t - messageMaxAge
301301 where m.receivedAt > cursor
302302 yield {id: m.messageId, eventType: "message",
303303 payload: toJson {toKey: m.toKey, fromKey: m.fromKey,
304304 encryptedBlob: m.encryptedBlob,
305305 timestamp: m.timestamp}}
306+ let authorized = case session of
307+ Nothing {} -> False {}
308+ _ -> True {}
306309 let newCursor = case session of
307310 Nothing {} -> -1
308311 _ -> if seqCounter > cursor then seqCounter else cursor
309− yield {events: evts, cursor: newCursor, expiredLocal: pr.expiredLocal, peerServers: pr.peerServers}
310− respond {events: result.events, cursor: result.cursor}
312+ yield {events: evts, cursor: newCursor, authorized: authorized, expiredLocal: pr.expiredLocal, peerServers: pr.peerServers}
313+ respond {events: result.events, cursor: result.cursor, authorized: result.authorized}
311314 broadcastOfflineGossip t result.peerServers result.expiredLocal
312315
313316 SendMessage {to, encryptedBlob, timestamp, authorization, respond} -> do
314317 t <- now
315318 msgId <- genToken
316319 let clampedTs = if timestamp > t then t else if timestamp < t - messageMaxAge then t else timestamp
317− let validInput = not (to == "") && not (encryptedBlob == "") && not (tooLong to maxPubkeyLen) && not (tooLong encryptedBlob maxBlobLen)
320+ let validInput = not (to == "") && not (encryptedBlob == "") && isValidHex to && not (tooLong to maxPubkeyLen) && not (tooLong encryptedBlob maxBlobLen)
318321 result <- atomic do
319322 pr <- pruneExpired t
320323 sessions <- *sessions
@@ -450,13 +453,14 @@ serve = \req -> case req of
450453 t <- now
451454 let authorized = verifyFederationAuth authorization
452455 let cappedHops = if hopsRemaining > 5 then 5 else hopsRemaining
453− let validRequest = authorized && cappedHops >= 1 && not (fromServer == "") && not (tooLong fromServer maxPubkeyLen) && count events <= maxGossipEvents
456+ let validRequest = authorized && cappedHops >= 1 && not (fromServer == "") && not (fromServer == serverHost) && not (tooLong fromServer maxPubkeyLen) && count events <= maxGossipEvents
454457 let onlineEntries = do
455458 e <- events
456459 where validRequest
457460 where e.eventType == "online"
458461 where not (e.server == "")
459462 where not (e.pubkey == "")
463+ where isValidHex e.pubkey
460464 where not (tooLong e.pubkey maxPubkeyLen)
461465 where not (tooLong e.server maxPubkeyLen)
462466 where not (e.server == serverHost)
@@ -469,6 +473,7 @@ serve = \req -> case req of
469473 where e.eventType == "offline"
470474 where not (e.server == "")
471475 where not (e.pubkey == "")
476+ where isValidHex e.pubkey
472477 where not (tooLong e.pubkey maxPubkeyLen)
473478 where not (tooLong e.server maxPubkeyLen)
474479 where not (e.server == serverHost)
@@ -623,7 +628,7 @@ serve = \req -> case req of
623628 let exists = count (do
624629 m <- messages
625630 where m.messageId == messageId
626− where m.timestamp >= t - messageMaxAge
631+ where m.receivedAt >= t - messageMaxAge
627632 yield m) > 0
628633 let shouldInsert = authorized && validOrigin && validFields && not exists
629634 set *seqCounter = if shouldInsert then seqTs else seqCounter
@@ -644,7 +649,7 @@ serve = \req -> case req of
644649 else if count acc >= maxMessageIds then acc
645650 else if count (do a <- acc; where a == mid; yield a) > 0 then acc
646651 else union acc [mid]) [] messageIds
647− let validAck = authorized && originServer == serverHost && not (fromServer == "") && not (fromServer == serverHost) && not (tooLong fromServer maxPubkeyLen) && count messageIds <= maxMessageIds
652+ let validAck = authorized && originServer == serverHost && not (fromServer == "") && not (fromServer == serverHost) && not (tooLong fromServer maxPubkeyLen) && count dedupedIds <= maxMessageIds
648653 pruneResult <- atomic do
649654 pr <- pruneExpired t
650655 messages <- *messages