opentpx 2.2.0.17
Sign up to get free protection for your applications and to get access to all the features.
- 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
|