Refuse image requests whose Referer is on referer_blocklist (closes #90)
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
This commit was merged in pull request #197.
This commit is contained in:
2026-10-04 22:24:50 +02:00
parent 8568c17d1b
commit 8314099abd
11 changed files with 559 additions and 48 deletions
@@ -5,6 +5,7 @@ import (
"log/slog"
"os"
"path/filepath"
"slices"
"strings"
"testing"
"time"
@@ -216,6 +217,25 @@ func TestCommaSeparatedAllowlistStillSupported(t *testing.T) {
}
}
// TestAllowlistHostsAcceptsUnderscore checks that an upstream host name with
// an underscore, which pixa can fetch from, is accepted as an entry.
func TestAllowlistHostsAcceptsUnderscore(t *testing.T) {
t.Parallel()
c, err := configFromYAML(t, signingKeyLine+`allowlist_hosts:
- my_bucket.example.com
- .my_bucket.example.org
`)
if err != nil {
t.Fatalf("host names with an underscore should load, got error: %v", err)
}
want := []string{"my_bucket.example.com", ".my_bucket.example.org"}
if !slices.Equal(c.AllowlistHosts, want) {
t.Errorf("AllowlistHosts = %v, want %v", c.AllowlistHosts, want)
}
}
// runAbortCases asserts that each case's config aborts startup with an
// error message mentioning every expected substring.
func runAbortCases(t *testing.T, cases []abortCase) {
@@ -318,6 +338,20 @@ func invalidHostAndCredentialCases() []abortCase {
keyAllowlistHosts, "example.com/images",
},
},
{
name: "allowlist host with wildcard",
yaml: signingKeyLine + "allowlist_hosts:\n - \"*.example.com\"\n",
wantErrSubstrings: []string{
keyAllowlistHosts, "*.example.com",
},
},
{
name: "allowlist host with port",
yaml: signingKeyLine + "allowlist_hosts:\n - example.com:8443\n",
wantErrSubstrings: []string{
keyAllowlistHosts, "example.com:8443",
},
},
{
name: "allowlist host with whitespace",
yaml: signingKeyLine + "allowlist_hosts:\n - \"exa mple.com\"\n",