From d0532bdb9ab40e06ee0702481f623d5054c8831a Mon Sep 17 00:00:00 2001 From: Han Kang Date: Fri, 4 Jan 2019 14:06:46 -0800 Subject: [PATCH] add a content-type filter to apiserver filters to autoset nosniff --- .../k8s.io/apiserver/pkg/server/filters/BUILD | 2 + .../pkg/server/filters/content_type.go | 28 +++++++++ .../pkg/server/filters/content_type_test.go | 60 +++++++++++++++++++ 3 files changed, 90 insertions(+) create mode 100644 staging/src/k8s.io/apiserver/pkg/server/filters/content_type.go create mode 100644 staging/src/k8s.io/apiserver/pkg/server/filters/content_type_test.go diff --git a/staging/src/k8s.io/apiserver/pkg/server/filters/BUILD b/staging/src/k8s.io/apiserver/pkg/server/filters/BUILD index b75b3de9824..44b669deb5a 100644 --- a/staging/src/k8s.io/apiserver/pkg/server/filters/BUILD +++ b/staging/src/k8s.io/apiserver/pkg/server/filters/BUILD @@ -10,6 +10,7 @@ go_test( name = "go_default_test", srcs = [ "compression_test.go", + "content_type_test.go", "cors_test.go", "maxinflight_test.go", "timeout_test.go", @@ -32,6 +33,7 @@ go_library( name = "go_default_library", srcs = [ "compression.go", + "content_type.go", "cors.go", "doc.go", "longrunning.go", diff --git a/staging/src/k8s.io/apiserver/pkg/server/filters/content_type.go b/staging/src/k8s.io/apiserver/pkg/server/filters/content_type.go new file mode 100644 index 00000000000..65c73fcdc46 --- /dev/null +++ b/staging/src/k8s.io/apiserver/pkg/server/filters/content_type.go @@ -0,0 +1,28 @@ +/* +Copyright 2019 The Kubernetes 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 filters + +import "net/http" + +// WithContentType sets both the Content-Type and the X-Content-Type-Options (nosniff) header +func WithContentType(handler http.Handler, contentType string) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", contentType) + w.Header().Set("X-Content-Type-Options", "nosniff") + handler.ServeHTTP(w, r) + }) +} diff --git a/staging/src/k8s.io/apiserver/pkg/server/filters/content_type_test.go b/staging/src/k8s.io/apiserver/pkg/server/filters/content_type_test.go new file mode 100644 index 00000000000..3fc163f18cf --- /dev/null +++ b/staging/src/k8s.io/apiserver/pkg/server/filters/content_type_test.go @@ -0,0 +1,60 @@ +/* +Copyright 2019 The Kubernetes 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 filters + +import ( + "net/http" + "net/http/httptest" + "testing" +) + +func noopHandler() http.HandlerFunc { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + // noop + }) +} + +func TestWithContentType(t *testing.T) { + mux := http.NewServeMux() + mux.Handle("/text", WithContentType(noopHandler(), "text/plain")) + mux.Handle("/json", WithContentType(noopHandler(), "application/json")) + tests := []struct { + description string + path string + expectedMimeType string + }{ + {"/text should return a plain text response", "/text", "text/plain"}, + {"/json should return a json response", "/json", "application/json"}, + } + for _, test := range tests { + path := "http://example.com" + test.path + t.Run(path, func(t *testing.T) { + req, err := http.NewRequest("GET", path, nil) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + w := httptest.NewRecorder() + mux.ServeHTTP(w, req) + if nosniffHeader := w.Header().Get("X-Content-Type-Options"); nosniffHeader != "nosniff" { + t.Errorf("expected nosniff header to be set, got %v", nosniffHeader) + } + if mimeTypeHeader := w.Header().Get("Content-Type"); mimeTypeHeader != test.expectedMimeType { + t.Errorf("expected %v, got %v", test.expectedMimeType, mimeTypeHeader) + } + }) + } +}