Files
coder/coderd/httpmw/csp_test.go
Asher 82c14e00ce feat: add csp headers for embedded apps (#18374)
I modified the proxy host cache we already had and were using for
websocket csp headers to also include the wildcard app host, then used
those for frame-src policies.

I did not add frame-ancestors, since if I understand correctly, those
would go on the app, and this middleware does not come into play there.
Maybe we will want to add it on workspace apps like we do with cors, if
we find apps are setting it to `none` or something.

Closes https://github.com/coder/internal/issues/684
2025-06-17 09:00:32 -08:00

68 lines
1.8 KiB
Go

package httpmw_test
import (
"net/http"
"net/http/httptest"
"strings"
"testing"
"github.com/stretchr/testify/require"
"github.com/coder/coder/v2/coderd/httpmw"
"github.com/coder/coder/v2/coderd/proxyhealth"
"github.com/coder/coder/v2/codersdk"
)
func TestCSP(t *testing.T) {
t.Parallel()
proxyHosts := []*proxyhealth.ProxyHost{
{
Host: "test.com",
AppHost: "*.test.com",
},
{
Host: "coder.com",
AppHost: "*.coder.com",
},
{
// Host is not added because it duplicates the host header.
Host: "example.com",
AppHost: "*.coder2.com",
},
}
expectedMedia := []string{"media.com", "media2.com"}
expected := []string{
"frame-src 'self' *.test.com *.coder.com *.coder2.com",
"media-src 'self' media.com media2.com",
strings.Join([]string{
"connect-src", "'self'",
// Added from host header.
"wss://example.com", "ws://example.com",
// Added via proxy hosts.
"wss://test.com", "ws://test.com", "https://test.com", "http://test.com",
"wss://coder.com", "ws://coder.com", "https://coder.com", "http://coder.com",
}, " "),
}
// When the host is empty, it uses example.com.
r := httptest.NewRequest(http.MethodGet, "/", nil)
rw := httptest.NewRecorder()
httpmw.CSPHeaders(codersdk.Experiments{
codersdk.ExperimentAITasks,
}, false, func() []*proxyhealth.ProxyHost {
return proxyHosts
}, map[httpmw.CSPFetchDirective][]string{
httpmw.CSPDirectiveMediaSrc: expectedMedia,
})(http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
rw.WriteHeader(http.StatusOK)
})).ServeHTTP(rw, r)
require.NotEmpty(t, rw.Header().Get("Content-Security-Policy"), "Content-Security-Policy header should not be empty")
for _, e := range expected {
require.Contains(t, rw.Header().Get("Content-Security-Policy"), e)
}
}