Skip to content

Commit

Permalink
feat: add coep, corp, x-dns-prefetch-control, x-permitted-cross-doman…
Browse files Browse the repository at this point in the history
…-policies (#102)

* feat: add coep, corp, x-dns-prefetch-control, x-permitted-cross-domain-policies headers

* updated readme

* update XDNSPrefetchControl to be of type string and fix test

* add newly added variables in the default section

* remove len check
  • Loading branch information
reversearrow authored Oct 22, 2024
1 parent 67655e9 commit c88f919
Show file tree
Hide file tree
Showing 3 changed files with 133 additions and 20 deletions.
9 changes: 8 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,10 @@ s := secure.New(secure.Options{
FeaturePolicy: "vibrate 'none';", // Deprecated: this header has been renamed to PermissionsPolicy. FeaturePolicy allows the Feature-Policy header with the value to be set with a custom value. Default is "".
PermissionsPolicy: "fullscreen=(), geolocation=()", // PermissionsPolicy allows the Permissions-Policy header with the value to be set with a custom value. Default is "".
CrossOriginOpenerPolicy: "same-origin", // CrossOriginOpenerPolicy allows the Cross-Origin-Opener-Policy header with the value to be set with a custom value. Default is "".

CrossOriginEmbedderPolicy: "require-corp", // CrossOriginEmbedderPolicy allows the Cross-Origin-Embedder-Policy header with the value to be set with a custom value. Default is "".
CrossOriginResourcePolicy: "same-origin", // CrossOriginResourcePolicy allows the Cross-Origin-Resource-Policy header with the value to be set with a custom value. Default is "".
XDNSPrefetchControl: "on", // XDNSPrefetchControl allows the X-DNS-Prefetch-Control header to be set via "on" or "off" keyword. Default is "".
XPermittedCrossDomainPolicies: "none", // XPermittedCrossDomainPolicies allows the X-Permitted-Cross-Domain-Policies to be set with a custom value. Default is "".
IsDevelopment: true, // This will cause the AllowedHosts, SSLRedirect, and STSSeconds/STSIncludeSubdomains options to be ignored during development. When deploying to production, be sure to set this to false.
})
// ...
Expand Down Expand Up @@ -121,6 +124,10 @@ l := secure.New(secure.Options{
FeaturePolicy: "",
PermissionsPolicy: "",
CrossOriginOpenerPolicy: "",
CrossOriginEmbedderPolicy: "",
CrossOriginResourcePolicy: "",
XDNSPrefetchControl: "",
XPermittedCrossDomainPolicies: "",
IsDevelopment: false,
})
~~~
Expand Down
76 changes: 57 additions & 19 deletions secure.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,25 +11,28 @@ import (
type secureCtxKey string

const (
stsHeader = "Strict-Transport-Security"
stsSubdomainString = "; includeSubDomains"
stsPreloadString = "; preload"
frameOptionsHeader = "X-Frame-Options"
frameOptionsValue = "DENY"
contentTypeHeader = "X-Content-Type-Options"
contentTypeValue = "nosniff"
xssProtectionHeader = "X-XSS-Protection"
xssProtectionValue = "1; mode=block"
cspHeader = "Content-Security-Policy"
cspReportOnlyHeader = "Content-Security-Policy-Report-Only"
hpkpHeader = "Public-Key-Pins"
referrerPolicyHeader = "Referrer-Policy"
featurePolicyHeader = "Feature-Policy"
permissionsPolicyHeader = "Permissions-Policy"
coopHeader = "Cross-Origin-Opener-Policy"

ctxDefaultSecureHeaderKey = secureCtxKey("SecureResponseHeader")
cspNonceSize = 16
stsHeader = "Strict-Transport-Security"
stsSubdomainString = "; includeSubDomains"
stsPreloadString = "; preload"
frameOptionsHeader = "X-Frame-Options"
frameOptionsValue = "DENY"
contentTypeHeader = "X-Content-Type-Options"
contentTypeValue = "nosniff"
xssProtectionHeader = "X-XSS-Protection"
xssProtectionValue = "1; mode=block"
cspHeader = "Content-Security-Policy"
cspReportOnlyHeader = "Content-Security-Policy-Report-Only"
hpkpHeader = "Public-Key-Pins"
referrerPolicyHeader = "Referrer-Policy"
featurePolicyHeader = "Feature-Policy"
permissionsPolicyHeader = "Permissions-Policy"
coopHeader = "Cross-Origin-Opener-Policy"
coepHeader = "Cross-Origin-Embedder-Policy"
corpHeader = "Cross-Origin-Resource-Policy"
dnsPreFetchControlHeader = "X-DNS-Prefetch-Control"
permittedCrossDomainPolicies = "X-Permitted-Cross-Domain-Policies"
ctxDefaultSecureHeaderKey = secureCtxKey("SecureResponseHeader")
cspNonceSize = 16
)

// SSLHostFunc is a custom function type that can be used to dynamically set the SSL host of a request.
Expand Down Expand Up @@ -91,6 +94,18 @@ type Options struct {
// CrossOriginOpenerPolicy allows you to ensure a top-level document does not share a browsing context group with cross-origin documents. Default is "".
// Reference: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Opener-Policy
CrossOriginOpenerPolicy string
// CrossOriginResourcePolicy header blocks others from loading your resources cross-origin in some cases.
// Reference https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Opener-Policy
CrossOriginResourcePolicy string
// CrossOriginEmbedderPolicy header helps control what resources can be loaded cross-origin.
// Reference https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Embedder-Policy
CrossOriginEmbedderPolicy string
// XDNSPrefetchControl header helps control DNS prefetching, which can improve user privacy at the expense of performance.
// Reference: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-DNS-Prefetch-Control
XDNSPrefetchControl string
// XPermittedCrossDomainPolicies header tells some clients (mostly Adobe products) your domain's policy for loading cross-domain content.
// Reference: https://owasp.org/www-project-secure-headers/
XPermittedCrossDomainPolicies string
// SSLHost is the host name that is used to redirect http requests to https. Default is "", which indicates to use the same host.
SSLHost string
// AllowedHosts is a slice of fully qualified domain names that are allowed. Default is an empty slice, which allows any and all host names.
Expand Down Expand Up @@ -466,6 +481,29 @@ func (s *Secure) processRequest(w http.ResponseWriter, r *http.Request) (http.He
responseHeader.Set(coopHeader, s.opt.CrossOriginOpenerPolicy)
}

// Cross Origin Resource Policy header.
if len(s.opt.CrossOriginResourcePolicy) > 0 {
responseHeader.Set(corpHeader, s.opt.CrossOriginResourcePolicy)
}

// Cross-Origin-Embedder-Policy header.
if len(s.opt.CrossOriginEmbedderPolicy) > 0 {
responseHeader.Set(coepHeader, s.opt.CrossOriginEmbedderPolicy)
}

// X-DNS-Prefetch-Control header.
switch strings.ToLower(s.opt.XDNSPrefetchControl) {
case "on":
responseHeader.Set(dnsPreFetchControlHeader, "on")
case "off":
responseHeader.Set(dnsPreFetchControlHeader, "off")
}

// X-Permitted-Cross-Domain-Policies header.
if len(s.opt.XPermittedCrossDomainPolicies) > 0 {
responseHeader.Set(permittedCrossDomainPolicies, s.opt.XPermittedCrossDomainPolicies)
}

return responseHeader, r, nil
}

Expand Down
68 changes: 68 additions & 0 deletions secure_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1046,6 +1046,74 @@ func TestCrossOriginOpenerPolicy(t *testing.T) {
expect(t, res.Header().Get("Cross-Origin-Opener-Policy"), "same-origin")
}

func TestCrossOriginEmbedderPolicy(t *testing.T) {
s := New(Options{
CrossOriginEmbedderPolicy: "require-corp",
})

res := httptest.NewRecorder()
req, _ := http.NewRequestWithContext(context.Background(), http.MethodGet, "/foo", nil)

s.Handler(myHandler).ServeHTTP(res, req)

expect(t, res.Code, http.StatusOK)
expect(t, res.Header().Get("Cross-Origin-Embedder-Policy"), "require-corp")
}

func TestCrossOriginResourcePolicy(t *testing.T) {
s := New(Options{
CrossOriginResourcePolicy: "same-origin",
})

res := httptest.NewRecorder()
req, _ := http.NewRequestWithContext(context.Background(), http.MethodGet, "/foo", nil)

s.Handler(myHandler).ServeHTTP(res, req)

expect(t, res.Code, http.StatusOK)
expect(t, res.Header().Get("Cross-Origin-Resource-Policy"), "same-origin")
}

func TestXDNSPreFetchControl(t *testing.T) {
s := New(Options{
XDNSPrefetchControl: "on",
})

res := httptest.NewRecorder()
req, _ := http.NewRequestWithContext(context.Background(), http.MethodGet, "/foo", nil)

s.Handler(myHandler).ServeHTTP(res, req)

expect(t, res.Code, http.StatusOK)
expect(t, res.Header().Get("X-DNS-Prefetch-Control"), "on")

k := New(Options{
XDNSPrefetchControl: "off",
})

res = httptest.NewRecorder()
req, _ = http.NewRequestWithContext(context.Background(), http.MethodGet, "/bar", nil)

k.Handler(myHandler).ServeHTTP(res, req)

expect(t, res.Code, http.StatusOK)
expect(t, res.Header().Get("X-DNS-Prefetch-Control"), "off")
}

func TestXPermittedCrossDomainPolicies(t *testing.T) {
s := New(Options{
XPermittedCrossDomainPolicies: "none",
})

res := httptest.NewRecorder()
req, _ := http.NewRequestWithContext(context.Background(), http.MethodGet, "/foo", nil)

s.Handler(myHandler).ServeHTTP(res, req)

expect(t, res.Code, http.StatusOK)
expect(t, res.Header().Get("X-Permitted-Cross-Domain-Policies"), "none")
}

func TestIsSSL(t *testing.T) {
s := New(Options{
SSLProxyHeaders: map[string]string{"X-Forwarded-Proto": "https"},
Expand Down

0 comments on commit c88f919

Please sign in to comment.