check / check (push) Failing after 2s
A new setting, referer_blocklist (PIXA_REFERER_BLOCKLIST), lists hosts whose pages may not show pixa's images. Entries are written and matched as allowlist_hosts are, with the same matcher. Both image routes check the Referer before the signature, the cache and the upstream fetch, and answer 403 with the JSON error, so a blocked request costs nothing and is refused whether or not the image is cached. No Referer, or one that does not parse, is served; README.md says this makes the list easy to get around. An entry of either host list that is not a host name (letters, digits, hyphens, underscores, dots, at most one leading dot) or an IP address now aborts startup naming the setting and the entry. Model: opus-5-5
167 lines
5.0 KiB
Go
167 lines
5.0 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)
|
|
}
|
|
}
|
|
|
|
// TestRefererBlocklistAcceptsIPAddresses checks that IPv4 and IPv6 addresses,
|
|
// the IPv6 one written without brackets, are accepted as entries.
|
|
func TestRefererBlocklistAcceptsIPAddresses(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
c, err := configFromYAML(t, signingKeyLine+`referer_blocklist:
|
|
- 192.0.2.7
|
|
- "2001:db8::7"
|
|
`)
|
|
if err != nil {
|
|
t.Fatalf("IP address entries should load, got error: %v", err)
|
|
}
|
|
|
|
want := []string{"192.0.2.7", "2001:db8::7"}
|
|
if !slices.Equal(c.RefererBlocklist, want) {
|
|
t.Errorf("RefererBlocklist = %v, want %v", c.RefererBlocklist, want)
|
|
}
|
|
}
|
|
|
|
// TestRefererBlocklistAcceptsUnderscore checks that a host name with an
|
|
// underscore, which a page can be served from, is accepted as an entry.
|
|
func TestRefererBlocklistAcceptsUnderscore(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
c, err := configFromYAML(t, signingKeyLine+`referer_blocklist:
|
|
- my_site.leech.example
|
|
- .my_site.hotlinker.example
|
|
`)
|
|
if err != nil {
|
|
t.Fatalf("host names with an underscore should load, got error: %v", err)
|
|
}
|
|
|
|
want := []string{"my_site.leech.example", ".my_site.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: "entry with a scheme",
|
|
yaml: signingKeyLine + "referer_blocklist:\n - https://leech.example\n",
|
|
wantErrSubstrings: []string{
|
|
keyRefererBlocklist, "https://leech.example",
|
|
},
|
|
},
|
|
{
|
|
name: "entry with a path",
|
|
yaml: signingKeyLine + "referer_blocklist:\n - leech.example/page\n",
|
|
wantErrSubstrings: []string{
|
|
keyRefererBlocklist, "leech.example/page",
|
|
},
|
|
},
|
|
{
|
|
name: "wildcard entry",
|
|
yaml: signingKeyLine + "referer_blocklist:\n - \"*.leech.example\"\n",
|
|
wantErrSubstrings: []string{
|
|
keyRefererBlocklist, "*.leech.example",
|
|
},
|
|
},
|
|
{
|
|
name: "entry with a port",
|
|
yaml: signingKeyLine + "referer_blocklist:\n - leech.example:8080\n",
|
|
wantErrSubstrings: []string{
|
|
keyRefererBlocklist, "leech.example:8080",
|
|
},
|
|
},
|
|
{
|
|
name: "two leading dots",
|
|
yaml: signingKeyLine + "referer_blocklist:\n - ..leech.example\n",
|
|
wantErrSubstrings: []string{
|
|
keyRefererBlocklist, "..leech.example",
|
|
},
|
|
},
|
|
{
|
|
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 value",
|
|
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")
|
|
}
|