github.com/noqcks/syft@v0.0.0-20230920222752-a9e2c4e288e5/syft/file/cataloger/filedigest/cataloger_test.go (about)

     1  package filedigest
     2  
     3  import (
     4  	"crypto"
     5  	"fmt"
     6  	"io"
     7  	"os"
     8  	"path/filepath"
     9  	"testing"
    10  
    11  	"github.com/stretchr/testify/assert"
    12  	"github.com/stretchr/testify/require"
    13  
    14  	stereoscopeFile "github.com/anchore/stereoscope/pkg/file"
    15  	"github.com/anchore/stereoscope/pkg/imagetest"
    16  	intFile "github.com/anchore/syft/internal/file"
    17  	"github.com/anchore/syft/syft/file"
    18  	"github.com/anchore/syft/syft/source"
    19  )
    20  
    21  func testDigests(t testing.TB, root string, files []string, hashes ...crypto.Hash) map[file.Coordinates][]file.Digest {
    22  	digests := make(map[file.Coordinates][]file.Digest)
    23  
    24  	for _, f := range files {
    25  		fh, err := os.Open(filepath.Join(root, f))
    26  		if err != nil {
    27  			t.Fatalf("could not open %q : %+v", f, err)
    28  		}
    29  		b, err := io.ReadAll(fh)
    30  		if err != nil {
    31  			t.Fatalf("could not read %q : %+v", f, err)
    32  		}
    33  
    34  		if len(b) == 0 {
    35  			// we don't keep digests for empty files
    36  			digests[file.NewLocation(f).Coordinates] = []file.Digest{}
    37  			continue
    38  		}
    39  
    40  		for _, hash := range hashes {
    41  			h := hash.New()
    42  			h.Write(b)
    43  			digests[file.NewLocation(f).Coordinates] = append(digests[file.NewLocation(f).Coordinates], file.Digest{
    44  				Algorithm: intFile.CleanDigestAlgorithmName(hash.String()),
    45  				Value:     fmt.Sprintf("%x", h.Sum(nil)),
    46  			})
    47  		}
    48  	}
    49  
    50  	return digests
    51  }
    52  
    53  func TestDigestsCataloger(t *testing.T) {
    54  
    55  	tests := []struct {
    56  		name     string
    57  		digests  []crypto.Hash
    58  		files    []string
    59  		expected map[file.Coordinates][]file.Digest
    60  	}{
    61  		{
    62  			name:     "md5",
    63  			digests:  []crypto.Hash{crypto.MD5},
    64  			files:    []string{"test-fixtures/last/empty/empty", "test-fixtures/last/path.txt"},
    65  			expected: testDigests(t, "test-fixtures/last", []string{"empty/empty", "path.txt"}, crypto.MD5),
    66  		},
    67  		{
    68  			name:     "md5-sha1-sha256",
    69  			digests:  []crypto.Hash{crypto.MD5, crypto.SHA1, crypto.SHA256},
    70  			files:    []string{"test-fixtures/last/empty/empty", "test-fixtures/last/path.txt"},
    71  			expected: testDigests(t, "test-fixtures/last", []string{"empty/empty", "path.txt"}, crypto.MD5, crypto.SHA1, crypto.SHA256),
    72  		},
    73  	}
    74  
    75  	for _, test := range tests {
    76  		t.Run(test.name, func(t *testing.T) {
    77  			c := NewCataloger(test.digests)
    78  
    79  			src, err := source.NewFromDirectoryPath("test-fixtures/last/")
    80  			require.NoError(t, err)
    81  
    82  			resolver, err := src.FileResolver(source.SquashedScope)
    83  			require.NoError(t, err)
    84  
    85  			actual, err := c.Catalog(resolver)
    86  			require.NoError(t, err)
    87  
    88  			assert.Equal(t, test.expected, actual, "mismatched digests")
    89  		})
    90  	}
    91  }
    92  
    93  func TestDigestsCataloger_MixFileTypes(t *testing.T) {
    94  	testImage := "image-file-type-mix"
    95  
    96  	img := imagetest.GetFixtureImage(t, "docker-archive", testImage)
    97  
    98  	src, err := source.NewFromStereoscopeImageObject(img, testImage, nil)
    99  	if err != nil {
   100  		t.Fatalf("could not create source: %+v", err)
   101  	}
   102  
   103  	resolver, err := src.FileResolver(source.SquashedScope)
   104  	if err != nil {
   105  		t.Fatalf("could not create resolver: %+v", err)
   106  	}
   107  
   108  	tests := []struct {
   109  		path     string
   110  		expected string
   111  	}{
   112  		{
   113  			path:     "/file-1.txt",
   114  			expected: "888c139e550867814eb7c33b84d76e4d",
   115  		},
   116  		// this is difficult to reproduce in a cross-platform way
   117  		//{
   118  		//	path: "/hardlink-1",
   119  		//},
   120  		{
   121  			path: "/symlink-1",
   122  		},
   123  		{
   124  			path: "/char-device-1",
   125  		},
   126  		{
   127  			path: "/block-device-1",
   128  		},
   129  		{
   130  			path: "/fifo-1",
   131  		},
   132  		{
   133  			path: "/bin",
   134  		},
   135  	}
   136  
   137  	for _, test := range tests {
   138  		t.Run(test.path, func(t *testing.T) {
   139  			c := NewCataloger([]crypto.Hash{crypto.MD5})
   140  
   141  			actual, err := c.Catalog(resolver)
   142  			if err != nil {
   143  				t.Fatalf("could not catalog: %+v", err)
   144  			}
   145  
   146  			_, ref, err := img.SquashedTree().File(stereoscopeFile.Path(test.path))
   147  			if err != nil {
   148  				t.Fatalf("unable to get file=%q : %+v", test.path, err)
   149  			}
   150  			l := file.NewLocationFromImage(test.path, *ref.Reference, img)
   151  
   152  			if len(actual[l.Coordinates]) == 0 {
   153  				if test.expected != "" {
   154  					t.Fatalf("no digest found, but expected one")
   155  				}
   156  
   157  			} else {
   158  				assert.Equal(t, actual[l.Coordinates][0].Value, test.expected, "mismatched digests")
   159  			}
   160  		})
   161  	}
   162  }