package storage import ( "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) func TestObjectKeyFromCDNURL(t *testing.T) { tests := []struct { name string rawURL string cdnBase string wantKey string owned bool wantErr bool }{ { name: "scheme less base", rawURL: "https://cdn.example.com/2026/11/pic.webp", cdnBase: "cdn.example.com", wantKey: "2026/11/pic.webp", owned: true, }, { name: "base path", rawURL: "https://cdn.example.com/assets/2026/pic.webp", cdnBase: "https://cdn.example.com/assets", wantKey: "2026/pic.webp", owned: true, }, { name: "base trailing slash and escaped space", rawURL: "https://cdn.example.com/tamabox/a%20b.webp", cdnBase: "cdn.example.com/tamabox/", wantKey: "a b.webp", owned: true, }, { name: "host case insensitive", rawURL: "https://CDN.EXAMPLE.COM/a.webp", cdnBase: "cdn.example.com", wantKey: "a.webp", owned: true, }, { name: "preserve bucket prefix", rawURL: "https://cdn.example.com/tamabox/2026/a.webp", cdnBase: "cdn.example.com", wantKey: "tamabox/2026/a.webp", owned: true, }, { name: "external host not owned", rawURL: "https://outside.example.com/a.webp", cdnBase: "cdn.example.com", owned: false, }, { name: "host suffix attack not owned", rawURL: "https://cdn.example.com.evil.test/a.webp", cdnBase: "cdn.example.com", owned: false, }, { name: "base path complete segment", rawURL: "https://cdn.example.com/assets2/a.webp", cdnBase: "cdn.example.com/assets", owned: false, }, { name: "base path case sensitive", rawURL: "https://cdn.example.com/Assets/a.webp", cdnBase: "cdn.example.com/assets", owned: false, }, {name: "HTTP rejected", rawURL: "http://cdn.example.com/a", cdnBase: "cdn.example.com", wantErr: true}, {name: "relative rejected", rawURL: "/a", cdnBase: "cdn.example.com", wantErr: true}, {name: "scheme relative rejected", rawURL: "//cdn.example.com/a", cdnBase: "cdn.example.com", wantErr: true}, {name: "userinfo rejected", rawURL: "https://user@cdn.example.com/a", cdnBase: "cdn.example.com", wantErr: true}, {name: "query rejected", rawURL: "https://cdn.example.com/a?x=1", cdnBase: "cdn.example.com", wantErr: true}, {name: "empty query rejected", rawURL: "https://cdn.example.com/a?", cdnBase: "cdn.example.com", wantErr: true}, {name: "fragment rejected", rawURL: "https://cdn.example.com/a#fragment", cdnBase: "cdn.example.com", wantErr: true}, {name: "empty fragment rejected", rawURL: "https://cdn.example.com/a#", cdnBase: "cdn.example.com", wantErr: true}, {name: "backslash rejected", rawURL: `https://cdn.example.com/a\b`, cdnBase: "cdn.example.com", wantErr: true}, {name: "encoded backslash rejected", rawURL: "https://cdn.example.com/a%5Cb", cdnBase: "cdn.example.com", wantErr: true}, {name: "encoded slash rejected", rawURL: "https://cdn.example.com/a%2Fb", cdnBase: "cdn.example.com", wantErr: true}, {name: "control rejected", rawURL: "https://cdn.example.com/a\x7fb", cdnBase: "cdn.example.com", wantErr: true}, {name: "encoded control rejected", rawURL: "https://cdn.example.com/a%0Ab", cdnBase: "cdn.example.com", wantErr: true}, {name: "dot rejected", rawURL: "https://cdn.example.com/a/./b", cdnBase: "cdn.example.com", wantErr: true}, {name: "dot dot rejected", rawURL: "https://cdn.example.com/a/../b", cdnBase: "cdn.example.com", wantErr: true}, {name: "encoded dot dot rejected", rawURL: "https://cdn.example.com/a/%2E%2E/b", cdnBase: "cdn.example.com", wantErr: true}, {name: "empty path rejected", rawURL: "https://cdn.example.com", cdnBase: "cdn.example.com", wantErr: true}, {name: "root path rejected", rawURL: "https://cdn.example.com/", cdnBase: "cdn.example.com", wantErr: true}, {name: "empty middle segment rejected", rawURL: "https://cdn.example.com/a//b", cdnBase: "cdn.example.com", wantErr: true}, {name: "empty last segment rejected", rawURL: "https://cdn.example.com/a/", cdnBase: "cdn.example.com", wantErr: true}, {name: "missing key below base rejected", rawURL: "https://cdn.example.com/assets", cdnBase: "cdn.example.com/assets", wantErr: true}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { key, owned, err := ObjectKeyFromCDNURL(tt.rawURL, tt.cdnBase) if tt.wantErr { require.Error(t, err) assert.False(t, owned) assert.Empty(t, key) return } require.NoError(t, err) assert.Equal(t, tt.owned, owned) assert.Equal(t, tt.wantKey, key) }) } } func TestObjectKeyFromCDNURLRejectsInvalidBase(t *testing.T) { for _, base := range []string{ "", "http://cdn.example.com", "cdn.example.com?x=1", "cdn.example.com#fragment", "cdn.example.com/assets/../other", "cdn.example.com/assets//other", "cdn.example.com/assets%2Fother", "user@cdn.example.com", } { t.Run(base, func(t *testing.T) { key, owned, err := ObjectKeyFromCDNURL("https://cdn.example.com/assets/a", base) require.Error(t, err) assert.False(t, owned) assert.Empty(t, key) }) } }