fleet/server/vulnerabilities/nvd/cpe_matching_rule.go
Lucas Manuel Rodriguez 31598ab721
Fix CVE-2020-10146 false positive being detected on all Microsoft Teams versions (#13839)
#11922

- [X] Changes file added for user-visible changes in `changes/` or
`orbit/changes/`.
See [Changes
files](https://fleetdm.com/docs/contributing/committing-changes#changes-files)
for more information.
- ~[ ] Documented any API changes (docs/Using-Fleet/REST-API.md or
docs/Contributing/API-for-contributors.md)~
- ~[ ] Documented any permissions changes (docs/Using
Fleet/manage-access.md)~
- ~[ ] Input data is properly validated, `SELECT *` is avoided, SQL
injection is prevented (using placeholders for values in statements)~
- ~[ ] Added support on fleet's osquery simulator `cmd/osquery-perf` for
new osquery data ingestion features.~
- [X] Added/updated tests
- [X] Manual QA for all new/changed functionality
  - ~For Orbit and Fleet Desktop changes:~
- ~[ ] Manual QA must be performed in the three main OSs, macOS, Windows
and Linux.~
- ~[ ] Auto-update manual QA, from released version of component to new
version (see [tools/tuf/test](../tools/tuf/test/README.md)).~
2023-09-11 16:51:53 -03:00

115 lines
3.3 KiB
Go

package nvd
import (
"errors"
"fmt"
"strings"
"github.com/Masterminds/semver"
"github.com/facebookincubator/nvdtools/wfn"
)
// CPEMatchingRuleSpec allows you to match against a CPE. Version ranges are supported via SemVer constraints.
type CPEMatchingRuleSpec struct {
Vendor string // Software vendor.
Product string // Software product name.
TargetSW string // Target software, this usually corresponds to the target OS.
// Specifies a version constraint. See https://pkg.go.dev/github.com/Masterminds/semver@v1.5.0#hdr-Checking_Version_Constraints
// for reference.
SemVerConstraint string
}
func (rule CPEMatchingRuleSpec) getCPEMeta() *wfn.Attributes {
return &wfn.Attributes{
Vendor: rule.Vendor,
Product: rule.Product,
TargetSW: rule.TargetSW,
}
}
// CPEMatchingRule allows you to express a matching rule based on some CPE properties, one or more
// CVEs and one or more SemVer constraint. This is used to 'fix' false positives resulting from bad
// data in the NVD dataset itself.
// For example: https://nvd.nist.gov/vuln/detail/CVE-2017-13797, one of the CPE entries specified is
// cpe:2.3:a:apple:icloud:*:*:*:*:*:*:*:* which will match with any iCloud installation, but the
// vulnerability in question only affects iCloud on Windows up to 7.0.x.
type CPEMatchingRule struct {
// CPESpecs are the rules that will be used to match a CPE.
CPESpecs []CPEMatchingRuleSpec
// CVEs is the set of CVEs that this rule targets.
CVEs map[string]struct{}
// IgnoreAll will cause all CPEs to not match hence ignoring a CVE.
IgnoreAll bool
}
// CPEMatches returns true if the provided CPE matches the rule.
func (rule CPEMatchingRule) CPEMatches(cpeMeta *wfn.Attributes) bool {
if cpeMeta == nil {
return false
}
if rule.IgnoreAll {
return false
}
ver, err := semver.NewVersion(wfn.StripSlashes(cpeMeta.Version))
if err != nil {
return false
}
for _, spec := range rule.CPESpecs {
// The SemVer constraint is validated at instantiation time, so it should be ok to ignore the error.
constraint, _ := semver.NewConstraint(spec.SemVerConstraint)
if cpeMeta.MatchWithoutVersion(spec.getCPEMeta()) && constraint.Check(ver) {
return true
}
}
return false
}
// Validate validates the rule, returns an error if there's something wrong.
func (rule CPEMatchingRule) Validate() error {
validateCPEPart := func(errPrefix, val string) error {
switch strings.TrimSpace(val) {
case "":
return fmt.Errorf("%s can't be empty", errPrefix)
case "*":
return fmt.Errorf("%s can't be 'ANY'", errPrefix)
case "-":
return fmt.Errorf("%s can't be 'NA'", errPrefix)
default:
return nil
}
}
for _, spec := range rule.CPESpecs {
// Validate CPE parts
if err := validateCPEPart("Vendor", spec.Vendor); err != nil {
return err
}
if err := validateCPEPart("Product", spec.Product); err != nil {
return err
}
if err := validateCPEPart("TargetSW", spec.TargetSW); err != nil {
return err
}
// Validate SemVerConstraint
if _, err := semver.NewConstraint(spec.SemVerConstraint); err != nil {
return err
}
}
// Validate CVEs entries
if len(rule.CVEs) == 0 {
return errors.New("At least one CVE is required")
}
for cve := range rule.CVEs {
if strings.TrimSpace(cve) == "" {
return errors.New("CVE can't be empty")
}
}
return nil
}