diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..51e8e4c --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,25 @@ +repos: + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.5.0 + hooks: + - id: trailing-whitespace + exclude_types: [markdown] + - id: end-of-file-fixer + - id: check-yaml + - id: check-added-large-files + + - repo: https://github.com/tekwizely/pre-commit-golang + rev: v1.0.0-rc.1 + hooks: + - id: go-imports + args: + - "-w" + - id: go-vet-repo-mod + - id: go-staticcheck-repo-mod + + - repo: https://github.com/beeper/pre-commit-go + rev: v0.3.1 + hooks: + - id: zerolog-ban-msgf + - id: zerolog-use-stringer + - id: prevent-literal-http-methods diff --git a/README.md b/README.md index ae68f36..cfaca54 100644 --- a/README.md +++ b/README.md @@ -98,4 +98,4 @@ You can get notifications when a new rageshake arrives on the server. Currently this tool supports pushing notifications as GitHub issues in a repo, through a Slack webhook or by email, cf sample config file for how to -configure them. \ No newline at end of file +configure them. diff --git a/logserver.go b/logserver.go index 5e75173..1793648 100644 --- a/logserver.go +++ b/logserver.go @@ -51,7 +51,7 @@ func (f *logServer) ServeHTTP(w http.ResponseWriter, r *http.Request) { // the check for '..' is a sanity-check because my understanding of `path.Clean` is that it should never return // a value including '..' for input starting with '/'. It's taken from the code for http.ServeFile // (https://golang.org/src/net/http/fs.go#L637). - if containsDotDot(upath) || strings.Contains(upath, "\x00") || (filepath.Separator != '/' && strings.IndexRune(upath, filepath.Separator) >= 0) { + if containsDotDot(upath) || strings.Contains(upath, "\x00") || (filepath.Separator != '/' && strings.ContainsRune(upath, filepath.Separator)) { http.Error(w, "invalid URL path", http.StatusBadRequest) return } @@ -137,14 +137,14 @@ func serveGzippedFile(w http.ResponseWriter, r *http.Request, path string, size } if acceptsGzip { - serveGzip(w, r, path, size) + serveGzip(w, path, size) } else { - serveUngzipped(w, r, path) + serveUngzipped(w, path) } } // serveGzip serves a gzipped file with gzip content-encoding -func serveGzip(w http.ResponseWriter, r *http.Request, path string, size int64) { +func serveGzip(w http.ResponseWriter, path string, size int64) { f, err := os.Open(path) if err != nil { msg, code := toHTTPError(err) @@ -161,7 +161,7 @@ func serveGzip(w http.ResponseWriter, r *http.Request, path string, size int64) } // serveUngzipped ungzips a gzipped file and serves it -func serveUngzipped(w http.ResponseWriter, r *http.Request, path string) { +func serveUngzipped(w http.ResponseWriter, path string) { f, err := os.Open(path) if err != nil { msg, code := toHTTPError(err) diff --git a/main.go b/main.go index 9580b2c..f9925b2 100644 --- a/main.go +++ b/main.go @@ -21,12 +21,10 @@ import ( "flag" "fmt" "log" - "math/rand" "net" "net/http" "os" "strings" - "time" "gopkg.in/yaml.v2" ) @@ -94,7 +92,6 @@ func main() { } log.Printf("Using %s/listing as public URI", apiPrefix) - rand.Seed(time.Now().UnixNano()) http.Handle("/api/submit", &submitServer{apiPrefix: apiPrefix, cfg: cfg}) // Make sure bugs directory exists diff --git a/submit.go b/submit.go index 611268b..656d960 100644 --- a/submit.go +++ b/submit.go @@ -85,7 +85,7 @@ type parsedPayload struct { VerifiedDeviceID string `json:"verified_device_id"` } -func (p parsedPayload) WriteTo(out io.Writer) { +func (p parsedPayload) WriteToBuffer(out *bytes.Buffer) { fmt.Fprintf( out, "%s\n\nNumber of logs: %d\nApplication: %s\n", @@ -129,7 +129,7 @@ func (s *submitServer) ServeHTTP(w http.ResponseWriter, req *http.Request) { defer req.Body.Close() defer io.Copy(io.Discard, req.Body) - if req.Method != "POST" && req.Method != "OPTIONS" { + if req.Method != http.MethodPost && req.Method != http.MethodOptions { respond(405, w) return } @@ -138,7 +138,7 @@ func (s *submitServer) ServeHTTP(w http.ResponseWriter, req *http.Request) { w.Header().Set("Access-Control-Allow-Origin", "*") w.Header().Set("Access-Control-Allow-Methods", "POST, OPTIONS") w.Header().Set("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept, Authorization") - if req.Method == "OPTIONS" { + if req.Method == http.MethodOptions { respond(200, w) return } @@ -352,14 +352,14 @@ func (s *submitServer) parseRequest(w http.ResponseWriter, req *http.Request, re var p *parsedPayload if isMultipart(req.Header.Get("Content-Type")) { - p, err = parseMultipartRequest(w, req, reportDir) + p, err = parseMultipartRequest(req, reportDir) if err != nil { log.Println("Error parsing multipart data:", err) http.Error(w, "Bad multipart data", http.StatusBadRequest) return nil } } else { - p, err = parseJSONRequest(w, req, reportDir) + p, err = parseJSONRequest(req, reportDir) if err != nil { log.Println("Error parsing JSON body", err) http.Error(w, fmt.Sprintf("Could not decode payload: %s", err.Error()), http.StatusBadRequest) @@ -412,7 +412,7 @@ func (s *submitServer) parseRequest(w http.ResponseWriter, req *http.Request, re return p } -func parseJSONRequest(w http.ResponseWriter, req *http.Request, reportDir string) (*parsedPayload, error) { +func parseJSONRequest(req *http.Request, reportDir string) (*parsedPayload, error) { var p jsonPayload if err := json.NewDecoder(req.Body).Decode(&p); err != nil { return nil, err @@ -473,7 +473,7 @@ func parseJSONRequest(w http.ResponseWriter, req *http.Request, reportDir string return &parsed, nil } -func parseMultipartRequest(w http.ResponseWriter, req *http.Request, reportDir string) (*parsedPayload, error) { +func parseMultipartRequest(req *http.Request, reportDir string) (*parsedPayload, error) { rdr, err := req.MultipartReader() if err != nil { return nil, err @@ -483,7 +483,7 @@ func parseMultipartRequest(w http.ResponseWriter, req *http.Request, reportDir s Data: make(map[string]string), } - for true { + for { part, err := rdr.NextPart() if err == io.EOF { break @@ -560,22 +560,23 @@ func parseFormPart(part *multipart.Part, p *parsedPayload, reportDir string) err // formPartToPayload updates the relevant part of *p from a name/value pair // read from the form data. func formPartToPayload(field, data string, p *parsedPayload) { - if field == "text" { + switch field { + case "text": p.UserText = data - } else if field == "app" { + case "app": p.AppName = data - } else if field == "version" { + case "version": p.Data["Version"] = data - } else if field == "user_agent" { + case "user_agent": p.Data["User-Agent"] = data - } else if field == "label" { + case "label": p.Labels = append(p.Labels, data) if len(p.Data[field]) == 0 { p.Data[field] = data } else { p.Data[field] = fmt.Sprintf("%s, %s", p.Data[field], data) } - } else { + default: p.Data[field] = data } } @@ -597,7 +598,7 @@ var filenameRegexp = regexp.MustCompile(`^[a-zA-Z0-9_-]+\.(jpg|jpeg|png|heic|gif // Returns the leafname of the saved file. func saveFormPart(leafName string, reader io.Reader, reportDir string) (string, error) { if !filenameRegexp.MatchString(leafName) { - return "", fmt.Errorf("Invalid upload filename") + return "", fmt.Errorf("invalid upload filename") } fullName := filepath.Join(reportDir, leafName) @@ -674,7 +675,7 @@ func (s *submitServer) saveReportBackground(p parsedPayload, reportDir, listingU func (s *submitServer) saveReport(p parsedPayload, reportDir, listingURL string) error { var summaryBuf bytes.Buffer - p.WriteTo(&summaryBuf) + p.WriteToBuffer(&summaryBuf) if err := gzipAndSave(summaryBuf.Bytes(), reportDir, "details.log.gz"); err != nil { return err }