github.com/google/go-safeweb@v0.0.0-20231219055052-64d8cfc90fbb/safehttp/header_test.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 safehttp 16 17 import ( 18 "net/http" 19 "testing" 20 21 "github.com/google/go-cmp/cmp" 22 ) 23 24 func TestSet(t *testing.T) { 25 h := NewHeader(http.Header{}) 26 h.Set("Foo-Key", "Bar-Value") 27 if got, want := h.Get("Foo-Key"), "Bar-Value"; got != want { 28 t.Errorf(`h.Get("Foo-Key") got: %q want %q`, got, want) 29 } 30 } 31 32 // TestSetCanonicalization verifies that names of headers 33 // are canonicalized before being interpreted as header 34 // names. 35 // Note that the casing of the header name is different 36 // when accessing and modifying the same header. 37 func TestSetCanonicalization(t *testing.T) { 38 h := NewHeader(http.Header{}) 39 h.Set("fOo-KeY", "Bar-Value") 40 if got, want := h.Get("FoO-kEy"), "Bar-Value"; got != want { 41 t.Errorf(`h.Get("FoO-kEy") got: %q want %q`, got, want) 42 } 43 } 44 45 func TestSetEmptySetCookie(t *testing.T) { 46 h := NewHeader(http.Header{}) 47 defer func() { 48 if r := recover(); r != nil { 49 return 50 } 51 t.Errorf(`h.Set("Set-Cookie", "x=y") expected panic`) 52 }() 53 h.Set("Set-Cookie", "x=y") 54 if diff := cmp.Diff([]string{}, h.Values("Set-Cookie")); diff != "" { 55 t.Errorf("h.Values(\"Set-Cookie\") mismatch (-want +got):\n%s", diff) 56 } 57 } 58 59 func TestSetClaimed(t *testing.T) { 60 h := NewHeader(http.Header{}) 61 h.Set("Foo-Key", "Pizza-Value") 62 h.Claim("Foo-Key") 63 defer func() { 64 if r := recover(); r != nil { 65 return 66 } 67 t.Errorf(`h.Set("Foo-Key", "Bar-Value") expected panic`) 68 }() 69 h.Set("Foo-Key", "Bar-Value") 70 if diff := cmp.Diff([]string{"Pizza-Value"}, h.Values("Foo-Key")); diff != "" { 71 t.Errorf("h.Values(\"Foo-Key\") mismatch (-want +got):\n%s", diff) 72 } 73 } 74 75 func TestSetEmptyClaimed(t *testing.T) { 76 h := NewHeader(http.Header{}) 77 h.Claim("Foo-Key") 78 defer func() { 79 if r := recover(); r != nil { 80 return 81 } 82 t.Errorf(`h.Set("Foo-Key", "Bar-Value") expected panic`) 83 }() 84 h.Set("Foo-Key", "Bar-Value") 85 if diff := cmp.Diff([]string{}, h.Values("Foo-Key")); diff != "" { 86 t.Errorf("h.Values(\"Foo-Key\") mismatch (-want +got):\n%s", diff) 87 } 88 } 89 90 func TestAdd(t *testing.T) { 91 h := NewHeader(http.Header{}) 92 h.Add("Foo-Key", "Bar-Value") 93 h.Add("Foo-Key", "Pizza-Value") 94 if diff := cmp.Diff([]string{"Bar-Value", "Pizza-Value"}, h.Values("Foo-Key")); diff != "" { 95 t.Errorf("h.Values(\"Foo-Key\") mismatch (-want +got):\n%s", diff) 96 } 97 } 98 99 // TestAddCanonicalization verifies that names of headers 100 // are canonicalized before being interpreted as header 101 // names. 102 // Note that the casing of the header name is different 103 // when accessing and modifying the same header. 104 func TestAddCanonicalization(t *testing.T) { 105 h := NewHeader(http.Header{}) 106 h.Add("fOo-KeY", "Bar-Value") 107 h.Add("FoO-kEy", "Pizza-Value") 108 if diff := cmp.Diff([]string{"Bar-Value", "Pizza-Value"}, h.Values("fOO-KEY")); diff != "" { 109 t.Errorf("h.Values(\"fOO-KEY\")) mismatch (-want +got):\n%s", diff) 110 } 111 } 112 113 func TestAddEmptySetCookie(t *testing.T) { 114 h := NewHeader(http.Header{}) 115 defer func() { 116 if r := recover(); r != nil { 117 return 118 } 119 t.Errorf(`h.Add("Set-Cookie", "x=y") expected panic`) 120 }() 121 h.Add("Set-Cookie", "x=y") 122 if diff := cmp.Diff([]string{}, h.Values("Set-Cookie")); diff != "" { 123 t.Errorf("h.Values(\"Set-Cookie\") mismatch (-want +got):\n%s", diff) 124 } 125 } 126 127 func TestAddClaimed(t *testing.T) { 128 h := NewHeader(http.Header{}) 129 h.Add("Foo-Key", "Bar-Value") 130 h.Claim("Foo-Key") 131 defer func() { 132 if r := recover(); r != nil { 133 return 134 } 135 t.Errorf(`h.Add("Foo-Key", "Pizza-Value") expected panic`) 136 }() 137 h.Add("Foo-Key", "Pizza-Value") 138 if diff := cmp.Diff([]string{"Bar-Value"}, h.Values("Foo-Key")); diff != "" { 139 t.Errorf("h.Values(\"Foo-Key\") mismatch (-want +got):\n%s", diff) 140 } 141 } 142 143 func TestAddEmptyClaimed(t *testing.T) { 144 h := NewHeader(http.Header{}) 145 h.Claim("Foo-Key") 146 defer func() { 147 if r := recover(); r != nil { 148 return 149 } 150 t.Errorf(`h.Add("Foo-Key", "Pizza-Value") expected panic`) 151 }() 152 h.Add("Foo-Key", "Pizza-Value") 153 if diff := cmp.Diff([]string{}, h.Values("Foo-Key")); diff != "" { 154 t.Errorf("h.Values(\"Foo-Key\") mismatch (-want +got):\n%s", diff) 155 } 156 } 157 158 func TestDel(t *testing.T) { 159 h := NewHeader(http.Header{}) 160 h.Set("Foo-Key", "Bar-Value") 161 h.Del("Foo-Key") 162 if diff := cmp.Diff([]string{}, h.Values("Foo-Key")); diff != "" { 163 t.Errorf("h.Values(\"Foo-Key\") mismatch (-want +got):\n%s", diff) 164 } 165 } 166 167 // TestDelCanonicalization verifies that names of headers 168 // are canonicalized before being interpreted as header 169 // names. 170 // Note that the casing of the header name is different 171 // when accessing and modifying the same header. 172 func TestDelCanonicalization(t *testing.T) { 173 h := NewHeader(http.Header{}) 174 h.Set("fOo-KeY", "Bar-Value") 175 h.Del("FoO-kEy") 176 if diff := cmp.Diff([]string{}, h.Values("FOO-kEY")); diff != "" { 177 t.Errorf("h.Values(\"FOO-kEY\") mismatch (-want +got):\n%s", diff) 178 } 179 } 180 181 func TestDelEmptySetCookie(t *testing.T) { 182 h := NewHeader(http.Header{}) 183 defer func() { 184 if r := recover(); r != nil { 185 return 186 } 187 t.Errorf(`h.Del("Set-Cookie") expected panic`) 188 }() 189 h.Del("Set-Cookie") 190 if diff := cmp.Diff([]string{}, h.Values("Set-Cookie")); diff != "" { 191 t.Errorf("h.Values(\"Set-Cookie\") mismatch (-want +got):\n%s", diff) 192 } 193 } 194 195 func TestDelClaimed(t *testing.T) { 196 h := NewHeader(http.Header{}) 197 h.Set("Foo-Key", "Bar-Value") 198 h.Claim("Foo-Key") 199 defer func() { 200 if r := recover(); r != nil { 201 return 202 } 203 t.Errorf(`h.Del("Foo-Key") expected panic`) 204 }() 205 h.Del("Foo-Key") 206 if diff := cmp.Diff([]string{"Bar-Value"}, h.Values("Foo-Key")); diff != "" { 207 t.Errorf("h.Values(\"Foo-Key\") mismatch (-want +got):\n%s", diff) 208 } 209 } 210 211 func TestDelEmptyClaimed(t *testing.T) { 212 h := NewHeader(http.Header{}) 213 h.Claim("Foo-Key") 214 defer func() { 215 if r := recover(); r != nil { 216 return 217 } 218 t.Errorf(`h.Del("Foo-Key") expected panic`) 219 }() 220 h.Del("Foo-Key") 221 if diff := cmp.Diff([]string{}, h.Values("Foo-Key")); diff != "" { 222 t.Errorf("h.Values(\"Foo-Key\") mismatch (-want +got):\n%s", diff) 223 } 224 } 225 226 // TestValuesModifyClaimed verifies that modifying the 227 // slice returned by Values() doesn't modify the underlying 228 // slice. The test ensures that Values() returns a copy 229 // of the underlying slice. 230 func TestValuesModifyClaimed(t *testing.T) { 231 h := NewHeader(http.Header{}) 232 h.Set("Foo-Key", "Bar-Value") 233 h.Claim("Foo-Key") 234 v := h.Values("Foo-Key") 235 if diff := cmp.Diff([]string{"Bar-Value"}, v); diff != "" { 236 t.Errorf("h.Values(\"Foo-Key\") mismatch (-want +got):\n%s", diff) 237 } 238 v[0] = "Evil-Value" 239 if got, want := h.Get("Foo-Key"), "Bar-Value"; got != want { 240 t.Errorf(`h.Get("Foo-Key") got: %v want: %v`, got, want) 241 } 242 } 243 244 // TestValuesOrdering ensures that the Values() function 245 // return the headers values in the order that they were 246 // set. 247 func TestValuesOrdering(t *testing.T) { 248 var tests = []struct { 249 name string 250 values []string 251 }{ 252 { 253 name: "Bar Pizza", 254 values: []string{"Bar-Value", "Pizza-Value"}, 255 }, 256 { 257 name: "Pizza Bar", 258 values: []string{"Pizza-Value", "Bar-Value"}, 259 }, 260 } 261 262 for _, tt := range tests { 263 t.Run(tt.name, func(t *testing.T) { 264 h := NewHeader(http.Header{}) 265 h.Add("Foo-Key", tt.values[0]) 266 h.Add("Foo-Key", tt.values[1]) 267 if diff := cmp.Diff(tt.values, h.Values("Foo-Key")); diff != "" { 268 t.Errorf("h.Values(\"Foo-Key\") mismatch (-want +got):\n%s", diff) 269 } 270 }) 271 } 272 } 273 274 func TestManyEqualKeyValuePairs(t *testing.T) { 275 h := NewHeader(http.Header{}) 276 h.Add("Foo-Key", "Bar-Value") 277 h.Add("Foo-Key", "Bar-Value") 278 if diff := cmp.Diff([]string{"Bar-Value", "Bar-Value"}, h.Values("Foo-Key")); diff != "" { 279 t.Errorf("h.Values(\"Foo-Key\") mismatch (-want +got):\n%s", diff) 280 } 281 } 282 283 // TestAddSet tests that calling Set() after calling Add() will 284 // correctly remove the previously added header before setting 285 // the new one. 286 func TestAddSet(t *testing.T) { 287 h := NewHeader(http.Header{}) 288 h.Add("Foo-Key", "Bar-Value") 289 h.Set("Foo-Key", "Pizza-Value") 290 if diff := cmp.Diff([]string{"Pizza-Value"}, h.Values("Foo-Key")); diff != "" { 291 t.Errorf("h.Values(\"Foo-Key\") mismatch (-want +got):\n%s", diff) 292 } 293 } 294 295 func TestValuesEmptyHeader(t *testing.T) { 296 h := NewHeader(http.Header{}) 297 if diff := cmp.Diff([]string{}, h.Values("Foo-Key")); diff != "" { 298 t.Errorf("h.Values(\"Foo-Key\") mismatch (-want +got):\n%s", diff) 299 } 300 } 301 302 func TestClaim(t *testing.T) { 303 h := NewHeader(http.Header{}) 304 set := h.Claim("Foo-Key") 305 set([]string{"Bar-Value", "Pizza-Value"}) 306 if diff := cmp.Diff([]string{"Bar-Value", "Pizza-Value"}, h.Values("Foo-Key")); diff != "" { 307 t.Errorf("h.Values(\"Foo-Key\") mismatch (-want +got):\n%s", diff) 308 } 309 } 310 311 // TestClaimCanonicalization verifies that names of headers 312 // are canonicalized before being interpreted as header 313 // names. 314 // Note that the casing of the header name is different 315 // when accessing and modifying the same header. 316 func TestClaimCanonicalization(t *testing.T) { 317 h := NewHeader(http.Header{}) 318 set := h.Claim("fOO-kEY") 319 set([]string{"Bar-Value", "Pizza-Value"}) 320 if diff := cmp.Diff([]string{"Bar-Value", "Pizza-Value"}, h.Values("fOo-kEy")); diff != "" { 321 t.Errorf("h.Values(\"fOo-kEy\") mismatch (-want +got):\n%s", diff) 322 } 323 } 324 325 func TestClaimSetCookie(t *testing.T) { 326 h := NewHeader(http.Header{}) 327 defer func() { 328 if r := recover(); r != nil { 329 return 330 } 331 t.Errorf(`h.Claim("Set-Cookie") expected panic`) 332 }() 333 h.Claim("Set-Cookie") 334 } 335 336 func TestClaimClaimed(t *testing.T) { 337 h := NewHeader(http.Header{}) 338 h.Claim("Foo-Key") 339 defer func() { 340 if r := recover(); r != nil { 341 return 342 } 343 t.Errorf(`h.Claim("Foo-Key") expected panic`) 344 }() 345 h.Claim("Foo-Key") 346 } 347 348 func TestHeaderIsClaimed(t *testing.T) { 349 h := NewHeader(http.Header{}) 350 h.Claim("Foo-Key") 351 if got := h.IsClaimed("Foo-Key"); got != true { 352 t.Errorf(`h.IsClaimed("Foo-Key") got: %v want: true`, got) 353 } 354 } 355 356 func TestHeaderIsClaimedCanonicalization(t *testing.T) { 357 h := NewHeader(http.Header{}) 358 h.Claim("fOo-KEY") 359 if got := h.IsClaimed("foo-keY"); got != true { 360 t.Errorf(`h.IsClaimed("foo-keY") got: %v want: true`, got) 361 } 362 } 363 364 func TestHeaderIsClaimedNotClaimed(t *testing.T) { 365 h := NewHeader(http.Header{}) 366 if got := h.IsClaimed("Foo-Key"); got != false { 367 t.Errorf(`h.IsClaimed("Foo-Key") got: %v want: true`, got) 368 } 369 } 370 371 func TestHeaderIsClaimedSetCookie(t *testing.T) { 372 h := NewHeader(http.Header{}) 373 if got := h.IsClaimed("Set-Cookie"); got != true { 374 t.Errorf(`h.IsClaimed("Set-Cookie") got: %v want: true`, got) 375 } 376 }