High severity8.3NVD Advisory· Published Oct 16, 2024· Updated Apr 15, 2026
CVE-2023-32192
CVE-2023-32192
Description
A vulnerability has been identified in which unauthenticated cross-site scripting (XSS) in the API Server's public API endpoint can be exploited, allowing an attacker to execute arbitrary JavaScript code in the victim browser
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
github.com/rancher/apiserverGo | < 0.0.0-20240207153957-4fd7d821d952 | 0.0.0-20240207153957-4fd7d821d952 |
Patches
64fd7d821d952[2.9] Fixes (#59)
3 files changed · +164 −5
pkg/server/server_test.go+130 −0 modified@@ -2,18 +2,24 @@ package server import ( "errors" + "fmt" "net/http" + "net/http/httptest" + "net/url" + "strings" "testing" "github.com/golang/mock/gomock" "github.com/rancher/apiserver/pkg/apierror" + "github.com/rancher/apiserver/pkg/builtin" "github.com/rancher/apiserver/pkg/fakes" "github.com/rancher/apiserver/pkg/parse" "github.com/rancher/apiserver/pkg/types" "github.com/rancher/apiserver/pkg/writer" "github.com/rancher/wrangler/v2/pkg/schemas" "github.com/rancher/wrangler/v2/pkg/schemas/validation" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" ) @@ -360,3 +366,127 @@ func (p *ServerSuite) TestServer_CustomAPIUIResponseWriter() { assert.NotNil(p.T(), w.JSURL) assert.NotNil(p.T(), w.APIUIVersion) } + +func TestServeHTMLEscaping(t *testing.T) { + const ( + defaultJS = "cattle.io" + defaultCSS = "cattle.io" + defaultAPIVersion = "v1/apps.daemonsets.0.0" + xss = "<script>alert('xss')</script>" + alphaNumeric = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" + badChars = `~!@#$%^&*()_+-=[]\{}|;':",./<>?` + ) + xssUrl := url.URL{RawPath: xss} + + var escapedBadChars strings.Builder + for _, r := range badChars { + escapedBadChars.WriteString(fmt.Sprintf("&#x%X;", r)) + } + + t.Parallel() + tests := []struct { + name string + CSSURL string + JSURL string + APIUIVersion string + URL string + desiredContent string + undesiredContent string + }{ + { + name: "base case no xss", + CSSURL: defaultCSS, + JSURL: defaultJS, + APIUIVersion: defaultAPIVersion, + URL: "https://cattle.io/v1/apps.daemonsets", + desiredContent: "https://cattle.io/v1/apps.daemonsets", + }, + { + name: "JSS alpha-numeric", + CSSURL: defaultCSS, + JSURL: alphaNumeric, + APIUIVersion: defaultAPIVersion, + URL: "https://cattle.io/v1/apps.daemonsets", + desiredContent: alphaNumeric, + }, + { + name: "JSS escaped non alpha-numeric", + CSSURL: defaultCSS, + JSURL: badChars, + APIUIVersion: defaultAPIVersion, + URL: "https://cattle.io/v1/apps.daemonsets", + desiredContent: escapedBadChars.String(), + undesiredContent: badChars, + }, + { + name: "CSS alpha-numeric", + CSSURL: alphaNumeric, + JSURL: defaultJS, + APIUIVersion: defaultAPIVersion, + URL: "https://cattle.io/v1/apps.daemonsets", + desiredContent: alphaNumeric, + }, + { + name: "CSS escaped non alpha-numeric", + CSSURL: badChars, + JSURL: defaultJS, + APIUIVersion: defaultAPIVersion, + URL: "https://cattle.io/v1/apps.daemonsets", + desiredContent: escapedBadChars.String(), + undesiredContent: badChars, + }, + { + name: "api version alpha-numeric", + APIUIVersion: alphaNumeric, + URL: "https://cattle.io/v3", + desiredContent: alphaNumeric, + }, + { + name: "api version escaped non alpha-numeric", + APIUIVersion: badChars, + URL: "https://cattle.io/v1/apps.daemonsets", + desiredContent: escapedBadChars.String(), + undesiredContent: badChars, + }, + { + name: "Link XSS", + URL: "https://cattle.io/v1/apps.daemonsets" + xss, + undesiredContent: xss, + desiredContent: xssUrl.String(), + }, + } + for _, test := range tests { + tt := test + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + respStr, err := sendTestRequest(tt.URL, tt.CSSURL, tt.JSURL, tt.APIUIVersion) + require.NoError(t, err, "failed to create server") + require.Contains(t, respStr, tt.desiredContent, "expected content missing from server response") + if tt.undesiredContent != "" { + require.NotContains(t, respStr, tt.undesiredContent, "unexpected content found in server response") + } + }) + } +} + +func sendTestRequest(url, cssURL, jssURL, apiUIVersion string) (string, error) { + resp := httptest.NewRecorder() + req := httptest.NewRequest(http.MethodGet, url, nil) + // These header values are needed to get an HTML return document + req.Header.Set("Accept", "*/*") + req.Header.Set("User-agent", "Mozilla") + srv := DefaultAPIServer() + srv.CustomAPIUIResponseWriter(stringGetter(cssURL), stringGetter(jssURL), stringGetter(apiUIVersion)) + srv.Schemas = builtin.Schemas + apiOp := &types.APIRequest{ + Request: req, + Response: resp, + Type: "schema", + } + srv.Handle(apiOp) + return resp.Body.String(), nil +} + +func stringGetter(val string) writer.StringGetter { + return func() string { return val } +}
pkg/urlbuilder/base.go+5 −4 modified@@ -2,16 +2,17 @@ package urlbuilder import ( "bytes" - "fmt" "net/http" "net/url" "strings" ) func ParseRequestURL(r *http.Request) string { - scheme := GetScheme(r) - host := GetHost(r, scheme) - return fmt.Sprintf("%s://%s%s%s", scheme, host, r.Header.Get(PrefixHeader), r.URL.Path) + var parsedURL url.URL + parsedURL.Scheme = GetScheme(r) + parsedURL.Host = GetHost(r, parsedURL.Scheme) + parsedURL = *parsedURL.JoinPath(r.Header.Get(PrefixHeader), r.URL.Path) + return parsedURL.String() } func GetHost(r *http.Request, scheme string) string {
pkg/writer/html.go+29 −1 modified@@ -2,6 +2,7 @@ package writer import ( "encoding/json" + "fmt" "strings" "github.com/rancher/apiserver/pkg/types" @@ -10,7 +11,7 @@ import ( const ( JSURL = "https://releases.rancher.com/api-ui/%API_UI_VERSION%/ui.min.js" CSSURL = "https://releases.rancher.com/api-ui/%API_UI_VERSION%/ui.min.css" - DefaultVersion = "1.1.10" + DefaultVersion = "1.1.11" ) var ( @@ -71,6 +72,11 @@ func (h *HTMLResponseWriter) write(apiOp *types.APIRequest, code int, obj interf jsurl = strings.Replace(JSURL, "%API_UI_VERSION%", DefaultVersion, 1) cssurl = strings.Replace(CSSURL, "%API_UI_VERSION%", DefaultVersion, 1) } + + // jsurl and cssurl are added to the document as attributes not entities which requires special encoding. + jsurl, _ = encodeAttribute(jsurl) + cssurl, _ = encodeAttribute(cssurl) + headerString = strings.Replace(headerString, "%JSURL%", jsurl, 1) headerString = strings.Replace(headerString, "%CSSURL%", cssurl, 1) @@ -89,3 +95,25 @@ func jsonEncodeURL(str string) string { data, _ := json.Marshal(str) return string(data) } + +// encodeAttribute encodes all characters with the HTML Entity &#xHH; format, including spaces, where HH represents the hexadecimal value of the character in Unicode. +// For example, A becomes A. All alphanumeric characters (letters A to Z, a to z, and digits 0 to 9) remain unencoded. +// more info: https://cheatsheetseries.owasp.org/cheatsheets/Cross_Site_Scripting_Prevention_Cheat_Sheet.html#output-encoding-rules-summary +func encodeAttribute(raw string) (string, error) { + var builder strings.Builder + for _, r := range raw { + if ('A' <= r && r <= 'Z') || ('a' <= r && r <= 'z') || ('0' <= r && r <= '9') { + _, err := builder.WriteRune(r) + if err != nil { + return "", fmt.Errorf("failed to write: %w", err) + } + } else { + // encode non-alphanumeric rune to hex. + _, err := fmt.Fprintf(&builder, "&#x%X;", r) + if err != nil { + return "", fmt.Errorf("failed to write: %w", err) + } + } + } + return builder.String(), nil +}
69b3c2b56f3f[2.8] Fixes (#58)
3 files changed · +164 −5
pkg/server/server_test.go+130 −0 modified@@ -2,18 +2,24 @@ package server import ( "errors" + "fmt" "net/http" + "net/http/httptest" + "net/url" + "strings" "testing" "github.com/golang/mock/gomock" "github.com/rancher/apiserver/pkg/apierror" + "github.com/rancher/apiserver/pkg/builtin" "github.com/rancher/apiserver/pkg/fakes" "github.com/rancher/apiserver/pkg/parse" "github.com/rancher/apiserver/pkg/types" "github.com/rancher/apiserver/pkg/writer" "github.com/rancher/wrangler/v2/pkg/schemas" "github.com/rancher/wrangler/v2/pkg/schemas/validation" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" ) @@ -360,3 +366,127 @@ func (p *ServerSuite) TestServer_CustomAPIUIResponseWriter() { assert.NotNil(p.T(), w.JSURL) assert.NotNil(p.T(), w.APIUIVersion) } + +func TestServeHTMLEscaping(t *testing.T) { + const ( + defaultJS = "cattle.io" + defaultCSS = "cattle.io" + defaultAPIVersion = "v1/apps.daemonsets.0.0" + xss = "<script>alert('xss')</script>" + alphaNumeric = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" + badChars = `~!@#$%^&*()_+-=[]\{}|;':",./<>?` + ) + xssUrl := url.URL{RawPath: xss} + + var escapedBadChars strings.Builder + for _, r := range badChars { + escapedBadChars.WriteString(fmt.Sprintf("&#x%X;", r)) + } + + t.Parallel() + tests := []struct { + name string + CSSURL string + JSURL string + APIUIVersion string + URL string + desiredContent string + undesiredContent string + }{ + { + name: "base case no xss", + CSSURL: defaultCSS, + JSURL: defaultJS, + APIUIVersion: defaultAPIVersion, + URL: "https://cattle.io/v1/apps.daemonsets", + desiredContent: "https://cattle.io/v1/apps.daemonsets", + }, + { + name: "JSS alpha-numeric", + CSSURL: defaultCSS, + JSURL: alphaNumeric, + APIUIVersion: defaultAPIVersion, + URL: "https://cattle.io/v1/apps.daemonsets", + desiredContent: alphaNumeric, + }, + { + name: "JSS escaped non alpha-numeric", + CSSURL: defaultCSS, + JSURL: badChars, + APIUIVersion: defaultAPIVersion, + URL: "https://cattle.io/v1/apps.daemonsets", + desiredContent: escapedBadChars.String(), + undesiredContent: badChars, + }, + { + name: "CSS alpha-numeric", + CSSURL: alphaNumeric, + JSURL: defaultJS, + APIUIVersion: defaultAPIVersion, + URL: "https://cattle.io/v1/apps.daemonsets", + desiredContent: alphaNumeric, + }, + { + name: "CSS escaped non alpha-numeric", + CSSURL: badChars, + JSURL: defaultJS, + APIUIVersion: defaultAPIVersion, + URL: "https://cattle.io/v1/apps.daemonsets", + desiredContent: escapedBadChars.String(), + undesiredContent: badChars, + }, + { + name: "api version alpha-numeric", + APIUIVersion: alphaNumeric, + URL: "https://cattle.io/v3", + desiredContent: alphaNumeric, + }, + { + name: "api version escaped non alpha-numeric", + APIUIVersion: badChars, + URL: "https://cattle.io/v1/apps.daemonsets", + desiredContent: escapedBadChars.String(), + undesiredContent: badChars, + }, + { + name: "Link XSS", + URL: "https://cattle.io/v1/apps.daemonsets" + xss, + undesiredContent: xss, + desiredContent: xssUrl.String(), + }, + } + for _, test := range tests { + tt := test + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + respStr, err := sendTestRequest(tt.URL, tt.CSSURL, tt.JSURL, tt.APIUIVersion) + require.NoError(t, err, "failed to create server") + require.Contains(t, respStr, tt.desiredContent, "expected content missing from server response") + if tt.undesiredContent != "" { + require.NotContains(t, respStr, tt.undesiredContent, "unexpected content found in server response") + } + }) + } +} + +func sendTestRequest(url, cssURL, jssURL, apiUIVersion string) (string, error) { + resp := httptest.NewRecorder() + req := httptest.NewRequest(http.MethodGet, url, nil) + // These header values are needed to get an HTML return document + req.Header.Set("Accept", "*/*") + req.Header.Set("User-agent", "Mozilla") + srv := DefaultAPIServer() + srv.CustomAPIUIResponseWriter(stringGetter(cssURL), stringGetter(jssURL), stringGetter(apiUIVersion)) + srv.Schemas = builtin.Schemas + apiOp := &types.APIRequest{ + Request: req, + Response: resp, + Type: "schema", + } + srv.Handle(apiOp) + return resp.Body.String(), nil +} + +func stringGetter(val string) writer.StringGetter { + return func() string { return val } +}
pkg/urlbuilder/base.go+5 −4 modified@@ -2,16 +2,17 @@ package urlbuilder import ( "bytes" - "fmt" "net/http" "net/url" "strings" ) func ParseRequestURL(r *http.Request) string { - scheme := GetScheme(r) - host := GetHost(r, scheme) - return fmt.Sprintf("%s://%s%s%s", scheme, host, r.Header.Get(PrefixHeader), r.URL.Path) + var parsedURL url.URL + parsedURL.Scheme = GetScheme(r) + parsedURL.Host = GetHost(r, parsedURL.Scheme) + parsedURL = *parsedURL.JoinPath(r.Header.Get(PrefixHeader), r.URL.Path) + return parsedURL.String() } func GetHost(r *http.Request, scheme string) string {
pkg/writer/html.go+29 −1 modified@@ -2,6 +2,7 @@ package writer import ( "encoding/json" + "fmt" "strings" "github.com/rancher/apiserver/pkg/types" @@ -10,7 +11,7 @@ import ( const ( JSURL = "https://releases.rancher.com/api-ui/%API_UI_VERSION%/ui.min.js" CSSURL = "https://releases.rancher.com/api-ui/%API_UI_VERSION%/ui.min.css" - DefaultVersion = "1.1.10" + DefaultVersion = "1.1.11" ) var ( @@ -71,6 +72,11 @@ func (h *HTMLResponseWriter) write(apiOp *types.APIRequest, code int, obj interf jsurl = strings.Replace(JSURL, "%API_UI_VERSION%", DefaultVersion, 1) cssurl = strings.Replace(CSSURL, "%API_UI_VERSION%", DefaultVersion, 1) } + + // jsurl and cssurl are added to the document as attributes not entities which requires special encoding. + jsurl, _ = encodeAttribute(jsurl) + cssurl, _ = encodeAttribute(cssurl) + headerString = strings.Replace(headerString, "%JSURL%", jsurl, 1) headerString = strings.Replace(headerString, "%CSSURL%", cssurl, 1) @@ -89,3 +95,25 @@ func jsonEncodeURL(str string) string { data, _ := json.Marshal(str) return string(data) } + +// encodeAttribute encodes all characters with the HTML Entity &#xHH; format, including spaces, where HH represents the hexadecimal value of the character in Unicode. +// For example, A becomes A. All alphanumeric characters (letters A to Z, a to z, and digits 0 to 9) remain unencoded. +// more info: https://cheatsheetseries.owasp.org/cheatsheets/Cross_Site_Scripting_Prevention_Cheat_Sheet.html#output-encoding-rules-summary +func encodeAttribute(raw string) (string, error) { + var builder strings.Builder + for _, r := range raw { + if ('A' <= r && r <= 'Z') || ('a' <= r && r <= 'z') || ('0' <= r && r <= '9') { + _, err := builder.WriteRune(r) + if err != nil { + return "", fmt.Errorf("failed to write: %w", err) + } + } else { + // encode non-alphanumeric rune to hex. + _, err := fmt.Fprintf(&builder, "&#x%X;", r) + if err != nil { + return "", fmt.Errorf("failed to write: %w", err) + } + } + } + return builder.String(), nil +}
4e102cf0d07b[2.7] Fixes (#57)
3 files changed · +164 −5
pkg/server/server_test.go+130 −0 modified@@ -2,18 +2,24 @@ package server import ( "errors" + "fmt" "net/http" + "net/http/httptest" + "net/url" + "strings" "testing" "github.com/golang/mock/gomock" "github.com/rancher/apiserver/pkg/apierror" + "github.com/rancher/apiserver/pkg/builtin" "github.com/rancher/apiserver/pkg/fakes" "github.com/rancher/apiserver/pkg/parse" "github.com/rancher/apiserver/pkg/types" "github.com/rancher/apiserver/pkg/writer" "github.com/rancher/wrangler/pkg/schemas" "github.com/rancher/wrangler/pkg/schemas/validation" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" ) @@ -360,3 +366,127 @@ func (p *ServerSuite) TestServer_CustomAPIUIResponseWriter() { assert.NotNil(p.T(), w.JSURL) assert.NotNil(p.T(), w.APIUIVersion) } + +func TestServeHTMLEscaping(t *testing.T) { + const ( + defaultJS = "cattle.io" + defaultCSS = "cattle.io" + defaultAPIVersion = "v1/apps.daemonsets.0.0" + xss = "<script>alert('xss')</script>" + alphaNumeric = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" + badChars = `~!@#$%^&*()_+-=[]\{}|;':",./<>?` + ) + xssUrl := url.URL{RawPath: xss} + + var escapedBadChars strings.Builder + for _, r := range badChars { + escapedBadChars.WriteString(fmt.Sprintf("&#x%X;", r)) + } + + t.Parallel() + tests := []struct { + name string + CSSURL string + JSURL string + APIUIVersion string + URL string + desiredContent string + undesiredContent string + }{ + { + name: "base case no xss", + CSSURL: defaultCSS, + JSURL: defaultJS, + APIUIVersion: defaultAPIVersion, + URL: "https://cattle.io/v1/apps.daemonsets", + desiredContent: "https://cattle.io/v1/apps.daemonsets", + }, + { + name: "JSS alpha-numeric", + CSSURL: defaultCSS, + JSURL: alphaNumeric, + APIUIVersion: defaultAPIVersion, + URL: "https://cattle.io/v1/apps.daemonsets", + desiredContent: alphaNumeric, + }, + { + name: "JSS escaped non alpha-numeric", + CSSURL: defaultCSS, + JSURL: badChars, + APIUIVersion: defaultAPIVersion, + URL: "https://cattle.io/v1/apps.daemonsets", + desiredContent: escapedBadChars.String(), + undesiredContent: badChars, + }, + { + name: "CSS alpha-numeric", + CSSURL: alphaNumeric, + JSURL: defaultJS, + APIUIVersion: defaultAPIVersion, + URL: "https://cattle.io/v1/apps.daemonsets", + desiredContent: alphaNumeric, + }, + { + name: "CSS escaped non alpha-numeric", + CSSURL: badChars, + JSURL: defaultJS, + APIUIVersion: defaultAPIVersion, + URL: "https://cattle.io/v1/apps.daemonsets", + desiredContent: escapedBadChars.String(), + undesiredContent: badChars, + }, + { + name: "api version alpha-numeric", + APIUIVersion: alphaNumeric, + URL: "https://cattle.io/v3", + desiredContent: alphaNumeric, + }, + { + name: "api version escaped non alpha-numeric", + APIUIVersion: badChars, + URL: "https://cattle.io/v1/apps.daemonsets", + desiredContent: escapedBadChars.String(), + undesiredContent: badChars, + }, + { + name: "Link XSS", + URL: "https://cattle.io/v1/apps.daemonsets" + xss, + undesiredContent: xss, + desiredContent: xssUrl.String(), + }, + } + for _, test := range tests { + tt := test + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + respStr, err := sendTestRequest(tt.URL, tt.CSSURL, tt.JSURL, tt.APIUIVersion) + require.NoError(t, err, "failed to create server") + require.Contains(t, respStr, tt.desiredContent, "expected content missing from server response") + if tt.undesiredContent != "" { + require.NotContains(t, respStr, tt.undesiredContent, "unexpected content found in server response") + } + }) + } +} + +func sendTestRequest(url, cssURL, jssURL, apiUIVersion string) (string, error) { + resp := httptest.NewRecorder() + req := httptest.NewRequest(http.MethodGet, url, nil) + // These header values are needed to get an HTML return document + req.Header.Set("Accept", "*/*") + req.Header.Set("User-agent", "Mozilla") + srv := DefaultAPIServer() + srv.CustomAPIUIResponseWriter(stringGetter(cssURL), stringGetter(jssURL), stringGetter(apiUIVersion)) + srv.Schemas = builtin.Schemas + apiOp := &types.APIRequest{ + Request: req, + Response: resp, + Type: "schema", + } + srv.Handle(apiOp) + return resp.Body.String(), nil +} + +func stringGetter(val string) writer.StringGetter { + return func() string { return val } +}
pkg/urlbuilder/base.go+5 −4 modified@@ -2,16 +2,17 @@ package urlbuilder import ( "bytes" - "fmt" "net/http" "net/url" "strings" ) func ParseRequestURL(r *http.Request) string { - scheme := GetScheme(r) - host := GetHost(r, scheme) - return fmt.Sprintf("%s://%s%s%s", scheme, host, r.Header.Get(PrefixHeader), r.URL.Path) + var parsedURL url.URL + parsedURL.Scheme = GetScheme(r) + parsedURL.Host = GetHost(r, parsedURL.Scheme) + parsedURL = *parsedURL.JoinPath(r.Header.Get(PrefixHeader), r.URL.Path) + return parsedURL.String() } func GetHost(r *http.Request, scheme string) string {
pkg/writer/html.go+29 −1 modified@@ -2,6 +2,7 @@ package writer import ( "encoding/json" + "fmt" "strings" "github.com/rancher/apiserver/pkg/types" @@ -10,7 +11,7 @@ import ( const ( JSURL = "https://releases.rancher.com/api-ui/%API_UI_VERSION%/ui.min.js" CSSURL = "https://releases.rancher.com/api-ui/%API_UI_VERSION%/ui.min.css" - DefaultVersion = "1.1.10" + DefaultVersion = "1.1.11" ) var ( @@ -71,6 +72,11 @@ func (h *HTMLResponseWriter) write(apiOp *types.APIRequest, code int, obj interf jsurl = strings.Replace(JSURL, "%API_UI_VERSION%", DefaultVersion, 1) cssurl = strings.Replace(CSSURL, "%API_UI_VERSION%", DefaultVersion, 1) } + + // jsurl and cssurl are added to the document as attributes not entities which requires special encoding. + jsurl, _ = encodeAttribute(jsurl) + cssurl, _ = encodeAttribute(cssurl) + headerString = strings.Replace(headerString, "%JSURL%", jsurl, 1) headerString = strings.Replace(headerString, "%CSSURL%", cssurl, 1) @@ -89,3 +95,25 @@ func jsonEncodeURL(str string) string { data, _ := json.Marshal(str) return string(data) } + +// encodeAttribute encodes all characters with the HTML Entity &#xHH; format, including spaces, where HH represents the hexadecimal value of the character in Unicode. +// For example, A becomes A. All alphanumeric characters (letters A to Z, a to z, and digits 0 to 9) remain unencoded. +// more info: https://cheatsheetseries.owasp.org/cheatsheets/Cross_Site_Scripting_Prevention_Cheat_Sheet.html#output-encoding-rules-summary +func encodeAttribute(raw string) (string, error) { + var builder strings.Builder + for _, r := range raw { + if ('A' <= r && r <= 'Z') || ('a' <= r && r <= 'z') || ('0' <= r && r <= '9') { + _, err := builder.WriteRune(r) + if err != nil { + return "", fmt.Errorf("failed to write: %w", err) + } + } else { + // encode non-alphanumeric rune to hex. + _, err := fmt.Fprintf(&builder, "&#x%X;", r) + if err != nil { + return "", fmt.Errorf("failed to write: %w", err) + } + } + } + return builder.String(), nil +}
4df268e250f6[2.6] Fixes (#55)
3 files changed · +173 −5
pkg/server/server_test.go+139 −0 added@@ -0,0 +1,139 @@ +package server + +import ( + "fmt" + "net/http" + "net/http/httptest" + "net/url" + "strings" + "testing" + + "github.com/rancher/apiserver/pkg/builtin" + "github.com/rancher/apiserver/pkg/types" + "github.com/rancher/apiserver/pkg/writer" + "github.com/stretchr/testify/require" +) + +func TestServeHTMLEscaping(t *testing.T) { + const ( + defaultJS = "cattle.io" + defaultCSS = "cattle.io" + defaultAPIVersion = "v1/apps.daemonsets.0.0" + xss = "<script>alert('xss')</script>" + alphaNumeric = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" + badChars = `~!@#$%^&*()_+-=[]\{}|;':",./<>?` + ) + xssUrl := url.URL{RawPath: xss} + + var escapedBadChars strings.Builder + for _, r := range badChars { + escapedBadChars.WriteString(fmt.Sprintf("&#x%X;", r)) + } + + t.Parallel() + tests := []struct { + name string + CSSURL string + JSURL string + APIUIVersion string + URL string + desiredContent string + undesiredContent string + }{ + { + name: "base case no xss", + CSSURL: defaultCSS, + JSURL: defaultJS, + APIUIVersion: defaultAPIVersion, + URL: "https://cattle.io/v1/apps.daemonsets", + desiredContent: "https://cattle.io/v1/apps.daemonsets", + }, + { + name: "JSS alpha-numeric", + CSSURL: defaultCSS, + JSURL: alphaNumeric, + APIUIVersion: defaultAPIVersion, + URL: "https://cattle.io/v1/apps.daemonsets", + desiredContent: alphaNumeric, + }, + { + name: "JSS escaped non alpha-numeric", + CSSURL: defaultCSS, + JSURL: badChars, + APIUIVersion: defaultAPIVersion, + URL: "https://cattle.io/v1/apps.daemonsets", + desiredContent: escapedBadChars.String(), + undesiredContent: badChars, + }, + { + name: "CSS alpha-numeric", + CSSURL: alphaNumeric, + JSURL: defaultJS, + APIUIVersion: defaultAPIVersion, + URL: "https://cattle.io/v1/apps.daemonsets", + desiredContent: alphaNumeric, + }, + { + name: "CSS escaped non alpha-numeric", + CSSURL: badChars, + JSURL: defaultJS, + APIUIVersion: defaultAPIVersion, + URL: "https://cattle.io/v1/apps.daemonsets", + desiredContent: escapedBadChars.String(), + undesiredContent: badChars, + }, + { + name: "api version alpha-numeric", + APIUIVersion: alphaNumeric, + URL: "https://cattle.io/v3", + desiredContent: alphaNumeric, + }, + { + name: "api version escaped non alpha-numeric", + APIUIVersion: badChars, + URL: "https://cattle.io/v1/apps.daemonsets", + desiredContent: escapedBadChars.String(), + undesiredContent: badChars, + }, + { + name: "Link XSS", + URL: "https://cattle.io/v1/apps.daemonsets" + xss, + undesiredContent: xss, + desiredContent: xssUrl.String(), + }, + } + for _, test := range tests { + tt := test + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + respStr, err := sendTestRequest(tt.URL, tt.CSSURL, tt.JSURL, tt.APIUIVersion) + require.NoError(t, err, "failed to create server") + require.Contains(t, respStr, tt.desiredContent, "expected content missing from server response") + if tt.undesiredContent != "" { + require.NotContains(t, respStr, tt.undesiredContent, "unexpected content found in server response") + } + }) + } +} + +func sendTestRequest(url, cssURL, jssURL, apiUIVersion string) (string, error) { + resp := httptest.NewRecorder() + req := httptest.NewRequest(http.MethodGet, url, nil) + // These header values are needed to get an HTML return document + req.Header.Set("Accept", "*/*") + req.Header.Set("User-agent", "Mozilla") + srv := DefaultAPIServer() + srv.CustomAPIUIResponseWriter(stringGetter(cssURL), stringGetter(jssURL), stringGetter(apiUIVersion)) + srv.Schemas = builtin.Schemas + apiOp := &types.APIRequest{ + Request: req, + Response: resp, + Type: "schema", + } + srv.Handle(apiOp) + return resp.Body.String(), nil +} + +func stringGetter(val string) writer.StringGetter { + return func() string { return val } +}
pkg/urlbuilder/base.go+5 −4 modified@@ -2,16 +2,17 @@ package urlbuilder import ( "bytes" - "fmt" "net/http" "net/url" "strings" ) func ParseRequestURL(r *http.Request) string { - scheme := GetScheme(r) - host := GetHost(r, scheme) - return fmt.Sprintf("%s://%s%s%s", scheme, host, r.Header.Get(PrefixHeader), r.URL.Path) + var parsedURL url.URL + parsedURL.Scheme = GetScheme(r) + parsedURL.Host = GetHost(r, parsedURL.Scheme) + parsedURL = *parsedURL.JoinPath(r.Header.Get(PrefixHeader), r.URL.Path) + return parsedURL.String() } func GetHost(r *http.Request, scheme string) string {
pkg/writer/html.go+29 −1 modified@@ -2,6 +2,7 @@ package writer import ( "encoding/json" + "fmt" "strings" "github.com/rancher/apiserver/pkg/types" @@ -10,7 +11,7 @@ import ( const ( JSURL = "https://releases.rancher.com/api-ui/%API_UI_VERSION%/ui.min.js" CSSURL = "https://releases.rancher.com/api-ui/%API_UI_VERSION%/ui.min.css" - DefaultVersion = "1.1.10" + DefaultVersion = "1.1.11" ) var ( @@ -71,6 +72,11 @@ func (h *HTMLResponseWriter) write(apiOp *types.APIRequest, code int, obj interf jsurl = strings.Replace(JSURL, "%API_UI_VERSION%", DefaultVersion, 1) cssurl = strings.Replace(CSSURL, "%API_UI_VERSION%", DefaultVersion, 1) } + + // jsurl and cssurl are added to the document as attributes not entities which requires special encoding. + jsurl, _ = encodeAttribute(jsurl) + cssurl, _ = encodeAttribute(cssurl) + headerString = strings.Replace(headerString, "%JSURL%", jsurl, 1) headerString = strings.Replace(headerString, "%CSSURL%", cssurl, 1) @@ -89,3 +95,25 @@ func jsonEncodeURL(str string) string { data, _ := json.Marshal(str) return string(data) } + +// encodeAttribute encodes all characters with the HTML Entity &#xHH; format, including spaces, where HH represents the hexadecimal value of the character in Unicode. +// For example, A becomes A. All alphanumeric characters (letters A to Z, a to z, and digits 0 to 9) remain unencoded. +// more info: https://cheatsheetseries.owasp.org/cheatsheets/Cross_Site_Scripting_Prevention_Cheat_Sheet.html#output-encoding-rules-summary +func encodeAttribute(raw string) (string, error) { + var builder strings.Builder + for _, r := range raw { + if ('A' <= r && r <= 'Z') || ('a' <= r && r <= 'z') || ('0' <= r && r <= '9') { + _, err := builder.WriteRune(r) + if err != nil { + return "", fmt.Errorf("failed to write: %w", err) + } + } else { + // encode non-alphanumeric rune to hex. + _, err := fmt.Fprintf(&builder, "&#x%X;", r) + if err != nil { + return "", fmt.Errorf("failed to write: %w", err) + } + } + } + return builder.String(), nil +}
97a10a30200c[2.7] Fixes (#54)
3 files changed · +164 −5
pkg/server/server_test.go+130 −0 modified@@ -2,18 +2,24 @@ package server import ( "errors" + "fmt" "net/http" + "net/http/httptest" + "net/url" + "strings" "testing" "github.com/golang/mock/gomock" "github.com/rancher/apiserver/pkg/apierror" + "github.com/rancher/apiserver/pkg/builtin" "github.com/rancher/apiserver/pkg/fakes" "github.com/rancher/apiserver/pkg/parse" "github.com/rancher/apiserver/pkg/types" "github.com/rancher/apiserver/pkg/writer" "github.com/rancher/wrangler/pkg/schemas" "github.com/rancher/wrangler/pkg/schemas/validation" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" ) @@ -360,3 +366,127 @@ func (p *ServerSuite) TestServer_CustomAPIUIResponseWriter() { assert.NotNil(p.T(), w.JSURL) assert.NotNil(p.T(), w.APIUIVersion) } + +func TestServeHTMLEscaping(t *testing.T) { + const ( + defaultJS = "cattle.io" + defaultCSS = "cattle.io" + defaultAPIVersion = "v1/apps.daemonsets.0.0" + xss = "<script>alert('xss')</script>" + alphaNumeric = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" + badChars = `~!@#$%^&*()_+-=[]\{}|;':",./<>?` + ) + xssUrl := url.URL{RawPath: xss} + + var escapedBadChars strings.Builder + for _, r := range badChars { + escapedBadChars.WriteString(fmt.Sprintf("&#x%X;", r)) + } + + t.Parallel() + tests := []struct { + name string + CSSURL string + JSURL string + APIUIVersion string + URL string + desiredContent string + undesiredContent string + }{ + { + name: "base case no xss", + CSSURL: defaultCSS, + JSURL: defaultJS, + APIUIVersion: defaultAPIVersion, + URL: "https://cattle.io/v1/apps.daemonsets", + desiredContent: "https://cattle.io/v1/apps.daemonsets", + }, + { + name: "JSS alpha-numeric", + CSSURL: defaultCSS, + JSURL: alphaNumeric, + APIUIVersion: defaultAPIVersion, + URL: "https://cattle.io/v1/apps.daemonsets", + desiredContent: alphaNumeric, + }, + { + name: "JSS escaped non alpha-numeric", + CSSURL: defaultCSS, + JSURL: badChars, + APIUIVersion: defaultAPIVersion, + URL: "https://cattle.io/v1/apps.daemonsets", + desiredContent: escapedBadChars.String(), + undesiredContent: badChars, + }, + { + name: "CSS alpha-numeric", + CSSURL: alphaNumeric, + JSURL: defaultJS, + APIUIVersion: defaultAPIVersion, + URL: "https://cattle.io/v1/apps.daemonsets", + desiredContent: alphaNumeric, + }, + { + name: "CSS escaped non alpha-numeric", + CSSURL: badChars, + JSURL: defaultJS, + APIUIVersion: defaultAPIVersion, + URL: "https://cattle.io/v1/apps.daemonsets", + desiredContent: escapedBadChars.String(), + undesiredContent: badChars, + }, + { + name: "api version alpha-numeric", + APIUIVersion: alphaNumeric, + URL: "https://cattle.io/v3", + desiredContent: alphaNumeric, + }, + { + name: "api version escaped non alpha-numeric", + APIUIVersion: badChars, + URL: "https://cattle.io/v1/apps.daemonsets", + desiredContent: escapedBadChars.String(), + undesiredContent: badChars, + }, + { + name: "Link XSS", + URL: "https://cattle.io/v1/apps.daemonsets" + xss, + undesiredContent: xss, + desiredContent: xssUrl.String(), + }, + } + for _, test := range tests { + tt := test + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + respStr, err := sendTestRequest(tt.URL, tt.CSSURL, tt.JSURL, tt.APIUIVersion) + require.NoError(t, err, "failed to create server") + require.Contains(t, respStr, tt.desiredContent, "expected content missing from server response") + if tt.undesiredContent != "" { + require.NotContains(t, respStr, tt.undesiredContent, "unexpected content found in server response") + } + }) + } +} + +func sendTestRequest(url, cssURL, jssURL, apiUIVersion string) (string, error) { + resp := httptest.NewRecorder() + req := httptest.NewRequest(http.MethodGet, url, nil) + // These header values are needed to get an HTML return document + req.Header.Set("Accept", "*/*") + req.Header.Set("User-agent", "Mozilla") + srv := DefaultAPIServer() + srv.CustomAPIUIResponseWriter(stringGetter(cssURL), stringGetter(jssURL), stringGetter(apiUIVersion)) + srv.Schemas = builtin.Schemas + apiOp := &types.APIRequest{ + Request: req, + Response: resp, + Type: "schema", + } + srv.Handle(apiOp) + return resp.Body.String(), nil +} + +func stringGetter(val string) writer.StringGetter { + return func() string { return val } +}
pkg/urlbuilder/base.go+5 −4 modified@@ -2,16 +2,17 @@ package urlbuilder import ( "bytes" - "fmt" "net/http" "net/url" "strings" ) func ParseRequestURL(r *http.Request) string { - scheme := GetScheme(r) - host := GetHost(r, scheme) - return fmt.Sprintf("%s://%s%s%s", scheme, host, r.Header.Get(PrefixHeader), r.URL.Path) + var parsedURL url.URL + parsedURL.Scheme = GetScheme(r) + parsedURL.Host = GetHost(r, parsedURL.Scheme) + parsedURL = *parsedURL.JoinPath(r.Header.Get(PrefixHeader), r.URL.Path) + return parsedURL.String() } func GetHost(r *http.Request, scheme string) string {
pkg/writer/html.go+29 −1 modified@@ -2,6 +2,7 @@ package writer import ( "encoding/json" + "fmt" "strings" "github.com/rancher/apiserver/pkg/types" @@ -10,7 +11,7 @@ import ( const ( JSURL = "https://releases.rancher.com/api-ui/%API_UI_VERSION%/ui.min.js" CSSURL = "https://releases.rancher.com/api-ui/%API_UI_VERSION%/ui.min.css" - DefaultVersion = "1.1.10" + DefaultVersion = "1.1.11" ) var ( @@ -71,6 +72,11 @@ func (h *HTMLResponseWriter) write(apiOp *types.APIRequest, code int, obj interf jsurl = strings.Replace(JSURL, "%API_UI_VERSION%", DefaultVersion, 1) cssurl = strings.Replace(CSSURL, "%API_UI_VERSION%", DefaultVersion, 1) } + + // jsurl and cssurl are added to the document as attributes not entities which requires special encoding. + jsurl, _ = encodeAttribute(jsurl) + cssurl, _ = encodeAttribute(cssurl) + headerString = strings.Replace(headerString, "%JSURL%", jsurl, 1) headerString = strings.Replace(headerString, "%CSSURL%", cssurl, 1) @@ -89,3 +95,25 @@ func jsonEncodeURL(str string) string { data, _ := json.Marshal(str) return string(data) } + +// encodeAttribute encodes all characters with the HTML Entity &#xHH; format, including spaces, where HH represents the hexadecimal value of the character in Unicode. +// For example, A becomes A. All alphanumeric characters (letters A to Z, a to z, and digits 0 to 9) remain unencoded. +// more info: https://cheatsheetseries.owasp.org/cheatsheets/Cross_Site_Scripting_Prevention_Cheat_Sheet.html#output-encoding-rules-summary +func encodeAttribute(raw string) (string, error) { + var builder strings.Builder + for _, r := range raw { + if ('A' <= r && r <= 'Z') || ('a' <= r && r <= 'z') || ('0' <= r && r <= '9') { + _, err := builder.WriteRune(r) + if err != nil { + return "", fmt.Errorf("failed to write: %w", err) + } + } else { + // encode non-alphanumeric rune to hex. + _, err := fmt.Fprintf(&builder, "&#x%X;", r) + if err != nil { + return "", fmt.Errorf("failed to write: %w", err) + } + } + } + return builder.String(), nil +}
a3b9e3721c1b[2.8] Fixes (#53)
3 files changed · +164 −5
pkg/server/server_test.go+130 −0 modified@@ -2,18 +2,24 @@ package server import ( "errors" + "fmt" "net/http" + "net/http/httptest" + "net/url" + "strings" "testing" "github.com/golang/mock/gomock" "github.com/rancher/apiserver/pkg/apierror" + "github.com/rancher/apiserver/pkg/builtin" "github.com/rancher/apiserver/pkg/fakes" "github.com/rancher/apiserver/pkg/parse" "github.com/rancher/apiserver/pkg/types" "github.com/rancher/apiserver/pkg/writer" "github.com/rancher/wrangler/pkg/schemas" "github.com/rancher/wrangler/pkg/schemas/validation" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" ) @@ -360,3 +366,127 @@ func (p *ServerSuite) TestServer_CustomAPIUIResponseWriter() { assert.NotNil(p.T(), w.JSURL) assert.NotNil(p.T(), w.APIUIVersion) } + +func TestServeHTMLEscaping(t *testing.T) { + const ( + defaultJS = "cattle.io" + defaultCSS = "cattle.io" + defaultAPIVersion = "v1/apps.daemonsets.0.0" + xss = "<script>alert('xss')</script>" + alphaNumeric = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" + badChars = `~!@#$%^&*()_+-=[]\{}|;':",./<>?` + ) + xssUrl := url.URL{RawPath: xss} + + var escapedBadChars strings.Builder + for _, r := range badChars { + escapedBadChars.WriteString(fmt.Sprintf("&#x%X;", r)) + } + + t.Parallel() + tests := []struct { + name string + CSSURL string + JSURL string + APIUIVersion string + URL string + desiredContent string + undesiredContent string + }{ + { + name: "base case no xss", + CSSURL: defaultCSS, + JSURL: defaultJS, + APIUIVersion: defaultAPIVersion, + URL: "https://cattle.io/v1/apps.daemonsets", + desiredContent: "https://cattle.io/v1/apps.daemonsets", + }, + { + name: "JSS alpha-numeric", + CSSURL: defaultCSS, + JSURL: alphaNumeric, + APIUIVersion: defaultAPIVersion, + URL: "https://cattle.io/v1/apps.daemonsets", + desiredContent: alphaNumeric, + }, + { + name: "JSS escaped non alpha-numeric", + CSSURL: defaultCSS, + JSURL: badChars, + APIUIVersion: defaultAPIVersion, + URL: "https://cattle.io/v1/apps.daemonsets", + desiredContent: escapedBadChars.String(), + undesiredContent: badChars, + }, + { + name: "CSS alpha-numeric", + CSSURL: alphaNumeric, + JSURL: defaultJS, + APIUIVersion: defaultAPIVersion, + URL: "https://cattle.io/v1/apps.daemonsets", + desiredContent: alphaNumeric, + }, + { + name: "CSS escaped non alpha-numeric", + CSSURL: badChars, + JSURL: defaultJS, + APIUIVersion: defaultAPIVersion, + URL: "https://cattle.io/v1/apps.daemonsets", + desiredContent: escapedBadChars.String(), + undesiredContent: badChars, + }, + { + name: "api version alpha-numeric", + APIUIVersion: alphaNumeric, + URL: "https://cattle.io/v3", + desiredContent: alphaNumeric, + }, + { + name: "api version escaped non alpha-numeric", + APIUIVersion: badChars, + URL: "https://cattle.io/v1/apps.daemonsets", + desiredContent: escapedBadChars.String(), + undesiredContent: badChars, + }, + { + name: "Link XSS", + URL: "https://cattle.io/v1/apps.daemonsets" + xss, + undesiredContent: xss, + desiredContent: xssUrl.String(), + }, + } + for _, test := range tests { + tt := test + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + respStr, err := sendTestRequest(tt.URL, tt.CSSURL, tt.JSURL, tt.APIUIVersion) + require.NoError(t, err, "failed to create server") + require.Contains(t, respStr, tt.desiredContent, "expected content missing from server response") + if tt.undesiredContent != "" { + require.NotContains(t, respStr, tt.undesiredContent, "unexpected content found in server response") + } + }) + } +} + +func sendTestRequest(url, cssURL, jssURL, apiUIVersion string) (string, error) { + resp := httptest.NewRecorder() + req := httptest.NewRequest(http.MethodGet, url, nil) + // These header values are needed to get an HTML return document + req.Header.Set("Accept", "*/*") + req.Header.Set("User-agent", "Mozilla") + srv := DefaultAPIServer() + srv.CustomAPIUIResponseWriter(stringGetter(cssURL), stringGetter(jssURL), stringGetter(apiUIVersion)) + srv.Schemas = builtin.Schemas + apiOp := &types.APIRequest{ + Request: req, + Response: resp, + Type: "schema", + } + srv.Handle(apiOp) + return resp.Body.String(), nil +} + +func stringGetter(val string) writer.StringGetter { + return func() string { return val } +}
pkg/urlbuilder/base.go+5 −4 modified@@ -2,16 +2,17 @@ package urlbuilder import ( "bytes" - "fmt" "net/http" "net/url" "strings" ) func ParseRequestURL(r *http.Request) string { - scheme := GetScheme(r) - host := GetHost(r, scheme) - return fmt.Sprintf("%s://%s%s%s", scheme, host, r.Header.Get(PrefixHeader), r.URL.Path) + var parsedURL url.URL + parsedURL.Scheme = GetScheme(r) + parsedURL.Host = GetHost(r, parsedURL.Scheme) + parsedURL = *parsedURL.JoinPath(r.Header.Get(PrefixHeader), r.URL.Path) + return parsedURL.String() } func GetHost(r *http.Request, scheme string) string {
pkg/writer/html.go+29 −1 modified@@ -2,6 +2,7 @@ package writer import ( "encoding/json" + "fmt" "strings" "github.com/rancher/apiserver/pkg/types" @@ -10,7 +11,7 @@ import ( const ( JSURL = "https://releases.rancher.com/api-ui/%API_UI_VERSION%/ui.min.js" CSSURL = "https://releases.rancher.com/api-ui/%API_UI_VERSION%/ui.min.css" - DefaultVersion = "1.1.10" + DefaultVersion = "1.1.11" ) var ( @@ -71,6 +72,11 @@ func (h *HTMLResponseWriter) write(apiOp *types.APIRequest, code int, obj interf jsurl = strings.Replace(JSURL, "%API_UI_VERSION%", DefaultVersion, 1) cssurl = strings.Replace(CSSURL, "%API_UI_VERSION%", DefaultVersion, 1) } + + // jsurl and cssurl are added to the document as attributes not entities which requires special encoding. + jsurl, _ = encodeAttribute(jsurl) + cssurl, _ = encodeAttribute(cssurl) + headerString = strings.Replace(headerString, "%JSURL%", jsurl, 1) headerString = strings.Replace(headerString, "%CSSURL%", cssurl, 1) @@ -89,3 +95,25 @@ func jsonEncodeURL(str string) string { data, _ := json.Marshal(str) return string(data) } + +// encodeAttribute encodes all characters with the HTML Entity &#xHH; format, including spaces, where HH represents the hexadecimal value of the character in Unicode. +// For example, A becomes A. All alphanumeric characters (letters A to Z, a to z, and digits 0 to 9) remain unencoded. +// more info: https://cheatsheetseries.owasp.org/cheatsheets/Cross_Site_Scripting_Prevention_Cheat_Sheet.html#output-encoding-rules-summary +func encodeAttribute(raw string) (string, error) { + var builder strings.Builder + for _, r := range raw { + if ('A' <= r && r <= 'Z') || ('a' <= r && r <= 'z') || ('0' <= r && r <= '9') { + _, err := builder.WriteRune(r) + if err != nil { + return "", fmt.Errorf("failed to write: %w", err) + } + } else { + // encode non-alphanumeric rune to hex. + _, err := fmt.Fprintf(&builder, "&#x%X;", r) + if err != nil { + return "", fmt.Errorf("failed to write: %w", err) + } + } + } + return builder.String(), nil +}
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
10- github.com/advisories/GHSA-833m-37f7-jq55ghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2023-32192ghsaADVISORY
- bugzilla.suse.com/show_bug.cginvdWEB
- github.com/rancher/apiserver/commit/4df268e250f625fa323349062636496e0aeff4e4ghsaWEB
- github.com/rancher/apiserver/commit/4e102cf0d07b1af3d10d82c3e5a751a869b8a6c7ghsaWEB
- github.com/rancher/apiserver/commit/4fd7d821d952510bfe38c9d4a3e2a65157f50525ghsaWEB
- github.com/rancher/apiserver/commit/69b3c2b56f3fa5a421889c533dada8cd08783cdaghsaWEB
- github.com/rancher/apiserver/commit/97a10a30200cb851afd8ee85ee6b2295c4b6e5eeghsaWEB
- github.com/rancher/apiserver/commit/a3b9e3721c1b558ee63aec9594e37c223a5c8437ghsaWEB
- github.com/rancher/apiserver/security/advisories/GHSA-833m-37f7-jq55nvdWEB
News mentions
0No linked articles in our index yet.