safeweb: add support for custom CSP (#13975)
To allow more flexibility with CSPs, add a fully customizable `CSP` type that can be provided in `Config` and encodes itself into the correct format. Preserve the `CSPAllowInlineStyles` option as is today, but maybe that'll get deprecated later in favor of the new CSP field. In particular, this allows for pages loading external JS, or inline JS with nonces or hashes (see https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/script-src#unsafe_inline_script) Updates https://github.com/tailscale/corp/issues/8027 Signed-off-by: Andrew Lytvynov <awly@tailscale.com>
This commit is contained in:
+18
-10
@@ -241,18 +241,26 @@ func TestCSRFProtection(t *testing.T) {
|
||||
func TestContentSecurityPolicyHeader(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
csp CSP
|
||||
apiRoute bool
|
||||
wantCSP bool
|
||||
wantCSP string
|
||||
}{
|
||||
{
|
||||
name: "default routes get CSP headers",
|
||||
apiRoute: false,
|
||||
wantCSP: true,
|
||||
name: "default CSP",
|
||||
wantCSP: `base-uri 'self'; block-all-mixed-content; default-src 'self'; form-action 'self'; frame-ancestors 'none';`,
|
||||
},
|
||||
{
|
||||
name: "custom CSP",
|
||||
csp: CSP{
|
||||
"default-src": {"'self'", "https://tailscale.com"},
|
||||
"upgrade-insecure-requests": nil,
|
||||
},
|
||||
wantCSP: `default-src 'self' https://tailscale.com; upgrade-insecure-requests;`,
|
||||
},
|
||||
{
|
||||
name: "`/api/*` routes do not get CSP headers",
|
||||
apiRoute: true,
|
||||
wantCSP: false,
|
||||
wantCSP: "",
|
||||
},
|
||||
}
|
||||
|
||||
@@ -265,9 +273,9 @@ func TestContentSecurityPolicyHeader(t *testing.T) {
|
||||
var s *Server
|
||||
var err error
|
||||
if tt.apiRoute {
|
||||
s, err = NewServer(Config{APIMux: h})
|
||||
s, err = NewServer(Config{APIMux: h, CSP: tt.csp})
|
||||
} else {
|
||||
s, err = NewServer(Config{BrowserMux: h})
|
||||
s, err = NewServer(Config{BrowserMux: h, CSP: tt.csp})
|
||||
}
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -279,8 +287,8 @@ func TestContentSecurityPolicyHeader(t *testing.T) {
|
||||
s.h.Handler.ServeHTTP(w, req)
|
||||
resp := w.Result()
|
||||
|
||||
if (resp.Header.Get("Content-Security-Policy") == "") == tt.wantCSP {
|
||||
t.Fatalf("content security policy want: %v; got: %v", tt.wantCSP, resp.Header.Get("Content-Security-Policy"))
|
||||
if got := resp.Header.Get("Content-Security-Policy"); got != tt.wantCSP {
|
||||
t.Fatalf("content security policy want: %q; got: %q", tt.wantCSP, got)
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -397,7 +405,7 @@ func TestCSPAllowInlineStyles(t *testing.T) {
|
||||
csp := resp.Header.Get("Content-Security-Policy")
|
||||
allowsStyles := strings.Contains(csp, "style-src 'self' 'unsafe-inline'")
|
||||
if allowsStyles != allow {
|
||||
t.Fatalf("CSP inline styles want: %v; got: %v", allow, allowsStyles)
|
||||
t.Fatalf("CSP inline styles want: %v, got: %v in %q", allow, allowsStyles, csp)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user