From cd21a96088590507a454f842e25748c1f9d48998 Mon Sep 17 00:00:00 2001 From: Xe Iaso Date: Tue, 16 Sep 2025 20:24:16 +0000 Subject: [PATCH] feat(metarefresh): randomly use the Refresh header There are several ways to trigger an automatic refresh without JavaScript. One of them is the "meta refresh" method[1], but the other is with the Refresh header[2]. Both are semantically identical and supported with browsers as old as Chrome version 1. Given that they are basically the same thing, this patch makes Anubis randomly select between them by using the challenge random data's first character. This will fire about 50% of the time. I expect this to have no impact. If this works out fine, then I will implement some kind of fallback logic for the fast challenge such that admins can opt into allowing clients with a no-js configuration to pass the fast challenge. This needs to bake in the oven though. [1]: https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/meta/http-equiv [2]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Refresh Signed-off-by: Xe Iaso --- lib/challenge/metarefresh/metarefresh.go | 13 +++++++- lib/challenge/metarefresh/metarefresh.templ | 6 ++-- .../metarefresh/metarefresh_templ.go | 32 ++++++++++++------- 3 files changed, 37 insertions(+), 14 deletions(-) diff --git a/lib/challenge/metarefresh/metarefresh.go b/lib/challenge/metarefresh/metarefresh.go index 9760d50d..9bc56bfc 100644 --- a/lib/challenge/metarefresh/metarefresh.go +++ b/lib/challenge/metarefresh/metarefresh.go @@ -35,9 +35,20 @@ func (i *Impl) Issue(w http.ResponseWriter, r *http.Request, lg *slog.Logger, in q.Set("id", in.Challenge.ID) u.RawQuery = q.Encode() + showMeta := false + + switch in.Challenge.RandomData[0] { + case '0', '1', '2', '3', '4', '5', '6', '7': + lg.Debug("rendering meta element") + showMeta = true + default: + lg.Debug("adding Refresh header") + w.Header().Add("Refresh", fmt.Sprintf("%d; url=%s", in.Rule.Challenge.Difficulty+1, u.String())) + } + loc := localization.GetLocalizer(r) - result := page(u.String(), in.Rule.Challenge.Difficulty, loc) + result := page(u.String(), in.Rule.Challenge.Difficulty, showMeta, loc) return result, nil } diff --git a/lib/challenge/metarefresh/metarefresh.templ b/lib/challenge/metarefresh/metarefresh.templ index dccf7653..c074f596 100644 --- a/lib/challenge/metarefresh/metarefresh.templ +++ b/lib/challenge/metarefresh/metarefresh.templ @@ -7,12 +7,14 @@ import ( "github.com/TecharoHQ/anubis/lib/localization" ) -templ page(redir string, difficulty int, loc *localization.SimpleLocalizer) { +templ page(redir string, difficulty int, showMeta bool, loc *localization.SimpleLocalizer) {
{ loc.T("loading") }

{ loc.T("connection_security") }

- + if showMeta { + + }
} diff --git a/lib/challenge/metarefresh/metarefresh_templ.go b/lib/challenge/metarefresh/metarefresh_templ.go index 048260bd..f54c45af 100644 --- a/lib/challenge/metarefresh/metarefresh_templ.go +++ b/lib/challenge/metarefresh/metarefresh_templ.go @@ -15,7 +15,7 @@ import ( "github.com/TecharoHQ/anubis/lib/localization" ) -func page(redir string, difficulty int, loc *localization.SimpleLocalizer) templ.Component { +func page(redir string, difficulty int, showMeta bool, loc *localization.SimpleLocalizer) templ.Component { return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil { @@ -88,20 +88,30 @@ func page(redir string, difficulty int, loc *localization.SimpleLocalizer) templ if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 5, "

") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - var templ_7745c5c3_Var6 string - templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%d; url=%s", difficulty+1, redir)) - if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `metarefresh.templ`, Line: 16, Col: 85} + if showMeta { + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 6, "") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var6)) - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 6, "\">") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 8, "") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err }