enhance alpine file discovery (#248)

Signed-off-by: Alex Goodman <alex.goodman@anchore.com>
This commit is contained in:
Alex Goodman 2020-11-04 09:56:52 -05:00 committed by GitHub
parent 773581704c
commit a52750bdd3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 583 additions and 24 deletions

View File

@ -89,7 +89,16 @@ func parseApkDBEntry(reader io.Reader) (*pkg.ApkMetadata, error) {
switch key { switch key {
case "F": case "F":
lastFile = "/" + value currentFile := "/" + value
newFileRecord := pkg.ApkFileRecord{
Path: currentFile,
}
files = append(files, newFileRecord)
fileRecord = &files[len(files)-1]
// future aux references are relative to previous "F" records
lastFile = currentFile
continue continue
case "R": case "R":
newFileRecord := pkg.ApkFileRecord{ newFileRecord := pkg.ApkFileRecord{
@ -97,7 +106,7 @@ func parseApkDBEntry(reader io.Reader) (*pkg.ApkMetadata, error) {
} }
files = append(files, newFileRecord) files = append(files, newFileRecord)
fileRecord = &files[len(files)-1] fileRecord = &files[len(files)-1]
case "a": case "a", "M":
ownershipFields := strings.Split(value, ":") ownershipFields := strings.Split(value, ":")
if len(ownershipFields) < 3 { if len(ownershipFields) < 3 {
log.Errorf("unexpected APK ownership field: %q", value) log.Errorf("unexpected APK ownership field: %q", value)
@ -108,7 +117,7 @@ func parseApkDBEntry(reader io.Reader) (*pkg.ApkMetadata, error) {
continue continue
} }
fileRecord.OwnerUID = ownershipFields[0] fileRecord.OwnerUID = ownershipFields[0]
fileRecord.OwnerGUI = ownershipFields[1] fileRecord.OwnerGID = ownershipFields[1]
fileRecord.Permissions = ownershipFields[2] fileRecord.Permissions = ownershipFields[2]
// note: there are more optional fields available that we are not capturing, e.g.: // note: there are more optional fields available that we are not capturing, e.g.:
// "0:0:755:Q1JaDEHQHBbizhEzoWK1YxuraNU/4=" // "0:0:755:Q1JaDEHQHBbizhEzoWK1YxuraNU/4="

View File

@ -19,10 +19,25 @@ func TestExtraFileAttributes(t *testing.T) {
name: "test extra file attributes (checksum) are ignored", name: "test extra file attributes (checksum) are ignored",
expected: pkg.ApkMetadata{ expected: pkg.ApkMetadata{
Files: []pkg.ApkFileRecord{ Files: []pkg.ApkFileRecord{
{
Path: "/usr",
},
{
Path: "/usr/lib",
},
{
Path: "/usr/lib/jvm",
},
{
Path: "/usr/lib/jvm/java-1.8-openjdk",
},
{
Path: "/usr/lib/jvm/java-1.8-openjdk/bin",
},
{ {
Path: "/usr/lib/jvm/java-1.8-openjdk/bin/policytool", Path: "/usr/lib/jvm/java-1.8-openjdk/bin/policytool",
OwnerUID: "0", OwnerUID: "0",
OwnerGUI: "0", OwnerGID: "0",
Permissions: "755", Permissions: "755",
Checksum: "Q1M0C9qfC/+kdRiOodeihG2GMRtkE=", Checksum: "Q1M0C9qfC/+kdRiOodeihG2GMRtkE=",
}, },
@ -60,13 +75,13 @@ func TestExtraFileAttributes(t *testing.T) {
} }
} }
func TestSinglePackage(t *testing.T) { func TestSinglePackageDetails(t *testing.T) {
tests := []struct { tests := []struct {
name string fixture string
expected pkg.ApkMetadata expected pkg.ApkMetadata
}{ }{
{ {
name: "Test Single Package", fixture: "test-fixtures/single",
expected: pkg.ApkMetadata{ expected: pkg.ApkMetadata{
Package: "musl-utils", Package: "musl-utils",
OriginPackage: "musl", OriginPackage: "musl",
@ -82,51 +97,428 @@ func TestSinglePackage(t *testing.T) {
PullChecksum: "Q1bTtF5526tETKfL+lnigzIDvm+2o=", PullChecksum: "Q1bTtF5526tETKfL+lnigzIDvm+2o=",
GitCommitOfAport: "4024cc3b29ad4c65544ad068b8f59172b5494306", GitCommitOfAport: "4024cc3b29ad4c65544ad068b8f59172b5494306",
Files: []pkg.ApkFileRecord{ Files: []pkg.ApkFileRecord{
{
Path: "/sbin",
},
{ {
Path: "/sbin/ldconfig", Path: "/sbin/ldconfig",
OwnerUID: "0", OwnerUID: "0",
OwnerGUI: "0", OwnerGID: "0",
Permissions: "755", Permissions: "755",
Checksum: "Q1Kja2+POZKxEkUOZqwSjC6kmaED4=", Checksum: "Q1Kja2+POZKxEkUOZqwSjC6kmaED4=",
}, },
{
Path: "/usr",
},
{
Path: "/usr/bin",
},
{ {
Path: "/usr/bin/iconv", Path: "/usr/bin/iconv",
OwnerUID: "0", OwnerUID: "0",
OwnerGUI: "0", OwnerGID: "0",
Permissions: "755", Permissions: "755",
Checksum: "Q1CVmFbdY+Hv6/jAHl1gec2Kbx1EY=", Checksum: "Q1CVmFbdY+Hv6/jAHl1gec2Kbx1EY=",
}, },
{ {
Path: "/usr/bin/ldd", Path: "/usr/bin/ldd",
OwnerUID: "0", OwnerUID: "0",
OwnerGUI: "0", OwnerGID: "0",
Permissions: "755", Permissions: "755",
Checksum: "Q1yFAhGggmL7ERgbIA7KQxyTzf3ks=", Checksum: "Q1yFAhGggmL7ERgbIA7KQxyTzf3ks=",
}, },
{ {
Path: "/usr/bin/getconf", Path: "/usr/bin/getconf",
OwnerUID: "0", OwnerUID: "0",
OwnerGUI: "0", OwnerGID: "0",
Permissions: "755", Permissions: "755",
Checksum: "Q1dAdYK8M/INibRQF5B3Rw7cmNDDA=", Checksum: "Q1dAdYK8M/INibRQF5B3Rw7cmNDDA=",
}, },
{ {
Path: "/usr/bin/getent", Path: "/usr/bin/getent",
OwnerUID: "0", OwnerUID: "0",
OwnerGUI: "0", OwnerGID: "0",
Permissions: "755", Permissions: "755",
Checksum: "Q1eR2Dz/WylabgbWMTkd2+hGmEya4=", Checksum: "Q1eR2Dz/WylabgbWMTkd2+hGmEya4=",
}, },
}, },
}, },
}, },
{
fixture: "test-fixtures/base",
expected: pkg.ApkMetadata{
Package: "alpine-baselayout",
OriginPackage: "alpine-baselayout",
Version: "3.2.0-r6",
Description: "Alpine base dir structure and init scripts",
Maintainer: "Natanael Copa <ncopa@alpinelinux.org>",
License: "GPL-2.0-only",
Architecture: "x86_64",
URL: "https://git.alpinelinux.org/cgit/aports/tree/main/alpine-baselayout",
Size: 19917,
InstalledSize: 409600,
PullDependencies: "/bin/sh so:libc.musl-x86_64.so.1",
PullChecksum: "Q1myMNfd7u5v5UTgNHeq1e31qTjZU=",
GitCommitOfAport: "e1c51734fa96fa4bac92e9f14a474324c67916fc",
Files: []pkg.ApkFileRecord{
{
Path: "/dev",
},
{
Path: "/dev/pts",
},
{
Path: "/dev/shm",
},
{
Path: "/etc",
},
{
Path: "/etc/fstab",
Checksum: "Q11Q7hNe8QpDS531guqCdrXBzoA/o=",
},
{
Path: "/etc/group",
Checksum: "Q1oJ16xWudgKOrXIEquEDzlF2Lsm4=",
},
{
Path: "/etc/hostname",
Checksum: "Q16nVwYVXP/tChvUPdukVD2ifXOmc=",
},
{
Path: "/etc/hosts",
Checksum: "Q1BD6zJKZTRWyqGnPi4tSfd3krsMU=",
},
{
Path: "/etc/inittab",
Checksum: "Q1TsthbhW7QzWRe1E/NKwTOuD4pHc=",
},
{
Path: "/etc/modules",
Checksum: "Q1toogjUipHGcMgECgPJX64SwUT1M=",
},
{
Path: "/etc/motd",
Checksum: "Q1XmduVVNURHQ27TvYp1Lr5TMtFcA=",
},
{
Path: "/etc/mtab",
OwnerUID: "0",
OwnerGID: "0",
Permissions: "777",
Checksum: "Q1kiljhXXH1LlQroHsEJIkPZg2eiw=",
},
{
Path: "/etc/passwd",
Checksum: "Q1TchuuLUfur0izvfZQZxgN/LJhB8=",
},
{
Path: "/etc/profile",
Checksum: "Q1KpFb8kl5LvwXWlY3e58FNsjrI34=",
},
{
Path: "/etc/protocols",
Checksum: "Q13FqXUnvuOpMDrH/6rehxuYAEE34=",
},
{
Path: "/etc/services",
Checksum: "Q1C6HJNgQvLWqt5VY+n7MZJ1rsDuY=",
},
{
Path: "/etc/shadow",
OwnerUID: "0",
OwnerGID: "42",
Permissions: "640",
Checksum: "Q1ltrPIAW2zHeDiajsex2Bdmq3uqA=",
},
{
Path: "/etc/shells",
Checksum: "Q1ojm2YdpCJ6B/apGDaZ/Sdb2xJkA=",
},
{
Path: "/etc/sysctl.conf",
Checksum: "Q14upz3tfnNxZkIEsUhWn7Xoiw96g=",
},
{
Path: "/etc/apk",
},
{
Path: "/etc/conf.d",
},
{
Path: "/etc/crontabs",
},
{
Path: "/etc/crontabs/root",
OwnerUID: "0",
OwnerGID: "0",
Permissions: "600",
Checksum: "Q1vfk1apUWI4yLJGhhNRd0kJixfvY=",
},
{
Path: "/etc/init.d",
},
{
Path: "/etc/modprobe.d",
},
{
Path: "/etc/modprobe.d/aliases.conf",
Checksum: "Q1WUbh6TBYNVK7e4Y+uUvLs/7viqk=",
},
{
Path: "/etc/modprobe.d/blacklist.conf",
Checksum: "Q1xxYGU6S6TLQvb7ervPrWWwAWqMg=",
},
{
Path: "/etc/modprobe.d/i386.conf",
Checksum: "Q1pnay/njn6ol9cCssL7KiZZ8etlc=",
},
{
Path: "/etc/modprobe.d/kms.conf",
Checksum: "Q1ynbLn3GYDpvajba/ldp1niayeog=",
},
{
Path: "/etc/modules-load.d",
},
{
Path: "/etc/network",
},
{
Path: "/etc/network/if-down.d",
},
{
Path: "/etc/network/if-post-down.d",
},
{
Path: "/etc/network/if-pre-up.d",
},
{
Path: "/etc/network/if-up.d",
},
{
Path: "/etc/opt",
},
{
Path: "/etc/periodic",
},
{
Path: "/etc/periodic/15min",
},
{
Path: "/etc/periodic/daily",
},
{
Path: "/etc/periodic/hourly",
},
{
Path: "/etc/periodic/monthly",
},
{
Path: "/etc/periodic/weekly",
},
{
Path: "/etc/profile.d",
},
{
Path: "/etc/profile.d/color_prompt",
Checksum: "Q10wL23GuSCVfumMRgakabUI6EsSk=",
},
{
Path: "/etc/profile.d/locale",
Checksum: "Q1R4bIEpnKxxOSrlnZy9AoawqZ5DU=",
},
{
Path: "/etc/sysctl.d",
},
{
Path: "/home",
},
{
Path: "/lib",
},
{
Path: "/lib/firmware",
},
{
Path: "/lib/mdev",
},
{
Path: "/lib/modules-load.d",
},
{
Path: "/lib/sysctl.d",
},
{
Path: "/lib/sysctl.d/00-alpine.conf",
Checksum: "Q1HpElzW1xEgmKfERtTy7oommnq6c=",
},
{
Path: "/media",
},
{
Path: "/media/cdrom",
},
{
Path: "/media/floppy",
},
{
Path: "/media/usb",
},
{
Path: "/mnt",
},
{
Path: "/opt",
},
{
Path: "/proc",
},
{
Path: "/root",
OwnerUID: "0",
OwnerGID: "0",
Permissions: "700",
},
{
Path: "/run",
},
{
Path: "/sbin",
},
{
Path: "/sbin/mkmntdirs",
OwnerUID: "0",
OwnerGID: "0",
Permissions: "755",
Checksum: "Q1YeuSmC7iDbEWrusPzA/zUQF6YSg=",
},
{
Path: "/srv",
},
{
Path: "/sys",
},
{
Path: "/tmp",
OwnerUID: "0",
OwnerGID: "0",
Permissions: "1777",
},
{
Path: "/usr",
},
{
Path: "/usr/lib",
},
{
Path: "/usr/lib/modules-load.d",
},
{
Path: "/usr/local",
},
{
Path: "/usr/local/bin",
},
{
Path: "/usr/local/lib",
},
{
Path: "/usr/local/share",
},
{
Path: "/usr/sbin",
},
{
Path: "/usr/share",
},
{
Path: "/usr/share/man",
},
{
Path: "/usr/share/misc",
},
{
Path: "/var",
},
{
Path: "/var/run",
OwnerUID: "0",
OwnerGID: "0",
Permissions: "777",
Checksum: "Q11/SNZz/8cK2dSKK+cJpVrZIuF4Q=",
},
{
Path: "/var/cache",
},
{
Path: "/var/cache/misc",
},
{
Path: "/var/empty",
OwnerUID: "0",
OwnerGID: "0",
Permissions: "555",
},
{
Path: "/var/lib",
},
{
Path: "/var/lib/misc",
},
{
Path: "/var/local",
},
{
Path: "/var/lock",
},
{
Path: "/var/lock/subsys",
},
{
Path: "/var/log",
},
{
Path: "/var/mail",
},
{
Path: "/var/opt",
},
{
Path: "/var/spool",
},
{
Path: "/var/spool/mail",
OwnerUID: "0",
OwnerGID: "0",
Permissions: "777",
Checksum: "Q1dzbdazYZA2nTzSIG3YyNw7d4Juc=",
},
{
Path: "/var/spool/cron",
},
{
Path: "/var/spool/cron/crontabs",
OwnerUID: "0",
OwnerGID: "0",
Permissions: "777",
Checksum: "Q1OFZt+ZMp7j0Gny0rqSKuWJyqYmA=",
},
{
Path: "/var/tmp",
OwnerUID: "0",
OwnerGID: "0",
Permissions: "1777",
},
},
},
},
} }
for _, test := range tests { for _, test := range tests {
t.Run(test.name, func(t *testing.T) { t.Run(test.fixture, func(t *testing.T) {
file, err := os.Open("test-fixtures/single") file, err := os.Open(test.fixture)
if err != nil { if err != nil {
t.Fatal("Unable to read test_fixtures/single: ", err) t.Fatal("Unable to read fixture: ", err)
} }
defer func() { defer func() {
err := file.Close() err := file.Close()
@ -203,38 +595,47 @@ func TestMultiplePackages(t *testing.T) {
PullChecksum: "Q1bTtF5526tETKfL+lnigzIDvm+2o=", PullChecksum: "Q1bTtF5526tETKfL+lnigzIDvm+2o=",
GitCommitOfAport: "4024cc3b29ad4c65544ad068b8f59172b5494306", GitCommitOfAport: "4024cc3b29ad4c65544ad068b8f59172b5494306",
Files: []pkg.ApkFileRecord{ Files: []pkg.ApkFileRecord{
{
Path: "/sbin",
},
{ {
Path: "/sbin/ldconfig", Path: "/sbin/ldconfig",
OwnerUID: "0", OwnerUID: "0",
OwnerGUI: "0", OwnerGID: "0",
Permissions: "755", Permissions: "755",
Checksum: "Q1Kja2+POZKxEkUOZqwSjC6kmaED4=", Checksum: "Q1Kja2+POZKxEkUOZqwSjC6kmaED4=",
}, },
{
Path: "/usr",
},
{
Path: "/usr/bin",
},
{ {
Path: "/usr/bin/iconv", Path: "/usr/bin/iconv",
OwnerUID: "0", OwnerUID: "0",
OwnerGUI: "0", OwnerGID: "0",
Permissions: "755", Permissions: "755",
Checksum: "Q1CVmFbdY+Hv6/jAHl1gec2Kbx1EY=", Checksum: "Q1CVmFbdY+Hv6/jAHl1gec2Kbx1EY=",
}, },
{ {
Path: "/usr/bin/ldd", Path: "/usr/bin/ldd",
OwnerUID: "0", OwnerUID: "0",
OwnerGUI: "0", OwnerGID: "0",
Permissions: "755", Permissions: "755",
Checksum: "Q1yFAhGggmL7ERgbIA7KQxyTzf3ks=", Checksum: "Q1yFAhGggmL7ERgbIA7KQxyTzf3ks=",
}, },
{ {
Path: "/usr/bin/getconf", Path: "/usr/bin/getconf",
OwnerUID: "0", OwnerUID: "0",
OwnerGUI: "0", OwnerGID: "0",
Permissions: "755", Permissions: "755",
Checksum: "Q1dAdYK8M/INibRQF5B3Rw7cmNDDA=", Checksum: "Q1dAdYK8M/INibRQF5B3Rw7cmNDDA=",
}, },
{ {
Path: "/usr/bin/getent", Path: "/usr/bin/getent",
OwnerUID: "0", OwnerUID: "0",
OwnerGUI: "0", OwnerGID: "0",
Permissions: "755", Permissions: "755",
Checksum: "Q1eR2Dz/WylabgbWMTkd2+hGmEya4=", Checksum: "Q1eR2Dz/WylabgbWMTkd2+hGmEya4=",
}, },

View File

@ -0,0 +1,149 @@
C:Q1myMNfd7u5v5UTgNHeq1e31qTjZU=
P:alpine-baselayout
V:3.2.0-r6
A:x86_64
S:19917
I:409600
T:Alpine base dir structure and init scripts
U:https://git.alpinelinux.org/cgit/aports/tree/main/alpine-baselayout
L:GPL-2.0-only
o:alpine-baselayout
m:Natanael Copa <ncopa@alpinelinux.org>
t:1590679557
c:e1c51734fa96fa4bac92e9f14a474324c67916fc
D:/bin/sh so:libc.musl-x86_64.so.1
p:cmd:mkmntdirs
F:dev
F:dev/pts
F:dev/shm
F:etc
R:fstab
Z:Q11Q7hNe8QpDS531guqCdrXBzoA/o=
R:group
Z:Q1oJ16xWudgKOrXIEquEDzlF2Lsm4=
R:hostname
Z:Q16nVwYVXP/tChvUPdukVD2ifXOmc=
R:hosts
Z:Q1BD6zJKZTRWyqGnPi4tSfd3krsMU=
R:inittab
Z:Q1TsthbhW7QzWRe1E/NKwTOuD4pHc=
R:modules
Z:Q1toogjUipHGcMgECgPJX64SwUT1M=
R:motd
Z:Q1XmduVVNURHQ27TvYp1Lr5TMtFcA=
R:mtab
a:0:0:777
Z:Q1kiljhXXH1LlQroHsEJIkPZg2eiw=
R:passwd
Z:Q1TchuuLUfur0izvfZQZxgN/LJhB8=
R:profile
Z:Q1KpFb8kl5LvwXWlY3e58FNsjrI34=
R:protocols
Z:Q13FqXUnvuOpMDrH/6rehxuYAEE34=
R:services
Z:Q1C6HJNgQvLWqt5VY+n7MZJ1rsDuY=
R:shadow
a:0:42:640
Z:Q1ltrPIAW2zHeDiajsex2Bdmq3uqA=
R:shells
Z:Q1ojm2YdpCJ6B/apGDaZ/Sdb2xJkA=
R:sysctl.conf
Z:Q14upz3tfnNxZkIEsUhWn7Xoiw96g=
F:etc/apk
F:etc/conf.d
F:etc/crontabs
R:root
a:0:0:600
Z:Q1vfk1apUWI4yLJGhhNRd0kJixfvY=
F:etc/init.d
F:etc/modprobe.d
R:aliases.conf
Z:Q1WUbh6TBYNVK7e4Y+uUvLs/7viqk=
R:blacklist.conf
Z:Q1xxYGU6S6TLQvb7ervPrWWwAWqMg=
R:i386.conf
Z:Q1pnay/njn6ol9cCssL7KiZZ8etlc=
R:kms.conf
Z:Q1ynbLn3GYDpvajba/ldp1niayeog=
F:etc/modules-load.d
F:etc/network
F:etc/network/if-down.d
F:etc/network/if-post-down.d
F:etc/network/if-pre-up.d
F:etc/network/if-up.d
F:etc/opt
F:etc/periodic
F:etc/periodic/15min
F:etc/periodic/daily
F:etc/periodic/hourly
F:etc/periodic/monthly
F:etc/periodic/weekly
F:etc/profile.d
R:color_prompt
Z:Q10wL23GuSCVfumMRgakabUI6EsSk=
R:locale
Z:Q1R4bIEpnKxxOSrlnZy9AoawqZ5DU=
F:etc/sysctl.d
F:home
F:lib
F:lib/firmware
F:lib/mdev
F:lib/modules-load.d
F:lib/sysctl.d
R:00-alpine.conf
Z:Q1HpElzW1xEgmKfERtTy7oommnq6c=
F:media
F:media/cdrom
F:media/floppy
F:media/usb
F:mnt
F:opt
F:proc
F:root
M:0:0:700
F:run
F:sbin
R:mkmntdirs
a:0:0:755
Z:Q1YeuSmC7iDbEWrusPzA/zUQF6YSg=
F:srv
F:sys
F:tmp
M:0:0:1777
F:usr
F:usr/lib
F:usr/lib/modules-load.d
F:usr/local
F:usr/local/bin
F:usr/local/lib
F:usr/local/share
F:usr/sbin
F:usr/share
F:usr/share/man
F:usr/share/misc
F:var
R:run
a:0:0:777
Z:Q11/SNZz/8cK2dSKK+cJpVrZIuF4Q=
F:var/cache
F:var/cache/misc
F:var/empty
M:0:0:555
F:var/lib
F:var/lib/misc
F:var/local
F:var/lock
F:var/lock/subsys
F:var/log
F:var/mail
F:var/opt
F:var/spool
R:mail
a:0:0:777
Z:Q1dzbdazYZA2nTzSIG3YyNw7d4Juc=
F:var/spool/cron
R:crontabs
a:0:0:777
Z:Q1OFZt+ZMp7j0Gny0rqSKuWJyqYmA=
F:var/tmp
M:0:0:1777

View File

@ -29,10 +29,10 @@ type ApkMetadata struct {
// ApkFileRecord represents a single file listing and metadata from a APK DB entry (which may have many of these file records). // ApkFileRecord represents a single file listing and metadata from a APK DB entry (which may have many of these file records).
type ApkFileRecord struct { type ApkFileRecord struct {
Path string `json:"path"` Path string `json:"path"`
OwnerUID string `json:"ownerUid"` OwnerUID string `json:"ownerUid,omitempty"`
OwnerGUI string `json:"ownerGid"` OwnerGID string `json:"ownerGid,omitempty"`
Permissions string `json:"permissions"` Permissions string `json:"permissions,omitempty"`
Checksum string `json:"checksum"` Checksum string `json:"checksum,omitempty"`
} }
func (m ApkMetadata) PackageURL() string { func (m ApkMetadata) PackageURL() string {