Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: support last_affected in OSVs #142

Merged
merged 1 commit into from
Jul 5, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 10 additions & 5 deletions pkg/database/osv.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,9 @@ func (p Package) NormalizedName() string {
}

type RangeEvent struct {
Introduced string `json:"introduced,omitempty"`
Fixed string `json:"fixed,omitempty"`
Introduced string `json:"introduced,omitempty"`
Fixed string `json:"fixed,omitempty"`
LastAffected string `json:"last_affected,omitempty"`
}

type AffectsRange struct {
Expand All @@ -70,10 +71,14 @@ func (ar AffectsRange) containsVersion(v string) bool {

var affected bool
for _, e := range ar.Events {
if !affected && e.Introduced != "" {
if affected {
if e.Fixed != "" {
affected = vp.CompareStr(e.Fixed) < 0
} else if e.LastAffected != "" {
affected = e.LastAffected == v || vp.CompareStr(e.LastAffected) <= 0
}
} else if e.Introduced != "" {
affected = e.Introduced == "0" || vp.CompareStr(e.Introduced) >= 0
} else if affected && e.Fixed != "" {
affected = vp.CompareStr(e.Fixed) < 0
}
}

Expand Down
139 changes: 139 additions & 0 deletions pkg/database/osv_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,64 @@ func TestOSV_IsAffected_AffectsWithEcosystem_SingleAffected(t *testing.T) {

// an empty version should always be treated as affected
expectIsAffected(t, osv, "", true)

// "LastAffected: 1" means all versions after this are not vulnerable
osv = buildOSVWithAffected(
database.Affected{
Package: database.Package{Ecosystem: lockfile.NpmEcosystem, Name: "my-package"},
Ranges: []database.AffectsRange{
buildEcosystemAffectsRange(
database.RangeEvent{Introduced: "0"},
database.RangeEvent{LastAffected: "1"},
),
},
},
)

for _, v := range []string{"0.0.0", "0.1.0", "0.0.0.1", "1.0.0-rc", "1.0.0"} {
expectIsAffected(t, osv, v, true)
}

for _, v := range []string{"1.0.1", "1.1.0", "2.0.0"} {
expectIsAffected(t, osv, v, false)
}

// an empty version should always be treated as affected
expectIsAffected(t, osv, "", true)

// mix of fixes, last_known_affected, and introduced
osv = buildOSVWithAffected(
database.Affected{
Package: database.Package{Ecosystem: lockfile.NpmEcosystem, Name: "my-package"},
Ranges: []database.AffectsRange{
buildEcosystemAffectsRange(
database.RangeEvent{Introduced: "0"},
database.RangeEvent{Fixed: "1"},
database.RangeEvent{Introduced: "2.1.0"},
database.RangeEvent{LastAffected: "3.1.9"},
),
},
},
)

for _, v := range []string{"0.0.0", "0.1.0", "0.0.0.1", "1.0.0-rc"} {
expectIsAffected(t, osv, v, true)
}

for _, v := range []string{"1.0.0", "1.1.0", "2.0.0rc2", "2.0.1"} {
expectIsAffected(t, osv, v, false)
}

for _, v := range []string{"2.1.1", "2.3.4", "3.0.0", "3.0.0-rc", "3.1.9"} {
expectIsAffected(t, osv, v, true)
}

for _, v := range []string{"3.2.0", "3.2.1", "4.0.0"} {
expectIsAffected(t, osv, v, false)
}

// an empty version should always be treated as affected
expectIsAffected(t, osv, "", true)
}

func TestOSV_IsAffected_AffectsWithEcosystem_MultipleAffected(t *testing.T) {
Expand All @@ -298,6 +356,15 @@ func TestOSV_IsAffected_AffectsWithEcosystem_MultipleAffected(t *testing.T) {
),
},
},
database.Affected{
Package: database.Package{Ecosystem: lockfile.NpmEcosystem, Name: "my-package"},
Ranges: []database.AffectsRange{
buildEcosystemAffectsRange(
database.RangeEvent{Introduced: "3.3.0"},
database.RangeEvent{LastAffected: "3.5.0"},
),
},
},
)

for _, v := range []string{"0.0.0", "0.1.0", "0.0.0.1", "1.0.0-rc"} {
Expand All @@ -316,6 +383,10 @@ func TestOSV_IsAffected_AffectsWithEcosystem_MultipleAffected(t *testing.T) {
expectIsAffected(t, osv, v, false)
}

for _, v := range []string{"3.3.1", "3.4.5"} {
expectIsAffected(t, osv, v, true)
}

// an empty version should always be treated as affected
expectIsAffected(t, osv, "", true)
}
Expand Down Expand Up @@ -453,6 +524,61 @@ func TestOSV_IsAffected_AffectsWithSemver_SingleAffected(t *testing.T) {

// an empty version should always be treated as affected
expectIsAffected(t, osv, "", true)

// "LastAffected: 1" means all versions after this are not vulnerable
osv = buildOSVWithAffected(
database.Affected{
Package: database.Package{Ecosystem: lockfile.NpmEcosystem, Name: "my-package"},
Ranges: []database.AffectsRange{
buildSemverAffectsRange(
database.RangeEvent{Introduced: "0"},
database.RangeEvent{LastAffected: "1.0.0"},
),
},
},
)

for _, v := range []string{"0.0.0", "0.1.0", "0.0.0.1", "1.0.0-rc", "1.0.0"} {
expectIsAffected(t, osv, v, true)
}

for _, v := range []string{"1.0.1", "1.1.0", "2.0.0"} {
expectIsAffected(t, osv, v, false)
}

// mix of fixes, last_known_affected, and introduced
osv = buildOSVWithAffected(
database.Affected{
Package: database.Package{Ecosystem: lockfile.NpmEcosystem, Name: "my-package"},
Ranges: []database.AffectsRange{
buildSemverAffectsRange(
database.RangeEvent{Introduced: "0"},
database.RangeEvent{Fixed: "1"},
database.RangeEvent{Introduced: "2.1.0"},
database.RangeEvent{LastAffected: "3.1.9"},
),
},
},
)

for _, v := range []string{"0.0.0", "0.1.0", "0.0.0.1", "1.0.0-rc"} {
expectIsAffected(t, osv, v, true)
}

for _, v := range []string{"1.0.0", "1.1.0", "2.0.0rc2", "2.0.1"} {
expectIsAffected(t, osv, v, false)
}

for _, v := range []string{"2.1.1", "2.3.4", "3.0.0", "3.0.0-rc"} {
expectIsAffected(t, osv, v, true)
}

for _, v := range []string{"3.2.0", "3.2.1", "4.0.0"} {
expectIsAffected(t, osv, v, false)
}

// an empty version should always be treated as affected
expectIsAffected(t, osv, "", true)
}

func TestOSV_IsAffected_AffectsWithSemver_MultipleAffected(t *testing.T) {
Expand All @@ -477,6 +603,15 @@ func TestOSV_IsAffected_AffectsWithSemver_MultipleAffected(t *testing.T) {
),
},
},
database.Affected{
Package: database.Package{Ecosystem: lockfile.NpmEcosystem, Name: "my-package"},
Ranges: []database.AffectsRange{
buildSemverAffectsRange(
database.RangeEvent{Introduced: "3.3.0"},
database.RangeEvent{LastAffected: "3.5.0"},
),
},
},
)

for _, v := range []string{"0.0.0", "0.1.0", "0.0.0.1", "1.0.0-rc"} {
Expand All @@ -495,6 +630,10 @@ func TestOSV_IsAffected_AffectsWithSemver_MultipleAffected(t *testing.T) {
expectIsAffected(t, osv, v, false)
}

for _, v := range []string{"3.3.1", "3.4.5", "3.5.0"} {
expectIsAffected(t, osv, v, true)
}

// an empty version should always be treated as affected
expectIsAffected(t, osv, "", true)
}
Expand Down