Skip to content

fix: NZBGet protocol selector, imageproxy race, CI hardening#396

Merged
vavallee merged 1 commit intomainfrom
fix/nzbget-imageproxy-ci
Apr 25, 2026
Merged

fix: NZBGet protocol selector, imageproxy race, CI hardening#396
vavallee merged 1 commit intomainfrom
fix/nzbget-imageproxy-ci

Conversation

@vavallee
Copy link
Copy Markdown
Owner

Summary

  • NZBGet completely broken for grabsGetFirstEnabledByProtocol and GetEnabledByProtocol only queried sabnzbd for the usenet protocol; NZBGet rows were never returned. Any user with only NZBGet configured got "no enabled download clients" on every grab. Added nzbget to both usenet IN-lists.
  • NZBGet credentials zeroed on readhydrateClientCredentials blanked username/password for all non-qBit/Transmission clients, silently wiping NZBGet auth before it reached the adapter. Fixed with a switch so nzbget/deluge pass credentials through as-is.
  • Deluge missing from torrent protocol query — both protocol-selector functions were also missing deluge from the torrent IN list. Fixed in the same pass.
  • Imageproxy concurrent-write race (TestImageProxy_ConcurrentSameURL failing on v1.2.5 tag CI) — all goroutines serving the same URL wrote to the shared imgFile+".tmp" path; one goroutine's O_TRUNC open could zero the file while another renamed it into the cache. Replaced fixed .tmp paths with os.CreateTemp() so each goroutine gets a unique temp file. Test now passes under -race.
  • Gitleaks false positive silencedinternal/api/auth_test.go session-secret fixture string was flagging the Security workflow on every push. Added # gitleaks:allow inline and test-secret- to .gitleaks.toml stopwords.
  • OpenSSF Scorecard fixes — scoped promote.yml token permissions to job level (was top-level contents: write); pinned govulncheck install to commit hash instead of semver tag to resolve downloadThenRun.

Test plan

  • go build ./... — clean
  • go test ./internal/db/... ./internal/api/... — pass
  • go test -race -run TestImageProxy ./internal/api/... — pass (was failing before fix)

🤖 Generated with Claude Code

…ardening

NZBGet/Deluge download client bugs (internal/db/download_clients.go):
- GetFirstEnabledByProtocol and GetEnabledByProtocol only queried
  "sabnzbd" for the usenet protocol — NZBGet rows were never returned,
  causing "no enabled download clients" for any user with only NZBGet
  configured. Added "nzbget" to both usenet IN-lists.
- Torrent protocol queries were also missing "deluge". Fixed both
  GetFirstEnabledByProtocol and GetEnabledByProtocol to include it.
- hydrateClientCredentials blanked username/password for all non-qBit/
  Transmission clients, silently zeroing NZBGet and Deluge credentials
  on every read. Switched to a switch statement so nzbget and deluge
  pass through as-is while sabnzbd (API-key auth) still gets zeroed.

Imageproxy concurrent-write race (internal/api/imageproxy.go):
- All concurrent goroutines fetching the same image URL wrote to the
  shared path imgFile+".tmp". One goroutine's O_TRUNC open could zero
  the file mid-write while another goroutine renamed it into place,
  resulting in an empty cache file served to subsequent cache-hit
  requests. Replaced both fixed .tmp paths with os.CreateTemp() so each
  goroutine gets a unique temp file; TestImageProxy_ConcurrentSameURL
  now passes under -race.

CI/security hardening:
- auth_test.go: add gitleaks:allow to session-secret fixture string to
  stop the Security workflow failing on every push.
- .gitleaks.toml: add "test-secret-" to global stopwords as a fallback.
- promote.yml: move contents+PR permissions to job scope; set top-level
  to {} to satisfy OpenSSF TokenPermissions check.
- ci.yml: pin govulncheck install to commit hash (v1.1.4 =
  d1f380186385b4f64e00313f31743df8e4b89a77) to resolve downloadThenRun.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@vavallee vavallee merged commit 4f6e1d6 into main Apr 25, 2026
20 of 21 checks passed
if _, werr := f.Write(body); werr == nil {
_ = f.Chmod(imageCacheMode)
f.Close()
_ = os.Rename(f.Name(), imgFile) // #nosec G304
_ = os.Rename(f.Name(), imgFile) // #nosec G304
} else {
f.Close()
_ = os.Remove(f.Name())
if _, werr := f.Write([]byte(ct)); werr == nil {
_ = f.Chmod(imageCacheMode)
f.Close()
_ = os.Rename(f.Name(), ctFile) // #nosec G304
_ = os.Rename(f.Name(), ctFile) // #nosec G304
} else {
f.Close()
_ = os.Remove(f.Name())
if f, ferr := os.CreateTemp(filepath.Dir(imgFile), ".img-*"); ferr == nil { // #nosec G304
if _, werr := f.Write(body); werr == nil {
_ = f.Chmod(imageCacheMode)
f.Close()
f.Close()
_ = os.Rename(f.Name(), imgFile) // #nosec G304
} else {
f.Close()
if f, ferr := os.CreateTemp(filepath.Dir(imgFile), ".ct-*"); ferr == nil { // #nosec G304
if _, werr := f.Write([]byte(ct)); werr == nil {
_ = f.Chmod(imageCacheMode)
f.Close()
f.Close()
_ = os.Rename(f.Name(), ctFile) // #nosec G304
} else {
f.Close()
@codecov
Copy link
Copy Markdown

codecov Bot commented Apr 25, 2026

Codecov Report

❌ Patch coverage is 64.28571% with 10 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
internal/api/imageproxy.go 62.50% 4 Missing and 2 partials ⚠️
internal/db/download_clients.go 66.66% 4 Missing ⚠️

📢 Thoughts on this report? Let us know!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants