VYPR
Critical severityNVD Advisory· Published May 6, 2022· Updated Apr 23, 2025

Improper path handling in kustomization files allows path traversal

CVE-2022-24877

Description

Flux is an open and extensible continuous delivery solution for Kubernetes. Path Traversal in the kustomize-controller via a malicious kustomization.yaml allows an attacker to expose sensitive data from the controller’s pod filesystem and possibly privilege escalation in multi-tenancy deployments. Workarounds include automated tooling in the user's CI/CD pipeline to validate kustomization.yaml files conform with specific policies. This vulnerability is fixed in kustomize-controller v0.24.0 and included in flux2 v0.29.0.

AI Insight

LLM-synthesized narrative grounded in this CVE's description and references.

Path traversal in Flux kustomize-controller via malicious kustomization.yaml allows file disclosure and potential privilege escalation in multi-tenancy Kubernetes clusters.

Vulnerability

A path traversal vulnerability exists in the Flux kustomize-controller versions prior to v0.24.0 (and hence in flux2 prior to v0.29.0). By crafting a malicious kustomization.yaml, an attacker can leverage Kustomize's built-in file system operations to read files outside the intended working directory. The insecure path handling permitted references to arbitrary paths on the controller's pod filesystem, bypassing the intended sandbox constraints [1], [2], [4].

Exploitation

An attacker needs write access to a Flux source (e.g., a Git repository or OCI artifact) that is reconciled by the kustomize-controller. By injecting a kustomization.yaml that uses Kustomize features like patches, configMapGenerator, or secretGenerator with file paths such as ../../some/sensitive/file, the controller will read and process those files. In multi-tenancy environments, any user who can push changes to a Flux source can craft this attack without further authentication [4].

Impact

Successful exploitation allows an attacker to read arbitrary files from the kustomize-controller pod's filesystem, including secrets, configuration data, and service account tokens. If the controller's service account has elevated permissions (common in multi-tenancy setups), this can lead to privilege escalation within the cluster. The vulnerability does not directly allow remote code execution, but the information disclosure can be leveraged for further compromise [2], [4].

Mitigation

The vulnerability is fixed in kustomize-controller v0.24.0 and flux2 v0.29.0, released on 2022-04-20. The fix introduces a secure file system implementation (in the fluxcd/pkg/kustomize package) that validates all file paths are within the kustomization's working directory [1], [3]. Users unable to upgrade can employ automated tooling in their CI/CD pipeline to validate that kustomization.yaml files do not reference paths outside allowed directories, thereby blocking exploitation [2], [4].

AI Insight generated on May 21, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
github.com/fluxcd/kustomize-controllerGo
< 0.24.00.24.0
github.com/fluxcd/flux2Go
< 0.29.00.29.0

Affected products

11

Patches

2
0ec014baf417

kustomize: introduce secure FS implementation

https://github.com/fluxcd/pkgHidde BeydalsApr 15, 2022via ghsa
2 files changed · +783 0
  • kustomize/filesys/fs_secure.go+232 0 added
    @@ -0,0 +1,232 @@
    +/*
    +Copyright 2022 The Flux authors
    +
    +Licensed under the Apache License, Version 2.0 (the "License");
    +you may not use this file except in compliance with the License.
    +You may obtain a copy of the License at
    +
    +    http://www.apache.org/licenses/LICENSE-2.0
    +
    +Unless required by applicable law or agreed to in writing, software
    +distributed under the License is distributed on an "AS IS" BASIS,
    +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +See the License for the specific language governing permissions and
    +limitations under the License.
    +*/
    +
    +package filesys
    +
    +import (
    +	"fmt"
    +	"os"
    +	"path/filepath"
    +
    +	"sigs.k8s.io/kustomize/kyaml/filesys"
    +)
    +
    +// MakeFsOnDiskSecure returns a secure file system which asserts any paths it
    +// handles to be inside root.
    +func MakeFsOnDiskSecure(root string) (filesys.FileSystem, error) {
    +	unsafeFS := filesys.MakeFsOnDisk()
    +	cleanedAbs, _, err := unsafeFS.CleanedAbs(root)
    +	if err != nil {
    +		return nil, err
    +	}
    +	return fsSecure{root: cleanedAbs, unsafeFS: unsafeFS}, nil
    +}
    +
    +// fsSecure wraps an unsafe FileSystem implementation, and secures it
    +// by confirming paths are inside root.
    +type fsSecure struct {
    +	root     filesys.ConfirmedDir
    +	unsafeFS filesys.FileSystem
    +}
    +
    +// ConstraintError records an error and the operation and file that
    +// violated it.
    +type ConstraintError struct {
    +	Op   string
    +	Path string
    +	Err  error
    +}
    +
    +func (e *ConstraintError) Error() string {
    +	return "fs-security-constraint " + e.Op + " " + e.Path + ": " + e.Err.Error()
    +}
    +
    +func (e *ConstraintError) Unwrap() error { return e.Err }
    +
    +// Create delegates to the embedded unsafe FS after having confirmed the path
    +// to be inside root. If the provided path violates this constraint, an error
    +// of type ConstraintError is returned.
    +func (fs fsSecure) Create(path string) (filesys.File, error) {
    +	if err := isSecurePath(fs.unsafeFS, fs.root, path); err != nil {
    +		return nil, &ConstraintError{Op: "create", Path: path, Err: err}
    +	}
    +	return fs.unsafeFS.Create(path)
    +}
    +
    +// Mkdir delegates to the embedded unsafe FS after having confirmed the path
    +// to be inside root. If the provided path violates this constraint, an error
    +// of type ConstraintError is returned.
    +func (fs fsSecure) Mkdir(path string) error {
    +	if err := isSecurePath(fs.unsafeFS, fs.root, path); err != nil {
    +		return &ConstraintError{Op: "mkdir", Path: path, Err: err}
    +	}
    +	return fs.unsafeFS.Mkdir(path)
    +}
    +
    +// MkdirAll delegates to the embedded unsafe FS after having confirmed the path
    +// to be inside root. If the provided path violates this constraint, an error
    +// type ConstraintError is returned.
    +func (fs fsSecure) MkdirAll(path string) error {
    +	if err := isSecurePath(fs.unsafeFS, fs.root, path); err != nil {
    +		return &ConstraintError{Op: "mkdir", Path: path, Err: err}
    +	}
    +	return fs.unsafeFS.MkdirAll(path)
    +}
    +
    +// RemoveAll delegates to the embedded unsafe FS after having confirmed the
    +// path to be inside root. If the provided path violates this constraint, an
    +// error of type ConstraintError is returned.
    +func (fs fsSecure) RemoveAll(path string) error {
    +	if err := isSecurePath(fs.unsafeFS, fs.root, path); err != nil {
    +		return &ConstraintError{Op: "remove", Path: path, Err: err}
    +	}
    +	return fs.unsafeFS.RemoveAll(path)
    +}
    +
    +// Open delegates to the embedded unsafe FS after having confirmed the path
    +// to be inside root. If the provided path violates this constraint, an error
    +// of type ConstraintError is returned.
    +func (fs fsSecure) Open(path string) (filesys.File, error) {
    +	if err := isSecurePath(fs.unsafeFS, fs.root, path); err != nil {
    +		return nil, &ConstraintError{Op: "open", Path: path, Err: err}
    +	}
    +	return fs.unsafeFS.Open(path)
    +}
    +
    +// IsDir delegates to the embedded unsafe FS after having confirmed the path
    +// to be inside root. If the provided path violates this constraint, it returns
    +// false.
    +func (fs fsSecure) IsDir(path string) bool {
    +	if err := isSecurePath(fs.unsafeFS, fs.root, path); err != nil {
    +		return false
    +	}
    +	return fs.unsafeFS.IsDir(path)
    +}
    +
    +// ReadDir delegates to the embedded unsafe FS after having confirmed the path
    +// to be inside root. If the provided path violates this constraint, an error
    +// of type ConstraintError is returned.
    +func (fs fsSecure) ReadDir(path string) ([]string, error) {
    +	if err := isSecurePath(fs.unsafeFS, fs.root, path); err != nil {
    +		return nil, &ConstraintError{Op: "open", Path: path, Err: err}
    +	}
    +	return fs.unsafeFS.ReadDir(path)
    +}
    +
    +// CleanedAbs delegates to the embedded unsafe FS, but confirms the returned
    +// result to be within root. If the results violates this constraint, an error
    +// of type ConstraintError is returned.
    +// In essence, it functions the same as Kustomize's loader.RestrictionRootOnly,
    +// but on FS levels, and while allowing file paths.
    +func (fs fsSecure) CleanedAbs(path string) (filesys.ConfirmedDir, string, error) {
    +	d, f, err := fs.unsafeFS.CleanedAbs(path)
    +	if err != nil {
    +		return d, f, err
    +	}
    +	if !d.HasPrefix(fs.root) {
    +		return "", "", &ConstraintError{Op: "abs", Path: path, Err: rootConstraintErr(path, fs.root.String())}
    +	}
    +	return d, f, err
    +}
    +
    +// Exists delegates to the embedded unsafe FS after having confirmed the path
    +// to be inside root. If the provided path violates this constraint, it returns
    +// false.
    +func (fs fsSecure) Exists(path string) bool {
    +	if err := isSecurePath(fs.unsafeFS, fs.root, path); err != nil {
    +		return false
    +	}
    +	return fs.unsafeFS.Exists(path)
    +}
    +
    +// Glob delegates to the embedded unsafe FS, but filters the returned paths to
    +// only include items inside root.
    +func (fs fsSecure) Glob(pattern string) ([]string, error) {
    +	paths, err := fs.unsafeFS.Glob(pattern)
    +	if err != nil {
    +		return nil, err
    +	}
    +	var securePaths []string
    +	for _, p := range paths {
    +		if err := isSecurePath(fs.unsafeFS, fs.root, p); err == nil {
    +			securePaths = append(securePaths, p)
    +		}
    +	}
    +	return securePaths, err
    +}
    +
    +// ReadFile delegates to the embedded unsafe FS after having confirmed the path
    +// to be inside root. If the provided path violates this constraint, an error
    +// of type ConstraintError is returned.
    +func (fs fsSecure) ReadFile(path string) ([]byte, error) {
    +	if err := isSecurePath(fs.unsafeFS, fs.root, path); err != nil {
    +		return nil, &ConstraintError{Op: "read", Path: path, Err: err}
    +	}
    +	return fs.unsafeFS.ReadFile(path)
    +}
    +
    +// WriteFile delegates to the embedded unsafe FS after having confirmed the
    +// path to be inside root. If the provided path violates this constraint, an
    +// error of type ConstraintError is returned.
    +func (fs fsSecure) WriteFile(path string, data []byte) error {
    +	if err := isSecurePath(fs.unsafeFS, fs.root, path); err != nil {
    +		return &ConstraintError{Op: "write", Path: path, Err: err}
    +	}
    +	return fs.unsafeFS.WriteFile(path, data)
    +}
    +
    +// Walk delegates to the embedded unsafe FS, wrapping falkFn in a callback which
    +// confirms the path to be inside root. If the path violates this constraint,
    +// an error of type ConstraintError is returned and walkFn is not called.
    +func (fs fsSecure) Walk(path string, walkFn filepath.WalkFunc) error {
    +	wrapWalkFn := func(path string, info os.FileInfo, err error) error {
    +		if err := isSecurePath(fs.unsafeFS, fs.root, path); err != nil {
    +			return &ConstraintError{Op: "walk", Path: path, Err: err}
    +		}
    +		return walkFn(path, info, err)
    +	}
    +	return fs.unsafeFS.Walk(path, wrapWalkFn)
    +}
    +
    +// isSecurePath confirms the given path is inside root using the provided file
    +// system. At present, it assumes the file system implementation to be on disk
    +// and makes use of filepath.EvalSymlinks.
    +func isSecurePath(fs filesys.FileSystem, root filesys.ConfirmedDir, path string) error {
    +	absRoot, err := filepath.Abs(path)
    +	if err != nil {
    +		return fmt.Errorf("abs path error on '%s': %v", path, err)
    +	}
    +	d := filesys.ConfirmedDir(filepath.Dir(absRoot))
    +	if fs.Exists(absRoot) {
    +		evaluated, err := filepath.EvalSymlinks(absRoot)
    +		if err != nil {
    +			return fmt.Errorf("evalsymlink failure on '%s': %w", path, err)
    +		}
    +		evaluatedDir := evaluated
    +		if !fs.IsDir(evaluatedDir) {
    +			evaluatedDir = filepath.Dir(evaluatedDir)
    +		}
    +		d = filesys.ConfirmedDir(evaluatedDir)
    +	}
    +	if !d.HasPrefix(root) {
    +		return rootConstraintErr(path, root.String())
    +	}
    +	return nil
    +}
    +
    +func rootConstraintErr(path, root string) error {
    +	return fmt.Errorf("path '%s' is not in or below '%s'", path, root)
    +}
    
  • kustomize/filesys/fs_secure_test.go+551 0 added
    @@ -0,0 +1,551 @@
    +/*
    +Copyright 2022 The Flux authors
    +
    +Licensed under the Apache License, Version 2.0 (the "License");
    +you may not use this file except in compliance with the License.
    +You may obtain a copy of the License at
    +
    +    http://www.apache.org/licenses/LICENSE-2.0
    +
    +Unless required by applicable law or agreed to in writing, software
    +distributed under the License is distributed on an "AS IS" BASIS,
    +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +See the License for the specific language governing permissions and
    +limitations under the License.
    +*/
    +
    +package filesys
    +
    +import (
    +	"bytes"
    +	"io"
    +	"math/rand"
    +	"os"
    +	"path/filepath"
    +	"strings"
    +	"testing"
    +
    +	. "github.com/onsi/gomega"
    +	"github.com/onsi/gomega/types"
    +	"sigs.k8s.io/kustomize/kyaml/filesys"
    +)
    +
    +func Test_fsSecure_Create(t *testing.T) {
    +	g := NewWithT(t)
    +
    +	root := t.TempDir()
    +	fs, err := MakeFsOnDiskSecure(root)
    +	g.Expect(err).ToNot(HaveOccurred())
    +
    +	t.Run("secure create", func(t *testing.T) {
    +		g := NewWithT(t)
    +
    +		path := filepath.Join(root, "file.txt")
    +		got, err := fs.Create(path)
    +		g.Expect(err).ToNot(HaveOccurred())
    +		g.Expect(got).ToNot(BeNil())
    +		g.Expect(got.Close()).To(Succeed())
    +		g.Expect(fs.Exists(path)).To(BeTrue())
    +	})
    +
    +	t.Run("illegal create", func(t *testing.T) {
    +		g := NewWithT(t)
    +
    +		path := filepath.Join(root, "../file.txt")
    +		got, err := fs.Create("/file.txt")
    +		g.Expect(err).To(HaveOccurred())
    +		g.Expect(got).To(BeNil())
    +		g.Expect(fs.Exists(path)).To(BeFalse())
    +	})
    +}
    +
    +func Test_fsSecure_Mkdir(t *testing.T) {
    +	g := NewWithT(t)
    +
    +	root := t.TempDir()
    +	fs, err := MakeFsOnDiskSecure(root)
    +	g.Expect(err).ToNot(HaveOccurred())
    +
    +	t.Run("secure mkdir", func(t *testing.T) {
    +		g := NewWithT(t)
    +
    +		path := filepath.Join(root, "secure")
    +		g.Expect(fs.Mkdir(path)).To(Succeed())
    +		g.Expect(path).To(BeADirectory())
    +	})
    +
    +	t.Run("illegal mkdir", func(t *testing.T) {
    +		g := NewWithT(t)
    +
    +		path := filepath.Join(os.TempDir(), "illegal")
    +		g.Expect(fs.Mkdir(path)).To(HaveOccurred())
    +		g.Expect(path).ToNot(BeADirectory())
    +		g.Expect(path).ToNot(BeAnExistingFile())
    +	})
    +}
    +
    +func Test_fsSecure_MkdirAll(t *testing.T) {
    +	g := NewWithT(t)
    +
    +	root := t.TempDir()
    +	fs, err := MakeFsOnDiskSecure(root)
    +	g.Expect(err).ToNot(HaveOccurred())
    +
    +	t.Run("secure mkdir all", func(t *testing.T) {
    +		g := NewWithT(t)
    +
    +		path := filepath.Join(root, "secure", "subdir")
    +		g.Expect(fs.MkdirAll(path)).To(Succeed())
    +		g.Expect(path).To(BeADirectory())
    +	})
    +
    +	t.Run("illegal mkdir all", func(t *testing.T) {
    +		g := NewWithT(t)
    +
    +		path := filepath.Join(root, "..", "..", "subdir")
    +		g.Expect(fs.MkdirAll(path)).To(HaveOccurred())
    +		g.Expect(path).ToNot(BeADirectory())
    +		g.Expect(path).ToNot(BeAnExistingFile())
    +	})
    +}
    +
    +func Test_fsSecure_RemoveAll(t *testing.T) {
    +	g := NewWithT(t)
    +
    +	tmpDir := t.TempDir()
    +	root := filepath.Join(tmpDir, "workdir")
    +
    +	g.Expect(os.MkdirAll(filepath.Join(root, "subdir"), 0o700)).To(Succeed())
    +	g.Expect(os.WriteFile(filepath.Join(root, "subdir", "file.txt"), []byte(""), 0o644)).To(Succeed())
    +	g.Expect(os.WriteFile(filepath.Join(tmpDir, "file.txt"), []byte(""), 0o644)).To(Succeed())
    +
    +	fs, err := MakeFsOnDiskSecure(root)
    +	g.Expect(err).ToNot(HaveOccurred())
    +
    +	t.Run("secure remove all", func(t *testing.T) {
    +		g := NewWithT(t)
    +
    +		path := filepath.Join(root, "subdir")
    +		g.Expect(fs.RemoveAll(path)).To(Succeed())
    +		g.Expect(path).NotTo(BeADirectory())
    +	})
    +
    +	t.Run("illegal remove all", func(t *testing.T) {
    +		g := NewWithT(t)
    +
    +		path := filepath.Join(tmpDir, "file.txt")
    +		g.Expect(fs.RemoveAll(path)).To(HaveOccurred())
    +		g.Expect(path).To(BeAnExistingFile())
    +	})
    +}
    +
    +func Test_fsSecure_Open(t *testing.T) {
    +	g := NewWithT(t)
    +
    +	tmpDir := t.TempDir()
    +
    +	root := filepath.Join(tmpDir, "workdir")
    +	g.Expect(os.Mkdir(root, 0o700)).To(Succeed())
    +	g.Expect(os.WriteFile(filepath.Join(root, "file.txt"), []byte("secure"), 0o644)).To(Succeed())
    +	g.Expect(os.WriteFile(filepath.Join(tmpDir, "file.txt"), []byte("illegal"), 0o644)).To(Succeed())
    +
    +	fs, err := MakeFsOnDiskSecure(root)
    +	g.Expect(err).ToNot(HaveOccurred())
    +
    +	t.Run("secure open", func(t *testing.T) {
    +		g := NewWithT(t)
    +
    +		path := filepath.Join(root, "file.txt")
    +		f, err := fs.Open(path)
    +		g.Expect(err).ToNot(HaveOccurred())
    +		g.Expect(f).ToNot(BeNil())
    +		var b bytes.Buffer
    +		_, err = io.Copy(&b, f)
    +		g.Expect(err).To(Succeed())
    +		g.Expect(b.String()).To(Equal("secure"))
    +	})
    +
    +	t.Run("illegal open", func(t *testing.T) {
    +		g := NewWithT(t)
    +
    +		path := filepath.Join(tmpDir, "file.txt")
    +		f, err := fs.Open(path)
    +		g.Expect(err).To(HaveOccurred())
    +		g.Expect(f).To(BeNil())
    +	})
    +}
    +
    +func Test_fsSecure_IsDir(t *testing.T) {
    +	g := NewWithT(t)
    +
    +	tmpDir := t.TempDir()
    +
    +	root := filepath.Join(tmpDir, "workdir")
    +	g.Expect(os.Mkdir(root, 0o700)).To(Succeed())
    +	g.Expect(os.Mkdir(filepath.Join(tmpDir, "illegal"), 0o700)).To(Succeed())
    +
    +	fs, err := MakeFsOnDiskSecure(root)
    +	g.Expect(err).ToNot(HaveOccurred())
    +
    +	t.Run("secure is dir", func(t *testing.T) {
    +		g := NewWithT(t)
    +
    +		path := filepath.Join(root, "")
    +		g.Expect(fs.IsDir(path)).To(BeTrue())
    +	})
    +
    +	t.Run("illegal is dir", func(t *testing.T) {
    +		g := NewWithT(t)
    +
    +		path := filepath.Join(tmpDir, "illegal")
    +		g.Expect(fs.IsDir(path)).To(BeFalse())
    +	})
    +}
    +
    +func Test_fsSecure_ReadDir(t *testing.T) {
    +	g := NewWithT(t)
    +
    +	tmpDir := t.TempDir()
    +
    +	root := filepath.Join(tmpDir, "workdir")
    +	g.Expect(os.Mkdir(root, 0o700)).To(Succeed())
    +	g.Expect(os.WriteFile(filepath.Join(root, "file.txt"), []byte("secure"), 0o644)).To(Succeed())
    +	g.Expect(os.Mkdir(filepath.Join(tmpDir, "illegal"), 0o700)).To(Succeed())
    +	g.Expect(os.WriteFile(filepath.Join(tmpDir, "illegal", "file.txt"), []byte("illegal"), 0o644)).To(Succeed())
    +
    +	fs, err := MakeFsOnDiskSecure(root)
    +	g.Expect(err).ToNot(HaveOccurred())
    +
    +	t.Run("secure read dir", func(t *testing.T) {
    +		g := NewWithT(t)
    +
    +		path := filepath.Join(root, "")
    +		files, err := fs.ReadDir(path)
    +		g.Expect(err).ToNot(HaveOccurred())
    +		g.Expect(files).To(HaveLen(1))
    +		g.Expect(files).To(ContainElement("file.txt"))
    +	})
    +
    +	t.Run("illegal is dir", func(t *testing.T) {
    +		g := NewWithT(t)
    +
    +		path := filepath.Join(tmpDir, "illegal")
    +		files, err := fs.ReadDir(path)
    +		g.Expect(err).To(HaveOccurred())
    +		g.Expect(files).To(HaveLen(0))
    +	})
    +}
    +
    +func Test_fsSecure_CleanedAbs(t *testing.T) {
    +	g := NewWithT(t)
    +
    +	tmpDir := t.TempDir()
    +
    +	root := filepath.Join(tmpDir, "workdir")
    +	g.Expect(os.Mkdir(root, 0o700)).To(Succeed())
    +
    +	fs, err := MakeFsOnDiskSecure(root)
    +	g.Expect(err).ToNot(HaveOccurred())
    +
    +	t.Run("secure cleaned abs", func(t *testing.T) {
    +		g := NewWithT(t)
    +
    +		d, f, err := fs.CleanedAbs(filepath.Join(root, "../workdir"))
    +		g.Expect(err).ToNot(HaveOccurred())
    +		g.Expect(d).To(Equal(filesys.ConfirmedDir(root)))
    +		g.Expect(f).To(BeEmpty())
    +	})
    +
    +	t.Run("illegal cleaned abs", func(t *testing.T) {
    +		g := NewWithT(t)
    +
    +		d, f, err := fs.CleanedAbs(filepath.Join(root, "../../workdir"))
    +		g.Expect(err).To(HaveOccurred())
    +		g.Expect(d).To(BeEmpty())
    +		g.Expect(f).To(BeEmpty())
    +	})
    +}
    +
    +func Test_fsSecure_Exists(t *testing.T) {
    +	g := NewWithT(t)
    +
    +	tmpDir := t.TempDir()
    +
    +	root := filepath.Join(tmpDir, "workdir")
    +	g.Expect(os.Mkdir(root, 0o700)).To(Succeed())
    +
    +	fs, err := MakeFsOnDiskSecure(root)
    +	g.Expect(err).ToNot(HaveOccurred())
    +
    +	t.Run("secure exists", func(t *testing.T) {
    +		g := NewWithT(t)
    +
    +		g.Expect(fs.Exists(root)).To(BeTrue())
    +	})
    +
    +	t.Run("illegal exists", func(t *testing.T) {
    +		g := NewWithT(t)
    +
    +		g.Expect(fs.Exists(tmpDir)).To(BeFalse())
    +	})
    +}
    +
    +func Test_fsSecure_Glob(t *testing.T) {
    +	g := NewWithT(t)
    +
    +	tmpDir := t.TempDir()
    +
    +	root := filepath.Join(tmpDir, "workdir")
    +	g.Expect(os.Mkdir(root, 0o700)).To(Succeed())
    +
    +	g.Expect(os.WriteFile(filepath.Join(root, "file.txt"), []byte("secure"), 0o644)).To(Succeed())
    +	g.Expect(os.WriteFile(filepath.Join(tmpDir, "file.txt"), []byte("illegal"), 0o644)).To(Succeed())
    +
    +	fs, err := MakeFsOnDiskSecure(root)
    +	g.Expect(err).ToNot(HaveOccurred())
    +
    +	files, err := fs.Glob(filepath.Join(tmpDir, "*/*.txt"))
    +	g.Expect(err).ToNot(HaveOccurred())
    +	g.Expect(files).To(ContainElement(filepath.Join(root, "file.txt")))
    +	g.Expect(files).ToNot(ContainElement(filepath.Join(tmpDir, "file.txt")))
    +}
    +
    +func Test_fsSecure_ReadFile(t *testing.T) {
    +	g := NewWithT(t)
    +
    +	tmpDir := t.TempDir()
    +
    +	root := filepath.Join(tmpDir, "workdir")
    +	g.Expect(os.Mkdir(root, 0o700)).To(Succeed())
    +	g.Expect(os.WriteFile(filepath.Join(root, "file.txt"), []byte("secure"), 0o644)).To(Succeed())
    +	g.Expect(os.WriteFile(filepath.Join(tmpDir, "file.txt"), []byte("illegal"), 0o644)).To(Succeed())
    +
    +	fs, err := MakeFsOnDiskSecure(root)
    +	g.Expect(err).ToNot(HaveOccurred())
    +
    +	t.Run("secure read file", func(t *testing.T) {
    +		g := NewWithT(t)
    +
    +		path := filepath.Join(root, "file.txt")
    +		b, err := fs.ReadFile(path)
    +		g.Expect(err).ToNot(HaveOccurred())
    +		g.Expect(b).To(Equal([]byte("secure")))
    +	})
    +
    +	t.Run("illegal read file", func(t *testing.T) {
    +		g := NewWithT(t)
    +
    +		path := filepath.Join(tmpDir, "file.txt")
    +		b, err := fs.ReadFile(path)
    +		g.Expect(err).To(HaveOccurred())
    +		g.Expect(b).To(BeNil())
    +	})
    +}
    +
    +func Test_fsSecure_WriteFile(t *testing.T) {
    +	g := NewWithT(t)
    +
    +	tmpDir := t.TempDir()
    +
    +	root := filepath.Join(tmpDir, "workdir")
    +	g.Expect(os.Mkdir(root, 0o700)).To(Succeed())
    +
    +	fs, err := MakeFsOnDiskSecure(root)
    +	g.Expect(err).ToNot(HaveOccurred())
    +
    +	t.Run("secure write file", func(t *testing.T) {
    +		g := NewWithT(t)
    +
    +		path := filepath.Join(root, "file.txt")
    +		data := []byte("secure")
    +		err := fs.WriteFile(path, data)
    +		g.Expect(err).ToNot(HaveOccurred())
    +		g.Expect(path).To(BeAnExistingFile())
    +		b, err := fs.ReadFile(path)
    +		g.Expect(err).ToNot(HaveOccurred())
    +		g.Expect(b).To(Equal(data))
    +	})
    +
    +	t.Run("illegal write file", func(t *testing.T) {
    +		g := NewWithT(t)
    +
    +		path := filepath.Join(tmpDir, "file.txt")
    +		err := fs.WriteFile(path, []byte("illegal"))
    +		g.Expect(err).To(HaveOccurred())
    +		g.Expect(path).ToNot(BeAnExistingFile())
    +	})
    +}
    +
    +func Test_fsSecure_Walk(t *testing.T) {
    +	g := NewWithT(t)
    +
    +	tmpDir := t.TempDir()
    +
    +	root := filepath.Join(tmpDir, "workdir")
    +	g.Expect(os.Mkdir(root, 0o700)).To(Succeed())
    +	g.Expect(os.WriteFile(filepath.Join(root, "file.txt"), []byte("secure"), 0o644)).To(Succeed())
    +	g.Expect(os.WriteFile(filepath.Join(tmpDir, "file.txt"), []byte("illegal"), 0o644)).To(Succeed())
    +
    +	fs, err := MakeFsOnDiskSecure(root)
    +	g.Expect(err).ToNot(HaveOccurred())
    +
    +	t.Run("secure walk", func(t *testing.T) {
    +		g := NewWithT(t)
    +
    +		var walkedPaths []string
    +		walk := func(path string, info os.FileInfo, err error) error {
    +			walkedPaths = append(walkedPaths, path)
    +			return nil
    +		}
    +		g.Expect(fs.Walk(root, walk)).To(Succeed())
    +		g.Expect(walkedPaths).To(Equal([]string{root, filepath.Join(root, "file.txt")}))
    +	})
    +
    +	t.Run("illegal walk", func(t *testing.T) {
    +		g := NewWithT(t)
    +
    +		var walkedPaths []string
    +		walk := func(path string, info os.FileInfo, err error) error {
    +			walkedPaths = append(walkedPaths, path)
    +			return nil
    +		}
    +		g.Expect(fs.Walk(tmpDir, walk)).To(HaveOccurred())
    +		g.Expect(walkedPaths).To(BeEmpty())
    +	})
    +}
    +
    +func Test_isSecurePath(t *testing.T) {
    +	type file struct {
    +		name    string
    +		symlink string
    +	}
    +	tests := []struct {
    +		name       string
    +		fs         filesys.FileSystem
    +		rootSuffix string
    +		files      []file
    +		path       string
    +		wantErr    types.GomegaMatcher
    +	}{
    +		{
    +			name:    "secure non existing path",
    +			fs:      filesys.MakeFsOnDisk(),
    +			path:    "<root>/filepath",
    +			wantErr: Succeed(),
    +		},
    +		{
    +			name:       "illegal relative path",
    +			fs:         filesys.MakeFsOnDisk(),
    +			rootSuffix: "subdir",
    +			path:       "../",
    +			wantErr:    HaveOccurred(),
    +		},
    +		{
    +			name:       "illegal absolute path",
    +			fs:         filesys.MakeFsOnDisk(),
    +			rootSuffix: "subdir",
    +			path:       "<root>",
    +			wantErr:    HaveOccurred(),
    +		},
    +		{
    +			name:       "relative symlink",
    +			fs:         filesys.MakeFsOnDisk(),
    +			rootSuffix: "subdir",
    +			files: []file{
    +				{name: "subdir/file.txt"},
    +				{name: "subdir/subsubdir/symlink", symlink: "../file.txt"},
    +			},
    +			path:    "<root>/subdir/subsubdir/symlink",
    +			wantErr: Succeed(),
    +		},
    +		{
    +			name:       "absolute symlink",
    +			fs:         filesys.MakeFsOnDisk(),
    +			rootSuffix: "subdir",
    +			files: []file{
    +				{name: "subdir/file.txt"},
    +				{name: "subdir/subsubdir/symlink", symlink: "<root>/subdir/file.txt"},
    +			},
    +			path:    "<root>/subdir/subsubdir/symlink",
    +			wantErr: Succeed(),
    +		},
    +		{
    +			name:       "illegal relative symlink",
    +			fs:         filesys.MakeFsOnDisk(),
    +			rootSuffix: "subdir",
    +			files: []file{
    +				{name: "file.txt"},
    +				{name: "subdir/symlink", symlink: "../file.txt"},
    +			},
    +			path:    "<root>/subdir/symlink",
    +			wantErr: HaveOccurred(),
    +		},
    +		{
    +			name:       "illegal absolute symlink",
    +			fs:         filesys.MakeFsOnDisk(),
    +			rootSuffix: "subdir",
    +			files: []file{
    +				{name: "file.txt"},
    +				{name: "subdir/symlink", symlink: "<root>/file.txt"},
    +			},
    +			path:    "<root>/subdir/symlink",
    +			wantErr: HaveOccurred(),
    +		},
    +	}
    +	for _, tt := range tests {
    +		t.Run(tt.name, func(t *testing.T) {
    +			g := NewWithT(t)
    +
    +			root := newTemp()
    +			realRoot := filesys.ConfirmedDir(filepath.Join(root, tt.rootSuffix))
    +			g.Expect(tt.fs.MkdirAll(realRoot.String())).To(Succeed())
    +			t.Cleanup(func() {
    +				g.Expect(tt.fs.RemoveAll(root)).To(Succeed())
    +			})
    +
    +			for _, f := range tt.files {
    +				fPath := filepath.Join(root, f.name)
    +				dir, base := filepath.Split(fPath)
    +				g.Expect(tt.fs.MkdirAll(dir)).To(Succeed())
    +
    +				if symlink := f.symlink; symlink != "" {
    +					if strings.HasPrefix(symlink, "<root>") {
    +						symlink = strings.Replace(symlink, "<root>", root, 1)
    +					}
    +					g.Expect(os.Symlink(symlink, fPath)).To(Succeed())
    +					continue
    +				}
    +
    +				if base != "" {
    +					file, err := tt.fs.Create(fPath)
    +					g.Expect(err).ToNot(HaveOccurred())
    +					_, err = file.Write([]byte(f.name + " data"))
    +					g.Expect(err).ToNot(HaveOccurred())
    +					g.Expect(file.Close()).To(Succeed())
    +				}
    +			}
    +
    +			path := tt.path
    +			if strings.HasPrefix(path, "<root>") {
    +				path = strings.Replace(path, "<root>", root, 1)
    +			}
    +
    +			err := isSecurePath(tt.fs, realRoot, path)
    +			g.Expect(err).To(tt.wantErr)
    +		})
    +	}
    +}
    +
    +func newTemp() string {
    +	return filepath.Join(os.TempDir(), "securefs-"+randStringBytes(5))
    +}
    +
    +func randStringBytes(n int) string {
    +	const letterBytes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
    +
    +	b := make([]byte, n)
    +	for i := range b {
    +		b[i] = letterBytes[rand.Int63()%int64(len(letterBytes))]
    +	}
    +	return string(b)
    +}
    
f4528fb25d61

controllers: use own Kustomize FS implementation

https://github.com/fluxcd/kustomize-controllerHidde BeydalsApr 15, 2022via ghsa
5 files changed · +58 44
  • controllers/kustomization_controller.go+9 12 modified
    @@ -52,7 +52,6 @@ import (
     	"sigs.k8s.io/controller-runtime/pkg/predicate"
     	"sigs.k8s.io/controller-runtime/pkg/reconcile"
     	"sigs.k8s.io/controller-runtime/pkg/source"
    -	"sigs.k8s.io/kustomize/kyaml/filesys"
     
     	apiacl "github.com/fluxcd/pkg/apis/acl"
     	"github.com/fluxcd/pkg/apis/meta"
    @@ -357,7 +356,7 @@ func (r *KustomizationReconciler) reconcile(
     	}
     
     	// generate kustomization.yaml if needed
    -	err = r.generate(kustomization, dirPath)
    +	err = r.generate(kustomization, tmpDir, dirPath)
     	if err != nil {
     		return kustomizev1.KustomizationNotReady(
     			kustomization,
    @@ -629,8 +628,8 @@ func (r *KustomizationReconciler) getSource(ctx context.Context, kustomization k
     	return source, nil
     }
     
    -func (r *KustomizationReconciler) generate(kustomization kustomizev1.Kustomization, dirPath string) error {
    -	gen := NewGenerator(kustomization)
    +func (r *KustomizationReconciler) generate(kustomization kustomizev1.Kustomization, workDir string, dirPath string) error {
    +	gen := NewGenerator(workDir, kustomization)
     	return gen.WriteFile(dirPath)
     }
     
    @@ -641,19 +640,17 @@ func (r *KustomizationReconciler) build(ctx context.Context, workDir string, kus
     	}
     	defer cleanup()
     
    -	// import OpenPGP keys if any
    +	// Import decryption keys
     	if err := dec.ImportKeys(ctx); err != nil {
     		return nil, err
     	}
     
    -	fs := filesys.MakeFsOnDisk()
    -	// decrypt .env files before building kustomization
    -	if kustomization.Spec.Decryption != nil {
    -		if err = dec.DecryptEnvSources(dirPath); err != nil {
    -			return nil, fmt.Errorf("error decrypting .env file: %w", err)
    -		}
    +	// Decrypt Kustomize EnvSources files before build
    +	if err = dec.DecryptEnvSources(dirPath); err != nil {
    +		return nil, fmt.Errorf("error decrypting env sources: %w", err)
     	}
    -	m, err := buildKustomization(fs, dirPath)
    +
    +	m, err := secureBuildKustomization(workDir, dirPath)
     	if err != nil {
     		return nil, fmt.Errorf("kustomize build failed: %w", err)
     	}
    
  • controllers/kustomization_decryptor.go+1 1 modified
    @@ -358,7 +358,7 @@ func (d *KustomizeDecryptor) DecryptResource(res *resource.Resource) (*resource.
     // outside the working directory of the decryptor, but returns any decryption
     // error.
     func (d *KustomizeDecryptor) DecryptEnvSources(path string) error {
    -	if d.kustomization.Spec.Decryption.Provider != DecryptionProviderSOPS {
    +	if d.kustomization.Spec.Decryption == nil || d.kustomization.Spec.Decryption.Provider != DecryptionProviderSOPS {
     		return nil
     	}
     
    
  • controllers/kustomization_generator.go+23 9 modified
    @@ -29,19 +29,22 @@ import (
     	"sigs.k8s.io/kustomize/api/provider"
     	"sigs.k8s.io/kustomize/api/resmap"
     	kustypes "sigs.k8s.io/kustomize/api/types"
    -	"sigs.k8s.io/kustomize/kyaml/filesys"
     	"sigs.k8s.io/yaml"
     
    -	kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1beta2"
     	"github.com/fluxcd/pkg/apis/kustomize"
    +	securefs "github.com/fluxcd/pkg/kustomize/filesys"
    +
    +	kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1beta2"
     )
     
     type KustomizeGenerator struct {
    +	root          string
     	kustomization kustomizev1.Kustomization
     }
     
    -func NewGenerator(kustomization kustomizev1.Kustomization) *KustomizeGenerator {
    +func NewGenerator(root string, kustomization kustomizev1.Kustomization) *KustomizeGenerator {
     	return &KustomizeGenerator{
    +		root:          root,
     		kustomization: kustomization,
     	}
     }
    @@ -127,7 +130,10 @@ func checkKustomizeImageExists(images []kustypes.Image, imageName string) (bool,
     }
     
     func (kg *KustomizeGenerator) generateKustomization(dirPath string) error {
    -	fs := filesys.MakeFsOnDisk()
    +	fs, err := securefs.MakeFsOnDiskSecure(kg.root)
    +	if err != nil {
    +		return err
    +	}
     
     	// Determine if there already is a Kustomization file at the root,
     	// as this means we do not have to generate one.
    @@ -234,11 +240,19 @@ func adaptSelector(selector *kustomize.Selector) (output *kustypes.Selector) {
     // TODO: remove mutex when kustomize fixes the concurrent map read/write panic
     var kustomizeBuildMutex sync.Mutex
     
    -// buildKustomization wraps krusty.MakeKustomizer with the following settings:
    -// - load files from outside the kustomization.yaml root
    -// - disable plugins except for the builtin ones
    -func buildKustomization(fs filesys.FileSystem, dirPath string) (resmap.ResMap, error) {
    -	// temporary workaround for concurrent map read and map write bug
    +// secureBuildKustomization wraps krusty.MakeKustomizer with the following settings:
    +//  - secure on-disk FS denying operations outside root
    +//  - load files from outside the kustomization dir path
    +//    (but not outside root)
    +//  - disable plugins except for the builtin ones
    +func secureBuildKustomization(root, dirPath string) (resmap.ResMap, error) {
    +	// Create secure FS for root
    +	fs, err := securefs.MakeFsOnDiskSecure(root)
    +	if err != nil {
    +		return nil, err
    +	}
    +
    +	// Temporary workaround for concurrent map read and map write bug
     	// https://github.com/kubernetes-sigs/kustomize/issues/3659
     	kustomizeBuildMutex.Lock()
     	defer kustomizeBuildMutex.Unlock()
    
  • go.mod+8 7 modified
    @@ -16,6 +16,7 @@ require (
     	github.com/fluxcd/pkg/apis/acl v0.0.3
     	github.com/fluxcd/pkg/apis/kustomize v0.3.2
     	github.com/fluxcd/pkg/apis/meta v0.12.1
    +	github.com/fluxcd/pkg/kustomize v0.2.0
     	github.com/fluxcd/pkg/runtime v0.13.3
     	github.com/fluxcd/pkg/ssa v0.15.1
     	github.com/fluxcd/pkg/testserver v0.2.0
    @@ -29,14 +30,13 @@ require (
     	go.mozilla.org/sops/v3 v3.7.2
     	golang.org/x/net v0.0.0-20220225172249-27dd8689420f
     	google.golang.org/grpc v1.45.0
    -	k8s.io/api v0.23.4
    -	k8s.io/apiextensions-apiserver v0.23.4
    -	k8s.io/apimachinery v0.23.4
    -	k8s.io/client-go v0.23.4
    +	k8s.io/api v0.23.5
    +	k8s.io/apiextensions-apiserver v0.23.5
    +	k8s.io/apimachinery v0.23.5
    +	k8s.io/client-go v0.23.5
     	sigs.k8s.io/cli-utils v0.29.3
    -	sigs.k8s.io/controller-runtime v0.11.1
    +	sigs.k8s.io/controller-runtime v0.11.2
     	sigs.k8s.io/kustomize/api v0.11.4
    -	sigs.k8s.io/kustomize/kyaml v0.13.6
     	sigs.k8s.io/yaml v1.3.0
     )
     
    @@ -199,11 +199,12 @@ require (
     	gopkg.in/yaml.v2 v2.4.0 // indirect
     	gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect
     	k8s.io/cli-runtime v0.23.2 // indirect
    -	k8s.io/component-base v0.23.4 // indirect
    +	k8s.io/component-base v0.23.5 // indirect
     	k8s.io/klog/v2 v2.50.0 // indirect
     	k8s.io/kube-openapi v0.0.0-20211115234752-e816edb12b65 // indirect
     	k8s.io/kubectl v0.23.2 // indirect
     	k8s.io/utils v0.0.0-20220210201930-3a6ce19ff2f9 // indirect
     	sigs.k8s.io/json v0.0.0-20211208200746-9f7c6b3444d2 // indirect
    +	sigs.k8s.io/kustomize/kyaml v0.13.6 // indirect
     	sigs.k8s.io/structured-merge-diff/v4 v4.2.1 // indirect
     )
    
  • go.sum+17 15 modified
    @@ -276,6 +276,8 @@ github.com/fluxcd/pkg/apis/kustomize v0.3.2 h1:ULoAwOOekHf5cy6mYIwL+K6v8/cfcNVVb
     github.com/fluxcd/pkg/apis/kustomize v0.3.2/go.mod h1:p8iAH5TeqMBnnxkkpCNNDvWYfKlNRx89a6WKOo+hJHA=
     github.com/fluxcd/pkg/apis/meta v0.12.1 h1:m5PfKAqbqWBvGp9+JRj1sv+xNkGsHwUVf+3rJ8wm6SE=
     github.com/fluxcd/pkg/apis/meta v0.12.1/go.mod h1:f8YVt70/KAhqzZ7xxhjvqyzKubOYx2pAbakb/FfCEg8=
    +github.com/fluxcd/pkg/kustomize v0.2.0 h1:twiGAFJctt2tyH8vHxL1uqb6BlU3B9ZqG8uSlluuioM=
    +github.com/fluxcd/pkg/kustomize v0.2.0/go.mod h1:Qczvl7vNY9NJBpyaFrldsxfGjj6uaMcMmKGsSJ6hcxc=
     github.com/fluxcd/pkg/runtime v0.13.3 h1:k0Xun+RoEC/F6iuAPTA6rQb+I4B4oecBx6pOcodX11A=
     github.com/fluxcd/pkg/runtime v0.13.3/go.mod h1:dzWNKqFzFXeittbpFcJzR3cdC9CWlbzw+pNOgaVvF/0=
     github.com/fluxcd/pkg/ssa v0.15.1 h1:HXAT+K6c9Yy8Evxdyk3DU0KTk3yZ+fwgTEEzU1W/1V8=
    @@ -1440,24 +1442,24 @@ honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt
     honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
     honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
     k8s.io/api v0.23.2/go.mod h1:sYuDb3flCtRPI8ghn6qFrcK5ZBu2mhbElxRE95qpwlI=
    -k8s.io/api v0.23.4 h1:85gnfXQOWbJa1SiWGpE9EEtHs0UVvDyIsSMpEtl2D4E=
    -k8s.io/api v0.23.4/go.mod h1:i77F4JfyNNrhOjZF7OwwNJS5Y1S9dpwvb9iYRYRczfI=
    -k8s.io/apiextensions-apiserver v0.23.4 h1:AFDUEu/yEf0YnuZhqhIFhPLPhhcQQVuR1u3WCh0rveU=
    -k8s.io/apiextensions-apiserver v0.23.4/go.mod h1:TWYAKymJx7nLMxWCgWm2RYGXHrGlVZnxIlGnvtfYu+g=
    +k8s.io/api v0.23.5 h1:zno3LUiMubxD/V1Zw3ijyKO3wxrhbUF1Ck+VjBvfaoA=
    +k8s.io/api v0.23.5/go.mod h1:Na4XuKng8PXJ2JsploYYrivXrINeTaycCGcYgF91Xm8=
    +k8s.io/apiextensions-apiserver v0.23.5 h1:5SKzdXyvIJKu+zbfPc3kCbWpbxi+O+zdmAJBm26UJqI=
    +k8s.io/apiextensions-apiserver v0.23.5/go.mod h1:ntcPWNXS8ZPKN+zTXuzYMeg731CP0heCTl6gYBxLcuQ=
     k8s.io/apimachinery v0.23.2/go.mod h1:zDqeV0AK62LbCI0CI7KbWCAYdLg+E+8UXJ0rIz5gmS8=
    -k8s.io/apimachinery v0.23.4 h1:fhnuMd/xUL3Cjfl64j5ULKZ1/J9n8NuQEgNL+WXWfdM=
    -k8s.io/apimachinery v0.23.4/go.mod h1:BEuFMMBaIbcOqVIJqNZJXGFTP4W6AycEpb5+m/97hrM=
    -k8s.io/apiserver v0.23.4/go.mod h1:A6l/ZcNtxGfPSqbFDoxxOjEjSKBaQmE+UTveOmMkpNc=
    +k8s.io/apimachinery v0.23.5 h1:Va7dwhp8wgkUPWsEXk6XglXWU4IKYLKNlv8VkX7SDM0=
    +k8s.io/apimachinery v0.23.5/go.mod h1:BEuFMMBaIbcOqVIJqNZJXGFTP4W6AycEpb5+m/97hrM=
    +k8s.io/apiserver v0.23.5/go.mod h1:7wvMtGJ42VRxzgVI7jkbKvMbuCbVbgsWFT7RyXiRNTw=
     k8s.io/cli-runtime v0.23.2 h1:4zOZX78mFSakwe4gef81XDBu94Yu0th6bfveTOx8ZQk=
     k8s.io/cli-runtime v0.23.2/go.mod h1:Ag70akCDvwux4HxY+nH2J3UqE2e6iwSSdG1HE6p1VTU=
     k8s.io/client-go v0.23.2/go.mod h1:k3YbsWg6GWdHF1THHTQP88X9RhB1DWPo3Dq7KfU/D1c=
    -k8s.io/client-go v0.23.4 h1:YVWvPeerA2gpUudLelvsolzH7c2sFoXXR5wM/sWqNFU=
    -k8s.io/client-go v0.23.4/go.mod h1:PKnIL4pqLuvYUK1WU7RLTMYKPiIh7MYShLshtRY9cj0=
    +k8s.io/client-go v0.23.5 h1:zUXHmEuqx0RY4+CsnkOn5l0GU+skkRXKGJrhmE2SLd8=
    +k8s.io/client-go v0.23.5/go.mod h1:flkeinTO1CirYgzMPRWxUCnV0G4Fbu2vLhYCObnt/r4=
     k8s.io/code-generator v0.23.2/go.mod h1:S0Q1JVA+kSzTI1oUvbKAxZY/DYbA/ZUb4Uknog12ETk=
    -k8s.io/code-generator v0.23.4/go.mod h1:S0Q1JVA+kSzTI1oUvbKAxZY/DYbA/ZUb4Uknog12ETk=
    +k8s.io/code-generator v0.23.5/go.mod h1:S0Q1JVA+kSzTI1oUvbKAxZY/DYbA/ZUb4Uknog12ETk=
     k8s.io/component-base v0.23.2/go.mod h1:wS9Z03MO3oJ0RU8bB/dbXTiluGju+SC/F5i660gxB8c=
    -k8s.io/component-base v0.23.4 h1:SziYh48+QKxK+ykJ3Ejqd98XdZIseVBG7sBaNLPqy6M=
    -k8s.io/component-base v0.23.4/go.mod h1:8o3Gg8i2vnUXGPOwciiYlkSaZT+p+7gA9Scoz8y4W4E=
    +k8s.io/component-base v0.23.5 h1:8qgP5R6jG1BBSXmRYW+dsmitIrpk8F/fPEvgDenMCCE=
    +k8s.io/component-base v0.23.5/go.mod h1:c5Nq44KZyt1aLl0IpHX82fhsn84Sb0jjzwjpcA42bY0=
     k8s.io/component-helpers v0.23.2/go.mod h1:J6CMwiaf0izLoNwiLl2OymB4+rGTsTpWp6PL/AqOM4U=
     k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
     k8s.io/gengo v0.0.0-20210813121822-485abfe95c7c/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E=
    @@ -1480,11 +1482,11 @@ k8s.io/utils v0.0.0-20220210201930-3a6ce19ff2f9/go.mod h1:jPW/WVKK9YHAvNhRxK0md/
     rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
     rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
     rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
    -sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.27/go.mod h1:tq2nT0Kx7W+/f2JVE+zxYtUhdjuELJkVpNz+x/QN5R4=
    +sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.30/go.mod h1:fEO7lRTdivWO2qYVCVG7dEADOMo/MLDCVr8So2g88Uw=
     sigs.k8s.io/cli-utils v0.29.3 h1:4QRB9ayCd5pd9M/D3q2KQgr+nYrvRaw3suW+rcOutvk=
     sigs.k8s.io/cli-utils v0.29.3/go.mod h1:WDVRa5/eQBKntG++uyKdyT+xU7MLdCR4XsgseqL5uX4=
    -sigs.k8s.io/controller-runtime v0.11.1 h1:7YIHT2QnHJArj/dk9aUkYhfqfK5cIxPOX5gPECfdZLU=
    -sigs.k8s.io/controller-runtime v0.11.1/go.mod h1:KKwLiTooNGu+JmLZGn9Sl3Gjmfj66eMbCQznLP5zcqA=
    +sigs.k8s.io/controller-runtime v0.11.2 h1:H5GTxQl0Mc9UjRJhORusqfJCIjBO8UtUxGggCwL1rLA=
    +sigs.k8s.io/controller-runtime v0.11.2/go.mod h1:P6QCzrEjLaZGqHsfd+os7JQ+WFZhvB8MRFsn4dWF7O4=
     sigs.k8s.io/json v0.0.0-20211020170558-c049b76a60c6/go.mod h1:p4QtZmO4uMYipTQNzagwnNoseA6OxSUutVw05NhYDRs=
     sigs.k8s.io/json v0.0.0-20211208200746-9f7c6b3444d2 h1:kDi4JBNAsJWfz1aEXhO8Jg87JJaPNLh5tIzYHgStQ9Y=
     sigs.k8s.io/json v0.0.0-20211208200746-9f7c6b3444d2/go.mod h1:B+TnT182UBxE84DiCz4CVE26eOSDAeYCpfDnC2kdKMY=
    

Vulnerability mechanics

Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

6

News mentions

0

No linked articles in our index yet.