summaryrefslogtreecommitdiff
path: root/internal/image/download.go
diff options
context:
space:
mode:
Diffstat (limited to 'internal/image/download.go')
-rw-r--r--internal/image/download.go38
1 files changed, 34 insertions, 4 deletions
diff --git a/internal/image/download.go b/internal/image/download.go
index 7a196fa..d66b946 100644
--- a/internal/image/download.go
+++ b/internal/image/download.go
@@ -111,7 +111,7 @@ func (d *Downloader) DownloadImage(ctx context.Context, result *SearchResult, ou
return fmt.Errorf("sync output file %q: %w", outputPath, err)
}
- if attribution := d.provider.GetAttribution(result); attribution != "" {
+ if attribution := strings.TrimSpace(result.Attribution); attribution != "" {
attrPath := strings.TrimSuffix(outputPath, filepath.Ext(outputPath)) + "_attribution.txt"
if err := os.WriteFile(attrPath, []byte(attribution), 0o644); err != nil {
fmt.Fprintf(os.Stderr, "Warning: failed to save attribution: %v\n", err)
@@ -154,7 +154,11 @@ func (d *Downloader) DownloadBestMatchWithOptions(ctx context.Context, opts *Sea
if d.options != nil && d.options.OutputDir != "" {
outputDir = d.options.OutputDir
}
- outputPath := filepath.Join(outputDir, filename)
+ outputPath, err := joinWithinBaseDir(outputDir, filename)
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "Warning: refusing unsafe output path %q: %v\n", filename, err)
+ continue
+ }
if err := d.DownloadImage(ctx, &result, outputPath); err == nil {
return &result, outputPath, nil
@@ -177,8 +181,8 @@ func (d *Downloader) generateFileName(word string, result *SearchResult, index i
filename = strings.ReplaceAll(filename, "{word}", sanitizeFileName(word))
if result != nil {
- filename = strings.ReplaceAll(filename, "{source}", result.Source)
- filename = strings.ReplaceAll(filename, "{id}", result.ID)
+ filename = strings.ReplaceAll(filename, "{source}", sanitizeFileName(result.Source))
+ filename = strings.ReplaceAll(filename, "{id}", sanitizeFileName(result.ID))
}
filename = strings.ReplaceAll(filename, "{index}", fmt.Sprintf("%d", index))
@@ -199,6 +203,32 @@ func (d *Downloader) generateFileName(word string, result *SearchResult, index i
return filename
}
+func joinWithinBaseDir(baseDir, name string) (string, error) {
+ if strings.TrimSpace(baseDir) == "" {
+ baseDir = "."
+ }
+
+ cleanBase, err := filepath.Abs(baseDir)
+ if err != nil {
+ return "", fmt.Errorf("resolve base dir: %w", err)
+ }
+
+ fullPath, err := filepath.Abs(filepath.Join(cleanBase, name))
+ if err != nil {
+ return "", fmt.Errorf("resolve output path: %w", err)
+ }
+
+ rel, err := filepath.Rel(cleanBase, fullPath)
+ if err != nil {
+ return "", fmt.Errorf("relativize output path: %w", err)
+ }
+ if rel == ".." || strings.HasPrefix(rel, ".."+string(os.PathSeparator)) {
+ return "", fmt.Errorf("path escapes base dir")
+ }
+
+ return fullPath, nil
+}
+
func sanitizeFileName(name string) string {
replacer := strings.NewReplacer(
"/", "_",