Compare commits

..

2 Commits

Author SHA1 Message Date
Xe Iaso d6f02ac5f9 fix(lib): try to make base path prefixing work again
Signed-off-by: Xe Iaso <me@xeiaso.net>
2025-07-12 19:07:19 +00:00
Xe Iaso 2ea8296682 feat(lib): enable multiple consecutive slash support
Replaces #808
Closes #754

Some web applications require the ability to include multiple
consecutive slashes in a URL. This could be for optional path variables
or for wiki article titles that start with a leading slash.

I wasn't aware that the RFC allowed this.

Signed-off-by: Xe Iaso <me@xeiaso.net>
2025-07-12 18:00:03 +00:00
9 changed files with 144 additions and 81 deletions
+2 -3
View File
@@ -1,9 +1,8 @@
name: Package builds (stable) name: Package builds (stable)
on: on:
workflow_dispatch: release:
# release: types: [published]
# types: [published]
permissions: permissions:
contents: write contents: write
+1 -1
View File
@@ -1 +1 @@
1.21.0-pre2 1.21.0-pre1
+2 -1
View File
@@ -17,7 +17,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
> Please, be at ease. You are among friends here. > Please, be at ease. You are among friends here.
In this release, Anubis becomes internationalized, gains the ability to use system load as input to issuing challenges, finally fixes the "invalid response" after "success" bug, and more! Please read these notes before upgrading as the changes are big enough that administrators should take action to ensure that the upgrade goes smoothly. In this release, Anubis becomes internationalized, gains the ability to use system load as input to issuing challenges,
### Big ticket changes ### Big ticket changes
@@ -99,6 +99,7 @@ There are a bunch of other assorted features and fixes too:
- Allow [Common Crawl](https://commoncrawl.org/) by default so scrapers have less incentive to scrape - Allow [Common Crawl](https://commoncrawl.org/) by default so scrapers have less incentive to scrape
- The [bbolt storage backend](./admin/policies.mdx#bbolt) now runs its cleanup every hour instead of every five minutes. - The [bbolt storage backend](./admin/policies.mdx#bbolt) now runs its cleanup every hour instead of every five minutes.
- Don't block Anubis starting up if [Thoth](./admin/thoth.mdx) health checks fail. - Don't block Anubis starting up if [Thoth](./admin/thoth.mdx) health checks fail.
- Multiple consecutive slashes are supported in upstream application URLs ([#754](https://github.com/TecharoHQ/anubis/issues/754)).
### Potentially breaking changes ### Potentially breaking changes
+1
View File
@@ -75,6 +75,7 @@ type Server struct {
hs512Secret []byte hs512Secret []byte
opts Options opts Options
store store.Interface store store.Interface
internalPath string
} }
func (s *Server) getTokenKeyfunc() jwt.Keyfunc { func (s *Server) getTokenKeyfunc() jwt.Keyfunc {
+57
View File
@@ -204,6 +204,63 @@ func TestCVE2025_24369(t *testing.T) {
} }
} }
func TestDoubleSlashes(t *testing.T) {
pol := loadPolicies(t, "", 0)
path := ""
srv := spawnAnubis(t, Options{
Next: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
path = r.URL.Path
}),
Policy: pol,
})
ts := httptest.NewServer(internal.RemoteXRealIP(true, "tcp", srv))
defer ts.Close()
cli := httpClient(t)
chall := makeChallenge(t, ts, cli)
resp := handleChallengeZeroDifficulty(t, ts, cli, chall)
if resp.StatusCode != http.StatusFound {
t.Fatal("can't solve challenge, see logs")
}
for _, tt := range []struct {
name, path string
}{
{
name: "basic",
path: "/foo",
},
{
name: "leading slashes",
path: "//foo",
},
{
name: "mid slashes",
path: "/foo//bar///baz",
},
{
name: "trailing slashes",
path: "/foo/bar///",
},
} {
t.Run(tt.name, func(t *testing.T) {
if _, err := cli.Get(ts.URL + tt.path); err != nil {
t.Errorf("can't make request to %s: %v", tt.path, err)
}
if path != tt.path {
t.Logf("want: %s", tt.path)
t.Logf("got: %s", path)
t.Error("invalid path sent to server")
}
})
}
}
func TestCookieCustomExpiration(t *testing.T) { func TestCookieCustomExpiration(t *testing.T) {
pol := loadPolicies(t, "", 0) pol := loadPolicies(t, "", 0)
ckieExpiration := 10 * time.Minute ckieExpiration := 10 * time.Minute
+1 -1
View File
@@ -108,6 +108,7 @@ func New(opts Options) (*Server, error) {
opts: opts, opts: opts,
OGTags: ogtags.NewOGTagCache(opts.Target, opts.Policy.OpenGraph, opts.Policy.Store), OGTags: ogtags.NewOGTagCache(opts.Target, opts.Policy.OpenGraph, opts.Policy.Store),
store: opts.Policy.Store, store: opts.Policy.Store,
internalPath: opts.BasePrefix + anubis.StaticPath,
} }
mux := http.NewServeMux() mux := http.NewServeMux()
@@ -154,7 +155,6 @@ func New(opts Options) (*Server, error) {
registerWithPrefix(anubis.APIPrefix+"pass-challenge", http.HandlerFunc(result.PassChallenge), "GET") registerWithPrefix(anubis.APIPrefix+"pass-challenge", http.HandlerFunc(result.PassChallenge), "GET")
registerWithPrefix(anubis.APIPrefix+"check", http.HandlerFunc(result.maybeReverseProxyHttpStatusOnly), "") registerWithPrefix(anubis.APIPrefix+"check", http.HandlerFunc(result.maybeReverseProxyHttpStatusOnly), "")
registerWithPrefix("/", http.HandlerFunc(result.maybeReverseProxyOrPage), "")
//goland:noinspection GoBoolExpressions //goland:noinspection GoBoolExpressions
if anubis.Version == "devel" { if anubis.Version == "devel" {
+5
View File
@@ -200,7 +200,12 @@ func (s *Server) respondWithStatus(w http.ResponseWriter, r *http.Request, msg s
} }
func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
switch strings.HasPrefix(r.URL.Path, s.internalPath) {
case true:
s.mux.ServeHTTP(w, r) s.mux.ServeHTTP(w, r)
case false:
s.maybeReverseProxyOrPage(w, r)
}
} }
func (s *Server) stripBasePrefixFromRequest(r *http.Request) *http.Request { func (s *Server) stripBasePrefixFromRequest(r *http.Request) *http.Request {
+2 -2
View File
@@ -1,12 +1,12 @@
{ {
"name": "@techaro/anubis", "name": "@techaro/anubis",
"version": "1.21.0-pre2", "version": "1.21.0-pre1",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "@techaro/anubis", "name": "@techaro/anubis",
"version": "1.21.0-pre2", "version": "1.21.0-pre1",
"license": "ISC", "license": "ISC",
"devDependencies": { "devDependencies": {
"cssnano": "^7.0.7", "cssnano": "^7.0.7",
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "@techaro/anubis", "name": "@techaro/anubis",
"version": "1.21.0-pre2", "version": "1.21.0-pre1",
"description": "", "description": "",
"main": "index.js", "main": "index.js",
"scripts": { "scripts": {