VYPR
Moderate severityNVD Advisory· Published Apr 22, 2019· Updated Aug 4, 2024

kubectl creates world-writeable cached schema files

CVE-2019-11244

Description

In Kubernetes v1.8.x-v1.14.x, schema info is cached by kubectl in the location specified by --cache-dir (defaulting to $HOME/.kube/http-cache), written with world-writeable permissions (rw-rw-rw-). If --cache-dir is specified and pointed at a different location accessible to other users/groups, the written files may be modified by other users/groups and disrupt the kubectl invocation.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
k8s.io/client-goGo
>= 1.8.0, < 1.12.91.12.9

Affected products

1

Patches

1
790a4f636321

fix CVE-2019-11244: `kubectl --http-cache=<world-accessible dir>` creates world-writeable cached schema files

https://github.com/kubernetes/client-goYucheng WuMay 14, 2019via ghsa
4 files changed · +84 2
  • discovery/cached/disk/cached_discovery.go+2 2 modified
    @@ -172,7 +172,7 @@ func (d *CachedDiscoveryClient) getCachedFile(filename string) ([]byte, error) {
     }
     
     func (d *CachedDiscoveryClient) writeCachedFile(filename string, obj runtime.Object) error {
    -	if err := os.MkdirAll(filepath.Dir(filename), 0755); err != nil {
    +	if err := os.MkdirAll(filepath.Dir(filename), 0750); err != nil {
     		return err
     	}
     
    @@ -191,7 +191,7 @@ func (d *CachedDiscoveryClient) writeCachedFile(filename string, obj runtime.Obj
     		return err
     	}
     
    -	err = os.Chmod(f.Name(), 0755)
    +	err = os.Chmod(f.Name(), 0660)
     	if err != nil {
     		return err
     	}
    
  • discovery/cached/disk/cached_discovery_test.go+27 0 modified
    @@ -19,6 +19,7 @@ package disk
     import (
     	"io/ioutil"
     	"os"
    +	"path/filepath"
     	"testing"
     	"time"
     
    @@ -96,6 +97,32 @@ func TestNewCachedDiscoveryClient_TTL(t *testing.T) {
     	assert.Equal(c.groupCalls, 2)
     }
     
    +func TestNewCachedDiscoveryClient_PathPerm(t *testing.T) {
    +	assert := assert.New(t)
    +
    +	d, err := ioutil.TempDir("", "")
    +	assert.NoError(err)
    +	os.RemoveAll(d)
    +	defer os.RemoveAll(d)
    +
    +	c := fakeDiscoveryClient{}
    +	cdc := newCachedDiscoveryClient(&c, d, 1*time.Nanosecond)
    +	cdc.ServerGroups()
    +
    +	err = filepath.Walk(d, func(path string, info os.FileInfo, err error) error {
    +		if err != nil {
    +			return err
    +		}
    +		if info.IsDir() {
    +			assert.Equal(os.FileMode(0750), info.Mode().Perm())
    +		} else {
    +			assert.Equal(os.FileMode(0660), info.Mode().Perm())
    +		}
    +		return nil
    +	})
    +	assert.NoError(err)
    +}
    +
     type fakeDiscoveryClient struct {
     	groupCalls    int
     	resourceCalls int
    
  • discovery/cached/disk/round_tripper.go+3 0 modified
    @@ -18,6 +18,7 @@ package disk
     
     import (
     	"net/http"
    +	"os"
     	"path/filepath"
     
     	"github.com/gregjones/httpcache"
    @@ -35,6 +36,8 @@ type cacheRoundTripper struct {
     // corresponding requests.
     func newCacheRoundTripper(cacheDir string, rt http.RoundTripper) http.RoundTripper {
     	d := diskv.New(diskv.Options{
    +		PathPerm: os.FileMode(0750),
    +		FilePerm: os.FileMode(0660),
     		BasePath: cacheDir,
     		TempDir:  filepath.Join(cacheDir, ".diskv-temp"),
     	})
    
  • discovery/cached/disk/round_tripper_test.go+52 0 modified
    @@ -22,7 +22,10 @@ import (
     	"net/http"
     	"net/url"
     	"os"
    +	"path/filepath"
     	"testing"
    +
    +	"github.com/stretchr/testify/assert"
     )
     
     // copied from k8s.io/client-go/transport/round_trippers_test.go
    @@ -93,3 +96,52 @@ func TestCacheRoundTripper(t *testing.T) {
     		t.Errorf("Invalid content read from cache %q", string(content))
     	}
     }
    +
    +func TestCacheRoundTripperPathPerm(t *testing.T) {
    +	assert := assert.New(t)
    +
    +	rt := &testRoundTripper{}
    +	cacheDir, err := ioutil.TempDir("", "cache-rt")
    +	os.RemoveAll(cacheDir)
    +	defer os.RemoveAll(cacheDir)
    +
    +	if err != nil {
    +		t.Fatal(err)
    +	}
    +	cache := newCacheRoundTripper(cacheDir, rt)
    +
    +	// First call, caches the response
    +	req := &http.Request{
    +		Method: http.MethodGet,
    +		URL:    &url.URL{Host: "localhost"},
    +	}
    +	rt.Response = &http.Response{
    +		Header:     http.Header{"ETag": []string{`"123456"`}},
    +		Body:       ioutil.NopCloser(bytes.NewReader([]byte("Content"))),
    +		StatusCode: http.StatusOK,
    +	}
    +	resp, err := cache.RoundTrip(req)
    +	if err != nil {
    +		t.Fatal(err)
    +	}
    +	content, err := ioutil.ReadAll(resp.Body)
    +	if err != nil {
    +		t.Fatal(err)
    +	}
    +	if string(content) != "Content" {
    +		t.Errorf(`Expected Body to be "Content", got %q`, string(content))
    +	}
    +
    +	err = filepath.Walk(cacheDir, func(path string, info os.FileInfo, err error) error {
    +		if err != nil {
    +			return err
    +		}
    +		if info.IsDir() {
    +			assert.Equal(os.FileMode(0750), info.Mode().Perm())
    +		} else {
    +			assert.Equal(os.FileMode(0660), info.Mode().Perm())
    +		}
    +		return nil
    +	})
    +	assert.NoError(err)
    +}
    

Vulnerability mechanics

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

References

12

News mentions

0

No linked articles in our index yet.