ruby-stix2 0.1.0 → 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (72) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/build.yml +4 -3
  3. data/Gemfile +1 -1
  4. data/Gemfile.lock +54 -1
  5. data/README.md +49 -3
  6. data/lib/stix2/base.rb +7 -0
  7. data/lib/stix2/bundle.rb +1 -2
  8. data/lib/stix2/common.rb +104 -22
  9. data/lib/stix2/confidence_scale.rb +106 -0
  10. data/lib/stix2/custom_object.rb +20 -0
  11. data/lib/stix2/cyberobservable_objects/artifact.rb +1 -1
  12. data/lib/stix2/cyberobservable_objects/directory.rb +1 -1
  13. data/lib/stix2/cyberobservable_objects/domain_name.rb +1 -1
  14. data/lib/stix2/cyberobservable_objects/email_message.rb +7 -7
  15. data/lib/stix2/cyberobservable_objects/file.rb +2 -2
  16. data/lib/stix2/cyberobservable_objects/ipv4_addr.rb +4 -4
  17. data/lib/stix2/cyberobservable_objects/ipv6_addr.rb +4 -4
  18. data/lib/stix2/cyberobservable_objects/network_traffic.rb +3 -3
  19. data/lib/stix2/cyberobservable_objects/process.rb +17 -0
  20. data/lib/stix2/cyberobservable_objects/software.rb +1 -1
  21. data/lib/stix2/cyberobservable_objects/user_account.rb +4 -4
  22. data/lib/stix2/cyberobservable_objects/x509_certificate.rb +4 -2
  23. data/lib/stix2/domain_objects/attack_pattern.rb +3 -3
  24. data/lib/stix2/domain_objects/campaign.rb +1 -1
  25. data/lib/stix2/domain_objects/grouping.rb +1 -1
  26. data/lib/stix2/domain_objects/identity.rb +1 -1
  27. data/lib/stix2/domain_objects/indicator.rb +2 -2
  28. data/lib/stix2/domain_objects/infrastructure.rb +3 -3
  29. data/lib/stix2/domain_objects/intrusion-set.rb +3 -3
  30. data/lib/stix2/domain_objects/malware.rb +9 -9
  31. data/lib/stix2/domain_objects/malware_analysis.rb +3 -3
  32. data/lib/stix2/domain_objects/note.rb +2 -2
  33. data/lib/stix2/domain_objects/observed_data.rb +1 -1
  34. data/lib/stix2/domain_objects/opinion.rb +2 -2
  35. data/lib/stix2/domain_objects/report.rb +2 -2
  36. data/lib/stix2/domain_objects/threat_actor.rb +6 -6
  37. data/lib/stix2/domain_objects/tool.rb +3 -3
  38. data/lib/stix2/enum.rb +81 -22
  39. data/lib/stix2/extension_definition.rb +10 -0
  40. data/lib/stix2/extensions/alternate_data_stream_type.rb +9 -0
  41. data/lib/stix2/extensions/archive_file.rb +8 -0
  42. data/lib/stix2/extensions/http_request.rb +12 -0
  43. data/lib/stix2/extensions/icmp.rb +8 -0
  44. data/lib/stix2/extensions/ntfs.rb +10 -0
  45. data/lib/stix2/extensions/pdf.rb +11 -0
  46. data/lib/stix2/extensions/raster_image.rb +10 -0
  47. data/lib/stix2/extensions/socket.rb +13 -0
  48. data/lib/stix2/extensions/tcp.rb +8 -0
  49. data/lib/stix2/extensions/unix_account.rb +10 -0
  50. data/lib/stix2/extensions/windows_pe_optional_header_type.rb +37 -0
  51. data/lib/stix2/extensions/windows_pe_section_type.rb +10 -0
  52. data/lib/stix2/extensions/windows_pebinary.rb +21 -0
  53. data/lib/stix2/extensions/windows_process.rb +13 -0
  54. data/lib/stix2/extensions/windows_service.rb +14 -0
  55. data/lib/stix2/external_reference.rb +2 -6
  56. data/lib/stix2/identifier.rb +2 -12
  57. data/lib/stix2/kill_chain_phase.rb +3 -7
  58. data/lib/stix2/languages.rb +236 -0
  59. data/lib/stix2/meta_objects/data_markings/base.rb +1 -4
  60. data/lib/stix2/meta_objects/data_markings/granular_marking.rb +2 -6
  61. data/lib/stix2/meta_objects/data_markings/marking_definition.rb +2 -2
  62. data/lib/stix2/meta_objects/data_markings/object_marking.rb +3 -13
  63. data/lib/stix2/meta_objects/language_content.rb +1 -1
  64. data/lib/stix2/ov.rb +266 -255
  65. data/lib/stix2/relationship_objects/relationship.rb +155 -2
  66. data/lib/stix2/relationship_objects/sighting.rb +3 -3
  67. data/lib/stix2/storage.rb +21 -15
  68. data/lib/stix2/version.rb +1 -1
  69. data/lib/stix2.rb +100 -72
  70. data/ruby-stix2.gemspec +25 -21
  71. metadata +73 -11
  72. data/lib/stix2/boolean.rb +0 -18
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 123ddb57694307c96be2fbdf9a9d9f8c9ac55fbbbf35dc7f71c196aadad728b6
4
- data.tar.gz: fff53b71f98c23069d7c3dd0da2a4ca3799f424a162f10629029361b0624800e
3
+ metadata.gz: 8cd2f086caa5d9a2a071c57ae11abd650797807ca3c87c6a0db54ecc3b327f2e
4
+ data.tar.gz: 87c05b9b056e4990e24c40f78e5fb1a04e2f5acf31d9931fdd44f37cfab55a86
5
5
  SHA512:
6
- metadata.gz: e7aae57f5bf2b8415431df88dd2999ed85cfbf56f9f1634e750a0f00a53375c6dff96060ae96a4052eb2efca4471d38ac8244f1824f7f8f03df18ac883430517
7
- data.tar.gz: 63a3575a2886265784846dccb94fe8e32f300ca4c3c8006311f27acb19c0dc3a87c792b2aa4530c73b5ce5e1a2fd8064a2bcf50c051778dd743e7f4fa6831d2a
6
+ metadata.gz: ea1dd38b79bf6012ed112b9b2ab25f21f44a098f3f91de66a84af02c9eee5300f6ff7d4e4cb9a2aa0bcd1f1d844972767b139b99d11ea8cc57fb69452ebb4779
7
+ data.tar.gz: 1b711607c4544bc3e3e82ae4cc35bac57fe99f4e1a90ed2c32ed94b30a6378d602d7521ddafc3c392debab3c0e24fd89e6ec13f3b641d957a9dfae91d80240c4
@@ -10,11 +10,11 @@ jobs:
10
10
  strategy:
11
11
  matrix:
12
12
  os: [ubuntu-latest, windows-latest]
13
- ruby: ['2.7', '3.0', '3.1', head]
13
+ ruby: ['3.0', '3.1', '3.2', '3.3', head]
14
14
  runs-on: ${{ matrix.os }}
15
15
  permissions: write-all
16
16
  steps:
17
- - uses: actions/checkout@v3
17
+ - uses: actions/checkout@v4
18
18
  - name: Set up Ruby
19
19
  uses: ruby/setup-ruby@v1
20
20
  with:
@@ -22,9 +22,10 @@ jobs:
22
22
  bundler: latest
23
23
  - run: bundle
24
24
  - run: bundle exec rake test
25
+ - run: bundle exec standardrb
25
26
  - name: SimpleCov Ruby ${{ matrix.ruby }}
26
27
  uses: joshmfrankel/simplecov-check-action@main
27
- if: ${{ matrix.os == 'ubuntu-latest' && matrix.ruby == '3.1' }}
28
+ if: ${{ matrix.os == 'ubuntu-latest' && matrix.ruby == '3.2' }}
28
29
  with:
29
30
  github_token: ${{ secrets.GITHUB_TOKEN }}
30
31
  check_job_name: SimpleCov ${{ matrix.ruby }}
data/Gemfile CHANGED
@@ -1,4 +1,4 @@
1
- source 'https://rubygems.org'
1
+ source "https://rubygems.org"
2
2
 
3
3
  # Specify your gem's dependencies in ruby-taxii.gemspec
4
4
  gemspec
data/Gemfile.lock CHANGED
@@ -1,43 +1,96 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- ruby-stix2 (0.1.0)
4
+ ruby-stix2 (0.1.1)
5
5
  hashie (~> 5.0.0)
6
6
 
7
7
  GEM
8
8
  remote: https://rubygems.org/
9
9
  specs:
10
+ ast (2.4.2)
10
11
  byebug (11.1.3)
11
12
  coderay (1.1.3)
12
13
  docile (1.4.0)
13
14
  hashie (5.0.0)
15
+ io-console (0.6.0)
16
+ irb (1.7.0)
17
+ reline (>= 0.3.0)
18
+ json (2.7.2)
19
+ language_server-protocol (3.17.0.3)
20
+ lint_roller (1.1.0)
14
21
  method_source (1.0.0)
15
22
  minitest (5.18.1)
23
+ mutex_m (0.2.0)
24
+ parallel (1.24.0)
25
+ parser (3.3.0.5)
26
+ ast (~> 2.4.1)
27
+ racc
16
28
  pry (0.13.1)
17
29
  coderay (~> 1.1)
18
30
  method_source (~> 1.0)
19
31
  pry-byebug (3.10.1)
20
32
  byebug (~> 11.0)
21
33
  pry (>= 0.13, < 0.15)
34
+ racc (1.7.3)
35
+ rainbow (3.1.1)
22
36
  rake (13.0.6)
37
+ regexp_parser (2.9.0)
38
+ reline (0.3.5)
39
+ io-console (~> 0.5)
40
+ rexml (3.2.6)
41
+ rubocop (1.62.1)
42
+ json (~> 2.3)
43
+ language_server-protocol (>= 3.17.0)
44
+ parallel (~> 1.10)
45
+ parser (>= 3.3.0.2)
46
+ rainbow (>= 2.2.2, < 4.0)
47
+ regexp_parser (>= 1.8, < 3.0)
48
+ rexml (>= 3.2.5, < 4.0)
49
+ rubocop-ast (>= 1.31.1, < 2.0)
50
+ ruby-progressbar (~> 1.7)
51
+ unicode-display_width (>= 2.4.0, < 3.0)
52
+ rubocop-ast (1.31.2)
53
+ parser (>= 3.3.0.4)
54
+ rubocop-performance (1.20.2)
55
+ rubocop (>= 1.48.1, < 2.0)
56
+ rubocop-ast (>= 1.30.0, < 2.0)
57
+ ruby-progressbar (1.13.0)
23
58
  simplecov (0.22.0)
24
59
  docile (~> 1.1)
25
60
  simplecov-html (~> 0.11)
26
61
  simplecov_json_formatter (~> 0.1)
27
62
  simplecov-html (0.12.3)
28
63
  simplecov_json_formatter (0.1.4)
64
+ standard (1.35.1)
65
+ language_server-protocol (~> 3.17.0.2)
66
+ lint_roller (~> 1.0)
67
+ rubocop (~> 1.62.0)
68
+ standard-custom (~> 1.0.0)
69
+ standard-performance (~> 1.3)
70
+ standard-custom (1.0.2)
71
+ lint_roller (~> 1.0)
72
+ rubocop (~> 1.50)
73
+ standard-performance (1.3.1)
74
+ lint_roller (~> 1.1)
75
+ rubocop-performance (~> 1.20.2)
76
+ standardrb (1.0.1)
77
+ standard
78
+ unicode-display_width (2.5.0)
29
79
 
30
80
  PLATFORMS
31
81
  x86_64-linux
32
82
 
33
83
  DEPENDENCIES
34
84
  bundler (~> 2.3)
85
+ irb (~> 1.7.0)
35
86
  minitest (~> 5.18.1)
87
+ mutex_m
36
88
  pry (~> 0.13.0)
37
89
  pry-byebug (~> 3.10.1)
38
90
  rake (~> 13.0)
39
91
  ruby-stix2!
40
92
  simplecov (~> 0.22.0)
93
+ standardrb
41
94
 
42
95
  BUNDLED WITH
43
96
  2.3.26
data/README.md CHANGED
@@ -12,7 +12,7 @@ gem install ruby-stix2
12
12
  or as part of the bundle
13
13
 
14
14
  ```
15
- bundle add typhoeus
15
+ bundle add ruby-stix2
16
16
  ```
17
17
 
18
18
  # Usage
@@ -69,7 +69,7 @@ Stix2 message.
69
69
  # Storage
70
70
 
71
71
  The Stix2 standard has several object types, some of which are containers of other objects (like `Bundle`). However we
72
- may want to save and retrieve Stix2 objects in a fast way. The gem provides a `storage` support for that.
72
+ may want to save and retrieve Stix2 objects in a fast way. The gem provides a `Stix::Storage` support for that.
73
73
 
74
74
  For any Stix2 attribute that is an `identifier` (`Stix2::Identifier` in the gem) the class gives one more method called
75
75
  `_instance` to retrieve the actual instance. If we have a `threat-actor` like this
@@ -98,7 +98,7 @@ we know that this object has been created by an identity `identity--f431f809-377
98
98
  retrieve the other object if already seen
99
99
 
100
100
  ```ruby
101
- Stix2.storage_activate # Activate the storage
101
+ Stix2::Storage.activate # Activate the storage
102
102
 
103
103
  identity = Stix2::DomainObject::Identity.new(id: 'identity--f431f809-377b-45e0-aa1c-6a4751cae5ff', ...)
104
104
  threat_actor = Stix2::DomainObject::ThreatActor.new(created_by_ref: 'identity--f431f809-377b-45e0-aa1c-6a4751cae5ff', ...)
@@ -107,6 +107,48 @@ threat_actor.created_by_ref # this gives the identifier => identity--f431f809-37
107
107
  threat_actor.created_by_ref_instance # this gives the actual object => Stix2::DomainObject::Identity
108
108
  ```
109
109
 
110
+ # Spec versions
111
+
112
+ This gem implements the spec version `2.1`. However older version (especially 2.0) can be compatible. To force the gem
113
+ to accept another spec version, just add them to the `SPEC_VERSIONS` variable.
114
+
115
+ ```ruby
116
+ Stix2::SPEC_VERSIONS << '2.0'
117
+ ```
118
+
119
+ # Custom Definitions
120
+
121
+ The Stix2 standard includes several extensions to base objects. All extensions are widely described in the standard
122
+ itself, please refer to it. However two of them need more attention for how they are implemented in this gem.
123
+
124
+ ## Extension Definition
125
+
126
+ This object allows the definition of new properties. One special property is `toplevel-property-extension` that allows
127
+ the definition of properties on the top-level of an object. According to the standard thos properties should be
128
+ defined solely on the object that is actually using the property. However due to the `hashie`-based implementation
129
+ the gem declares the new properties on the class itself. This is a known limitation of the current implementation and
130
+ it may be fixed in the future.
131
+
132
+ ## Custom Object
133
+
134
+ A `CustomObject` can also be used within the Gem. The standard defines the rules that must be fulfilled for a custom
135
+ object. Since those rules are several, the code stacks all the errors altogether and raises an exception when some
136
+ errors happen. The exception is RuntimeError, that gives the user a string. It is suboptimal to have multiple errors
137
+ in a string, and it may be fixed in the future.
138
+
139
+ # Confidence
140
+
141
+ A Stix2 object can have the property `confidence` set. This value can be expressed according to several conficence
142
+ scales. To make this conversion smooth, an object offers the method `confidence_scale` that is an instance of
143
+ `Stix2::ConfidenceScale`. This class offers method for all the scales the standard includes.
144
+
145
+ ```ruby
146
+ indicator = Stix2::DomainObject::Indicator.new(confidence: i)
147
+ indicator.confidence # This is the raw integer
148
+ indicator.confidence_scale.to_admiralty_credibility # this is a string in this scale
149
+ indicator.confidence_scale.to_admiralty_credibility_stix # this is a string in stix mode
150
+ ```
151
+
110
152
  # Contribution
111
153
 
112
154
  You can contribute to this project in 2 ways:
@@ -114,3 +156,7 @@ You can contribute to this project in 2 ways:
114
156
  - with a PR: just follow the standard github workflow
115
157
  - by pointing out missing support: open an issue and please provide a json containing the missing support, to simplify
116
158
  the development
159
+
160
+ # See also
161
+
162
+ Ruby Stix2: https://github.com/crondaemon/ruby-taxii2
data/lib/stix2/base.rb ADDED
@@ -0,0 +1,7 @@
1
+ module Stix2
2
+ class Base < Hashie::Dash
3
+ include Hashie::Extensions::Dash::PredefinedValues
4
+ include Hashie::Extensions::IndifferentAccess
5
+ include Hashie::Extensions::Dash::Coercion
6
+ end
7
+ end
data/lib/stix2/bundle.rb CHANGED
@@ -1,7 +1,6 @@
1
1
  module Stix2
2
2
  class Bundle < Stix2::Common
3
3
  property :type, required: true, coerce: String
4
- property :id, coerce: String
5
- property :objects, coerce: ->(values){ values.map{ Stix2.parse(_1) } }
4
+ property :objects, coerce: ->(array) { array.all? { |element| element.is_a?(::Stix2::Common) || raise("Invalid Object") } && array }
6
5
  end
7
6
  end
data/lib/stix2/common.rb CHANGED
@@ -1,44 +1,70 @@
1
+ require "securerandom"
2
+
1
3
  module Stix2
2
- class Common < Hashie::Dash
3
- include Hashie::Extensions::Dash::PredefinedValues
4
- include Hashie::Extensions::IndifferentAccess
5
- include Hashie::Extensions::Dash::Coercion
4
+ SPEC_VERSIONS = ["2.1"]
5
+ UUID_NAMESPACE = "00abedb4-aa42-466c-9c01-fed23315a9b7"
6
6
 
7
+ class Common < Stix2::Base
8
+ include Hashie::Extensions::Dash::PropertyTranslation
7
9
  property :type, required: true, coerce: String
8
- property :spec_version, coerce: String, values: ['2.1']
9
- property :id, coerce: Identifier
10
+ property :spec_version, coerce: String, values: Stix2::SPEC_VERSIONS, default: SPEC_VERSIONS.last
11
+ property :id, coerce: Identifier, required: true
10
12
  property :created_by_ref, coerce: Identifier
11
13
  property :created, coerce: Time
12
14
  property :modified, coerce: Time
13
- property :revoked, coerce: Stix2::Boolean
14
- property :labels, coerce: Array[String]
15
- property :confidence, coerce: Integer
15
+ property :revoked, coerce: ->(value) { Stix2.to_bool(value) }
16
+ property :labels, coerce: [String]
17
+ property :confidence, coerce: ->(value) {
18
+ int = Integer(value)
19
+ [0..100].include?(int)
20
+ int
21
+ }
16
22
  property :lang, coerce: String
17
- property :external_references, coerce: Array[ExternalReference]
18
- property :object_marking_refs, coerce: Array[Stix2::MetaObject::DataMarking::ObjectMarking]
19
- property :granular_markings, coerce: Array[MetaObject::DataMarking::GranularMarking]
20
- property :defanged, coerce: Stix2::Boolean
23
+ property :external_references, coerce: [ExternalReference]
24
+ property :object_marking_refs, coerce: [Stix2::MetaObject::DataMarking::ObjectMarking]
25
+ property :granular_markings, coerce: [MetaObject::DataMarking::GranularMarking]
26
+ property :defanged, coerce: ->(value) { Stix2.to_bool(value) }
21
27
  property :extensions, coerce: Hash
22
28
 
23
29
  def initialize(options = {})
24
30
  Hashie.symbolize_keys!(options)
25
- type = to_dash(self.class.name.split('::').last)
31
+ type = to_dash(self.class.name.split("::").last)
26
32
  if options[:type]
27
- raise("Property 'type' must be '#{type}'") if options[:type] != type
33
+ if !options[:type].start_with?("x-") && options[:type] != type
34
+ raise("Property 'type' must be '#{type}'")
35
+ end
28
36
  else
29
37
  options[:type] = type
30
38
  end
39
+
40
+ options[:id] ||= "#{type}--#{SecureRandom.uuid}"
41
+
42
+ process_toplevel_property_extension(options[:extensions])
31
43
  super(options)
32
- Stix2.storage_add(self)
44
+ process_extensions(options)
45
+ Stix2::Storage.add(self)
33
46
  end
34
47
 
35
48
  def method_missing(m, *args, &block)
36
- super(m, args, block) if !m.to_s.end_with?('_instance')
49
+ if !m.to_s.end_with?("_instance")
50
+ # :nocov:
51
+ super(m, args, block)
52
+ return
53
+ # :nocov:
54
+ end
37
55
  # Retrieve the original method
38
- ref_method = m.to_s.gsub(/_instance$/, '')
56
+ ref_method = m.to_s.gsub(/_instance$/, "")
39
57
  obj = send(ref_method)
40
58
  raise("Can't get a Stix2::Identifier from #{ref_method}") if !obj.is_a?(Stix2::Identifier)
41
- Stix2.storage_find(obj)
59
+ Stix2::Storage.find(obj)
60
+ end
61
+
62
+ def respond_to_missing?(method_name, include_private = false)
63
+ method_name.to_s.start_with?("_instance") || super
64
+ end
65
+
66
+ def confidence_scale
67
+ Stix2::ConfidenceScale.new(confidence)
42
68
  end
43
69
 
44
70
  private
@@ -48,15 +74,71 @@ module Stix2
48
74
  end
49
75
 
50
76
  def self.validate_array(list, valid_values)
51
- excess = (Array(list) - valid_values)
77
+ excess = (Array(list).map(&:to_s) - valid_values.map(&:to_s))
52
78
  excess.empty? || raise("Invalid values: #{excess}")
53
79
  list
54
80
  end
81
+ private_class_method :validate_array
55
82
 
56
83
  def self.hash_dict(hsh)
57
- invalids = hsh.keys.map(&:to_s) - HASH_ALGORITHM_OV
58
- invalids.empty? || raise("Invalid values: #{invalids}")
84
+ validate_array(hsh.keys, HASH_ALGORITHM_OV)
59
85
  hsh
60
86
  end
87
+ private_class_method :hash_dict
88
+
89
+ def process_toplevel_property_extension(extensions)
90
+ extension_definition = extensions&.find { |key, val| key.to_s.start_with?("extension-definition") }
91
+ return if !extension_definition
92
+
93
+ id = extension_definition.first
94
+ type = extension_definition.last[:extension_type]
95
+ if type == "toplevel-property-extension"
96
+ Stix2::Storage.active? || raise("Stix.storage must be active to use toplevel-property-extension")
97
+ ext = Stix2::Storage.find(id)
98
+ ext.extension_properties.each do |prop|
99
+ self.class.class_eval do
100
+ property prop
101
+ end
102
+ end
103
+ end
104
+ end
105
+
106
+ def process_extensions(options)
107
+ options[:extensions]&.each do |id, value|
108
+ case id.to_s
109
+ when /[A-Z]/
110
+ raise("Invalid extension name format.")
111
+ when "archive-ext"
112
+ extensions[id] = Stix2::Extensions::ArchiveFile.new(value)
113
+ when /^extension-definition/
114
+ # Ignore it, already processes
115
+ when "socket-ext"
116
+ extensions[id] = Stix2::Extensions::Socket.new(value)
117
+ when "icmp-ext"
118
+ extensions[id] = Stix2::Extensions::Icmp.new(value)
119
+ when "http-request-ext"
120
+ extensions[id] = Stix2::Extensions::HttpRequest.new(value)
121
+ when "ntfs-ext"
122
+ extensions[id] = Stix2::Extensions::Ntfs.new(value)
123
+ when "tcp-ext"
124
+ extensions[id] = Stix2::Extensions::Tcp.new(value)
125
+ when "windows-process-ext"
126
+ extensions[id] = Stix2::Extensions::WindowsProcess.new(value)
127
+ when "windows-service-ext"
128
+ extensions[id] = Stix2::Extensions::WindowsService.new(value)
129
+ when "unix-account-ext"
130
+ extensions[id] = Stix2::Extensions::UnixAccount.new(value)
131
+ when "pdf-ext"
132
+ extensions[id] = Stix2::Extensions::Pdf.new(value)
133
+ when "raster-image-ext"
134
+ extensions[id] = Stix2::Extensions::RasterImage.new(value)
135
+ when "windows-pebinary-ext"
136
+ extensions[id] = Stix2::Extensions::WindowsPebinary.new(value)
137
+ else
138
+ # Ensure we have a hash
139
+ value.is_a?(Hash) || raise("Custom extension must be Hash: #{value}")
140
+ end
141
+ end
142
+ end
61
143
  end
62
144
  end
@@ -0,0 +1,106 @@
1
+ module Stix2
2
+ class ConfidenceScale
3
+ SCALE_NONE_LOW_MED_HIGH = {
4
+ 0..0 => {scale: "None", stix: 0},
5
+ 1..29 => {scale: "Low", stix: 15},
6
+ 30..69 => {scale: "Med", stix: 50},
7
+ 70..100 => {scale: "High", stix: 85}
8
+ }.freeze
9
+
10
+ SCALE_0_10 = {
11
+ 0..4 => {scale: 0, stix: 0},
12
+ 5..14 => {scale: 1, stix: 10},
13
+ 15..24 => {scale: 2, stix: 20},
14
+ 25..34 => {scale: 3, stix: 30},
15
+ 35..44 => {scale: 4, stix: 40},
16
+ 45..54 => {scale: 5, stix: 50},
17
+ 55..64 => {scale: 6, stix: 60},
18
+ 65..74 => {scale: 7, stix: 70},
19
+ 75..84 => {scale: 8, stix: 80},
20
+ 85..94 => {scale: 9, stix: 90},
21
+ 95..100 => {scale: 10, stix: 100}
22
+ }.freeze
23
+
24
+ SCALE_ADMIRALTY_CREDIBILITY = {
25
+ 0..19 => {scale: 5, stix: 10},
26
+ 20..39 => {scale: 4, stix: 30},
27
+ 40..59 => {scale: 3, stix: 50},
28
+ 60..79 => {scale: 2, stix: 70},
29
+ 80..100 => {scale: 1, stix: 90}
30
+ }.freeze
31
+
32
+ SCALE_WEP = {
33
+ 0..0 => {scale: "Impossible", stix: 0},
34
+ 1..19 => {scale: "Highly Unlikely/Almost Certainly Not", stix: 10},
35
+ 20..39 => {scale: "Unlikely/Probably Not", stix: 30},
36
+ 40..59 => {scale: "Even Chance", stix: 50},
37
+ 60..79 => {scale: "Likely/Probable", stix: 70},
38
+ 80..99 => {scale: "Highly likely/Almost Certain", stix: 90},
39
+ 100..100 => {scale: "Certain", stix: 100}
40
+ }.freeze
41
+
42
+ SCALE_DNI = {
43
+ 0..9 => {scale: "Almost No Chance / Remote", stix: 5},
44
+ 10..19 => {scale: "Very Unlikely / Highly Improbable", stix: 15},
45
+ 20..39 => {scale: "Unlikely / Improbable", stix: 30},
46
+ 40..59 => {scale: "Roughly Even Chance / Roughly Even Odds", stix: 50},
47
+ 60..79 => {scale: "Likely / Probable", stix: 70},
48
+ 80..89 => {scale: "Very Likely / Highly Probable", stix: 85},
49
+ 90..100 => {scale: "Almost Certain / Nearly Certain", stix: 95}
50
+ }.freeze
51
+
52
+ def initialize(value = nil)
53
+ @value = value
54
+ end
55
+
56
+ def to_none_low_med_high
57
+ !@value && "Not Specified"
58
+ find_range(SCALE_NONE_LOW_MED_HIGH, :scale)
59
+ end
60
+
61
+ def to_none_low_med_high_stix
62
+ !@value && "Not Specified"
63
+ find_range(SCALE_NONE_LOW_MED_HIGH, :stix)
64
+ end
65
+
66
+ def to_0_10
67
+ !@value && 6
68
+ find_range(SCALE_0_10, :scale)
69
+ end
70
+
71
+ def to_0_10_stix
72
+ find_range(SCALE_0_10, :stix)
73
+ end
74
+
75
+ def to_admiralty_credibility
76
+ find_range(SCALE_ADMIRALTY_CREDIBILITY, :scale)
77
+ end
78
+
79
+ def to_admiralty_credibility_stix
80
+ find_range(SCALE_ADMIRALTY_CREDIBILITY, :stix)
81
+ end
82
+
83
+ def to_wep
84
+ find_range(SCALE_WEP, :scale)
85
+ end
86
+
87
+ def to_wep_stix
88
+ find_range(SCALE_WEP, :stix)
89
+ end
90
+
91
+ def to_dni_scale
92
+ find_range(SCALE_DNI, :scale)
93
+ end
94
+
95
+ def to_dni_scale_stix
96
+ find_range(SCALE_DNI, :stix)
97
+ end
98
+
99
+ private
100
+
101
+ def find_range(constant, type)
102
+ !@value || "Not Specified"
103
+ constant.find { |k, v| k.cover?(@value) }.last[type]
104
+ end
105
+ end
106
+ end
@@ -0,0 +1,20 @@
1
+ module Stix2
2
+ class CustomObject < Stix2::Common
3
+ include Hashie::Extensions::IgnoreUndeclared
4
+
5
+ property :id, coerce: Identifier
6
+
7
+ def initialize(options)
8
+ Hashie.symbolize_keys!(options)
9
+ raise("A CustomObject must have at least one property") if options[:type] && options.count == 1
10
+ errors = Hash.new { |k, v| k[v] = [] }
11
+ options.each do |key, value|
12
+ errors["Too short"] << key if key != :id && key.size < 3
13
+ errors["Invalid name"] << key if !key.match?(/^[a-z0-9_]*$/)
14
+ errors["Too long"] << key if key.size > 250
15
+ end
16
+ raise("Error creating CustomObject: #{errors}") if !errors.empty?
17
+ super(options)
18
+ end
19
+ end
20
+ end
@@ -4,7 +4,7 @@ module Stix2
4
4
  property :mime_type, coerce: String
5
5
  property :payload_bin, coerce: String
6
6
  property :url, coerce: String
7
- property :hashes, coerce: ->(hsh){ hash_dict(hsh) }
7
+ property :hashes, coerce: ->(hsh) { hash_dict(hsh) }
8
8
  property :encryption_algorithm, values: ENCRYPTION_ALGORITHM_ENUM
9
9
  property :decryption_key, coerce: String
10
10
  end
@@ -6,7 +6,7 @@ module Stix2
6
6
  property :ctime, coerce: Time
7
7
  property :mtime, coerce: Time
8
8
  property :atime, coerce: Time
9
- property :contains_refs, coerce: Array[Identifier]
9
+ property :contains_refs, coerce: [Identifier]
10
10
  end
11
11
  end
12
12
  end
@@ -2,7 +2,7 @@ module Stix2
2
2
  module CyberobservableObject
3
3
  class DomainName < Base
4
4
  property :value, required: true, coerce: String
5
- property :resolves_to_refs, coerce: Array[Identifier]
5
+ property :resolves_to_refs, coerce: [Identifier]
6
6
  end
7
7
  end
8
8
  end
@@ -1,20 +1,20 @@
1
1
  module Stix2
2
2
  module CyberobservableObject
3
3
  class EmailMessage < Base
4
- property :is_multipart, required: true, coerce: Stix2::Boolean
4
+ property :is_multipart, required: true, coerce: ->(value) { Stix2.to_bool(value) }
5
5
  property :date, coerce: Time
6
6
  property :content_type, coerce: String
7
7
  property :from_ref, coerce: Identifier
8
8
  property :sender_ref, coerce: Identifier
9
- property :to_refs, coerce: Array[Identifier]
10
- property :cc_refs, coerce: Array[Identifier]
11
- property :bcc_refs, coerce: Array[Identifier]
9
+ property :to_refs, coerce: [Identifier]
10
+ property :cc_refs, coerce: [Identifier]
11
+ property :bcc_refs, coerce: [Identifier]
12
12
  property :message_id, coerce: String
13
13
  property :subject, coerce: String
14
- property :received_lines, coerce: Array[String]
15
- property :additional_header_fields, coerce: Hash[String => String]
14
+ property :received_lines, coerce: [String]
15
+ property :additional_header_fields, coerce: {String => String}
16
16
  property :body, coerce: String
17
- property :body_multipart, coerce: Array[EmailMimePartType]
17
+ property :body_multipart, coerce: [EmailMimePartType]
18
18
  property :raw_email_ref, coerce: Identifier
19
19
  end
20
20
  end
@@ -1,7 +1,7 @@
1
1
  module Stix2
2
2
  module CyberobservableObject
3
3
  class File < Base
4
- property :hashes, coerce: ->(hsh){ hash_dict(hsh) }
4
+ property :hashes, coerce: ->(hsh) { hash_dict(hsh) }
5
5
  property :size, coerce: Integer
6
6
  property :name, coerce: String
7
7
  property :name_enc, coerce: String
@@ -11,7 +11,7 @@ module Stix2
11
11
  property :mtime, coerce: String
12
12
  property :atime, coerce: String
13
13
  property :parent_directory_ref, coerce: Identifier
14
- property :contains_refs, coerce: Array[Identifier]
14
+ property :contains_refs, coerce: [Identifier]
15
15
  property :content_ref, coerce: Identifier
16
16
  end
17
17
  end
@@ -1,11 +1,11 @@
1
- require 'ipaddr'
1
+ require "ipaddr"
2
2
 
3
3
  module Stix2
4
4
  module CyberobservableObject
5
5
  class Ipv4Addr < Base
6
- property :value, required: true, coerce: ->(v){ IPAddr.new(v, Socket::AF_INET).to_s }
7
- property :resolves_to_refs, coerce: Array[Identifier]
8
- property :resolves_to_refs, coerce: Array[Identifier]
6
+ property :value, required: true, coerce: ->(v) { IPAddr.new(v, Socket::AF_INET).to_s }
7
+ property :resolves_to_refs, coerce: [Identifier]
8
+ property :resolves_to_refs, coerce: [Identifier]
9
9
  end
10
10
  end
11
11
  end
@@ -1,11 +1,11 @@
1
- require 'ipaddr'
1
+ require "ipaddr"
2
2
 
3
3
  module Stix2
4
4
  module CyberobservableObject
5
5
  class Ipv6Addr < Base
6
- property :value, required: true, coerce: ->(v){ IPAddr.new(v, Socket::AF_INET6).to_s }
7
- property :resolves_to_refs, coerce: Array[Identifier]
8
- property :resolves_to_refs, coerce: Array[Identifier]
6
+ property :value, required: true, coerce: ->(v) { IPAddr.new(v, Socket::AF_INET6).to_s }
7
+ property :resolves_to_refs, coerce: [Identifier]
8
+ property :resolves_to_refs, coerce: [Identifier]
9
9
  end
10
10
  end
11
11
  end
@@ -3,12 +3,12 @@ module Stix2
3
3
  class NetworkTraffic < Base
4
4
  property :start, coerce: Time
5
5
  property :end, coerce: Time
6
- property :is_active, coerce: ->(v){ boolean(v) }
6
+ property :is_active, coerce: ->(v) { Stix2.to_bool(v) }
7
7
  property :src_ref, coerce: Identifier
8
8
  property :dst_ref, coerce: Identifier
9
9
  property :src_port, coerce: Integer
10
10
  property :dst_port, coerce: Integer
11
- property :protocols, required: true, coerce: Array[String]
11
+ property :protocols, required: true, coerce: [String]
12
12
  property :src_byte_count, coerce: Integer
13
13
  property :dst_byte_count, coerce: Integer
14
14
  property :src_packets, coerce: Integer
@@ -16,7 +16,7 @@ module Stix2
16
16
  property :ipfix, coerce: Hash
17
17
  property :src_payload_ref, coerce: Identifier
18
18
  property :dst_payload_ref, coerce: Identifier
19
- property :encapsulates_refs, coerce: Array[Identifier]
19
+ property :encapsulates_refs, coerce: [Identifier]
20
20
  property :encapsulated_by_ref, coerce: Identifier
21
21
  end
22
22
  end