Skip to content

Commit 00971dc

Browse files
authored
Merge pull request #6744 from snyk/release-candidate
chore: Release 1.1304.1
2 parents 28558dc + 1f72612 commit 00971dc

12 files changed

Lines changed: 661 additions & 269 deletions

File tree

binary-releases/RELEASE_NOTES.md

Lines changed: 5 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,10 @@
1-
## [1.1304.0](https://github.com/snyk/snyk/compare/v1.1303.2...v1.1304.0) (2026-04-09)
1+
## [1.1304.1](https://github.com/snyk/snyk/compare/v1.1304.0...v1.1304.1) (2026-04-27)
22

3-
The Snyk CLI is being deployed to different deployment channels, users can select the stability level according to their needs. For details please see [this documentation](https://docs.snyk.io/snyk-cli/releases-and-channels-for-the-snyk-cli)
4-
5-
### Features
6-
7-
* **aibom**: Introduces the `snyk aibom test` command. ([2978044](https://github.com/snyk/snyk/commit/297804447be12f47b33f3ed9630a1db0a6994d70))
8-
* **test, monitor, sbom**: Introduce `--maven-skip-wrapper` flag to force the use of a globally installed `mvn` command. ([0ee90ca](https://github.com/snyk/snyk/commit/0ee90caeec58d9a04843dfa9f64b35bc10f543f5), [ff31066](https://github.com/snyk/snyk/commit/ff31066d7f327f4ea25b758108bb62abed3b286a))
9-
* **general**: Introduce explicit configuration for network retry `max-attempts`. ([1fbdf38](https://github.com/snyk/snyk/commit/1fbdf38647c30e29bfc6d6c3612241be9c5b90ca))
10-
* **container**: Add deprecation warnings for `-shaded-jars-depth` and non-numeric values for `--nested-jars-depth`. ([321b6f5](https://github.com/snyk/snyk/commit/321b6f5516ae4f41dfd184911c3bd117c595ea68))
11-
* **container**: Extend support for java runtime binary scanning ([b60473a](https://github.com/snyk/snyk/commit/b60473a20ffa55ea82c558c88783d4bcbc2dc54d))
12-
* **mcp**: Improves auto-enable behavior for Snyk Code, promotes package health checks to stable. ([5f5898f](https://github.com/snyk/snyk/commit/5f5898f531ac84c7ee63feb7ab150fa1949373e7))
13-
* **redteam**: Adds a vulnerability summary to scanned output. ([52eaf5a](https://github.com/snyk/snyk/commit/52eaf5afdc93c0d86324972387df8cecba0c6d48))
14-
* **redteam**: Add `--json` flag support for list commands, `exhaustive` and `eager` modes. ([e962c4d](https://github.com/snyk/snyk/commit/e962c4d71cf4e077c7b262c91d69bcf40022d003))
153

4+
The Snyk CLI is being deployed to different deployment channels, users can select the stability level according to their needs. For details please see [this documentation](https://docs.snyk.io/snyk-cli/releases-and-channels-for-the-snyk-cli)
165

176
### Bug Fixes
187

19-
* **general**: Fix printing JSON output on stdout when only `--json-file-output` is specified. ([32f65f0](https://github.com/snyk/snyk/commit/32f65f099b716a216cbe0ad2ce9d8fef4be81704))
20-
* **test**: Fixes an issue where no files were uploaded when using `--skip-unresolved`. ([71ca761](https://github.com/snyk/snyk/commit/71ca761b28ce3b4122c6778791433e46f82dcd29))
21-
* **test**: Prevents scan failures when Maven builds succeed with non-fatal errors. ([b30db97](https://github.com/snyk/snyk/commit/b30db97978ab74e4bb66c1a6095019fae9259939))
22-
* **test**: Fixes Go PackageURL generation and import path normalization for projects using `replace` directives. ([7c7a366](https://github.com/snyk/snyk/commit/7c7a3661a72b6e210aac0c4bd9d6acb2bd7bbd62), [ee7d72b](https://github.com/snyk/snyk/commit/ee7d72bb2c317dc571498e91239c8efb50257744))
23-
* **test**: Improves SDK detection when host and SDK versions differ. ([96d0817](https://github.com/snyk/snyk/commit/96d0817068472ee4a8bb916fb54dd3296f384d7a))
24-
* **test**: Ensures project names are populated when scanning NuGet projects from repository root. ([c043553](https://github.com/snyk/snyk/commit/c0435535a03fc7fcb3b35b671eda54c1f313593f))
25-
* **container**: Snyk Container scans of tar files on Windows should now report vulnerabilities for Python application package files. ([9b86790](https://github.com/snyk/snyk/commit/9b867908c4a89046ee475be26a058dff2301f40d))
26-
* **container**: Override packages with inaccurate pom.properties files ([b60473a](https://github.com/snyk/snyk/commit/b60473a20ffa55ea82c558c88783d4bcbc2dc54d))
27-
* **test**: Ensure Yarn workspace pacakges matches are actual members defined in the root `package.json`. ([0dd6581](https://github.com/snyk/snyk/commit/0dd6581046148aceb4adedd6d0e70838970c9c84))
28-
* **test**: Fix increased scan times when testing Golang projects. ([f2f5ba2](https://github.com/snyk/snyk/commit/f2f5ba2811d49156016c0ce6ac7d180d40ef1efc))
29-
* **code**: Snyk Code scans now return clearer error message and exit codes when testing unsupported projects ([6f5b4e3](https://github.com/snyk/snyk/commit/6f5b4e3de3e1c2b7359215c7dde24af447a8b3df))
30-
* **test**: Fix a bug where aliased packages were being resolved with the target name insted of the alias for yarn projects. ([dcbec6f](https://github.com/snyk/snyk/commit/dcbec6fb44397bbb1849ecafd0810d1f6b22fcb0))
31-
* **test**: Fix a bug where Python packages with `.` characters in their name were incorrectly parsed to include `-` characters. ([9a2a36e](https://github.com/snyk/snyk/commit/9a2a36e90cd14139735c18b696bd9de50774f0fc))
32-
* **deps**: Updates dependencies to fix vulnerabilities:
33-
- CVE-2026-26996 ([8e7873f](https://github.com/snyk/snyk/commit/8e7873ff6e9f852ad57bac4c0024231892754e05))
34-
- CVE-2026-29786 ([1a08533](https://github.com/snyk/snyk/commit/1a085335b0f3252818865d3df13e8abba815ffb2))
35-
- CVE-2026-31802 ([1321575](https://github.com/snyk/snyk/commit/132157557160f9d60cba22e073925bdcc1b53656))
36-
- CVE-2025-69873 ([8ff6aad](https://github.com/snyk/snyk/commit/8ff6aad6423d92cf39aad1de76fedb2cc10b5ef0))
37-
- CVE-2026-33186 ([e98d9ef](https://github.com/snyk/snyk/commit/e98d9ef256c6ed21dc452bde7fed03581cc0a6db))
38-
- CVE-2026-32283 ([d26e83f](https://github.com/snyk/cli/commit/d26e83fa349a5e21cfa907ee6a0d1cc29b7fe8c1))
39-
- CVE-2026-29181 ([f5418b6](https://github.com/snyk/cli/commit/f5418b676671eb4e8f5268af2df7218f439e9261))
40-
8+
* **general**: Improved error handling to prioritize and surface the most relevant error and derive the correct exit code when multiple errors occur during CLI execution. ([b505a96](https://github.com/snyk/snyk/commit/b505a962897f67fafa1626be70d56ee7918c095b))
9+
* **deps**: Updates dependencies to fix vulnerabilities for CVE-2026-4660 and CVE-2026-39883 ([2a95d85](https://github.com/snyk/snyk/commit/2a95d85aff26f562f9cf1400d106d9ed12e3beca))
10+
* **agent-scan**: Improved CI flexibility with an issues ignore option, and added support for Windows x86 and macOS x86 architectures. ([7d72bbf](https://github.com/snyk/snyk/commit/7d72bbf7de756f7098d36f4e04a4f74567816727))

cliv2/cmd/cliv2/errorhandling.go

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,8 @@ func decorateError(err error) error {
2828

2929
var errorCatalogError snyk_errors.Error
3030
if !errors.As(err, &errorCatalogError) {
31-
genericError := cli.NewGeneralCLIFailureError(err.Error())
32-
genericError.StatusCode = 0
33-
err = errors.Join(err, genericError)
31+
genericError := cli.NewGeneralCLIFailureError(err.Error(), snyk_errors.WithCause(err))
32+
return genericError
3433
}
3534
return err
3635
}

cliv2/cmd/cliv2/main.go

Lines changed: 22 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -541,9 +541,9 @@ func initExtensions(engine workflow.Engine, config configuration.Configuration)
541541
engine.AddExtensionInitializer(workflows.InitConnectivityCheckWorkflow)
542542
engine.AddExtensionInitializer(ignoreworkflow.InitIgnoreWorkflows)
543543
engine.AddExtensionInitializer(agentscan.Init)
544+
engine.AddExtensionInitializer(secrets.Init)
544545

545546
if config.GetBool(configuration.PREVIEW_FEATURES_ENABLED) {
546-
engine.AddExtensionInitializer(secrets.Init)
547547
config.Set("INTERNAL_USE_UFM_PRESENTER", true)
548548
}
549549
}
@@ -681,20 +681,23 @@ func MainWithErrorCode() int {
681681
// ignore
682682
}
683683

684+
outputError := err
685+
allErrors := errorList
686+
684687
if err != nil {
685-
errorList, err = processError(err, errorList)
688+
allErrors, outputError = processError(err, errorList)
686689

687-
for _, tempError := range errorList {
690+
for _, tempError := range allErrors {
688691
if tempError != nil {
689692
cliAnalytics.AddError(tempError)
690693
}
691694
}
692695
}
693696

694-
displayError(err, globalEngine.GetUserInterface(), globalConfiguration, ctx)
697+
displayError(outputError, globalEngine.GetUserInterface(), globalConfiguration, ctx)
695698

696-
exitCode := cliv2.DeriveExitCode(err)
697-
globalLogger.Printf("Deriving Exit Code %d (cause: %v)", exitCode, err)
699+
exitCode := cliv2.DeriveExitCode(outputError)
700+
globalLogger.Printf("Deriving Exit Code %d (cause: %v)", exitCode, outputError)
698701

699702
updateInstrumentationDataBeforeSending(cliAnalytics, startTime, ua, exitCode)
700703

@@ -711,38 +714,35 @@ func MainWithErrorCode() int {
711714
}
712715

713716
if debugEnabled {
714-
writeLogFooter(exitCode, errorList, globalConfiguration, networkAccess)
717+
writeLogFooter(exitCode, allErrors, globalConfiguration, networkAccess)
715718
}
716719

717720
return exitCode
718721
}
719722

720723
func processError(err error, errorList []error) ([]error, error) {
721724
// ensure to use generic fallback error catalog error if no other is available
722-
err = decorateError(err)
725+
resultError := decorateError(err)
726+
resultErrorList := errorList
723727

724728
// filter legacycli terminate errors since it is only used for internal purposes
725-
if exitErr, isExitError := err.(*exec.ExitError); isExitError && exitErr.ExitCode() == constants.SNYK_EXIT_CODE_TS_CLI_TERMINATED {
726-
err = nil
729+
if exitErr, isExitError := resultError.(*exec.ExitError); isExitError && exitErr.ExitCode() == constants.SNYK_EXIT_CODE_TS_CLI_TERMINATED {
730+
resultError = nil
727731
}
728732

729-
// add all errors to analytics
730-
if err != nil {
731-
errorList = append([]error{err}, errorList...)
733+
// ensure to have all errors in the list for analytics, determining the most relevant error to surface to the user ...
734+
if resultError != nil {
735+
resultErrorList = append([]error{resultError}, resultErrorList...)
732736
}
733737

734-
// create a single error from all errors
735-
if len(errorList) == 1 {
736-
err = errorList[0]
737-
} else if len(errorList) > 1 {
738-
err = errors.Join(errorList...)
739-
}
738+
// determine the most relevant error to surface to the user and derive the exit code from
739+
resultError = cli_errors.FindMostRelevantError(resultErrorList)
740740

741741
// ensure to apply exit code mapping based on errors
742-
if exitCode := mapErrorToExitCode(err); exitCode != unsetExitCode {
743-
err = createErrorWithExitCode(exitCode, err)
742+
if exitCode := mapErrorToExitCode(resultError); exitCode != unsetExitCode {
743+
resultError = createErrorWithExitCode(exitCode, resultError)
744744
}
745-
return errorList, err
745+
return resultErrorList, resultError
746746
}
747747

748748
func setTimeout(config configuration.Configuration, onTimeout func()) {

cliv2/cmd/cliv2/main_test.go

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -627,6 +627,130 @@ type wrErr struct{ wraps error }
627627
func (e *wrErr) Error() string { return "something went wrong" }
628628
func (e *wrErr) Unwrap() error { return e.wraps }
629629

630+
func Test_processError(t *testing.T) {
631+
t.Run("nil error returns nil", func(t *testing.T) {
632+
errorList, err := processError(nil, nil)
633+
assert.Nil(t, err)
634+
assert.Empty(t, errorList)
635+
})
636+
637+
t.Run("ExitError with exit code 1 preserves exit code", func(t *testing.T) {
638+
// Create a real exec.ExitError by running a command that fails
639+
cmd := exec.Command("sh", "-c", "exit 1")
640+
exitErr := cmd.Run()
641+
require.Error(t, exitErr)
642+
643+
errorList, err := processError(exitErr, nil)
644+
assert.NotNil(t, err)
645+
assert.Len(t, errorList, 1)
646+
647+
// The exit code should be preserved through DeriveExitCode
648+
exitCode := cliv2.DeriveExitCode(err)
649+
assert.Equal(t, 1, exitCode)
650+
})
651+
652+
t.Run("ExitError with TS_CLI_TERMINATED is filtered out", func(t *testing.T) {
653+
// Create a real exec.ExitError with the terminate code
654+
cmd := exec.Command("sh", "-c", fmt.Sprintf("exit %d", constants.SNYK_EXIT_CODE_TS_CLI_TERMINATED))
655+
exitErr := cmd.Run()
656+
require.Error(t, exitErr)
657+
658+
errorList, err := processError(exitErr, nil)
659+
assert.Nil(t, err)
660+
assert.Empty(t, errorList)
661+
})
662+
663+
t.Run("ErrorWithExitCode is preserved", func(t *testing.T) {
664+
inputErr := &clierrors.ErrorWithExitCode{ExitCode: 1}
665+
errorList, err := processError(inputErr, nil)
666+
667+
assert.NotNil(t, err)
668+
assert.Len(t, errorList, 1)
669+
670+
var resultExitCode *clierrors.ErrorWithExitCode
671+
assert.True(t, errors.As(err, &resultExitCode))
672+
assert.Equal(t, 1, resultExitCode.ExitCode)
673+
674+
exitCode := cliv2.DeriveExitCode(err)
675+
assert.Equal(t, 1, exitCode)
676+
})
677+
678+
t.Run("multiple errors are joined and exit code is preserved", func(t *testing.T) {
679+
// Create a real exec.ExitError
680+
cmd := exec.Command("sh", "-c", "exit 1")
681+
exitErr := cmd.Run()
682+
require.Error(t, exitErr)
683+
684+
otherErr := fmt.Errorf("some other error")
685+
errorList := []error{otherErr}
686+
687+
resultList, err := processError(exitErr, errorList)
688+
assert.NotNil(t, err)
689+
assert.Len(t, resultList, 2)
690+
691+
// The exit code should still be derivable
692+
exitCode := cliv2.DeriveExitCode(err)
693+
assert.Equal(t, 1, exitCode)
694+
})
695+
696+
t.Run("snyk_errors.Error without special mapping gets exit code 2", func(t *testing.T) {
697+
// A snyk_errors.Error that's not in the exit code mapping
698+
snykErr := snyk_errors.Error{
699+
Title: "Some Error",
700+
ErrorCode: "SNYK-9999",
701+
Level: "error",
702+
}
703+
704+
errorList, err := processError(snykErr, nil)
705+
assert.NotNil(t, err)
706+
assert.Len(t, errorList, 1)
707+
708+
// This should result in exit code 2 since it's not mapped
709+
exitCode := cliv2.DeriveExitCode(err)
710+
assert.Equal(t, constants.SNYK_EXIT_CODE_ERROR, exitCode)
711+
})
712+
713+
t.Run("maintenance error gets mapped to EX_TEMPFAIL", func(t *testing.T) {
714+
maintenanceErr := snyk_errors.Error{
715+
Title: "Maintenance",
716+
ErrorCode: "SNYK-0099",
717+
Level: "error",
718+
}
719+
720+
errorList, err := processError(maintenanceErr, nil)
721+
assert.NotNil(t, err)
722+
assert.Len(t, errorList, 1)
723+
724+
// Should be mapped to EX_TEMPFAIL (75)
725+
exitCode := cliv2.DeriveExitCode(err)
726+
assert.Equal(t, constants.SNYK_EXIT_CODE_EX_TEMPFAIL, exitCode)
727+
})
728+
729+
t.Run("maintenance error in error list takes priority", func(t *testing.T) {
730+
// Create a real exec.ExitError with exit code 1
731+
cmd := exec.Command("sh", "-c", "exit 1")
732+
exitErr := cmd.Run()
733+
require.Error(t, exitErr)
734+
735+
maintenanceErr := snyk_errors.Error{
736+
Title: "Maintenance",
737+
ErrorCode: "SNYK-0099",
738+
Level: "error",
739+
}
740+
741+
// Pass exitErr as the main error, maintenance error in the list
742+
errorList := []error{maintenanceErr}
743+
resultList, err := processError(exitErr, errorList)
744+
745+
assert.NotNil(t, err)
746+
assert.Len(t, resultList, 2)
747+
748+
// Maintenance error should take priority, resulting in EX_TEMPFAIL
749+
exitCode := cliv2.DeriveExitCode(err)
750+
assert.Equal(t, constants.SNYK_EXIT_CODE_EX_TEMPFAIL, exitCode)
751+
})
752+
}
753+
630754
func loadJsonFile(t *testing.T, filename string) []byte {
631755
t.Helper()
632756

0 commit comments

Comments
 (0)