From 61a3d1784a1ede4e92a55384d50d5e1ba9952c25 Mon Sep 17 00:00:00 2001 From: Adam McClenaghan Date: Thu, 24 Apr 2025 17:22:36 +0100 Subject: [PATCH] (feat): support skipping archive extraction with file source (#3795) Signed-off-by: Adam McClenaghan --- syft/source/filesource/file_source.go | 21 +++++++++----- syft/source/filesource/file_source_test.go | 32 +++++++++++++++------- 2 files changed, 36 insertions(+), 17 deletions(-) diff --git a/syft/source/filesource/file_source.go b/syft/source/filesource/file_source.go index a87ace55d..fead9cce5 100644 --- a/syft/source/filesource/file_source.go +++ b/syft/source/filesource/file_source.go @@ -26,10 +26,11 @@ import ( var _ source.Source = (*fileSource)(nil) type Config struct { - Path string - Exclude source.ExcludeConfig - DigestAlgorithms []crypto.Hash - Alias source.Alias + Path string + Exclude source.ExcludeConfig + DigestAlgorithms []crypto.Hash + Alias source.Alias + SkipExtractArchive bool } type fileSource struct { @@ -58,7 +59,7 @@ func New(cfg Config) (source.Source, error) { return nil, fmt.Errorf("given path is a directory: %q", cfg.Path) } - analysisPath, cleanupFn := fileAnalysisPath(cfg.Path) + analysisPath, cleanupFn := fileAnalysisPath(cfg.Path, cfg.SkipExtractArchive) var digests []file.Digest if len(cfg.DigestAlgorithms) > 0 { @@ -206,9 +207,15 @@ func (s *fileSource) Close() error { // fileAnalysisPath returns the path given, or in the case the path is an archive, the location where the archive // contents have been made available. A cleanup function is provided for any temp files created (if any). -func fileAnalysisPath(path string) (string, func() error) { - var analysisPath = path +// Users can disable unpacking archives, allowing individual cataloguers to extract them instead (where +// supported) +func fileAnalysisPath(path string, skipExtractArchive bool) (string, func() error) { var cleanupFn = func() error { return nil } + var analysisPath = path + + if skipExtractArchive { + return analysisPath, cleanupFn + } // if the given file is an archive (as indicated by the file extension and not MIME type) then unarchive it and // use the contents as the source. Note: this does NOT recursively unarchive contents, only the given path is diff --git a/syft/source/filesource/file_source_test.go b/syft/source/filesource/file_source_test.go index 1f046d253..ff5bc824a 100644 --- a/syft/source/filesource/file_source_test.go +++ b/syft/source/filesource/file_source_test.go @@ -99,13 +99,14 @@ func TestNewFromFile_WithArchive(t *testing.T) { testutil.Chdir(t, "..") // run with source/test-fixtures testCases := []struct { - desc string - input string - expString string - inputPaths []string - expRefs int - layer2 bool - contents string + desc string + input string + expString string + inputPaths []string + expRefs int + layer2 bool + contents string + skipExtractArchive bool }{ { desc: "path detected", @@ -121,14 +122,25 @@ func TestNewFromFile_WithArchive(t *testing.T) { layer2: true, contents: "Another .vimrc file", }, + { + desc: "skip extract archive", + input: "test-fixtures/path-detected", + inputPaths: []string{"/.vimrc"}, + expRefs: 0, + layer2: false, + skipExtractArchive: true, + }, } for _, test := range testCases { t.Run(test.desc, func(t *testing.T) { archivePath := setupArchiveTest(t, test.input, test.layer2) - src, err := New(Config{ - Path: archivePath, - }) + cfg := Config{ + Path: archivePath, + SkipExtractArchive: test.skipExtractArchive, + } + + src, err := New(cfg) require.NoError(t, err) t.Cleanup(func() { require.NoError(t, src.Close())