What went wrong
Navidrome's native artist API accepts a role parameter used to select categories such as album artist or composer. The set of meaningful roles is small, so the value looks like an enum. At the HTTP boundary, however, it is still an arbitrary string supplied by the requester.
The filtering path checked that string against Navidrome's known roles. The sorting path did not. When the artist list was ordered by song count, album count or size, the repository inserted the raw role inside a JSON-path expression and stored the resulting text in its sort mapping. That mapping later entered the SQL ORDER BY clause as a trusted fragment.
This distinction matters because values and SQL structure are protected differently. A database placeholder can safely carry a value, but it cannot stand in for an identifier or a complete ordering expression. Once request data was used to construct that expression, characters in the role stopped being data and became SQL syntax.
Argo surfaced the unsafe sort path. I traced the value from the authenticated artist endpoint into the unparameterised ordering expression before reporting it privately. The published advisory credits @Sudo-Ivan and @gigioneggiando as independent reporters.
Why it mattered
The vulnerable endpoint was available to any user who could log in to the web interface; administrator privileges were not required. The advisory states that the resulting injection could execute arbitrary SQL against navidrome.db, allowing access to other users' data and stored secrets as well as modification or deletion of database contents.
The upstream advisory rates the issue High with a CVSS 3.1 score of 8.8. It lists versions from 0.55.0 up to, but not including, 0.64.0 as affected. The native API used by the web interface was vulnerable, while the Subsonic API was not. At publication time, the advisory has no assigned CVE.
This was therefore not an unauthenticated compromise of every Navidrome installation. It required a valid account and a reachable native API. Within that boundary, however, a low-privileged user could reach a database operation that should never have been controlled by an artist-list option.
How it was fixed
The repair introduces one helper that converts the requested role into a safe application value. Empty or aggregate requests resolve to the fixed total key, known roles are accepted, and unknown strings are rejected instead of entering SQL construction.
Both filtering and sorting now use that shared allow-list. The sort mapping is built only after the role has crossed it, so every expression that reaches ORDER BY contains a value chosen by the application rather than text chosen by the requester. Regression tests cover accepted roles, the aggregate case and malicious or unknown input.
The fix shipped in Navidrome v0.64.0. The advisory lists no workaround, so affected installations should upgrade rather than rely on account trust or on filtering elsewhere in the request path.
After the patch
A value does not become safe because the application intends to use it like an enum. It becomes safe only when the program turns the external string into an internal choice and refuses everything outside that choice.
This is especially important around query builders. Their value parameters may be safe while methods for ordering, grouping, identifiers or expressions still accept raw SQL. When reviewing those calls, follow each fragment back to its origin and look for duplicated validation: one protected filter path does not protect a neighbouring sort path. The strongest design maps a validated semantic option to a fixed expression and never lets the request supply the expression itself.