opentpx 2.2.0.17
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/LICENSE.txt +15 -0
- data/README.md +44 -0
- data/bin/opentpx_tools +15 -0
- data/lib/tpx.rb +7 -0
- data/lib/tpx/2_2/attribute_accessors.rb +34 -0
- data/lib/tpx/2_2/classification_element.rb +11 -0
- data/lib/tpx/2_2/classification_element_list.rb +11 -0
- data/lib/tpx/2_2/collection.rb +21 -0
- data/lib/tpx/2_2/collection_element.rb +13 -0
- data/lib/tpx/2_2/data_model.rb +32 -0
- data/lib/tpx/2_2/element_observable.rb +41 -0
- data/lib/tpx/2_2/element_observable_list.rb +17 -0
- data/lib/tpx/2_2/exceptions.rb +13 -0
- data/lib/tpx/2_2/exchange.rb +220 -0
- data/lib/tpx/2_2/heterogeneous_list.rb +136 -0
- data/lib/tpx/2_2/homogeneous_list.rb +82 -0
- data/lib/tpx/2_2/mandatory_attributes.rb +69 -0
- data/lib/tpx/2_2/merging_heterogeneous_list.rb +36 -0
- data/lib/tpx/2_2/merging_homogeneous_list.rb +37 -0
- data/lib/tpx/2_2/network.rb +23 -0
- data/lib/tpx/2_2/network_list.rb +11 -0
- data/lib/tpx/2_2/observable.rb +13 -0
- data/lib/tpx/2_2/observable_attribute_map.rb +12 -0
- data/lib/tpx/2_2/observable_definition.rb +15 -0
- data/lib/tpx/2_2/observable_dictionary.rb +12 -0
- data/lib/tpx/2_2/schema/tpx.2.2.schema.json +632 -0
- data/lib/tpx/2_2/threat_observable.rb +14 -0
- data/lib/tpx/2_2/validator.rb +279 -0
- data/lib/tpx/tools.rb +81 -0
- data/lib/tpx/version.rb +3 -0
- data/lib/tpx_2_2.rb +14 -0
- metadata +218 -0
@@ -0,0 +1,136 @@
|
|
1
|
+
require 'tpx/2_2/exceptions'
|
2
|
+
|
3
|
+
module TPX_2_2
|
4
|
+
# A list (Array) whose elements are not all the same type or do not have the same id attribute.
|
5
|
+
# The HeterogenousList defines an array of [ChildClass, child_id_attribute_name] as the accepted :child_types.
|
6
|
+
class HeterogeneousList < Array
|
7
|
+
include Enumerable
|
8
|
+
|
9
|
+
class << self
|
10
|
+
# The types of objects and their id attribute names accepted by the HeterogeneousList.
|
11
|
+
# An Array of Arrays like [[ChildClass, child_id_attribute], [AnotherChildClass, another_child_id_attribute]].
|
12
|
+
attr_accessor :child_types
|
13
|
+
|
14
|
+
# Defines the child class types that the list accepts. Another accessor for `child_types`.
|
15
|
+
#
|
16
|
+
# @param [Array] child_class_id_array The list of [Class, id_atttribute_name] to accept as list members.
|
17
|
+
def children_of_class_and_id(child_class_id_array)
|
18
|
+
self.child_types = child_class_id_array
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
# Hash lookup accessor to list members.
|
23
|
+
# Hash of hashes like child_id_of[ChildClass][child_id_val] = child
|
24
|
+
attr_accessor :child_id_of
|
25
|
+
|
26
|
+
# Initialize a new HeterogeneousList from an Array.
|
27
|
+
#
|
28
|
+
# @param [Array] input_array The Array of HeterogeneousList.child_types included classes (or Hash to be initialized as HeterogeneousList.child_type classes) to create the HeterogeneousList object from.
|
29
|
+
def initialize(input_array)
|
30
|
+
unless input_array.is_a? Array
|
31
|
+
raise ValidationError, "Supplied parameter (#{input_array.inspect}) to #{self.class}#initialize should be of type Array!"
|
32
|
+
end
|
33
|
+
|
34
|
+
@child_id_of = {}
|
35
|
+
validated_array = []
|
36
|
+
|
37
|
+
input_array.each_with_index do |child, i|
|
38
|
+
validate_expected_child_type(child, i)
|
39
|
+
new_initialized_child = nil
|
40
|
+
new_initialized_child_type = nil
|
41
|
+
|
42
|
+
self.class.child_types.each do |child_type|
|
43
|
+
if child.class == child_type[0] && child.has_key?(child_type[1])
|
44
|
+
new_initialized_child = child
|
45
|
+
new_initialized_child_type = child_type
|
46
|
+
break
|
47
|
+
elsif child.has_key?(child_type[1])
|
48
|
+
new_initialized_child = child_type[0].new(child)
|
49
|
+
new_initialized_child_type = child_type
|
50
|
+
break
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
if new_initialized_child
|
55
|
+
validate_unique(new_initialized_child, new_initialized_child_type)
|
56
|
+
child_id_of[new_initialized_child_type[0]] ||= {}
|
57
|
+
child_id_of[new_initialized_child_type[0]][new_initialized_child[new_initialized_child_type[1]]] = new_initialized_child
|
58
|
+
validated_array << new_initialized_child
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
super validated_array
|
63
|
+
end
|
64
|
+
|
65
|
+
# Add a new child to the HeterogeneousList (push).
|
66
|
+
#
|
67
|
+
# @param [Hash or HeterogeneousList.type] child The object to add to the HeterogeneousList.
|
68
|
+
def <<(child)
|
69
|
+
child_type = validate_expected_child_type(child)
|
70
|
+
validate_unique(child, child_type)
|
71
|
+
child_id_of[child_type[0]] ||= {}
|
72
|
+
child_id_of[child_type[0]][child[child_type[1]]] = child
|
73
|
+
super child
|
74
|
+
end
|
75
|
+
|
76
|
+
# Validate that a child is of the proper type to add to the HeterogeneousList.
|
77
|
+
#
|
78
|
+
# @param [Hash or one of HeterogeneousList.child_types] child The object to verify is one of HeterogeneousList.child_types.
|
79
|
+
# @param [Integer] i The position, if any, in the list of the object to verify is one of HeterogeneousList.child_types.
|
80
|
+
def validate_expected_child_type(child, i=nil)
|
81
|
+
if child.class == Hash || child.class == HashWithIndifferentAccess
|
82
|
+
validate_expected_child_type_from_hash(child, i)
|
83
|
+
else
|
84
|
+
validate_expected_child_type_from_initialized_object(child, i)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
# Validate that a child is of the proper type to add to the HeterogeneousList.
|
89
|
+
#
|
90
|
+
# @param [Hash] child The object to verify is one of HeterogeneousList.child_types.
|
91
|
+
# @param [Integer] i The position, if any, in the list of the object to verify is one of HeterogeneousList.child_types.
|
92
|
+
def validate_expected_child_type_from_hash(child, i=nil)
|
93
|
+
child_type_key_count = 0
|
94
|
+
child_type_found = nil
|
95
|
+
self.class.child_types.each do |child_type|
|
96
|
+
if child.has_key?(child_type[1])
|
97
|
+
child_type_key_count += 1
|
98
|
+
child_type_found = child_type
|
99
|
+
end
|
100
|
+
end
|
101
|
+
if child_type_key_count > 1
|
102
|
+
raise ValidationError, "Supplied input object #{child.inspect}#{pos_msg} has multiple subject types in #{self.class}!"
|
103
|
+
elsif child_type_key_count == 0
|
104
|
+
raise ValidationError, "Supplied input object #{child.inspect}#{pos_msg(i)} not one of required types #{self.class.child_types} in #{self.class}!"
|
105
|
+
end
|
106
|
+
return child_type_found
|
107
|
+
end
|
108
|
+
|
109
|
+
# Validate that a child is of the proper type to add to the HeterogeneousList.
|
110
|
+
#
|
111
|
+
# @param [one of HeterogeneousList.child_types] child The object to verify is one of HeterogeneousList.child_types.
|
112
|
+
# @param [Integer] i The position, if any, in the list of the object to verify is one of HeterogeneousList.child_types.
|
113
|
+
def validate_expected_child_type_from_initialized_object(child, i=nil)
|
114
|
+
self.class.child_types.each do |child_type|
|
115
|
+
if child.class == child_type[0] && child.has_key?(child_type[1])
|
116
|
+
return child_type
|
117
|
+
end
|
118
|
+
end
|
119
|
+
raise ValidationError, "Supplied input object #{child.inspect}#{pos_msg(i)} not one of required types #{self.class.child_types} in #{self.class}!"
|
120
|
+
end
|
121
|
+
|
122
|
+
def pos_msg(i)
|
123
|
+
pos_msg = i.nil? ? '' : " at position #{i}"
|
124
|
+
end
|
125
|
+
|
126
|
+
# Validate that a child is not contained in the list prior to adding to the HeterogeneousList.
|
127
|
+
#
|
128
|
+
# @param [Hash or HeterogeneousList.type] child The object to verify is not already contained in HeterogeneousList.
|
129
|
+
def validate_unique(child, child_type)
|
130
|
+
if child_id_of.has_key?(child_type[0]) and child_id_of[child_type[0]].has_key?(child[child_type[1]])
|
131
|
+
raise TPX_2_2::DuplicateElementInsertError, "Duplicate input object id #{child[child_type[1]]} provided to #{self.class}!"
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
end
|
136
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
require 'tpx/2_2/exceptions'
|
2
|
+
|
3
|
+
module TPX_2_2
|
4
|
+
# A list (Array) whose elements are all the same type and have the same id attribute (HomogeneousList.child_id).
|
5
|
+
class HomogeneousList < Array
|
6
|
+
include Enumerable
|
7
|
+
|
8
|
+
class << self
|
9
|
+
# The child class type accepted.
|
10
|
+
attr_accessor :type
|
11
|
+
# The id attribute name of the children objects.
|
12
|
+
attr_accessor :child_id
|
13
|
+
|
14
|
+
# Defines the child class that the list accepts.
|
15
|
+
#
|
16
|
+
# @param [Class] klass The Class to accept as list members.
|
17
|
+
def homogeneous_list_of(klass)
|
18
|
+
self.type = klass
|
19
|
+
end
|
20
|
+
|
21
|
+
# Defines the child class id attribute name.
|
22
|
+
#
|
23
|
+
# @param [Symbol] child_id The id field of list members.
|
24
|
+
def children_keyed_by(child_id)
|
25
|
+
self.child_id = child_id
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
# Hash lookup accessor to list members.
|
30
|
+
# Hash of hashes like child_id_of[ChildClass][child_id_val] = child
|
31
|
+
attr_accessor :child_id_of
|
32
|
+
|
33
|
+
# Initialize a new HomogeneousList from an Array.
|
34
|
+
#
|
35
|
+
# @param [Array] input_array The Array of HomogeneousList.type classes (or Hash to be initialized as HomogeneousList.type classes) to create the HomogeneousList object from.
|
36
|
+
def initialize(input_array)
|
37
|
+
unless input_array.is_a? Array
|
38
|
+
raise ValidationError, "Supplied parameter (#{input_array.inspect}) to #{self.class}#initialize should be of type Array!"
|
39
|
+
end
|
40
|
+
@child_id_of = {}
|
41
|
+
input_array.each_with_index do |child, i|
|
42
|
+
if child.class == Hash || child.class == HashWithIndifferentAccess
|
43
|
+
child = self.class.type.new(child)
|
44
|
+
end
|
45
|
+
validate_homogeneous_type_of(child, i)
|
46
|
+
validate_unique(child)
|
47
|
+
@child_id_of[child[self.class.child_id]] = child
|
48
|
+
end
|
49
|
+
super input_array
|
50
|
+
end
|
51
|
+
|
52
|
+
# Add a new child to the HomogeneousList (push).
|
53
|
+
#
|
54
|
+
# @param [Hash or HomogeneousList.type] child The object to add to the HomogeneousList.
|
55
|
+
def <<(child)
|
56
|
+
validate_homogeneous_type_of(child)
|
57
|
+
validate_unique(child)
|
58
|
+
@child_id_of[child[self.class.child_id]] = child
|
59
|
+
super child
|
60
|
+
end
|
61
|
+
|
62
|
+
# Validate that a child is of the proper type to add to the HomogeneousList.
|
63
|
+
#
|
64
|
+
# @param [Hash or HomogeneousList.type] child The object to verify is one of HomogeneousList.type.
|
65
|
+
# @param [Integer] i The position, if any, in the list of the object to verify is one of HomogeneousList.type.
|
66
|
+
def validate_homogeneous_type_of(child, i=nil)
|
67
|
+
unless child.is_a? self.class.type
|
68
|
+
pos_msg = i.nil? ? '' : " at position #{i}"
|
69
|
+
raise ValidationError, "Supplied input object #{child.inspect}#{pos_msg} not of required type #{self.class.type} in #{self.class}!"
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
# Validate that a child is not contained in the list prior to adding to the HomogeneousList.
|
74
|
+
#
|
75
|
+
# @param [Hash or HomogeneousList.type] child The object to verify is not already contained in HomogeneousList.
|
76
|
+
def validate_unique(child)
|
77
|
+
if @child_id_of.has_key?(child[self.class.child_id])
|
78
|
+
raise TPX_2_2::DuplicateElementInsertError, "Duplicate input object id #{child[self.class.child_id]} provided to #{self.class}!"
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
module TPX_2_2
|
2
|
+
module MandatoryAttributes
|
3
|
+
MANDATORY_ATTRIBUTES = [] # override in consumer
|
4
|
+
MUST_HAVE_ONE_OF_ATTRIBUTES = []
|
5
|
+
MUST_HAVE_ONE_AND_ONLY_ONE_OF_ATTRIBUTES = []
|
6
|
+
|
7
|
+
# Perform all validations.
|
8
|
+
def validate!
|
9
|
+
validate_mandatory_attributes!
|
10
|
+
|
11
|
+
validate_must_have_one_of_attributes!
|
12
|
+
|
13
|
+
validate_must_have_one_and_only_one_of_attributes!
|
14
|
+
end # def validate!
|
15
|
+
|
16
|
+
# Perform validation of all mandatory attributes.
|
17
|
+
def validate_mandatory_attributes!
|
18
|
+
self.class::MANDATORY_ATTRIBUTES.each do |attrib|
|
19
|
+
if self[attrib].nil?
|
20
|
+
raise ValidationError, "The mandatory attribute `#{attrib}` is missing from the supplied input_hash paramater to #{self.class}."
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
# Perform validation of all sets of attributes of which at least one are required.
|
26
|
+
def validate_must_have_one_of_attributes!
|
27
|
+
self.class::MUST_HAVE_ONE_OF_ATTRIBUTES.each do |attrib_set|
|
28
|
+
unless attrib_set.is_a? Array
|
29
|
+
raise AttributeDefinitionError, "Elements of #{self.class}::MUST_HAVE_ONE_OF_ATTRIBUTES must be of type Array."
|
30
|
+
end
|
31
|
+
|
32
|
+
has_one_of_mandatory_set = false
|
33
|
+
attrib_set.each do |attrib|
|
34
|
+
if self.has_key?(attrib) && !self[attrib].nil?
|
35
|
+
has_one_of_mandatory_set = true
|
36
|
+
break
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
unless has_one_of_mandatory_set
|
41
|
+
raise ValidationError, "A member of the mandatory attribute set `#{attrib_set}` is missing from the supplied input_hash paramater to #{self.class}."
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
# Perform validation of all sets of attributes of which only one is required.
|
47
|
+
def validate_must_have_one_and_only_one_of_attributes!
|
48
|
+
self.class::MUST_HAVE_ONE_AND_ONLY_ONE_OF_ATTRIBUTES.each do |attrib_set|
|
49
|
+
unless attrib_set.is_a? Array
|
50
|
+
raise AttributeDefinitionError, "Elements of #{self.class}::MUST_HAVE_ONE_AND_ONLY_ONE_OF_ATTRIBUTES must be of type Array."
|
51
|
+
end
|
52
|
+
|
53
|
+
count_of_mandatory_set = 0
|
54
|
+
attrib_set.each do |attrib|
|
55
|
+
if self.has_key?(attrib)
|
56
|
+
count_of_mandatory_set += 1
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
if count_of_mandatory_set == 0
|
61
|
+
raise ValidationError, "A member of the mandatory attribute set `#{attrib_set}` is missing from the supplied input_hash paramater to #{self.class}."
|
62
|
+
elsif count_of_mandatory_set > 1
|
63
|
+
raise ValidationError, "More than one member of the exclusive and mandatory attribute set `#{attrib_set}` was provided to #{self.class}."
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'tpx/2_2/heterogeneous_list'
|
2
|
+
|
3
|
+
module TPX_2_2
|
4
|
+
# A list (Array) whose elements are not all the same type or do not have the same id attribute
|
5
|
+
# and who merges certain attributes on duplicate object insert.
|
6
|
+
# The MergingHeterogenousList defines an array of [ChildClass, child_id_attribute_name] as the accepted :child_types.
|
7
|
+
class MergingHeterogeneousList < HeterogeneousList
|
8
|
+
|
9
|
+
class << self
|
10
|
+
# This list of attributes to merge on duplicate object id insert.
|
11
|
+
attr_accessor :attributes_to_merge
|
12
|
+
|
13
|
+
def on_duplicate_addition_merge_attributes(attributes)
|
14
|
+
self.attributes_to_merge = attributes
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
# Add a new child to the MergingHeterogenousList (push). Merge `attributes_to_merge` on a duplicate child id insert.
|
19
|
+
#
|
20
|
+
# @param [Hash or MergingHeterogenousList.type] child The object to add to the MergingHeterogenousList.
|
21
|
+
def <<(child)
|
22
|
+
child_type = validate_expected_child_type(child)
|
23
|
+
if child_id_of.has_key?(child_type[0]) && child_id_of[child_type[0]].has_key?(child[child_type[1]])
|
24
|
+
self.class.attributes_to_merge.each do |attribute|
|
25
|
+
child_id_of[child_type[0]][child[child_type[1]]][attribute].merge!(child[attribute])
|
26
|
+
end
|
27
|
+
else
|
28
|
+
child_id_of[child_type[0]] ||= {}
|
29
|
+
child_id_of[child_type[0]][child[child_type[1]]] = child
|
30
|
+
meth = Array.instance_method(:<<)
|
31
|
+
meth.bind(self).call(child)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module TPX_2_2
|
2
|
+
# A list (Array) whose elements are all the same type and have the same id attribute
|
3
|
+
# (MergingHomogeneousList.child_id) and who merges certain attributes on duplicate object insert.
|
4
|
+
class MergingHomogeneousList < HomogeneousList
|
5
|
+
|
6
|
+
class << self
|
7
|
+
# This list of attributes to merge on duplicate object id insert.
|
8
|
+
attr_accessor :attributes_to_merge
|
9
|
+
|
10
|
+
# Defines the child attribute names that the list will merge when an object of the same id as an existing list member is inserted.
|
11
|
+
# Another accessor for `attributes_to_merge`.
|
12
|
+
#
|
13
|
+
# @param [Array] attributes The list of attribute names to merge.
|
14
|
+
def on_duplicate_addition_merge_attributes(attributes)
|
15
|
+
self.attributes_to_merge = attributes
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
# Add a new child to the MergingHomogeneousList (push). Merge `attributes_to_merge` on a duplicate child id insert.
|
20
|
+
#
|
21
|
+
# @param [Hash or MergingHomogeneousList.type] child The object to add to the MergingHomogeneousList.
|
22
|
+
def <<(child)
|
23
|
+
unless child.is_a?(self.class.type) && child.has_key?(self.class.child_id)
|
24
|
+
raise TPX_2_2::ValidationError, "Element provided to #{self.class}#<< must be #{self.class.type} with key `#{self.class.child_id}`."
|
25
|
+
end
|
26
|
+
element = self.find {|e| e[self.class.child_id] == child[self.class.child_id] }
|
27
|
+
if element.nil?
|
28
|
+
super
|
29
|
+
else
|
30
|
+
self.class.attributes_to_merge.each do |attribute|
|
31
|
+
element[attribute].merge!(child[attribute])
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'tpx/2_2/data_model'
|
2
|
+
|
3
|
+
module TPX_2_2
|
4
|
+
|
5
|
+
# A set of defined network membership, routing topology, ownership, and network announcements.
|
6
|
+
class Network < DataModel
|
7
|
+
MANDATORY_ATTRIBUTES = [
|
8
|
+
:occurred_at_t,
|
9
|
+
:asn_number_ui # TODO: Add to specification document
|
10
|
+
]
|
11
|
+
|
12
|
+
# Overrides the default initialize to add a default occurred_at_t.
|
13
|
+
#
|
14
|
+
# @param [Hash] input_hash The input hash.
|
15
|
+
#
|
16
|
+
# @return [DataModel] The returned object.
|
17
|
+
def initialize(input_hash)
|
18
|
+
input_hash[:occurred_at_t] ||= Time.now.getutc.to_i # TODO: Should we do this here? Should we do this everywhere except ThreatObservable?
|
19
|
+
super input_hash
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'tpx/2_2/data_model'
|
2
|
+
|
3
|
+
module TPX_2_2
|
4
|
+
|
5
|
+
# A name associated with a measurement of risk including a
|
6
|
+
# description of the risk and one or more classifications
|
7
|
+
# associated with one or more network elements
|
8
|
+
class Observable < DataModel
|
9
|
+
MANDATORY_ATTRIBUTES = [
|
10
|
+
:observable_id_s
|
11
|
+
]
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'tpx/2_2/data_model'
|
2
|
+
require 'tpx/2_2/observable_attribute_map'
|
3
|
+
require 'tpx/2_2/classification_element_list'
|
4
|
+
|
5
|
+
module TPX_2_2
|
6
|
+
|
7
|
+
# The definition of an observable.
|
8
|
+
class ObservableDefinition < DataModel
|
9
|
+
MANDATORY_ATTRIBUTES = [
|
10
|
+
:observable_id_s, # TODO: Clarify handling of file hash in specification document
|
11
|
+
:description_s,
|
12
|
+
:classification_c_array,
|
13
|
+
]
|
14
|
+
end
|
15
|
+
end
|