package vulnerability_test

import (
	"testing"

	"github.com/stretchr/testify/assert"

	"github.com/aquasecurity/trivy-db/pkg/db"
	dbTypes "github.com/aquasecurity/trivy-db/pkg/types"
	"github.com/aquasecurity/trivy-db/pkg/utils"
	"github.com/aquasecurity/trivy-db/pkg/vulnsrc/vulnerability"
	"github.com/aquasecurity/trivy/internal/dbtest"
	"github.com/aquasecurity/trivy/pkg/types"
	vuln "github.com/aquasecurity/trivy/pkg/vulnerability"
)

func TestClient_FillInfo(t *testing.T) {
	tests := []struct {
		name                    string
		vulnSeveritySources     []dbTypes.SourceID
		fixtures                []string
		vulns                   []types.DetectedVulnerability
		expectedVulnerabilities []types.DetectedVulnerability
	}{
		{
			name:     "happy path, with only OS vulnerability but no vendor severity, no NVD",
			fixtures: []string{"testdata/fixtures/vulnerability.yaml"},
			vulns: []types.DetectedVulnerability{
				{VulnerabilityID: "CVE-2019-0001"},
			},
			expectedVulnerabilities: []types.DetectedVulnerability{
				{
					VulnerabilityID: "CVE-2019-0001",
					Status:          dbTypes.StatusAffected,
					Vulnerability: dbTypes.Vulnerability{
						Title:            "dos",
						Description:      "dos vulnerability",
						Severity:         dbTypes.SeverityMedium.String(),
						References:       []string{"http://example.com"},
						LastModifiedDate: utils.MustTimeParse("2020-01-01T01:01:00Z"),
						PublishedDate:    utils.MustTimeParse("2001-01-01T01:01:00Z"),
					},
					PrimaryURL: "https://avd.aquasec.com/nvd/cve-2019-0001",
				},
			},
		},
		{
			name:     "happy path, with only OS vulnerability but no vendor severity, yes NVD",
			fixtures: []string{"testdata/fixtures/vulnerability.yaml"},
			vulns: []types.DetectedVulnerability{
				{
					VulnerabilityID: "RUSTSEC-2018-0017",
					DataSource: &dbTypes.DataSource{
						ID:   vulnerability.OSV,
						Name: "RustSec Advisory Database",
						URL:  "https://github.com/RustSec/advisory-db",
					},
				},
			},
			expectedVulnerabilities: []types.DetectedVulnerability{
				{
					VulnerabilityID: "RUSTSEC-2018-0017",
					Status:          dbTypes.StatusAffected,
					Vulnerability: dbTypes.Vulnerability{
						Title:       "dos",
						Description: "dos vulnerability",
						Severity:    dbTypes.SeverityLow.String(),
						VendorSeverity: dbTypes.VendorSeverity{
							vulnerability.NVD: dbTypes.SeverityLow,
						},
						LastModifiedDate: utils.MustTimeParse("2020-01-01T01:01:00Z"),
						PublishedDate:    utils.MustTimeParse("2001-01-01T01:01:00Z"),
					},
					SeveritySource: vulnerability.NVD,
					PrimaryURL:     "https://osv.dev/vulnerability/RUSTSEC-2018-0017",
					DataSource: &dbTypes.DataSource{
						ID:   vulnerability.OSV,
						Name: "RustSec Advisory Database",
						URL:  "https://github.com/RustSec/advisory-db",
					},
				},
			},
		},
		{
			name:     "happy path, with only OS vulnerability but no severity, no vendor severity, no NVD",
			fixtures: []string{"testdata/fixtures/vulnerability.yaml"},
			vulns: []types.DetectedVulnerability{
				{
					VulnerabilityID: "GHSA-28fw-88hq-6jmm",
					DataSource: &dbTypes.DataSource{
						ID:   vulnerability.GHSA,
						Name: "GitHub Security Advisory Pip",
						URL:  "https://github.com/advisories?query=type%3Areviewed+ecosystem%3Apip",
					},
				},
			},
			expectedVulnerabilities: []types.DetectedVulnerability{
				{
					VulnerabilityID: "GHSA-28fw-88hq-6jmm",
					Status:          dbTypes.StatusAffected,
					Vulnerability: dbTypes.Vulnerability{
						Title:       "dos",
						Description: "dos vulnerability",
						Severity:    dbTypes.SeverityUnknown.String(),
						References:  []string{"http://example.com"},
					},
					PrimaryURL: "https://github.com/advisories/GHSA-28fw-88hq-6jmm",
					DataSource: &dbTypes.DataSource{
						ID:   vulnerability.GHSA,
						Name: "GitHub Security Advisory Pip",
						URL:  "https://github.com/advisories?query=type%3Areviewed+ecosystem%3Apip",
					},
				},
			},
		},
		{
			name:     "happy path, with only OS vulnerability, yes vendor severity, with both NVD and CVSS info",
			fixtures: []string{"testdata/fixtures/vulnerability.yaml"},
			vulns: []types.DetectedVulnerability{
				{
					VulnerabilityID: "CVE-2019-0004",
					DataSource: &dbTypes.DataSource{
						ID:   vulnerability.RedHat,
						Name: "Red Hat OVAL v2",
						URL:  "https://www.redhat.com/security/data/oval/v2/",
					},
				},
			},
			expectedVulnerabilities: []types.DetectedVulnerability{
				{
					VulnerabilityID: "CVE-2019-0004",
					Status:          dbTypes.StatusAffected,
					Vulnerability: dbTypes.Vulnerability{
						Title:       "dos",
						Description: "dos vulnerability",
						Severity:    dbTypes.SeverityLow.String(),
						VendorSeverity: dbTypes.VendorSeverity{
							vulnerability.RedHat: dbTypes.SeverityLow,
						},
						CweIDs:     []string{"CWE-311"},
						References: []string{"http://example.com"},
						CVSS: map[dbTypes.SourceID]dbTypes.CVSS{
							vulnerability.NVD: {
								V2Vector: "AV:N/AC:L/Au:N/C:P/I:P/A:P",
								V2Score:  4.5,
								V3Vector: "CVSS:3.0/PR:N/UI:N/S:U/C:H/I:H/A:H",
								V3Score:  5.6,
							},
							vulnerability.RedHat: {
								V2Vector: "AV:N/AC:M/Au:N/C:N/I:P/A:N",
								V2Score:  7.8,
								V3Vector: "CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H",
								V3Score:  9.8,
							},
						},
					},
					SeveritySource: vulnerability.RedHat,
					PrimaryURL:     "https://avd.aquasec.com/nvd/cve-2019-0004",
					DataSource: &dbTypes.DataSource{
						ID:   vulnerability.RedHat,
						Name: "Red Hat OVAL v2",
						URL:  "https://www.redhat.com/security/data/oval/v2/",
					},
				},
			},
		},
		{
			name:     "happy path, with only library vulnerability",
			fixtures: []string{"testdata/fixtures/vulnerability.yaml"},
			vulns: []types.DetectedVulnerability{
				{
					VulnerabilityID: "CVE-2019-0005",
					Status:          dbTypes.StatusAffected,
					DataSource: &dbTypes.DataSource{
						ID:   vulnerability.GHSA,
						Name: "GitHub Security Advisory Pip",
						URL:  "https://github.com/advisories?query=type%3Areviewed+ecosystem%3Apip",
					},
				},
			},
			expectedVulnerabilities: []types.DetectedVulnerability{
				{
					VulnerabilityID: "CVE-2019-0005",
					Status:          dbTypes.StatusAffected,
					Vulnerability: dbTypes.Vulnerability{
						Title:       "COVID-19",
						Description: "a nasty virus vulnerability for humans",
						Severity:    dbTypes.SeverityCritical.String(),
						VendorSeverity: dbTypes.VendorSeverity{
							vulnerability.GHSA: dbTypes.SeverityCritical,
						},
						References: []string{"https://www.who.int/emergencies/diseases/novel-coronavirus-2019"},
					},
					SeveritySource: vulnerability.GHSA,
					PrimaryURL:     "https://avd.aquasec.com/nvd/cve-2019-0005",
					DataSource: &dbTypes.DataSource{
						ID:   vulnerability.GHSA,
						Name: "GitHub Security Advisory Pip",
						URL:  "https://github.com/advisories?query=type%3Areviewed+ecosystem%3Apip",
					},
				},
			},
		},
		{
			name:     "happy path, with package-specific severity",
			fixtures: []string{"testdata/fixtures/vulnerability.yaml"},
			vulns: []types.DetectedVulnerability{
				{
					VulnerabilityID: "CVE-2019-0001",
					SeveritySource:  vulnerability.Debian,
					Vulnerability: dbTypes.Vulnerability{
						Severity: dbTypes.SeverityLow.String(),
					},
				},
			},
			expectedVulnerabilities: []types.DetectedVulnerability{
				{
					VulnerabilityID: "CVE-2019-0001",
					Status:          dbTypes.StatusAffected,
					SeveritySource:  vulnerability.Debian,
					Vulnerability: dbTypes.Vulnerability{
						Title:       "dos",
						Description: "dos vulnerability",
						Severity:    dbTypes.SeverityLow.String(),
						VendorSeverity: map[dbTypes.SourceID]dbTypes.Severity{
							vulnerability.Debian: dbTypes.SeverityLow,
						},
						References:       []string{"http://example.com"},
						LastModifiedDate: utils.MustTimeParse("2020-01-01T01:01:00Z"),
						PublishedDate:    utils.MustTimeParse("2001-01-01T01:01:00Z"),
					},
					PrimaryURL: "https://avd.aquasec.com/nvd/cve-2019-0001",
				},
			},
		},
		{
			name:     "happy path. GHSA-xxx. Severity gets from ghsa",
			fixtures: []string{"testdata/fixtures/vulnerability.yaml"},
			vulns: []types.DetectedVulnerability{
				{VulnerabilityID: "GHSA-0000-aaaa-1111"},
			},
			expectedVulnerabilities: []types.DetectedVulnerability{
				{
					VulnerabilityID: "GHSA-0000-aaaa-1111",
					Status:          dbTypes.StatusAffected,
					SeveritySource:  vulnerability.GHSA,
					Vulnerability: dbTypes.Vulnerability{
						Title:       "dos",
						Description: "dos vulnerability",
						Severity:    dbTypes.SeverityHigh.String(),
						References:  []string{"http://example.com"},
						VendorSeverity: map[dbTypes.SourceID]dbTypes.Severity{
							"nvd":  dbTypes.SeverityLow,
							"ghsa": dbTypes.SeverityHigh,
						},
					},
					PrimaryURL: "https://github.com/advisories/GHSA-0000-aaaa-1111",
				},
			},
		},
		{
			name:     "happy path. CVE-xxx. Severity gets from nvd",
			fixtures: []string{"testdata/fixtures/vulnerability.yaml"},
			vulns: []types.DetectedVulnerability{
				{VulnerabilityID: "CVE-2022-0001"},
			},
			expectedVulnerabilities: []types.DetectedVulnerability{
				{
					VulnerabilityID: "CVE-2022-0001",
					Status:          dbTypes.StatusAffected,
					SeveritySource:  vulnerability.NVD,
					Vulnerability: dbTypes.Vulnerability{
						Title:       "dos",
						Description: "dos vulnerability",
						Severity:    dbTypes.SeverityLow.String(),
						References:  []string{"http://example.com"},
						VendorSeverity: map[dbTypes.SourceID]dbTypes.Severity{
							"nvd":  dbTypes.SeverityLow,
							"ghsa": dbTypes.SeverityHigh,
						},
					},
					PrimaryURL: "https://avd.aquasec.com/nvd/cve-2022-0001",
				},
			},
		},
		{
			name: "happy path. Severity got from 'nvd' from VulnSeveritySources",
			vulnSeveritySources: []dbTypes.SourceID{
				"alma",
				"alpine",
				"nvd",
			},
			fixtures: []string{"testdata/fixtures/vulnerability.yaml"},
			vulns: []types.DetectedVulnerability{
				{VulnerabilityID: "CVE-2022-0001"},
			},
			expectedVulnerabilities: []types.DetectedVulnerability{
				{
					VulnerabilityID: "CVE-2022-0001",
					Status:          dbTypes.StatusAffected,
					SeveritySource:  vulnerability.NVD,
					Vulnerability: dbTypes.Vulnerability{
						Title:       "dos",
						Description: "dos vulnerability",
						Severity:    dbTypes.SeverityLow.String(),
						References:  []string{"http://example.com"},
						VendorSeverity: map[dbTypes.SourceID]dbTypes.Severity{
							"nvd":  dbTypes.SeverityLow,
							"ghsa": dbTypes.SeverityHigh,
						},
					},
					PrimaryURL: "https://avd.aquasec.com/nvd/cve-2022-0001",
				},
			},
		},
		{
			name: "happy path. Severity got from 'auto' from VulnSeveritySources",
			vulnSeveritySources: []dbTypes.SourceID{
				"alma",
				"alpine",
				"auto",
			},
			fixtures: []string{"testdata/fixtures/vulnerability.yaml"},
			vulns: []types.DetectedVulnerability{
				{VulnerabilityID: "CVE-2022-0001"},
			},
			expectedVulnerabilities: []types.DetectedVulnerability{
				{
					VulnerabilityID: "CVE-2022-0001",
					Status:          dbTypes.StatusAffected,
					SeveritySource:  vulnerability.NVD,
					Vulnerability: dbTypes.Vulnerability{
						Title:       "dos",
						Description: "dos vulnerability",
						Severity:    dbTypes.SeverityLow.String(),
						References:  []string{"http://example.com"},
						VendorSeverity: map[dbTypes.SourceID]dbTypes.Severity{
							"nvd":  dbTypes.SeverityLow,
							"ghsa": dbTypes.SeverityHigh,
						},
					},
					PrimaryURL: "https://avd.aquasec.com/nvd/cve-2022-0001",
				},
			},
		},
		{
			name: "happy path. Severity didn't find from VulnSeveritySources",
			vulnSeveritySources: []dbTypes.SourceID{
				"alma",
				"alpine",
			},
			fixtures: []string{"testdata/fixtures/vulnerability.yaml"},
			vulns: []types.DetectedVulnerability{
				{VulnerabilityID: "CVE-2022-0001"},
			},
			expectedVulnerabilities: []types.DetectedVulnerability{
				{
					VulnerabilityID: "CVE-2022-0001",
					Status:          dbTypes.StatusAffected,
					Vulnerability: dbTypes.Vulnerability{
						Title:       "dos",
						Description: "dos vulnerability",
						Severity:    dbTypes.SeverityUnknown.String(),
						References:  []string{"http://example.com"},
						VendorSeverity: map[dbTypes.SourceID]dbTypes.Severity{
							"nvd":  dbTypes.SeverityLow,
							"ghsa": dbTypes.SeverityHigh,
						},
					},
					PrimaryURL: "https://avd.aquasec.com/nvd/cve-2022-0001",
				},
			},
		},
		{
			name:     "happy path. Use alpine severity for root.io vulnerability",
			fixtures: []string{"testdata/fixtures/vulnerability.yaml"},
			vulns: []types.DetectedVulnerability{
				{
					VulnerabilityID: "CVE-2023-0001",
					DataSource: &dbTypes.DataSource{
						ID:     vulnerability.RootIO,
						BaseID: vulnerability.Alpine,
						Name:   "Root.io Security Patches (alpine)",
						URL:    "https://api.root.io/external/patch_feed",
					},
				},
			},
			expectedVulnerabilities: []types.DetectedVulnerability{
				{
					VulnerabilityID: "CVE-2023-0001",
					Status:          dbTypes.StatusAffected,
					SeveritySource:  vulnerability.Alpine,
					DataSource: &dbTypes.DataSource{
						ID:     vulnerability.RootIO,
						BaseID: vulnerability.Alpine,
						Name:   "Root.io Security Patches (alpine)",
						URL:    "https://api.root.io/external/patch_feed",
					},
					Vulnerability: dbTypes.Vulnerability{
						Title:       "dos",
						Description: "dos vulnerability",
						Severity:    dbTypes.SeverityMedium.String(),
						References:  []string{"http://example.com"},
						VendorSeverity: map[dbTypes.SourceID]dbTypes.Severity{
							"nvd":    dbTypes.SeverityLow,
							"alpine": dbTypes.SeverityMedium,
							"ubuntu": dbTypes.SeverityHigh,
						},
					},
					PrimaryURL: "https://avd.aquasec.com/nvd/cve-2023-0001",
				},
			},
		},
		{
			name:     "happy path. Use debian severity for root.io vulnerability",
			fixtures: []string{"testdata/fixtures/vulnerability.yaml"},
			vulns: []types.DetectedVulnerability{
				{
					VulnerabilityID: "CVE-2023-0001",
					SeveritySource:  vulnerability.Debian,
					DataSource: &dbTypes.DataSource{
						ID:     vulnerability.RootIO,
						BaseID: vulnerability.Debian,
						Name:   "Root.io Security Patches (debian)",
						URL:    "https://api.root.io/external/patch_feed",
					},
					Vulnerability: dbTypes.Vulnerability{
						Severity: dbTypes.SeverityCritical.String(),
					},
				},
			},
			expectedVulnerabilities: []types.DetectedVulnerability{
				{
					VulnerabilityID: "CVE-2023-0001",
					Status:          dbTypes.StatusAffected,
					SeveritySource:  vulnerability.Debian,
					DataSource: &dbTypes.DataSource{
						ID:     vulnerability.RootIO,
						BaseID: vulnerability.Debian,
						Name:   "Root.io Security Patches (debian)",
						URL:    "https://api.root.io/external/patch_feed",
					},
					Vulnerability: dbTypes.Vulnerability{
						Title:       "dos",
						Description: "dos vulnerability",
						Severity:    dbTypes.SeverityCritical.String(),
						References:  []string{"http://example.com"},
						VendorSeverity: map[dbTypes.SourceID]dbTypes.Severity{
							"nvd":    dbTypes.SeverityLow,
							"alpine": dbTypes.SeverityMedium,
							"ubuntu": dbTypes.SeverityHigh,
							"debian": dbTypes.SeverityCritical,
						},
					},
					PrimaryURL: "https://avd.aquasec.com/nvd/cve-2023-0001",
				},
			},
		},
		{
			name:     "GetVulnerability returns an error",
			fixtures: []string{"testdata/fixtures/sad.yaml"},
			vulns: []types.DetectedVulnerability{
				{
					VulnerabilityID: "CVE-2019-0004",
					Status:          dbTypes.StatusAffected,
				},
			},
			expectedVulnerabilities: []types.DetectedVulnerability{
				{
					VulnerabilityID: "CVE-2019-0004",
					Status:          dbTypes.StatusAffected,
				},
			},
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			dbtest.InitDB(t, tt.fixtures)
			defer db.Close()

			c := vuln.NewClient(db.Config{})
			vulnSeveritySources := []dbTypes.SourceID{
				"auto",
			}
			if len(tt.vulnSeveritySources) > 0 {
				vulnSeveritySources = tt.vulnSeveritySources
			}
			c.FillInfo(tt.vulns, vulnSeveritySources)
			assert.Equal(t, tt.expectedVulnerabilities, tt.vulns, tt.name)
		})
	}
}
