test: add proxy target rejection tests

This commit is contained in:
2026-06-26 23:56:29 +02:00
parent 5338c61a5d
commit 3c4f5375ed
2 changed files with 105 additions and 0 deletions

View File

@@ -0,0 +1,78 @@
package handler
import (
"context"
"io"
"mal/internal/domain"
"net/http"
"net/http/httptest"
"strings"
"testing"
"github.com/gin-gonic/gin"
)
type proxyTargetPlaybackService struct {
targetURL string
referer string
}
func (s proxyTargetPlaybackService) BuildWatchData(context.Context, int, []string, string, string, string) (domain.WatchPageData, error) {
return domain.WatchPageData{}, nil
}
func (s proxyTargetPlaybackService) SaveProgress(context.Context, string, int64, int, float64) error {
return nil
}
func (s proxyTargetPlaybackService) CompleteAnime(context.Context, string, int64) error {
return nil
}
func (s proxyTargetPlaybackService) SignProxyToken(string, string, string) (string, error) {
return "token", nil
}
func (s proxyTargetPlaybackService) ResolveProxyToken(string, string) (string, string, error) {
return s.targetURL, s.referer, nil
}
func (s proxyTargetPlaybackService) UpsertSkipSegmentOverride(context.Context, string, int64, int, string, float64, float64) error {
return nil
}
type recordingRoundTripper struct {
called bool
}
func (rt *recordingRoundTripper) RoundTrip(*http.Request) (*http.Response, error) {
rt.called = true
return &http.Response{
StatusCode: http.StatusOK,
Header: make(http.Header),
Body: io.NopCloser(strings.NewReader("WEBVTT\n")),
}, nil
}
func TestHandleProxySubtitleRejectsUnsafeTargetBeforeFetch(t *testing.T) {
rt := &recordingRoundTripper{}
h := &PlaybackHandler{
svc: proxyTargetPlaybackService{targetURL: "http://127.0.0.1/subtitle.vtt"},
proxyClient: &http.Client{Transport: rt},
subtitleCache: newSubtitleCache(0, 1),
}
req := httptest.NewRequestWithContext(context.Background(), http.MethodGet, "/watch/proxy/subtitle?token=token", nil)
rec := httptest.NewRecorder()
gin.SetMode(gin.TestMode)
router := gin.New()
router.GET("/watch/proxy/subtitle", h.HandleProxySubtitle)
router.ServeHTTP(rec, req)
if rec.Code != http.StatusBadGateway {
t.Fatalf("status = %d, want %d", rec.Code, http.StatusBadGateway)
}
if rt.called {
t.Fatal("proxy client was called for unsafe target")
}
}

View File

@@ -189,6 +189,33 @@ func TestPlaybackServiceProxyTokenDisabled(t *testing.T) {
}
}
func TestPlaybackServiceSignProxyTokenRejectsUnsafeTargets(t *testing.T) {
svc := &playbackService{proxyTokenKey: "secret", proxyTokens: newProxyTokenStore()}
tests := []struct {
name string
targetURL string
}{
{name: "loopback ipv4", targetURL: "http://127.0.0.1/video.m3u8"},
{name: "private ipv4", targetURL: "https://10.0.0.4/segment.ts"},
{name: "loopback ipv6", targetURL: "http://[::1]/subtitle.vtt"},
{name: "unsupported scheme", targetURL: "file:///tmp/video.m3u8"},
{name: "missing host", targetURL: "https:///segment.ts"},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
token, err := svc.SignProxyToken(tt.targetURL, "", "stream")
if err == nil {
t.Fatalf("SignProxyToken(%q) error = nil, token = %q", tt.targetURL, token)
}
if token != "" {
t.Fatalf("SignProxyToken(%q) token = %q, want empty", tt.targetURL, token)
}
})
}
}
type fakePlaybackRepository struct {
inTxCalled bool
getAnimeErr error