diff --git a/backend/internal/kvm/kvm_test.go b/backend/internal/kvm/kvm_test.go index fdfa5ec..76bf438 100644 --- a/backend/internal/kvm/kvm_test.go +++ b/backend/internal/kvm/kvm_test.go @@ -3,6 +3,7 @@ package kvm import ( "crypto/ed25519" "crypto/rand" + "path/filepath" "reflect" "testing" @@ -11,14 +12,15 @@ import ( "golang.org/x/crypto/ssh" ) -func TestLocalImageIDRejectsPathExpressions(t *testing.T) { - for _, id := range []string{"", ".", "..", "../../etc/passwd", `..\\..\\windows`, "/absolute"} { - if got := localImageID(id); got != "__invalid_image_id__" { - t.Fatalf("localImageID(%q) = %q", id, got) +func TestImagePathUsesAllowlistedImageID(t *testing.T) { + for _, id := range []string{"", ".", "..", "../../etc/passwd", `..\\..\\windows`, "/absolute", "unknown-image"} { + if got := filepath.Base(ImagePath(id)); got != "__invalid_image_id__.qcow2" { + t.Fatalf("ImagePath(%q) basename = %q", id, got) } } - if got := localImageID("debian-13-kvm"); got != "debian-13-kvm" { - t.Fatalf("localImageID(valid) = %q", got) + validID := GetImages()[0].ID + if got := filepath.Base(ImagePath(validID)); got != validID+".qcow2" { + t.Fatalf("ImagePath(%q) basename = %q", validID, got) } } diff --git a/backend/internal/kvm/templates.go b/backend/internal/kvm/templates.go index d36777d..5ca1e9e 100644 --- a/backend/internal/kvm/templates.go +++ b/backend/internal/kvm/templates.go @@ -4,7 +4,6 @@ import ( "os" "path/filepath" "runtime" - "strings" "clicd/internal/config" ) @@ -193,10 +192,14 @@ func CacheDir() string { func ImagePath(id string) string { img := FindImage(id) ext := ".qcow2" + safeID := "__invalid_image_id__" + if img != nil { + safeID = img.ID + } if img != nil && img.Distro == "windows" { ext = ".iso" } - fileName := localImageID(id) + ext + fileName := safeID + ext for _, pool := range config.StoragePoolsForContent(config.StorageContentImages) { candidate := filepath.Join(pool.Path, "images", "kvm", fileName) if info, err := os.Stat(candidate); err == nil && !info.IsDir() { @@ -210,15 +213,6 @@ func ImagePath(id string) string { return filepath.Join(CacheDir(), fileName) } -func localImageID(id string) string { - trimmed := strings.TrimSpace(id) - local := filepath.Base(trimmed) - if trimmed == "" || local == "." || local == ".." || local != trimmed || strings.ContainsAny(trimmed, `/\\`) { - return "__invalid_image_id__" - } - return local -} - // IsWindowsImage returns true if the image distro is "windows". func IsWindowsImage(id string) bool { img := FindImage(id) diff --git a/backend/internal/lxc/lxc.go b/backend/internal/lxc/lxc.go index 12c7255..01efbe4 100644 --- a/backend/internal/lxc/lxc.go +++ b/backend/internal/lxc/lxc.go @@ -708,7 +708,7 @@ func (m *Manager) applyLANIPv4Config(lxcName string, cfg ContainerConfig) (strin values["lxc.net.0.ipv4.gateway"] = strings.TrimSpace(cfg.LANIPv4Gateway) } seen := map[string]bool{} - next := make([]string, 0, len(lines)+len(values)) + next := make([]string, 0, len(lines)) for _, line := range lines { trimmed := strings.TrimSpace(line) if !cfg.WantsLANStaticIPv4() && (strings.HasPrefix(trimmed, "lxc.net.0.ipv4.address") || strings.HasPrefix(trimmed, "lxc.net.0.ipv4.gateway")) {