github.com/google/go-safeweb@v0.0.0-20231219055052-64d8cfc90fbb/safehttp/plugins/fetchmetadata/resource_test.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_test 16 17 import ( 18 "net/http" 19 "testing" 20 21 "github.com/google/go-cmp/cmp" 22 "github.com/google/go-safeweb/safehttp/plugins/fetchmetadata" 23 "github.com/google/go-safeweb/safehttp/plugins/fetchmetadata/internalunsafefetchmetadata/unsafefetchmetadatafortests" 24 25 "github.com/google/go-safeweb/safehttp" 26 "github.com/google/go-safeweb/safehttp/safehttptest" 27 ) 28 29 var ( 30 allowedRIPHeaders = []testHeaders{ 31 { 32 name: "Fetch Metadata not supported", 33 method: safehttp.MethodGet, 34 site: "", 35 }, 36 { 37 name: "same origin", 38 method: safehttp.MethodGet, 39 site: "same-origin", 40 }, 41 { 42 name: "same site", 43 method: safehttp.MethodGet, 44 site: "same-site", 45 }, 46 { 47 name: "user agent initiated", 48 method: safehttp.MethodGet, 49 site: "none", 50 }, 51 { 52 name: "cors bug missing mode", 53 method: safehttp.MethodOptions, 54 site: "cross-site", 55 mode: "", 56 }, 57 } 58 59 allowedRIPNavHeaders = []testHeaders{ 60 { 61 name: "cross origin GET navigate from document", 62 method: safehttp.MethodGet, 63 site: "cross-site", 64 mode: "navigate", 65 dest: "document", 66 }, 67 { 68 name: "cross origin HEAD navigate from document", 69 method: safehttp.MethodHead, 70 site: "cross-site", 71 mode: "navigate", 72 dest: "document", 73 }, 74 { 75 name: "cross origin GET navigate from nested-document", 76 method: safehttp.MethodGet, 77 site: "cross-site", 78 mode: "navigate", 79 dest: "nested-document", 80 }, 81 { 82 name: "cross origin HEAD navigate from nested-document", 83 method: safehttp.MethodHead, 84 site: "cross-site", 85 mode: "navigate", 86 dest: "nested-document", 87 }, 88 { 89 name: "cross origin GET nested-navigate from document", 90 method: safehttp.MethodGet, 91 site: "cross-site", 92 mode: "nested-navigate", 93 dest: "document", 94 }, 95 { 96 name: "cross origin HEAD nested-navigate from document", 97 method: safehttp.MethodHead, 98 site: "cross-site", 99 mode: "nested-navigate", 100 dest: "document", 101 }, 102 { 103 name: "cross origin GET nested-navigate from nested-document", 104 method: safehttp.MethodGet, 105 site: "cross-site", 106 mode: "nested-navigate", 107 dest: "nested-document", 108 }, 109 { 110 name: "cross origin HEAD nested-navigate from nested-document", 111 method: safehttp.MethodHead, 112 site: "cross-site", 113 mode: "nested-navigate", 114 dest: "nested-document", 115 }, 116 } 117 118 disallowedRIPNavHeaders = []testHeaders{ 119 { 120 name: "cross origin POST", 121 method: safehttp.MethodPost, 122 site: "cross-site", 123 mode: "navigate", 124 dest: "document", 125 }, 126 { 127 name: "cross origin GET from object", 128 method: safehttp.MethodGet, 129 site: "cross-site", 130 mode: "navigate", 131 dest: "object", 132 }, 133 { 134 name: "cross origin HEAD from embed", 135 method: safehttp.MethodHead, 136 site: "cross-site", 137 mode: "navigate", 138 dest: "embed", 139 }, 140 } 141 142 disallowedRIPHeaders = []testHeaders{ 143 { 144 name: "cross origin no cors", 145 method: safehttp.MethodPost, 146 site: "cross-site", 147 mode: "cors", 148 dest: "document", 149 }, 150 { 151 name: "cross origin no cors", 152 method: safehttp.MethodPost, 153 site: "cross-site", 154 mode: "no-cors", 155 dest: "nested-document", 156 }, 157 } 158 ) 159 160 func TestAllowedResourceIsolationEnforceMode(t *testing.T) { 161 tests := append(allowedRIPHeaders, allowedRIPNavHeaders...) 162 for _, test := range tests { 163 t.Run(test.name, func(t *testing.T) { 164 req := safehttptest.NewRequest(test.method, "https://spaghetti.com/carbonara", nil) 165 req.Header.Add("Sec-Fetch-Site", test.site) 166 req.Header.Add("Sec-Fetch-Mode", test.mode) 167 req.Header.Add("Sec-Fetch-Dest", test.dest) 168 fakeRW, rr := safehttptest.NewFakeResponseWriter() 169 170 p := fetchmetadata.ResourceIsolationPolicy() 171 p.Before(fakeRW, req, nil) 172 173 if want, got := int(safehttp.StatusOK), rr.Code; got != want { 174 t.Errorf("rr.Code got: %v want: %v", got, want) 175 } 176 if diff := cmp.Diff(http.Header{}, rr.Header()); diff != "" { 177 t.Errorf("rr.Header() mismatch (-want +got):\n%s", diff) 178 } 179 if want, got := "", rr.Body.String(); got != want { 180 t.Errorf("rr.Body.String() got: %q want: %q", got, want) 181 } 182 }) 183 } 184 } 185 186 func TestRejectedResourceIsolationEnforceMode(t *testing.T) { 187 tests := append(disallowedRIPHeaders, disallowedRIPNavHeaders...) 188 for _, test := range tests { 189 t.Run(test.name, func(t *testing.T) { 190 req := safehttptest.NewRequest(test.method, "https://spaghetti.com/carbonara", nil) 191 req.Header.Add("Sec-Fetch-Site", test.site) 192 req.Header.Add("Sec-Fetch-Mode", test.mode) 193 req.Header.Add("Sec-Fetch-Dest", test.dest) 194 fakeRW, rr := safehttptest.NewFakeResponseWriter() 195 196 p := fetchmetadata.ResourceIsolationPolicy() 197 p.Before(fakeRW, req, nil) 198 199 if want, got := safehttp.StatusForbidden, safehttp.StatusCode(rr.Code); want != got { 200 t.Errorf("rr.Code got: %v want: %v", got, want) 201 } 202 if diff := cmp.Diff(http.Header{}, rr.Header()); diff != "" { 203 t.Errorf("rr.Header() mismatch (-want +got):\n%s", diff) 204 } 205 }) 206 } 207 } 208 209 func TestDisableResourceIsolationPolicy(t *testing.T) { 210 type reportTests struct { 211 name, method, site, mode, dest, blocked string 212 } 213 var tests []reportTests 214 for _, t := range append(allowedRIPNavHeaders, allowedRIPHeaders...) { 215 tests = append(tests, reportTests{ 216 name: t.name, 217 method: t.method, 218 site: t.site, 219 mode: t.mode, 220 dest: t.dest, 221 blocked: "false", 222 }) 223 } 224 for _, t := range append(disallowedRIPNavHeaders, disallowedRIPHeaders...) { 225 tests = append(tests, reportTests{ 226 name: t.name, 227 method: t.method, 228 site: t.site, 229 mode: t.mode, 230 dest: t.dest, 231 blocked: "true", 232 }) 233 } 234 for _, test := range tests { 235 t.Run(test.name, func(t *testing.T) { 236 req := safehttptest.NewRequest(test.method, "https://spaghetti.com/carbonara", nil) 237 req.Header.Add("Sec-Fetch-Site", test.site) 238 req.Header.Add("Sec-Fetch-Mode", test.mode) 239 req.Header.Add("Sec-Fetch-Dest", test.dest) 240 fakeRW, rr := safehttptest.NewFakeResponseWriter() 241 242 p := fetchmetadata.ResourceIsolationPolicy() 243 p.Before(fakeRW, req, unsafefetchmetadatafortests.DisableResourceIsolationPolicy()) 244 245 if want, got := safehttp.StatusOK, safehttp.StatusCode(rr.Code); want != got { 246 t.Errorf("rr.Code got: %v want: %v", got, want) 247 } 248 if diff := cmp.Diff(http.Header{}, rr.Header()); diff != "" { 249 t.Errorf("rr.Header() mismatch (-want +got):\n%s", diff) 250 } 251 if want, got := "", rr.Body.String(); got != want { 252 t.Errorf("rr.Body.String() got: %q want: %q", got, want) 253 } 254 }) 255 } 256 }