@@ -47,6 +47,8 @@ const (
4747 statusDir = "var/lib/dpkg/status.d/"
4848 infoDir = "var/lib/dpkg/info/"
4949 availableFile = "var/lib/dpkg/available"
50+
51+ md5sumsExtension = ".md5sums"
5052)
5153
5254var (
@@ -72,14 +74,14 @@ func (a dpkgAnalyzer) PostAnalyze(_ context.Context, input analyzer.PostAnalysis
7274
7375 // parse other files
7476 err = fsutils .WalkDir (input .FS , "." , required , func (path string , _ fs.DirEntry , r io.Reader ) error {
75- // parse list files
76- if a .isListFile (filepath .Split (path )) {
77+ // parse *md5sums files
78+ if a .isMd5SumsFile (filepath .Split (path )) {
7779 scanner := bufio .NewScanner (r )
78- systemFiles , err := a .parseDpkgInfoList (scanner )
80+ systemFiles , err := a .parseDpkgMd5sums (scanner )
7981 if err != nil {
80- return err
82+ return xerrors . Errorf ( "failed to parse %s file: %w" , path , err )
8183 }
82- packageFiles [strings .TrimSuffix (filepath .Base (path ), ".list" )] = systemFiles
84+ packageFiles [strings .TrimSuffix (filepath .Base (path ), md5sumsExtension )] = systemFiles
8385 systemInstalledFiles = append (systemInstalledFiles , systemFiles ... )
8486 return nil
8587 }
@@ -113,46 +115,31 @@ func (a dpkgAnalyzer) PostAnalyze(_ context.Context, input analyzer.PostAnalysis
113115
114116}
115117
116- // parseDpkgInfoList parses /var/lib/dpkg/info/*.list
117- func (a dpkgAnalyzer ) parseDpkgInfoList (scanner * bufio.Scanner ) ([]string , error ) {
118- var (
119- allLines []string
120- installedFiles []string
121- previous string
122- )
123-
118+ // parseDpkgMd5sums parses `/var/lib/dpkg/*/*.md5sums` file.
119+ //
120+ // `*.md5sums` files don't contain links (see https://github.com/aquasecurity/trivy/pull/9131#discussion_r2182557288).
121+ // But Trivy doesn't support links, so this will not cause problems.
122+ // TODO use `*.list` files instead of `*.md5sums` files when Trivy will support links.
123+ func (a dpkgAnalyzer ) parseDpkgMd5sums (scanner * bufio.Scanner ) ([]string , error ) {
124+ var installedFiles []string
124125 for scanner .Scan () {
125126 current := scanner .Text ()
126- if current == "/." {
127- continue
127+
128+ // md5sums file use the following format:
129+ // <digest> <filepath> (2 spaces)
130+ // cf. https://man7.org/linux/man-pages/man5/deb-md5sums.5.html
131+ _ , file , ok := strings .Cut (current , " " )
132+ if ! ok {
133+ return nil , xerrors .Errorf ("invalid md5sums line format: %s" , current )
128134 }
129- allLines = append (allLines , current )
135+ installedFiles = append (installedFiles , "/" + file ) // md5sums files don't contain leading slash
130136 }
131137
132138 if err := scanner .Err (); err != nil {
133139 return nil , xerrors .Errorf ("scan error: %w" , err )
134140 }
135141
136- // Add the file if it is not directory.
137- // e.g.
138- // /usr/sbin
139- // /usr/sbin/tarcat
140- //
141- // In the above case, we should take only /usr/sbin/tarcat since /usr/sbin is a directory
142- // sort first,see here:https://github.com/aquasecurity/trivy/discussions/6543
143- sort .Strings (allLines )
144- for _ , current := range allLines {
145- if ! strings .HasPrefix (current , previous + "/" ) {
146- installedFiles = append (installedFiles , previous )
147- }
148- previous = current
149- }
150-
151- // // Add the last file
152- if previous != "" && ! strings .HasSuffix (previous , "/" ) {
153- installedFiles = append (installedFiles , previous )
154- }
155-
142+ sort .Strings (installedFiles )
156143 return installedFiles , nil
157144}
158145
@@ -290,14 +277,10 @@ func (a dpkgAnalyzer) parseDpkgPkg(header textproto.MIMEHeader) *types.Package {
290277
291278func (a dpkgAnalyzer ) Required (filePath string , _ os.FileInfo ) bool {
292279 dir , fileName := filepath .Split (filePath )
293- if a .isListFile (dir , fileName ) || filePath == statusFile || filePath == availableFile {
280+ if a .isMd5SumsFile (dir , fileName ) || filePath == statusFile || filePath == availableFile || dir == statusDir {
294281 return true
295282 }
296283
297- // skip `*.md5sums` files from `status.d` directory
298- if dir == statusDir && filepath .Ext (fileName ) != ".md5sums" {
299- return true
300- }
301284 return false
302285}
303286
@@ -356,12 +339,14 @@ func (a dpkgAnalyzer) consolidateDependencies(pkgs map[string]*types.Package, pk
356339 }
357340}
358341
359- func (a dpkgAnalyzer ) isListFile (dir , fileName string ) bool {
360- if dir != infoDir {
342+ func (a dpkgAnalyzer ) isMd5SumsFile (dir , fileName string ) bool {
343+ // - var/lib/dpkg/info/*.md5sums is default path
344+ // - var/lib/dpkg/status.d/*.md5sums path in distroless images (see https://github.com/GoogleContainerTools/distroless/blob/5c119701429fb742ab45682cfc3073f911bad4bf/PACKAGE_METADATA.md#omitted-files)
345+ if dir != infoDir && dir != statusDir {
361346 return false
362347 }
363348
364- return strings .HasSuffix (fileName , ".list" )
349+ return strings .HasSuffix (fileName , md5sumsExtension )
365350}
366351
367352func (a dpkgAnalyzer ) Type () analyzer.Type {
0 commit comments