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