github.com/ishita82/trivy-gitaction@v0.0.0-20240206054925-e937cc05f8e3/integration/repo_test.go (about) 1 //go:build integration 2 3 package integration 4 5 import ( 6 "fmt" 7 "github.com/stretchr/testify/assert" 8 "github.com/stretchr/testify/require" 9 "os" 10 "path/filepath" 11 "strings" 12 "testing" 13 14 ftypes "github.com/aquasecurity/trivy/pkg/fanal/types" 15 "github.com/aquasecurity/trivy/pkg/types" 16 "github.com/aquasecurity/trivy/pkg/uuid" 17 ) 18 19 // TestRepository tests `trivy repo` with the local code repositories 20 func TestRepository(t *testing.T) { 21 type args struct { 22 scanner types.Scanner 23 ignoreIDs []string 24 policyPaths []string 25 namespaces []string 26 listAllPkgs bool 27 input string 28 secretConfig string 29 filePatterns []string 30 helmSet []string 31 helmValuesFile []string 32 skipFiles []string 33 skipDirs []string 34 command string 35 format types.Format 36 includeDevDeps bool 37 parallel int 38 } 39 tests := []struct { 40 name string 41 args args 42 golden string 43 override func(*types.Report) 44 }{ 45 { 46 name: "gomod", 47 args: args{ 48 scanner: types.VulnerabilityScanner, 49 input: "testdata/fixtures/repo/gomod", 50 }, 51 golden: "testdata/gomod.json.golden", 52 }, 53 { 54 name: "gomod with skip files", 55 args: args{ 56 scanner: types.VulnerabilityScanner, 57 input: "testdata/fixtures/repo/gomod", 58 skipFiles: []string{"testdata/fixtures/repo/gomod/submod2/go.mod"}, 59 }, 60 golden: "testdata/gomod-skip.json.golden", 61 }, 62 { 63 name: "gomod with skip dirs", 64 args: args{ 65 scanner: types.VulnerabilityScanner, 66 input: "testdata/fixtures/repo/gomod", 67 skipDirs: []string{"testdata/fixtures/repo/gomod/submod2"}, 68 }, 69 golden: "testdata/gomod-skip.json.golden", 70 }, 71 { 72 name: "gomod in series", 73 args: args{ 74 scanner: types.VulnerabilityScanner, 75 input: "testdata/fixtures/repo/gomod", 76 parallel: 1, 77 }, 78 golden: "testdata/gomod.json.golden", 79 }, 80 { 81 name: "npm", 82 args: args{ 83 scanner: types.VulnerabilityScanner, 84 input: "testdata/fixtures/repo/npm", 85 listAllPkgs: true, 86 }, 87 golden: "testdata/npm.json.golden", 88 }, 89 { 90 name: "npm with dev deps", 91 args: args{ 92 scanner: types.VulnerabilityScanner, 93 input: "testdata/fixtures/repo/npm", 94 listAllPkgs: true, 95 includeDevDeps: true, 96 }, 97 golden: "testdata/npm-with-dev.json.golden", 98 }, 99 { 100 name: "yarn", 101 args: args{ 102 scanner: types.VulnerabilityScanner, 103 input: "testdata/fixtures/repo/yarn", 104 listAllPkgs: true, 105 }, 106 golden: "testdata/yarn.json.golden", 107 }, 108 { 109 name: "pnpm", 110 args: args{ 111 scanner: types.VulnerabilityScanner, 112 input: "testdata/fixtures/repo/pnpm", 113 }, 114 golden: "testdata/pnpm.json.golden", 115 }, 116 { 117 name: "pip", 118 args: args{ 119 scanner: types.VulnerabilityScanner, 120 listAllPkgs: true, 121 input: "testdata/fixtures/repo/pip", 122 }, 123 golden: "testdata/pip.json.golden", 124 }, 125 { 126 name: "pipenv", 127 args: args{ 128 scanner: types.VulnerabilityScanner, 129 listAllPkgs: true, 130 input: "testdata/fixtures/repo/pipenv", 131 }, 132 golden: "testdata/pipenv.json.golden", 133 }, 134 { 135 name: "poetry", 136 args: args{ 137 scanner: types.VulnerabilityScanner, 138 listAllPkgs: true, 139 input: "testdata/fixtures/repo/poetry", 140 }, 141 golden: "testdata/poetry.json.golden", 142 }, 143 { 144 name: "pom", 145 args: args{ 146 scanner: types.VulnerabilityScanner, 147 input: "testdata/fixtures/repo/pom", 148 }, 149 golden: "testdata/pom.json.golden", 150 }, 151 { 152 name: "gradle", 153 args: args{ 154 scanner: types.VulnerabilityScanner, 155 input: "testdata/fixtures/repo/gradle", 156 }, 157 golden: "testdata/gradle.json.golden", 158 }, 159 { 160 name: "conan", 161 args: args{ 162 scanner: types.VulnerabilityScanner, 163 listAllPkgs: true, 164 input: "testdata/fixtures/repo/conan", 165 }, 166 golden: "testdata/conan.json.golden", 167 }, 168 { 169 name: "nuget", 170 args: args{ 171 scanner: types.VulnerabilityScanner, 172 listAllPkgs: true, 173 input: "testdata/fixtures/repo/nuget", 174 }, 175 golden: "testdata/nuget.json.golden", 176 }, 177 { 178 name: "dotnet", 179 args: args{ 180 scanner: types.VulnerabilityScanner, 181 listAllPkgs: true, 182 input: "testdata/fixtures/repo/dotnet", 183 }, 184 golden: "testdata/dotnet.json.golden", 185 }, 186 { 187 name: "packages-props", 188 args: args{ 189 scanner: types.VulnerabilityScanner, 190 listAllPkgs: true, 191 input: "testdata/fixtures/repo/packagesprops", 192 }, 193 golden: "testdata/packagesprops.json.golden", 194 }, 195 { 196 name: "swift", 197 args: args{ 198 scanner: types.VulnerabilityScanner, 199 listAllPkgs: true, 200 input: "testdata/fixtures/repo/swift", 201 }, 202 golden: "testdata/swift.json.golden", 203 }, 204 { 205 name: "cocoapods", 206 args: args{ 207 scanner: types.VulnerabilityScanner, 208 listAllPkgs: true, 209 input: "testdata/fixtures/repo/cocoapods", 210 }, 211 golden: "testdata/cocoapods.json.golden", 212 }, 213 { 214 name: "pubspec.lock", 215 args: args{ 216 scanner: types.VulnerabilityScanner, 217 listAllPkgs: true, 218 input: "testdata/fixtures/repo/pubspec", 219 }, 220 golden: "testdata/pubspec.lock.json.golden", 221 }, 222 { 223 name: "mix.lock", 224 args: args{ 225 scanner: types.VulnerabilityScanner, 226 listAllPkgs: true, 227 input: "testdata/fixtures/repo/mixlock", 228 }, 229 golden: "testdata/mix.lock.json.golden", 230 }, 231 { 232 name: "composer.lock", 233 args: args{ 234 scanner: types.VulnerabilityScanner, 235 listAllPkgs: true, 236 input: "testdata/fixtures/repo/composer", 237 }, 238 golden: "testdata/composer.lock.json.golden", 239 }, 240 { 241 name: "dockerfile", 242 args: args{ 243 scanner: types.MisconfigScanner, 244 input: "testdata/fixtures/repo/dockerfile", 245 namespaces: []string{"testing"}, 246 }, 247 golden: "testdata/dockerfile.json.golden", 248 }, 249 { 250 name: "dockerfile with custom file pattern", 251 args: args{ 252 scanner: types.MisconfigScanner, 253 input: "testdata/fixtures/repo/dockerfile_file_pattern", 254 namespaces: []string{"testing"}, 255 filePatterns: []string{"dockerfile:Customfile"}, 256 }, 257 golden: "testdata/dockerfile_file_pattern.json.golden", 258 }, 259 { 260 name: "dockerfile with rule exception", 261 args: args{ 262 scanner: types.MisconfigScanner, 263 policyPaths: []string{"testdata/fixtures/repo/rule-exception/policy"}, 264 input: "testdata/fixtures/repo/rule-exception", 265 }, 266 golden: "testdata/dockerfile-rule-exception.json.golden", 267 }, 268 { 269 name: "dockerfile with namespace exception", 270 args: args{ 271 scanner: types.MisconfigScanner, 272 policyPaths: []string{"testdata/fixtures/repo/namespace-exception/policy"}, 273 input: "testdata/fixtures/repo/namespace-exception", 274 }, 275 golden: "testdata/dockerfile-namespace-exception.json.golden", 276 }, 277 { 278 name: "dockerfile with custom policies", 279 args: args{ 280 scanner: types.MisconfigScanner, 281 policyPaths: []string{"testdata/fixtures/repo/custom-policy/policy"}, 282 namespaces: []string{"user"}, 283 input: "testdata/fixtures/repo/custom-policy", 284 }, 285 golden: "testdata/dockerfile-custom-policies.json.golden", 286 }, 287 { 288 name: "tarball helm chart scanning with builtin policies", 289 args: args{ 290 scanner: types.MisconfigScanner, 291 input: "testdata/fixtures/repo/helm", 292 }, 293 golden: "testdata/helm.json.golden", 294 }, 295 { 296 name: "helm chart directory scanning with builtin policies", 297 args: args{ 298 scanner: types.MisconfigScanner, 299 input: "testdata/fixtures/repo/helm_testchart", 300 }, 301 golden: "testdata/helm_testchart.json.golden", 302 }, 303 { 304 name: "helm chart directory scanning with value overrides using set", 305 args: args{ 306 scanner: types.MisconfigScanner, 307 input: "testdata/fixtures/repo/helm_testchart", 308 helmSet: []string{"securityContext.runAsUser=0"}, 309 }, 310 golden: "testdata/helm_testchart.overridden.json.golden", 311 }, 312 { 313 name: "helm chart directory scanning with value overrides using value file", 314 args: args{ 315 scanner: types.MisconfigScanner, 316 input: "testdata/fixtures/repo/helm_testchart", 317 helmValuesFile: []string{"testdata/fixtures/repo/helm_values/values.yaml"}, 318 }, 319 golden: "testdata/helm_testchart.overridden.json.golden", 320 }, 321 { 322 name: "helm chart directory scanning with builtin policies and non string Chart name", 323 args: args{ 324 scanner: types.MisconfigScanner, 325 input: "testdata/fixtures/repo/helm_badname", 326 }, 327 golden: "testdata/helm_badname.json.golden", 328 }, 329 { 330 name: "secrets", 331 args: args{ 332 scanner: "vuln,secret", 333 input: "testdata/fixtures/repo/secrets", 334 secretConfig: "testdata/fixtures/repo/secrets/trivy-secret.yaml", 335 }, 336 golden: "testdata/secrets.json.golden", 337 }, 338 { 339 name: "conda generating CycloneDX SBOM", 340 args: args{ 341 command: "rootfs", 342 format: "cyclonedx", 343 input: "testdata/fixtures/repo/conda", 344 }, 345 golden: "testdata/conda-cyclonedx.json.golden", 346 }, 347 { 348 name: "pom.xml generating CycloneDX SBOM (with vulnerabilities)", 349 args: args{ 350 command: "fs", 351 scanner: types.VulnerabilityScanner, 352 format: "cyclonedx", 353 input: "testdata/fixtures/repo/pom", 354 }, 355 golden: "testdata/pom-cyclonedx.json.golden", 356 }, 357 { 358 name: "conda generating SPDX SBOM", 359 args: args{ 360 command: "rootfs", 361 format: "spdx-json", 362 input: "testdata/fixtures/repo/conda", 363 }, 364 golden: "testdata/conda-spdx.json.golden", 365 }, 366 { 367 name: "gomod with fs subcommand", 368 args: args{ 369 command: "fs", 370 scanner: types.VulnerabilityScanner, 371 input: "testdata/fixtures/repo/gomod", 372 skipFiles: []string{"testdata/fixtures/repo/gomod/submod2/go.mod"}, 373 }, 374 golden: "testdata/gomod-skip.json.golden", 375 override: func(report *types.Report) { 376 report.ArtifactType = ftypes.ArtifactFilesystem 377 }, 378 }, 379 { 380 name: "dockerfile with fs subcommand and an alias scanner", 381 args: args{ 382 command: "fs", 383 scanner: "config", // for backward compatibility 384 policyPaths: []string{"testdata/fixtures/repo/custom-policy/policy"}, 385 namespaces: []string{"user"}, 386 input: "testdata/fixtures/repo/custom-policy", 387 }, 388 golden: "testdata/dockerfile-custom-policies.json.golden", 389 override: func(report *types.Report) { 390 report.ArtifactType = ftypes.ArtifactFilesystem 391 }, 392 }, 393 } 394 395 // Set up testing DB 396 cacheDir := initDB(t) 397 398 // Set a temp dir so that modules will not be loaded 399 t.Setenv("XDG_DATA_HOME", cacheDir) 400 401 for _, tt := range tests { 402 t.Run(tt.name, func(t *testing.T) { 403 404 command := "repo" 405 if tt.args.command != "" { 406 command = tt.args.command 407 } 408 409 format := types.FormatJSON 410 if tt.args.format != "" { 411 format = tt.args.format 412 } 413 414 osArgs := []string{ 415 "-q", 416 "--cache-dir", 417 cacheDir, 418 command, 419 "--skip-db-update", 420 "--skip-policy-update", 421 "--format", 422 string(format), 423 "--parallel", 424 fmt.Sprint(tt.args.parallel), 425 "--offline-scan", 426 } 427 428 if tt.args.scanner != "" { 429 osArgs = append(osArgs, "--scanners", string(tt.args.scanner)) 430 } 431 432 if len(tt.args.policyPaths) != 0 { 433 for _, policyPath := range tt.args.policyPaths { 434 osArgs = append(osArgs, "--config-policy", policyPath) 435 } 436 } 437 438 if len(tt.args.namespaces) != 0 { 439 for _, namespace := range tt.args.namespaces { 440 osArgs = append(osArgs, "--policy-namespaces", namespace) 441 } 442 } 443 444 if len(tt.args.ignoreIDs) != 0 { 445 trivyIgnore := ".trivyignore" 446 err := os.WriteFile(trivyIgnore, []byte(strings.Join(tt.args.ignoreIDs, "\n")), 0444) 447 assert.NoError(t, err, "failed to write .trivyignore") 448 defer os.Remove(trivyIgnore) 449 } 450 451 if len(tt.args.filePatterns) != 0 { 452 for _, filePattern := range tt.args.filePatterns { 453 osArgs = append(osArgs, "--file-patterns", filePattern) 454 } 455 } 456 457 if len(tt.args.helmSet) != 0 { 458 for _, helmSet := range tt.args.helmSet { 459 osArgs = append(osArgs, "--helm-set", helmSet) 460 } 461 } 462 463 if len(tt.args.helmValuesFile) != 0 { 464 for _, helmValuesFile := range tt.args.helmValuesFile { 465 osArgs = append(osArgs, "--helm-values", helmValuesFile) 466 } 467 } 468 469 if len(tt.args.skipFiles) != 0 { 470 for _, skipFile := range tt.args.skipFiles { 471 osArgs = append(osArgs, "--skip-files", skipFile) 472 } 473 } 474 475 if len(tt.args.skipDirs) != 0 { 476 for _, skipDir := range tt.args.skipDirs { 477 osArgs = append(osArgs, "--skip-dirs", skipDir) 478 } 479 } 480 481 // Setup the output file 482 outputFile := filepath.Join(t.TempDir(), "output.json") 483 if *update && tt.override == nil { 484 outputFile = tt.golden 485 } 486 487 if tt.args.listAllPkgs { 488 osArgs = append(osArgs, "--list-all-pkgs") 489 } 490 491 if tt.args.includeDevDeps { 492 osArgs = append(osArgs, "--include-dev-deps") 493 } 494 495 if tt.args.secretConfig != "" { 496 osArgs = append(osArgs, "--secret-config", tt.args.secretConfig) 497 } 498 499 osArgs = append(osArgs, "--output", outputFile) 500 osArgs = append(osArgs, tt.args.input) 501 502 uuid.SetFakeUUID(t, "3ff14136-e09f-4df9-80ea-%012d") 503 504 // Run "trivy repo" 505 err := execute(osArgs) 506 require.NoError(t, err) 507 508 // Compare want and got 509 switch format { 510 case types.FormatCycloneDX: 511 compareCycloneDX(t, tt.golden, outputFile) 512 case types.FormatSPDXJSON: 513 compareSPDXJson(t, tt.golden, outputFile) 514 case types.FormatJSON: 515 compareReports(t, tt.golden, outputFile, tt.override) 516 default: 517 require.Fail(t, "invalid format", "format: %s", format) 518 } 519 }) 520 } 521 }