github.com/google/go-safeweb@v0.0.0-20231219055052-64d8cfc90fbb/safehttp/plugins/fetchmetadata/resource.go (about)

     1  // Copyright 2022 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 fetchmetadata
    16  
    17  import (
    18  	"log"
    19  
    20  	"github.com/google/go-safeweb/safehttp"
    21  	"github.com/google/go-safeweb/safehttp/plugins/fetchmetadata/internalunsafefetchmetadata"
    22  )
    23  
    24  var (
    25  	navigationalDests = map[string]bool{
    26  		"document":        true,
    27  		"nested-document": true,
    28  	}
    29  	statePreservingMethods = map[string]bool{
    30  		safehttp.MethodGet:  true,
    31  		safehttp.MethodHead: true,
    32  	}
    33  )
    34  
    35  // ResourceIsolationPolicy protects resources.
    36  //
    37  // See https://web.dev/fetch-metadata/ for more details.
    38  func ResourceIsolationPolicy() *Policy {
    39  	return &Policy{
    40  		isAllowed: func(r *safehttp.IncomingRequest) bool {
    41  			h := r.Header
    42  			if h.Get("Sec-Fetch-Site") != "cross-site" {
    43  				// The request is allowed to pass because one of the following applies:
    44  				// - Fetch Metadata is not supported by the browser
    45  				// - the request is same-origin, same-site or caused by the user
    46  				// explicitly interacting with the user-agent
    47  				return true
    48  			}
    49  
    50  			method := r.Method()
    51  			mode := h.Get("Sec-Fetch-Mode")
    52  			dest := h.Get("Sec-Fetch-Dest")
    53  			// Allow CORS options requests if neither Mode nor Dest is set.
    54  			// https://github.com/w3c/webappsec-fetch-metadata/issues/35
    55  			// https://bugs.chromium.org/p/chromium/issues/detail?id=979946
    56  			if mode == "" && dest == "" && method == safehttp.MethodOptions {
    57  				return true
    58  			}
    59  
    60  			if navigationalModes[mode] && navigationalDests[dest] && statePreservingMethods[method] {
    61  				// The request is cross-site, but a simple top-level navigation from a
    62  				// safe destination so we allow it to pass.
    63  				return true
    64  			}
    65  			if safehttp.IsLocalDev() {
    66  				log.Println("fetchmetadata plugin resource protection detected a potentially malicious request")
    67  			}
    68  			return false
    69  		},
    70  		match: func(cfg safehttp.InterceptorConfig) bool {
    71  			_, ok := cfg.(internalunsafefetchmetadata.DisableResourceIsolationPolicy)
    72  			return ok
    73  		},
    74  		skip: func(cfg safehttp.InterceptorConfig) (skip, skipReports bool) {
    75  			if override, ok := cfg.(internalunsafefetchmetadata.DisableResourceIsolationPolicy); ok {
    76  				return true, override.SkipReports
    77  			}
    78  			return false, false
    79  		},
    80  		signal: "RESOURCE_ISOLATION",
    81  	}
    82  }