summaryrefslogtreecommitdiff
path: root/internal/image/download_test.go
diff options
context:
space:
mode:
Diffstat (limited to 'internal/image/download_test.go')
-rw-r--r--internal/image/download_test.go77
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()