File: cpe_name_matcher.go

package info (click to toggle)
golang-github-knqyf263-go-cpe 0.0~git20180327.659663f6-3
  • links: PTS, VCS
  • area: main
  • in suites: buster, experimental
  • size: 18,040 kB
  • sloc: sh: 11; makefile: 5
file content (233 lines) | stat: -rw-r--r-- 7,257 bytes parent folder | download | duplicates (2)
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
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
package matching

import (
	"strings"

	"github.com/knqyf263/go-cpe/common"
)

// Relation is enumeration for relational values.
type Relation int

const (
	// DISJOINT : disjoint
	DISJOINT Relation = iota
	// SUBSET : subset
	SUBSET
	// SUPERSET : superset
	SUPERSET
	// EQUAL : equal
	EQUAL
	// UNDEFINED : undefined
	UNDEFINED
)

// IsDisjoint tests two Well Formed Names for disjointness.
// @param source Source WFN
// @param target Target WFN
// @return true if the names are disjoint, false otherwise
func IsDisjoint(source, target common.WellFormedName) bool {
	// if any pairwise comparison is disjoint, the names are disjoint.
	results := CompareWFNs(source, target)
	for _, result := range results {
		if result == DISJOINT {
			return true
		}
	}
	return false
}

// IsEqual tests two Well Formed Names for equality.
// @param source Source WFN
// @param target Target WFN
// @return true if the names are equal, false otherwise
func IsEqual(source, target common.WellFormedName) bool {
	// if every pairwise comparison is equal, the names are equal.
	results := CompareWFNs(source, target)
	for _, result := range results {
		if result != EQUAL {
			return false
		}
	}
	return true
}

// IsSubset tests if the source Well Formed Name is a subset of the target Well Formed
// Name.
// @param source Source WFN
// @param target Target WFN
// @return true if the source is a subset of the target, false otherwise
func IsSubset(source, target common.WellFormedName) bool {
	// if any comparison is anything other than subset or equal, then target is
	// not a subset of source.
	results := CompareWFNs(source, target)
	for _, result := range results {
		if result != SUBSET && result != EQUAL {
			return false
		}
	}
	return true
}

// IsSuperset tests if the source Well Formed name is a superset of the target Well Formed Name.
// @param source Source WFN
// @param target Target WFN
// @return true if the source is a superset of the target, false otherwise
func IsSuperset(source, target common.WellFormedName) bool {
	// if any comparison is anything other than superset or equal, then target is not
	// a superset of source.
	results := CompareWFNs(source, target)
	for _, result := range results {
		if result != SUPERSET && result != EQUAL {
			return false
		}
	}
	return true
}

// CompareWFNs compares each attribute value pair in two Well Formed Names.
// @param source Source WFN
// @param target Target WFN
// @return A Hashtable mapping attribute string to attribute value Relation
func CompareWFNs(source, target common.WellFormedName) map[string]Relation {
	result := map[string]Relation{}
	result[common.AttributePart] = compare(source.Get(common.AttributePart), target.Get(common.AttributePart))
	result[common.AttributeVendor] = compare(source.Get(common.AttributeVendor), target.Get(common.AttributeVendor))
	result[common.AttributeProduct] = compare(source.Get(common.AttributeProduct), target.Get(common.AttributeProduct))
	result[common.AttributeVersion] = compare(source.Get(common.AttributeVersion), target.Get(common.AttributeVersion))
	result[common.AttributeUpdate] = compare(source.Get(common.AttributeUpdate), target.Get(common.AttributeUpdate))
	result[common.AttributeEdition] = compare(source.Get(common.AttributeEdition), target.Get(common.AttributeEdition))
	result[common.AttributeLanguage] = compare(source.Get(common.AttributeLanguage), target.Get(common.AttributeLanguage))
	result[common.AttributeSwEdition] = compare(source.Get(common.AttributeSwEdition), target.Get(common.AttributeSwEdition))
	result[common.AttributeTargetSw] = compare(source.Get(common.AttributeTargetSw), target.Get(common.AttributeTargetSw))
	result[common.AttributeTargetHw] = compare(source.Get(common.AttributeTargetHw), target.Get(common.AttributeTargetHw))
	result[common.AttributeOther] = compare(source.Get(common.AttributeOther), target.Get(common.AttributeOther))
	return result
}

// Compares an attribute value pair.
// @param source Source attribute value.
// @param target Target attribute value.
// @return The relation between the two attribute values.
func compare(source, target interface{}) Relation {
	var s, t string
	var ok bool

	// matching is case insensitive, convert strings to lowercase.
	if s, ok = source.(string); ok {
		s = strings.ToLower(s)
	}
	if t, ok = target.(string); ok {
		t = strings.ToLower(t)
	}
	// Unquoted wildcard characters yield an undefined result.
	if common.ContainsWildcards(t) {
		return UNDEFINED
	}

	// If source and target values are equal, then result is equal.
	if source == target {
		return EQUAL
	}

	// Check to see if source or target are Logical Values.
	var lvSource, lvTarget common.LogicalValue
	if lv, ok := source.(common.LogicalValue); ok {
		lvSource = lv
	}
	if lv, ok := target.(common.LogicalValue); ok {
		lvTarget = lv
	}
	// If source value is ANY, result is a superset.
	if lvSource.IsANY() {
		return SUPERSET
	}
	// If target value is ANY, result is a subset.
	if lvTarget.IsANY() {
		return SUBSET
	}
	// If source or target is NA, result is disjoint.
	if lvSource.IsNA() {
		return DISJOINT
	}
	// if (lvTarget != null) {
	if lvTarget.IsNA() {
		return DISJOINT
	}
	// only Strings will get to this point, not LogicalValues
	return compareStrings(s, t)
}

// compareStrings compares a source string to a target string, and addresses the condition
// in which the source string includes unquoted special characters. It
// performs a simple regular expression  match, with the assumption that
// (as required) unquoted special characters appear only at the beginning
// and/or the end of the source string. It also properly differentiates
// between unquoted and quoted special characters.
//
// @return Relation between source and target Strings.
func compareStrings(source, target string) Relation {
	var start, begins, ends, index, leftover, escapes int
	end := len(source)

	if source[0] == '*' {
		start = 1
		begins = -1
	} else {
		for start < len(source) && source[start] == '?' {
			start++
			begins++
		}
	}

	if source[end-1] == '*' && IsEvenWildcards(source, end-1) {
		end--
		ends = -1
	} else {
		for end > 0 && source[end-1] == '?' && IsEvenWildcards(source, end-1) {
			end--
			ends++
		}
	}

	// only ? (e.g. "???")
	if strings.Trim(source, "?") == "" {
		if len(source) >= common.LengthWithEscapeCharacters(target) {
			return SUPERSET
		}
		return DISJOINT
	}
	source = source[start:end]
	index = -1
	leftover = len(target)
	for leftover > 0 {
		index = common.IndexOf(target, source, index+1)
		if index == -1 {
			break
		}
		escapes = common.CountEscapeCharacters(target[:index])
		if index > 0 && begins != -1 && begins < (index-escapes) {
			break
		}
		escapes = common.CountEscapeCharacters(target[index+1:])
		leftover = len(target) - index - escapes - len(source)
		if leftover > 0 && ends != -1 && leftover > ends {
			continue
		}
		return SUPERSET
	}
	return DISJOINT
}

// IsEvenWildcards searches a string for the backslash character
// @param str string to search in
// @param idx end index
// @return true if the number of backslash characters is even, false if odd
func IsEvenWildcards(str string, idx int) bool {
	result := 0
	for idx > 0 && str[idx-1] == '\\' {
		idx--
		result++
	}
	return result%2 == 0
}