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.
- checksums.yaml +4 -4
- data/.github/workflows/build.yml +4 -3
- data/Gemfile +1 -1
- data/Gemfile.lock +54 -1
- data/README.md +49 -3
- data/lib/stix2/base.rb +7 -0
- data/lib/stix2/bundle.rb +1 -2
- data/lib/stix2/common.rb +104 -22
- data/lib/stix2/confidence_scale.rb +106 -0
- data/lib/stix2/custom_object.rb +20 -0
- data/lib/stix2/cyberobservable_objects/artifact.rb +1 -1
- data/lib/stix2/cyberobservable_objects/directory.rb +1 -1
- data/lib/stix2/cyberobservable_objects/domain_name.rb +1 -1
- data/lib/stix2/cyberobservable_objects/email_message.rb +7 -7
- data/lib/stix2/cyberobservable_objects/file.rb +2 -2
- data/lib/stix2/cyberobservable_objects/ipv4_addr.rb +4 -4
- data/lib/stix2/cyberobservable_objects/ipv6_addr.rb +4 -4
- data/lib/stix2/cyberobservable_objects/network_traffic.rb +3 -3
- data/lib/stix2/cyberobservable_objects/process.rb +17 -0
- data/lib/stix2/cyberobservable_objects/software.rb +1 -1
- data/lib/stix2/cyberobservable_objects/user_account.rb +4 -4
- data/lib/stix2/cyberobservable_objects/x509_certificate.rb +4 -2
- data/lib/stix2/domain_objects/attack_pattern.rb +3 -3
- data/lib/stix2/domain_objects/campaign.rb +1 -1
- data/lib/stix2/domain_objects/grouping.rb +1 -1
- data/lib/stix2/domain_objects/identity.rb +1 -1
- data/lib/stix2/domain_objects/indicator.rb +2 -2
- data/lib/stix2/domain_objects/infrastructure.rb +3 -3
- data/lib/stix2/domain_objects/intrusion-set.rb +3 -3
- data/lib/stix2/domain_objects/malware.rb +9 -9
- data/lib/stix2/domain_objects/malware_analysis.rb +3 -3
- data/lib/stix2/domain_objects/note.rb +2 -2
- data/lib/stix2/domain_objects/observed_data.rb +1 -1
- data/lib/stix2/domain_objects/opinion.rb +2 -2
- data/lib/stix2/domain_objects/report.rb +2 -2
- data/lib/stix2/domain_objects/threat_actor.rb +6 -6
- data/lib/stix2/domain_objects/tool.rb +3 -3
- data/lib/stix2/enum.rb +81 -22
- data/lib/stix2/extension_definition.rb +10 -0
- data/lib/stix2/extensions/alternate_data_stream_type.rb +9 -0
- data/lib/stix2/extensions/archive_file.rb +8 -0
- data/lib/stix2/extensions/http_request.rb +12 -0
- data/lib/stix2/extensions/icmp.rb +8 -0
- data/lib/stix2/extensions/ntfs.rb +10 -0
- data/lib/stix2/extensions/pdf.rb +11 -0
- data/lib/stix2/extensions/raster_image.rb +10 -0
- data/lib/stix2/extensions/socket.rb +13 -0
- data/lib/stix2/extensions/tcp.rb +8 -0
- data/lib/stix2/extensions/unix_account.rb +10 -0
- data/lib/stix2/extensions/windows_pe_optional_header_type.rb +37 -0
- data/lib/stix2/extensions/windows_pe_section_type.rb +10 -0
- data/lib/stix2/extensions/windows_pebinary.rb +21 -0
- data/lib/stix2/extensions/windows_process.rb +13 -0
- data/lib/stix2/extensions/windows_service.rb +14 -0
- data/lib/stix2/external_reference.rb +2 -6
- data/lib/stix2/identifier.rb +2 -12
- data/lib/stix2/kill_chain_phase.rb +3 -7
- data/lib/stix2/languages.rb +236 -0
- data/lib/stix2/meta_objects/data_markings/base.rb +1 -4
- data/lib/stix2/meta_objects/data_markings/granular_marking.rb +2 -6
- data/lib/stix2/meta_objects/data_markings/marking_definition.rb +2 -2
- data/lib/stix2/meta_objects/data_markings/object_marking.rb +3 -13
- data/lib/stix2/meta_objects/language_content.rb +1 -1
- data/lib/stix2/ov.rb +266 -255
- data/lib/stix2/relationship_objects/relationship.rb +155 -2
- data/lib/stix2/relationship_objects/sighting.rb +3 -3
- data/lib/stix2/storage.rb +21 -15
- data/lib/stix2/version.rb +1 -1
- data/lib/stix2.rb +100 -72
- data/ruby-stix2.gemspec +25 -21
- metadata +73 -11
- data/lib/stix2/boolean.rb +0 -18
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8cd2f086caa5d9a2a071c57ae11abd650797807ca3c87c6a0db54ecc3b327f2e
|
4
|
+
data.tar.gz: 87c05b9b056e4990e24c40f78e5fb1a04e2f5acf31d9931fdd44f37cfab55a86
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ea1dd38b79bf6012ed112b9b2ab25f21f44a098f3f91de66a84af02c9eee5300f6ff7d4e4cb9a2aa0bcd1f1d844972767b139b99d11ea8cc57fb69452ebb4779
|
7
|
+
data.tar.gz: 1b711607c4544bc3e3e82ae4cc35bac57fe99f4e1a90ed2c32ed94b30a6378d602d7521ddafc3c392debab3c0e24fd89e6ec13f3b641d957a9dfae91d80240c4
|
data/.github/workflows/build.yml
CHANGED
@@ -10,11 +10,11 @@ jobs:
|
|
10
10
|
strategy:
|
11
11
|
matrix:
|
12
12
|
os: [ubuntu-latest, windows-latest]
|
13
|
-
ruby: ['
|
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@
|
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.
|
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
data/Gemfile.lock
CHANGED
@@ -1,43 +1,96 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
ruby-stix2 (0.1.
|
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
|
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 `
|
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.
|
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
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 :
|
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
|
-
|
3
|
-
|
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:
|
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
|
14
|
-
property :labels, coerce:
|
15
|
-
property :confidence, coerce:
|
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:
|
18
|
-
property :object_marking_refs, coerce:
|
19
|
-
property :granular_markings, coerce:
|
20
|
-
property :defanged, coerce: Stix2
|
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(
|
31
|
+
type = to_dash(self.class.name.split("::").last)
|
26
32
|
if options[:type]
|
27
|
-
|
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
|
-
|
44
|
+
process_extensions(options)
|
45
|
+
Stix2::Storage.add(self)
|
33
46
|
end
|
34
47
|
|
35
48
|
def method_missing(m, *args, &block)
|
36
|
-
|
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.
|
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
|
-
|
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
|
@@ -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
|
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:
|
10
|
-
property :cc_refs, coerce:
|
11
|
-
property :bcc_refs, coerce:
|
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:
|
15
|
-
property :additional_header_fields, coerce:
|
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:
|
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:
|
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
|
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:
|
8
|
-
property :resolves_to_refs, coerce:
|
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
|
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:
|
8
|
-
property :resolves_to_refs, coerce:
|
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){
|
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:
|
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:
|
19
|
+
property :encapsulates_refs, coerce: [Identifier]
|
20
20
|
property :encapsulated_by_ref, coerce: Identifier
|
21
21
|
end
|
22
22
|
end
|