diff --git a/README.md b/README.md index 2d91f6a..9299b4d 100644 --- a/README.md +++ b/README.md @@ -222,8 +222,9 @@ container. starts once set-up is done; if it cannot listen, the bot exits with an error, as when the chat client fails. Every request must carry the credential, compared in constant time; every response carries headers - that forbid framing, content sniffing, caching and referrers; a - request body is capped at 64 KiB and a request's work at 10 seconds. + that forbid framing, content sniffing, caching and referrers, and a + `Permissions-Policy` that denies the camera, microphone and location; + a request body is capped at 64 KiB and a request's work at 10 seconds. Handlers call the chat client on the request's own goroutine, never on the one that delivers events, which also delivers the chat client's answers. When the bot stops, requests in progress get 5 seconds to diff --git a/internal/api/api.go b/internal/api/api.go index bbf0aad..3fbd21c 100644 --- a/internal/api/api.go +++ b/internal/api/api.go @@ -151,7 +151,8 @@ func (h *handlers) respondError(w http.ResponseWriter, status int, sentence stri // securityHeaders go on every response. The API returns JSON to // programs, so a browser may not frame, sniff, cache or refer from it, -// and must reach it over HTTPS. +// nor give it the camera, microphone or location, and must reach it +// over HTTPS. func securityHeaders(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { header := w.Header() @@ -160,6 +161,8 @@ func securityHeaders(next http.Handler) http.Handler { "default-src 'none'; frame-ancestors 'none'") header.Set("X-Frame-Options", "DENY") header.Set("Referrer-Policy", "no-referrer") + header.Set("Permissions-Policy", + "camera=(), microphone=(), geolocation=()") header.Set("Strict-Transport-Security", "max-age=31536000; includeSubDomains") header.Set("Cache-Control", "no-store") diff --git a/internal/api/api_test.go b/internal/api/api_test.go index 5056e81..be3a2da 100644 --- a/internal/api/api_test.go +++ b/internal/api/api_test.go @@ -185,6 +185,7 @@ func TestHeaders(t *testing.T) { "Content-Security-Policy": "default-src 'none'; frame-ancestors 'none'", "X-Frame-Options": "DENY", "Referrer-Policy": "no-referrer", + "Permissions-Policy": "camera=(), microphone=(), geolocation=()", "Strict-Transport-Security": "max-age=31536000; includeSubDomains", "Cache-Control": "no-store", }