fix(scanner): prevent infinite recursion in pid configuration

closes #4920

Signed-off-by: Deluan <deluan@navidrome.org>
This commit is contained in:
Deluan
2026-01-21 13:44:14 -05:00
parent b1b488be77
commit 1c4a7e8556
2 changed files with 25 additions and 2 deletions
+7 -2
View File
@@ -8,6 +8,7 @@ import (
"github.com/navidrome/navidrome/conf" "github.com/navidrome/navidrome/conf"
"github.com/navidrome/navidrome/consts" "github.com/navidrome/navidrome/consts"
"github.com/navidrome/navidrome/log"
"github.com/navidrome/navidrome/model" "github.com/navidrome/navidrome/model"
"github.com/navidrome/navidrome/model/id" "github.com/navidrome/navidrome/model/id"
"github.com/navidrome/navidrome/utils" "github.com/navidrome/navidrome/utils"
@@ -26,10 +27,14 @@ type getPIDFunc = func(mf model.MediaFile, md Metadata, spec string, prependLibI
func createGetPID(hash hashFunc) getPIDFunc { func createGetPID(hash hashFunc) getPIDFunc {
var getPID getPIDFunc var getPID getPIDFunc
getAttr := func(mf model.MediaFile, md Metadata, attr string, prependLibId bool) string { getAttr := func(mf model.MediaFile, md Metadata, attr string, prependLibId bool, spec string) string {
attr = strings.TrimSpace(strings.ToLower(attr)) attr = strings.TrimSpace(strings.ToLower(attr))
switch attr { switch attr {
case "albumid": case "albumid":
if spec == conf.Server.PID.Album {
log.Error("Recursive PID definition detected, ignoring `albumid`", "spec", spec)
return ""
}
return getPID(mf, md, conf.Server.PID.Album, prependLibId) return getPID(mf, md, conf.Server.PID.Album, prependLibId)
case "folder": case "folder":
return filepath.Dir(mf.Path) return filepath.Dir(mf.Path)
@@ -49,7 +54,7 @@ func createGetPID(hash hashFunc) getPIDFunc {
attributes := strings.Split(field, ",") attributes := strings.Split(field, ",")
hasValue := false hasValue := false
values := slice.Map(attributes, func(attr string) string { values := slice.Map(attributes, func(attr string) string {
v := getAttr(mf, md, attr, prependLibId) v := getAttr(mf, md, attr, prependLibId, spec)
if v != "" { if v != "" {
hasValue = true hasValue = true
} }
+18
View File
@@ -114,6 +114,24 @@ var _ = Describe("getPID", func() {
Expect(getPID(mf, md, spec, false)).To(Equal("(album name)")) Expect(getPID(mf, md, spec, false)).To(Equal("(album name)"))
}) })
}) })
When("albumid configuration refers to albumid recursively", func() {
It("should avoid infinite recursion", func() {
// Reproduce the issue from #4920
conf.Server.PID.Album = "albumid,album,albumversion,releasedate"
spec := conf.Server.PID.Album
md.tags = map[model.TagName][]string{
"album": {"Album Name"},
"albumversion": {"Version"},
"releasedate": {"2022"},
}
// Should not panic and return a valid PID ignoring the recursive "albumid"
Expect(func() {
pid := getPID(mf, md, spec, false)
Expect(pid).To(Equal("(\\album name\\Version\\2022)"))
}).To(Not(Panic()))
})
})
}) })
Context("edge cases", func() { Context("edge cases", func() {