diff --git a/policies/common/common.rego b/policies/common/common.rego index 17e9aa82d..198b0be61 100644 --- a/policies/common/common.rego +++ b/policies/common/common.rego @@ -1,3 +1,4 @@ +# Copyright 2025, 2026 Element Creations Ltd. # Copyright 2025 New Vector Ltd. # # SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial @@ -68,22 +69,27 @@ requester_banned(requester, policy) if ip_in_list(requester.ip_address, policy.b requester_banned(requester, policy) if matches_string_constraints(requester.user_agent, policy.banned_user_agents) +# The fields are `null` (not absent) when unknown: a bare `requester.user_agent` +# check lets `null` through to `sprintf`, which aborts the WASM evaluation. The +# helper rule is undefined for both null and absent values, which `not` handles. +requester_has(requester, key) if is_string(requester[key]) + format_requester(requester) := "unknown" if { - not requester.ip_address - not requester.user_agent + not requester_has(requester, "ip_address") + not requester_has(requester, "user_agent") } format_requester(requester) := sprintf("%s / %s", [requester.ip_address, requester.user_agent]) if { - requester.ip_address - requester.user_agent + requester_has(requester, "ip_address") + requester_has(requester, "user_agent") } -format_requester(requester) := sprintf("%s", [requester.ip_address]) if { - requester.ip_address - not requester.user_agent +format_requester(requester) := requester.ip_address if { + requester_has(requester, "ip_address") + not requester_has(requester, "user_agent") } -format_requester(requester) := sprintf("%s", [requester.user_agent]) if { - not requester.ip_address - requester.user_agent +format_requester(requester) := requester.user_agent if { + not requester_has(requester, "ip_address") + requester_has(requester, "user_agent") } diff --git a/policies/common/common_test.rego b/policies/common/common_test.rego index 18770fdc7..982ffdb40 100644 --- a/policies/common/common_test.rego +++ b/policies/common/common_test.rego @@ -46,4 +46,18 @@ test_requester_banned if { {"ip_address": "192.168.1.1", "user_agent": "Mozilla/5.0"}, {"banned_ips": ["192.168.1.1"]}, ) + + # A missing user agent is serialized as null, not omitted + common.requester_banned( + {"ip_address": "192.168.1.1", "user_agent": null}, + {"banned_ips": ["192.168.1.1"]}, + ) +} + +test_format_requester if { + common.format_requester({"ip_address": "192.168.1.1", "user_agent": "Mozilla/5.0"}) == "192.168.1.1 / Mozilla/5.0" + common.format_requester({"ip_address": "192.168.1.1", "user_agent": null}) == "192.168.1.1" + common.format_requester({"ip_address": null, "user_agent": "Mozilla/5.0"}) == "Mozilla/5.0" + common.format_requester({"ip_address": null, "user_agent": null}) == "unknown" + common.format_requester({}) == "unknown" }