forked from facebookincubator/nvdtools
-
Notifications
You must be signed in to change notification settings - Fork 0
/
match_cpe.go
136 lines (114 loc) · 3.92 KB
/
match_cpe.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
// Copyright (c) Facebook, Inc. and its affiliates.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package nvd
import (
"fmt"
"github.com/Daviid-P/nvdtools/cvefeed/nvd/schema"
"github.com/Daviid-P/nvdtools/wfn"
)
// cpeMatch is a wrapper around the actual NVDCVEFeedJSON10DefCPEMatch
type cpeMatch struct {
*wfn.Attributes
vulnerable bool
versionEndExcluding string
versionEndIncluding string
versionStartExcluding string
versionStartIncluding string
hasVersionRanges bool
}
// Matcher returns an object which knows how to match attributes
func cpeMatcher(ID string, nvdMatch *schema.NVDCVEFeedJSON10DefCPEMatch) (wfn.Matcher, error) {
parse := func(uri string) (*wfn.Attributes, error) {
if uri == "" {
return nil, fmt.Errorf("%s: can't parse empty uri", ID)
}
return wfn.Parse(uri)
}
// parse
match := cpeMatch{vulnerable: nvdMatch.Vulnerable}
var err error
if match.Attributes, err = parse(nvdMatch.Cpe23Uri); err != nil {
if match.Attributes, err = parse(nvdMatch.Cpe22Uri); err != nil {
return nil, fmt.Errorf("%s: unable to parse both cpe2.2 and cpe2.3", ID)
}
}
match.versionEndExcluding = nvdMatch.VersionEndExcluding
match.versionEndIncluding = nvdMatch.VersionEndIncluding
match.versionStartExcluding = nvdMatch.VersionStartExcluding
match.versionStartIncluding = nvdMatch.VersionStartIncluding
if match.versionStartIncluding != "" || match.versionStartExcluding != "" ||
match.versionEndIncluding != "" || match.versionEndExcluding != "" {
match.hasVersionRanges = true
}
return &match, nil
}
// Match is part of the Matcher interface
func (cm *cpeMatch) Match(attrs []*wfn.Attributes, requireVersion bool) (matches []*wfn.Attributes) {
for _, attr := range attrs {
if cm.match(attr, requireVersion) {
matches = append(matches, attr)
}
}
return matches
}
// Match implements wfn.Matcher interface
func (cm *cpeMatch) match(attr *wfn.Attributes, requireVersion bool) bool {
if cm == nil || cm.Attributes == nil {
return false
}
if requireVersion {
// if we require version, then we need either version ranges or version not to be *
if !cm.hasVersionRanges && cm.Attributes.Version == wfn.Any {
return false
}
}
// here we have a version: either actual one or ranges
// check whether everything except for version matches
if !cm.Attributes.MatchWithoutVersion(attr) {
return false
}
if cm.Attributes.Version == wfn.Any {
if !cm.hasVersionRanges {
// if version is any and doesn't have version ranges, then it matches any
return !requireVersion
} // otherwise we try to match it at the end of the function
} else if cm.Attributes.MatchOnlyVersion(attr) {
return true // version matched
}
// if it got to here, it means:
// - matched attr without version
// - didn't match version, or require version was set and version was *
if attr.Version == wfn.Any {
return true
}
if !cm.hasVersionRanges {
return false
}
// match version to ranges
ver := wfn.StripSlashes(attr.Version)
matches := true
if cm.versionStartIncluding != "" {
matches = matches && smartVerCmp(ver, cm.versionStartIncluding) >= 0
}
if cm.versionStartExcluding != "" {
matches = matches && smartVerCmp(ver, cm.versionStartExcluding) > 0
}
if cm.versionEndIncluding != "" {
matches = matches && smartVerCmp(ver, cm.versionEndIncluding) <= 0
}
if cm.versionEndExcluding != "" {
matches = matches && smartVerCmp(ver, cm.versionEndExcluding) < 0
}
return matches
}