Repository: anchore/grype
Pull request #2553 (2025-03-20 20:23:48)

`db list` should render out full URLs for text format
DB list is not filling out assumed values when printing the latest.json URL and DB URL. This PR adjusts the output (diff shown between old and new values):
```diff
# diff -C 5 bad good
*** bad    Thu Mar 20 16:21:34 2025
--- good   Thu Mar 20 16:21:52 2025
***************
*** 1,6 ****
  Status:   active
  Schema:   v6.0.2
  Built:    2025-03-20T04:07:28Z
! Listing:  https://grype.anchore.io/databases
! DB URL:   https://grype.anchore.io/vulnerability-db_v6.0.2_2025-03-20T01:30:59Z_1742443648.tar.zst
  Checksum: sha256:d668b9a4bdc4ca4a116452501ee915c669811bc9cc4f0baa3c4ac67a50cf5ca0
--- 1,6 ----
  Status:   active
  Schema:   v6.0.2
  Built:    2025-03-20T04:07:28Z
! Listing:  https://grype.anchore.io/databases/v6/latest.json
! DB URL:   https://grype.anchore.io/databases/v6/vulnerability-db_v6.0.2_2025-03-20T01:30:59Z_1742443648.tar.zst
  Checksum: sha256:d668b9a4bdc4ca4a116452501ee915c669811bc9cc4f0baa3c4ac67a50cf5ca0
```

This only affects the text output


Patch:
diff --git a/cmd/grype/cli/commands/db_list.go b/cmd/grype/cli/commands/db_list.go
index f20ecd84..3be6a4a9 100644
--- a/cmd/grype/cli/commands/db_list.go
+++ b/cmd/grype/cli/commands/db_list.go
@@ -6,7 +6,6 @@ import (
 	"io"
 	"net/url"
 	"os"
-	"path"
 
 	"github.com/spf13/cobra"
 
@@ -62,28 +61,39 @@ func runDBList(opts dbListOptions) error {
 		return fmt.Errorf("unable to get database listing: %w", err)
 	}
 
-	return presentDBList(opts.Output, opts.DB.UpdateURL, os.Stdout, latest)
+	u, err := c.ResolveArchiveURL(latest.Archive)
+	if err != nil {
+		return fmt.Errorf("unable to resolve database URL: %w", err)
+	}
+
+	return presentDBList(opts.Output, u, opts.DB.UpdateURL, os.Stdout, latest)
 }
 
-func presentDBList(format string, u string, writer io.Writer, latest *distribution.LatestDocument) error {
+func presentDBList(format string, archiveURL, listingURL string, writer io.Writer, latest *distribution.LatestDocument) error {
 	if latest == nil {
 		return fmt.Errorf("no database listing found")
 	}
 
-	parsedURL, err := url.Parse(u)
+	// remove query params
+	archiveURLObj, err := url.Parse(archiveURL)
 	if err != nil {
-		return fmt.Errorf("failed to parse base URL: %w", err)
+		return fmt.Errorf("unable to parse db URL %q: %w", archiveURL, err)
 	}
 
-	parsedURL.Path = path.Join(path.Dir(parsedURL.Path), latest.Path)
+	archiveURLObj.RawQuery = ""
+
+	if listingURL == distribution.DefaultConfig().LatestURL {
+		// append on the schema
+		listingURL = fmt.Sprintf("%s/v%v/%s", listingURL, latest.SchemaVersion.Model, distribution.LatestFileName)
+	}
 
 	switch format {
 	case textOutputFormat:
 		fmt.Fprintf(writer, "Status:   %s\n", latest.Status)
 		fmt.Fprintf(writer, "Schema:   %s\n", latest.SchemaVersion.String())
 		fmt.Fprintf(writer, "Built:    %s\n", latest.Built.String())
-		fmt.Fprintf(writer, "Listing:  %s\n", u)
-		fmt.Fprintf(writer, "DB URL:   %s\n", parsedURL.String())
+		fmt.Fprintf(writer, "Listing:  %s\n", listingURL)
+		fmt.Fprintf(writer, "DB URL:   %s\n", archiveURLObj.String())
 		fmt.Fprintf(writer, "Checksum: %s\n", latest.Checksum)
 	case jsonOutputFormat, "raw":
 		enc := json.NewEncoder(writer)


Test patch:
diff --git a/cmd/grype/cli/commands/db_list_test.go b/cmd/grype/cli/commands/db_list_test.go
index 30d24194..3492f8e4 100644
--- a/cmd/grype/cli/commands/db_list_test.go
+++ b/cmd/grype/cli/commands/db_list_test.go
@@ -66,7 +66,6 @@ func Test_ListingUserAgent(t *testing.T) {
 }
 
 func TestPresentDBList(t *testing.T) {
-	baseURL := "http://localhost:8000/latest.json"
 	latestDoc := &distribution.LatestDocument{
 		Status: "active",
 		Archive: distribution.Archive{
@@ -82,20 +81,39 @@ func TestPresentDBList(t *testing.T) {
 	tests := []struct {
 		name         string
 		format       string
+		baseURL      string
+		archiveURL   string
 		latest       *distribution.LatestDocument
 		expectedText string
 		expectedErr  require.ErrorAssertionFunc
 	}{
 		{
-			name:   "valid text format",
-			format: textOutputFormat,
-			latest: latestDoc,
+			name:       "valid text format",
+			format:     textOutputFormat,
+			latest:     latestDoc,
+			baseURL:    "http://localhost:8000/latest.json",
+			archiveURL: "http://localhost:8000/vulnerability-db_v6.0.0_2024-11-25T01:31:56Z_1732718597.tar.zst",
 			expectedText: `Status:   active
 Schema:   v6.0.0
 Built:    2024-11-27T14:43:17Z
 Listing:  http://localhost:8000/latest.json
 DB URL:   http://localhost:8000/vulnerability-db_v6.0.0_2024-11-25T01:31:56Z_1732718597.tar.zst
 Checksum: sha256:16bcb6551c748056f752f299fcdb4fa50fe61589d086be3889e670261ff21ca4
+`,
+			expectedErr: require.NoError,
+		},
+		{
+			name:       "complete default values",
+			format:     textOutputFormat,
+			latest:     latestDoc,
+			baseURL:    "https://grype.anchore.io/databases",
+			archiveURL: "https://grype.anchore.io/databases/v6/vulnerability-db_v6.0.0_2024-11-25T01:31:56Z_1732718597.tar.zst",
+			expectedText: `Status:   active
+Schema:   v6.0.0
+Built:    2024-11-27T14:43:17Z
+Listing:  https://grype.anchore.io/databases/v6/latest.json
+DB URL:   https://grype.anchore.io/databases/v6/vulnerability-db_v6.0.0_2024-11-25T01:31:56Z_1732718597.tar.zst
+Checksum: sha256:16bcb6551c748056f752f299fcdb4fa50fe61589d086be3889e670261ff21ca4
 `,
 			expectedErr: require.NoError,
 		},
@@ -133,7 +151,7 @@ Checksum: sha256:16bcb6551c748056f752f299fcdb4fa50fe61589d086be3889e670261ff21ca
 		t.Run(tt.name, func(t *testing.T) {
 			writer := &bytes.Buffer{}
 
-			err := presentDBList(tt.format, baseURL, writer, tt.latest)
+			err := presentDBList(tt.format, tt.archiveURL, tt.baseURL, writer, tt.latest)
 			if tt.expectedErr == nil {
 				tt.expectedErr = require.NoError
 			}