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
}