mirror of
https://git.sr.ht/~joren/streamrip-go
synced 2026-06-17 15:05:39 +02:00
harden search parsing and qobuz refresh validation
This commit is contained in:
@@ -13,6 +13,7 @@ import (
|
||||
"net/url"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"runtime"
|
||||
"strconv"
|
||||
@@ -1995,6 +1996,12 @@ func parseSearchArgs(args []string, defaultLimit int) (searchOptions, error) {
|
||||
first := false
|
||||
outputFile := ""
|
||||
for i := 0; i < len(args); i++ {
|
||||
if args[i] == "--" {
|
||||
if i+1 < len(args) {
|
||||
parts = append(parts, args[i+1:]...)
|
||||
}
|
||||
break
|
||||
}
|
||||
switch args[i] {
|
||||
case "--force", "--ignore-db":
|
||||
ignoreDB = true
|
||||
@@ -2010,6 +2017,9 @@ func parseSearchArgs(args []string, defaultLimit int) (searchOptions, error) {
|
||||
return searchOptions{}, fmt.Errorf("--output-file requires a path")
|
||||
}
|
||||
outputFile = strings.TrimSpace(args[i+1])
|
||||
if outputFile == "" {
|
||||
return searchOptions{}, fmt.Errorf("--output-file requires a non-empty path")
|
||||
}
|
||||
i++
|
||||
continue
|
||||
case "--num-results":
|
||||
@@ -2036,6 +2046,9 @@ func parseSearchArgs(args []string, defaultLimit int) (searchOptions, error) {
|
||||
i++
|
||||
continue
|
||||
}
|
||||
if strings.HasPrefix(args[i], "-") {
|
||||
return searchOptions{}, fmt.Errorf("unknown option %q", args[i])
|
||||
}
|
||||
parts = append(parts, args[i])
|
||||
}
|
||||
return searchOptions{
|
||||
@@ -2196,6 +2209,12 @@ func writeSearchResultsToFile(source, mediaType string, results []searchResult,
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
dir := filepath.Dir(path)
|
||||
if dir != "" && dir != "." {
|
||||
if err = os.MkdirAll(dir, 0o755); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return os.WriteFile(path, b, 0o644)
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,9 @@ package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
@@ -198,6 +201,38 @@ func TestParseSearchArgsAllowsFirstAndOutputFileButCallerCanReject(t *testing.T)
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseSearchArgsRejectsUnknownOption(t *testing.T) {
|
||||
_, err := parseSearchArgs([]string{"query", "--bogus"}, 20)
|
||||
if err == nil || !strings.Contains(err.Error(), "unknown option") {
|
||||
t.Fatalf("expected unknown option error, got %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseSearchArgsSupportsDoubleDashTerminator(t *testing.T) {
|
||||
opts, err := parseSearchArgs([]string{"--limit", "10", "--", "--weird", "track"}, 20)
|
||||
if err != nil {
|
||||
t.Fatalf("parseSearchArgs() error = %v", err)
|
||||
}
|
||||
if opts.limit != 10 {
|
||||
t.Fatalf("limit = %d, want 10", opts.limit)
|
||||
}
|
||||
if opts.query != "--weird track" {
|
||||
t.Fatalf("query = %q, want %q", opts.query, "--weird track")
|
||||
}
|
||||
}
|
||||
|
||||
func TestWriteSearchResultsToFileCreatesParentDirectory(t *testing.T) {
|
||||
tmp := t.TempDir()
|
||||
out := filepath.Join(tmp, "nested", "search", "results.json")
|
||||
results := []searchResult{{ID: "1", Title: "Dreams"}}
|
||||
if err := writeSearchResultsToFile("qobuz", "track", results, out); err != nil {
|
||||
t.Fatalf("writeSearchResultsToFile() error = %v", err)
|
||||
}
|
||||
if _, err := os.Stat(out); err != nil {
|
||||
t.Fatalf("expected output file, stat error = %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestErrorWithActionableHintForSSL(t *testing.T) {
|
||||
err := errors.New("x509: certificate signed by unknown authority")
|
||||
msg := errorWithActionableHint(err, globalOptions{})
|
||||
|
||||
Reference in New Issue
Block a user