diff --git a/Makefile b/Makefile index 13bf251169..37ba1b5597 100644 --- a/Makefile +++ b/Makefile @@ -89,11 +89,8 @@ $(BINARY_OUTPUT_FOLDER)/sha256sums.txt.asc: $(BINARY_OUTPUT_FOLDER)/release.json: ./release-scripts/release.json.sh -# --commit-path is forwarded to `git log `. -# We're using this to remove CLIv2 changes in v1's changelogs. -# :(exclude) syntax: https://git-scm.com/docs/gitglossary.html#Documentation/gitglossary.txt-exclude -# Release notes uses version from package.json so we need to prepack beforehand. -$(BINARY_OUTPUT_FOLDER)/RELEASE_NOTES.md: $(BINARY_OUTPUT_FOLDER) $(BINARY_OUTPUT_FOLDER)/version +# This target must not have any dependency to support checked in Release Notes that don't get overridden. +$(BINARY_OUTPUT_FOLDER)/RELEASE_NOTES.md: npx conventional-changelog-cli -l -r 1 -n ./release-scripts/conventional-changelog-cli-config.js > $(BINARY_OUTPUT_FOLDER)/RELEASE_NOTES.md $(BINARY_OUTPUT_FOLDER)/fips/RELEASE_NOTES.md: $(BINARY_OUTPUT_FOLDER)/RELEASE_NOTES.md $(BINARY_OUTPUT_FOLDER)/fips diff --git a/cliv2/cmd/cliv2/main.go b/cliv2/cmd/cliv2/main.go index 9ef7bee4fa..67eca31c9f 100644 --- a/cliv2/cmd/cliv2/main.go +++ b/cliv2/cmd/cliv2/main.go @@ -447,7 +447,9 @@ func displayError(err error, userInterface ui.UserInterface, config configuratio } uiError := userInterface.OutputError(err) - globalLogger.Err(uiError).Msg("ui failed show error") + if uiError != nil { + globalLogger.Err(uiError).Msg("ui failed to show error") + } } } } @@ -573,6 +575,9 @@ func MainWithErrorCode() int { cliAnalytics.GetInstrumentation().SetStatus(analytics.Failure) } + // cleanup resources in use + basic_workflows.Cleanup() + return exitCode } diff --git a/cliv2/go.mod b/cliv2/go.mod index a4eeafb675..3aea9cd7c0 100644 --- a/cliv2/go.mod +++ b/cliv2/go.mod @@ -12,10 +12,10 @@ require ( github.com/rs/zerolog v1.32.0 github.com/snyk/cli-extension-dep-graph v0.0.0-20240426125928-8d56ac52821e github.com/snyk/cli-extension-iac-rules v0.0.0-20240422133948-ae17a4306672 - github.com/snyk/cli-extension-sbom v0.0.0-20240523084359-a2830fadb001 + github.com/snyk/cli-extension-sbom v0.0.0-20240619142341-3b3fe79e862c github.com/snyk/container-cli v0.0.0-20240322120441-6d9b9482f9b1 github.com/snyk/error-catalog-golang-public v0.0.0-20240605115201-8461850930e6 - github.com/snyk/go-application-framework v0.0.0-20240614132431-77ccf7f7ec6b + github.com/snyk/go-application-framework v0.0.0-20240624121329-845d8c3cb5a2 github.com/snyk/go-httpauth v0.0.0-20240307114523-1f5ea3f55c65 github.com/snyk/snyk-iac-capture v0.6.5 github.com/snyk/snyk-ls v0.0.0-20240626071017-1de2c7f68e09 diff --git a/cliv2/go.sum b/cliv2/go.sum index e941035898..c86af4c27b 100644 --- a/cliv2/go.sum +++ b/cliv2/go.sum @@ -736,16 +736,16 @@ github.com/snyk/cli-extension-dep-graph v0.0.0-20240426125928-8d56ac52821e h1:j1 github.com/snyk/cli-extension-dep-graph v0.0.0-20240426125928-8d56ac52821e/go.mod h1:QF3v8HBpOpyudYNCuR8LqfULutO76c91sBdLzD+pBJU= github.com/snyk/cli-extension-iac-rules v0.0.0-20240422133948-ae17a4306672 h1:AkLej8Lk//vFex1fiygSYFrQTUd0xP+GyRbsI+m2kwQ= github.com/snyk/cli-extension-iac-rules v0.0.0-20240422133948-ae17a4306672/go.mod h1:2vKTUsW73sVbDcyD19iNLfN0so2GSu9BE3k/fqG0mjA= -github.com/snyk/cli-extension-sbom v0.0.0-20240523084359-a2830fadb001 h1:EP9cL93+Lqw/wP/C80Sx+pyMYrqQY2NiuLDrad0lZ9w= -github.com/snyk/cli-extension-sbom v0.0.0-20240523084359-a2830fadb001/go.mod h1:lqmQT+QdzLdfi7qsqIH4qvCsSWu+P09GDFwQcmFfC0g= +github.com/snyk/cli-extension-sbom v0.0.0-20240619142341-3b3fe79e862c h1:nqFKbstv5PqnRKP4CgW2/Egmbjq6Ej89GzgYFaj+cZ0= +github.com/snyk/cli-extension-sbom v0.0.0-20240619142341-3b3fe79e862c/go.mod h1:lqmQT+QdzLdfi7qsqIH4qvCsSWu+P09GDFwQcmFfC0g= github.com/snyk/code-client-go v1.8.0 h1:6H883KAn7ybiSIxhvL2QR9yEyHgAwA2+9WVHMDNEKa8= github.com/snyk/code-client-go v1.8.0/go.mod h1:orU911flV1kJQOlxxx0InUQkAfpBrcERsb2olfnlI8s= github.com/snyk/container-cli v0.0.0-20240322120441-6d9b9482f9b1 h1:9RKY9NdX5DrJAoVXDP0JiqrXT+4Nb9NH8pjEcA0NsLA= github.com/snyk/container-cli v0.0.0-20240322120441-6d9b9482f9b1/go.mod h1:38w+dcAQp9eG3P5t2eNS9eG0reut10AeJjLv5lJ5lpM= github.com/snyk/error-catalog-golang-public v0.0.0-20240605115201-8461850930e6 h1:0t2lDRZY9zn/zgmY9sbRZ4WZFFR+7lIV/4+CMPUhJOs= github.com/snyk/error-catalog-golang-public v0.0.0-20240605115201-8461850930e6/go.mod h1:Ytttq7Pw4vOCu9NtRQaOeDU2dhBYUyNBe6kX4+nIIQ4= -github.com/snyk/go-application-framework v0.0.0-20240614132431-77ccf7f7ec6b h1:dnbbyMpHnd6cAOYfZ2jM5LZ1LU8g8LdQ+6GajWXqWpg= -github.com/snyk/go-application-framework v0.0.0-20240614132431-77ccf7f7ec6b/go.mod h1:v76hiANT35FweRFQEosU5KQ8q27BviioeJkWOh00ghY= +github.com/snyk/go-application-framework v0.0.0-20240624121329-845d8c3cb5a2 h1:i0/X/lV0TFZBRZfrgYmPF+OAIipHjeMlXTVIcFv5A+0= +github.com/snyk/go-application-framework v0.0.0-20240624121329-845d8c3cb5a2/go.mod h1:gz3PN/OfEBbtB4VxbnV33XipM8MjBcVszPJeOhCu2DU= github.com/snyk/go-httpauth v0.0.0-20240307114523-1f5ea3f55c65 h1:CEQuYv0Go6MEyRCD3YjLYM2u3Oxkx8GpCpFBd4rUTUk= github.com/snyk/go-httpauth v0.0.0-20240307114523-1f5ea3f55c65/go.mod h1:88KbbvGYlmLgee4OcQ19yr0bNpXpOr2kciOthaSzCAg= github.com/snyk/policy-engine v0.30.11 h1:wUy5LMar2vccMbNM62MSBRdjAQAhAbIm7aNXXO+g2tk= diff --git a/cliv2/internal/cliv2/cliv2.go b/cliv2/internal/cliv2/cliv2.go index a965acf323..020e9cbddf 100644 --- a/cliv2/internal/cliv2/cliv2.go +++ b/cliv2/internal/cliv2/cliv2.go @@ -13,9 +13,12 @@ import ( "os" "os/exec" "path" + "path/filepath" "regexp" "slices" + "strconv" "strings" + "syscall" "time" "github.com/gofrs/flock" @@ -135,6 +138,16 @@ func (c *CLI) Init() (err error) { } func (c *CLI) ClearCache() error { + err := c.clearVersionFolders() + if err != nil { + return err + } + + err = c.clearTemporaryProcessFolders() + return err +} + +func (c *CLI) clearVersionFolders() error { // Get files in directory fileInfo, err := os.ReadDir(c.CacheDirectory) if err != nil { @@ -163,6 +176,58 @@ func (c *CLI) ClearCache() error { return nil } +func (c *CLI) clearTemporaryProcessFolders() error { + // clean up the tmp dir of the current version + maxConsecutiveDeletes := 5 + deleteCount := 0 + tempDir := filepath.Dir(c.GetTempDir()) + fileInfo, err := os.ReadDir(tempDir) + if err != nil { + return err + } + + // cleanup tmp files related to a non-existing process + processTempPattern := regexp.MustCompile("pid([0-9]*)") + for _, file := range fileInfo { + currentPath := path.Join(tempDir, file.Name()) + matches := processTempPattern.FindStringSubmatch(file.Name()) + if len(matches) == 2 { + processFound := true + pid, localError := strconv.Atoi(matches[1]) + if localError != nil { + continue + } + + p, localError := os.FindProcess(pid) + if localError != nil { + processFound = false + } + + if p != nil { + localError = p.Signal(syscall.Signal(0)) + if localError != nil { + processFound = false + } + } + + if !processFound { + deleteCount++ + err = os.RemoveAll(currentPath) + if err != nil { + c.DebugLogger.Println("Error deleting temporary files: ", currentPath) + } + } + } + + // Stop the loop after 5 deletions to not create too much overhead + if deleteCount == maxConsecutiveDeletes { + break + } + } + + return nil +} + func (c *CLI) AppendEnvironmentVariables(env []string) { c.env = append(c.env, env...) } @@ -210,6 +275,10 @@ func (c *CLI) GetBinaryLocation() string { return c.v1BinaryLocation } +func (c *CLI) GetTempDir() string { + return local_utils.GetTemporaryDirectory(c.CacheDirectory, cliv1.CLIV1Version()) +} + func (c *CLI) printVersion() { _, _ = fmt.Fprintln(c.stdout, GetFullVersion()) } diff --git a/cliv2/internal/cliv2/cliv2_test.go b/cliv2/internal/cliv2/cliv2_test.go index 875263c10c..fd752b882e 100644 --- a/cliv2/internal/cliv2/cliv2_test.go +++ b/cliv2/internal/cliv2/cliv2_test.go @@ -8,6 +8,7 @@ import ( "os" "os/exec" "path" + "path/filepath" "runtime" "sort" "testing" @@ -410,11 +411,16 @@ func Test_clearCache(t *testing.T) { lockfile := path.Join(cli.CacheDirectory, "v1.914.0.lock") randomFile := path.Join(versionNoV, "filename") currentVersion := cli.GetBinaryLocation() + tempDir := filepath.Dir(cli.GetTempDir()) + oldProcessTempDir := path.Join(tempDir, "pid123") + oldProcessTempDirFile := path.Join(oldProcessTempDir, "bla.txt") - _ = os.Mkdir(versionWithV, 0755) - _ = os.Mkdir(versionNoV, 0755) - _ = os.WriteFile(randomFile, []byte("Writing some strings"), 0666) - _ = os.WriteFile(lockfile, []byte("Writing some strings"), 0666) + assert.NoError(t, os.Mkdir(versionWithV, 0755)) + assert.NoError(t, os.Mkdir(versionNoV, 0755)) + assert.NoError(t, os.Mkdir(oldProcessTempDir, 0755)) + assert.NoError(t, os.WriteFile(randomFile, []byte("Writing some strings"), 0666)) + assert.NoError(t, os.WriteFile(lockfile, []byte("Writing some strings"), 0666)) + assert.NoError(t, os.WriteFile(oldProcessTempDirFile, []byte("Writing some strings"), 0666)) // clear cache err := cli.ClearCache() @@ -424,9 +430,11 @@ func Test_clearCache(t *testing.T) { assert.NoDirExists(t, versionWithV) assert.NoDirExists(t, versionNoV) assert.NoFileExists(t, randomFile) + assert.NoFileExists(t, oldProcessTempDirFile) // check if directories that need to exist still exist assert.FileExists(t, currentVersion) assert.FileExists(t, lockfile) + assert.DirExists(t, cli.GetTempDir()) } func Test_clearCacheBigCache(t *testing.T) { diff --git a/cliv2/internal/utils/directories.go b/cliv2/internal/utils/directories.go index 6764d20735..bc6a5a598c 100644 --- a/cliv2/internal/utils/directories.go +++ b/cliv2/internal/utils/directories.go @@ -1,6 +1,7 @@ package utils import ( + "fmt" "os" "path" @@ -15,7 +16,8 @@ const CACHEDIR_PERMISSION = 0755 // |- Temp directory (example: /Users/username/Library/Caches/snyk/snyk-cli/1.1075.0/tmp/) func GetTemporaryDirectory(baseCacheDirectory string, versionNumber string) string { - return path.Join(GetVersionCacheDirectory(baseCacheDirectory, versionNumber), "tmp") + pid := os.Getpid() + return path.Join(GetVersionCacheDirectory(baseCacheDirectory, versionNumber), "tmp", fmt.Sprintf("pid%d", pid)) } func GetVersionCacheDirectory(baseCacheDirectory string, versionNumber string) string { diff --git a/cliv2/pkg/basic_workflows/legacycli.go b/cliv2/pkg/basic_workflows/legacycli.go index 3ed93af28f..dc3b71a440 100644 --- a/cliv2/pkg/basic_workflows/legacycli.go +++ b/cliv2/pkg/basic_workflows/legacycli.go @@ -5,8 +5,10 @@ import ( "bytes" "net/http" "os" + "sync" "github.com/pkg/errors" + "github.com/rs/zerolog" "github.com/snyk/go-application-framework/pkg/auth" "github.com/snyk/go-application-framework/pkg/configuration" "github.com/snyk/go-application-framework/pkg/logging" @@ -23,6 +25,9 @@ import ( var WORKFLOWID_LEGACY_CLI workflow.Identifier = workflow.NewWorkflowIdentifier("legacycli") var DATATYPEID_LEGACY_CLI_STDOUT workflow.Identifier = workflow.NewTypeIdentifier(WORKFLOWID_LEGACY_CLI, "stdout") +var proxySingleton *proxy.WrapperProxy +var proxyMutex sync.Mutex + const ( PROXY_NOAUTH string = "proxy-noauth" ) @@ -132,12 +137,10 @@ func legacycliWorkflow( cli.SetIoStreams(os.Stdin, os.Stdout, scrubbedStderr) } - // init proxy object - wrapperProxy, err := proxy.NewWrapperProxy(config, cliv2.GetFullVersion(), debugLogger) + wrapperProxy, err := getProxyInstance(config, debugLogger) if err != nil { - return output, errors.Wrap(err, "Failed to create proxy!") + return output, err } - defer wrapperProxy.Close() wrapperProxy.SetUpstreamProxyAuthentication(proxyAuthenticationMechanism) @@ -175,3 +178,28 @@ func legacycliWorkflow( return output, err } + +func Cleanup() { + proxyMutex.Lock() + defer proxyMutex.Unlock() + if proxySingleton != nil { + proxySingleton.Close() + proxySingleton = nil + } +} + +func getProxyInstance(config configuration.Configuration, debugLogger *zerolog.Logger) (*proxy.WrapperProxy, error) { + var err error + proxyMutex.Lock() + defer proxyMutex.Unlock() + + if proxySingleton == nil { + // init proxy object + proxySingleton, err = proxy.NewWrapperProxy(config, cliv2.GetFullVersion(), debugLogger) + if err != nil { + return nil, errors.Wrap(err, "Failed to create proxy!") + } + } + + return proxySingleton, nil +} diff --git a/release-scripts/prepare-release.sh b/release-scripts/prepare-release.sh index e1487984d4..307cff0bc4 100755 --- a/release-scripts/prepare-release.sh +++ b/release-scripts/prepare-release.sh @@ -22,7 +22,7 @@ if [ -f binary-releases/RELEASE_NOTES.md ]; then fi # Generate the release notes baseline from the commits -make binary-releases/RELEASE_NOTES.md format +make binary-releases binary-releases/version binary-releases/RELEASE_NOTES.md format # if the release notes are generated locally, the version contains something like X.Y.Z-dev.hash # the replacement below ensures that the version in the RELEASE_NOTES.md is X.Y.Z diff --git a/test/jest/acceptance/snyk-sbom-test/all-projects.spec.ts b/test/jest/acceptance/snyk-sbom-test/all-projects.spec.ts index 85dd16e39d..3143a27099 100644 --- a/test/jest/acceptance/snyk-sbom-test/all-projects.spec.ts +++ b/test/jest/acceptance/snyk-sbom-test/all-projects.spec.ts @@ -103,6 +103,8 @@ describe('snyk sbom test (mocked server only)', () => { ); expect(stdout).toContain('"version":"3.0.4",'); expect(stdout).toContain('"name":"minimatch"'); + expect(stdout).toContain('"CWE":["CWE-1333"]'); + expect(stdout).toContain('"semver":{"vulnerable":["3.0.4"]}'); expect(code).toEqual(1);