cpe23 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +8 -0
- data/.travis.yml +6 -0
- data/Gemfile +12 -0
- data/LICENSE.txt +21 -0
- data/README.md +52 -0
- data/Rakefile +10 -0
- data/cpe23.gemspec +26 -0
- data/lib/cpe23.rb +293 -0
- data/lib/cpe23/cpe23.citrus +13 -0
- data/lib/cpe23/version.rb +5 -0
- data/lib/cpe23/version_wildcard.rb +48 -0
- data/lib/cpe23/wfn.citrus +33 -0
- metadata +70 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: aab30b1d7b5b8b3a9be29bc890e242c9ab72d2d47f13ebbe8268d680e9995dd5
|
4
|
+
data.tar.gz: 9a3bddfa0059410d25ce32c8aa8dfce9fefd0b0a23bf6ce224cb0957c9e1c12d
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: c0183e27dec8bde99cbf321e832ac5d21ea05ac2df87d0b128e56d0c67427776669a11144e2b4665b3ce81c3f8365954df55c239779a4099cd1a0e34365906fa
|
7
|
+
data.tar.gz: aea8a89821b56ffa184bc200b3d2aa62b5326aaf5780b4e8b6d62d3d75b0f53aac63f616f838a8fc9043e7c2d8c340650b8183384c08e9d5d8c91728d05a0a87
|
data/.gitignore
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
source "https://rubygems.org"
|
2
|
+
|
3
|
+
# Specify your gem's dependencies in cpe23.gemspec
|
4
|
+
gemspec
|
5
|
+
|
6
|
+
gem "rake", "~> 12.0"
|
7
|
+
gem "minitest", "~> 5.0"
|
8
|
+
|
9
|
+
gem "pry", "~> 0.12.2", :groups => [:development, :test]
|
10
|
+
|
11
|
+
gem "pry-byebug", "~> 3.8", :groups => [:development, :test]
|
12
|
+
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2020 Jeremy Symon
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
# Cpe23
|
2
|
+
|
3
|
+
Parse and serialise CPEs in CPE23, URI, and WFN formats.
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
```ruby
|
10
|
+
gem 'cpe23'
|
11
|
+
```
|
12
|
+
|
13
|
+
And then execute:
|
14
|
+
|
15
|
+
$ bundle install
|
16
|
+
|
17
|
+
Or install it yourself as:
|
18
|
+
|
19
|
+
$ gem install cpe23
|
20
|
+
|
21
|
+
## Usage
|
22
|
+
|
23
|
+
Parse a CPE in CPE23, URI, or WFN format:
|
24
|
+
``` ruby
|
25
|
+
Cpe23.parse(string)
|
26
|
+
```
|
27
|
+
|
28
|
+
Serialise a CPE:
|
29
|
+
``` ruby
|
30
|
+
cpe.to_str
|
31
|
+
cpe.to_uri
|
32
|
+
cpe.to_wfn
|
33
|
+
```
|
34
|
+
|
35
|
+
Compare two CPEs:
|
36
|
+
- all non-wildcard components must match
|
37
|
+
- version compares the least specific of the two CPEs
|
38
|
+
- CPEs that differ only in version are ordered by their version
|
39
|
+
|
40
|
+
## Development
|
41
|
+
|
42
|
+
After checking out the repo, run `bundle install` to install dependencies. Then, run `rake test` to run the tests.
|
43
|
+
|
44
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
45
|
+
|
46
|
+
## Contributing
|
47
|
+
|
48
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/jtsymon/cpe23.
|
49
|
+
|
50
|
+
## License
|
51
|
+
|
52
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
data/Rakefile
ADDED
data/cpe23.gemspec
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
require_relative 'lib/cpe23/version'
|
2
|
+
|
3
|
+
Gem::Specification.new do |spec|
|
4
|
+
spec.name = "cpe23"
|
5
|
+
spec.version = Cpe23::VERSION
|
6
|
+
spec.authors = ["Jeremy Symon"]
|
7
|
+
spec.email = ["jtsymon@gmail.com"]
|
8
|
+
|
9
|
+
spec.summary = %q{CPE parser/generator/matcher}
|
10
|
+
spec.description = %q{Library for parsing, generating, and matching CPEs}
|
11
|
+
spec.homepage = "https://github.com/jtsymon/cpe23"
|
12
|
+
spec.license = "MIT"
|
13
|
+
spec.required_ruby_version = Gem::Requirement.new(">= 2.3.0")
|
14
|
+
|
15
|
+
spec.metadata["homepage_uri"] = spec.homepage
|
16
|
+
|
17
|
+
# Specify which files should be added to the gem when it is released.
|
18
|
+
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
19
|
+
spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
|
20
|
+
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
21
|
+
end
|
22
|
+
spec.bindir = "exe"
|
23
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
24
|
+
spec.require_paths = ["lib"]
|
25
|
+
spec.add_runtime_dependency('citrus', '~> 3.0')
|
26
|
+
end
|
data/lib/cpe23.rb
ADDED
@@ -0,0 +1,293 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'cpe23/version'
|
4
|
+
require 'cpe23/version_wildcard'
|
5
|
+
require 'citrus'
|
6
|
+
|
7
|
+
Citrus.load(File.expand_path('cpe23/wfn', File.dirname(__FILE__)))
|
8
|
+
Citrus.load(File.expand_path('cpe23/cpe23', File.dirname(__FILE__)))
|
9
|
+
|
10
|
+
# Implementation of CPE 2.3: https://cpe.mitre.org/specification
|
11
|
+
class Cpe23
|
12
|
+
include Comparable
|
13
|
+
# The part attribute SHALL have one of these three string values:
|
14
|
+
# The value "a", when the WFN is for a class of applications.
|
15
|
+
# The value "o", when the WFN is for a class of operating systems.
|
16
|
+
# The value "h", when the WFN is for a class of hardware devices.
|
17
|
+
attr_accessor :part
|
18
|
+
|
19
|
+
# Values for this attribute SHOULD describe or identify the person or
|
20
|
+
# organization that manufactured or created the product. Values for this
|
21
|
+
# attribute SHOULD be selected from an attribute-specific valid-values list,
|
22
|
+
# which MAY be defined by other specifications that utilize this
|
23
|
+
# specification. Any character string meeting the requirements for WFNs (cf.
|
24
|
+
# 5.3.2) MAY be specified as the value of the attribute.
|
25
|
+
attr_accessor :vendor
|
26
|
+
|
27
|
+
# Values for this attribute SHOULD describe or identify the most common and
|
28
|
+
# recognizable title or name of the product. Values for this attribute SHOULD
|
29
|
+
# be selected from an attribute-specific valid-values list, which MAYbe
|
30
|
+
# defined by other specifications that utilize this specification. Any
|
31
|
+
# character string meeting the requirements for WFNs(cf. 5.3.2) MAY be
|
32
|
+
# specified as the value of the attribute.
|
33
|
+
attr_accessor :product
|
34
|
+
|
35
|
+
# Values for this attribute SHOULD be vendor-specific alphanumeric strings
|
36
|
+
# characterizing the particular release version of the product. Version
|
37
|
+
# information SHOULD be copied directly (with escaping of printable
|
38
|
+
# non-alphanumeric characters as required) from discoverable data and SHOULD
|
39
|
+
# NOT be truncated or otherwise modified. Any character string meeting the
|
40
|
+
# requirements for WFNs (cf. 5.3.2) MAY be specified as the value of the
|
41
|
+
# attribute.
|
42
|
+
def version_raw
|
43
|
+
@version
|
44
|
+
end
|
45
|
+
|
46
|
+
def version
|
47
|
+
Cpe23::Version.new(@version)
|
48
|
+
end
|
49
|
+
|
50
|
+
attr_writer :version
|
51
|
+
|
52
|
+
# Values for this attribute SHOULD be vendor-specific alphanumeric strings
|
53
|
+
# characterizing the particular update, service pack, or point release of the
|
54
|
+
# product.Values for this attribute SHOULD be selected from an
|
55
|
+
# attribute-specific valid-values list, which MAYbe defined by other
|
56
|
+
# specifications that utilize this specification. Any character string meeting
|
57
|
+
# the requirements for WFNs (cf. 5.3.2) MAYbe specified as the value of the
|
58
|
+
# attribute.
|
59
|
+
attr_accessor :update
|
60
|
+
|
61
|
+
# The edition attribute isconsidered deprecatedin this specification, and it
|
62
|
+
# SHOULD be assigned the logical value ANY except where required for backward
|
63
|
+
# compatibility with version 2.2 of the CPE specification.This attribute is
|
64
|
+
# referred to as the "legacyedition" attribute. If this attribute is used,
|
65
|
+
# values for this attribute SHOULD capture edition-related terms applied by
|
66
|
+
# the vendor to the product. Values for this attribute SHOULD be selected from
|
67
|
+
# an attribute-specific valid-values list, which MAYbe defined by other
|
68
|
+
# specifications that utilize this specification. Any character string meeting
|
69
|
+
# the requirements for WFNs (cf. 5.3.2) MAY be specified as the value of the
|
70
|
+
# attribute.
|
71
|
+
attr_accessor :edition
|
72
|
+
|
73
|
+
# Values for thisattribute SHALL be valid language tagsas defined by
|
74
|
+
# [RFC5646], and SHOULD be used to define the language supported in the user
|
75
|
+
# interface of the product being described.Although any valid language tag MAY
|
76
|
+
# be used, only tags containing language and region codes SHOULD be used.
|
77
|
+
attr_accessor :language
|
78
|
+
|
79
|
+
# Values for this attribute SHOULD characterize how the product is tailored to
|
80
|
+
# a particular market or class of end users. Values for this attribute SHOULD
|
81
|
+
# be selected from an attribute-specific valid-values list, which MAYbe
|
82
|
+
# defined by other specifications that utilize this specification. Any
|
83
|
+
# character string meeting the requirements for WFNs(cf. 5.3.2) MAYbe
|
84
|
+
# specified as the value of the attribute.
|
85
|
+
attr_accessor :sw_edition
|
86
|
+
|
87
|
+
# Values for this attribute SHOULDcharacterize the software computing
|
88
|
+
# environment within which the product operates.Values for this attribute
|
89
|
+
# SHOULD be selected from an attribute-specific valid-values list, which MAYbe
|
90
|
+
# defined by other specifications that utilize this specification. Any
|
91
|
+
# character string meeting the requirements for WFNs(cf. 5.3.2) MAYbe
|
92
|
+
# specified as the value of the attribute.
|
93
|
+
attr_accessor :target_sw
|
94
|
+
|
95
|
+
# Valuesfor this attribute SHOULD characterize the instruction set
|
96
|
+
# architecture (e.g., x86) on which the product being described or identified
|
97
|
+
# by the WFN operates. Bytecode-intermediate languages, such as Java bytecode
|
98
|
+
# for the Java Virtual Machine or Microsoft Common Intermediate Language for
|
99
|
+
# the Common Language Runtime virtual machine, SHALL be considered instruction
|
100
|
+
# set architectures. Values for this attribute SHOULD be selected from an
|
101
|
+
# attribute-specific valid-values list, which MAY be defined by other
|
102
|
+
# specifications that utilize this specification. Any character string meeting
|
103
|
+
# the requirements for WFNs(cf. 5.3.2) MAY be specified as the value of the
|
104
|
+
# attribute.
|
105
|
+
attr_accessor :target_hw
|
106
|
+
|
107
|
+
# Values for this attribute SHOULD capture any other general descriptive or
|
108
|
+
# identifying information which is vendor-or product-specific and which does
|
109
|
+
# not logically fit in any other attribute value. Values SHOULD NOT be used
|
110
|
+
# for storing instance-specific data (e.g., globally-unique identifiers or
|
111
|
+
# Internet Protocol addresses).Values for this attribute SHOULD be selected
|
112
|
+
# from a valid-values list that is refined over time; this list MAYbe defined
|
113
|
+
# by other specifications that utilize this specification. Any character
|
114
|
+
# string meeting the requirements for WFNs (cf. 5.3.2) MAYbe specified as the
|
115
|
+
# value of the attribute.
|
116
|
+
attr_accessor :other
|
117
|
+
|
118
|
+
def initialize(part: nil, vendor: nil, product: nil, version: nil,
|
119
|
+
update: nil, edition: nil, language: nil, sw_edition: nil,
|
120
|
+
target_sw: nil, target_hw: nil, other: nil)
|
121
|
+
@part = part
|
122
|
+
@vendor = vendor
|
123
|
+
@product = product
|
124
|
+
@version = version
|
125
|
+
@update = update
|
126
|
+
@edition = edition
|
127
|
+
@language = language
|
128
|
+
@sw_edition = sw_edition
|
129
|
+
@target_sw = target_sw
|
130
|
+
@target_hw = target_hw
|
131
|
+
@other = other
|
132
|
+
end
|
133
|
+
|
134
|
+
def <=>(other)
|
135
|
+
unless other.is_a? Cpe23
|
136
|
+
begin
|
137
|
+
other = Cpe23.parse(other)
|
138
|
+
rescue StandardError
|
139
|
+
return nil
|
140
|
+
end
|
141
|
+
end
|
142
|
+
return nil unless
|
143
|
+
Cpe23.attr_match?(part, other.part) &&
|
144
|
+
Cpe23.attr_match?(vendor, other.vendor) &&
|
145
|
+
Cpe23.attr_match?(product, other.product) &&
|
146
|
+
Cpe23.attr_match?(update, other.update) &&
|
147
|
+
Cpe23.attr_match?(edition, other.edition) &&
|
148
|
+
Cpe23.attr_match?(language, other.language) &&
|
149
|
+
Cpe23.attr_match?(target_sw, other.target_sw) &&
|
150
|
+
Cpe23.attr_match?(target_hw, other.target_hw) &&
|
151
|
+
Cpe23.attr_match?(self.other, other.other)
|
152
|
+
|
153
|
+
version <=> other.version
|
154
|
+
end
|
155
|
+
|
156
|
+
def to_wfn
|
157
|
+
attrs = %i[part vendor product version update edition language sw_edition
|
158
|
+
target_sw target_hw other].map do |key|
|
159
|
+
value = instance_variable_get("@#{key}")
|
160
|
+
str = case value
|
161
|
+
when nil then 'NA'
|
162
|
+
when '*' then 'ANY'
|
163
|
+
else "\"#{value.downcase}\""
|
164
|
+
end
|
165
|
+
"#{key}=#{str}"
|
166
|
+
end
|
167
|
+
"wfn:[#{attrs.join(',')}]"
|
168
|
+
end
|
169
|
+
|
170
|
+
def to_uri
|
171
|
+
fields = [@part, @vendor, @product, @version, @update, @edition, @language]
|
172
|
+
# Strip trailing empty fields
|
173
|
+
fields = fields[0...-1] while fields.any? && fields[-1].nil?
|
174
|
+
fields.map! do |f|
|
175
|
+
f.sub('?', '%01')
|
176
|
+
.sub('*', '%02')
|
177
|
+
end
|
178
|
+
'cpe:/' + fields.join(':').downcase
|
179
|
+
end
|
180
|
+
|
181
|
+
def to_str
|
182
|
+
['cpe', '2.3', @part, @vendor, @product, @version, @update, @edition,
|
183
|
+
@language, @sw_edition, @target_sw, @target_hw, @other].join(':').downcase
|
184
|
+
end
|
185
|
+
|
186
|
+
alias to_s to_str
|
187
|
+
|
188
|
+
class << self
|
189
|
+
def parse(str)
|
190
|
+
str = str.strip
|
191
|
+
if str.start_with? 'wfn:'
|
192
|
+
parse_wfn(str)
|
193
|
+
elsif str.start_with? 'cpe:/'
|
194
|
+
parse_uri(str)
|
195
|
+
elsif str.start_with? 'cpe:2.3:'
|
196
|
+
parse_str(str)
|
197
|
+
else
|
198
|
+
raise ArgumentError, 'CPE malformed'
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
202
|
+
def attr_match?(first, second)
|
203
|
+
first == '*' || second == '*' || first == second
|
204
|
+
end
|
205
|
+
|
206
|
+
private
|
207
|
+
|
208
|
+
def parse_wfn(str)
|
209
|
+
data = {}
|
210
|
+
WFN.parse(str)[:attr].each do |attr|
|
211
|
+
key = attr.capture(:symbol).value.to_sym
|
212
|
+
value = attr.capture(:value).then do |val|
|
213
|
+
if (str = val.capture(:string))
|
214
|
+
str.capture(:content).value
|
215
|
+
else
|
216
|
+
# Translate WFN special values (only applies to non-string)
|
217
|
+
case (str = val.value)
|
218
|
+
when 'ANY' then '*'
|
219
|
+
when 'NA' then nil
|
220
|
+
else str
|
221
|
+
end
|
222
|
+
end
|
223
|
+
end
|
224
|
+
if data.include? key
|
225
|
+
raise ArgumentError, 'Attribute defined multiple times'
|
226
|
+
end
|
227
|
+
|
228
|
+
data[key] = value
|
229
|
+
end
|
230
|
+
|
231
|
+
new(**data)
|
232
|
+
end
|
233
|
+
|
234
|
+
def parse_uri(str)
|
235
|
+
tag, body = str.split(':/', 2)
|
236
|
+
raise ArgumentError, 'Not a CPE URI' if tag != 'cpe' || body.nil?
|
237
|
+
|
238
|
+
body.sub!('%01', '?')
|
239
|
+
body.sub!('%02', '*')
|
240
|
+
|
241
|
+
data = {}
|
242
|
+
data[:part], data[:vendor], data[:product], data[:version], data[:update],
|
243
|
+
data[:edition], data[:language], remainder = body.split(':')
|
244
|
+
|
245
|
+
raise ArgumentError, 'CPE URI malformed' unless remainder.nil?
|
246
|
+
|
247
|
+
# All attributes are optional.
|
248
|
+
new(**data)
|
249
|
+
end
|
250
|
+
|
251
|
+
# Faster implementation of CPE parser... Citrus is not fast :(
|
252
|
+
def parse_cpe23(str)
|
253
|
+
raise ArgumentError, 'Not a CPE str' unless str.start_with?('cpe:2.3')
|
254
|
+
|
255
|
+
index = 7
|
256
|
+
size = str.size
|
257
|
+
char = str[index]
|
258
|
+
attr = 11.times.map do
|
259
|
+
raise ArgumentError, 'CPE formatted string malformed' if char != ':'
|
260
|
+
|
261
|
+
index += 1
|
262
|
+
attr_index = index
|
263
|
+
until index >= size || (char = str[index]) == ':'
|
264
|
+
index += 1
|
265
|
+
index += 1 if char == '\\' # Skip escaped characters
|
266
|
+
end
|
267
|
+
str[attr_index...index]
|
268
|
+
end
|
269
|
+
raise ArgumentError, 'CPE formatted string malformed' if index != size
|
270
|
+
|
271
|
+
attr
|
272
|
+
end
|
273
|
+
|
274
|
+
def parse_str(str)
|
275
|
+
# attr = Cpe23.parse(str)[:attr].map(&:value)
|
276
|
+
attr = parse_cpe23(str)
|
277
|
+
data = {}
|
278
|
+
data[:part], data[:vendor], data[:product], data[:version],
|
279
|
+
data[:update], data[:edition], data[:language], data[:sw_edition],
|
280
|
+
data[:target_sw], data[:target_hw], data[:other], remainder = attr
|
281
|
+
|
282
|
+
# All attributes MUST appear
|
283
|
+
if data.any? { |_k, v| v.nil? } || !remainder.nil?
|
284
|
+
raise ArgumentError, 'CPE formatted string malformed'
|
285
|
+
end
|
286
|
+
|
287
|
+
# Remove empty attributes to avoid confusing the constructor
|
288
|
+
data.reject! { |_k, v| v.empty? }
|
289
|
+
|
290
|
+
new(**data)
|
291
|
+
end
|
292
|
+
end
|
293
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Cpe23
|
4
|
+
class Version
|
5
|
+
include Comparable
|
6
|
+
attr_reader :parts
|
7
|
+
|
8
|
+
def initialize(str)
|
9
|
+
@parts = str.split('.')
|
10
|
+
wildcard_index = @parts.index '*'
|
11
|
+
if wildcard_index.nil?
|
12
|
+
@parts << '*'
|
13
|
+
elsif wildcard_index < @parts.size - 1
|
14
|
+
raise 'Wildcard must be at the end of a version'
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def <=>(other)
|
19
|
+
unless other.is_a? Version
|
20
|
+
begin
|
21
|
+
other = Version.new(other)
|
22
|
+
rescue StandardError
|
23
|
+
return nil
|
24
|
+
end
|
25
|
+
end
|
26
|
+
@parts.zip(other.parts).each do |a, b|
|
27
|
+
break if a == '*' || b == '*'
|
28
|
+
|
29
|
+
# Compare parts numerically if they are numeric
|
30
|
+
if int?(a) && int?(b)
|
31
|
+
a = a.to_i
|
32
|
+
b = b.to_i
|
33
|
+
end
|
34
|
+
return -1 if a.to_i < b.to_i
|
35
|
+
return 1 if a.to_i > b.to_i
|
36
|
+
end
|
37
|
+
0
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
def int?(str)
|
43
|
+
true if Integer(str)
|
44
|
+
rescue StandardError
|
45
|
+
false
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
grammar WFN
|
2
|
+
rule wfn
|
3
|
+
'wfn:[' space (attr (space ',' space attr)*)? space ']'
|
4
|
+
end
|
5
|
+
|
6
|
+
rule attr
|
7
|
+
symbol space '=' space value
|
8
|
+
end
|
9
|
+
|
10
|
+
rule symbol
|
11
|
+
[a-zA-Z0-9_]*
|
12
|
+
end
|
13
|
+
|
14
|
+
rule escaped
|
15
|
+
'\\' .
|
16
|
+
end
|
17
|
+
|
18
|
+
rule content
|
19
|
+
(escaped | /[^"]/)*
|
20
|
+
end
|
21
|
+
|
22
|
+
rule string
|
23
|
+
'"' content '"'
|
24
|
+
end
|
25
|
+
|
26
|
+
rule value
|
27
|
+
string | symbol
|
28
|
+
end
|
29
|
+
|
30
|
+
rule space
|
31
|
+
[ \t]*
|
32
|
+
end
|
33
|
+
end
|
metadata
ADDED
@@ -0,0 +1,70 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: cpe23
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Jeremy Symon
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2020-02-14 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: citrus
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '3.0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '3.0'
|
27
|
+
description: Library for parsing, generating, and matching CPEs
|
28
|
+
email:
|
29
|
+
- jtsymon@gmail.com
|
30
|
+
executables: []
|
31
|
+
extensions: []
|
32
|
+
extra_rdoc_files: []
|
33
|
+
files:
|
34
|
+
- ".gitignore"
|
35
|
+
- ".travis.yml"
|
36
|
+
- Gemfile
|
37
|
+
- LICENSE.txt
|
38
|
+
- README.md
|
39
|
+
- Rakefile
|
40
|
+
- cpe23.gemspec
|
41
|
+
- lib/cpe23.rb
|
42
|
+
- lib/cpe23/cpe23.citrus
|
43
|
+
- lib/cpe23/version.rb
|
44
|
+
- lib/cpe23/version_wildcard.rb
|
45
|
+
- lib/cpe23/wfn.citrus
|
46
|
+
homepage: https://github.com/jtsymon/cpe23
|
47
|
+
licenses:
|
48
|
+
- MIT
|
49
|
+
metadata:
|
50
|
+
homepage_uri: https://github.com/jtsymon/cpe23
|
51
|
+
post_install_message:
|
52
|
+
rdoc_options: []
|
53
|
+
require_paths:
|
54
|
+
- lib
|
55
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
56
|
+
requirements:
|
57
|
+
- - ">="
|
58
|
+
- !ruby/object:Gem::Version
|
59
|
+
version: 2.3.0
|
60
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
61
|
+
requirements:
|
62
|
+
- - ">="
|
63
|
+
- !ruby/object:Gem::Version
|
64
|
+
version: '0'
|
65
|
+
requirements: []
|
66
|
+
rubygems_version: 3.1.2
|
67
|
+
signing_key:
|
68
|
+
specification_version: 4
|
69
|
+
summary: CPE parser/generator/matcher
|
70
|
+
test_files: []
|