113 lines
4.9 KiB
Go
113 lines
4.9 KiB
Go
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://[email protected]/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",
|
|
"[email protected]",
|
|
} {
|
|
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)
|
|
})
|
|
}
|
|
}
|