ruby_stix 0.0.2-java

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 112ae61c03f537dfd3b01d0dffaeedd15ad82300
4
+ data.tar.gz: 695c4969f7eca8b727d458c77ae76597a8335782
5
+ SHA512:
6
+ metadata.gz: c1ad8b82de21c3ddc359aaf98d7b0ead9f06dbe34b795efd2934c4730b4d741d4c72cf7d7e4279503aa9594b2b21d4d4af1ba6a807c27bffb3d3fc0bfd31777a
7
+ data.tar.gz: 6a7731bf85bdd57817956d43c1c7e667643114fa245d39a6d6eb20e76670568ba7046d43fde478fa3875889d6e2e98c1c7f860931129167468ddb0689b1de1c2
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --colour
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ jruby-1.7.9
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source 'http://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in ruby_stix.gemspec
4
+ gemspec
5
+
6
+ gem 'rspec'
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 John Wunder
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,30 @@
1
+ # StixRuby
2
+
3
+ Bindings for generating and parsing STIX documents in JRuby. It relies on java-stix.
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'ruby_stix', :git => "http://github.com/johnwunder/ruby-stix.git"
10
+
11
+ And then execute:
12
+
13
+ $ bundle install
14
+
15
+ Or, clone this repository, build the gem, and install it manually as:
16
+
17
+ $ rake build
18
+ $ gem install pkg/ruby_stix.gem
19
+
20
+ ## Usage
21
+
22
+ TODO
23
+
24
+ ## Contributing
25
+
26
+ 1. Fork it
27
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
28
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
29
+ 4. Push to the branch (`git push origin my-new-feature`)
30
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
Binary file
@@ -0,0 +1,307 @@
1
+ require 'active_support/inflector'
2
+ require 'set'
3
+
4
+ java_import 'javax.xml.datatype.DatatypeFactory'
5
+
6
+ ActiveSupport::Inflector.inflections do |inflect|
7
+ inflect.acronym 'TTP'
8
+ inflect.acronym 'TTPs'
9
+ end
10
+
11
+ class Java::OrgMitre::ApiHelper
12
+
13
+ BLACKLIST = ["getClass", "hashCode", "equals", "toString", "notify", "notifyAll", "wait"]
14
+
15
+ def initialize(*args)
16
+ # Every time we create an object, we try to annotate that object's class. If the class has been annotated already,
17
+ # this does nothing.
18
+ annotate_class!
19
+
20
+ # Call the super constructor to create the java object backing
21
+ super()
22
+
23
+ # We add several options for creating new objects. This method figures out what the particular object we're creating
24
+ # supports and then processes the arguments. It will throw an error if invalid arguments are passed.
25
+ process_constructor_args(*args)
26
+
27
+ # (TODO: This may not be the right place to put this?)
28
+ # Generate an ID if the object supports it and we didn't set something manually
29
+ self.generate_id! if should_create_id?
30
+ end
31
+
32
+ # Returns whether or not a new object should have an auto-generated ID. The criteria are:
33
+ # 1. It responds to "id" and "idref" (i.e. is a STIX-idable object)
34
+ # 2. It doesn't have "suppress_id" set
35
+ # 3. There's no id or idref set already
36
+ def should_create_id?
37
+ self.respond_to?(:id) && self.respond_to?(:idref) && !self.class.suppress_id? && self.idref.nil? && self.id.nil?
38
+ end
39
+
40
+ # Process arguments that are passed to the constructor
41
+ # There are a couple options here:
42
+
43
+ # 1. A hash is passed, which will call appropriate setter methods to set each value to the key
44
+ # 2. A string is passed, which will set the string value of the element
45
+ # 3. A string and hash are passed, which will do both
46
+ # 4. Nothing is passed, which will just create the object
47
+ def process_constructor_args(*args)
48
+ # If two arguments are passed, option #3 is the winner. The first argument will set the value and the second argument is the kv hash
49
+ if args.length == 2 && self.respond_to?("value=")
50
+ self.value = args[0]
51
+ args = args[1]
52
+ # If one argument is passed and it's not a hash, try to set it as the value (likely it's a string) and use an empty hash as the kv hash
53
+ elsif args.length == 1 && !args[0].kind_of?(Hash) && self.respond_to?("value=")
54
+ self.value = args[0]
55
+ args = {}
56
+ # If one argument is passed and it's a hash, use that as the kv hash
57
+ elsif args.length == 1 && args[0].kind_of?(Hash)
58
+ args = args[0]
59
+ # If nothing was passed, use an empty hash as the kv hash
60
+ elsif args.length == 0
61
+ args = {}
62
+ elsif args.first.kind_of?(Array)
63
+ handle_array_argument(self, args.first)
64
+ args = {}
65
+ # Finally, throw an error if the arguments are anything else
66
+ else
67
+ raise "Invalid arguments to construct #{self.class.to_s}: #{args.inspect}"
68
+ end
69
+
70
+ # If key/value pairs were passed, use them
71
+ process_args(args).each do |key, value|
72
+ process_single_argument(key, value)
73
+ end
74
+ end
75
+
76
+ def process_single_argument(k, v)
77
+ # If we respond to the setter, call it. This effectively allows Java-style keyword argument names to be used
78
+ if self.respond_to?("set#{k}")
79
+ self.send("set#{k}", v)
80
+ # If we respond to the Ruby setter, call it. This allows Ruby-style keyword argument names to be used
81
+ elsif self.respond_to?("#{k}=")
82
+ self.send("#{k}=", v)
83
+ # If the value is an array, we can handle it a little differently
84
+ # Note that some array arguments might get caught by the setter
85
+ elsif v.kind_of?(Array)
86
+ # Find the Java method name even if we used a Ruby-style name. This is imperfect so may throw errors.
87
+ java_method_name = java_method_name_for_key(k)
88
+
89
+ expected_type = nil
90
+
91
+ # Add each value individually to the list
92
+ v.each do |value|
93
+ argument_type = find_generic_argument_for(java_method_name)
94
+
95
+ value = auto_create_object(argument_type, value)
96
+
97
+ # Finally, set the value
98
+ self.send(java_method_name).add(value)
99
+ end
100
+ else
101
+ raise ArgumentError.new("Invalid argument to construct #{self.class.to_s}: `#{k}`")
102
+ end
103
+ end
104
+
105
+ def java_method_name_for_key(k)
106
+ if self.respond_to?("get#{k}")
107
+ "get#{k}"
108
+ elsif self.respond_to?("get#{to_java_name(k)}")
109
+ "get#{to_java_name(k)}"
110
+ else
111
+ raise "Unable to find corresponding java method for #{k}"
112
+ end
113
+ end
114
+
115
+ # Finds the expected class for a list by parsing it out of the Java signature. This kind of blows but the way Java
116
+ # implements generics (type erasure) means the JRuby code does not have access to the generic type.
117
+ def find_generic_argument_for(k)
118
+ return eval(self.java_class.java_method(k).to_generic_string.match(/<(.+)>/)[1])
119
+ end
120
+
121
+ # Convert a Ruby-style method name (lower snake) to a Java-style method name (camel)
122
+ # This is imperfect, really I would like to re-use the JRuby logic but don't know how.
123
+ def to_java_name(string)
124
+ string.to_s.camelize
125
+ end
126
+
127
+ # Generate a random ID. Uses the ID namespace if it's been set.
128
+ def generate_id!
129
+ self.id = StixRuby.generate_id(self.class.to_s.split('::').last.gsub('Type', '').downcase)
130
+ end
131
+
132
+ # Theoretically method_missing might be used for more, but currently it just tries to catch "add_"
133
+ # calls and direct them to the appropriate child.
134
+ # TODO: Should we just define these methods manually by iterating over all methods and finding lists?
135
+ def method_missing(method_name, *args)
136
+ # Catch us trying to "add_observable" to "ObservablesType" and correctly handle it
137
+ if matches = method_name.to_s.match(/^add_(.+)$/)
138
+ if matches[1] && self.respond_to?(matches[1].pluralize)
139
+ java_method_name = java_method_name_for_key(matches[1].pluralize)
140
+ if respond_to?(java_method_name)
141
+ # If the method is a list, try to add the object
142
+ if send(java_method_name).class == Java::JavaUtil::ArrayList # Need to do an equality check on the class because sometimes other classes masquerade as lists
143
+ argument_type = find_generic_argument_for(java_method_name)
144
+ self.send(java_method_name).add(auto_create_object(argument_type, args.first))
145
+ elsif send(java_method_name).nil?
146
+ # Use the setter...
147
+ send(java_method_name.gsub(/^get/, "set"), args)
148
+ else
149
+ # We already have an item in the list, so just add the new one
150
+ intermediate = send(java_method_name)
151
+ argument_type = intermediate.find_generic_argument_for(java_method_name)
152
+ intermediate.send(java_method_name).add(auto_create_object(argument_type, args.first))
153
+ end
154
+ else
155
+ super
156
+ end
157
+ else
158
+ super
159
+ end
160
+ else
161
+ super
162
+ end
163
+ end
164
+
165
+ # This is a callback that children can override to add fancy helpers to constructor arguments
166
+ # Here though it's a pass-through
167
+ def process_args(args)
168
+ args
169
+ end
170
+
171
+ # Some behavior to determine whether to generate an ID
172
+ def self.suppress_id
173
+ @suppress_id = true
174
+ end
175
+
176
+ # TODO: This would be better without the multiple checks...
177
+ def self.suppress_id?
178
+ @suppress_id == true || (superclass.respond_to?(:suppress_id) && superclass.suppress_id?)
179
+ end
180
+
181
+ def self.annotate!
182
+ # Mark us as annotated
183
+ @annotated = true
184
+
185
+ # Annotate superclass if it's ok with that
186
+ self.superclass.annotate! if self.superclass.respond_to?(:annotate!)
187
+
188
+ # JRuby->Ruby name translation is not perfect and, for example, screws up "TTP"
189
+ # This will go through all methods and correct the ruby methods
190
+ StixRuby::IRREGULARS.each do |irregular_pattern, correct_pattern|
191
+ self.instance_methods.select {|m| m.to_s =~ irregular_pattern}.each do |irregular|
192
+ alias_method irregular.to_s.gsub(irregular_pattern, correct_pattern), irregular
193
+ end
194
+ end
195
+
196
+ # Hooks into both Ruby and Java-style setters and makes them a little more intelligent by trying
197
+ # to handle arrays appropriately and call constructors automatically when necessary
198
+ self.setter_methods.each do |method_name, java_method|
199
+ # Find the type of the argument and the name of the method
200
+ argument_type = java_method.argument_types.first.ruby_class
201
+
202
+ # Do not annotate this method if it's already annotated or has a basic value constructor
203
+ next if argument_type == Java::JavaLang::Object || self.annotated_method?(method_name)
204
+
205
+ # Mark this method as annotated
206
+ self.annotated_method(method_name)
207
+
208
+ # Alias the raw version
209
+ alias_method method_name + "Raw", method_name
210
+
211
+ # Re-define the method
212
+ define_method method_name, ->(*args) do
213
+ # Must have at least one argument
214
+ raise ArgumentError.new("Wrong number of arguments (0 for 1)") if args.nil? || args.length == 0
215
+
216
+ # Pass the argument to the raw setter if it's already of the correct type
217
+ if args.first.kind_of?(argument_type)
218
+ send(method_name + "Raw", *args)
219
+ # This handles cases where we have essentially a wrapper element around an array
220
+ # and allows us to just set the array
221
+ elsif args.first.kind_of?(Array)
222
+ new_obj = argument_type.new
223
+ handle_array_argument(new_obj, args.first)
224
+ send(method_name + "Raw", new_obj)
225
+ else
226
+ # Try to auto-create the object (magic happens here)
227
+ object = auto_create_object(argument_type, args.first)
228
+
229
+ send(method_name + "Raw", object)
230
+ end
231
+ end
232
+ end
233
+ end
234
+
235
+ # Returns the Ruby or Java setter method name and the corresponding java setter method reference
236
+ def self.setter_methods
237
+ self.java_class.java_instance_methods.select {|method| method.name =~ /^set/ && !(method.name =~ /Raw$/)}.map { |method|
238
+ # If the method accepts more than one argument, ignore it
239
+ if method.argument_types.length == 1
240
+ methods = [[method.name, method]]
241
+ methods << [ruby_name(method.name), method] if self.instance_methods.find {|m| m.to_s == ruby_name(method.name)}
242
+ ruby_setter = ruby_name(method.name.gsub("set", "") + "=")
243
+ methods << [ruby_setter, method] if self.instance_methods.find {|m| m.to_s == ruby_setter}
244
+ methods
245
+ else
246
+ nil
247
+ end
248
+ }.compact.flatten(1)
249
+ end
250
+
251
+ def handle_array_argument(assign_to, argument)
252
+ # Create the array destination
253
+ # Try to find the appropriate getter method for the array
254
+ getter = assign_to.java_class.java_instance_methods.reject {|m| BLACKLIST.include?(m.name) || m.return_type != java.util.List.java_class }
255
+ raise "Unable to automatically determine array container, please explicitly specify it" if getter.length != 1
256
+ getter = getter.first.name
257
+ array = assign_to.send(getter)
258
+ getter_reference = assign_to.java_class.java_method(getter).to_generic_string.match(/<(.+)>/)[1]
259
+ expected_type = eval(getter_reference)
260
+ argument.each {|item|
261
+ array.add(auto_create_object(expected_type, item))
262
+ }
263
+ end
264
+
265
+ def auto_create_object(argument_type, arg)
266
+ if arg.kind_of?(argument_type)
267
+ arg
268
+ elsif argument_type.respond_to?(:from_value)
269
+ argument_type.from_value(arg)
270
+ # Handle an array argument
271
+ # JAXB dates are really F'd up, so autoconvert them
272
+ elsif argument_type == javax.xml.datatype.XMLGregorianCalendar
273
+ calendar = java.util.GregorianCalendar.new
274
+ calendar.setTime(arg.to_java)
275
+ DatatypeFactory.newInstance.newXMLGregorianCalendar(calendar)
276
+ else
277
+ argument_type.new(arg)
278
+ end
279
+ end
280
+
281
+ def self.ruby_name(method)
282
+ method.underscore
283
+ end
284
+
285
+ # A bunch of crap for detecting when things have already been annotated
286
+ def self.annotated?
287
+ @annotated == true
288
+ end
289
+
290
+ def annotated?
291
+ self.class.annotated?
292
+ end
293
+
294
+ def self.annotated_method?(name)
295
+ @annotated_methods ||= Set.new
296
+ @annotated_methods.include?(name) || (self.superclass.respond_to?(:annotated_method?) && self.superclass.annotated_method?(name))
297
+ end
298
+
299
+ def self.annotated_method(method)
300
+ @annotated_methods ||= Set.new
301
+ @annotated_methods.add(method)
302
+ end
303
+
304
+ def annotate_class!
305
+ self.class.annotate! unless annotated?
306
+ end
307
+ end
@@ -0,0 +1,3 @@
1
+ class Java::OrgMitreCyboxCommon::BaseObjectPropertyType
2
+ suppress_id
3
+ end
@@ -0,0 +1,28 @@
1
+ class Java::OrgMitreStixIndicator::IndicatorType
2
+ def item=(val)
3
+ if val.kind_of?(org.mitre.cybox.core.ObservableType)
4
+ self.observable = val
5
+ elsif val.kind_of?(org.mitre.cybox.core.ObjectType)
6
+ self.observable = org.mitre.cybox.core.ObservableType.new(:object => val)
7
+ elsif val.kind_of?(org.mitre.cybox.core.EventType)
8
+ self.observable = org.mitre.cybox.core.ObservableType.new(:event => val)
9
+ elsif val.kind_of?(org.mitre.cybox.common.ObjectPropertiesType)
10
+ self.observable = org.mitre.cybox.core.ObservableType.new(:object => org.mitre.cybox.core.ObjectType.new(:properties => val))
11
+ elsif val.kind_of?(Hash) && val[:operator]
12
+ self.composite_indicator_expression = process_composition(val)
13
+ else
14
+ raise "Unknown item type: #{val.class}"
15
+ end
16
+ end
17
+
18
+ def process_composition(composition_hash)
19
+ operator = org.mitre.stix.indicator.OperatorTypeEnum.from_value(composition_hash[:operator].to_s.upcase)
20
+ composition = org.mitre.stix.indicator.CompositeIndicatorExpressionType.new(:operator => operator)
21
+ composition_hash[:items].each do |item|
22
+ indicator = item.kind_of?(org.mitre.stix.common.IndicatorBaseType) ? item : self.class.new(:item => item)
23
+ composition.add_indicator(indicator)
24
+ end
25
+
26
+ return composition
27
+ end
28
+ end
@@ -0,0 +1,26 @@
1
+ class Java::OrgMitreCyboxCore::ObjectType
2
+ def add_related_object(object, relationship = nil)
3
+ self.related_objects ||= org.mitre.cybox.core.RelatedObjectsType.new
4
+
5
+ if object.kind_of?(org.mitre.cybox.core.RelatedObjectType)
6
+ self.related_objects.add_related_object(object)
7
+ else
8
+ related_object = org.mitre.cybox.core.RelatedObjectType.new(:idref => object.id)
9
+ if relationship
10
+ if relationship.kind_of?(String)
11
+ # Ugh, why does Java throw an exception here?
12
+ begin
13
+ enum = org.mitre.cybox.vocabularies.ObjectRelationshipEnum10.from_value(relationship)
14
+ related_object.relationship = org.mitre.cybox.vocabularies.ObjectRelationshipVocab10.new(:value => enum.value)
15
+ rescue
16
+ related_object.relationship = org.mitre.cybox.common.ControlledVocabularyStringType.new(:value => relationship)
17
+ end
18
+ else
19
+ related_object.relationship = relationship
20
+ end
21
+ end
22
+
23
+ self.related_objects.add_related_object(related_object)
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,26 @@
1
+ class Java::OrgMitreCyboxCore::ObservableType
2
+ def item=(val)
3
+ if val.kind_of?(org.mitre.cybox.core.ObjectType)
4
+ self.object = val
5
+ elsif val.kind_of?(org.mitre.cybox.core.EventType)
6
+ self.event = val
7
+ elsif val.kind_of?(org.mitre.cybox.common.ObjectPropertiesType)
8
+ self.object = Java::OrgMitreCyboxCore::ObjectType.new(:properties => val)
9
+ elsif val.kind_of?(Hash) && val[:operator]
10
+ self.observable_composition = process_composition(val)
11
+ else
12
+ raise "Unknown item type: #{val.class}"
13
+ end
14
+ end
15
+
16
+ def process_composition(composition_hash)
17
+ operator = org.mitre.cybox.core.OperatorTypeEnum.from_value(composition_hash[:operator].to_s.upcase)
18
+ composition = Java::OrgMitreCyboxCore::ObservableCompositionType.new(:operator => operator)
19
+ composition_hash[:items].each do |item|
20
+ observable = item.kind_of?(org.mitre.cybox.core.ObservableType) ? item : self.class.new(:item => item)
21
+ composition.add_observable(observable)
22
+ end
23
+
24
+ return composition
25
+ end
26
+ end
@@ -0,0 +1,8 @@
1
+ class Java::OrgMitreCyboxCore::ObservablesType
2
+ include StixRuby::Marshall
3
+
4
+ def process_args(args)
5
+ args[:cybox_update_version] = "1"
6
+ args
7
+ end
8
+ end
@@ -0,0 +1,56 @@
1
+ class Java::OrgMitreStixCore::STIXType
2
+ include StixRuby::Marshall
3
+
4
+ def add_observable(observable)
5
+ self.observables ||= org.mitre.cybox.core.ObservablesType.new(:cybox_major_version => '2', :cybox_minor_version => '1')
6
+ self.observables.add_observable(observable)
7
+ end
8
+
9
+ def add_campaign(campaign)
10
+ self.campaigns ||= org.mitre.stix.core.CampaignsType.new
11
+ self.campaigns.add_campaign(campaign)
12
+ end
13
+
14
+ def add_course_of_action(coa)
15
+ self.courses_of_action ||= org.mitre.stix.core.CoursesOfActionType.new
16
+ self.courses_of_action.course_of_actions.add(coa)
17
+ end
18
+
19
+ def add_exploit_target(et)
20
+ self.exploit_targets ||= org.mitre.stix.common.ExploitTargetsType.new
21
+ self.exploit_targets.add_exploit_target(et)
22
+ end
23
+
24
+ def add_incident(incident)
25
+ self.incidents ||= org.mitre.stix.core.IncidentsType.new
26
+ self.incidents.add_incident(incident)
27
+ end
28
+
29
+ def add_indicator(indicator)
30
+ self.indicators ||= org.mitre.stix.core.IndicatorsType.new
31
+ self.indicators.add_indicator(indicator)
32
+ end
33
+
34
+ def add_threat_actor(ta)
35
+ self.threat_actors ||= org.mitre.stix.core.ThreatActorsType.new
36
+ self.threat_actors.add_threat_actor(ta)
37
+ end
38
+
39
+ def ttps
40
+ self.getTTPs
41
+ end
42
+
43
+ def ttps=(val)
44
+ self.setTTPs(val)
45
+ end
46
+
47
+ def add_ttp(ttp)
48
+ self.ttps ||= org.mitre.stix.core.TTPsType.new
49
+ self.ttps.getTTPS.add(ttp)
50
+ end
51
+
52
+ def process_args(args)
53
+ args[:version] ||= "1.0.1"
54
+ args
55
+ end
56
+ end
@@ -0,0 +1,11 @@
1
+ require 'ruby_stix'
2
+
3
+ require 'ruby_stix/marshall'
4
+
5
+ require 'ruby_stix/api/api_helper'
6
+ require 'ruby_stix/api/indicator'
7
+ require 'ruby_stix/api/observable'
8
+ require 'ruby_stix/api/object'
9
+ require 'ruby_stix/api/observables_type'
10
+ require 'ruby_stix/api/stix_type'
11
+ require 'ruby_stix/api/base_object_property_type'