cpedict/lookup.go (74 lines of code) (raw):
// 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 cpedict
import (
"fmt"
"github.com/facebookincubator/nvdtools/wfn"
)
// MatchType represents the type of match in dictionary lookup
type MatchType int
// Possible values of MatchType
const (
None MatchType = iota
Subset
Exact
Superset
)
// String() implements Stringer interface for MatchType
func (mt MatchType) String() string {
switch mt {
case None:
return "None"
case Subset:
return "Subset"
case Exact:
return "Exact"
case Superset:
return "Superset"
default:
return fmt.Sprintf("invalid MatchType %d", mt)
}
}
// Search determinces how WFN (NamePattern) relates the given dictionary.
// Deprecated matching names are resolved to their replacements; since item can be deprecated by multiple
// names, which might contain wildcards and in general refer to the whole family of products, this resolve isn't
// performed during exact match.
// If exact is true and an exact match is found, the function will return the match and match type of Exact.
// If the needle is a superset of any of the dictionary names, the function will return that set of names
// and match type of Superset. If the needle is a subset of one or more dictionary names, the function will
// return that set and the match type of Subset. Otherwise an empty slice and match type None are returned.
// TODO: optimise the performance -- now it's O(n) to O(n^2), it should be easy enough to make it O(log n)
// or even O(1) (e.g. use map keyed with WFNs instead of slice)
func (dict CPEList) Search(needle NamePattern, exact bool) ([]CPEItem, MatchType) {
if exact {
result := make([]CPEItem, 0)
for _, item := range dict.Items {
cmp, _ := wfn.Compare((*wfn.Attributes)(&needle), (*wfn.Attributes)(&item.Name))
if cmp.IsEqual() {
return append(result, item), Exact
}
}
return nil, None
}
superset := make([]CPEItem, 0)
subset := make([]CPEItem, 0)
for _, item := range dict.Items {
cmp, _ := wfn.Compare((*wfn.Attributes)(&needle), (*wfn.Attributes)(&item.Name))
if cmp.IsSuperset() {
superset = append(superset, resolveDeprecation(dict, item)...)
} else if cmp.IsSubset() {
subset = append(subset, resolveDeprecation(dict, item)...)
}
}
if len(superset) > 0 {
return superset, Superset
}
if len(subset) > 0 {
return subset, Subset
}
return nil, None
}
func resolveDeprecation(dict CPEList, item CPEItem) []CPEItem {
if !item.Deprecated || item.CPE23.Deprecation == nil {
return []CPEItem{item}
}
var names []wfn.Attributes
for _, depBy := range item.CPE23.Deprecation.DeprecatedBy {
names = append(names, wfn.Attributes(depBy.Name))
}
results := make([]CPEItem, 0)
for _, i := range dict.Items {
for _, name := range names {
cmp, _ := wfn.Compare(&name, (*wfn.Attributes)(&i.Name))
if cmp.IsEqual() {
results = append(results, i)
}
}
}
return results
}