github.com/cozy/cozy-stack@v0.0.0-20240603063001-31110fa4cae1/web/middlewares/secure_test.go (about)

     1  package middlewares
     2  
     3  import (
     4  	"net/http"
     5  	"net/http/httptest"
     6  	"testing"
     7  	"time"
     8  
     9  	"github.com/cozy/cozy-stack/model/instance"
    10  	"github.com/cozy/cozy-stack/pkg/assets/dynamic"
    11  	"github.com/cozy/cozy-stack/pkg/config/config"
    12  	"github.com/cozy/cozy-stack/tests/testutils"
    13  	"github.com/labstack/echo/v4"
    14  	"github.com/stretchr/testify/assert"
    15  	"github.com/stretchr/testify/require"
    16  )
    17  
    18  func TestSecure(t *testing.T) {
    19  	if testing.Short() {
    20  		t.Skip("an instance is required for this test: test skipped due to the use of --short flag")
    21  	}
    22  
    23  	config.UseTestFile(t)
    24  	config.GetConfig().Assets = "../../assets"
    25  	setup := testutils.NewSetup(t, t.Name())
    26  
    27  	setup.SetupSwiftTest()
    28  	require.NoError(t, dynamic.InitDynamicAssetFS(config.FsURL().String()), "Could not init dynamic FS")
    29  
    30  	t.Run("SecureMiddlewareHSTS", func(t *testing.T) {
    31  		e := echo.New()
    32  		req, _ := http.NewRequest(echo.GET, "http://app.cozy.local/", nil)
    33  		rec := httptest.NewRecorder()
    34  		c := e.NewContext(req, rec)
    35  		h := Secure(&SecureConfig{
    36  			HSTSMaxAge: 3600 * time.Second,
    37  		})(echo.NotFoundHandler)
    38  		_ = h(c)
    39  		assert.Equal(t, "max-age=3600; includeSubDomains", rec.Header().Get(echo.HeaderStrictTransportSecurity))
    40  	})
    41  
    42  	t.Run("SecureMiddlewareCSP", func(t *testing.T) {
    43  		e1 := echo.New()
    44  		req1, _ := http.NewRequest(echo.GET, "http://app.cozy.local/", nil)
    45  		rec1 := httptest.NewRecorder()
    46  		c1 := e1.NewContext(req1, rec1)
    47  		h1 := Secure(&SecureConfig{
    48  			CSPConnectSrc: nil,
    49  			CSPFrameSrc:   nil,
    50  			CSPScriptSrc:  nil,
    51  		})(echo.NotFoundHandler)
    52  		_ = h1(c1)
    53  
    54  		e2 := echo.New()
    55  		req2, _ := http.NewRequest(echo.GET, "http://app.cozy.local/", nil)
    56  		rec2 := httptest.NewRecorder()
    57  		c2 := e2.NewContext(req2, rec2)
    58  		h2 := Secure(&SecureConfig{
    59  			CSPConnectSrc: nil,
    60  			CSPFrameSrc:   []CSPSource{CSPSrcAny},
    61  			CSPScriptSrc:  []CSPSource{CSPSrcSelf},
    62  		})(echo.NotFoundHandler)
    63  		_ = h2(c2)
    64  
    65  		e3 := echo.New()
    66  		req3, _ := http.NewRequest(echo.GET, "http://app.cozy.local/", nil)
    67  		rec3 := httptest.NewRecorder()
    68  		c3 := e3.NewContext(req3, rec3)
    69  		h3 := Secure(&SecureConfig{
    70  			CSPConnectSrc: []CSPSource{CSPSrcParent, CSPSrcSelf},
    71  			CSPFrameSrc:   []CSPSource{CSPSrcAny},
    72  			CSPScriptSrc:  []CSPSource{CSPSrcSiblings},
    73  		})(echo.NotFoundHandler)
    74  		_ = h3(c3)
    75  
    76  		e4 := echo.New()
    77  		req4, _ := http.NewRequest(echo.GET, "http://app.cozy.local/", nil)
    78  		rec4 := httptest.NewRecorder()
    79  		c4 := e4.NewContext(req4, rec4)
    80  		c4.Set("instance", &instance.Instance{ContextName: "test"})
    81  		h4 := Secure(&SecureConfig{
    82  			CSPConnectSrc: nil,
    83  			CSPFrameSrc:   []CSPSource{CSPSrcAny},
    84  			CSPScriptSrc:  []CSPSource{CSPSrcSelf},
    85  			CSPPerContext: map[string]map[string]string{
    86  				"test": {
    87  					"frame":   "https://example.net",
    88  					"connect": "https://example.com",
    89  				},
    90  			},
    91  		})(echo.NotFoundHandler)
    92  		_ = h4(c4)
    93  
    94  		assert.Equal(t, "", rec1.Header().Get(echo.HeaderContentSecurityPolicy))
    95  		assert.Equal(t, "script-src 'self';frame-src *;", rec2.Header().Get(echo.HeaderContentSecurityPolicy))
    96  		assert.Equal(t, "script-src https://*.cozy.local;frame-src *;connect-src https://cozy.local 'self';", rec3.Header().Get(echo.HeaderContentSecurityPolicy))
    97  		assert.Equal(t, "script-src 'self';frame-src * https://example.net;connect-src https://example.com;", rec4.Header().Get(echo.HeaderContentSecurityPolicy))
    98  	})
    99  
   100  	t.Run("AppendCSPRule", func(t *testing.T) {
   101  		r := appendCSPRule("", "frame-ancestors", "new-rule")
   102  		assert.Equal(t, "frame-ancestors new-rule;", r)
   103  
   104  		r = appendCSPRule("frame-ancestors;", "frame-ancestors", "new-rule")
   105  		assert.Equal(t, "frame-ancestors new-rule;", r)
   106  
   107  		r = appendCSPRule("frame-ancestors 1 2 3 ;", "frame-ancestors", "new-rule")
   108  		assert.Equal(t, "frame-ancestors 1 2 3 new-rule;", r)
   109  
   110  		r = appendCSPRule("frame-ancestors 1 2 3 ;", "frame-ancestors", "new-rule", "new-rule-2")
   111  		assert.Equal(t, "frame-ancestors 1 2 3 new-rule new-rule-2;", r)
   112  
   113  		r = appendCSPRule("frame-ancestors 'none';", "frame-ancestors", "new-rule")
   114  		assert.Equal(t, "frame-ancestors new-rule;", r)
   115  
   116  		r = appendCSPRule("script '*'; frame-ancestors 'self';", "frame-ancestors", "new-rule")
   117  		assert.Equal(t, "script '*'; frame-ancestors 'self' new-rule;", r)
   118  
   119  		r = appendCSPRule("script '*'; frame-ancestors 'self'; plop plop;", "frame-ancestors", "new-rule")
   120  		assert.Equal(t, "script '*'; frame-ancestors 'self' new-rule; plop plop;", r)
   121  
   122  		r = appendCSPRule("script '*'; toto;", "frame-ancestors", "new-rule")
   123  		assert.Equal(t, "script '*'; toto;frame-ancestors new-rule;", r)
   124  	})
   125  }