diff --git a/.github/actions/spelling/expect.txt b/.github/actions/spelling/expect.txt index f5bb8ff..13486e6 100644 --- a/.github/actions/spelling/expect.txt +++ b/.github/actions/spelling/expect.txt @@ -137,6 +137,7 @@ gptbot Graphene grpcprom grw +gzw Hashcash hashrate headermap @@ -288,7 +289,6 @@ Sidetrade simprint sitemap sls -Smartphone sni Spambot sparkline @@ -368,7 +368,6 @@ Yda yeet yeetfile yourdomain -yoursite yyz Zenos zizmor diff --git a/docs/docs/CHANGELOG.md b/docs/docs/CHANGELOG.md index 0807463..48b888e 100644 --- a/docs/docs/CHANGELOG.md +++ b/docs/docs/CHANGELOG.md @@ -37,6 +37,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Add option to set `targetSNI` to special keyword 'auto' to indicate that it should be automatically set to the request Host name ([424](https://github.com/TecharoHQ/anubis/issues/424)). - The Preact challenge has been removed from the default configuration. It will be deprecated in the future. +### Better error messages + +In order to make it easier for legitimate clients to debug issues with their browser configuration and Anubis, Anubis will emit internal error detail in base 64 so that administrators can chase down issues. Future versions of this may also include a variant that encrypts the error detail messages. + ### Bug Fixes Sometimes the enhanced temporal assurance in [#1038](https://github.com/TecharoHQ/anubis/pull/1038) and [#1068](https://github.com/TecharoHQ/anubis/pull/1068) could backfire because Chromium and its ilk randomize the amount of time they wait in order to avoid a timing side channel attack. This has been fixed by both increasing the amount of time a client has to wait for the metarefresh and preact challenges as well as making the server side logic more permissive. diff --git a/lib/anubis.go b/lib/anubis.go index 6a44e1f..da5727c 100644 --- a/lib/anubis.go +++ b/lib/anubis.go @@ -164,7 +164,7 @@ func (s *Server) maybeReverseProxy(w http.ResponseWriter, r *http.Request, httpS if err != nil { lg.Error("check failed", "err", err) localizer := localization.GetLocalizer(r) - s.respondWithError(w, r, fmt.Sprintf("%s \"maybeReverseProxy\"", localizer.T("internal_server_error"))) + s.respondWithError(w, r, fmt.Sprintf("%s \"maybeReverseProxy\"", localizer.T("internal_server_error")), makeCode(err)) return } @@ -267,13 +267,13 @@ func (s *Server) checkRules(w http.ResponseWriter, r *http.Request, cr policy.Ch lg.Info("explicit deny") if rule == nil { lg.Error("rule is nil, cannot calculate checksum") - s.respondWithError(w, r, fmt.Sprintf("%s \"maybeReverseProxy.RuleDeny\"", localizer.T("internal_server_error"))) + s.respondWithError(w, r, fmt.Sprintf("%s \"maybeReverseProxy.RuleDeny\"", localizer.T("internal_server_error")), makeCode(ErrActualAnubisBug)) return true } hash := rule.Hash() lg.Debug("rule hash", "hash", hash) - s.respondWithStatus(w, r, fmt.Sprintf("%s %s", localizer.T("access_denied"), hash), s.policy.StatusCodes.Deny) + s.respondWithStatus(w, r, fmt.Sprintf("%s %s", localizer.T("access_denied"), hash), "", s.policy.StatusCodes.Deny) return true case config.RuleChallenge: lg.Debug("challenge requested") @@ -284,7 +284,7 @@ func (s *Server) checkRules(w http.ResponseWriter, r *http.Request, cr policy.Ch default: s.ClearCookie(w, CookieOpts{Path: cookiePath, Host: r.Host}) lg.Error("CONFIG ERROR: unknown rule", "rule", cr.Rule) - s.respondWithError(w, r, fmt.Sprintf("%s \"maybeReverseProxy.Rules\"", localizer.T("internal_server_error"))) + s.respondWithError(w, r, fmt.Sprintf("%s \"maybeReverseProxy.Rules\"", localizer.T("internal_server_error")), makeCode(ErrActualAnubisBug)) return true } return false @@ -311,7 +311,7 @@ func (s *Server) handleDNSBL(w http.ResponseWriter, r *http.Request, ip string, localizer.T("dronebl_entry"), resp.String(), localizer.T("see_dronebl_lookup"), - ip), s.policy.StatusCodes.Deny) + ip), "", s.policy.StatusCodes.Deny) return true } } @@ -399,7 +399,7 @@ func (s *Server) PassChallenge(w http.ResponseWriter, r *http.Request) { redirURL, err := url.ParseRequestURI(redir) if err != nil { lg.Error("invalid redirect", "err", err) - s.respondWithStatus(w, r, localizer.T("invalid_redirect"), http.StatusBadRequest) + s.respondWithStatus(w, r, localizer.T("invalid_redirect"), makeCode(err), http.StatusBadRequest) return } @@ -408,7 +408,7 @@ func (s *Server) PassChallenge(w http.ResponseWriter, r *http.Request) { // allowed default: lg.Error("XSS attempt blocked, invalid redirect scheme", "scheme", redirURL.Scheme) - s.respondWithStatus(w, r, localizer.T("invalid_redirect"), http.StatusBadRequest) + s.respondWithStatus(w, r, localizer.T("invalid_redirect"), "", http.StatusBadRequest) return } @@ -422,7 +422,7 @@ func (s *Server) PassChallenge(w http.ResponseWriter, r *http.Request) { s.ClearCookie(w, CookieOpts{Path: cookiePath, Host: r.Host}) s.ClearCookie(w, CookieOpts{Name: anubis.TestCookieName, Host: r.Host}) lg.Warn("user has cookies disabled, this is not an anubis bug") - s.respondWithError(w, r, localizer.T("cookies_disabled")) + s.respondWithError(w, r, localizer.T("cookies_disabled"), "") return } @@ -431,19 +431,19 @@ func (s *Server) PassChallenge(w http.ResponseWriter, r *http.Request) { urlParsed, err := r.URL.Parse(redir) if err != nil { - s.respondWithError(w, r, localizer.T("redirect_not_parseable")) + s.respondWithError(w, r, localizer.T("redirect_not_parseable"), makeCode(err)) return } if (len(urlParsed.Host) > 0 && len(s.opts.RedirectDomains) != 0 && !matchRedirectDomain(s.opts.RedirectDomains, urlParsed.Host)) || urlParsed.Host != r.URL.Host { lg.Debug("domain not allowed", "domain", urlParsed.Host) - s.respondWithError(w, r, localizer.T("redirect_domain_not_allowed")) + s.respondWithError(w, r, localizer.T("redirect_domain_not_allowed"), "") return } cr, rule, err := s.check(r, lg) if err != nil { lg.Error("check failed", "err", err) - s.respondWithError(w, r, fmt.Sprintf("%s \"passChallenge\"", localizer.T("internal_server_error"))) + s.respondWithError(w, r, fmt.Sprintf("%s \"passChallenge\"", localizer.T("internal_server_error")), makeCode(err)) return } lg = lg.With("check_result", cr) @@ -451,20 +451,20 @@ func (s *Server) PassChallenge(w http.ResponseWriter, r *http.Request) { chall, err := s.getChallenge(r) if err != nil { lg.Error("getChallenge failed", "err", err) - s.respondWithError(w, r, fmt.Sprintf("%s: %s", localizer.T("internal_server_error"), rule.Challenge.Algorithm)) + s.respondWithError(w, r, fmt.Sprintf("%s: %s", localizer.T("internal_server_error"), rule.Challenge.Algorithm), makeCode(err)) return } if chall.Spent { lg.Error("double spend prevented", "reason", "double_spend") - s.respondWithError(w, r, fmt.Sprintf("%s: %s", localizer.T("internal_server_error"), "double_spend")) + s.respondWithError(w, r, fmt.Sprintf("%s: %s", localizer.T("internal_server_error"), "double_spend"), "") return } impl, ok := challenge.Get(chall.Method) if !ok { lg.Error("check failed", "err", err) - s.respondWithError(w, r, fmt.Sprintf("%s: %s", localizer.T("internal_server_error"), rule.Challenge.Algorithm)) + s.respondWithError(w, r, fmt.Sprintf("%s: %s", localizer.T("internal_server_error"), rule.Challenge.Algorithm), makeCode(ErrActualAnubisBug)) return } @@ -487,11 +487,11 @@ func (s *Server) PassChallenge(w http.ResponseWriter, r *http.Request) { switch { case errors.Is(err, challenge.ErrFailed): lg.Error("challenge failed", "err", err) - s.respondWithStatus(w, r, cerr.PublicReason, cerr.StatusCode) + s.respondWithStatus(w, r, cerr.PublicReason, makeCode(err), cerr.StatusCode) return case errors.Is(err, challenge.ErrInvalidFormat), errors.Is(err, challenge.ErrMissingField): lg.Error("invalid challenge format", "err", err) - s.respondWithError(w, r, cerr.PublicReason) + s.respondWithError(w, r, cerr.PublicReason, makeCode(err)) return } } @@ -511,7 +511,7 @@ func (s *Server) PassChallenge(w http.ResponseWriter, r *http.Request) { if r.Header.Get(s.opts.JWTRestrictionHeader) == "" { lg.Error("JWTRestrictionHeader is set in config but not found in request, please check your reverse proxy config.") s.ClearCookie(w, CookieOpts{Path: cookiePath, Host: r.Host}) - s.respondWithError(w, r, "failed to sign JWT") + s.respondWithError(w, r, "failed to sign JWT", makeCode(err)) return } else { claims["restriction"] = internal.SHA256sum(r.Header.Get(s.opts.JWTRestrictionHeader)) @@ -525,7 +525,7 @@ func (s *Server) PassChallenge(w http.ResponseWriter, r *http.Request) { if err != nil { lg.Error("failed to sign JWT", "err", err) s.ClearCookie(w, CookieOpts{Path: cookiePath, Host: r.Host}) - s.respondWithError(w, r, localizer.T("failed_to_sign_jwt")) + s.respondWithError(w, r, localizer.T("failed_to_sign_jwt"), makeCode(err)) return } diff --git a/lib/http.go b/lib/http.go index 0f63ba7..65f9640 100644 --- a/lib/http.go +++ b/lib/http.go @@ -1,6 +1,9 @@ package lib import ( + "bytes" + "compress/gzip" + "encoding/base64" "errors" "fmt" "math/rand" @@ -25,6 +28,10 @@ import ( var domainMatchRegexp = regexp.MustCompile(`^((xn--)?[a-z0-9]+(-[a-z0-9]+)*\.)+[a-z]{2,}$`) +var ( + ErrActualAnubisBug = errors.New("this is an actual bug in Anubis, please file an issue with the magic string 'taco bell'") +) + // matchRedirectDomain returns true if host matches any of the allowed redirect // domain patterns. Patterns may contain '*' which are matched using the // internal glob matcher. Matching is case-insensitive on hostnames. @@ -145,6 +152,46 @@ func randomChance(n int) bool { return rand.Intn(n) == 0 } +// XXX(Xe): generated by ChatGPT +func rot13(s string) string { + rotated := make([]rune, len(s)) + for i, c := range s { + switch { + case c >= 'A' && c <= 'Z': + rotated[i] = 'A' + ((c - 'A' + 13) % 26) + case c >= 'a' && c <= 'z': + rotated[i] = 'a' + ((c - 'a' + 13) % 26) + default: + rotated[i] = c + } + } + return string(rotated) +} + +func makeCode(err error) string { + var buf bytes.Buffer + gzw := gzip.NewWriter(&buf) + errStr := fmt.Sprintf("internal error: %v", err) + + fmt.Fprintln(gzw, rot13(errStr)) + if err := gzw.Close(); err != nil { + panic("can't write to gzip in ram buffer") + } + const width = 16 + + enc := base64.StdEncoding.EncodeToString(buf.Bytes()) + var builder strings.Builder + for i := 0; i < len(enc); i += width { + end := i + width + if end > len(enc) { + end = len(enc) + } + builder.WriteString(enc[i:end]) + builder.WriteByte('\n') + } + return builder.String() +} + func (s *Server) RenderIndex(w http.ResponseWriter, r *http.Request, cr policy.CheckResult, rule *policy.Bot, returnHTTPStatusOnly bool) { localizer := localization.GetLocalizer(r) @@ -155,7 +202,7 @@ func (s *Server) RenderIndex(w http.ResponseWriter, r *http.Request, cr policy.C } else { redirectURL, err := s.constructRedirectURL(r) if err != nil { - s.respondWithStatus(w, r, err.Error(), http.StatusBadRequest) + s.respondWithStatus(w, r, err.Error(), "", http.StatusBadRequest) return } http.Redirect(w, r, redirectURL, http.StatusTemporaryRedirect) @@ -167,7 +214,7 @@ func (s *Server) RenderIndex(w http.ResponseWriter, r *http.Request, cr policy.C if !strings.Contains(r.Header.Get("Accept-Encoding"), "gzip") && randomChance(64) { lg.Error("client was given a challenge but does not in fact support gzip compression") - s.respondWithError(w, r, localizer.T("client_error_browser")) + s.respondWithError(w, r, localizer.T("client_error_browser"), "") return } @@ -176,7 +223,7 @@ func (s *Server) RenderIndex(w http.ResponseWriter, r *http.Request, cr policy.C if err != nil { lg.Error("can't get challenge", "err", err) s.ClearCookie(w, CookieOpts{Name: anubis.TestCookieName, Host: r.Host}) - s.respondWithError(w, r, fmt.Sprintf("%s: %s", localizer.T("internal_server_error"), rule.Challenge.Algorithm)) + s.respondWithError(w, r, fmt.Sprintf("%s: %s", localizer.T("internal_server_error"), rule.Challenge.Algorithm), makeCode(err)) return } @@ -203,7 +250,7 @@ func (s *Server) RenderIndex(w http.ResponseWriter, r *http.Request, cr policy.C if !ok { lg.Error("check failed", "err", "can't get algorithm", "algorithm", rule.Challenge.Algorithm) s.ClearCookie(w, CookieOpts{Name: anubis.TestCookieName, Host: r.Host}) - s.respondWithError(w, r, fmt.Sprintf("%s: %s", localizer.T("internal_server_error"), rule.Challenge.Algorithm)) + s.respondWithError(w, r, fmt.Sprintf("%s: %s", localizer.T("internal_server_error"), rule.Challenge.Algorithm), makeCode(err)) return } @@ -218,7 +265,7 @@ func (s *Server) RenderIndex(w http.ResponseWriter, r *http.Request, cr policy.C component, err := impl.Issue(w, r, lg, in) if err != nil { lg.Error("[unexpected] challenge component render failed, please open an issue", "err", err) // This is likely a bug in the template. Should never be triggered as CI tests for this. - s.respondWithError(w, r, fmt.Sprintf("%s \"RenderIndex\"", localizer.T("internal_server_error"))) + s.respondWithError(w, r, fmt.Sprintf("%s \"RenderIndex\"", localizer.T("internal_server_error")), makeCode(err)) return } @@ -269,14 +316,14 @@ func (s *Server) RenderBench(w http.ResponseWriter, r *http.Request) { ).ServeHTTP(w, r) } -func (s *Server) respondWithError(w http.ResponseWriter, r *http.Request, message string) { - s.respondWithStatus(w, r, message, http.StatusInternalServerError) +func (s *Server) respondWithError(w http.ResponseWriter, r *http.Request, message, code string) { + s.respondWithStatus(w, r, message, code, http.StatusInternalServerError) } -func (s *Server) respondWithStatus(w http.ResponseWriter, r *http.Request, msg string, status int) { +func (s *Server) respondWithStatus(w http.ResponseWriter, r *http.Request, msg, code string, status int) { localizer := localization.GetLocalizer(r) - templ.Handler(web.Base(localizer.T("oh_noes"), web.ErrorPage(msg, s.opts.WebmasterEmail, localizer), s.policy.Impressum, localizer), templ.WithStatus(status)).ServeHTTP(w, r) + templ.Handler(web.Base(localizer.T("oh_noes"), web.ErrorPage(msg, s.opts.WebmasterEmail, code, localizer), s.policy.Impressum, localizer), templ.WithStatus(status)).ServeHTTP(w, r) } func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { @@ -324,7 +371,7 @@ func (s *Server) ServeHTTPNext(w http.ResponseWriter, r *http.Request) { redir := r.FormValue("redir") urlParsed, err := r.URL.Parse(redir) if err != nil { - s.respondWithStatus(w, r, localizer.T("redirect_not_parseable"), http.StatusBadRequest) + s.respondWithStatus(w, r, localizer.T("redirect_not_parseable"), makeCode(err), http.StatusBadRequest) return } @@ -336,7 +383,7 @@ func (s *Server) ServeHTTPNext(w http.ResponseWriter, r *http.Request) { if hostNotAllowed || hostMismatch { lg := internal.GetRequestLogger(s.logger, r) lg.Debug("domain not allowed", "domain", urlParsed.Host) - s.respondWithStatus(w, r, localizer.T("redirect_domain_not_allowed"), http.StatusBadRequest) + s.respondWithStatus(w, r, localizer.T("redirect_domain_not_allowed"), makeCode(err), http.StatusBadRequest) return } diff --git a/web/index.go b/web/index.go index c3ba03d..7b171ea 100644 --- a/web/index.go +++ b/web/index.go @@ -22,8 +22,8 @@ func BaseWithChallengeAndOGTags(title string, body templ.Component, impressum *c }, ogTags, localizer) } -func ErrorPage(msg, mail string, localizer *localization.SimpleLocalizer) templ.Component { - return errorPage(msg, mail, localizer) +func ErrorPage(msg, mail, code string, localizer *localization.SimpleLocalizer) templ.Component { + return errorPage(msg, mail, code, localizer) } func Bench(localizer *localization.SimpleLocalizer) templ.Component { diff --git a/web/index.templ b/web/index.templ index 54b368c..8e34ea4 100644 --- a/web/index.templ +++ b/web/index.templ @@ -88,10 +88,13 @@ templ base(title string, body templ.Component, impressum *config.Impressum, chal } -templ errorPage(message, mail string, localizer *localization.SimpleLocalizer) { +templ errorPage(message, mail, code string, localizer *localization.SimpleLocalizer) {
Sad Anubis
{ code }
+ } if mail != "" {

{ localizer.T("go_home") } { localizer.T("contact_webmaster") } diff --git a/web/index_templ.go b/web/index_templ.go index 13f17e4..b672b30 100644 --- a/web/index_templ.go +++ b/web/index_templ.go @@ -283,7 +283,7 @@ func base(title string, body templ.Component, impressum *config.Impressum, chall }) } -func errorPage(message, mail string, localizer *localization.SimpleLocalizer) templ.Component { +func errorPage(message, mail, code string, localizer *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 { @@ -334,72 +334,73 @@ func errorPage(message, mail string, localizer *localization.SimpleLocalizer) te if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - if mail != "" { - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 26, "

") + if code != "" { + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 26, "

")
 			if templ_7745c5c3_Err != nil {
 				return templ_7745c5c3_Err
 			}
 			var templ_7745c5c3_Var19 string
-			templ_7745c5c3_Var19, templ_7745c5c3_Err = templ.JoinStringErrs(localizer.T("go_home"))
+			templ_7745c5c3_Var19, templ_7745c5c3_Err = templ.JoinStringErrs(code)
 			if templ_7745c5c3_Err != nil {
-				return templ.Error{Err: templ_7745c5c3_Err, FileName: `index.templ`, Line: 97, Col: 40}
+				return templ.Error{Err: templ_7745c5c3_Err, FileName: `index.templ`, Line: 96, Col: 20}
 			}
 			_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var19))
 			if templ_7745c5c3_Err != nil {
 				return templ_7745c5c3_Err
 			}
-			templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 27, " ")
+			templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 27, "
") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + } + if mail != "" { + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 28, "

") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } var templ_7745c5c3_Var20 string - templ_7745c5c3_Var20, templ_7745c5c3_Err = templ.JoinStringErrs(localizer.T("contact_webmaster")) + templ_7745c5c3_Var20, templ_7745c5c3_Err = templ.JoinStringErrs(localizer.T("go_home")) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `index.templ`, Line: 97, Col: 81} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `index.templ`, Line: 100, Col: 40} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var20)) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 28, " ") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - var templ_7745c5c3_Var21 templ.SafeURL - templ_7745c5c3_Var21, templ_7745c5c3_Err = templ.JoinURLErrs("mailto:" + templ.SafeURL(mail)) + var templ_7745c5c3_Var21 string + templ_7745c5c3_Var21, templ_7745c5c3_Err = templ.JoinStringErrs(localizer.T("contact_webmaster")) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `index.templ`, Line: 98, Col: 45} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `index.templ`, Line: 100, Col: 81} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var21)) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 29, "\">") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 30, "

") - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - } else { - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 31, "

") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 31, "\">") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } var templ_7745c5c3_Var23 string - templ_7745c5c3_Var23, templ_7745c5c3_Err = templ.JoinStringErrs(localizer.T("go_home")) + templ_7745c5c3_Var23, templ_7745c5c3_Err = templ.JoinStringErrs(mail) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `index.templ`, Line: 103, Col: 42} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `index.templ`, Line: 102, Col: 11} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var23)) if templ_7745c5c3_Err != nil { @@ -409,8 +410,26 @@ func errorPage(message, mail string, localizer *localization.SimpleLocalizer) te if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } + } else { + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 33, "

") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + var templ_7745c5c3_Var24 string + templ_7745c5c3_Var24, templ_7745c5c3_Err = templ.JoinStringErrs(localizer.T("go_home")) + if templ_7745c5c3_Err != nil { + return templ.Error{Err: templ_7745c5c3_Err, FileName: `index.templ`, Line: 106, Col: 42} + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var24)) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 34, "

") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 33, "
") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 35, "") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -434,39 +453,39 @@ func StaticHappy(localizer *localization.SimpleLocalizer) templ.Component { }() } ctx = templ.InitializeContext(ctx) - templ_7745c5c3_Var24 := templ.GetChildren(ctx) - if templ_7745c5c3_Var24 == nil { - templ_7745c5c3_Var24 = templ.NopComponent + templ_7745c5c3_Var25 := templ.GetChildren(ctx) + if templ_7745c5c3_Var25 == nil { + templ_7745c5c3_Var25 = templ.NopComponent } ctx = templ.ClearChildren(ctx) - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 34, "

") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 36, "

") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 37, "\">

") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + var templ_7745c5c3_Var27 string + templ_7745c5c3_Var27, templ_7745c5c3_Err = templ.JoinStringErrs(localizer.T("static_check_endpoint")) + if templ_7745c5c3_Err != nil { + return templ.Error{Err: templ_7745c5c3_Err, FileName: `index.templ`, Line: 119, Col: 43} + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var27)) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 38, "

") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -490,181 +509,181 @@ func bench(localizer *localization.SimpleLocalizer) templ.Component { }() } ctx = templ.InitializeContext(ctx) - templ_7745c5c3_Var27 := templ.GetChildren(ctx) - if templ_7745c5c3_Var27 == nil { - templ_7745c5c3_Var27 = templ.NopComponent + templ_7745c5c3_Var28 := templ.GetChildren(ctx) + if templ_7745c5c3_Var28 == nil { + templ_7745c5c3_Var28 = templ.NopComponent } ctx = templ.ClearChildren(ctx) - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 37, "
") - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - var templ_7745c5c3_Var28 string - templ_7745c5c3_Var28, templ_7745c5c3_Err = templ.JoinStringErrs(localizer.T("time")) - if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `index.templ`, Line: 127, Col: 51} - } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var28)) - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 38, "") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 39, "
") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } var templ_7745c5c3_Var29 string - templ_7745c5c3_Var29, templ_7745c5c3_Err = templ.JoinStringErrs(localizer.T("iters")) + templ_7745c5c3_Var29, templ_7745c5c3_Err = templ.JoinStringErrs(localizer.T("time")) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `index.templ`, Line: 128, Col: 50} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `index.templ`, Line: 130, Col: 51} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var29)) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 39, "
") - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - var templ_7745c5c3_Var30 string - templ_7745c5c3_Var30, templ_7745c5c3_Err = templ.JoinStringErrs(localizer.T("time_a")) - if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `index.templ`, Line: 131, Col: 53} - } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var30)) - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 40, "") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - var templ_7745c5c3_Var31 string - templ_7745c5c3_Var31, templ_7745c5c3_Err = templ.JoinStringErrs(localizer.T("iters_a")) + var templ_7745c5c3_Var30 string + templ_7745c5c3_Var30, templ_7745c5c3_Err = templ.JoinStringErrs(localizer.T("iters")) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `index.templ`, Line: 132, Col: 52} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `index.templ`, Line: 131, Col: 50} + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var30)) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 41, "
") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + var templ_7745c5c3_Var31 string + templ_7745c5c3_Var31, templ_7745c5c3_Err = templ.JoinStringErrs(localizer.T("time_a")) + if templ_7745c5c3_Err != nil { + return templ.Error{Err: templ_7745c5c3_Err, FileName: `index.templ`, Line: 134, Col: 53} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var31)) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 41, "") - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - var templ_7745c5c3_Var32 string - templ_7745c5c3_Var32, templ_7745c5c3_Err = templ.JoinStringErrs(localizer.T("time_b")) - if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `index.templ`, Line: 133, Col: 53} - } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var32)) - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 42, "") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - var templ_7745c5c3_Var33 string - templ_7745c5c3_Var33, templ_7745c5c3_Err = templ.JoinStringErrs(localizer.T("iters_b")) + var templ_7745c5c3_Var32 string + templ_7745c5c3_Var32, templ_7745c5c3_Err = templ.JoinStringErrs(localizer.T("iters_a")) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `index.templ`, Line: 134, Col: 52} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `index.templ`, Line: 135, Col: 52} + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var32)) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 43, "") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + var templ_7745c5c3_Var33 string + templ_7745c5c3_Var33, templ_7745c5c3_Err = templ.JoinStringErrs(localizer.T("time_b")) + if templ_7745c5c3_Err != nil { + return templ.Error{Err: templ_7745c5c3_Err, FileName: `index.templ`, Line: 136, Col: 53} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var33)) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 43, "
") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } var templ_7745c5c3_Var34 string - templ_7745c5c3_Var34, templ_7745c5c3_Err = templ.JoinStringErrs(anubis.BasePrefix + "/.within.website/x/cmd/anubis/static/img/pensive.webp?cacheBuster=" + anubis.Version) + templ_7745c5c3_Var34, templ_7745c5c3_Err = templ.JoinStringErrs(localizer.T("iters_b")) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `index.templ`, Line: 143, Col: 166} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `index.templ`, Line: 137, Col: 52} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var34)) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 44, "\">

") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 45, "

") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 51, "
") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err }