mirror of
https://github.com/TecharoHQ/anubis.git
synced 2025-08-03 09:48:08 -04:00

* feat(decaymap): add Delete method Signed-off-by: Xe Iaso <me@xeiaso.net> * chore(lib/challenge): refactor Validate to take ValidateInput Signed-off-by: Xe Iaso <me@xeiaso.net> * feat(lib): implement store interface Signed-off-by: Xe Iaso <me@xeiaso.net> * feat(lib/store): all metapackage to import all store implementations Signed-off-by: Xe Iaso <me@xeiaso.net> * chore(policy): import all store backends Signed-off-by: Xe Iaso <me@xeiaso.net> * feat(lib): use new challenge creation flow Previously Anubis constructed challenge strings from request metadata. This was a good idea in spirit, but has turned out to be a very bad idea in practice. This new flow reuses the Store facility to dynamically create challenge values with completely random data. This is a fairly big rewrite of how Anubis processes challenges. Right now it defaults to using the in-memory storage backend, but on-disk (boltdb) and valkey-based adaptors will come soon. Signed-off-by: Xe Iaso <me@xeiaso.net> * chore(decaymap): fix documentation typo Signed-off-by: Xe Iaso <me@xeiaso.net> * chore(lib): fix SA4004 Signed-off-by: Xe Iaso <me@xeiaso.net> * test(lib/store): make generic storage interface test adaptor Signed-off-by: Xe Iaso <me@xeiaso.net> * chore: spelling Signed-off-by: Xe Iaso <me@xeiaso.net> * fix(decaymap): invert locking process for Delete Signed-off-by: Xe Iaso <me@xeiaso.net> * feat(lib/store): add bbolt store implementation Signed-off-by: Xe Iaso <me@xeiaso.net> * chore: spelling Signed-off-by: Xe Iaso <me@xeiaso.net> * chore: go mod tidy Signed-off-by: Xe Iaso <me@xeiaso.net> * chore(devcontainer): adapt to docker compose, add valkey service Signed-off-by: Xe Iaso <me@xeiaso.net> * fix(lib): make challenges live for 30 minutes by default Signed-off-by: Xe Iaso <me@xeiaso.net> * feat(lib/store): implement valkey backend Signed-off-by: Xe Iaso <me@xeiaso.net> * test(lib/store/valkey): disable tests if not using docker Signed-off-by: Xe Iaso <me@xeiaso.net> * test(lib/policy/config): ensure valkey stores can be loaded Signed-off-by: Xe Iaso <me@xeiaso.net> * Update metadata check-spelling run (pull_request) for Xe/store-interface Signed-off-by: check-spelling-bot <check-spelling-bot@users.noreply.github.com> on-behalf-of: @check-spelling <check-spelling-bot@check-spelling.dev> * chore(devcontainer): remove port forwards because vs code handles that for you Signed-off-by: Xe Iaso <me@xeiaso.net> * docs(default-config): add a nudge to the storage backends section of the docs Signed-off-by: Xe Iaso <me@xeiaso.net> * chore(docs): listen on 0.0.0.0 for dev container support Signed-off-by: Xe Iaso <me@xeiaso.net> * docs(policy): document storage backends Signed-off-by: Xe Iaso <me@xeiaso.net> * docs: update CHANGELOG and internal links Signed-off-by: Xe Iaso <me@xeiaso.net> * docs(admin/policies): don't start a sentence with as Signed-off-by: Xe Iaso <me@xeiaso.net> * chore: fixes found in review Signed-off-by: Xe Iaso <me@xeiaso.net> --------- Signed-off-by: Xe Iaso <me@xeiaso.net> Signed-off-by: check-spelling-bot <check-spelling-bot@users.noreply.github.com>
151 lines
3.2 KiB
Go
151 lines
3.2 KiB
Go
package proofofwork
|
|
|
|
import (
|
|
"errors"
|
|
"log/slog"
|
|
"net/http"
|
|
"testing"
|
|
|
|
"github.com/TecharoHQ/anubis/lib/challenge"
|
|
"github.com/TecharoHQ/anubis/lib/policy"
|
|
"github.com/TecharoHQ/anubis/lib/policy/config"
|
|
)
|
|
|
|
func mkRequest(t *testing.T, values map[string]string) *http.Request {
|
|
t.Helper()
|
|
req, err := http.NewRequestWithContext(t.Context(), http.MethodGet, "/", nil)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
q := req.URL.Query()
|
|
|
|
for k, v := range values {
|
|
q.Set(k, v)
|
|
}
|
|
|
|
req.URL.RawQuery = q.Encode()
|
|
|
|
return req
|
|
}
|
|
|
|
func TestBasic(t *testing.T) {
|
|
i := &Impl{Algorithm: "fast"}
|
|
bot := &policy.Bot{
|
|
Challenge: &config.ChallengeRules{
|
|
Algorithm: "fast",
|
|
Difficulty: 0,
|
|
ReportAs: 0,
|
|
},
|
|
}
|
|
const challengeStr = "hunter"
|
|
const response = "2652bdba8fb4d2ab39ef28d8534d7694c557a4ae146c1e9237bd8d950280500e"
|
|
|
|
for _, cs := range []struct {
|
|
name string
|
|
req *http.Request
|
|
err error
|
|
challengeStr string
|
|
}{
|
|
{
|
|
name: "allgood",
|
|
req: mkRequest(t, map[string]string{
|
|
"nonce": "0",
|
|
"elapsedTime": "69",
|
|
"response": response,
|
|
}),
|
|
err: nil,
|
|
challengeStr: challengeStr,
|
|
},
|
|
{
|
|
name: "no-params",
|
|
req: mkRequest(t, map[string]string{}),
|
|
err: challenge.ErrMissingField,
|
|
challengeStr: challengeStr,
|
|
},
|
|
{
|
|
name: "missing-nonce",
|
|
req: mkRequest(t, map[string]string{
|
|
"elapsedTime": "69",
|
|
"response": response,
|
|
}),
|
|
err: challenge.ErrMissingField,
|
|
challengeStr: challengeStr,
|
|
},
|
|
{
|
|
name: "missing-elapsedTime",
|
|
req: mkRequest(t, map[string]string{
|
|
"nonce": "0",
|
|
"response": response,
|
|
}),
|
|
err: challenge.ErrMissingField,
|
|
challengeStr: challengeStr,
|
|
},
|
|
{
|
|
name: "missing-response",
|
|
req: mkRequest(t, map[string]string{
|
|
"nonce": "0",
|
|
"elapsedTime": "69",
|
|
}),
|
|
err: challenge.ErrMissingField,
|
|
challengeStr: challengeStr,
|
|
},
|
|
{
|
|
name: "wrong-nonce-format",
|
|
req: mkRequest(t, map[string]string{
|
|
"nonce": "taco",
|
|
"elapsedTime": "69",
|
|
"response": response,
|
|
}),
|
|
err: challenge.ErrInvalidFormat,
|
|
challengeStr: challengeStr,
|
|
},
|
|
{
|
|
name: "wrong-elapsedTime-format",
|
|
req: mkRequest(t, map[string]string{
|
|
"nonce": "0",
|
|
"elapsedTime": "taco",
|
|
"response": response,
|
|
}),
|
|
err: challenge.ErrInvalidFormat,
|
|
challengeStr: challengeStr,
|
|
},
|
|
{
|
|
name: "invalid-response",
|
|
req: mkRequest(t, map[string]string{
|
|
"nonce": "0",
|
|
"elapsedTime": "69",
|
|
"response": response,
|
|
}),
|
|
err: challenge.ErrFailed,
|
|
challengeStr: "Tacos are tasty",
|
|
},
|
|
} {
|
|
t.Run(cs.name, func(t *testing.T) {
|
|
lg := slog.With()
|
|
|
|
i.Setup(http.NewServeMux())
|
|
|
|
inp := &challenge.IssueInput{
|
|
Rule: bot,
|
|
Challenge: &challenge.Challenge{
|
|
RandomData: cs.challengeStr,
|
|
},
|
|
}
|
|
|
|
if _, err := i.Issue(cs.req, lg, inp); err != nil {
|
|
t.Errorf("can't issue challenge: %v", err)
|
|
}
|
|
|
|
if err := i.Validate(cs.req, lg, &challenge.ValidateInput{
|
|
Rule: bot,
|
|
Challenge: &challenge.Challenge{
|
|
RandomData: cs.challengeStr,
|
|
},
|
|
}); !errors.Is(err, cs.err) {
|
|
t.Errorf("got wrong error from Validate, got %v but wanted %v", err, cs.err)
|
|
}
|
|
})
|
|
}
|
|
}
|