CVE-2025-6624
Description
Versions of the package snyk before 1.1297.3 are vulnerable to Insertion of Sensitive Information into Log File through local Snyk CLI debug logs. Container Registry credentials provided via environment variables or command line arguments can be exposed when executing Snyk CLI in DEBUG or DEBUG/TRACE mode. The issue affects the following Snyk commands: 1. When snyk container test or snyk container monitor commands are run against a container registry, with debug mode enabled, the container registry credentials may be written into the local Snyk CLI debug log. This only happens with credentials specified in environment variables (SNYK_REGISTRY_USERNAME and SNYK_REGISTRY_PASSWORD), or in the CLI (--password/-p and --username/-u). 2. When snyk auth command is executed with debug mode enabled AND the log level is set to TRACE, the Snyk access / refresh credential tokens used to connect the CLI to Snyk may be written into the local CLI debug logs. 3. When snyk iac test is executed with a Remote IAC Custom rules bundle, debug mode enabled, AND the log level is set to TRACE, the docker registry token may be written into the local CLI debug logs.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
snyknpm | < 1.1297.3 | 1.1297.3 |
github.com/snyk/go-application-frameworkGo | >= 0 | — |
Affected products
1Patches
2ca7ba7d72e68fix: improve the sanitization of credentials in local debug logs (#376)
6 files changed · +262 −89
pkg/analytics/analytics.go+5 −17 modified@@ -2,6 +2,8 @@ package analytics import ( "bytes" + "github.com/snyk/go-application-framework/pkg/logging" + //nolint:gosec // insecure sha1 used for legacy identifier "crypto/sha1" "encoding/json" @@ -98,22 +100,8 @@ type dataOutput struct { Data analyticsOutput `json:"data"` } -var ( - // sensitiveFieldNames is a list of field names that should be sanitized. - // data sanitization is used to prevent sensitive data from being sent to the analytics server. - sensitiveFieldNames = []string{ - "headers", - "user", - "passw", - "token", - "key", - "secret", - } -) - const ( - sanitizeReplacementString string = "REDACTED" - apiEndpoint string = "/v1/analytics/cli" + apiEndpoint string = "/v1/analytics/cli" ) // New creates a new Analytics instance. @@ -243,7 +231,7 @@ func (a *AnalyticsImpl) GetRequest() (*http.Request, error) { return nil, err } - outputJson, err = SanitizeValuesByKey(sensitiveFieldNames, sanitizeReplacementString, outputJson) + outputJson, err = SanitizeValuesByKey(logging.SENSITIVE_FIELD_NAMES, logging.SANITIZE_REPLACEMENT_STRING, outputJson) if err != nil { return nil, err } @@ -252,7 +240,7 @@ func (a *AnalyticsImpl) GetRequest() (*http.Request, error) { if err != nil { return nil, err } - outputJson, err = SanitizeUsername(user.Username, user.HomeDir, sanitizeReplacementString, outputJson) + outputJson, err = SanitizeUsername(user.Username, user.HomeDir, logging.SANITIZE_REPLACEMENT_STRING, outputJson) if err != nil { return nil, err }
pkg/analytics/analytics_test.go+5 −4 modified@@ -3,6 +3,7 @@ package analytics import ( "encoding/json" "fmt" + "github.com/snyk/go-application-framework/pkg/logging" "io" "net/http" "os/user" @@ -74,7 +75,7 @@ func Test_Basic(t *testing.T) { body, err := io.ReadAll(request.Body) assert.Nil(t, err) // expect no CLI args to be sent to analytics (CLI-586) - assert.Equal(t, 0, strings.Count(string(body), sanitizeReplacementString)) + assert.Equal(t, 0, strings.Count(string(body), logging.SANITIZE_REPLACEMENT_STRING)) var requestBody dataOutput err = json.Unmarshal(body, &requestBody) @@ -118,11 +119,11 @@ func Test_SanitizeValuesByKey(t *testing.T) { } // test input - filter := sensitiveFieldNames + filter := logging.SENSITIVE_FIELD_NAMES input, err := json.Marshal(inputStruct) assert.NoError(t, err) - replacement := sanitizeReplacementString + replacement := logging.SANITIZE_REPLACEMENT_STRING fmt.Println("Before: " + string(input)) @@ -171,7 +172,7 @@ func Test_SanitizeUsername(t *testing.T) { // 2. with domain name // 3. user name and path are different // 4. current OS values - replacement := "REDACTED" + replacement := "***" inputData := []input{ { userName: "some.user",
pkg/analytics/instrumentation_collector.go+3 −2 modified@@ -4,6 +4,7 @@ import ( "encoding/json" "errors" "fmt" + "github.com/snyk/go-application-framework/pkg/logging" "os/user" "time" @@ -180,7 +181,7 @@ func (ic *instrumentationCollectorImpl) sanitizeExtensionData(logger *zerolog.Lo } var sanitized []byte - sanitized, err = SanitizeValuesByKey(sensitiveFieldNames, sanitizeReplacementString, extension) + sanitized, err = SanitizeValuesByKey(logging.SENSITIVE_FIELD_NAMES, logging.SANITIZE_REPLACEMENT_STRING, extension) if err != nil { logger.Printf("failed to sanitize extension, removing object from analytics payload as sanitzation was not possible: %v", err) return result @@ -192,7 +193,7 @@ func (ic *instrumentationCollectorImpl) sanitizeExtensionData(logger *zerolog.Lo return result } - sanitized, err = SanitizeUsername(u.Username, u.HomeDir, sanitizeReplacementString, sanitized) + sanitized, err = SanitizeUsername(u.Username, u.HomeDir, logging.SANITIZE_REPLACEMENT_STRING, sanitized) if err != nil { logger.Printf("failed to sanitize user information in extension payload, removing object from analytics payload as sanitzation was not possible: %v", err) return result
pkg/analytics/instrumentation_collector_test.go+1 −1 modified@@ -214,7 +214,7 @@ func Test_InstrumentationCollector(t *testing.T) { mockExtension := map[string]interface{}{ "strings": "hello world", - "password": "REDACTED", + "password": "***", } expectedV2InstrumentationObject.Data.Attributes.Interaction.Extension = &mockExtension
pkg/logging/scrubbingLogWriter.go+85 −12 modified@@ -21,6 +21,7 @@ import ( "io" "os/user" "regexp" + "sort" "strings" "sync" "time" @@ -31,8 +32,18 @@ import ( "github.com/snyk/go-application-framework/pkg/configuration" ) -const redactMask string = "***" const MAX_WRITE_RETRIES = 10 +const SANITIZE_REPLACEMENT_STRING string = "***" + +// SENSITIVE_FIELD_NAMES is a list of field names that should be sanitized. +var SENSITIVE_FIELD_NAMES = []string{ + "headers", + "user", + "passw", + "token", + "key", + "secret", +} type ScrubbingLogWriter interface { AddTerm(term string, matchGroup int) @@ -146,37 +157,44 @@ func addMandatoryMasking(dict ScrubbingDict) ScrubbingDict { groupToRedact: 3, regex: regexp.MustCompile(s), } + s = fmt.Sprintf(`([t|T]oken )(%s)`, charGroup) dict[s] = scrubStruct{ groupToRedact: 2, regex: regexp.MustCompile(s), } + s = fmt.Sprintf(`([b|B]earer )(%s)`, charGroup) dict[s] = scrubStruct{ groupToRedact: 2, regex: regexp.MustCompile(s), } + s = fmt.Sprintf(`([b|B]asic )(%s)`, charGroup) dict[s] = scrubStruct{ groupToRedact: 2, regex: regexp.MustCompile(s), } + s = fmt.Sprintf("(gh[ps])_(%s)", charGroup) dict[s] = scrubStruct{ groupToRedact: 2, regex: regexp.MustCompile(s), } + s = fmt.Sprintf("(github_pat_)(%s)", charGroup) dict[s] = scrubStruct{ groupToRedact: 2, regex: regexp.MustCompile(s), } + // github s = fmt.Sprintf(`(access_token[\\="\s:]+)(%s)&?`, charGroup) dict[s] = scrubStruct{ groupToRedact: 2, regex: regexp.MustCompile(s), } + s = fmt.Sprintf(`(refresh_token[\\="\s:]+)(%s)&?`, charGroup) dict[s] = scrubStruct{ groupToRedact: 2, @@ -195,23 +213,70 @@ func addMandatoryMasking(dict ScrubbingDict) ScrubbingDict { regex: regexp.MustCompile(s), } - // Scrub all kinds of "username/password" combinations sent to the log in various cases and with or without equal signs, etc. - s = `(?im)(\b\w*username\b|\-u|'u|"u)["'=:\s]+([\S\s]*?)["'\s]` + // Hide whatever is the current username + u, err := user.Current() + if err == nil { + s = fmt.Sprintf(`\b%s\b`, regexp.QuoteMeta(u.Username)) + addTermToDict(s, 0, dict) + } + + // The legacy CLI's snyk-config package prints the entire configuration in debug mode. + // It begins with some pseudo-JSON structure, which we can redact. + s = `(?s)_:\s*\[(?<everything_inside_hard_brackets>.*)\]` + dict[s] = scrubStruct{ + groupToRedact: 1, + regex: regexp.MustCompile(s), + } + + // JSON-formatted data, in general + kws := strings.Join(SENSITIVE_FIELD_NAMES, "|") + s = fmt.Sprintf(`(?i)"[^"]*(?<json_key>%s)[^"]*"\s*:\s*"(?<json_value>[^"]*)"`, kws) dict[s] = scrubStruct{ groupToRedact: 2, regex: regexp.MustCompile(s), } - s = `(?im)(\b\w*password\b|\-p|'p|"p)["'=:\s]+([\S\s]*?)["'\s]` + // CLI argument mapping from the snyk-config debug logging + // I.e., if --argument=value is passed, it will be logged as { 'argument=value': true } + s = fmt.Sprintf(`(?im)(%s)[^=]*=(?P<value>.*)['"]`, kws) dict[s] = scrubStruct{ groupToRedact: 2, regex: regexp.MustCompile(s), } - u, err := user.Current() - if err == nil { - s = fmt.Sprintf(`\b%s\b`, regexp.QuoteMeta(u.Username)) - addTermToDict(s, 0, dict) + // Same as above, only with short form + shorts := []string{"p", "u"} + shortForm := strings.Join(shorts, "") + s = fmt.Sprintf(`(?im)'[%s]=(?<value>.*)'`, shortForm) + dict[s] = scrubStruct{ + groupToRedact: 2, + regex: regexp.MustCompile(s), + } + + // Specific short-form scrubbing of the JSON-ish log structures + // Appear in the snyk-config debug logging as various constellations of { 'u': 'john.doe', } with or without quotes, + // and values can contain spaces, double and/or single quotes. + + s = fmt.Sprintf(`(?i)(?<short_form_key>\b[%s]\b)[,'":]+\s*(?:['"](?<short_form_value>.*)['"]|([^,'"\s]+))[,}]?`, shortForm) + dict[s] = scrubStruct{ + groupToRedact: 2, + regex: regexp.MustCompile(s), + } + + // CLI argument-style-specific scrubbing + // Many cases are already covered by the JSON scrubbing above, thus this might seem incomplete. + // Refer to the unit tests for the full set of covered cases. + s = fmt.Sprintf(`(?im)\-[%s][\s=](?<short_form_value>\S*)`, shortForm) + dict[s] = scrubStruct{ + groupToRedact: 1, + regex: regexp.MustCompile(s), + } + + // Long-form, rest is covered by the JSON scrubbing above + s = fmt.Sprintf(`(?im)--(?<argument_key>[^=\s]*(?:%s)[^=\s]*)[\s=]['"]?(?<argument_value>\S*)['"]?`, kws) + dict[s] = scrubStruct{ + groupToRedact: 2, + regex: regexp.MustCompile(s), } return dict @@ -226,14 +291,22 @@ func (w *scrubbingLevelWriter) Write(p []byte) (int, error) { func scrub(p []byte, scrubDict ScrubbingDict) []byte { s := string(p) - for _, entry := range scrubDict { + + // The dictionary order is important here, as we want potentially overlapping regexes to be applied + // in a specific order every time. Since dictionaries are unordered, we sort the keys here. + keys := make([]string, 0, len(scrubDict)) + for k := range scrubDict { + keys = append(keys, k) + } + sort.Strings(keys) + for _, key := range keys { + entry := scrubDict[key] matches := entry.regex.FindAllStringSubmatch(s, -1) for _, match := range matches { - if match[entry.groupToRedact] == "" { + if entry.groupToRedact >= len(match) || match[entry.groupToRedact] == "" { continue } - - s = strings.Replace(s, match[entry.groupToRedact], redactMask, -1) + s = strings.Replace(s, match[entry.groupToRedact], SANITIZE_REPLACEMENT_STRING, -1) } } return []byte(s)
pkg/logging/scrubbingLogWriter_test.go+163 −53 modified@@ -19,16 +19,15 @@ package logging import ( "bytes" "fmt" + "github.com/snyk/go-application-framework/pkg/auth" + "github.com/snyk/go-application-framework/pkg/configuration" + "github.com/stretchr/testify/require" "os/user" "regexp" "testing" "github.com/rs/zerolog" "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/snyk/go-application-framework/pkg/auth" - "github.com/snyk/go-application-framework/pkg/configuration" ) type mockWriter struct { @@ -96,7 +95,7 @@ func TestScrubbingIoWriter(t *testing.T) { pattern := "%s for my account, including my %s" patternWithSecret := fmt.Sprintf(pattern, "secret", "token") - patternWithMaskedSecret := fmt.Sprintf(pattern, redactMask, redactMask) + patternWithMaskedSecret := fmt.Sprintf(pattern, SANITIZE_REPLACEMENT_STRING, SANITIZE_REPLACEMENT_STRING) bufioWriter := bytes.NewBufferString("") writer := NewScrubbingIoWriter(bufioWriter, scrubDict) @@ -255,9 +254,24 @@ func TestAddDefaults(t *testing.T) { expected: "refresh_token=***&expire", }, { - name: "token in json", - input: `"token":"alittlesecret"`, - expected: `"token":"***"`, + name: "access token in json", + input: `{"access_token":"secret_access_token"}`, + expected: `{"access_token":"***"}`, + }, + { + name: "access token in json with multiple fields", + input: `{"unrelated":"foobar", "access_token":"secret_access_token","expires_in":300,"issued_at":"2025-06-20T15:32:38.38731422Z"}`, + expected: `{"unrelated":"foobar", "access_token":"***","expires_in":300,"issued_at":"2025-06-20T15:32:38.38731422Z"}`, + }, + { + name: "any type of token in json", + input: `{"something_token":"secret_access_token"}`, + expected: `{"something_token":"***"}`, + }, + { + name: "any type of token in json with postfix", + input: `{"something_token_and_a_postfix":"secret_access_token"}`, + expected: `{"something_token_and_a_postfix":"***"}`, }, { name: "SNYK_TOKEN", @@ -267,68 +281,164 @@ func TestAddDefaults(t *testing.T) { { name: "username", input: fmt.Sprintf("User %s.%s is repeatedly mentioned, but not partially.", u.Username, u.Username), - expected: fmt.Sprintf("User %s.%s is repeatedly mentioned, but not partially.", redactMask, redactMask), + expected: fmt.Sprintf("User %s.%s is repeatedly mentioned, but not partially.", SANITIZE_REPLACEMENT_STRING, SANITIZE_REPLACEMENT_STRING), + }, + { + name: "JSON-ish argument structure with verbatim output from snyk-config", + input: `_: [ + 'gcr.io/distroless/nodejs:latest', + 'john.doe', + 'hunter2', + [other things] + ],`, + expected: `_: [***],`, }, { - name: "username/password passed as an argument somewhere in the log", - input: ` - { - _: [ 'test' ], + name: "cli argument mapping from snyk-config debug logging", + input: `{ + username: 'username-set', + 'username=john.doe': true, password: 'password-set', 'password=foobar': true, - '-u user', - '-p 1234', - u: true, + 'u=foobar': true, + 'password-with-double-quotes=foo"bar': true, + "password-with-single-quotes=foo'bar": true, + 'password-with-comma=foo,bar': true, + 'my-password-with-spaces=foo bar': true, + 'my-password-thats-solid=solid': true, + 'p=foobar': true, debug: true, - 'log-level': 'trace', - "REGISTRY_USERNAME": "user", - "REGISTRY_PASSWORD": "foobar", - "API": "https://api.snyk.io", - "INTEGRATION_NAME": "CLI_V1_PLUGIN" + 'log-level': 'trace' }`, - expected: ` - { - _: [ 'test' ], - password: '***', + expected: `{ + username: 'username-set', + 'username=***': true, + password: 'password-set', 'password=***': true, - '-u ***', - '-p ***', - u: true, + 'u=***': true, + 'password-with-double-quotes=***': true, + "password-with-single-quotes=***": true, + 'password-with-comma=***': true, + 'my-password-with-spaces=***': true, + 'my-password-thats-***=***': true, + 'p=***': true, debug: true, - 'log-level': 'trace', + 'log-level': 'trace' + }`, + }, + { + name: "username and password constellations passed in a JSON-ish structure with verbatim output from snyk-config", + input: `{ + unrelated: dont-scrub, + "unrelated": "dont-scrub", + 'unrelated': 'dont-scrub', + unrelated: dont-scrub, + '-p': 'hunter2', + 'u': 'john.doe', + 'p': 'hunter2', + 'p': 'hun"ter2', + 'p': 'hun ter2', + 'p': 'hun,ter2', + "u": "john.doe", + "u": "john'doe", + "u": "john,doe", + "u": "john doe", + "p": "hunter2", + u: 'john.doe', + p: 'hunter2', + "REGISTRY_USERNAME": "user", + "REGISTRY_PASSWORD": "foobar", + "MORE_UNRELATED": "DONT_SCRUB" + }`, + expected: `{ + unrelated: dont-scrub, + "unrelated": "dont-scrub", + 'unrelated': 'dont-scrub', + unrelated: dont-scrub, + '-p': '***', + 'u': '***', + 'p': '***', + 'p': '***', + 'p': '***', + 'p': '***', + "u": "***", + "u": "***", + "u": "***", + "u": "***", + "p": "***", + u: '***', + p: '***', "REGISTRY_USERNAME": "***", "REGISTRY_PASSWORD": "***", - "API": "https://api.snyk.io", - "INTEGRATION_NAME": "CLI_V1_PLUGIN" + "MORE_UNRELATED": "DONT_SCRUB" }`, }, { - name: "Various authentication request/response combinations", - input: ` + name: "CLI arguments logged to the debug logs (multi-line)", + input: `Arguments:[ + container test gcr.io/distroless/nodejs:latest + --platform=linux/arm64 + --unrelated-argument + --unrelated-argument-with-value "value" + --unrelated-argument-with-equals-sign="value" + -u john.doe + -p hunter2 + --username john.doe + --password hunter2 + --a-password-with-secret-in-the-value 'super-secret-password' + --a-password-with-an-equals-sign='hunter2' + --a-password-with-spaces='hun ter2' + --a-password-with-double-quotes='hun"ter2' + --a-password-with-single-quotes="hun'ter2" + --token "token" + --password-with-no-value + --another-unrelated-at-the-end + --log-level=trace + -d + --debug + ]`, + expected: `Arguments:[ + container test gcr.io/distroless/nodejs:latest + --platform=linux/arm64 + --unrelated-argument + --unrelated-argument-with-value "value" + --unrelated-argument-with-equals-sign="value" + -u *** + -p *** + --username *** + --password *** + --a-password-with-secret-in-the-value '*** + --a-password-with-an-equals-sign=*** + --a-password-with-spaces=*** + --a-password-with-double-quotes=*** + --a-password-with-single-quotes=*** + --token "*** + --password-with-no-value + --another-unrelated-at-the-end + --log-level=trace + -d + --debug + ]`, + }, { - "token": "super_secret_token", - "access_token": "super_secret_token", - "expires_in": 3599, - "refresh_expires_in": 15552000, - "refresh_token": "super_secret_refresh_token", - "scope": "org.read", - "token_type": "bearer" - }`, - expected: ` + name: "CLI arguments logged to the debug logs (same line, short-form, no equals signs)", + input: `container test gcr.io/distroless/nodejs:latest --platform=linux/arm64 --unrelated-argument --unrelated-argument-with-value "value" --unrelated-argument-with-equals-sign="value" -u john.doe -p hunter2 --log-level=trace`, + expected: `container test gcr.io/distroless/nodejs:latest --platform=linux/arm64 --unrelated-argument --unrelated-argument-with-value "value" --unrelated-argument-with-equals-sign="value" -u *** -p *** --log-level=trace`, + }, + { + name: "CLI arguments logged to the debug logs (same line, short-form, with equals signs)", + input: `container test gcr.io/distroless/nodejs:latest --platform=linux/arm64 --unrelated-argument --unrelated-argument-with-value "value" --unrelated-argument-with-equals-sign="value" -u=john.doe -p=hunter2 --log-level=trace`, + expected: `container test gcr.io/distroless/nodejs:latest --platform=linux/arm64 --unrelated-argument --unrelated-argument-with-value "value" --unrelated-argument-with-equals-sign="value" -u=*** -p=*** --log-level=trace`, + }, { - "token": "***", - "access_token": "***", - "expires_in": 3599, - "refresh_expires_in": 15552000, - "refresh_token": "***", - "scope": "org.read", - "token_type": "bearer" - }`, + name: "CLI arguments logged to the debug logs (same line, long-form, no equals signs)", + input: `container test ubuntu:latest --username john.doe --password solidpassword -d`, + expected: `container test ubuntu:latest --username *** --password *** -d`, }, { - name: "Various passed arguments", - input: "Arguments:[container test gcr.io/distroless/nodejs:latest --platform=linux/arm64 --u=johndoe --p=hunter2 --log-level=trace -d]", - expected: "Arguments:[container test gcr.io/distroless/nodejs:latest --platform=linux/arm64 --u=*** --p=*** --log-level=trace -d]", + name: "CLI arguments logged to the debug logs (same line, long-form, with equals signs)", + input: `container test ubuntu:latest --username=john.doe --password=solidpassword -d`, + expected: `container test ubuntu:latest --username=*** --password=*** -d`, }, } for _, test := range tests {
38322f377da7fix(logging): Improve the sanitization of credentials in local debug logs
8 files changed · +118 −25
binary-releases/RELEASE_NOTES.md+2 −5 modified@@ -1,10 +1,7 @@ -## [1.1297.2](https://github.com/snyk/snyk/compare/v1.1297.1...1.1297.2) (2025-06-16) +## [1.1297.3](https://github.com/snyk/snyk/compare/v1.1297.2...1.1297.3) (2025-06-23) 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) ### Bug Fixes -* **logging:** Improves the sanitization of credentials in local debug logs. ([e054455](https://github.com/snyk/snyk/commit/e054455eab8e686f19c165a8bad86259103a5f5d)) -* **language-server:** IDE Connectivity for Proxy Users: Fixes an issue where IDE plugins could fail to connect when operating behind an NTLM proxy. -* **language-server:** Snyk Code Local Engine Fix: Addresses a regression that prevented the Snyk Code Local Engine (SCLE) from functioning correctly within the IDEs. - +* **logging:** Improves the sanitization of credentials in local debug logs. ([e50fb22](https://github.com/snyk/snyk/commit/e50fb22cde76b3ca01e6a21d5495534c06586df9)) \ No newline at end of file
cliv2/go.mod+1 −1 modified@@ -16,7 +16,7 @@ require ( github.com/snyk/cli-extension-sbom v0.0.0-20250422133603-a5ae6fdf0934 github.com/snyk/container-cli v0.0.0-20250321132345-1e2e01681dd7 github.com/snyk/error-catalog-golang-public v0.0.0-20250429130542-564b0605020e - github.com/snyk/go-application-framework v0.0.0-20250612130357-31093e6eb8ad + github.com/snyk/go-application-framework v0.0.0-20250623124518-ca7ba7d72e68 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-20250613113919-2b232b9d448d
cliv2/go.sum+2 −2 modified@@ -808,8 +808,8 @@ github.com/snyk/container-cli v0.0.0-20250321132345-1e2e01681dd7 h1:/2+2piwQtB9f github.com/snyk/container-cli v0.0.0-20250321132345-1e2e01681dd7/go.mod h1:38w+dcAQp9eG3P5t2eNS9eG0reut10AeJjLv5lJ5lpM= github.com/snyk/error-catalog-golang-public v0.0.0-20250429130542-564b0605020e h1:XFGkHDWA8JTPLr82QzoKVqGytofEYBf68VqoUq8yvXk= github.com/snyk/error-catalog-golang-public v0.0.0-20250429130542-564b0605020e/go.mod h1:Ytttq7Pw4vOCu9NtRQaOeDU2dhBYUyNBe6kX4+nIIQ4= -github.com/snyk/go-application-framework v0.0.0-20250612130357-31093e6eb8ad h1:RpUp1oayxILiWL6jGnXgAYiz7E44minwFEeDXJU3Xc0= -github.com/snyk/go-application-framework v0.0.0-20250612130357-31093e6eb8ad/go.mod h1:Hy8dugDhTPRPe99Bf4mG7zeh7+OobdWfX5dzhbeQQsU= +github.com/snyk/go-application-framework v0.0.0-20250623124518-ca7ba7d72e68 h1:eTR15PecAEX3F9HvptgPWI1Z936qirRoIGCiK3kALfU= +github.com/snyk/go-application-framework v0.0.0-20250623124518-ca7ba7d72e68/go.mod h1:Hy8dugDhTPRPe99Bf4mG7zeh7+OobdWfX5dzhbeQQsU= 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.33.2 h1:ZxD6/RQ4vqUAXa64V72SsGjZ8vmnBgZNGYQxMIqctYo=
cliv2/internal/cliv2/cliv2.go+1 −1 modified@@ -427,7 +427,7 @@ func (c *CLI) executeV1Default(proxyInfo *proxy.ProxyInfo, passThroughArgs []str c.DebugLogger.Println("Launching: ") c.DebugLogger.Println(" ", c.v1BinaryLocation) c.DebugLogger.Println(" With Arguments:") - c.DebugLogger.Println(" ", strings.Join(passThroughArgs, ", ")) + c.DebugLogger.Println(" ", strings.Join(passThroughArgs, " ")) c.DebugLogger.Println(" With Environment: ") variablesMap := utils.ToKeyValueMap(snykCmd.Env, "=")
cliv2/pkg/basic_workflows/legacycli.go+0 −1 modified@@ -84,7 +84,6 @@ func legacycliWorkflow( proxyAuthenticationMechanism := httpauth.AuthenticationMechanismFromString(proxyAuthenticationMechanismString) analyticsDisabled := config.GetBool(configuration.ANALYTICS_DISABLED) - debugLogger.Print("Arguments:", args) debugLogger.Print("Use StdIO:", useStdIo) debugLogger.Print("Working directory:", workingDirectory)
src/cli/args.ts+0 −5 modified@@ -8,7 +8,6 @@ import { SupportedUserReachableFacingCliArgs, } from '../lib/types'; import { getContainerImageSavePath } from '../lib/container'; -import { obfuscateArgs } from '../lib/utils'; export declare interface Global extends NodeJS.Global { ignoreUnknownCA: boolean; @@ -119,8 +118,6 @@ export function args(rawArgv: string[]): Args { debugModule.enable(enable); } - const debug = debugModule('snyk'); - // Late require, see the note re "debug" option above. const cli = require('./commands'); @@ -273,8 +270,6 @@ export function args(rawArgv: string[]): Args { global.ignoreUnknownCA = true; } - debug(command, obfuscateArgs(argv)); - return { command, method,
test/jest/acceptance/debuglog.spec.ts+111 −7 modified@@ -4,7 +4,7 @@ import { createProjectFromWorkspace, } from '../util/createProject'; -jest.setTimeout(1000 * 60); +jest.setTimeout(10000 * 60); describe('debug log', () => { it('redacts token from env var', async () => { @@ -48,7 +48,85 @@ describe('debug log', () => { it('redacts basic authentication', async () => { const { stderr } = await runSnykCLI( - 'container test ubuntu:latest --username=us --password=pw -d', + 'container test ubuntu:latest --username=john.doe@domain.org --password=solidpassword -d', + { + env: { + ...process.env, + SNYK_DISABLE_ANALYTICS: '1', + }, + }, + ); + + expect(stderr).not.toContain('Basic am9ob'); + expect(stderr).toContain('Basic ***'); + + expect(stderr).not.toContain('john.doe@domain.org'); + expect(stderr).not.toContain('solidpassword'); + }); + + it('redacts short-form basic authentication', async () => { + const { stderr } = await runSnykCLI( + 'container test ubuntu:latest -u=john.doe@domain.org -p=solidpassword -d', + { + env: { + ...process.env, + SNYK_DISABLE_ANALYTICS: '1', + }, + }, + ); + + expect(stderr).not.toContain('Basic am9ob'); + expect(stderr).toContain('Basic ***'); + + expect(stderr).not.toContain('john.doe@domain.org'); + expect(stderr).not.toContain('solidpassword'); + + expect(stderr).toContain('-u=***'); + expect(stderr).toContain('-p=***'); + }); + + it('redacts ENV-driven basic authentication', async () => { + const { stderr } = await runSnykCLI('container test ubuntu:latest -d', { + env: { + ...process.env, + SNYK_DISABLE_ANALYTICS: '1', + SNYK_REGISTRY_USERNAME: 'john.doe@domain.org', + SNYK_REGISTRY_PASSWORD: 'solidpassword', + }, + }); + + expect(stderr).not.toContain('Basic am9ob'); + expect(stderr).toContain('Basic ***'); + + expect(stderr).not.toContain('john.doe@domain.org'); + expect(stderr).not.toContain('solidpassword'); + }); + + it('redacts basic authentication with trace log level', async () => { + const { stderr } = await runSnykCLI( + 'container test ubuntu:latest --username=john.doe@domain.org --password=solidpassword -d', + { + env: { + ...process.env, + SNYK_DISABLE_ANALYTICS: '1', + SNYK_LOG_LEVEL: 'trace', + }, + }, + ); + + expect(stderr).not.toContain('Basic am9ob'); + expect(stderr).toContain('Basic ***'); + + expect(stderr).not.toContain('john.doe@domain.org'); + expect(stderr).not.toContain('solidpassword'); + + expect(stderr).toContain('"username": "***"'); + expect(stderr).toContain('"password": "***"'); + }); + + it('redacts short-form basic authentication with trace log level', async () => { + const { stderr } = await runSnykCLI( + 'container test ubuntu:latest -u=john.doe@domain.org -p=solidpassword -d', { env: { ...process.env, @@ -58,11 +136,37 @@ describe('debug log', () => { }, ); - // this test only makes sense when Basic auth would be expected, otherwise the checks below - if (stderr.includes('Basic ')) { - expect(stderr).not.toContain('Basic dXM6cHc='); - expect(stderr).toContain('Basic ***'); - } + expect(stderr).not.toContain('Basic am9ob'); + expect(stderr).toContain('Basic ***'); + + expect(stderr).not.toContain('john.doe@domain.org'); + expect(stderr).not.toContain('solidpassword'); + + expect(stderr).toContain('-u=***'); + expect(stderr).toContain('"u": "***"'); + expect(stderr).toContain('-p=***'); + expect(stderr).toContain('"p": "***"'); + }); + + it('redacts ENV-driven basic authentication with trace log level', async () => { + const { stderr } = await runSnykCLI('container test ubuntu:latest -d', { + env: { + ...process.env, + SNYK_DISABLE_ANALYTICS: '1', + SNYK_REGISTRY_USERNAME: 'john.doe@domain.org', + SNYK_REGISTRY_PASSWORD: 'solidpassword', + SNYK_LOG_LEVEL: 'trace', + }, + }); + + expect(stderr).not.toContain('Basic am9ob'); + expect(stderr).toContain('Basic ***'); + + expect(stderr).not.toContain('john.doe@domain.org'); + expect(stderr).not.toContain('solidpassword'); + + expect(stderr).toContain('"REGISTRY_USERNAME": "***"'); + expect(stderr).toContain('"REGISTRY_PASSWORD": "***"'); }); it('redacts externally injected bearer token', async () => {
ts-binary-wrapper/src/common.ts+1 −3 modified@@ -181,9 +181,7 @@ export function runWrapper(executable: string, cliArguments: string[]): number { const debug = debugEnabled(cliArguments); if (debug) { - logErrorWithTimeStamps( - 'Executing: ' + executable + ' ' + cliArguments.join(' '), - ); + logErrorWithTimeStamps('Executing: ' + executable); } const res = spawnSync(executable, cliArguments, {
Vulnerability mechanics
Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
7- github.com/snyk/cli/commit/38322f377da7e5f1391e1f641710be50989fa4dfnvdPatchWEB
- github.com/snyk/go-application-framework/commit/ca7ba7d72e68455afb466a7a47bb2c9aece86c18nvdPatchWEB
- github.com/advisories/GHSA-6hwc-9h8r-3vmfghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2025-6624ghsaADVISORY
- security.snyk.io/vuln/SNYK-JS-SNYK-10497607nvdThird Party AdvisoryWEB
- docs.snyk.io/snyk-cli/debugging-the-snyk-clinvdTechnical DescriptionWEB
- github.com/snyk/cli/releases/tag/v1.1297.3nvdRelease NotesWEB
News mentions
0No linked articles in our index yet.