diff options
Diffstat (limited to 'internal/image/download_test.go')
| -rw-r--r-- | internal/image/download_test.go | 77 |
1 files changed, 62 insertions, 15 deletions
diff --git a/internal/image/download_test.go b/internal/image/download_test.go index 588d875..b9a6e31 100644 --- a/internal/image/download_test.go +++ b/internal/image/download_test.go @@ -13,9 +13,9 @@ type mockDownloaderProvider struct { results []SearchResult searchErr error payload string - attribution string searchQueries []string downloadURLs []string + getAttrCalls int } func (m *mockDownloaderProvider) Name() string { return "mock" } @@ -36,7 +36,8 @@ func (m *mockDownloaderProvider) Download(_ context.Context, url string) (io.Rea } func (m *mockDownloaderProvider) GetAttribution(*SearchResult) string { - return m.attribution + m.getAttrCalls++ + return "provider attribution" } func TestDownloaderGenerateFileName_DataURIUsesPNG(t *testing.T) { @@ -55,12 +56,11 @@ func TestDownloaderGenerateFileName_DataURIUsesPNG(t *testing.T) { } } -func TestDownloadImageWritesAttribution(t *testing.T) { +func TestDownloadImageWritesResultAttribution(t *testing.T) { t.Parallel() provider := &mockDownloaderProvider{ - payload: "image-bytes", - attribution: "attribution text", + payload: "image-bytes", } d := NewDownloader(provider, &DownloadOptions{ OutputDir: t.TempDir(), @@ -72,9 +72,10 @@ func TestDownloadImageWritesAttribution(t *testing.T) { outputPath := filepath.Join(d.options.OutputDir, "ябълка_gemini.png") if err := d.DownloadImage(context.Background(), &SearchResult{ - URL: "https://example.com/image.png", - Source: Gemini, - ID: "1", + URL: "https://example.com/image.png", + Source: Gemini, + ID: "1", + Attribution: "result attribution text", }, outputPath); err != nil { t.Fatalf("DownloadImage() error = %v", err) } @@ -86,14 +87,17 @@ func TestDownloadImageWritesAttribution(t *testing.T) { if string(data) != "image-bytes" { t.Fatalf("downloaded file = %q, want %q", string(data), "image-bytes") } + if provider.getAttrCalls != 0 { + t.Fatalf("GetAttribution() calls = %d, want 0", provider.getAttrCalls) + } attrPath := strings.TrimSuffix(outputPath, filepath.Ext(outputPath)) + "_attribution.txt" attr, err := os.ReadFile(attrPath) if err != nil { t.Fatalf("ReadFile(attribution) error = %v", err) } - if string(attr) != "attribution text" { - t.Fatalf("attribution = %q, want %q", string(attr), "attribution text") + if string(attr) != "result attribution text" { + t.Fatalf("attribution = %q, want %q", string(attr), "result attribution text") } } @@ -103,13 +107,13 @@ func TestDownloadBestMatchWithOptions(t *testing.T) { provider := &mockDownloaderProvider{ results: []SearchResult{ { - ID: "1", - URL: "https://example.com/image1.jpg", - Source: Gemini, + ID: "1", + URL: "https://example.com/image1.jpg", + Source: Gemini, + Attribution: "result attribution text", }, }, - payload: "image-bytes", - attribution: "attribution text", + payload: "image-bytes", } d := NewDownloader(provider, &DownloadOptions{ OutputDir: t.TempDir(), @@ -134,6 +138,49 @@ func TestDownloadBestMatchWithOptions(t *testing.T) { } } +func TestDownloadBestMatchWithOptions_SanitizesUnsafeProviderFields(t *testing.T) { + t.Parallel() + + outputDir := t.TempDir() + provider := &mockDownloaderProvider{ + results: []SearchResult{ + { + ID: "../escape/..//id", + URL: "https://example.com/image1.jpg", + Source: "../../outside/path", + Attribution: "safe attribution", + }, + }, + payload: "image-bytes", + } + d := NewDownloader(provider, &DownloadOptions{ + OutputDir: outputDir, + CreateDir: true, + OverwriteExisting: true, + FileNamePattern: "{word}_{source}_{id}", + MaxSizeBytes: 10 * 1024 * 1024, + }) + + _, path, err := d.DownloadBestMatchWithOptions(context.Background(), &SearchOptions{Query: "ябълка"}) + if err != nil { + t.Fatalf("DownloadBestMatchWithOptions() error = %v", err) + } + + rel, err := filepath.Rel(outputDir, path) + if err != nil { + t.Fatalf("filepath.Rel() error = %v", err) + } + if strings.HasPrefix(rel, "..") { + t.Fatalf("path escaped output dir: %q (rel=%q)", path, rel) + } + if strings.Contains(path, "..") { + t.Fatalf("path contains path traversal elements: %q", path) + } + if _, err := os.Stat(path); err != nil { + t.Fatalf("downloaded file missing: %v", err) + } +} + func TestDownloadImageRejectsNilResult(t *testing.T) { t.Parallel() |
