From 48f0a4680f3b26f33ed335bc82710d254b82f3da Mon Sep 17 00:00:00 2001 From: Alex Goodman Date: Wed, 5 Oct 2022 16:29:05 -0400 Subject: [PATCH] fix apkdb checksum representation (#1247) Signed-off-by: Alex Goodman Signed-off-by: Alex Goodman --- syft/pkg/cataloger/apkdb/parse_apk_db.go | 25 +++- syft/pkg/cataloger/apkdb/parse_apk_db_test.go | 107 +++++++++++------- 2 files changed, 90 insertions(+), 42 deletions(-) diff --git a/syft/pkg/cataloger/apkdb/parse_apk_db.go b/syft/pkg/cataloger/apkdb/parse_apk_db.go index 95433fda408..25503b7caf3 100644 --- a/syft/pkg/cataloger/apkdb/parse_apk_db.go +++ b/syft/pkg/cataloger/apkdb/parse_apk_db.go @@ -134,10 +134,7 @@ func parseApkDBEntry(reader io.Reader) (*pkg.ApkMetadata, error) { log.Warnf("checksum field with no parent record: %q", value) continue } - fileRecord.Digest = &file.Digest{ - Algorithm: "sha1", - Value: value, - } + fileRecord.Digest = processChecksum(value) case "I", "S": // coerce to integer iVal, err := strconv.Atoi(value) @@ -161,3 +158,23 @@ func parseApkDBEntry(reader io.Reader) (*pkg.ApkMetadata, error) { return &entry, nil } + +func processChecksum(value string) *file.Digest { + // from: https://wiki.alpinelinux.org/wiki/Apk_spec + // The package checksum field is the SHA1 hash of the second gzip stream (control stream) in the package. The + // binary hash digest is base64 encoded. This is prefixed with Q1 to differentiate it from the MD5 hashes + // used in older index formats. It is not possible to compute this checksum with standard command line tools + // but the apk-tools can compute it in their index operation. + + // based on https://github.com/alpinelinux/apk-tools/blob/dd1908f2fc20b4cfe2c15c55fafaa5fadfb599dc/src/blob.c#L379-L393 + // it seems that the old md5 checksum value was only the hex representation (not base64) + algorithm := "md5" + if strings.HasPrefix(value, "Q1") { + algorithm = "'Q1'+base64(sha1)" + } + + return &file.Digest{ + Algorithm: algorithm, + Value: value, + } +} diff --git a/syft/pkg/cataloger/apkdb/parse_apk_db_test.go b/syft/pkg/cataloger/apkdb/parse_apk_db_test.go index 6b914747581..3f8845f1338 100644 --- a/syft/pkg/cataloger/apkdb/parse_apk_db_test.go +++ b/syft/pkg/cataloger/apkdb/parse_apk_db_test.go @@ -6,6 +6,7 @@ import ( "testing" "github.com/go-test/deep" + "github.com/stretchr/testify/assert" "github.com/anchore/syft/syft/file" "github.com/anchore/syft/syft/pkg" @@ -41,7 +42,7 @@ func TestExtraFileAttributes(t *testing.T) { OwnerGID: "0", Permissions: "755", Digest: &file.Digest{ - Algorithm: "sha1", + Algorithm: "'Q1'+base64(sha1)", Value: "Q1M0C9qfC/+kdRiOodeihG2GMRtkE=", }, }, @@ -110,7 +111,7 @@ func TestSinglePackageDetails(t *testing.T) { OwnerGID: "0", Permissions: "755", Digest: &file.Digest{ - Algorithm: "sha1", + Algorithm: "'Q1'+base64(sha1)", Value: "Q1Kja2+POZKxEkUOZqwSjC6kmaED4=", }, }, @@ -126,7 +127,7 @@ func TestSinglePackageDetails(t *testing.T) { OwnerGID: "0", Permissions: "755", Digest: &file.Digest{ - Algorithm: "sha1", + Algorithm: "'Q1'+base64(sha1)", Value: "Q1CVmFbdY+Hv6/jAHl1gec2Kbx1EY=", }, }, @@ -136,7 +137,7 @@ func TestSinglePackageDetails(t *testing.T) { OwnerGID: "0", Permissions: "755", Digest: &file.Digest{ - Algorithm: "sha1", + Algorithm: "'Q1'+base64(sha1)", Value: "Q1yFAhGggmL7ERgbIA7KQxyTzf3ks=", }, }, @@ -146,7 +147,7 @@ func TestSinglePackageDetails(t *testing.T) { OwnerGID: "0", Permissions: "755", Digest: &file.Digest{ - Algorithm: "sha1", + Algorithm: "'Q1'+base64(sha1)", Value: "Q1dAdYK8M/INibRQF5B3Rw7cmNDDA=", }, }, @@ -156,7 +157,7 @@ func TestSinglePackageDetails(t *testing.T) { OwnerGID: "0", Permissions: "755", Digest: &file.Digest{ - Algorithm: "sha1", + Algorithm: "'Q1'+base64(sha1)", Value: "Q1eR2Dz/WylabgbWMTkd2+hGmEya4=", }, }, @@ -195,49 +196,49 @@ func TestSinglePackageDetails(t *testing.T) { { Path: "/etc/fstab", Digest: &file.Digest{ - Algorithm: "sha1", + Algorithm: "'Q1'+base64(sha1)", Value: "Q11Q7hNe8QpDS531guqCdrXBzoA/o=", }, }, { Path: "/etc/group", Digest: &file.Digest{ - Algorithm: "sha1", + Algorithm: "'Q1'+base64(sha1)", Value: "Q1oJ16xWudgKOrXIEquEDzlF2Lsm4=", }, }, { Path: "/etc/hostname", Digest: &file.Digest{ - Algorithm: "sha1", + Algorithm: "'Q1'+base64(sha1)", Value: "Q16nVwYVXP/tChvUPdukVD2ifXOmc=", }, }, { Path: "/etc/hosts", Digest: &file.Digest{ - Algorithm: "sha1", + Algorithm: "'Q1'+base64(sha1)", Value: "Q1BD6zJKZTRWyqGnPi4tSfd3krsMU=", }, }, { Path: "/etc/inittab", Digest: &file.Digest{ - Algorithm: "sha1", + Algorithm: "'Q1'+base64(sha1)", Value: "Q1TsthbhW7QzWRe1E/NKwTOuD4pHc=", }, }, { Path: "/etc/modules", Digest: &file.Digest{ - Algorithm: "sha1", + Algorithm: "'Q1'+base64(sha1)", Value: "Q1toogjUipHGcMgECgPJX64SwUT1M=", }, }, { Path: "/etc/motd", Digest: &file.Digest{ - Algorithm: "sha1", + Algorithm: "'Q1'+base64(sha1)", Value: "Q1XmduVVNURHQ27TvYp1Lr5TMtFcA=", }, }, @@ -247,35 +248,35 @@ func TestSinglePackageDetails(t *testing.T) { OwnerGID: "0", Permissions: "777", Digest: &file.Digest{ - Algorithm: "sha1", + Algorithm: "'Q1'+base64(sha1)", Value: "Q1kiljhXXH1LlQroHsEJIkPZg2eiw=", }, }, { Path: "/etc/passwd", Digest: &file.Digest{ - Algorithm: "sha1", + Algorithm: "'Q1'+base64(sha1)", Value: "Q1TchuuLUfur0izvfZQZxgN/LJhB8=", }, }, { Path: "/etc/profile", Digest: &file.Digest{ - Algorithm: "sha1", + Algorithm: "'Q1'+base64(sha1)", Value: "Q1KpFb8kl5LvwXWlY3e58FNsjrI34=", }, }, { Path: "/etc/protocols", Digest: &file.Digest{ - Algorithm: "sha1", + Algorithm: "'Q1'+base64(sha1)", Value: "Q13FqXUnvuOpMDrH/6rehxuYAEE34=", }, }, { Path: "/etc/services", Digest: &file.Digest{ - Algorithm: "sha1", + Algorithm: "'Q1'+base64(sha1)", Value: "Q1C6HJNgQvLWqt5VY+n7MZJ1rsDuY=", }, }, @@ -285,21 +286,21 @@ func TestSinglePackageDetails(t *testing.T) { OwnerGID: "42", Permissions: "640", Digest: &file.Digest{ - Algorithm: "sha1", + Algorithm: "'Q1'+base64(sha1)", Value: "Q1ltrPIAW2zHeDiajsex2Bdmq3uqA=", }, }, { Path: "/etc/shells", Digest: &file.Digest{ - Algorithm: "sha1", + Algorithm: "'Q1'+base64(sha1)", Value: "Q1ojm2YdpCJ6B/apGDaZ/Sdb2xJkA=", }, }, { Path: "/etc/sysctl.conf", Digest: &file.Digest{ - Algorithm: "sha1", + Algorithm: "'Q1'+base64(sha1)", Value: "Q14upz3tfnNxZkIEsUhWn7Xoiw96g=", }, }, @@ -318,7 +319,7 @@ func TestSinglePackageDetails(t *testing.T) { OwnerGID: "0", Permissions: "600", Digest: &file.Digest{ - Algorithm: "sha1", + Algorithm: "'Q1'+base64(sha1)", Value: "Q1vfk1apUWI4yLJGhhNRd0kJixfvY=", }, }, @@ -331,28 +332,28 @@ func TestSinglePackageDetails(t *testing.T) { { Path: "/etc/modprobe.d/aliases.conf", Digest: &file.Digest{ - Algorithm: "sha1", + Algorithm: "'Q1'+base64(sha1)", Value: "Q1WUbh6TBYNVK7e4Y+uUvLs/7viqk=", }, }, { Path: "/etc/modprobe.d/blacklist.conf", Digest: &file.Digest{ - Algorithm: "sha1", + Algorithm: "'Q1'+base64(sha1)", Value: "Q1xxYGU6S6TLQvb7ervPrWWwAWqMg=", }, }, { Path: "/etc/modprobe.d/i386.conf", Digest: &file.Digest{ - Algorithm: "sha1", + Algorithm: "'Q1'+base64(sha1)", Value: "Q1pnay/njn6ol9cCssL7KiZZ8etlc=", }, }, { Path: "/etc/modprobe.d/kms.conf", Digest: &file.Digest{ - Algorithm: "sha1", + Algorithm: "'Q1'+base64(sha1)", Value: "Q1ynbLn3GYDpvajba/ldp1niayeog=", }, }, @@ -401,14 +402,14 @@ func TestSinglePackageDetails(t *testing.T) { { Path: "/etc/profile.d/color_prompt", Digest: &file.Digest{ - Algorithm: "sha1", + Algorithm: "'Q1'+base64(sha1)", Value: "Q10wL23GuSCVfumMRgakabUI6EsSk=", }, }, { Path: "/etc/profile.d/locale", Digest: &file.Digest{ - Algorithm: "sha1", + Algorithm: "'Q1'+base64(sha1)", Value: "Q1R4bIEpnKxxOSrlnZy9AoawqZ5DU=", }, }, @@ -436,7 +437,7 @@ func TestSinglePackageDetails(t *testing.T) { { Path: "/lib/sysctl.d/00-alpine.conf", Digest: &file.Digest{ - Algorithm: "sha1", + Algorithm: "'Q1'+base64(sha1)", Value: "Q1HpElzW1xEgmKfERtTy7oommnq6c=", }, }, @@ -479,7 +480,7 @@ func TestSinglePackageDetails(t *testing.T) { OwnerGID: "0", Permissions: "755", Digest: &file.Digest{ - Algorithm: "sha1", + Algorithm: "'Q1'+base64(sha1)", Value: "Q1YeuSmC7iDbEWrusPzA/zUQF6YSg=", }, }, @@ -537,7 +538,7 @@ func TestSinglePackageDetails(t *testing.T) { OwnerGID: "0", Permissions: "777", Digest: &file.Digest{ - Algorithm: "sha1", + Algorithm: "'Q1'+base64(sha1)", Value: "Q11/SNZz/8cK2dSKK+cJpVrZIuF4Q=", }, }, @@ -586,7 +587,7 @@ func TestSinglePackageDetails(t *testing.T) { OwnerGID: "0", Permissions: "777", Digest: &file.Digest{ - Algorithm: "sha1", + Algorithm: "'Q1'+base64(sha1)", Value: "Q1dzbdazYZA2nTzSIG3YyNw7d4Juc=", }, }, @@ -599,7 +600,7 @@ func TestSinglePackageDetails(t *testing.T) { OwnerGID: "0", Permissions: "777", Digest: &file.Digest{ - Algorithm: "sha1", + Algorithm: "'Q1'+base64(sha1)", Value: "Q1OFZt+ZMp7j0Gny0rqSKuWJyqYmA=", }, }, @@ -704,7 +705,7 @@ func TestMultiplePackages(t *testing.T) { OwnerGID: "0", Permissions: "755", Digest: &file.Digest{ - Algorithm: "sha1", + Algorithm: "'Q1'+base64(sha1)", Value: "Q1Kja2+POZKxEkUOZqwSjC6kmaED4=", }, }, @@ -720,7 +721,7 @@ func TestMultiplePackages(t *testing.T) { OwnerGID: "0", Permissions: "755", Digest: &file.Digest{ - Algorithm: "sha1", + Algorithm: "'Q1'+base64(sha1)", Value: "Q1CVmFbdY+Hv6/jAHl1gec2Kbx1EY=", }, }, @@ -730,7 +731,7 @@ func TestMultiplePackages(t *testing.T) { OwnerGID: "0", Permissions: "755", Digest: &file.Digest{ - Algorithm: "sha1", + Algorithm: "'Q1'+base64(sha1)", Value: "Q1yFAhGggmL7ERgbIA7KQxyTzf3ks=", }, }, @@ -740,7 +741,7 @@ func TestMultiplePackages(t *testing.T) { OwnerGID: "0", Permissions: "755", Digest: &file.Digest{ - Algorithm: "sha1", + Algorithm: "'Q1'+base64(sha1)", Value: "Q1dAdYK8M/INibRQF5B3Rw7cmNDDA=", }, }, @@ -750,7 +751,7 @@ func TestMultiplePackages(t *testing.T) { OwnerGID: "0", Permissions: "755", Digest: &file.Digest{ - Algorithm: "sha1", + Algorithm: "'Q1'+base64(sha1)", Value: "Q1eR2Dz/WylabgbWMTkd2+hGmEya4=", }, }, @@ -795,3 +796,33 @@ func TestMultiplePackages(t *testing.T) { }) } } + +func Test_processChecksum(t *testing.T) { + tests := []struct { + name string + value string + want file.Digest + }{ + { + name: "md5", + value: "38870ede8700535d7382ff66a46fcc2f", + want: file.Digest{ + Algorithm: "md5", + Value: "38870ede8700535d7382ff66a46fcc2f", + }, + }, + { + name: "sha1", + value: "Q1Kja2+POZKxEkUOZqwSjC6kmaED4=", + want: file.Digest{ + Algorithm: "'Q1'+base64(sha1)", + Value: "Q1Kja2+POZKxEkUOZqwSjC6kmaED4=", + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + assert.Equal(t, &tt.want, processChecksum(tt.value)) + }) + } +}