The code and data behind xeiaso.net
5
fork

Configure Feed

Select the types of activity you want to include in your feed.

internal: add validation for referers and accept-encoding

Signed-off-by: Xe Iaso <me@xeiaso.net>

Xe Iaso 2f5df3bf 57bd9082

+204 -2
+88 -1
internal/accept_encoding.go
··· 3 3 import ( 4 4 "expvar" 5 5 "net/http" 6 + "strconv" 7 + "strings" 6 8 7 9 "tailscale.com/metrics" 8 10 ) 9 11 10 12 var ( 11 13 acceptEncodings = &metrics.LabelMap{Label: "encoding"} 14 + 15 + validEncodings = []string{ 16 + "gzip", 17 + "x-gzip", 18 + "deflate", 19 + "br", 20 + "identity", 21 + "snappy", 22 + "bzip2", 23 + "lzma", 24 + "zstd", 25 + } 12 26 ) 13 27 14 28 func init() { 15 29 expvar.Publish("gauge_xesite_accept_encoding", acceptEncodings) 30 + } 31 + 32 + func inValidEncodings(enc string) bool { 33 + for _, validEnc := range validEncodings { 34 + if enc == validEnc { 35 + return true 36 + } 37 + } 38 + return false 16 39 } 17 40 18 41 func AcceptEncodingMiddleware(next http.Handler) http.Handler { 19 42 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 20 - acceptEncodings.Add(r.Header.Get("Accept-Encoding"), 1) 43 + for _, enc := range ParseAcceptEncoding(r.Header.Get("Accept-Encoding")) { 44 + if !inValidEncodings(enc.Encoding) { 45 + continue 46 + } 47 + acceptEncodings.Add(enc.Encoding, 1) 48 + } 49 + 21 50 next.ServeHTTP(w, r) 22 51 }) 23 52 } 53 + 54 + type EncodingQ struct { 55 + Encoding string 56 + Q float64 57 + } 58 + 59 + func ParseAcceptEncoding(acptEnc string) []EncodingQ { 60 + var eqs []EncodingQ 61 + 62 + encQStrs := strings.Split(acptEnc, ",") 63 + for _, encQStr := range encQStrs { 64 + trimedEncQStr := strings.Trim(encQStr, " ") 65 + 66 + encQ := strings.Split(trimedEncQStr, ";") 67 + if len(encQ) == 1 { 68 + eq := EncodingQ{encQ[0], 1} 69 + eqs = append(eqs, eq) 70 + } else { 71 + qp := strings.Split(encQ[1], "=") 72 + q, err := strconv.ParseFloat(qp[1], 64) 73 + if err != nil { 74 + panic(err) 75 + } 76 + eq := EncodingQ{encQ[0], q} 77 + eqs = append(eqs, eq) 78 + } 79 + } 80 + return eqs 81 + } 82 + 83 + type LangQ struct { 84 + Lang string 85 + Q float64 86 + } 87 + 88 + func ParseAcceptLanguage(acptLang string) []LangQ { 89 + var lqs []LangQ 90 + 91 + langQStrs := strings.Split(acptLang, ",") 92 + for _, langQStr := range langQStrs { 93 + trimedLangQStr := strings.Trim(langQStr, " ") 94 + 95 + langQ := strings.Split(trimedLangQStr, ";") 96 + if len(langQ) == 1 { 97 + lq := LangQ{langQ[0], 1} 98 + lqs = append(lqs, lq) 99 + } else { 100 + qp := strings.Split(langQ[1], "=") 101 + q, err := strconv.ParseFloat(qp[1], 64) 102 + if err != nil { 103 + panic(err) 104 + } 105 + lq := LangQ{langQ[0], q} 106 + lqs = append(lqs, lq) 107 + } 108 + } 109 + return lqs 110 + }
+109
internal/accept_encoding_test.go
··· 1 + package internal 2 + 3 + import "testing" 4 + 5 + func TestInValidEncodings(t *testing.T) { 6 + tests := []struct { 7 + enc string 8 + ok bool 9 + }{ 10 + {"gzip", true}, 11 + {"x-gzip", true}, 12 + {"tacobell", false}, 13 + } 14 + 15 + for _, test := range tests { 16 + t.Run(test.enc, func(t *testing.T) { 17 + ok := inValidEncodings(test.enc) 18 + if ok != test.ok { 19 + t.Errorf("ok = %t, want %t", ok, test.ok) 20 + } 21 + }) 22 + } 23 + } 24 + 25 + func TestParseAcceptLanguage(t *testing.T) { 26 + acptLang := "en-US,en;q=0.9,ja-JP;q=0.8,ja;q=0.7" 27 + lqs := ParseAcceptLanguage(acptLang) 28 + if len(lqs) != 4 { 29 + t.Errorf("len(lqs) = %d, want 4", len(lqs)) 30 + } 31 + if lqs[0].Lang != "en-US" { 32 + t.Errorf("lqs[0].Lang = %s, want en-US", lqs[0].Lang) 33 + } 34 + if lqs[0].Q != 1 { 35 + t.Errorf("lqs[0].Q = %f, want 1", lqs[0].Q) 36 + } 37 + if lqs[1].Lang != "en" { 38 + t.Errorf("lqs[1].Lang = %s, want en", lqs[1].Lang) 39 + } 40 + if lqs[1].Q != 0.9 { 41 + t.Errorf("lqs[1].Q = %f, want 0.9", lqs[1].Q) 42 + } 43 + if lqs[2].Lang != "ja-JP" { 44 + t.Errorf("lqs[2].Lang = %s, want ja-JP", lqs[2].Lang) 45 + } 46 + if lqs[2].Q != 0.8 { 47 + t.Errorf("lqs[2].Q = %f, want 0.8", lqs[2].Q) 48 + } 49 + if lqs[3].Lang != "ja" { 50 + t.Errorf("lqs[3].Lang = %s, want ja", lqs[3].Lang) 51 + } 52 + if lqs[3].Q != 0.7 { 53 + t.Errorf("lqs[3].Q = %f, want 0.7", lqs[3].Q) 54 + } 55 + 56 + t.Run("invalid", func(t *testing.T) { 57 + panicked := false 58 + acptEnc := "taco;q=beer" 59 + defer func() { 60 + if r := recover(); r != nil { 61 + panicked = true 62 + } 63 + if !panicked { 64 + t.Errorf("did not panic") 65 + } 66 + }() 67 + ParseAcceptLanguage(acptEnc) 68 + }) 69 + } 70 + 71 + func TestParseAcceptEncoding(t *testing.T) { 72 + acptEnc := "gzip, deflate, br;q=0.9" 73 + eqs := ParseAcceptEncoding(acptEnc) 74 + if len(eqs) != 3 { 75 + t.Errorf("len(eqs) = %d, want 3", len(eqs)) 76 + } 77 + if eqs[0].Encoding != "gzip" { 78 + t.Errorf("eqs[0].Encoding = %s, want gzip", eqs[0].Encoding) 79 + } 80 + if eqs[0].Q != 1 { 81 + t.Errorf("eqs[0].Q = %f, want 1", eqs[0].Q) 82 + } 83 + if eqs[1].Encoding != "deflate" { 84 + t.Errorf("eqs[1].Encoding = %s, want deflate", eqs[1].Encoding) 85 + } 86 + if eqs[1].Q != 1 { 87 + t.Errorf("eqs[1].Q = %f, want 1", eqs[1].Q) 88 + } 89 + if eqs[2].Encoding != "br" { 90 + t.Errorf("eqs[2].Encoding = %s, want br", eqs[2].Encoding) 91 + } 92 + if eqs[2].Q != 1 { 93 + t.Errorf("eqs[2].Q = %f, want 1", eqs[2].Q) 94 + } 95 + 96 + t.Run("invalid", func(t *testing.T) { 97 + panicked := false 98 + acptEnc := "gzip, deflate, taco;q=beer" 99 + defer func() { 100 + if r := recover(); r != nil { 101 + panicked = true 102 + } 103 + if !panicked { 104 + t.Errorf("did not panic") 105 + } 106 + }() 107 + ParseAcceptEncoding(acptEnc) 108 + }) 109 + }
+7 -1
internal/referer.go
··· 3 3 import ( 4 4 "expvar" 5 5 "net/http" 6 + "net/url" 6 7 7 8 "tailscale.com/metrics" 8 9 ) ··· 17 18 18 19 func RefererMiddleware(next http.Handler) http.HandlerFunc { 19 20 return func(w http.ResponseWriter, r *http.Request) { 20 - referers.Add(r.Header.Get("Referer"), 1) 21 + if referer := r.Header.Get("Referer"); referer != "" { 22 + _, err := url.Parse(referer) 23 + if err == nil { 24 + referers.Add(referer, 1) 25 + } 26 + } 21 27 next.ServeHTTP(w, r) 22 28 } 23 29 }