diff --git a/.github/workflows/golangci-lint.yml b/.github/workflows/golangci-lint.yml index a9d7314..f13ac6d 100644 --- a/.github/workflows/golangci-lint.yml +++ b/.github/workflows/golangci-lint.yml @@ -16,23 +16,5 @@ jobs: - name: golangci-lint uses: golangci/golangci-lint-action@v6 with: - # Optional: version of golangci-lint to use in form of v1.2 or v1.2.3 or `latest` to use the latest version version: latest - - # Optional: working directory, useful for monorepos - # working-directory: somedir - - # Optional: golangci-lint command line arguments. args: -E bodyclose,misspell,gosec,goconst,gci,errorlint - - # Optional: show only new issues if it's a pull request. The default value is `false`. - # only-new-issues: true - - # Optional: if set to true then the action will use pre-installed Go. - # skip-go-installation: true - - # Optional: if set to true then the action don't cache or restore ~/go/pkg. - skip-pkg-cache: true - - # Optional: if set to true then the action don't cache or restore ~/.cache/go-build. - skip-build-cache: true diff --git a/extract.go b/extract.go index 89b60b0..9a34eff 100644 --- a/extract.go +++ b/extract.go @@ -4,7 +4,7 @@ import ( "bufio" "bytes" "fmt" - "io/ioutil" + "io" "net/http" "net/url" "os" @@ -33,18 +33,19 @@ func ExtractPostmortems(loc string, dir string) error { var data []byte if isURL(loc) { + // #nosec resp, err := http.Get(loc) if err != nil { return fmt.Errorf("could not get %q: %w", loc, err) } defer resp.Body.Close() - data, err = ioutil.ReadAll(resp.Body) + data, err = io.ReadAll(resp.Body) if err != nil { return fmt.Errorf("could not read response body: %w", err) } } else if isFile(loc) { - data, err = ioutil.ReadFile(loc) + data, err = os.ReadFile(loc) if err != nil { return fmt.Errorf("error opening file %q: %w", loc, err) } diff --git a/postmortem.go b/postmortem.go index 3ddb35e..5ebd200 100644 --- a/postmortem.go +++ b/postmortem.go @@ -5,7 +5,6 @@ import ( "encoding/json" "fmt" "io" - "io/ioutil" "os" "path/filepath" "text/template" @@ -118,7 +117,7 @@ func GenerateJSON(d string) error { return err } - err = ioutil.WriteFile(fp, j, 0644) + err = os.WriteFile(fp, j, 0600) if err != nil { return err } @@ -149,7 +148,7 @@ func GenerateJSON(d string) error { if err != nil { return err } - err = ioutil.WriteFile(fp, j, 0644) + err = os.WriteFile(fp, j, 0600) if err != nil { return err } @@ -189,7 +188,7 @@ func (pm *Postmortem) Save(dir string) error { } // Write postmortem data from memory to file. - if err := ioutil.WriteFile(filepath.Join(dir, pm.UUID+".md"), data.Bytes(), 0644); err != nil { + if err := os.WriteFile(filepath.Join(dir, pm.UUID+".md"), data.Bytes(), 0600); err != nil { return fmt.Errorf("error writing file: %w", err) } diff --git a/server/handlers.go b/server/handlers.go index 217dcfc..67e02ce 100644 --- a/server/handlers.go +++ b/server/handlers.go @@ -3,10 +3,10 @@ package server import ( "compress/flate" "fmt" - "io/ioutil" "net/http" "os" "path/filepath" + "strings" "text/template" "github.com/go-chi/chi/v5" @@ -76,7 +76,7 @@ func LoadPostmortem(dir, filename string) (*postmortems.Postmortem, error) { // LoadPostmortems parses the postmortem files // and returns a slice with their content. func LoadPostmortems(dir string) ([]*postmortems.Postmortem, error) { - files, err := ioutil.ReadDir(dir) + files, err := os.ReadDir(dir) if err != nil { return nil, fmt.Errorf("error opening data folder: %w", err) } @@ -203,9 +203,15 @@ func postmortemPageHandler(w http.ResponseWriter, r *http.Request) { func postmortemJSONPageHandler(w http.ResponseWriter, r *http.Request) { pmID := chi.URLParam(r, "id") + // Validate pmID to ensure it does not contain path separators or parent directory references + if strings.Contains(pmID, "/") || strings.Contains(pmID, "\\") || strings.Contains(pmID, "..") { + http.Error(w, "Invalid postmortem ID", http.StatusBadRequest) + return + } + jsonPM := pmID + ".json" - data, err := ioutil.ReadFile(filepath.Join("output/", jsonPM)) + data, err := os.ReadFile(filepath.Join("output/", jsonPM)) if err != nil { log.Errorw("load postmortem json", "pmid", pmID, zap.Error(err)) http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound) diff --git a/tool/main.go b/tool/main.go index badd0a5..83df45a 100644 --- a/tool/main.go +++ b/tool/main.go @@ -1,6 +1,7 @@ package main import ( + "errors" "flag" "fmt" "net/http" @@ -81,7 +82,12 @@ func Serve() error { log.Infow("Starting up", "host", fmt.Sprintf("http://localhost:%s", port)) - return http.ListenAndServe(":"+port, router) + srv := &http.Server{ + Addr: ":" + port, + Handler: router, + } + + return srv.ListenAndServe() } func main() { @@ -135,7 +141,7 @@ func newPostmortem(dir string) error { pm := postmortems.New() err := survey.Ask(qs, pm) - if err == terminal.InterruptErr { + if errors.Is(err, terminal.InterruptErr) { fmt.Println("interrupted") os.Exit(0) } else if err != nil { @@ -153,8 +159,7 @@ func IsURL() survey.Validator { return fmt.Errorf("could not decode string") } - _, err := url.Parse(str) - if err != nil { + if _, err := url.Parse(str); err != nil { return fmt.Errorf("value is not a valid URL: %w", err) }