test(nano): cover the nano delivery path and its loopback default

felis nano serves the same hasJoined handler as felis api, but behind
nanoStubRepo, which implements only the bar-list lookup and embeds a nil
Repo for everything else. Only the full-api path was tested, against a
complete fake store, so a second store call added to handleHasJoined
would pass CI and panic on every nano login. The loopback default of
-listen, the one thing keeping nano from being an open auth relay, was
not pinned either.

The default moves into a nanoDefaultListen constant, and two tests
cover the path. One serves a login through api.HasJoinedHandler with
nanoStubRepo and a fake identity source and expects the profile back.
The other requires the default to parse as a loopback IP. Taking the
bar-list method off the stub makes the first panic on the nil Repo;
defaulting to 0.0.0.0:8081 or :8081 fails the second.
This commit is contained in:
flyemoji committed 2026-09-22 13:37:51 +09:00
1 parent e0ad78af98
commit 59ec23d4a8
2 files changed
+36 -5

No files matched your search

+7 -5
View File
@@ -48,6 +48,12 @@ type nanoStubRepo struct{ api.Repo }
func (nanoStubRepo) IsUsernameBlacklisted(context.Context, string) (bool, error) { return false, nil }
// nanoDefaultListen is loopback because hasJoined carries no auth token (Velocity speaks the
// vanilla sessionserver protocol), so a public bind is an open auth relay: anyone can point
// their proxy at it and spend this host's egress IP on Mojang. A same-host Velocity reaches
// 127.0.0.1; serving an off-host proxy is an explicit -listen opt-in.
const nanoDefaultListen = "127.0.0.1:8081"
// nanoLogURIMax is room for a real hasJoined query (a 16-character name, a 41-character
// serverId, an address) several times over.
const nanoLogURIMax = 256
@@ -56,11 +62,7 @@ func cmdNano(args []string, stdout, stderr io.Writer) int {
fs := flag.NewFlagSet("nano", flag.ContinueOnError)
fs.SetOutput(stderr)
cfgPath := fs.String("config", "/etc/felis/felis.toml", "path to felis.toml (reads [[auth_source]])")
// Loopback default: hasJoined carries no auth token (authlib speaks the vanilla
// sessionserver protocol), so a public bind is an open auth relay — anyone can point
// their proxy at it and spend this host's egress IP on Mojang. A same-host Velocity
// reaches 127.0.0.1; serving an off-host proxy is an explicit -listen opt-in.
listen := fs.String("listen", "127.0.0.1:8081", "listen address for the hasJoined endpoint")
listen := fs.String("listen", nanoDefaultListen, "listen address for the hasJoined endpoint")
if err := fs.Parse(args); err != nil {
return 2
}