ruby-stix2 0.1.0 → 0.1.2

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.
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