diff --git a/cmd/grype/cli/commands/root.go b/cmd/grype/cli/commands/root.go index 8f6987ac9da..ecda8a7e23a 100644 --- a/cmd/grype/cli/commands/root.go +++ b/cmd/grype/cli/commands/root.go @@ -236,6 +236,9 @@ func runGrype(app clio.Application, opts *options.Grype, userInput string) (errs log.WithFields("time", time.Since(startTime)).Info("found vulnerability matches") startTime = time.Now() + // clear out the registry auth information to avoid including possibly sensitive information in the report + opts.Registry.Auth = nil + model, err := models.NewDocument(app.ID(), packages, pkgContext, *remainingMatches, ignoredMatches, vp, opts, dbInfo(status, vp), models.SortStrategy(opts.SortBy.Criteria), opts.Timestamp) if err != nil { return fmt.Errorf("failed to create document: %w", err) diff --git a/cmd/grype/cli/options/registry.go b/cmd/grype/cli/options/registry.go index f32d48b4adb..d3ff1dcccf0 100644 --- a/cmd/grype/cli/options/registry.go +++ b/cmd/grype/cli/options/registry.go @@ -21,7 +21,7 @@ type RegistryCredentials struct { type registry struct { InsecureSkipTLSVerify bool `yaml:"insecure-skip-tls-verify" json:"insecure-skip-tls-verify" mapstructure:"insecure-skip-tls-verify"` InsecureUseHTTP bool `yaml:"insecure-use-http" json:"insecure-use-http" mapstructure:"insecure-use-http"` - Auth []RegistryCredentials `yaml:"auth" json:"auth" mapstructure:"auth"` + Auth []RegistryCredentials `yaml:"auth" json:"auth,omitempty" mapstructure:"auth"` CACert string `yaml:"ca-cert" json:"ca-cert" mapstructure:"ca-cert"` } @@ -82,9 +82,9 @@ func (cfg *registry) ToOptions() *image.RegistryOptions { for i, a := range cfg.Auth { auth[i] = image.RegistryCredentials{ Authority: a.Authority, - Username: a.Username.String(), - Password: a.Password.String(), - Token: a.Token.String(), + Username: string(a.Username), + Password: string(a.Password), + Token: string(a.Token), ClientCert: a.TLSCert, ClientKey: a.TLSKey, } diff --git a/cmd/grype/cli/options/secret.go b/cmd/grype/cli/options/secret.go index dcdf2b905df..9f73c9ae199 100644 --- a/cmd/grype/cli/options/secret.go +++ b/cmd/grype/cli/options/secret.go @@ -21,5 +21,13 @@ func (r *secret) PostLoad() error { } func (r secret) String() string { - return string(r) + if r == "" { + return "" + } + // match the redactor's behavior, replacing with 7 asterisks + return "*******" +} + +func (r secret) MarshalText() ([]byte, error) { + return []byte(r.String()), nil } diff --git a/test/cli/registry_auth_test.go b/test/cli/registry_auth_test.go index 0ac8ef92af4..53179ddc709 100644 --- a/test/cli/registry_auth_test.go +++ b/test/cli/registry_auth_test.go @@ -1,8 +1,12 @@ package cli import ( + "os" + "path/filepath" "strings" "testing" + + "github.com/stretchr/testify/require" ) func TestRegistryAuth(t *testing.T) { @@ -97,3 +101,92 @@ func TestRegistryAuth(t *testing.T) { }) } } + +func TestRegistryAuthRedactions(t *testing.T) { + tmp := filepath.Join(t.TempDir(), "output.json") + + assertNotInFile := func(text string) traitAssertion { + return func(tb testing.TB, stdout, stderr string, rc int) { + contents, err := os.ReadFile(tmp) + require.NoError(tb, err) + require.NotEmpty(tb, contents) + require.NotContains(tb, string(contents), text) + } + } + + tests := []struct { + name string + args []string + env map[string]string + assertions []traitAssertion + }{ + { + name: "use creds", + args: []string{"-vv", "sbom:test-fixtures/sbom-grype-source.json", "-o", "json"}, + env: map[string]string{ + "GRYPE_REGISTRY_AUTH_USERNAME": "foobar-username", + "GRYPE_REGISTRY_AUTH_PASSWORD": "foobar-password", + }, + assertions: []traitAssertion{ + assertSucceedingReturnCode, + assertNotInOutput("foobar-username"), + assertNotInOutput("foobar-password"), + }, + }, + { + name: "use token", + args: []string{"-vv", "sbom:test-fixtures/sbom-grype-source.json", "-o", "json"}, + env: map[string]string{ + "GRYPE_REGISTRY_AUTH_TOKEN": "foobar-token", + }, + assertions: []traitAssertion{ + assertSucceedingReturnCode, + assertNotInOutput("foobar-token"), + }, + }, + { + name: "use creds file", + args: []string{"-vv", "sbom:test-fixtures/sbom-grype-source.json", "-o", "json", "--file", tmp}, + env: map[string]string{ + "GRYPE_REGISTRY_AUTH_USERNAME": "foobar-username", + "GRYPE_REGISTRY_AUTH_PASSWORD": "foobar-password", + }, + assertions: []traitAssertion{ + assertSucceedingReturnCode, + assertNotInFile("foobar-username"), + assertNotInFile("foobar-password"), + assertNotInOutput("foobar-username"), + assertNotInOutput("foobar-password"), + }, + }, + { + name: "use token file", + args: []string{"-vv", "sbom:test-fixtures/sbom-grype-source.json", "-o", "json", "--file", tmp}, + env: map[string]string{ + "GRYPE_REGISTRY_AUTH_TOKEN": "foobar-token", + }, + assertions: []traitAssertion{ + assertSucceedingReturnCode, + assertNotInFile("foobar-token"), + assertNotInOutput("foobar-token"), + }, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + _ = os.Remove(tmp) // ok to fail + cmd, stdout, stderr := runGrype(t, test.env, test.args...) + for _, traitAssertionFn := range test.assertions { + traitAssertionFn(t, stdout, stderr, cmd.ProcessState.ExitCode()) + } + if t.Failed() { + fileContents, _ := os.ReadFile(tmp) + t.Log("FILE:\n", string(fileContents)) + t.Log("STDOUT:\n", stdout) + t.Log("STDERR:\n", stderr) + t.Log("COMMAND:", strings.Join(cmd.Args, " ")) + } + }) + } +}