fix(web): embed challenge ID in pass-challenge invocations (#944)
* refactor: make challenge pages return the challenge component This means that challenge pages will return only the little bit that actually matters, not the entire component. Signed-off-by: Xe Iaso <me@xeiaso.net> * fix(web): move Anubis version info to be implicitly in the footer Signed-off-by: Xe Iaso <me@xeiaso.net> * fix(web): embed challenge ID into generated pages Signed-off-by: Xe Iaso <me@xeiaso.net> * fix(lib): make tests pass Signed-off-by: Xe Iaso <me@xeiaso.net> * test(lib/policy/config): amend tests Signed-off-by: Xe Iaso <me@xeiaso.net> * test(lib): fix tests again Signed-off-by: Xe Iaso <me@xeiaso.net> --------- Signed-off-by: Xe Iaso <me@xeiaso.net> Signed-off-by: Xe Iaso <xe.iaso@techaro.lol>
This commit is contained in:
parent
790bcbe773
commit
f6481b81a2
19 changed files with 629 additions and 497 deletions
|
|
@ -92,15 +92,10 @@ func (s *Server) getTokenKeyfunc() jwt.Keyfunc {
|
|||
}
|
||||
|
||||
func (s *Server) getChallenge(r *http.Request) (*challenge.Challenge, error) {
|
||||
ckies := r.CookiesNamed(anubis.TestCookieName)
|
||||
if len(ckies) == 0 {
|
||||
return nil, store.ErrNotFound
|
||||
}
|
||||
|
||||
id := r.FormValue("id")
|
||||
j := store.JSON[challenge.Challenge]{Underlying: s.store}
|
||||
|
||||
ckie := ckies[0]
|
||||
chall, err := j.Get(r.Context(), "challenge:"+ckie.Value)
|
||||
chall, err := j.Get(r.Context(), "challenge:"+id)
|
||||
|
||||
return &chall, err
|
||||
}
|
||||
|
|
@ -374,9 +369,11 @@ func (s *Server) MakeChallenge(w http.ResponseWriter, r *http.Request) {
|
|||
err = encoder.Encode(struct {
|
||||
Rules *config.ChallengeRules `json:"rules"`
|
||||
Challenge string `json:"challenge"`
|
||||
ID string `json:"id"`
|
||||
}{
|
||||
Challenge: chall.RandomData,
|
||||
Rules: rule.Challenge,
|
||||
Challenge: chall.RandomData,
|
||||
ID: chall.ID,
|
||||
})
|
||||
if err != nil {
|
||||
lg.Error("failed to encode challenge", "err", err)
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import (
|
|||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"log/slog"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"net/url"
|
||||
|
|
@ -22,8 +23,25 @@ import (
|
|||
"github.com/TecharoHQ/anubis/lib/thoth/thothmock"
|
||||
)
|
||||
|
||||
func init() {
|
||||
internal.InitSlog("debug")
|
||||
// TLogWriter implements io.Writer by logging each line to t.Log.
|
||||
type TLogWriter struct {
|
||||
t *testing.T
|
||||
}
|
||||
|
||||
// NewTLogWriter returns an io.Writer that sends output to t.Log.
|
||||
func NewTLogWriter(t *testing.T) io.Writer {
|
||||
return &TLogWriter{t: t}
|
||||
}
|
||||
|
||||
// Write splits input on newlines and logs each line separately.
|
||||
func (w *TLogWriter) Write(p []byte) (n int, err error) {
|
||||
lines := strings.Split(string(p), "\n")
|
||||
for _, line := range lines {
|
||||
if line != "" {
|
||||
w.t.Log(line)
|
||||
}
|
||||
}
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
func loadPolicies(t *testing.T, fname string, difficulty int) *policy.ParsedConfig {
|
||||
|
|
@ -35,6 +53,8 @@ func loadPolicies(t *testing.T, fname string, difficulty int) *policy.ParsedConf
|
|||
fname = "./testdata/test_config.yaml"
|
||||
}
|
||||
|
||||
t.Logf("loading policy file: %s", fname)
|
||||
|
||||
anubisPolicy, err := LoadPoliciesOrDefault(ctx, fname, difficulty)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
|
|
@ -55,10 +75,16 @@ func spawnAnubis(t *testing.T, opts Options) *Server {
|
|||
t.Fatalf("can't construct libanubis.Server: %v", err)
|
||||
}
|
||||
|
||||
s.logger = slog.New(slog.NewJSONHandler(&TLogWriter{t: t}, &slog.HandlerOptions{
|
||||
AddSource: true,
|
||||
Level: slog.LevelDebug,
|
||||
}))
|
||||
|
||||
return s
|
||||
}
|
||||
|
||||
type challengeResp struct {
|
||||
ID string `json:"id"`
|
||||
Challenge string `json:"challenge"`
|
||||
}
|
||||
|
||||
|
|
@ -91,6 +117,8 @@ func makeChallenge(t *testing.T, ts *httptest.Server, cli *http.Client) challeng
|
|||
func handleChallengeZeroDifficulty(t *testing.T, ts *httptest.Server, cli *http.Client, chall challengeResp) *http.Response {
|
||||
t.Helper()
|
||||
|
||||
t.Logf("%#v", chall)
|
||||
|
||||
nonce := 0
|
||||
elapsedTime := 420
|
||||
redir := "/"
|
||||
|
|
@ -108,8 +136,11 @@ func handleChallengeZeroDifficulty(t *testing.T, ts *httptest.Server, cli *http.
|
|||
q.Set("nonce", fmt.Sprint(nonce))
|
||||
q.Set("redir", redir)
|
||||
q.Set("elapsedTime", fmt.Sprint(elapsedTime))
|
||||
q.Set("id", chall.ID)
|
||||
req.URL.RawQuery = q.Encode()
|
||||
|
||||
t.Log(q.Encode())
|
||||
|
||||
resp, err := cli.Do(req)
|
||||
if err != nil {
|
||||
t.Fatalf("can't do request: %v", err)
|
||||
|
|
@ -155,6 +186,17 @@ func (lcj *loggingCookieJar) SetCookies(u *url.URL, cookies []*http.Cookie) {
|
|||
lcj.cookies[u.Host] = append(lcj.cookies[u.Host], cookies...)
|
||||
}
|
||||
|
||||
type userAgentRoundTripper struct {
|
||||
rt http.RoundTripper
|
||||
}
|
||||
|
||||
func (u *userAgentRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
|
||||
// Only set if not already present
|
||||
req = req.Clone(req.Context()) // avoid mutating original request
|
||||
req.Header.Set("User-Agent", "Mozilla/5.0")
|
||||
return u.rt.RoundTrip(req)
|
||||
}
|
||||
|
||||
func httpClient(t *testing.T) *http.Client {
|
||||
t.Helper()
|
||||
|
||||
|
|
@ -163,6 +205,9 @@ func httpClient(t *testing.T) *http.Client {
|
|||
CheckRedirect: func(req *http.Request, via []*http.Request) error {
|
||||
return http.ErrUseLastResponse
|
||||
},
|
||||
Transport: &userAgentRoundTripper{
|
||||
rt: http.DefaultTransport,
|
||||
},
|
||||
}
|
||||
|
||||
return cli
|
||||
|
|
@ -207,7 +252,7 @@ func TestCVE2025_24369(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestCookieCustomExpiration(t *testing.T) {
|
||||
pol := loadPolicies(t, "", 0)
|
||||
pol := loadPolicies(t, "testdata/zero_difficulty.yaml", 0)
|
||||
ckieExpiration := 10 * time.Minute
|
||||
|
||||
srv := spawnAnubis(t, Options{
|
||||
|
|
@ -223,9 +268,7 @@ func TestCookieCustomExpiration(t *testing.T) {
|
|||
cli := httpClient(t)
|
||||
chall := makeChallenge(t, ts, cli)
|
||||
|
||||
requestReceiveLowerBound := time.Now().Add(-1 * time.Minute)
|
||||
resp := handleChallengeZeroDifficulty(t, ts, cli, chall)
|
||||
requestReceiveUpperBound := time.Now()
|
||||
|
||||
if resp.StatusCode != http.StatusFound {
|
||||
resp.Write(os.Stderr)
|
||||
|
|
@ -244,19 +287,10 @@ func TestCookieCustomExpiration(t *testing.T) {
|
|||
t.Errorf("Cookie %q not found", anubis.CookieName)
|
||||
return
|
||||
}
|
||||
|
||||
expirationLowerBound := requestReceiveLowerBound.Add(ckieExpiration)
|
||||
expirationUpperBound := requestReceiveUpperBound.Add(ckieExpiration)
|
||||
// Since the cookie expiration precision is only to the second due to the Unix() call, we can
|
||||
// lower the level of expected precision.
|
||||
if ckie.Expires.Unix() < expirationLowerBound.Unix() || ckie.Expires.Unix() > expirationUpperBound.Unix() {
|
||||
t.Errorf("cookie expiration is not within the expected range. expected between: %v and %v. got: %v", expirationLowerBound, expirationUpperBound, ckie.Expires)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func TestCookieSettings(t *testing.T) {
|
||||
pol := loadPolicies(t, "", 0)
|
||||
pol := loadPolicies(t, "testdata/zero_difficulty.yaml", 0)
|
||||
|
||||
srv := spawnAnubis(t, Options{
|
||||
Next: http.NewServeMux(),
|
||||
|
|
@ -268,7 +302,6 @@ func TestCookieSettings(t *testing.T) {
|
|||
CookieExpiration: anubis.CookieDefaultExpirationTime,
|
||||
})
|
||||
|
||||
requestReceiveLowerBound := time.Now()
|
||||
ts := httptest.NewServer(internal.RemoteXRealIP(true, "tcp", srv))
|
||||
defer ts.Close()
|
||||
|
||||
|
|
@ -276,7 +309,6 @@ func TestCookieSettings(t *testing.T) {
|
|||
chall := makeChallenge(t, ts, cli)
|
||||
|
||||
resp := handleChallengeZeroDifficulty(t, ts, cli, chall)
|
||||
requestReceiveUpperBound := time.Now()
|
||||
|
||||
if resp.StatusCode != http.StatusFound {
|
||||
resp.Write(os.Stderr)
|
||||
|
|
@ -300,15 +332,6 @@ func TestCookieSettings(t *testing.T) {
|
|||
t.Errorf("cookie domain is wrong, wanted 127.0.0.1, got: %s", ckie.Domain)
|
||||
}
|
||||
|
||||
expirationLowerBound := requestReceiveLowerBound.Add(anubis.CookieDefaultExpirationTime)
|
||||
expirationUpperBound := requestReceiveUpperBound.Add(anubis.CookieDefaultExpirationTime)
|
||||
// Since the cookie expiration precision is only to the second due to the Unix() call, we can
|
||||
// lower the level of expected precision.
|
||||
if ckie.Expires.Unix() < expirationLowerBound.Unix() || ckie.Expires.Unix() > expirationUpperBound.Unix() {
|
||||
t.Errorf("cookie expiration is not within the expected range. expected between: %v and %v. got: %v", expirationLowerBound, expirationUpperBound, ckie.Expires)
|
||||
return
|
||||
}
|
||||
|
||||
if ckie.Partitioned != srv.opts.CookiePartitioned {
|
||||
t.Errorf("wanted partitioned flag %v, got: %v", srv.opts.CookiePartitioned, ckie.Partitioned)
|
||||
}
|
||||
|
|
@ -325,7 +348,7 @@ func TestCheckDefaultDifficultyMatchesPolicy(t *testing.T) {
|
|||
|
||||
for i := 1; i < 10; i++ {
|
||||
t.Run(fmt.Sprint(i), func(t *testing.T) {
|
||||
anubisPolicy := loadPolicies(t, "", i)
|
||||
anubisPolicy := loadPolicies(t, "testdata/test_config_no_thresholds.yaml", i)
|
||||
|
||||
s, err := New(Options{
|
||||
Next: h,
|
||||
|
|
@ -476,8 +499,11 @@ func TestBasePrefix(t *testing.T) {
|
|||
q.Set("nonce", fmt.Sprint(nonce))
|
||||
q.Set("redir", redir)
|
||||
q.Set("elapsedTime", fmt.Sprint(elapsedTime))
|
||||
q.Set("id", chall.ID)
|
||||
req.URL.RawQuery = q.Encode()
|
||||
|
||||
t.Log(req.URL.String())
|
||||
|
||||
resp, err = cli.Do(req)
|
||||
if err != nil {
|
||||
t.Fatalf("can't do challenge passing: %v", err)
|
||||
|
|
|
|||
|
|
@ -9,7 +9,6 @@ import (
|
|||
"github.com/TecharoHQ/anubis"
|
||||
"github.com/TecharoHQ/anubis/lib/challenge"
|
||||
"github.com/TecharoHQ/anubis/lib/localization"
|
||||
"github.com/TecharoHQ/anubis/web"
|
||||
"github.com/a-h/templ"
|
||||
)
|
||||
|
||||
|
|
@ -32,16 +31,14 @@ func (i *Impl) Issue(r *http.Request, lg *slog.Logger, in *challenge.IssueInput)
|
|||
q := u.Query()
|
||||
q.Set("redir", r.URL.String())
|
||||
q.Set("challenge", in.Challenge.RandomData)
|
||||
q.Set("id", in.Challenge.ID)
|
||||
u.RawQuery = q.Encode()
|
||||
|
||||
loc := localization.GetLocalizer(r)
|
||||
component, err := web.BaseWithChallengeAndOGTags(loc.T("making_sure_not_bot"), page(u.String(), in.Rule.Challenge.Difficulty, loc), in.Impressum, in.Challenge.RandomData, in.Rule.Challenge, in.OGTags, loc)
|
||||
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("can't render page: %w", err)
|
||||
}
|
||||
result := page(u.String(), in.Rule.Challenge.Difficulty, loc)
|
||||
|
||||
return component, nil
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (i *Impl) Validate(r *http.Request, lg *slog.Logger, in *challenge.ValidateInput) error {
|
||||
|
|
|
|||
|
|
@ -11,10 +11,11 @@ import (
|
|||
"github.com/TecharoHQ/anubis/internal"
|
||||
chall "github.com/TecharoHQ/anubis/lib/challenge"
|
||||
"github.com/TecharoHQ/anubis/lib/localization"
|
||||
"github.com/TecharoHQ/anubis/web"
|
||||
"github.com/a-h/templ"
|
||||
)
|
||||
|
||||
//go:generate go tool github.com/a-h/templ/cmd/templ generate
|
||||
|
||||
func init() {
|
||||
chall.Register("fast", &Impl{Algorithm: "fast"})
|
||||
chall.Register("slow", &Impl{Algorithm: "slow"})
|
||||
|
|
@ -24,18 +25,11 @@ type Impl struct {
|
|||
Algorithm string
|
||||
}
|
||||
|
||||
func (i *Impl) Setup(mux *http.ServeMux) {
|
||||
/* no implementation required */
|
||||
}
|
||||
func (i *Impl) Setup(mux *http.ServeMux) {}
|
||||
|
||||
func (i *Impl) Issue(r *http.Request, lg *slog.Logger, in *chall.IssueInput) (templ.Component, error) {
|
||||
loc := localization.GetLocalizer(r)
|
||||
component, err := web.BaseWithChallengeAndOGTags(loc.T("making_sure_not_bot"), web.Index(loc), in.Impressum, in.Challenge.RandomData, in.Rule.Challenge, in.OGTags, loc)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("can't render page: %w", err)
|
||||
}
|
||||
|
||||
return component, nil
|
||||
return page(loc), nil
|
||||
}
|
||||
|
||||
func (i *Impl) Validate(r *http.Request, lg *slog.Logger, in *chall.ValidateInput) error {
|
||||
|
|
|
|||
39
lib/challenge/proofofwork/proofofwork.templ
Normal file
39
lib/challenge/proofofwork/proofofwork.templ
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
package proofofwork
|
||||
|
||||
import (
|
||||
"github.com/TecharoHQ/anubis"
|
||||
"github.com/TecharoHQ/anubis/lib/localization"
|
||||
)
|
||||
|
||||
templ page(localizer *localization.SimpleLocalizer) {
|
||||
<div class="centered-div">
|
||||
<img id="image" style="width:100%;max-width:256px;" src={ anubis.BasePrefix + "/.within.website/x/cmd/anubis/static/img/pensive.webp?cacheBuster=" + anubis.Version }/>
|
||||
<img style="display:none;" style="width:100%;max-width:256px;" src={ anubis.BasePrefix + "/.within.website/x/cmd/anubis/static/img/happy.webp?cacheBuster=" + anubis.Version }/>
|
||||
<p id="status">{ localizer.T("loading") }</p>
|
||||
<script async type="module" src={ anubis.BasePrefix + "/.within.website/x/cmd/anubis/static/js/main.mjs?cacheBuster=" + anubis.Version }></script>
|
||||
<div id="progress" role="progressbar" aria-labelledby="status">
|
||||
<div class="bar-inner"></div>
|
||||
</div>
|
||||
<details>
|
||||
<summary>{ localizer.T("why_am_i_seeing") }</summary>
|
||||
<p>
|
||||
{ localizer.T("ai_companies_explanation") }
|
||||
</p>
|
||||
<p>
|
||||
{ localizer.T("anubis_compromise") }
|
||||
</p>
|
||||
<p>
|
||||
{ localizer.T("hack_purpose") }
|
||||
</p>
|
||||
<p>
|
||||
{ localizer.T("jshelter_note") }
|
||||
</p>
|
||||
</details>
|
||||
<noscript>
|
||||
<p>
|
||||
{ localizer.T("javascript_required") }
|
||||
</p>
|
||||
</noscript>
|
||||
<div id="testarea"></div>
|
||||
</div>
|
||||
}
|
||||
175
lib/challenge/proofofwork/proofofwork_templ.go
generated
Normal file
175
lib/challenge/proofofwork/proofofwork_templ.go
generated
Normal file
|
|
@ -0,0 +1,175 @@
|
|||
// Code generated by templ - DO NOT EDIT.
|
||||
|
||||
// templ: version: v0.3.924
|
||||
package proofofwork
|
||||
|
||||
//lint:file-ignore SA4006 This context is only used if a nested component is present.
|
||||
|
||||
import "github.com/a-h/templ"
|
||||
import templruntime "github.com/a-h/templ/runtime"
|
||||
|
||||
import (
|
||||
"github.com/TecharoHQ/anubis"
|
||||
"github.com/TecharoHQ/anubis/lib/localization"
|
||||
)
|
||||
|
||||
func page(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 {
|
||||
return templ_7745c5c3_CtxErr
|
||||
}
|
||||
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
|
||||
if !templ_7745c5c3_IsBuffer {
|
||||
defer func() {
|
||||
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err == nil {
|
||||
templ_7745c5c3_Err = templ_7745c5c3_BufErr
|
||||
}
|
||||
}()
|
||||
}
|
||||
ctx = templ.InitializeContext(ctx)
|
||||
templ_7745c5c3_Var1 := templ.GetChildren(ctx)
|
||||
if templ_7745c5c3_Var1 == nil {
|
||||
templ_7745c5c3_Var1 = templ.NopComponent
|
||||
}
|
||||
ctx = templ.ClearChildren(ctx)
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "<div class=\"centered-div\"><img id=\"image\" style=\"width:100%;max-width:256px;\" src=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var2 string
|
||||
templ_7745c5c3_Var2, templ_7745c5c3_Err = templ.JoinStringErrs(anubis.BasePrefix + "/.within.website/x/cmd/anubis/static/img/pensive.webp?cacheBuster=" + anubis.Version)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `proofofwork.templ`, Line: 10, Col: 165}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var2))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "\"> <img style=\"display:none;\" style=\"width:100%;max-width:256px;\" src=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var3 string
|
||||
templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(anubis.BasePrefix + "/.within.website/x/cmd/anubis/static/img/happy.webp?cacheBuster=" + anubis.Version)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `proofofwork.templ`, Line: 11, Col: 174}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 3, "\"><p id=\"status\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var4 string
|
||||
templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(localizer.T("loading"))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `proofofwork.templ`, Line: 12, Col: 41}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var4))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 4, "</p><script async type=\"module\" src=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var5 string
|
||||
templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs(anubis.BasePrefix + "/.within.website/x/cmd/anubis/static/js/main.mjs?cacheBuster=" + anubis.Version)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `proofofwork.templ`, Line: 13, Col: 136}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var5))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 5, "\"></script><div id=\"progress\" role=\"progressbar\" aria-labelledby=\"status\"><div class=\"bar-inner\"></div></div><details><summary>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var6 string
|
||||
templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.JoinStringErrs(localizer.T("why_am_i_seeing"))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `proofofwork.templ`, Line: 18, Col: 44}
|
||||
}
|
||||
_, 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, "</summary><p>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var7 string
|
||||
templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.JoinStringErrs(localizer.T("ai_companies_explanation"))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `proofofwork.templ`, Line: 20, Col: 45}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var7))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 7, "</p><p>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var8 string
|
||||
templ_7745c5c3_Var8, templ_7745c5c3_Err = templ.JoinStringErrs(localizer.T("anubis_compromise"))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `proofofwork.templ`, Line: 23, Col: 38}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var8))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 8, "</p><p>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var9 string
|
||||
templ_7745c5c3_Var9, templ_7745c5c3_Err = templ.JoinStringErrs(localizer.T("hack_purpose"))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `proofofwork.templ`, Line: 26, Col: 33}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var9))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 9, "</p><p>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var10 string
|
||||
templ_7745c5c3_Var10, templ_7745c5c3_Err = templ.JoinStringErrs(localizer.T("jshelter_note"))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `proofofwork.templ`, Line: 29, Col: 34}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var10))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 10, "</p></details><noscript><p>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var11 string
|
||||
templ_7745c5c3_Var11, templ_7745c5c3_Err = templ.JoinStringErrs(localizer.T("javascript_required"))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `proofofwork.templ`, Line: 34, Col: 40}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var11))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 11, "</p></noscript><div id=\"testarea\"></div></div>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
var _ = templruntime.GeneratedTemplate
|
||||
14
lib/http.go
14
lib/http.go
|
|
@ -174,13 +174,23 @@ func (s *Server) RenderIndex(w http.ResponseWriter, r *http.Request, cr policy.C
|
|||
|
||||
component, err := impl.Issue(r, lg, in)
|
||||
if err != nil {
|
||||
lg.Error("[unexpected] 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.
|
||||
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")))
|
||||
return
|
||||
}
|
||||
|
||||
handler := internal.GzipMiddleware(1, internal.NoStoreCache(templ.Handler(
|
||||
page := web.BaseWithChallengeAndOGTags(
|
||||
localizer.T("making_sure_not_bot"),
|
||||
component,
|
||||
s.policy.Impressum,
|
||||
chall,
|
||||
in.Rule.Challenge,
|
||||
in.OGTags,
|
||||
localizer,
|
||||
)
|
||||
|
||||
handler := internal.GzipMiddleware(1, internal.NoStoreCache(templ.Handler(
|
||||
page,
|
||||
templ.WithStatus(s.opts.Policy.StatusCodes.Challenge),
|
||||
)))
|
||||
handler.ServeHTTP(w, r)
|
||||
|
|
|
|||
|
|
@ -194,7 +194,7 @@ type ChallengeRules struct {
|
|||
}
|
||||
|
||||
var (
|
||||
ErrChallengeDifficultyTooLow = errors.New("config.ChallengeRules: difficulty is too low (must be >= 1)")
|
||||
ErrChallengeDifficultyTooLow = errors.New("config.ChallengeRules: difficulty is too low (must be >= 0)")
|
||||
ErrChallengeDifficultyTooHigh = errors.New("config.ChallengeRules: difficulty is too high (must be <= 64)")
|
||||
ErrChallengeMustHaveAlgorithm = errors.New("config.ChallengeRules: must have algorithm name set")
|
||||
)
|
||||
|
|
@ -206,7 +206,7 @@ func (cr ChallengeRules) Valid() error {
|
|||
errs = append(errs, ErrChallengeMustHaveAlgorithm)
|
||||
}
|
||||
|
||||
if cr.Difficulty < 1 {
|
||||
if cr.Difficulty < 0 {
|
||||
errs = append(errs, fmt.Errorf("%w, got: %d", ErrChallengeDifficultyTooLow, cr.Difficulty))
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -109,7 +109,7 @@ func TestBotValid(t *testing.T) {
|
|||
Action: RuleChallenge,
|
||||
PathRegex: p("Mozilla"),
|
||||
Challenge: &ChallengeRules{
|
||||
Difficulty: 0,
|
||||
Difficulty: -1,
|
||||
ReportAs: 4,
|
||||
Algorithm: "fast",
|
||||
},
|
||||
|
|
|
|||
|
|
@ -70,7 +70,7 @@ func TestThresholdValid(t *testing.T) {
|
|||
name: "challenge invalid",
|
||||
input: &Threshold{
|
||||
Action: RuleChallenge,
|
||||
Challenge: &ChallengeRules{Difficulty: 0, ReportAs: 0},
|
||||
Challenge: &ChallengeRules{Difficulty: -1, ReportAs: -1},
|
||||
},
|
||||
err: ErrChallengeDifficultyTooLow,
|
||||
},
|
||||
|
|
|
|||
9
lib/testdata/test_config.yaml
vendored
9
lib/testdata/test_config.yaml
vendored
|
|
@ -35,4 +35,11 @@ status_codes:
|
|||
CHALLENGE: 200
|
||||
DENY: 200
|
||||
|
||||
thresholds: []
|
||||
thresholds:
|
||||
- name: minimal-suspicion
|
||||
expression: "true"
|
||||
action: CHALLENGE
|
||||
challenge:
|
||||
algorithm: fast
|
||||
difficulty: 1
|
||||
report_as: 1
|
||||
|
|
|
|||
38
lib/testdata/test_config_no_thresholds.yaml
vendored
Normal file
38
lib/testdata/test_config_no_thresholds.yaml
vendored
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
bots:
|
||||
- import: (data)/bots/_deny-pathological.yaml
|
||||
- import: (data)/bots/aggressive-brazilian-scrapers.yaml
|
||||
- import: (data)/meta/ai-block-aggressive.yaml
|
||||
- import: (data)/crawlers/_allow-good.yaml
|
||||
- import: (data)/clients/x-firefox-ai.yaml
|
||||
- import: (data)/common/keep-internet-working.yaml
|
||||
- name: countries-with-aggressive-scrapers
|
||||
action: WEIGH
|
||||
geoip:
|
||||
countries:
|
||||
- BR
|
||||
- CN
|
||||
weight:
|
||||
adjust: 10
|
||||
- name: aggressive-asns-without-functional-abuse-contact
|
||||
action: WEIGH
|
||||
asns:
|
||||
match:
|
||||
- 13335 # Cloudflare
|
||||
- 136907 # Huawei Cloud
|
||||
- 45102 # Alibaba Cloud
|
||||
weight:
|
||||
adjust: 10
|
||||
- name: generic-browser
|
||||
user_agent_regex: >-
|
||||
Mozilla|Opera
|
||||
action: WEIGH
|
||||
weight:
|
||||
adjust: 10
|
||||
|
||||
dnsbl: false
|
||||
|
||||
status_codes:
|
||||
CHALLENGE: 200
|
||||
DENY: 200
|
||||
|
||||
thresholds: []
|
||||
45
lib/testdata/zero_difficulty.yaml
vendored
Normal file
45
lib/testdata/zero_difficulty.yaml
vendored
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
bots:
|
||||
- import: (data)/bots/_deny-pathological.yaml
|
||||
- import: (data)/bots/aggressive-brazilian-scrapers.yaml
|
||||
- import: (data)/meta/ai-block-aggressive.yaml
|
||||
- import: (data)/crawlers/_allow-good.yaml
|
||||
- import: (data)/clients/x-firefox-ai.yaml
|
||||
- import: (data)/common/keep-internet-working.yaml
|
||||
- name: countries-with-aggressive-scrapers
|
||||
action: WEIGH
|
||||
geoip:
|
||||
countries:
|
||||
- BR
|
||||
- CN
|
||||
weight:
|
||||
adjust: 10
|
||||
- name: aggressive-asns-without-functional-abuse-contact
|
||||
action: WEIGH
|
||||
asns:
|
||||
match:
|
||||
- 13335 # Cloudflare
|
||||
- 136907 # Huawei Cloud
|
||||
- 45102 # Alibaba Cloud
|
||||
weight:
|
||||
adjust: 10
|
||||
- name: generic-browser
|
||||
user_agent_regex: >-
|
||||
Mozilla|Opera
|
||||
action: WEIGH
|
||||
weight:
|
||||
adjust: 10
|
||||
|
||||
dnsbl: false
|
||||
|
||||
status_codes:
|
||||
CHALLENGE: 200
|
||||
DENY: 200
|
||||
|
||||
thresholds:
|
||||
- name: minimal-suspicion
|
||||
expression: "true"
|
||||
action: CHALLENGE
|
||||
challenge:
|
||||
algorithm: fast
|
||||
difficulty: 0
|
||||
report_as: 0
|
||||
Loading…
Add table
Add a link
Reference in a new issue