Both image routes must refuse a request whose Referer names a host on referer_blocklist with 403 and the JSON error, without fetching from the upstream host and whether or not the image is cached, and must serve a request with no Referer, one that does not parse, or one naming another host. Hosts match as allowlist_hosts matches them. The config tests check the list is read from the file and from PIXA_REFERER_BLOCKLIST, and that an entry that is not a host aborts startup naming the setting and the entry. These do not compile until the setting exists. Model: opus-5-5
108 lines
3.2 KiB
Go
108 lines
3.2 KiB
Go
package config
|
|
|
|
import (
|
|
"slices"
|
|
"testing"
|
|
)
|
|
|
|
// TestRefererBlocklistParsed loads a referer_blocklist with a host and a
|
|
// pattern starting with "." and checks both are kept in order.
|
|
func TestRefererBlocklistParsed(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
c, err := configFromYAML(t, signingKeyLine+`referer_blocklist:
|
|
- leech.example
|
|
- .hotlinker.example
|
|
`)
|
|
if err != nil {
|
|
t.Fatalf("valid referer_blocklist should load, got error: %v", err)
|
|
}
|
|
|
|
want := []string{"leech.example", ".hotlinker.example"}
|
|
if !slices.Equal(c.RefererBlocklist, want) {
|
|
t.Errorf("RefererBlocklist = %v, want %v", c.RefererBlocklist, want)
|
|
}
|
|
}
|
|
|
|
// TestRefererBlocklistOmittedIsEmpty checks that an omitted key blocks no
|
|
// referer.
|
|
func TestRefererBlocklistOmittedIsEmpty(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
c, err := configFromYAML(t, signingKeyLine)
|
|
if err != nil {
|
|
t.Fatalf("minimal config should be valid, got error: %v", err)
|
|
}
|
|
|
|
if len(c.RefererBlocklist) != 0 {
|
|
t.Errorf("RefererBlocklist = %v, want empty", c.RefererBlocklist)
|
|
}
|
|
}
|
|
|
|
// TestRefererBlocklistInvalidAbortsStartup checks that an entry that is not a
|
|
// host, or a value that is not a list of them, aborts startup with an error
|
|
// naming the key and the entry.
|
|
func TestRefererBlocklistInvalidAbortsStartup(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
runAbortCases(t, []abortCase{
|
|
{
|
|
name: "url",
|
|
yaml: signingKeyLine + "referer_blocklist:\n - https://leech.example\n",
|
|
wantErrSubstrings: []string{
|
|
keyRefererBlocklist, "https://leech.example",
|
|
},
|
|
},
|
|
{
|
|
name: "path",
|
|
yaml: signingKeyLine + "referer_blocklist:\n - leech.example/page\n",
|
|
wantErrSubstrings: []string{
|
|
keyRefererBlocklist, "leech.example/page",
|
|
},
|
|
},
|
|
{
|
|
name: "dot only",
|
|
yaml: signingKeyLine + "referer_blocklist:\n - \".\"\n",
|
|
wantErrSubstrings: []string{keyRefererBlocklist, `"."`},
|
|
},
|
|
{
|
|
name: "empty entry",
|
|
yaml: signingKeyLine + "referer_blocklist:\n - \"\"\n",
|
|
wantErrSubstrings: []string{keyRefererBlocklist},
|
|
},
|
|
{
|
|
name: "entry not a string",
|
|
yaml: signingKeyLine + "referer_blocklist:\n - 42\n",
|
|
wantErrSubstrings: []string{keyRefererBlocklist, "42"},
|
|
},
|
|
{
|
|
name: "null",
|
|
yaml: signingKeyLine + "referer_blocklist:\n",
|
|
wantErrSubstrings: []string{keyRefererBlocklist, nullValueText},
|
|
},
|
|
})
|
|
}
|
|
|
|
// TestRefererBlocklistFromEnvironment checks that PIXA_REFERER_BLOCKLIST
|
|
// takes comma-separated entries, and that an entry in it that is not a host
|
|
// aborts startup naming the variable and the entry.
|
|
func TestRefererBlocklistFromEnvironment(t *testing.T) {
|
|
t.Setenv("PIXA_SIGNING_KEY", validTestSigningKey)
|
|
t.Setenv("PIXA_REFERER_BLOCKLIST", " leech.example , .hotlinker.example ")
|
|
|
|
c, err := newFromSmartConfig(nil)
|
|
if err != nil {
|
|
t.Fatalf("valid PIXA_REFERER_BLOCKLIST should load, got error: %v", err)
|
|
}
|
|
|
|
want := []string{"leech.example", ".hotlinker.example"}
|
|
if !slices.Equal(c.RefererBlocklist, want) {
|
|
t.Errorf("RefererBlocklist = %v, want %v", c.RefererBlocklist, want)
|
|
}
|
|
|
|
t.Setenv("PIXA_REFERER_BLOCKLIST", "leech.example,https://hotlinker.example")
|
|
|
|
_, err = newFromSmartConfig(nil)
|
|
wantStartupError(t, err, "PIXA_REFERER_BLOCKLIST", "https://hotlinker.example")
|
|
}
|