github.com/google/go-safeweb@v0.0.0-20231219055052-64d8cfc90fbb/safehttp/plugins/coop/coop.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 coop provides Cross-Origin-Opener-Policy protection. Specification: https://html.spec.whatwg.org/#cross-origin-opener-policies
    16  package coop
    17  
    18  import (
    19  	"github.com/google/go-safeweb/safehttp"
    20  )
    21  
    22  var _ safehttp.Interceptor = Interceptor{}
    23  
    24  // Mode represents a COOP mode.
    25  type Mode string
    26  
    27  const (
    28  	// SameOrigin is the strictest and safest COOP available: windows can keep a reference of windows they open only if they are same-origin.
    29  	SameOrigin Mode = "same-origin"
    30  	// SameOriginAllowPopups relaxes the same-origin COOP: windows on this origin that open other windows are allowed to keep a reference, but the opposite is not valid.
    31  	SameOriginAllowPopups Mode = "same-origin-allow-popups"
    32  	// UnsafeNone disables COOP: this is the default value in browsers.
    33  	UnsafeNone Mode = "unsafe-none"
    34  )
    35  
    36  // Policy represents a Cross-Origin-Opener-Policy value.
    37  type Policy struct {
    38  	// Mode is the mode for the policy.
    39  	Mode Mode
    40  	// ReportingGroup is an optional reporting group that needs to be defined with the Reporting API.
    41  	ReportingGroup string
    42  	// ReportOnly makes the policy report-only if set.
    43  	ReportOnly bool
    44  }
    45  
    46  // String serializes the policy. The returned value can be used as a header value.
    47  func (p Policy) String() string {
    48  	if p.ReportingGroup == "" {
    49  		return string(p.Mode)
    50  	}
    51  	return string(p.Mode) + `; report-to "` + p.ReportingGroup + `"`
    52  }
    53  
    54  type serializedPolicies struct {
    55  	rep []string
    56  	enf []string
    57  }
    58  
    59  func serializePolicies(policies ...Policy) serializedPolicies {
    60  	var s serializedPolicies
    61  	for _, p := range policies {
    62  		if p.ReportOnly {
    63  			s.rep = append(s.rep, p.String())
    64  		} else {
    65  			s.enf = append(s.enf, p.String())
    66  		}
    67  	}
    68  	return s
    69  }
    70  
    71  // NewInterceptor constructs an interceptor that applies the given policies.
    72  func NewInterceptor(policies ...Policy) Interceptor {
    73  	return Interceptor(serializePolicies(policies...))
    74  }
    75  
    76  // Default returns a same-origin enforcing interceptor with the given (potentially empty) report group.
    77  func Default(reportGroup string) Interceptor {
    78  	return NewInterceptor(Policy{Mode: SameOrigin, ReportingGroup: reportGroup})
    79  }
    80  
    81  // Interceptor is the interceptor for COOP.
    82  type Interceptor serializedPolicies
    83  
    84  // Before claims and sets the Report-Only and Enforcement headers for COOP.
    85  func (it Interceptor) Before(w safehttp.ResponseWriter, r *safehttp.IncomingRequest, cfg safehttp.InterceptorConfig) safehttp.Result {
    86  	if cfg != nil {
    87  		// We got an override, run its Before phase instead.
    88  		return Interceptor(cfg.(Overrider)).Before(w, r, nil)
    89  	}
    90  	w.Header().Claim("Cross-Origin-Opener-Policy")(it.enf)
    91  	w.Header().Claim("Cross-Origin-Opener-Policy-Report-Only")(it.rep)
    92  	return safehttp.NotWritten()
    93  }
    94  
    95  // Commit is a no-op, required to satisfy the safehttp.Interceptor interface.
    96  func (it Interceptor) Commit(w safehttp.ResponseHeadersWriter, r *safehttp.IncomingRequest, resp safehttp.Response, _ safehttp.InterceptorConfig) {
    97  }
    98  
    99  // Match recognizes Overriders as COOP configurations.
   100  func (it Interceptor) Match(cfg safehttp.InterceptorConfig) bool {
   101  	_, ok := cfg.(Overrider)
   102  	return ok
   103  }
   104  
   105  // Overrider is a safehttp.InterceptorConfig that allows to override COOP for a specific handler.
   106  type Overrider serializedPolicies
   107  
   108  // Override creates an Overrider with the given policies.
   109  func Override(reason string, policies ...Policy) Overrider {
   110  	return Overrider(serializePolicies(policies...))
   111  }