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  }