diff --git a/internal/handler/download_tools.go b/internal/handler/download_tools.go index a1086f9..1ef3cac 100644 --- a/internal/handler/download_tools.go +++ b/internal/handler/download_tools.go @@ -13,6 +13,7 @@ import ( "time" "github.com/gofiber/fiber/v3" + "github.com/sirupsen/logrus" "github.com/peterqiu0516/sub-store/internal/middleware" "github.com/peterqiu0516/sub-store/internal/model" @@ -55,7 +56,8 @@ func (d *Deps) HandleDownloadCollection(c fiber.Ctx) error { ProxyURL: d.Cfg.Fetcher.ProxyURL, }) if err != nil { - return failed(c, err.Error(), 500) + logrus.WithError(err).Error("Failed to build subscription") + return failed(c, "Failed to build subscription", 500) } return d.sendDownloadResponse(c, result, target) } @@ -86,7 +88,8 @@ func (d *Deps) HandleDownloadSource(c fiber.Ctx) error { ProxyURL: d.Cfg.Fetcher.ProxyURL, }) if err != nil { - return failed(c, err.Error(), 500) + logrus.WithError(err).Error("Failed to build subscription") + return failed(c, "Failed to build subscription", 500) } return d.sendDownloadResponse(c, result, target) } diff --git a/internal/handler/handler_test.go b/internal/handler/handler_test.go index 88c418c..87fa4cf 100644 --- a/internal/handler/handler_test.go +++ b/internal/handler/handler_test.go @@ -1836,6 +1836,37 @@ func TestHandleDownloadSourceDisabled(t *testing.T) { assertStatus(t, "DownloadSource disabled", code, 404) } +func TestHandleDownloadBuildErrorDoesNotLeakUpstreamURL(t *testing.T) { + for _, kind := range []string{"source", "collection"} { + t.Run(kind, func(t *testing.T) { + server := httptest.NewServer(http.HandlerFunc(func(http.ResponseWriter, *http.Request) {})) + upstreamURL := server.URL + "/subscription?token=upstream-secret" + server.Close() + + deps := newTestDeps(t) + app := newApp(deps) + deps.SourceRepo.Upsert(model.SourceRecord{ID: "remote", Name: "Remote", Type: "remote", URL: upstreamURL, Enabled: true}) + + path := "/sources/remote/dl-tok?target=json" + if kind == "collection" { + deps.CollectionRepo.Upsert(model.CollectionRecord{ID: "remote", Name: "Remote", SourceIds: []string{"remote"}, Enabled: true}) + path = "/collections/remote/dl-tok?target=json" + } + + code, body := doRequest(t, app, "GET", path, "", nil) + assertStatus(t, kind, code, http.StatusInternalServerError) + errorBody, ok := body["error"].(map[string]any) + if !ok || errorBody["message"] != "Failed to build subscription" { + t.Fatalf("response = %v, want fixed generic error", body) + } + response, _ := json.Marshal(body) + if strings.Contains(string(response), "upstream-secret") || strings.Contains(string(response), upstreamURL) { + t.Fatalf("response leaked upstream URL: %s", response) + } + }) + } +} + // --------------------------------------------------------------------------- // Preview handlers // ---------------------------------------------------------------------------