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 }