on master help sign in
Fix SSRF via port-only hostnames, ackedIds batch eviction, and remove dead code

- Add check for hostnames starting with ':' (e.g. ".:8080") in isBadServerName to prevent SSRF - Replace single-eviction logic with evictOldestAckedIds that removes N oldest entries when batch acks exceed maxAckedIds - Remove unused containsDoubleColon function (covered by other checks)

commit8815b21f2402c85db6d4c88d43a27165dc08f67c authoriko <mail@iko.soy> dateTue 31 Mar 2026, 11:39 (7 months ago) parent9a8af83
1 file changed+15−14
server.knot+15−14
server.knot+15−14
@@ -190,11 +190,6 @@ isCgnatRange = \s ->
190190 then True {}
191191 else False {}
192192
193−containsDoubleColon = \s ->
194− if s == "" then False {}
195− else if take 1 s == ":" && take 1 (drop 1 s) == ":" then True {}
196− else containsDoubleColon (drop 1 s)
197−
198193containsColon = \s ->
199194 if s == "" then False {}
200195 else if take 1 s == ":" then True {}
@@ -275,6 +270,7 @@ isBadServerName = \s ->
275270 let sn = toLower (stripTrailingDot s)
276271 sn == ""
277272 || take 1 sn == "."
273+ || take 1 sn == ":"
278274 || containsBadUrlChar sn
279275 || sn == "localhost" || take 10 sn == "localhost:"
280276 || sn == "localhost.localdomain" || take 22 sn == "localhost.localdomain:"
@@ -337,6 +333,16 @@ verifyMaintenanceAuth = \authorization ->
337333
338334clampTtl = \ttl -> if ttl < 1 then 1 else if ttl > maxPresenceTtl then maxPresenceTtl else ttl
339335
336+evictOldestAckedIds = \items n ->
337+ if n <= 0 then items
338+ else
339+ let oldest = fold (\acc a -> if acc.ackedAt == -1 || a.ackedAt < acc.ackedAt then a else acc) {messageId: "", toKey: "", ackedAt: -1, originServer: ""} items
340+ in if oldest.ackedAt == -1 then items
341+ else evictOldestAckedIds (fold (\acc a ->
342+ if not acc.removed && a == oldest
343+ then {removed: True {}, items: acc.items}
344+ else {removed: acc.removed, items: union acc.items [a]}) {removed: False {}, items: []} items).items (n - 1)
345+
340346safeCompareLoop = \s1 s2 result ->
341347 if s2 == ""
342348 then result + (if s1 == "" then 0 else 1)
@@ -746,15 +752,10 @@ serve = \req -> case req of
746752 yield {messageId: m.messageId, toKey: m.toKey, ackedAt: t, originServer: m.originServer}
747753 ackedIds <- *ackedIds
748754 let combinedAckedIds = union ackedIds ackedFederated
749− let oldestAckedAt = if count combinedAckedIds > maxAckedIds
750− then fold (\acc a -> if acc == -1 || a.ackedAt < acc then a.ackedAt else acc) -1 combinedAckedIds
751− else -1
752− set *ackedIds = if oldestAckedAt >= 0
753− then (fold (\acc a ->
754− if not acc.removed && a.ackedAt == oldestAckedAt
755− then {removed: True {}, items: acc.items}
756− else {removed: acc.removed, items: union acc.items [a]}) {removed: False {}, items: []} combinedAckedIds).items
757− else combinedAckedIds
755+ let excessCount = count combinedAckedIds - maxAckedIds
756+ set *ackedIds = if excessCount <= 0
757+ then combinedAckedIds
758+ else evictOldestAckedIds combinedAckedIds excessCount
758759 let ok = case session of
759760 Nothing {} -> False {}
760761 _ -> True {}