navidrome/core/artwork
Deluan Quintão 237276efcd
fix(artwork): block private and loopback addresses in remote image fetches (#6181)
* fix(artwork): block private and loopback addresses in remote image fetches

fromURL fetched any URL with a plain HTTP client, and two untrusted inputs reach it. A playlist
can set #EXTALBUMARTURL to an http(s) URL, which the artwork worker later fetches when
EnableM3UExternalAlbumArt is on, so any user who can import a playlist controls the target.
Metadata agents, including WASM plugins without the http permission, return image URLs that the
core fetches too. Either path could make the server request loopback, LAN or link-local
addresses and store the response as artwork that is served back.

Add httpclient.NewExternal, which dials through a net.Dialer Control hook that rejects private,
loopback, link-local and unspecified addresses. The check runs at dial time on the resolved IP,
so DNS names, redirects and DNS rebinding are covered. fromURL now uses one shared client built
with it and treats a refused address as a definitive miss, so the item settles absent instead of
retrying and tripping the agent's circuit breaker. httpclient.New is unchanged for the other callers.

The IP classification moves from plugins to the new utils/netguard package, shared by the plugin
host client and the new constructor. The artwork test suite swaps in a client that allows
loopback so existing specs can keep using httptest servers; the fromURL specs use the production
client to assert the refusal.

* fix(httpclient): keep dialing a configured proxy in the guarded client

The guard runs on the resolved address, and with HTTP_PROXY set that address is the proxy, not
the image host. A proxy on a private address would have had every remote artwork fetch refused,
and a refusal settles the item as absent, so covers would silently disappear for those setups.

Dial the configured proxy endpoint directly and keep the guard for every other dial. A proxy
relays the request itself, so it is the operator's egress policy, the same one every other
httpclient.New caller already goes through.

* fix(httpclient): exempt only the hop that actually goes through the proxy

The exemption matched any dial to a configured proxy's address, but net/http never proxies
loopback targets, so a URL aimed at a loopback proxy was dialed directly and skipped the guard.
That let an image URL reach that one address.

Tag each request with the proxy it resolves to and exempt a dial only when it is that hop.
Redirects re-enter the RoundTripper, so every hop is tagged on its own.
2026-09-20 20:46:46 -04:00
..
blurhash perf(artwork): compute the blurhash DCT separably (#5989) 2026-08-19 16:20:38 -04:00
dominant feat(artwork): new artwork pipeline with background resolution and Low Quality Image Placeholders (#5847) 2026-08-09 15:03:27 -04:00
e2e fix(test): stop the Windows test job from failing at random (#6182) 2026-09-20 15:27:20 -04:00
thumbhash feat(artwork): new artwork pipeline with background resolution and Low Quality Image Placeholders (#5847) 2026-08-09 15:03:27 -04:00
agent_images.go fix(scrobbler): back off when a provider asks us to, instead of retrying per play (#6028) 2026-08-29 17:28:29 -04:00
agent_images_test.go fix(scrobbler): back off when a provider asks us to, instead of retrying per play (#6028) 2026-08-29 17:28:29 -04:00
animation.go feat(artwork): preserve animated image artwork during resize (#5184) 2026-03-13 18:11:12 -04:00
animation_test.go feat(artwork): preserve animated image artwork during resize (#5184) 2026-03-13 18:11:12 -04:00
artwork.go fix(artwork): only use image files as local artwork sources (#6180) 2026-09-20 13:40:14 -04:00
artwork_suite_test.go fix(artwork): block private and loopback addresses in remote image fetches (#6181) 2026-09-20 20:46:46 -04:00
artwork_test.go fix(artwork): only use image files as local artwork sources (#6180) 2026-09-20 13:40:14 -04:00
benchmark_decode_test.go perf(artwork): improve image serving performance with WebP encoding and optimized pipeline (#5181) 2026-03-13 09:35:59 -04:00
benchmark_encode_test.go perf(artwork): improve image serving performance with WebP encoding and optimized pipeline (#5181) 2026-03-13 09:35:59 -04:00
benchmark_helpers_test.go feat(artwork): new artwork pipeline with background resolution and Low Quality Image Placeholders (#5847) 2026-08-09 15:03:27 -04:00
benchmark_pipeline_test.go feat(artwork): preserve animated image artwork during resize (#5184) 2026-03-13 18:11:12 -04:00
benchmark_tag_test.go perf(artwork): improve image serving performance with WebP encoding and optimized pipeline (#5181) 2026-03-13 09:35:59 -04:00
disc.go feat(cli): add an artwork command group for diagnosing and re-driving artwork (#5957) 2026-08-14 21:07:56 -04:00
disc_test.go feat(cli): add an artwork command group for diagnosing and re-driving artwork (#5957) 2026-08-14 21:07:56 -04:00
folders_album.go feat(artwork): new artwork pipeline with background resolution and Low Quality Image Placeholders (#5847) 2026-08-09 15:03:27 -04:00
folders_album_test.go feat(artwork): new artwork pipeline with background resolution and Low Quality Image Placeholders (#5847) 2026-08-09 15:03:27 -04:00
folders_artist.go feat(artwork): new artwork pipeline with background resolution and Low Quality Image Placeholders (#5847) 2026-08-09 15:03:27 -04:00
folders_artist_paths_test.go feat(artwork): new artwork pipeline with background resolution and Low Quality Image Placeholders (#5847) 2026-08-09 15:03:27 -04:00
folders_artist_test.go feat(artwork): new artwork pipeline with background resolution and Low Quality Image Placeholders (#5847) 2026-08-09 15:03:27 -04:00
gate.go fix(artwork): honor a provider's explicit retry-later delay in the circuit breaker (#6056) 2026-09-01 11:07:58 -04:00
gate_test.go fix(artwork): honor a provider's explicit retry-later delay in the circuit breaker (#6056) 2026-09-01 11:07:58 -04:00
hash_encoder_bench_test.go feat(artwork): new artwork pipeline with background resolution and Low Quality Image Placeholders (#5847) 2026-08-09 15:03:27 -04:00
housekeeping.go fix(artwork): never retry absent artwork on its own (#6054) 2026-09-01 20:48:41 -04:00
housekeeping_test.go fix(artwork): never retry absent artwork on its own (#6054) 2026-09-01 20:48:41 -04:00
image_cache.go feat(cli): add an artwork command group for diagnosing and re-driving artwork (#5957) 2026-08-14 21:07:56 -04:00
image_cache_test.go feat(cli): add an artwork command group for diagnosing and re-driving artwork (#5957) 2026-08-14 21:07:56 -04:00
image_store.go feat(artwork): new artwork pipeline with background resolution and Low Quality Image Placeholders (#5847) 2026-08-09 15:03:27 -04:00
image_store_test.go feat(artwork): new artwork pipeline with background resolution and Low Quality Image Placeholders (#5847) 2026-08-09 15:03:27 -04:00
library_fs.go feat(artwork): new artwork pipeline with background resolution and Low Quality Image Placeholders (#5847) 2026-08-09 15:03:27 -04:00
library_fs_test.go feat(artwork): new artwork pipeline with background resolution and Low Quality Image Placeholders (#5847) 2026-08-09 15:03:27 -04:00
playlist_cover.go feat(artwork): new artwork pipeline with background resolution and Low Quality Image Placeholders (#5847) 2026-08-09 15:03:27 -04:00
processor.go fix(scrobbler): back off when a provider asks us to, instead of retrying per play (#6028) 2026-08-29 17:28:29 -04:00
processor_test.go fix(scrobbler): back off when a provider asks us to, instead of retrying per play (#6028) 2026-08-29 17:28:29 -04:00
prune.go feat(artwork): new artwork pipeline with background resolution and Low Quality Image Placeholders (#5847) 2026-08-09 15:03:27 -04:00
prune_test.go feat(artwork): new artwork pipeline with background resolution and Low Quality Image Placeholders (#5847) 2026-08-09 15:03:27 -04:00
resize.go fix: miscellaneous fixes for shares, artwork resize, auth limits, and watcher start (#6098) 2026-09-11 15:03:54 -04:00
resize_test.go fix: miscellaneous fixes for shares, artwork resize, auth limits, and watcher start (#6098) 2026-09-11 15:03:54 -04:00
resolve.go fix(artwork): only use image files as local artwork sources (#6180) 2026-09-20 13:40:14 -04:00
resolve_test.go fix(artwork): only use image files as local artwork sources (#6180) 2026-09-20 13:40:14 -04:00
sources.go fix(artwork): block private and loopback addresses in remote image fetches (#6181) 2026-09-20 20:46:46 -04:00
sources_internal_test.go fix(artwork): block private and loopback addresses in remote image fetches (#6181) 2026-09-20 20:46:46 -04:00
trace.go feat(artwork): store the resolution trace so artwork explain works offline (#5980) 2026-08-21 10:24:01 -04:00
trace_test.go feat(artwork): store the resolution trace so artwork explain works offline (#5980) 2026-08-21 10:24:01 -04:00
uploader.go feat(artwork): make the artwork image size cap configurable (#5931) 2026-08-11 10:42:39 -04:00
uploader_test.go feat(artwork): new artwork pipeline with background resolution and Low Quality Image Placeholders (#5847) 2026-08-09 15:03:27 -04:00
wire_providers.go feat(artwork): new artwork pipeline with background resolution and Low Quality Image Placeholders (#5847) 2026-08-09 15:03:27 -04:00
worker.go fix(artwork): never retry absent artwork on its own (#6054) 2026-09-01 20:48:41 -04:00
worker_soak_test.go fix(scrobbler): back off when a provider asks us to, instead of retrying per play (#6028) 2026-08-29 17:28:29 -04:00
worker_test.go fix(artwork): never retry absent artwork on its own (#6054) 2026-09-01 20:48:41 -04:00
worker_timing_test.go fix(artwork): ramp the external circuit breaker back up instead of closing on one answer (#5961) 2026-08-15 10:36:14 -04:00