Risk scoring has a new zone setting, challenge.risk.lie_block: lying browsers. It catches a challenge when two independent checks on the server both say the client lies about its browser: the TLS ClientHello doesn't match the browser in the User-Agent (tls_mismatch), the Sec-CH-UA client hints contradict it too (client_hints_mismatch), and the handshake comes from no browser's TLS stack at all.
Each check alone can come from a real person: a corporate proxy that inspects TLS, a User-Agent switcher, DevTools device emulation. That's why neither blocks on its own, and why the rule only fires when the TLS stack isn't a browser's. Firefox, Safari, Android WebViews and browsers on iOS are never asked for client hints.
It works on the challenge page and the Verify widget, when the challenge is issued and when it is solved. It is off by default. In log it only counts; in enforce it answers 403, the same as the score's block level, whatever the score and even with the block threshold at 0. It needs risk scoring in log or enforce.
Turn it on in Security → Verification → Risk scoring → Block lying browsers, with PATCH …/edge and {"challenge": {"risk": {"lie_block": "log"}}}, openwaf-cloud ctl zones set example.com challenge.risk.lie_block=log or the MCP tool update_settings. The Verification analytics (dashboard, openwaf-cloud ctl challenge report, MCP challenge_report) show risk.lie_block and risk.browser_lie, the challenges the rule caught; risk.blocked includes them in enforce. Start in log and switch to enforce once you've seen a few days of normal traffic.