Secure-by-default HTTP servers in Go.
CTF writeups
Kodsport/sakerhetssm-2020-solutions 1
Lösningar till Säkerhets-SM 2020
Kodsport/sakerhetssm-monthly-solutions 1
Lösningar till Månadens Problem
Python NFC-reader utility library
Kodsport/sakerhetssm-2019-solutions 0
Lösningar till Säkerhets-SM 2019
Implementation of a simple dummy server and a test harness that mocks the network stack. The mini-projects were done in order to gain familiarity with Golang
push eventsakerhetssm/sakerhetssm.github.io
commit sha 791205ecdb68bbbc55cd1e02726da5bfcfab19d9
Added files and hosting for spooktober mini-CTF tasks
push time in 14 hours
startedincompl/csslayoutsite
started time in 14 hours
startedKariaro/AmpleProgrammingLanguage
started time in 15 hours
push eventsakerhetssm/sakerhetssm.github.io
commit sha 879a26aebdeda6dceb98fae72ee7079ef759b5a4
updated ip
push time in 24 days
push eventsakerhetssm/sakerhetssm.github.io
commit sha 3e350573f853b403e4928b1c954d935b99895c6d
fixed jul-2019
push time in 24 days
push eventKodsport/sakerhetssm-monthly-solutions
commit sha c8008af7d36baaa961e3552787db1f75c17e2671
sep-2020 solution
push time in a month
User lookup and information management system
fork in a month
push eventsakerhetssm/sakerhetssm.github.io
commit sha 9e0b0d0337699ad674f5fb95c39df4efd8df70f7
hint2 sep-2020
push time in a month
delete branch google/go-safeweb
delete branch : grenfeldt-fix-interceptor-signatures
delete time in a month
PR opened google/go-safeweb
Fixes #169
Fixed by adding type assertion in each interceptor package like this:
var _ safehttp.Interceptor = Interceptor{}
pr created time in a month
create barnchgoogle/go-safeweb
branch : grenfeldt-fix-interceptor-signatures
created branch time in a month
push eventsakerhetssm/sakerhetssm.github.io
commit sha 29b4e60aa995e2e16c3498389aae27d7a0819f28
hint sep-2020
push time in a month
Pull request review commentgoogle/go-safeweb
Address linter comments in the safehttp package
func TestRequestSetNilContext(t *testing.T) { t.Errorf(`ir.SetContext(nil): expected panic`) } }()-- ir.SetContext(nil)+ var nilContext context.Context+ ir.SetContext(nilContext)
Add a comment explaining why this is done.
comment created time in a month
delete branch google/go-safeweb
delete branch : maramihali-test-dispatcher-cleanup
delete time in a month
push eventmattiasgrenfeldt/go-director
commit sha c25581421488284c14a9381348e2d5d1d770942d
imap chunk added, changed parsing style to use encoding/binary.Read
push time in 2 months
push eventmattiasgrenfeldt/go-director
commit sha ad15a06ac5117ce87f9fc59c475ba54e6a4d6fb5
Basic RIFX parsing
push time in 2 months
startedjsannemo/spooky-vm
started time in 2 months
delete branch google/go-safeweb
delete branch : grenfeldt-mihalimara22-fix-todos-in-godocs
delete time in 2 months
pull request commentgoogle/go-safeweb
Rewrite email in commits from personal email to google email.
I realized that this can't be done using a PR but must be done by force pushing to master.
comment created time in 2 months
delete branch google/go-safeweb
delete branch : grenfeldt-package-level-godocs
delete time in 2 months
PR closed google/go-safeweb
I discovered that I had accidentally used my personal email for most of the commits during this project. These commits should instead be made using my @google.com email. I have rewritten history so that the @google.com email is used everywhere.
pr closed time in 2 months
PR opened google/go-safeweb
I discovered that I had accidentally used my personal email for most of the commits during this project. These commits should instead be made using my @google.com email.
pr created time in 2 months
pull request commentgoogle/go-safeweb
Completed the framework's godoc
@googlebot I consent.
comment created time in 2 months
push eventgoogle/go-safeweb
commit sha 64a10ad7193045b52ca029c8e62cce760650be65
Updated godocs according to review comments
push time in 2 months
Pull request review commentgoogle/go-safeweb
Completed the framework's godoc
package safehttp -// Handler TODO+// Handler responds to an HTTP request.+//+// ServeHTTP should set headers, call one of the writing functions on the ResponseWriter+// to write the body of the response and then return the Result received from writing+// to the ResponseWriter.+//+// It is not valid to use the ResponseWriter or read from the body of the IncomingRequest+// after or concurrently with the completion of the ServeHTTP call.
We copied this pretty much straight off from the godoc of net/http.Handler. I agree it's pretty confusing so I'll remove it.
comment created time in 2 months
Pull request review commentgoogle/go-safeweb
Added package level godocs to Collector, CORS, CSP, HSTS, staticheaders and safehttptest.
var disallowedContentTypes = map[string]bool{ // // Each CORS request must contain the header "X-Cors: 1". //+// The HEAD request method is disallowed.+// // All of this is to prevent XSRF. type Interceptor struct { // AllowedOrigins determines which origins should be allowed in the // Access-Control-Allow-Origin header. AllowedOrigins map[string]bool- allowedHeaders map[string]bool
This move is to make the godoc look better. If this is placed here then there will be an empty line in the godoc between AllowedOrigins and ExposedHeaders. Like this:
// AllowedOrigins determines which origins should be allowed in the
// Access-Control-Allow-Origin header.
AllowedOrigins map[string]bool
// ExposedHeaders ...
But if this is moved to be below all of the exported fields then the empty line is removed.
comment created time in 2 months
PR opened google/go-safeweb
As part of improving the documentation of the project ( #132 ) I've added package level godocs for the following packages:
- safehttp/plugins/collector
- safehttp/plugins/cors
- safehttp/plugins/csp
- safehttp/plugins/hsts
- safehttp/plugins/staticheaders
- safehttp/safehttptest
Package level comments left to write are for:
- safehttp/plugins/xsrf
- safehttp/plugins/fetch_metadata
I'll let @maramihali write those since she wrote those packages.
pr created time in 2 months
create barnchgoogle/go-safeweb
branch : grenfeldt-package-level-godocs
created branch time in 2 months
delete branch google/go-safeweb
delete branch : grenfeldt-mihalimara22-commit-phase
delete time in 2 months
Pull request review commentgoogle/go-safeweb
func TestResponseWriterWriteTwicePanic(t *testing.T) { w.Write(safehtml.HTMLEscaped("<h1>Escaped, so not really a heading</h1>")) }, },+ {+ name: "Call WriteJSON twice",+ write: func(w *safehttp.ResponseWriter) {+ obj := struct{ Field string }{Field: "myField"}+ w.WriteJSON(obj)+ w.WriteJSON(obj)+ },
I opened an issue for testing every combination and ordering of writing methods. #157
comment created time in 2 months
issue openedgoogle/go-safeweb
The TestResponseWriterWriteTwicePanic test in response_writer_test.go tests whether writing twice to ResponseWriter will cause a panic. But it only checks whether calling the same method twice causes a panic. For example:
w.Write(safehtml.HTMLEscaped("<h1>Escaped, so not really a heading</h1>"))
w.Write(safehtml.HTMLEscaped("<h1>Escaped, so not really a heading</h1>"))
or
w.NoContent()
w.NoContent()
It should be refactored to check every combination and ordering of calling two writing methods.
created time in 2 months
Pull request review commentgoogle/go-safeweb
func NotWritten() Result { return Result{} } -// Write TODO+// Write dispatches the response to the Dispatcher, setting the Content-Type to+// text/html; charset=utf-8 and response status to 200 OK if resp is a safe+// response. The Dispatcher will then write the response to the underlying+// Response Writer.+//+// TODO: replace panics with proper error handling when getting the response+// Content-Type or writing the response fails. func (w *ResponseWriter) Write(resp Response) Result {- w.markWritten()+ ct, err := w.d.ContentType(resp)+ if err != nil {+ panic(err)+ }+ w.rw.WriteHeader(int(StatusOK))+ w.rw.Header().Set("Content-Type", ct)
I agree that we need a better error strategy for the dispatcher, but as you said, that's not V0.
To detect these kinds of errors in tests we need thorough integration tests between the ServeMux, the ResponseWriter and a Dispatcher.
But I still think that the ResponseWriter should not call WriteHeader. The Dispatcher should start doing the actual writing to the network connection.
comment created time in 2 months
Pull request review commentgoogle/go-safeweb
func NotWritten() Result { return Result{} } -// Write TODO+// Write dispatches the response to the Dispatcher, setting the Content-Type to+// text/html; charset=utf-8 and response status to 200 OK if resp is a safe+// response. The Dispatcher will then write the response to the underlying+// Response Writer.+//+// TODO: replace panics with proper error handling when getting the response+// Content-Type or writing the response fails. func (w *ResponseWriter) Write(resp Response) Result { w.markWritten()+ ct, err := w.d.ContentType(resp)+ if err != nil {+ panic(err)+ }+ w.rw.Header().Set("Content-Type", ct)+ w.rw.WriteHeader(int(StatusOK)) if err := w.d.Write(w.rw, resp); err != nil {- panic("error")+ panic(err) } return Result{} } -// WriteTemplate TODO+// WriteJSON encapsulates data into a JSON response and dispatches it to the+// Dispatcher, to be serialised and written to the ResponseWriter. It will also+// set the Content-Type to application/json; charset=utf-8 and the response+// status to 200 OK.+//+// TODO: replace panics with proper error handling when getting the response+// Content-Type or writing the response fails.+func (w *ResponseWriter) WriteJSON(data interface{}) Result {+ w.markWritten()+ resp := JSONResponse{Data: data}+ ct, err := w.d.ContentType(resp)+ if err != nil {+ panic(err)+ }+ w.rw.Header().Set("Content-Type", ct)+ w.rw.WriteHeader(int(StatusOK))+ if err := w.d.WriteJSON(w.rw, resp); err != nil {+ panic(err)+ }+ return Result{}+}++// WriteTemplate dispatches a parsed template and a data object to the+// Dispatcher to be executed and written to the underlying Response Writer, in+// case the template is a safe HTML template. If t is a safe HTML Template, the+// Content-Type will also be set to text/html; charset=utf-8 and the response+// status to 200 OK.+//+// TODO: replace panics with proper error handling when getting the response+// Content-Type or writing the response fails.
The Content-Type for safe html templates depends on the Dispatcher implementation and should be left out of this comment.
comment created time in 2 months
Pull request review commentgoogle/go-safeweb
func NotWritten() Result { return Result{} } -// Write TODO+// Write dispatches the response to the Dispatcher, setting the Content-Type to+// text/html; charset=utf-8 and response status to 200 OK if resp is a safe+// response. The Dispatcher will then write the response to the underlying+// Response Writer.+//+// TODO: replace panics with proper error handling when getting the response+// Content-Type or writing the response fails. func (w *ResponseWriter) Write(resp Response) Result { w.markWritten()+ ct, err := w.d.ContentType(resp)+ if err != nil {+ panic(err)+ }+ w.rw.Header().Set("Content-Type", ct)+ w.rw.WriteHeader(int(StatusOK)) if err := w.d.Write(w.rw, resp); err != nil {- panic("error")+ panic(err) } return Result{} } -// WriteTemplate TODO+// WriteJSON encapsulates data into a JSON response and dispatches it to the+// Dispatcher, to be serialised and written to the ResponseWriter. It will also+// set the Content-Type to application/json; charset=utf-8 and the response+// status to 200 OK.+//+// TODO: replace panics with proper error handling when getting the response+// Content-Type or writing the response fails.+func (w *ResponseWriter) WriteJSON(data interface{}) Result {+ w.markWritten()+ resp := JSONResponse{Data: data}+ ct, err := w.d.ContentType(resp)+ if err != nil {+ panic(err)+ }+ w.rw.Header().Set("Content-Type", ct)
Shouldn't this method always set the Content-Type to application/json? Then it doesn't need to interrogate the Dispatcher
comment created time in 2 months
Pull request review commentgoogle/go-safeweb
func NotWritten() Result { return Result{} } -// Write TODO+// Write dispatches the response to the Dispatcher, setting the Content-Type to+// text/html; charset=utf-8 and response status to 200 OK if resp is a safe
This sets the Content-Type to whatever the dispatcher tells it to. Not necessarily text/html
comment created time in 2 months
issue commentgoogle/go-safeweb
Consider removing WriteTemplate and WriteJSON, implement helpers to just rely on Write
This relates to #85 (Just adding so we don't forget)
comment created time in 2 months
create barnchgoogle/go-safeweb
branch : grenfeldt-mihalimara22-fix-todos-in-godocs
created branch time in 2 months
issue openedgoogle/go-safeweb
Currently the ReponseWriter.Redirect method still checks whether the given status code is in the right range. We have removed this check for WriteError and should remove it for Redirect to.
created time in 2 months
Pull request review commentgoogle/go-safeweb
Added a safehttp.NotWritten result so that safehttp.Result{} can stop being used.
func (m methodHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { } } - h.ServeHTTP(rw, ir)+ if res := h.ServeHTTP(rw, ir); !res.written {+ rw.NoContent()
Sorry, I missed your response. But I opened #153 so this won't be forgotten.
comment created time in 2 months
issue openedgoogle/go-safeweb
In #120 @empijei suggested that we should respond with 200 Ok with an empty body if a handler never writes a response instead of 204 No Content. Responding with 200 Ok is what net/http does. I missed to implement this in that PR, so I'm opening this issue.
created time in 2 months
push eventgoogle/go-safeweb
commit sha b435fb6a90759c7b315a2671079c1e2df4c412fd
Added first version of CORS plugin A CORS interceptor is implemented that handles preflights and applies the specified CORS rules on incoming requests. The request methods GET, HEAD and POST are banned. The Content-Types application/x-www-form-urlencoded, multipart/form-data and text/plain are also banned. Each request must also contain either 'X-Cors: 1' or 'Sec-Fetch-Mode: cors'. All of this is to prevent XSRF on CORS requests.
commit sha 4d0042ad881c15fbbef2198c9a1b8579d489bddf
Rephrased comments according to code review comments.
commit sha 2e56833fdd8b5180167363120521d71fc6d618c3
Added check if Access-Control-Request-Method is empty or missing.
commit sha 3c1acb99924d769b0c79113a225fdf37bf51a420
Renamed actualRequest to just request.
commit sha a1b05a20cccd3526c60682a083e3a08cb00ab16f
Removed setHeaders closure in Before.
commit sha 567cf19888098a193b028de39445ca5244ecc540
Removed Sec-Fetch-Mode: cors as a valid header, now X-Cors: 1 is required.
commit sha 3dcbb083476c4e1f93b812883b9864a7bb3c7a6e
Make sure that requests with no Origin header are allowed.
commit sha 0cd8b8ae4e93f4fe999ccf6634c1e88fdf47862c
Make sure that a non-standard Content-Type is required on every request
commit sha d7fcfe907c8dd11c9efe710246e463824d71f407
Removed disallowed request methods.
commit sha 5a7843f749f1616f42178eca13fbf14a248db274
Improved comments
commit sha 4cb65c2bc114e3a30f996494e80103d90ef7e8e8
Ban "*" from being set as an allowed header
commit sha bfa142c5dbd0ad5d47a538a086946e3f11440862
Renamed customHeader to requiredHeader
commit sha d30b5ed11590daa0d5b5aac075c3e6b06845014d
Removed check disallowing requests with cookies when AllowCredentials is false, this should happen in the browser.
commit sha 190afca731fe9dedb4624c8c546c4873af348e10
Updated comments to reflect allowing GET/HEAD/POST etc. and fixed godoc formatting issues.
commit sha dbc0625452e8479554c61e52450e34535465f75f
Clarified godoc for MaxAge that it is specified in seconds
commit sha 95e88e8be9612b15825a94d52e5bec5a29a86bb5
Improved godoc
commit sha b1c2f247a395e3bf8e7529bf3630a0bd8d0c7a60
Mentioned in godoc that the wildcard * is disallowed from ACAH header
commit sha bce23a4440ef02b33cffda0313e3642c61e65317
Refactored CORS plugin to use WriteError and new Header API
commit sha a3a3ec535c64348e7e168c5e4bb9ed523828725f
Append to the Vary header and don't claim it.
commit sha 150dd35b0fa19eef0510f3e7e23b0d6a0382fbfc
Head method is forbidden in Access-Control-Request-Method
push time in 2 months
delete branch google/go-safeweb
delete branch : grenfeldt-cors-plugin-first-version
delete time in 2 months
Pull request review commentgoogle/go-safeweb
func NotWritten() Result { return Result{} } -// Write TODO+// Write dispatches the response to the Dispatcher, setting the Content-Type to+// text/html; charset=utf-8 and response status to 200 OK if resp is a safe+// response. The Dispatcher will then write the response to the underlying+// Response Writer.+//+// TODO: replace panics with proper error handling when getting the response+// Content-Type or writing the response fails. func (w *ResponseWriter) Write(resp Response) Result {- w.markWritten()+ ct, err := w.d.ContentType(resp)+ if err != nil {+ panic(err)+ }+ w.rw.WriteHeader(int(StatusOK))+ w.rw.Header().Set("Content-Type", ct)
I am not sure what to do here.
comment created time in 2 months
push eventgoogle/go-safeweb
commit sha a9dd4edb5ed4336446c83b79e36820321967cbe9
Empty Origin header on preflight requests disallowed.
push time in 2 months
Pull request review commentgoogle/go-safeweb
func TestMuxInterceptors(t *testing.T) { } type setHeaderConfig struct {- name string- value string
Fixed!
comment created time in 2 months
push eventgoogle/go-safeweb
commit sha cf31c312d2b50f8447358a12da06ba5dd61f84d5
Removed unrelated changes to tests to make code reviewing easier
push time in 2 months
Pull request review commentgoogle/go-safeweb
func (setHeaderConfig) Match(i safehttp.Interceptor) bool { type setHeaderConfigInterceptor struct{} func (p setHeaderConfigInterceptor) Before(w *safehttp.ResponseWriter, _ *safehttp.IncomingRequest, cfg interface{}) safehttp.Result {- name := "Pizza" value := "Hawaii" if c, ok := cfg.(setHeaderConfig); ok {- name = c.name- value = c.value+ value = c.pizzaValue }- w.Header().Set(name, value)+ w.Header().Set("Pizza", value) return safehttp.Result{} } +func (p setHeaderConfigInterceptor) Commit(w *safehttp.CommitResponseWriter, r *safehttp.IncomingRequest, resp safehttp.Response, cfg interface{}) safehttp.Result {+ value := "Fusili"+ if c, ok := cfg.(setHeaderConfig); ok {+ value = c.pastaValue+ }
Ah, yes, this check was to check if a valid config was passed. If there was no valid config passed then the default values would be used.
comment created time in 2 months
Pull request review commentgoogle/go-safeweb
Added first version of CORS plugin
+// Copyright 2020 Google LLC+//+// Licensed under the Apache License, Version 2.0 (the "License");+// you may not use this file except in compliance with the License.+// You may obtain a copy of the License at+//+// https://www.apache.org/licenses/LICENSE-2.0+//+// Unless required by applicable law or agreed to in writing, software+// distributed under the License is distributed on an "AS IS" BASIS,+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+// See the License for the specific language governing permissions and+// limitations under the License.++package cors_test++import (+ "testing"++ "github.com/google/go-cmp/cmp"+ "github.com/google/go-safeweb/safehttp"+ "github.com/google/go-safeweb/safehttp/plugins/cors"+ "github.com/google/go-safeweb/safehttp/safehttptest"+)++func TestRequest(t *testing.T) {+ tests := []struct {+ name string+ req *safehttp.IncomingRequest+ allowCredentials bool+ exposedHeaders []string+ want map[string][]string+ }{+ {+ name: "Basic GET",+ req: func() *safehttp.IncomingRequest {+ r := safehttptest.NewRequest(safehttp.MethodGet, "http://bar.com", nil)+ r.Header.Set("Origin", "https://foo.com")+ r.Header.Set("X-Cors", "1")+ r.Header.Set("Content-Type", "application/json")+ return r+ }(),+ want: map[string][]string{+ "Access-Control-Allow-Origin": {"https://foo.com"},+ "Vary": {"Origin"},+ },+ },+ {+ name: "Basic PUT",+ req: func() *safehttp.IncomingRequest {+ r := safehttptest.NewRequest(safehttp.MethodPut, "http://bar.com", nil)+ r.Header.Set("Origin", "https://foo.com")+ r.Header.Set("X-Cors", "1")+ r.Header.Set("Content-Type", "application/json")+ return r+ }(),+ want: map[string][]string{+ "Access-Control-Allow-Origin": {"https://foo.com"},+ "Vary": {"Origin"},+ },+ },+ {+ name: "Basic POST",+ req: func() *safehttp.IncomingRequest {+ r := safehttptest.NewRequest(safehttp.MethodPost, "http://bar.com", nil)+ r.Header.Set("Origin", "https://foo.com")+ r.Header.Set("X-Cors", "1")+ r.Header.Set("Content-Type", "application/json")+ return r+ }(),+ want: map[string][]string{+ "Access-Control-Allow-Origin": {"https://foo.com"},+ "Vary": {"Origin"},+ },+ },+ {+ name: "Basic HEAD",+ req: func() *safehttp.IncomingRequest {+ r := safehttptest.NewRequest(safehttp.MethodHead, "http://bar.com", nil)+ r.Header.Set("Origin", "https://foo.com")+ r.Header.Set("X-Cors", "1")+ r.Header.Set("Content-Type", "application/json")+ return r+ }(),+ want: map[string][]string{+ "Access-Control-Allow-Origin": {"https://foo.com"},+ "Vary": {"Origin"},+ },+ },+ {+ name: "No Origin header",+ req: func() *safehttp.IncomingRequest {+ r := safehttptest.NewRequest(safehttp.MethodPut, "http://bar.com", nil)+ r.Header.Set("X-Cors", "1")+ r.Header.Set("Content-Type", "application/json")+ return r+ }(),+ want: map[string][]string{},+ },+ {+ name: "AllowCredentials but no cookies",+ req: func() *safehttp.IncomingRequest {+ r := safehttptest.NewRequest(safehttp.MethodPut, "http://bar.com", nil)+ r.Header.Set("Origin", "https://foo.com")+ r.Header.Set("X-Cors", "1")+ r.Header.Set("Content-Type", "application/json")+ return r+ }(),+ allowCredentials: true,+ want: map[string][]string{+ "Access-Control-Allow-Origin": {"https://foo.com"},+ "Vary": {"Origin"},+ },+ },+ {+ name: "AllowCredentials with cookies",+ req: func() *safehttp.IncomingRequest {+ r := safehttptest.NewRequest(safehttp.MethodPut, "http://bar.com", nil)+ r.Header.Set("Origin", "https://foo.com")+ r.Header.Set("X-Cors", "1")+ r.Header.Set("Content-Type", "application/json")+ r.Header.Set("Cookie", "a=b")+ return r+ }(),+ allowCredentials: true,+ want: map[string][]string{+ "Access-Control-Allow-Credentials": {"true"},+ "Access-Control-Allow-Origin": {"https://foo.com"},+ "Vary": {"Origin"},+ },+ },+ {+ name: "Expose one header",+ req: func() *safehttp.IncomingRequest {+ r := safehttptest.NewRequest(safehttp.MethodPut, "http://bar.com", nil)+ r.Header.Set("Origin", "https://foo.com")+ r.Header.Set("X-Cors", "1")+ r.Header.Set("Content-Type", "application/json")+ return r+ }(),+ exposedHeaders: []string{"Aaaa"},+ want: map[string][]string{+ "Access-Control-Expose-Headers": {"Aaaa"},+ "Access-Control-Allow-Origin": {"https://foo.com"},+ "Vary": {"Origin"},+ },+ },+ {+ name: "Expose multiple headers",+ req: func() *safehttp.IncomingRequest {+ r := safehttptest.NewRequest(safehttp.MethodPut, "http://bar.com", nil)+ r.Header.Set("Origin", "https://foo.com")+ r.Header.Set("X-Cors", "1")+ r.Header.Set("Content-Type", "application/json")+ return r+ }(),+ exposedHeaders: []string{"Aaaa", "Bbbb", "Cccc"},+ want: map[string][]string{+ "Access-Control-Expose-Headers": {"Aaaa, Bbbb, Cccc"},+ "Access-Control-Allow-Origin": {"https://foo.com"},+ "Vary": {"Origin"},+ },+ },+ }++ for _, tt := range tests {+ t.Run(tt.name, func(t *testing.T) {+ rr := safehttptest.NewResponseRecorder()++ it := cors.Default("https://foo.com")+ it.AllowCredentials = tt.allowCredentials+ it.ExposedHeaders = tt.exposedHeaders+ it.Before(rr.ResponseWriter, tt.req)+
Opened #152
comment created time in 2 months
issue openedgoogle/go-safeweb
Currently the ResponseRecorder in safehttptest sets the status code to 200 if no status code has been set yet. This should be changed to 0 so that interceptors that don't set a status code and leave it up to the handler can be tested properly.
created time in 2 months
push eventgoogle/go-safeweb
commit sha 88e9f0190041facdc35d4b02c382e338a343270c
Refactored big if-else chain into switch statement
push time in 2 months
delete branch google/go-safeweb
delete branch : grenfeldt-dispatcher-wrapper
delete time in 2 months
push eventgoogle/go-safeweb
commit sha d7995fb291a191a6db41098bdfceb34385c85120
Fixed phrasing in comment Co-authored-by: maramihali <[email protected]>
push time in 2 months
push eventgoogle/go-safeweb
commit sha f448ddce7b81e785f1ebbef6e8886cd7443c7f6c
commitPhase moved to handlerWithInterceptors from ResponseWriter.
commit sha b3846b4daf05f9bed1e308033a801a2f184d7e2f
Unexported appliedInterceptors again and fixed TODO godocs
push time in 2 months
Pull request review commentgoogle/go-safeweb
func TestMuxInterceptors(t *testing.T) { } type setHeaderConfig struct {- name string- value string
What do you mean with unrelated changes? We have updated the SetHeaderInterceptor with config and the SetHeaderInterceptor with mismatching config tests to also test the commit phase. Earlier they only tested the Before phase. How is this unrelated from the PR introducing the commit phase?
I looked through all our changes and found one unrelated change in mux_test.go in TestMuxInterceptors:
- wantStatus: 500,
+ wantStatus: safehttp.StatusInternalServerError,
We have in an earlier PR replaced all numeric status codes with constants and apparently I had missed this instance. How could it not be ok to fix these kinds of tiny nits when they are discovered? The other option would be to open a new issue and PR to just change this little thing.
comment created time in 2 months
Pull request review commentgoogle/go-safeweb
func NewServeMux(d Dispatcher, domains ...string) *ServeMux { } } -type appliedInterceptor struct {+// AppliedInterceptor TODO+type AppliedInterceptor struct {
It was part of the public API since we wanted to pass it as part of the ResponseWriter constructor and for that it needs to be exported, so that the constructor doesn't become unusable outside the package.
comment created time in 2 months
Pull request review commentgoogle/go-safeweb
type ResponseWriter struct { // Having this field unexported is essential for security. Otherwise one can // easily overwrite the struct bypassing all our safety guarantees.- header Header- written bool+ header Header+ appliedInterceps []AppliedInterceptor+ req *IncomingRequest
Well, both appliedInterceps and req needed to be part of the ResponseWriter for our design where the ResponseWriter would run the commitPhase. In the newer design I'm working on now the ResponseWriter still needs to own the req but instead of appliedInterceps it has a reference to the handlerWithInterceptors and the commitPhase is moved to the handlerWithInterceptors.
comment created time in 2 months
pull request commentgoogle/go-safeweb
Added first version of CORS plugin
@empijei I fixed the two most recent comments you left about appending Vary and disallowing HEAD.
comment created time in 2 months
push eventgoogle/go-safeweb
commit sha d01703e410a53b2ada5a41ee09e528229c57fd4d
Removed error return values from Add, Set, Claim and del in Header and panic instead.
commit sha fdcfcb443a5f2dd71d4c2ad3f21a695b64d2a114
Added IsClaimed method to Header type
commit sha 7b590e3f36a44c512a9eb74c10c2f24b1e8259d1
Improved godoc for Header type methods
commit sha 06c0696df68349adc017c7e4388fcfbe6bd76691
Replaced ClientError and ServerError with WriteError in ResponseWriter
commit sha 2ec19976fb0a5a1758ebb2c4eb12e15a499b6391
Make it more clear that error responses are in the range 400-599
commit sha 3930a0f6b435c4ba7f803b5fd21f2ffe249154b1
Removed unused safehttp.HandleFunc
commit sha 450a41597a7efcddb8f45b637cf3f1c48e313087
Removed interceptor keys from multiplexer. Now Interceptors are stored in slices instead of maps. To interact with an interceptor from the handler that interceptor needs to implement package level functions which take ResponseWriters and IncomingRequests and do things. State can be stored in the context of the request.
commit sha 16d085d22036bad318e812d3bc35a686a3031134
Fixed nit in godoc and renamed appliedInterceps to just interceps
commit sha 06e2a04f5958ccfdc9aab7d6920177997a7b6700
Renamed ai to interceptor
commit sha 2a1bb79b5e3f671cb4ce8cc1c3adacb5d6fcdbc7
Added pre-login XSRF protection to the XSRF plugin and tests to verify the functionality
commit sha bd5c56b0c83d4fb12b0fc2ab9a6342cbba15cdea
switch from checking whether the cookie is nil to checking whether the error is non-nil in call to IncomingRequest.Cookie
commit sha bc15e1a94ce14d25f24d3dab630b557988e021f5
Improve naming and documentation
commit sha 98bc621a460174b78177bf16ae40ef124dff7472
Return an error instead of panicking if rand.Read fails
commit sha 7533ec91e55da464b82c82be04f808f717302ffd
Added first version of CORS plugin A CORS interceptor is implemented that handles preflights and applies the specified CORS rules on incoming requests. The request methods GET, HEAD and POST are banned. The Content-Types application/x-www-form-urlencoded, multipart/form-data and text/plain are also banned. Each request must also contain either 'X-Cors: 1' or 'Sec-Fetch-Mode: cors'. All of this is to prevent XSRF on CORS requests.
commit sha ef28686ed7626d8ae053f40a9bd4f862fb433e84
Rephrased comments according to code review comments.
commit sha b210eb5249c477321277eecc048bfb0eb914478a
Added check if Access-Control-Request-Method is empty or missing.
commit sha 42e0477ea0c747bfbd9415d8a6eb3765770dbe10
Renamed actualRequest to just request.
commit sha 902410bdb24101de57d29e8d22ab5a662d17dd7e
Removed setHeaders closure in Before.
commit sha 176fb7b9a6c844125f4153612459bbd2e7b43ea3
Removed Sec-Fetch-Mode: cors as a valid header, now X-Cors: 1 is required.
commit sha 120ebb43c7810da344699f30820457a0f4c89ad8
Make sure that requests with no Origin header are allowed.
push time in 2 months
Pull request review commentgoogle/go-safeweb
func NotWritten() Result { return Result{} } -// Write TODO+// Write dispatches the response to the Dispatcher, setting the Content-Type to+// text/html; charset=utf-8 and response status to 200 OK if resp is a safe+// response. The Dispatcher will then write the response to the underlying+// Response Writer.+//+// TODO: replace panics with proper error handling when getting the response+// Content-Type or writing the response fails. func (w *ResponseWriter) Write(resp Response) Result {- w.markWritten()+ ct, err := w.d.ContentType(resp)+ if err != nil {+ panic(err)+ }+ w.rw.WriteHeader(int(StatusOK))+ w.rw.Header().Set("Content-Type", ct)
Imagine that w.d.Write panics or returns an error, in which case ResponseWriter.Write will panic. This panic will be recovered by the framework (ServeMux) and a 500 Internal Server Error response will be written to the ResponseWriter. But the status code and the headers have already been written, so things will break.
We need to remove the call to WriteHeader.
comment created time in 2 months
Pull request review commentgoogle/go-safeweb
Added first version of CORS plugin
+// Copyright 2020 Google LLC+//+// Licensed under the Apache License, Version 2.0 (the "License");+// you may not use this file except in compliance with the License.+// You may obtain a copy of the License at+//+// https://www.apache.org/licenses/LICENSE-2.0+//+// Unless required by applicable law or agreed to in writing, software+// distributed under the License is distributed on an "AS IS" BASIS,+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+// See the License for the specific language governing permissions and+// limitations under the License.++package cors++import (+ "net/textproto"+ "strconv"+ "strings"++ "github.com/google/go-safeweb/safehttp"+)++const customHeader string = "X-Cors"++var disallowedMethods = map[string]bool{+ safehttp.MethodHead: true,+ safehttp.MethodGet: true,+ safehttp.MethodPost: true,+}++var disallowedContentTypes = map[string]bool{+ "application/x-www-form-urlencoded": true,+ "multipart/form-data": true,+ "text/plain": true,+}++// Interceptor handles CORS requests based on its settings.+//+// For more info about CORS, see: https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS+//+// Constraints+//+// - The request methods GET, HEAD and POST are banned and will result in a 405+// Method Not Allowed response.+// - The content types "application/x-www-form-urlencoded", "multipart/form-data"+// and "text/plain" are also banned and will result in a 415 Unsupported Media+// Type response.+// - Each CORS request must contain the header "X-Cors: 1" or "Sec-Fetch-Mode: cors".+// All of this is to prevent XSRF.+type Interceptor struct {+ // AllowedOrigins determines which origins should be allowed in the+ // Access-Control-Allow-Origin header.+ AllowedOrigins map[string]bool+ allowedHeaders map[string]bool+ // ExposedHeaders determines which headers should be set in the+ // Access-Control-Expose-Headers header. If ExposedHeaders is nil, then the+ // header is not set.+ ExposedHeaders []string+ // AllowCredentials determines if Access-Control-Allow-Credentials should be+ // set to true when requests containing Cookie headers arrive.+ AllowCredentials bool+ // MaxAge sets the Access-Control-Max-Age header.+ // MaxAge=0 results in MaxAge: 5.+ MaxAge int+}++// Default creates a CORS Interceptor with default settings.+// Those defaults are:+// - No Exposed Headers+// - No Allowed Headers+// - AllowCredentials: false+// - MaxAge: 5 seconds+func Default(allowedOrigins ...string) *Interceptor {+ ao := map[string]bool{}+ for _, o := range allowedOrigins {+ ao[o] = true+ }+ return &Interceptor{+ AllowedOrigins: ao,+ allowedHeaders: map[string]bool{},+ }+}++// SetAllowedHeaders sets which headers should be allowed in the Access-Control-Allow-Headers+// header. The headers are first canonicalized using textproto.CanonicalMIMEHeaderKey.+func (it *Interceptor) SetAllowedHeaders(headers ...string) {+ it.allowedHeaders = map[string]bool{}+ for _, h := range headers {+ it.allowedHeaders[textproto.CanonicalMIMEHeaderKey(h)] = true+ }+}++// Before handles the incoming request and sets headers or responds with an error+// accordingly.+func (it *Interceptor) Before(w safehttp.ResponseWriter, r *safehttp.IncomingRequest) safehttp.Result {+ origin := r.Header.Get("Origin")+ if !it.AllowedOrigins[origin] {+ return w.ClientError(safehttp.StatusForbidden)+ }+ h := w.Header()+ allowOrigin, err := h.Claim("Access-Control-Allow-Origin")+ if err != nil {+ return w.ServerError(safehttp.StatusInternalServerError)+ }+ vary, err := h.Claim("Vary")
No, so I will fix it
comment created time in 2 months
delete branch google/go-safeweb
delete branch : grenfeldt-remove-plugin-keys
delete time in 2 months
issue openedgoogle/go-safeweb
Feature Request: Ban multiple configs for the same interceptor.
Currently it is possible to send multiple different configs for the same Interceptor when calling ServeMux.Handle. This should be banned. Instead the method should probably panic if this occurs.
created time in 2 months
push eventgoogle/go-safeweb
commit sha 5c0f4324171d8b49da941eaf77cd9079359db5c0
Renamed ai to interceptor
push time in 2 months
Pull request review commentgoogle/go-safeweb
+// Copyright 2020 Google LLC+//+// Licensed under the Apache License, Version 2.0 (the "License");+// you may not use this file except in compliance with the License.+// You may obtain a copy of the License at+//+// https://www.apache.org/licenses/LICENSE-2.0+//+// Unless required by applicable law or agreed to in writing, software+// distributed under the License is distributed on an "AS IS" BASIS,+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+// See the License for the specific language governing permissions and+// limitations under the License.++package safehttp_test++import (+ "github.com/google/go-safeweb/safehttp"+ "github.com/google/safehtml"+ safetemplate "github.com/google/safehtml/template"+ "html/template"+ "math"+ "net/http"+ "strings"+ "testing"+)++func TestDefaultDispatcherValidResponse(t *testing.T) {+ tests := []struct {+ name string+ write func(w http.ResponseWriter) error+ wantStatus safehttp.StatusCode+ wantHeaders map[string][]string+ wantBody string+ }{+ {+ name: "Safe HTML Response",+ write: func(w http.ResponseWriter) error {+ d := &safehttp.DefaultDispatcher{}+ return d.Write(w, safehtml.HTMLEscaped("<h1>Hello World!</h1>"))+ },+ wantBody: "<h1>Hello World!</h1>",+ },+ {+ name: "Safe HTML Template Response",+ write: func(w http.ResponseWriter) error {+ d := &safehttp.DefaultDispatcher{}+ return d.ExecuteTemplate(w, safetemplate.Must(safetemplate.New("name").Parse("<h1>{{ . }}</h1>")), "This is an actual heading, though.")+ },+ wantBody: "<h1>This is an actual heading, though.</h1>",+ },+ {+ name: "Valid JSON Response",+ write: func(w http.ResponseWriter) error {+ d := &safehttp.DefaultDispatcher{}+ data := struct {+ Field string `json:"field"`+ }{Field: "myField"}+ return d.WriteJSON(w, safehttp.JSONResponse{Data: data})+ },+ wantBody: `{"field":"myField"}`,+ },+ }+ for _, tt := range tests {+ t.Run(tt.name, func(t *testing.T) {+ b := &strings.Builder{}+ rw := newResponseRecorder(b)++ err := tt.write(rw)++ if err != nil {+ t.Errorf("tt.write(rw): got error %v, want nil", err)+ }++ if gotBody := b.String(); tt.wantBody != gotBody {+ t.Errorf("response body: got %q, want %q", gotBody, tt.wantBody)+ }+ })+ }+}++func TestDefaultDispatcherInvalidResponse(t *testing.T) {+ tests := []struct {+ name string+ write func(w http.ResponseWriter) error+ }{+ {+ name: "Unsafe HTML Response",+ write: func(w http.ResponseWriter) error {+ d := &safehttp.DefaultDispatcher{}+ return d.Write(w, "<h1>Hello World!</h1>")+ },+ },+
Remove empty line
comment created time in 2 months
Pull request review commentgoogle/go-safeweb
func TestResponseWriterWriteTwicePanic(t *testing.T) { w.Write(safehtml.HTMLEscaped("<h1>Escaped, so not really a heading</h1>")) }, },+ {+ name: "Call WriteJSON twice",+ write: func(w *safehttp.ResponseWriter) {+ data := struct{ Field string }{Field: "myField"}+ jsonObj, err := json.Marshal(data)+ if err != nil {+ t.Fatalf("invalid json object")+ }+ w.WriteJSON(jsonObj)+ w.WriteJSON(jsonObj)+ },
When you use WriteJSON you send the actual struct, right? The dispatcher will marshal it for you so that isn't needed here. Now you are sending just a string.
comment created time in 2 months
Pull request review commentgoogle/go-safeweb
func (w *ResponseWriter) Interceptor(key string) Interceptor { } // Result TODO-type Result struct{}+type Result struct {+ err error+}
Is this field used anywhere? If it isn't, remove it for now.
comment created time in 2 months
Pull request review commentgoogle/go-safeweb
func NotWritten() Result { return Result{} } -// Write TODO+// Write dispatches the response to the Dispatcher, setting the Content-Type to+// text/html; charset=utf-8 and response status to 200 OK if resp is a safe+// response. The Dispatcher will then write the response to the underlying+// Response Writer.+//+// TODO: replace panics with proper error handling when getting the response+// Content-Type or writing the response fails. func (w *ResponseWriter) Write(resp Response) Result {- w.markWritten()+ ct, err := w.d.ContentType(resp)+ if err != nil {+ panic(err)+ }+ w.rw.WriteHeader(int(StatusOK))+ w.rw.Header().Set("Content-Type", ct)
You need to set the ct header before calling WriteHeader. The headers of the response are written when you call WriteHeader, so they must be modified first.
The underlying responsewriter will write a 200 OK response if WriteHeader was never called, so you can remove the call to WriteHeader. And if there is a error during w.d.Write which results in a panic then something else will try to write to the responsewriter and in that case it would be bad if the response code is already set.
So remove the call to WriteHeader here, in WriteJSON and in WriteTemplate.
comment created time in 2 months
Pull request review commentgoogle/go-safeweb
+// Copyright 2020 Google LLC+//+// Licensed under the Apache License, Version 2.0 (the "License");+// you may not use this file except in compliance with the License.+// You may obtain a copy of the License at+//+// https://www.apache.org/licenses/LICENSE-2.0+//+// Unless required by applicable law or agreed to in writing, software+// distributed under the License is distributed on an "AS IS" BASIS,+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+// See the License for the specific language governing permissions and+// limitations under the License.++package safehttp++import (+ "encoding/json"+ "errors"+ "github.com/google/safehtml"+ "github.com/google/safehtml/template"+ "net/http"+)++// DefaultDispatcher is responsible of writing the body of a response to an+// Response Writer if it's of a safe type.+type DefaultDispatcher struct{}++// ContentType returns the Content-Type of a safe respons if it's of a safe-type+// and a non-nil error otherwise.
and an error otherwise
comment created time in 2 months
Pull request review commentgoogle/go-safeweb
func TestMuxHandlerReturnsNotWritten(t *testing.T) { t.Errorf(`response body got: %q want: ""`, got) } }++func TestMuxNilDispatcher(t *testing.T) {+ defer func() {+ if r := recover(); r == nil {+ t.Errorf(`mux.NewServeMux(nil): expected panic`)+ }+ }()+ _ = safehttp.NewServeMux(nil)
You can remove the _ =
comment created time in 2 months
Pull request review commentgoogle/go-safeweb
+// Copyright 2020 Google LLC+//+// Licensed under the Apache License, Version 2.0 (the "License");+// you may not use this file except in compliance with the License.+// You may obtain a copy of the License at+//+// https://www.apache.org/licenses/LICENSE-2.0+//+// Unless required by applicable law or agreed to in writing, software+// distributed under the License is distributed on an "AS IS" BASIS,+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+// See the License for the specific language governing permissions and+// limitations under the License.++package safehttp++import (+ "encoding/json"+ "errors"+ "github.com/google/safehtml"+ "github.com/google/safehtml/template"+ "net/http"+)++// DefaultDispatcher is responsible of writing the body of a response to an+// Response Writer if it's of a safe type.+type DefaultDispatcher struct{}++// ContentType returns the Content-Type of a safe respons if it's of a safe-type
response with an e
comment created time in 2 months