diff --git a/pkg/cwhub/dataset.go b/pkg/cwhub/dataset.go index c900752b8..4612f3576 100644 --- a/pkg/cwhub/dataset.go +++ b/pkg/cwhub/dataset.go @@ -6,6 +6,7 @@ import ( "io" "net/http" "os" + "path/filepath" "time" "github.com/sirupsen/logrus" @@ -31,19 +32,32 @@ func downloadFile(url string, destPath string) error { return fmt.Errorf("bad http code %d for %s", resp.StatusCode, url) } - file, err := os.Create(destPath) + tmpFile, err := os.CreateTemp(filepath.Dir(destPath), filepath.Base(destPath)+".*.tmp") if err != nil { return err } - defer file.Close() + + tmpFileName := tmpFile.Name() + defer func() { + tmpFile.Close() + os.Remove(tmpFileName) + }() // avoid reading the whole file in memory - _, err = io.Copy(file, resp.Body) + _, err = io.Copy(tmpFile, resp.Body) if err != nil { return err } - if err = file.Sync(); err != nil { + if err = tmpFile.Sync(); err != nil { + return err + } + + if err = tmpFile.Close(); err != nil { + return err + } + + if err = os.Rename(tmpFileName, destPath); err != nil { return err } diff --git a/pkg/cwhub/dataset_test.go b/pkg/cwhub/dataset_test.go index f23f48782..93d3e3bf0 100644 --- a/pkg/cwhub/dataset_test.go +++ b/pkg/cwhub/dataset_test.go @@ -16,7 +16,7 @@ func TestDownloadFile(t *testing.T) { httpmock.Activate() defer httpmock.DeactivateAndReset() - //OK + // OK httpmock.RegisterResponder( "GET", "https://example.com/xx", @@ -36,15 +36,15 @@ func TestDownloadFile(t *testing.T) { assert.Equal(t, "example content oneoneone", string(content)) require.NoError(t, err) - //bad uri + // bad uri err = downloadFile("https://zz.com", examplePath) require.Error(t, err) - //404 + // 404 err = downloadFile("https://example.com/x", examplePath) require.Error(t, err) - //bad target + // bad target err = downloadFile("https://example.com/xx", "") require.Error(t, err) } diff --git a/pkg/cwhub/errors.go b/pkg/cwhub/errors.go index 789c2eced..f1e779b54 100644 --- a/pkg/cwhub/errors.go +++ b/pkg/cwhub/errors.go @@ -6,7 +6,7 @@ import ( ) var ( - // ErrNilRemoteHub is returned when the remote hub configuration is not provided to the NewHub constructor. + // ErrNilRemoteHub is returned when trying to download with a local-only configuration. ErrNilRemoteHub = errors.New("remote hub configuration is not provided. Please report this issue to the developers") ) diff --git a/pkg/cwhub/hub.go b/pkg/cwhub/hub.go index 21a19bc45..44e24020d 100644 --- a/pkg/cwhub/hub.go +++ b/pkg/cwhub/hub.go @@ -3,6 +3,7 @@ package cwhub import ( "bytes" "encoding/json" + "errors" "fmt" "io" "os" @@ -34,7 +35,7 @@ func (h *Hub) GetDataDir() string { // All download operations (including updateIndex) return ErrNilRemoteHub if the remote configuration is not set. func NewHub(local *csconfig.LocalHubCfg, remote *RemoteHubCfg, updateIndex bool, logger *logrus.Logger) (*Hub, error) { if local == nil { - return nil, fmt.Errorf("no hub configuration found") + return nil, errors.New("no hub configuration found") } if logger == nil { diff --git a/pkg/cwhub/sync.go b/pkg/cwhub/sync.go index 8ce91dc21..cb7bf3786 100644 --- a/pkg/cwhub/sync.go +++ b/pkg/cwhub/sync.go @@ -77,9 +77,9 @@ func (h *Hub) getItemFileInfo(path string, logger *logrus.Logger) (*itemFileInfo if strings.HasPrefix(path, hubDir) { logger.Tracef("in hub dir") - //.../hub/parsers/s00-raw/crowdsec/skip-pretag.yaml - //.../hub/scenarios/crowdsec/ssh_bf.yaml - //.../hub/profiles/crowdsec/linux.yaml + // .../hub/parsers/s00-raw/crowdsec/skip-pretag.yaml + // .../hub/scenarios/crowdsec/ssh_bf.yaml + // .../hub/profiles/crowdsec/linux.yaml if len(subs) < 4 { return nil, fmt.Errorf("path is too short: %s (%d)", path, len(subs)) } @@ -93,13 +93,14 @@ func (h *Hub) getItemFileInfo(path string, logger *logrus.Logger) (*itemFileInfo } } else if strings.HasPrefix(path, installDir) { // we're in install /etc/crowdsec//... logger.Tracef("in install dir") + if len(subs) < 3 { return nil, fmt.Errorf("path is too short: %s (%d)", path, len(subs)) } - ///.../config/parser/stage/file.yaml - ///.../config/postoverflow/stage/file.yaml - ///.../config/scenarios/scenar.yaml - ///.../config/collections/linux.yaml //file is empty + // .../config/parser/stage/file.yaml + // .../config/postoverflow/stage/file.yaml + // .../config/scenarios/scenar.yaml + // .../config/collections/linux.yaml //file is empty ret = &itemFileInfo{ inhub: false, fname: subs[len(subs)-1],