github.com/anchore/syft@v1.38.2/syft/pkg/cataloger/internal/pe/pe_test.go (about)

     1  package pe
     2  
     3  import (
     4  	"testing"
     5  
     6  	"github.com/google/go-cmp/cmp"
     7  	"github.com/stretchr/testify/assert"
     8  	"github.com/stretchr/testify/require"
     9  
    10  	"github.com/anchore/stereoscope/pkg/imagetest"
    11  	"github.com/anchore/syft/syft/file"
    12  	"github.com/anchore/syft/syft/source"
    13  	"github.com/anchore/syft/syft/source/stereoscopesource"
    14  )
    15  
    16  func Test_Read_DotNetDetection(t *testing.T) {
    17  	tests := []struct {
    18  		name    string
    19  		fixture string
    20  		path    string
    21  		wantVR  map[string]string
    22  		wantCLR bool
    23  		wantErr require.ErrorAssertionFunc
    24  	}{
    25  		{
    26  			name:    "newtonsoft",
    27  			path:    "/app/Newtonsoft.Json.dll",
    28  			fixture: "image-net8-app",
    29  			wantCLR: true,
    30  			wantVR: map[string]string{
    31  				// the numbers are the field parse order, which helped for debugging and understanding corrupted fields
    32  				"Comments":         "Json.NET is a popular high-performance JSON framework for .NET", // 1
    33  				"CompanyName":      "Newtonsoft",                                                     // 2
    34  				"FileDescription":  "Json.NET .NET 6.0",                                              // 3
    35  				"FileVersion":      "13.0.3.27908",                                                   // 4
    36  				"InternalName":     "Newtonsoft.Json.dll",                                            // 5
    37  				"LegalCopyright":   "Copyright © James Newton-King 2008",                             // 6
    38  				"LegalTrademarks":  "",                                                               // 7 (empty value actually exists in the string table)
    39  				"OriginalFilename": "Newtonsoft.Json.dll",                                            // 8
    40  				"ProductName":      "Json.NET",                                                       // 9
    41  				"ProductVersion":   "13.0.3+0a2e291c0d9c0c7675d445703e51750363a549ef",                // 10
    42  				"Assembly Version": "13.0.0.0",                                                       // 11
    43  			},
    44  		},
    45  		{
    46  			name:    "humanizer",
    47  			path:    "/app/Humanizer.dll",
    48  			fixture: "image-net8-app",
    49  			wantCLR: true,
    50  			wantVR: map[string]string{
    51  				"Comments":         "A micro-framework that turns your normal strings, type names, enum fields, date fields ETC into a human friendly format",
    52  				"CompanyName":      "Mehdi Khalili, Claire Novotny",
    53  				"FileDescription":  "Humanizer",
    54  				"FileVersion":      "2.14.1.48190",
    55  				"InternalName":     "Humanizer.dll",
    56  				"LegalCopyright":   "Copyright © .NET Foundation and Contributors",
    57  				"OriginalFilename": "Humanizer.dll",
    58  				"ProductName":      "Humanizer (net6.0)",
    59  				"ProductVersion":   "2.14.1+3ebc38de58",
    60  				"Assembly Version": "2.14.0.0",
    61  			},
    62  			wantErr: require.NoError,
    63  		},
    64  		{
    65  			name:    "dotnetapp",
    66  			path:    "/app/dotnetapp.dll",
    67  			fixture: "image-net8-app",
    68  			wantCLR: true,
    69  			wantVR: map[string]string{
    70  				"CompanyName":      "dotnetapp",
    71  				"FileDescription":  "dotnetapp",
    72  				"FileVersion":      "1.0.0.0",
    73  				"InternalName":     "dotnetapp.dll",
    74  				"LegalCopyright":   " ",
    75  				"OriginalFilename": "dotnetapp.dll",
    76  				"ProductName":      "dotnetapp",
    77  				"ProductVersion":   "1.0.0",
    78  				"Assembly Version": "1.0.0.0",
    79  			},
    80  			wantErr: require.NoError,
    81  		},
    82  		{
    83  			name:    "jruby",
    84  			path:    "/app/jruby_windows_9_3_15_0.exe",
    85  			fixture: "image-net8-app",
    86  			wantCLR: false, // important!
    87  			wantVR: map[string]string{
    88  				"CompanyName":      "JRuby Dev Team",
    89  				"FileDescription":  "JRuby",
    90  				"FileVersion":      "9.3.15.0",
    91  				"InternalName":     "jruby",
    92  				"LegalCopyright":   "JRuby Dev Team",
    93  				"OriginalFilename": "jruby_windows-x32_9_3_15_0.exe",
    94  				"ProductName":      "JRuby",
    95  				"ProductVersion":   "9.3.15.0",
    96  			},
    97  			wantErr: require.NoError,
    98  		},
    99  		{
   100  			name:    "single file deployment",
   101  			path:    "/app/dotnetapp.exe",
   102  			fixture: "image-net8-app-single-file",
   103  			// single file deployment does not have CLR metadata embedded in the COM descriptor. Instead we need
   104  			// to look for evidence of the CLR in other resources directory names, specifically for "CLRDEBUGINFO".
   105  			wantCLR: true,
   106  			wantVR: map[string]string{
   107  				"CompanyName":      "dotnetapp",
   108  				"FileDescription":  "dotnetapp",
   109  				"FileVersion":      "1.0.0.0",
   110  				"InternalName":     "dotnetapp.dll",
   111  				"LegalCopyright":   " ",
   112  				"OriginalFilename": "dotnetapp.dll",
   113  				"ProductName":      "dotnetapp",
   114  				"ProductVersion":   "1.0.0",
   115  				"Assembly Version": "1.0.0.0",
   116  			},
   117  			wantErr: require.NoError,
   118  		},
   119  	}
   120  
   121  	for _, tt := range tests {
   122  		t.Run(tt.name, func(t *testing.T) {
   123  			if tt.wantErr == nil {
   124  				tt.wantErr = require.NoError
   125  			}
   126  
   127  			reader := fixtureFile(t, tt.fixture, tt.path)
   128  
   129  			got, err := Read(reader)
   130  			tt.wantErr(t, err)
   131  			if err != nil {
   132  				return
   133  			}
   134  
   135  			if d := cmp.Diff(tt.wantVR, got.VersionResources); d != "" {
   136  				t.Errorf("unexpected version resources (-want +got): %s", d)
   137  			}
   138  
   139  			assert.Equal(t, tt.wantCLR, got.CLR.HasEvidenceOfCLR())
   140  		})
   141  	}
   142  }
   143  
   144  func fixtureFile(t *testing.T, fixture, path string) file.LocationReadCloser {
   145  	img := imagetest.GetFixtureImage(t, "docker-archive", fixture)
   146  
   147  	s := stereoscopesource.New(img, stereoscopesource.ImageConfig{
   148  		Reference: fixture,
   149  	})
   150  
   151  	r, err := s.FileResolver(source.SquashedScope)
   152  	require.NoError(t, err)
   153  
   154  	locs, err := r.FilesByPath(path)
   155  	require.NoError(t, err)
   156  
   157  	require.Len(t, locs, 1)
   158  	loc := locs[0]
   159  
   160  	reader, err := r.FileContentsByLocation(loc)
   161  	require.NoError(t, err)
   162  	return file.NewLocationReadCloser(loc, reader)
   163  }