2022-05-03 23:46:09 +00:00
|
|
|
// Copyright 2020-2022 the Pinniped contributors. All Rights Reserved.
|
2020-11-20 01:57:07 +00:00
|
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
|
|
|
|
package testutil
|
|
|
|
|
|
|
|
import (
|
2020-12-04 23:40:17 +00:00
|
|
|
"context"
|
2020-12-04 15:06:55 +00:00
|
|
|
"mime"
|
2020-12-14 23:28:32 +00:00
|
|
|
"net/http/httptest"
|
2020-11-20 01:57:07 +00:00
|
|
|
"testing"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
"github.com/stretchr/testify/require"
|
2020-12-04 23:40:17 +00:00
|
|
|
v12 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
|
|
"k8s.io/apimachinery/pkg/labels"
|
2022-07-20 20:55:56 +00:00
|
|
|
"k8s.io/apimachinery/pkg/selection"
|
2020-12-04 23:40:17 +00:00
|
|
|
v1 "k8s.io/client-go/kubernetes/typed/core/v1"
|
2020-11-20 01:57:07 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
func RequireTimeInDelta(t *testing.T, t1 time.Time, t2 time.Time, delta time.Duration) {
|
|
|
|
require.InDeltaf(t,
|
|
|
|
float64(t1.UnixNano()),
|
|
|
|
float64(t2.UnixNano()),
|
|
|
|
float64(delta.Nanoseconds()),
|
|
|
|
"expected %s and %s to be < %s apart, but they are %s apart",
|
|
|
|
t1.Format(time.RFC3339Nano),
|
|
|
|
t2.Format(time.RFC3339Nano),
|
|
|
|
delta.String(),
|
|
|
|
t1.Sub(t2).String(),
|
|
|
|
)
|
|
|
|
}
|
2020-12-04 15:06:55 +00:00
|
|
|
|
|
|
|
func RequireEqualContentType(t *testing.T, actual string, expected string) {
|
|
|
|
t.Helper()
|
|
|
|
|
|
|
|
if expected == "" {
|
|
|
|
require.Empty(t, actual)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
actualContentType, actualContentTypeParams, err := mime.ParseMediaType(expected)
|
|
|
|
require.NoError(t, err)
|
|
|
|
expectedContentType, expectedContentTypeParams, err := mime.ParseMediaType(expected)
|
|
|
|
require.NoError(t, err)
|
|
|
|
require.Equal(t, actualContentType, expectedContentType)
|
|
|
|
require.Equal(t, actualContentTypeParams, expectedContentTypeParams)
|
|
|
|
}
|
2020-12-04 23:40:17 +00:00
|
|
|
|
|
|
|
func RequireNumberOfSecretsMatchingLabelSelector(t *testing.T, secrets v1.SecretInterface, labelSet labels.Set, expectedNumberOfSecrets int) {
|
|
|
|
t.Helper()
|
|
|
|
storedAuthcodeSecrets, err := secrets.List(context.Background(), v12.ListOptions{
|
|
|
|
LabelSelector: labelSet.String(),
|
|
|
|
})
|
|
|
|
require.NoError(t, err)
|
|
|
|
require.Len(t, storedAuthcodeSecrets.Items, expectedNumberOfSecrets)
|
|
|
|
}
|
2020-12-14 23:28:32 +00:00
|
|
|
|
2022-07-20 20:55:56 +00:00
|
|
|
func RequireNumberOfSecretsExcludingLabelSelector(t *testing.T, secrets v1.SecretInterface, labelSet labels.Set, expectedNumberOfSecrets int) {
|
|
|
|
t.Helper()
|
|
|
|
|
|
|
|
selector := labels.Everything()
|
|
|
|
for k, v := range labelSet {
|
|
|
|
requirement, err := labels.NewRequirement(k, selection.NotEquals, []string{v})
|
|
|
|
require.NoError(t, err)
|
|
|
|
selector = selector.Add(*requirement)
|
|
|
|
}
|
|
|
|
|
|
|
|
storedAuthcodeSecrets, err := secrets.List(context.Background(), v12.ListOptions{
|
|
|
|
LabelSelector: selector.String(),
|
|
|
|
})
|
|
|
|
require.NoError(t, err)
|
|
|
|
require.Len(t, storedAuthcodeSecrets.Items, expectedNumberOfSecrets)
|
|
|
|
}
|
|
|
|
|
2022-05-05 20:12:06 +00:00
|
|
|
func RequireSecurityHeadersWithFormPostPageCSPs(t *testing.T, response *httptest.ResponseRecorder) {
|
2022-05-03 23:46:09 +00:00
|
|
|
// Loosely confirm that the unique CSPs needed for the form_post page were used.
|
|
|
|
cspHeader := response.Header().Get("Content-Security-Policy")
|
|
|
|
require.Contains(t, cspHeader, "script-src '") // loose assertion
|
|
|
|
require.Contains(t, cspHeader, "style-src '") // loose assertion
|
|
|
|
require.Contains(t, cspHeader, "img-src data:")
|
|
|
|
require.Contains(t, cspHeader, "connect-src *")
|
|
|
|
|
|
|
|
// Also require all the usual security headers.
|
|
|
|
requireSecurityHeaders(t, response)
|
|
|
|
}
|
|
|
|
|
2022-05-05 20:12:06 +00:00
|
|
|
func RequireSecurityHeadersWithLoginPageCSPs(t *testing.T, response *httptest.ResponseRecorder) {
|
|
|
|
// Loosely confirm that the unique CSPs needed for the login page were used.
|
|
|
|
cspHeader := response.Header().Get("Content-Security-Policy")
|
|
|
|
require.Contains(t, cspHeader, "style-src '") // loose assertion
|
|
|
|
require.NotContains(t, cspHeader, "script-src") // only needed by form_post page
|
|
|
|
require.NotContains(t, cspHeader, "img-src data:") // only needed by form_post page
|
|
|
|
require.NotContains(t, cspHeader, "connect-src *") // only needed by form_post page
|
|
|
|
|
|
|
|
// Also require all the usual security headers.
|
|
|
|
requireSecurityHeaders(t, response)
|
|
|
|
}
|
|
|
|
|
|
|
|
func RequireSecurityHeadersWithoutCustomCSPs(t *testing.T, response *httptest.ResponseRecorder) {
|
|
|
|
// Confirm that the unique CSPs needed for the form_post or login page were NOT used.
|
2022-05-03 23:46:09 +00:00
|
|
|
cspHeader := response.Header().Get("Content-Security-Policy")
|
|
|
|
require.NotContains(t, cspHeader, "script-src")
|
|
|
|
require.NotContains(t, cspHeader, "style-src")
|
|
|
|
require.NotContains(t, cspHeader, "img-src data:")
|
|
|
|
require.NotContains(t, cspHeader, "connect-src *")
|
|
|
|
|
|
|
|
// Also require all the usual security headers.
|
|
|
|
requireSecurityHeaders(t, response)
|
|
|
|
}
|
|
|
|
|
|
|
|
func requireSecurityHeaders(t *testing.T, response *httptest.ResponseRecorder) {
|
2022-05-05 20:12:06 +00:00
|
|
|
// Loosely confirm that the generic default CSPs were used.
|
2022-05-03 23:46:09 +00:00
|
|
|
cspHeader := response.Header().Get("Content-Security-Policy")
|
|
|
|
require.Contains(t, cspHeader, "default-src 'none'")
|
|
|
|
require.Contains(t, cspHeader, "frame-ancestors 'none'")
|
2021-06-30 16:50:01 +00:00
|
|
|
|
2020-12-14 23:28:32 +00:00
|
|
|
require.Equal(t, "DENY", response.Header().Get("X-Frame-Options"))
|
|
|
|
require.Equal(t, "1; mode=block", response.Header().Get("X-XSS-Protection"))
|
|
|
|
require.Equal(t, "nosniff", response.Header().Get("X-Content-Type-Options"))
|
|
|
|
require.Equal(t, "no-referrer", response.Header().Get("Referrer-Policy"))
|
|
|
|
require.Equal(t, "off", response.Header().Get("X-DNS-Prefetch-Control"))
|
|
|
|
require.Equal(t, "no-cache", response.Header().Get("Pragma"))
|
|
|
|
require.Equal(t, "0", response.Header().Get("Expires"))
|
2020-12-16 19:15:38 +00:00
|
|
|
|
|
|
|
// This check is more relaxed since Fosite can override the base header we set.
|
|
|
|
require.Contains(t, response.Header().Get("Cache-Control"), "no-store")
|
2020-12-14 23:28:32 +00:00
|
|
|
}
|