Skip to content

Commit a409a34

Browse files
authored
fix: Open csp-images to allow external (#1835)
External images are required for the README parts of templates. Only allowing https right now
1 parent 7a5c873 commit a409a34

File tree

4 files changed

+47
-3
lines changed

4 files changed

+47
-3
lines changed

coderd/coderd.go

+3-1
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,6 @@ func New(options *Options) *API {
101101
Message: "Route not found.",
102102
})
103103
})
104-
105104
r.Use(
106105
// Specific routes can specify smaller limits.
107106
httpmw.RateLimitPerMinute(options.APIRateLimit),
@@ -112,6 +111,9 @@ func New(options *Options) *API {
112111
Message: "👋",
113112
})
114113
})
114+
// All CSP errors will be logged
115+
r.Post("/csp/reports", api.logReportCSPViolations)
116+
115117
r.Route("/buildinfo", func(r chi.Router) {
116118
r.Get("/", func(rw http.ResponseWriter, r *http.Request) {
117119
httpapi.Write(rw, http.StatusOK, codersdk.BuildInfoResponse{

coderd/coderd_test.go

+1
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,7 @@ func TestAuthorizeAllEndpoints(t *testing.T) {
119119
"POST:/api/v2/users/login": {NoAuthorize: true},
120120
"POST:/api/v2/users/logout": {NoAuthorize: true},
121121
"GET:/api/v2/users/authmethods": {NoAuthorize: true},
122+
"POST:/api/v2/csp/reports": {NoAuthorize: true},
122123

123124
// Has it's own auth
124125
"GET:/api/v2/users/oauth2/github/callback": {NoAuthorize: true},

coderd/csp.go

+38
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package coderd
2+
3+
import (
4+
"encoding/json"
5+
"net/http"
6+
7+
"github.com/coder/coder/coderd/httpapi"
8+
9+
"cdr.dev/slog"
10+
)
11+
12+
type cspViolation struct {
13+
Report map[string]interface{} `json:"csp-report"`
14+
}
15+
16+
// logReportCSPViolations will log all reported csp violations.
17+
func (api *API) logReportCSPViolations(rw http.ResponseWriter, r *http.Request) {
18+
ctx := r.Context()
19+
var v cspViolation
20+
21+
dec := json.NewDecoder(r.Body)
22+
err := dec.Decode(&v)
23+
if err != nil {
24+
api.Logger.Warn(ctx, "csp violation", slog.Error(err))
25+
httpapi.Write(rw, http.StatusBadRequest, httpapi.Response{
26+
Message: "failed to read body",
27+
})
28+
return
29+
}
30+
31+
fields := make([]slog.Field, 0, len(v.Report))
32+
for k, v := range v.Report {
33+
fields = append(fields, slog.F(k, v))
34+
}
35+
api.Logger.Warn(ctx, "csp violation", fields...)
36+
37+
httpapi.Write(rw, http.StatusOK, "ok")
38+
}

site/embed.go

+5-2
Original file line numberDiff line numberDiff line change
@@ -261,11 +261,14 @@ func secureHeaders(next http.Handler) http.Handler {
261261
CSPDirectiveManifestSrc: {"'self' blob:"},
262262
CSPDirectiveFrameSrc: {"'self'"},
263263
// data: for loading base64 encoded icons for generic applications.
264-
CSPDirectiveImgSrc: {"'self' https://cdn.coder.com data:"},
264+
// https: allows loading images from external sources. This is not ideal
265+
// but is required for the templates page that renders readmes.
266+
// We should find a better solution in the future.
267+
CSPDirectiveImgSrc: {"'self' https: https://cdn.coder.com data:"},
265268
CSPDirectiveFormAction: {"'self'"},
266269
CSPDirectiveMediaSrc: {"'self'"},
267270
// Report all violations back to the server to log
268-
CSPDirectiveReportURI: {"/api/private/csp/reports"},
271+
CSPDirectiveReportURI: {"/api/v2/csp/reports"},
269272
CSPFrameAncestors: {"'none'"},
270273

271274
// Only scripts can manipulate the dom. This prevents someone from

0 commit comments

Comments
 (0)