github.com/google/go-safeweb@v0.0.0-20231219055052-64d8cfc90fbb/tests/integration/csp/csp_test.go (about)

     1  // Copyright 2020 Google LLC
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //	https://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package csp_test
    16  
    17  import (
    18  	"net/http/httptest"
    19  	"testing"
    20  
    21  	"github.com/google/go-cmp/cmp"
    22  	"github.com/google/go-safeweb/safehttp"
    23  	"github.com/google/go-safeweb/safehttp/plugins/csp"
    24  	"github.com/google/safehtml/template"
    25  )
    26  
    27  func TestServeMuxInstallCSP(t *testing.T) {
    28  	mb := safehttp.NewServeMuxConfig(nil)
    29  	for _, i := range csp.Default("") {
    30  		mb.Intercept(i)
    31  	}
    32  
    33  	var nonce string
    34  	var err error
    35  	handler := safehttp.HandlerFunc(func(w safehttp.ResponseWriter, r *safehttp.IncomingRequest) safehttp.Result {
    36  		fns := map[string]interface{}{
    37  			"CSPNonce": func() string { return "WrongNonce" },
    38  		}
    39  		// These are not used in the handler, just recorded to test whether the
    40  		// handler can retrieve the CSP nonce (e.g. for when the CSP plugin is
    41  		// installed, but auto-injection isn't yet supported and has to be done
    42  		// manually by the handler).
    43  		nonce, err = csp.Nonce(r.Context())
    44  		t := template.Must(template.New("name").Funcs(fns).Parse(`<script nonce="{{CSPNonce}}" type="application/javascript">alert("script")</script><h1>{{.}}</h1>`))
    45  
    46  		return safehttp.ExecuteTemplateWithFuncs(w, t, "Content", fns)
    47  	})
    48  	mux := mb.Mux()
    49  	mux.Handle("/bar", safehttp.MethodGet, handler)
    50  
    51  	rr := httptest.NewRecorder()
    52  
    53  	req := httptest.NewRequest(safehttp.MethodGet, "https://foo.com/bar", nil)
    54  
    55  	mux.ServeHTTP(rr, req)
    56  
    57  	if err != nil {
    58  		t.Fatalf("csp.Nonce: got error %v", err)
    59  	}
    60  
    61  	if nonce == "" {
    62  		t.Fatalf("csp.Nonce: got %q, want non-empty", nonce)
    63  	}
    64  
    65  	if got, want := rr.Code, safehttp.StatusOK; got != int(want) {
    66  		t.Errorf("rr.Code got: %v want: %v", got, want)
    67  	}
    68  
    69  	wantHeaders := map[string][]string{
    70  		"Content-Type": {"text/html; charset=utf-8"},
    71  		"Content-Security-Policy": {
    72  			"object-src 'none'; script-src 'unsafe-inline' 'nonce-" + nonce + "' 'strict-dynamic' https: http:; base-uri 'none'",
    73  			"require-trusted-types-for 'script'"},
    74  	}
    75  	if diff := cmp.Diff(wantHeaders, map[string][]string(rr.Header())); diff != "" {
    76  		t.Errorf("rr.Header(): mismatch (-want +got):\n%s", diff)
    77  	}
    78  
    79  	wantBody := `<script nonce="` + nonce +
    80  		`" type="application/javascript">alert("script")</script><h1>Content</h1>`
    81  	if gotBody := rr.Body.String(); gotBody != wantBody {
    82  		t.Errorf("response body: got %q, want nonce %q", gotBody, wantBody)
    83  	}
    84  
    85  }