github.com/devseccon/trivy@v0.47.1-0.20231123133102-bd902a0bd996/pkg/fanal/analyzer/language/rust/cargo/cargo_test.go (about)

     1  package cargo
     2  
     3  import (
     4  	"context"
     5  	"os"
     6  	"testing"
     7  
     8  	"github.com/stretchr/testify/assert"
     9  	"github.com/stretchr/testify/require"
    10  
    11  	"github.com/devseccon/trivy/pkg/detector/library/compare"
    12  	"github.com/devseccon/trivy/pkg/fanal/analyzer"
    13  	"github.com/devseccon/trivy/pkg/fanal/types"
    14  )
    15  
    16  func Test_cargoAnalyzer_Analyze(t *testing.T) {
    17  	tests := []struct {
    18  		name string
    19  		dir  string
    20  		want *analyzer.AnalysisResult
    21  	}{
    22  		{
    23  			name: "happy path",
    24  			dir:  "testdata/happy",
    25  			want: &analyzer.AnalysisResult{
    26  				Applications: []types.Application{
    27  					{
    28  						Type:     types.Cargo,
    29  						FilePath: "Cargo.lock",
    30  						Libraries: types.Packages{
    31  							{
    32  								ID:       "aho-corasick@0.7.20",
    33  								Name:     "aho-corasick",
    34  								Version:  "0.7.20",
    35  								Indirect: true,
    36  								Locations: []types.Location{
    37  									{
    38  										StartLine: 4,
    39  										EndLine:   11,
    40  									},
    41  								},
    42  								DependsOn: []string{"memchr@2.5.0"},
    43  							},
    44  							{
    45  								ID:       "libc@0.2.140",
    46  								Name:     "libc",
    47  								Version:  "0.2.140",
    48  								Indirect: true,
    49  								Locations: []types.Location{
    50  									{
    51  										StartLine: 22,
    52  										EndLine:   26,
    53  									},
    54  								},
    55  							},
    56  							{
    57  								ID:       "memchr@1.0.2",
    58  								Name:     "memchr",
    59  								Version:  "1.0.2",
    60  								Indirect: false,
    61  								Locations: []types.Location{
    62  									{
    63  										StartLine: 28,
    64  										EndLine:   35,
    65  									},
    66  								},
    67  								DependsOn: []string{"libc@0.2.140"},
    68  							},
    69  							{
    70  								ID:       "memchr@2.5.0",
    71  								Name:     "memchr",
    72  								Version:  "2.5.0",
    73  								Indirect: true,
    74  								Locations: []types.Location{
    75  									{
    76  										StartLine: 37,
    77  										EndLine:   41,
    78  									},
    79  								},
    80  							},
    81  							{
    82  								ID:       "regex@1.7.3",
    83  								Name:     "regex",
    84  								Version:  "1.7.3",
    85  								Indirect: false,
    86  								Locations: []types.Location{
    87  									{
    88  										StartLine: 43,
    89  										EndLine:   52,
    90  									},
    91  								},
    92  								DependsOn: []string{
    93  									"aho-corasick@0.7.20",
    94  									"memchr@2.5.0",
    95  									"regex-syntax@0.6.29",
    96  								},
    97  							},
    98  							{
    99  								ID:       "regex-syntax@0.5.6",
   100  								Name:     "regex-syntax",
   101  								Version:  "0.5.6",
   102  								Indirect: false,
   103  								Locations: []types.Location{
   104  									{
   105  										StartLine: 54,
   106  										EndLine:   61,
   107  									},
   108  								},
   109  								DependsOn: []string{"ucd-util@0.1.10"},
   110  							},
   111  							{
   112  								ID:       "regex-syntax@0.6.29",
   113  								Name:     "regex-syntax",
   114  								Version:  "0.6.29",
   115  								Indirect: true,
   116  								Locations: []types.Location{
   117  									{
   118  										StartLine: 63,
   119  										EndLine:   67,
   120  									},
   121  								},
   122  							},
   123  							{
   124  								ID:       "ucd-util@0.1.10",
   125  								Name:     "ucd-util",
   126  								Version:  "0.1.10",
   127  								Indirect: true,
   128  								Locations: []types.Location{
   129  									{
   130  										StartLine: 69,
   131  										EndLine:   73,
   132  									},
   133  								},
   134  							},
   135  						},
   136  					},
   137  				},
   138  			},
   139  		},
   140  		{
   141  			name: "Cargo.toml doesn't include `Dependencies` field",
   142  			dir:  "testdata/toml-only-workspace-deps",
   143  			want: &analyzer.AnalysisResult{
   144  				Applications: []types.Application{
   145  					{
   146  						Type:     types.Cargo,
   147  						FilePath: "Cargo.lock",
   148  						Libraries: types.Packages{
   149  							{
   150  								ID:       "memchr@2.5.0",
   151  								Name:     "memchr",
   152  								Version:  "2.5.0",
   153  								Indirect: false,
   154  								Locations: []types.Location{
   155  									{
   156  										StartLine: 11,
   157  										EndLine:   15,
   158  									},
   159  								},
   160  							},
   161  						},
   162  					},
   163  				},
   164  			},
   165  		},
   166  		{
   167  			name: "no Cargo.toml",
   168  			dir:  "testdata/no-cargo-toml",
   169  			want: &analyzer.AnalysisResult{
   170  				Applications: []types.Application{
   171  					{
   172  						Type:     types.Cargo,
   173  						FilePath: "Cargo.lock",
   174  						Libraries: types.Packages{
   175  							{
   176  								ID:       "aho-corasick@0.7.20",
   177  								Name:     "aho-corasick",
   178  								Version:  "0.7.20",
   179  								Indirect: false,
   180  								Locations: []types.Location{
   181  									{
   182  										StartLine: 4,
   183  										EndLine:   11,
   184  									},
   185  								},
   186  								DependsOn: []string{"memchr@2.5.0"},
   187  							},
   188  							{
   189  								ID:       "app@0.1.0",
   190  								Name:     "app",
   191  								Version:  "0.1.0",
   192  								Indirect: false,
   193  								Locations: []types.Location{
   194  									{
   195  										StartLine: 13,
   196  										EndLine:   20,
   197  									},
   198  								},
   199  								DependsOn: []string{
   200  									"memchr@1.0.2",
   201  									"regex-syntax@0.5.6",
   202  									"regex@1.7.3",
   203  								},
   204  							},
   205  							{
   206  								ID:       "libc@0.2.140",
   207  								Name:     "libc",
   208  								Version:  "0.2.140",
   209  								Indirect: false,
   210  								Locations: []types.Location{
   211  									{
   212  										StartLine: 22,
   213  										EndLine:   26,
   214  									},
   215  								},
   216  							},
   217  							{
   218  								ID:       "memchr@1.0.2",
   219  								Name:     "memchr",
   220  								Version:  "1.0.2",
   221  								Indirect: false,
   222  								Locations: []types.Location{
   223  									{
   224  										StartLine: 28,
   225  										EndLine:   35,
   226  									},
   227  								},
   228  								DependsOn: []string{"libc@0.2.140"},
   229  							},
   230  							{
   231  								ID:       "memchr@2.5.0",
   232  								Name:     "memchr",
   233  								Version:  "2.5.0",
   234  								Indirect: false,
   235  								Locations: []types.Location{
   236  									{
   237  										StartLine: 37,
   238  										EndLine:   41,
   239  									},
   240  								},
   241  							},
   242  							{
   243  								ID:       "regex@1.7.3",
   244  								Name:     "regex",
   245  								Version:  "1.7.3",
   246  								Indirect: false,
   247  								Locations: []types.Location{
   248  									{
   249  										StartLine: 43,
   250  										EndLine:   52,
   251  									},
   252  								},
   253  								DependsOn: []string{
   254  									"aho-corasick@0.7.20",
   255  									"memchr@2.5.0",
   256  									"regex-syntax@0.6.29",
   257  								},
   258  							},
   259  							{
   260  								ID:       "regex-syntax@0.5.6",
   261  								Name:     "regex-syntax",
   262  								Version:  "0.5.6",
   263  								Indirect: false,
   264  								Locations: []types.Location{
   265  									{
   266  										StartLine: 54,
   267  										EndLine:   61,
   268  									},
   269  								},
   270  								DependsOn: []string{"ucd-util@0.1.10"},
   271  							},
   272  							{
   273  								ID:       "regex-syntax@0.6.29",
   274  								Name:     "regex-syntax",
   275  								Version:  "0.6.29",
   276  								Indirect: false,
   277  								Locations: []types.Location{
   278  									{
   279  										StartLine: 63,
   280  										EndLine:   67,
   281  									},
   282  								},
   283  							},
   284  							{
   285  								ID:       "ucd-util@0.1.10",
   286  								Name:     "ucd-util",
   287  								Version:  "0.1.10",
   288  								Indirect: false,
   289  								Locations: []types.Location{
   290  									{
   291  										StartLine: 69,
   292  										EndLine:   73,
   293  									},
   294  								},
   295  							},
   296  							{
   297  								ID:       "winapi@0.3.9",
   298  								Name:     "winapi",
   299  								Version:  "0.3.9",
   300  								Indirect: false,
   301  								Locations: []types.Location{
   302  									{
   303  										StartLine: 75,
   304  										EndLine:   83,
   305  									},
   306  								},
   307  								DependsOn: []string{
   308  									"winapi-i686-pc-windows-gnu@0.4.0",
   309  									"winapi-x86_64-pc-windows-gnu@0.4.0",
   310  								},
   311  							},
   312  							{
   313  								ID:       "winapi-i686-pc-windows-gnu@0.4.0",
   314  								Name:     "winapi-i686-pc-windows-gnu",
   315  								Version:  "0.4.0",
   316  								Indirect: false,
   317  								Locations: []types.Location{
   318  									{
   319  										StartLine: 85,
   320  										EndLine:   89,
   321  									},
   322  								},
   323  							},
   324  							{
   325  								ID:       "winapi-x86_64-pc-windows-gnu@0.4.0",
   326  								Name:     "winapi-x86_64-pc-windows-gnu",
   327  								Version:  "0.4.0",
   328  								Indirect: false,
   329  								Locations: []types.Location{
   330  									{
   331  										StartLine: 91,
   332  										EndLine:   95,
   333  									},
   334  								},
   335  							},
   336  						},
   337  					},
   338  				},
   339  			},
   340  		},
   341  		{
   342  			name: "wrong Cargo.toml",
   343  			dir:  "testdata/wrong-cargo-toml",
   344  			want: &analyzer.AnalysisResult{
   345  				Applications: []types.Application{
   346  					{
   347  						Type:     types.Cargo,
   348  						FilePath: "Cargo.lock",
   349  						Libraries: types.Packages{
   350  							{
   351  								ID:       "app@0.1.0",
   352  								Name:     "app",
   353  								Version:  "0.1.0",
   354  								Indirect: false,
   355  								Locations: []types.Location{
   356  									{
   357  										StartLine: 5,
   358  										EndLine:   10,
   359  									},
   360  								},
   361  								DependsOn: []string{"memchr@2.5.0"},
   362  							},
   363  							{
   364  								ID:       "memchr@2.5.0",
   365  								Name:     "memchr",
   366  								Version:  "2.5.0",
   367  								Indirect: false,
   368  								Locations: []types.Location{
   369  									{
   370  										StartLine: 12,
   371  										EndLine:   16,
   372  									},
   373  								},
   374  							},
   375  						},
   376  					},
   377  				},
   378  			},
   379  		},
   380  		{
   381  			name: "broken Cargo.lock",
   382  			dir:  "testdata/sad",
   383  			want: &analyzer.AnalysisResult{},
   384  		},
   385  	}
   386  
   387  	for _, tt := range tests {
   388  		t.Run(tt.name, func(t *testing.T) {
   389  			a, err := newCargoAnalyzer(analyzer.AnalyzerOptions{})
   390  			require.NoError(t, err)
   391  
   392  			got, err := a.PostAnalyze(context.Background(), analyzer.PostAnalysisInput{
   393  				FS: os.DirFS(tt.dir),
   394  			})
   395  
   396  			assert.NoError(t, err)
   397  			assert.Equal(t, tt.want, got)
   398  		})
   399  	}
   400  }
   401  
   402  func TestMatchVersion(t *testing.T) {
   403  	tests := []struct {
   404  		name       string
   405  		version    string // version from Cargo.lock
   406  		constraint string // version from Cargo.toml
   407  		want       bool
   408  	}{
   409  		{
   410  			name:       "major version == 0.",
   411  			version:    "0.0.4",
   412  			constraint: "0.0.3",
   413  			want:       false,
   414  		},
   415  		{
   416  			name:       "major version > 0.",
   417  			version:    "1.2.4",
   418  			constraint: "1.2.3",
   419  			want:       true,
   420  		},
   421  		{
   422  			name:       "Caret prefix",
   423  			version:    "1.5.0",
   424  			constraint: "^1.2",
   425  			want:       true,
   426  		},
   427  		{
   428  			name:       "Tilde prefix. Minor version",
   429  			version:    "1.3.4",
   430  			constraint: "~ 1.2",
   431  			want:       false,
   432  		},
   433  		{
   434  			name:       "Tilde prefix. Patch version",
   435  			version:    "1.2.4",
   436  			constraint: "~ 1.2.3",
   437  			want:       true,
   438  		},
   439  		{
   440  			name:       "Comparison prefix",
   441  			version:    "2.5.0",
   442  			constraint: "< 2.5.0",
   443  			want:       false,
   444  		},
   445  		{
   446  			name:       "Multiple prefixes",
   447  			version:    "2.5.0",
   448  			constraint: ">= 2.5, < 2.5.1",
   449  			want:       true,
   450  		},
   451  		{
   452  			name:       "= prefix",
   453  			version:    "2.5.0",
   454  			constraint: "= 2.5",
   455  			want:       true,
   456  		},
   457  		{
   458  			name:       "`*` constraint",
   459  			version:    "2.5.0",
   460  			constraint: "*",
   461  			want:       true,
   462  		},
   463  		{
   464  			name:       "constraint with `.*`",
   465  			version:    "2.5.0",
   466  			constraint: "2.5.*",
   467  			want:       true,
   468  		},
   469  	}
   470  
   471  	for _, tt := range tests {
   472  		t.Run(tt.name, func(t *testing.T) {
   473  			a := cargoAnalyzer{
   474  				comparer: compare.GenericComparer{},
   475  			}
   476  			match, _ := a.matchVersion(tt.version, tt.constraint)
   477  			assert.Equal(t, tt.want, match)
   478  		})
   479  	}
   480  }