opentpx 2.2.0.17

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,14 @@
1
+ require 'tpx/2_2/data_model'
2
+ require 'tpx/2_2/observable'
3
+
4
+
5
+ module TPX_2_2
6
+
7
+ # The representation of the threat observable (the hash map keyed
8
+ # by the observable_id within the element observable).
9
+ class ThreatObservable < Observable
10
+ MANDATORY_ATTRIBUTES = Observable::MANDATORY_ATTRIBUTES + [
11
+ :occurred_at_t
12
+ ]
13
+ end # class
14
+ end
@@ -0,0 +1,279 @@
1
+ require 'deep_merge'
2
+ # This uses the TPX JSON Schema to validate an input document.
3
+ module TPX_2_2
4
+ class Validator
5
+
6
+ class << self
7
+ TPX_KEYS = ['observable_dictionary_c_array', 'element_observable_c_array', 'collection_c_array', 'asn_c_array']
8
+ MANIFEST_KEYS = ['dictionary_file_manifest', 'observable_element_file_manifest', 'collection_file_manifest', 'network_file_manifest']
9
+
10
+ # validate_file! opens json file and validates it. Raises an error if validation fails.
11
+ #
12
+ # @param [String] filepath path to file which needs to be validated.
13
+ # @example
14
+ # TPX_2_2::Validator.validate_file!('folder_name/file_name.json')
15
+ def validate_file!(filepath)
16
+ unless File.exists? filepath
17
+ raise ValidationError , "No such file or directory '#{filepath}'"
18
+ end
19
+
20
+ @@current_path = File.dirname(filepath)
21
+
22
+ begin
23
+ h = Oj.load_file(filepath)
24
+ rescue => e
25
+ raise ValidationError, "File '#{filepath}' is not a valid JSON file:\n#{e}"
26
+ end
27
+
28
+ if h.kind_of?(Hash) and tpx_manifest?(h)
29
+ DeepMerge::deep_merge!(load_manifest_files(h), h, {:merge_hash_arrays => true})
30
+ delete_manifest_keys!(h)
31
+ end
32
+
33
+ validate!(h)
34
+ end
35
+
36
+ # validate_json! validates json string. Raises an error if validation fails.
37
+ #
38
+ # @param [String] json_enc_str the json string which need to be validated.
39
+ # @example
40
+ # TPX_2_2::Validator.validate_json!("{\"provider_s\":\"provider\",\"schema_version_s\":\"2.1.6\",\"source_observable_s\":\"source_observable\",\"source_description_s\":\"source_description\",\"distribution_time_t\":1438862400,\"last_updated_t\":1438862400,\"list_name_s\":\"list_name\",\"element_observable_c_array\":[{\"subject_s\":\"192.168.0.1\",\"type_s\":\"ipv4\",\"score_i\":80,\"score_24hr_decay_i\":0,\"threat_observable_c_map\":{\"Conficker\":{\"occurred_at_t\":4355545,\"last_seen_t\":13123}}}],\"observable_dictionary_c_array\":[{\"observable_id_s\":\"Malicious Host\",\"description_s\":\"This network node is a malicious host.\",\"criticality_i\":20,\"classification_c_array\":[{\"classification_id_s\":\"Malicious Host\",\"criticality_i\":20}]}],\"collection_c_array\":[{\"name_id_s\":\"MarketSeg1\",\"occurred_at_t\":1212312323,\"last_updated_t\":1212312323,\"description_s\":\"This collection is related to MarketSeg1\",\"author_s\":\"Allan Thomson\",\"workspace_s\":\"lg-system\"}],\"asn_c_array\":[{\"asn_number_ui\":1,\"occurred_at_t\":1212312323,\"as_owner_s\":\"ABC Corp\"}]}")
41
+ def validate_json!(json_enc_str)
42
+ h = Oj.load(json_enc_str)
43
+ validate!(h)
44
+ end
45
+
46
+ # validate! validates hash. Raises an error if validation fails.
47
+ #
48
+ # @param [Hash] input_hash the hash which needs to be validated.
49
+ # @example
50
+ # TPX_2_2::Validator.validate!({
51
+ # 'provider_s': 'provider',
52
+ # 'schema_version_s': '2.1.6',
53
+ # 'source_observable_s': 'source_observable',
54
+ # 'source_description_s': 'source_description',
55
+ # 'distribution_time_t': 1438862400,
56
+ # 'last_updated_t': 1438862400,
57
+ # 'list_name_s': 'list_name',
58
+ # 'element_observable_c_array': [
59
+ # {
60
+ # 'subject_s': '192.168.0.1',
61
+ # 'type_s': 'ipv4',
62
+ # 'score_i': 80,
63
+ # 'score_24hr_decay_i': 0,
64
+ # 'threat_observable_c_map': {
65
+ # 'Conficker': {
66
+ # 'occurred_at_t': 4355545,
67
+ # 'last_seen_t': 13123
68
+ # }
69
+ # }
70
+ # }
71
+ # ],
72
+ # 'observable_dictionary_c_array': [
73
+ # {
74
+ # 'observable_id_s': 'Malicious Host',
75
+ # 'description_s': 'This network node is a malicious host.',
76
+ # 'criticality_i': 20,
77
+ # 'classification_c_array': [
78
+ # {
79
+ # 'classification_id_s': 'Malicious Host',
80
+ # 'criticality_i': 20
81
+ # }
82
+ # ]
83
+ # }
84
+ # ],
85
+ # 'collection_c_array': [
86
+ # {
87
+ # 'name_id_s': 'MarketSeg1',
88
+ # 'occurred_at_t': 1212312323,
89
+ # 'last_updated_t': 1212312323,
90
+ # 'description_s': 'This collection is related to MarketSeg1',
91
+ # 'author_s': 'Allan Thomson',
92
+ # 'workspace_s': 'lg-system'
93
+ # }
94
+ # ],
95
+ # 'asn_c_array': [
96
+ # {
97
+ # 'asn_number_ui': 1,
98
+ # 'occurred_at_t': 1212312323,
99
+ # 'as_owner_s': 'ABC Corp'
100
+ # }
101
+ # ]
102
+ # })
103
+ def validate!(input_hash)
104
+ @@undefined_observables = []
105
+
106
+ if input_hash.nil? || input_hash.empty?
107
+ raise ValidationError, " TPX has no content"
108
+ end
109
+
110
+ validate_schema!(input_hash)
111
+
112
+ if (TPX_KEYS & input_hash.keys).length > 0 && tpx_manifest?(input_hash)
113
+ raise TPX_2_2::ValidationError, "TPX file must either be in single-file or manifest format."
114
+ end
115
+
116
+ validate_element_observables!(input_hash['element_observable_c_array'])
117
+ validate_observable_dictionary!(input_hash['observable_dictionary_c_array'], input_hash['element_observable_c_array'])
118
+ validate_collections!(input_hash['collection_c_array'])
119
+ validate_networks!(input_hash['asn_c_array'])
120
+ if (input_hash['observable_dictionary_c_array'].nil? || input_hash['observable_dictionary_c_array'].empty?)
121
+ raise TPX_2_2::ValidationWarning, "observable dictionary is not defined"
122
+ end
123
+ unless @@undefined_observables.empty?
124
+ raise TPX_2_2::ValidationWarning, "observables #{@@undefined_observables.inspect} are not defined in the observable dictionary"
125
+ end
126
+ end
127
+
128
+
129
+ private
130
+
131
+ def validate_schema!(input_hash)
132
+ errors = JSON::Validator.fully_validate(TPX_2_2::SCHEMA, input_hash)
133
+ raise ValidationError, "#{errors.join(' ')}" unless errors.empty?
134
+ end
135
+
136
+ def validate_element_observables!(element_observable_list)
137
+ return if element_observable_list.nil? #nothing to validate, just return
138
+
139
+ schema = {
140
+ "type" => "object",
141
+ "required" => ["threat_observable_c_map"],
142
+ "properties" => {
143
+ "threat_observable_c_map" => {"type" => "hash"}
144
+ }
145
+ }
146
+ element_observable_list.each do |element_observable|
147
+ errors = JSON::Validator.fully_validate(schema, element_observable)
148
+ raise ValidationError, "#{errors.join(' ')}" unless errors.empty?
149
+ end
150
+ end
151
+
152
+ def validate_observable_dictionary!(observable_dictionaries, element_observable_list)
153
+ return if observable_dictionaries.nil? #nothing to validate, just return
154
+
155
+ schema = {
156
+ "type" => "object",
157
+ "required" => [
158
+ "observable_id_s",
159
+ "description_s",
160
+ "classification_c_array"
161
+ ],
162
+ "properties" => {
163
+ "observable_id_s" => {"type" => "string"},
164
+ "description_s" => {"type" => "string"},
165
+ "classification_c_array" => {"type" => "hash"}
166
+ }
167
+ }
168
+
169
+ observables = []
170
+
171
+ observable_dictionaries.each do |observable_dictionary|
172
+ errors = JSON::Validator.fully_validate(schema, observable_dictionary)
173
+ raise TPX_2_2::ValidationError, "#{errors.join(' ')}" unless errors.empty?
174
+ observables << observable_dictionary['observable_id_s']
175
+ end
176
+
177
+ return if element_observable_list.nil?
178
+ element_observable_list.each do |element_observable|
179
+ element = element_observable['threat_observable_c_map'] || element_observable[:threat_observable_c_map]
180
+ element.each_key do |observable|
181
+ @@undefined_observables << """#{observable}""" unless observables.include? observable
182
+ end
183
+ end
184
+
185
+ end
186
+
187
+ def validate_collections!(collections)
188
+ return if collections.nil? #nothing to validate, just return
189
+
190
+ schema = {
191
+ "type" => "object",
192
+ "required" => ["name_id_s"],
193
+ "properties" => {
194
+ "name_id_s" => {"type" => "string"}
195
+ }
196
+ }
197
+
198
+ collections.each do |collection|
199
+ errors = JSON::Validator.fully_validate(schema, collection)
200
+ raise ValidationError, "#{errors.join(' ')}" unless errors.empty?
201
+ collection.each do |key, value|
202
+ if (key == 'collection_c_array') && value.is_a?(Array)
203
+ validate_collections!(value)
204
+ end
205
+ end
206
+ end
207
+ end
208
+
209
+ def validate_networks!(networks)
210
+ return if networks.nil? #nothing to validate, just return
211
+
212
+ schema = {
213
+ "type" => "object",
214
+ "required" => ["asn_number_ui"],
215
+ "properties" => {
216
+ "asn_number_ui" => {"type" => "integer"}
217
+ }
218
+ }
219
+
220
+ networks.each do |net|
221
+ errors = JSON::Validator.fully_validate(schema, net)
222
+ raise ValidationError, "#{errors.join(' ')}" unless errors.empty?
223
+ end
224
+ end
225
+
226
+ # tpx_manifest? returns true if provided hash represents TPX manifest and contains TPX manifest entries
227
+ #
228
+ # @param [Hash] Hash with tpx content
229
+ # @example
230
+ # TPX_2_2::Validator.tpx_manifest?({ "schema_version_s"=> "2.2.0", "provider_s"=> "Lookingglass Cyber Solutions", "list_name_s"=> "Virus Tracker", "source_observable_s"=> "Virus Tracker", "source_file_s"=> "https://virustracker.net/", "source_description_s"=> "Virus Tracker provides real-time information about virus infections observed through custom sinkholes.", "distribution_time_t"=> 1443007504, "last_updated_t"=> 1443007504, "score_i"=> 90, "dictionary_file_manifest"=> [ "dictionary.json" ], "observable_element_file_manifest"=> [ "data8.json" ]})
231
+ def tpx_manifest?(input_hash)
232
+ (MANIFEST_KEYS & input_hash.keys).length > 0
233
+ end
234
+
235
+ # current_path returns the current path which can be one of: 1) current working directory 2) file directory path being validated
236
+ def current_path
237
+ @@current_path || Dir.pwd
238
+ end
239
+
240
+ # load_manifest_files loads files referenced in manifests and returns merged content
241
+ #
242
+ # @param [Hash] Hash with tpx content
243
+ def load_manifest_files(input_hash)
244
+ res = {}
245
+ MANIFEST_KEYS.each do |manifest_section|
246
+ files = input_hash[manifest_section]
247
+ unless files.blank?
248
+ files.each do |manifest_file|
249
+ DeepMerge::deep_merge!(load_manifest_file(manifest_file), res, {:merge_hash_arrays => true})
250
+ end
251
+ end
252
+ end
253
+ res
254
+ end
255
+
256
+ # delete_manifest_keys! deletes manifest keys from input_hash in order to be valid TPX
257
+ #
258
+ # @param [Hash] Hash with tpx content
259
+ def delete_manifest_keys!(input_hash)
260
+ MANIFEST_KEYS.each do |key|
261
+ input_hash.delete key
262
+ end
263
+ end
264
+
265
+ # load_manifest_file loads TPX content from specified file, in case if file doesn't exists searches for it in current_path. In case if file not found ValidationError is raised
266
+ #
267
+ # @param [String] filename
268
+ def load_manifest_file(filename)
269
+ return Oj.load_file(filename) if File.exists? filename
270
+
271
+ filepath = File.join(current_path, File.basename(filename))
272
+ return Oj.load_file(filepath) if File.exists? filepath
273
+
274
+ raise ValidationError, "Could not find #{filename}"
275
+ end
276
+
277
+ end # class < self
278
+ end
279
+ end
@@ -0,0 +1,81 @@
1
+ require 'gli'
2
+ require 'tpx'
3
+
4
+ module TPX
5
+ class Tools
6
+ extend ::GLI::App
7
+
8
+ TPX_VALID = "Validation succeeded"
9
+ TPX_INVALID = "The TPX file provided is invalid for the following reasons:"
10
+ TPX_VERSION_UNKNOWN = "Unknown TPX Version: "
11
+
12
+ class << self
13
+ attr_accessor :quiet
14
+
15
+ def msg(message)
16
+ puts message unless quiet
17
+ end
18
+
19
+ def get_tpx_version_const(tpx_version)
20
+ underscored_tpx_version = tpx_version.gsub('.', '_')
21
+ Object.const_get("TPX_#{underscored_tpx_version}")
22
+ rescue NameError => e
23
+ msg( e )
24
+ raise TPX_VERSION_UNKNOWN + "'#{tpx_version}'"
25
+ end
26
+
27
+ def validate(tpx_version_const, filepath)
28
+ begin
29
+ tpx_version_const::Validator.validate_file! filepath
30
+ rescue tpx_version_const::ValidationWarning => w
31
+ msg( "Warning: #{w}" )
32
+ puts TPX_VALID + " against " + tpx_version_const.to_s
33
+ rescue => e
34
+ puts TPX_INVALID
35
+ puts e
36
+ else
37
+ puts TPX_VALID
38
+ end
39
+ end
40
+
41
+
42
+ end
43
+
44
+ program_desc 'OpenTPX Tools'
45
+ version TPX::VERSION
46
+
47
+ switch [:q, :quiet], default_value: false,
48
+ desc: 'quiet non-essential output and warnings.'
49
+
50
+ #--- COMMAND validate
51
+ desc "validates that a file is of a valid TPX file format"
52
+ long_desc "validates that a file is of a valid TPX file format\n\n" \
53
+ "TPX_FILE_PATH - The path to the TPX file. The file may be either a" \
54
+ " stand-alone TPX file or a tpx manifest file. For details on TPX" \
55
+ " manifest, please see https://github.com/Lookingglass/tpx"
56
+
57
+ arg 'TPX_FILE_PATH'
58
+
59
+ command :validate do |c|
60
+ c.flag [:v, :tpx_version], default_value: "2.2",
61
+ arg_name: 'VERSION',
62
+ type: String,
63
+ desc: "The version of tpx to validate the file against. Possible" \
64
+ " values: '2.2'"
65
+
66
+ c.action do |global_options, options, args|
67
+ self.quiet = global_options[:quiet]
68
+ tpx_version = options[:tpx_version]
69
+ tpx_file_path = args[0]
70
+
71
+ raise "Missing option TPX_VERSION" if tpx_version.nil?
72
+ raise "Missing arg TPX_FILE_PATH" if tpx_file_path.nil?
73
+
74
+ tpx_version = tpx_version.strip
75
+ msg( "Validating '#{tpx_file_path}' against TPX '#{tpx_version}' schema" )
76
+ tpx_version_const = get_tpx_version_const(tpx_version)
77
+ validate(tpx_version_const, tpx_file_path)
78
+ end
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,3 @@
1
+ module TPX
2
+ VERSION = '2.2.0.17'
3
+ end
@@ -0,0 +1,14 @@
1
+ module TPX_2_2
2
+ CURRENT_SCHEMA_VERSION = '2.2.0'
3
+ SCHEMA = File.join(File.dirname(File.expand_path(__FILE__)), 'tpx', '2_2', 'schema', 'tpx.2.2.schema.json')
4
+
5
+ require 'json-schema'
6
+ require 'active_support'
7
+ require 'active_support/core_ext/object/blank'
8
+ require 'active_support/core_ext/hash'
9
+ require 'oj'
10
+
11
+ require 'tpx/2_2/exceptions'
12
+ require 'tpx/2_2/validator'
13
+ require 'tpx/2_2/exchange'
14
+ end
metadata ADDED
@@ -0,0 +1,218 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: opentpx
3
+ version: !ruby/object:Gem::Version
4
+ version: 2.2.0.17
5
+ platform: ruby
6
+ authors:
7
+ - LookingGlass
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-10-17 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rspec
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: timecop
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: yard
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: bundler
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rake
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: activesupport
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :runtime
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: oj
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :runtime
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: json-schema
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: 2.5.1
118
+ type: :runtime
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ version: 2.5.1
125
+ - !ruby/object:Gem::Dependency
126
+ name: deep_merge
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - ">="
130
+ - !ruby/object:Gem::Version
131
+ version: '0'
132
+ type: :runtime
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - ">="
137
+ - !ruby/object:Gem::Version
138
+ version: '0'
139
+ - !ruby/object:Gem::Dependency
140
+ name: gli
141
+ requirement: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - "~>"
144
+ - !ruby/object:Gem::Version
145
+ version: 2.13.2
146
+ type: :runtime
147
+ prerelease: false
148
+ version_requirements: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - "~>"
151
+ - !ruby/object:Gem::Version
152
+ version: 2.13.2
153
+ description: An open-source format and tools for exchanging threat intelligence data. This
154
+ is a JSON-based format that allows sharing of data between partner organizations.
155
+ email:
156
+ - support@lgscout.com
157
+ executables:
158
+ - opentpx_tools
159
+ extensions: []
160
+ extra_rdoc_files: []
161
+ files:
162
+ - LICENSE.txt
163
+ - README.md
164
+ - bin/opentpx_tools
165
+ - lib/tpx.rb
166
+ - lib/tpx/2_2/attribute_accessors.rb
167
+ - lib/tpx/2_2/classification_element.rb
168
+ - lib/tpx/2_2/classification_element_list.rb
169
+ - lib/tpx/2_2/collection.rb
170
+ - lib/tpx/2_2/collection_element.rb
171
+ - lib/tpx/2_2/data_model.rb
172
+ - lib/tpx/2_2/element_observable.rb
173
+ - lib/tpx/2_2/element_observable_list.rb
174
+ - lib/tpx/2_2/exceptions.rb
175
+ - lib/tpx/2_2/exchange.rb
176
+ - lib/tpx/2_2/heterogeneous_list.rb
177
+ - lib/tpx/2_2/homogeneous_list.rb
178
+ - lib/tpx/2_2/mandatory_attributes.rb
179
+ - lib/tpx/2_2/merging_heterogeneous_list.rb
180
+ - lib/tpx/2_2/merging_homogeneous_list.rb
181
+ - lib/tpx/2_2/network.rb
182
+ - lib/tpx/2_2/network_list.rb
183
+ - lib/tpx/2_2/observable.rb
184
+ - lib/tpx/2_2/observable_attribute_map.rb
185
+ - lib/tpx/2_2/observable_definition.rb
186
+ - lib/tpx/2_2/observable_dictionary.rb
187
+ - lib/tpx/2_2/schema/tpx.2.2.schema.json
188
+ - lib/tpx/2_2/threat_observable.rb
189
+ - lib/tpx/2_2/validator.rb
190
+ - lib/tpx/tools.rb
191
+ - lib/tpx/version.rb
192
+ - lib/tpx_2_2.rb
193
+ homepage: http://www.opentpx.org
194
+ licenses:
195
+ - The Apache License, Version 2.0. See LICENSE.txt
196
+ metadata: {}
197
+ post_install_message:
198
+ rdoc_options: []
199
+ require_paths:
200
+ - lib
201
+ required_ruby_version: !ruby/object:Gem::Requirement
202
+ requirements:
203
+ - - ">="
204
+ - !ruby/object:Gem::Version
205
+ version: '0'
206
+ required_rubygems_version: !ruby/object:Gem::Requirement
207
+ requirements:
208
+ - - ">="
209
+ - !ruby/object:Gem::Version
210
+ version: '0'
211
+ requirements: []
212
+ rubyforge_project:
213
+ rubygems_version: 2.4.3
214
+ signing_key:
215
+ specification_version: 4
216
+ summary: Open Threat Partner Exchange (OpenTPX)
217
+ test_files: []
218
+ has_rdoc: