misp 0.1.0 → 0.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: db847f1e32b2e0a1064180b310b087ade5c9d608282bcbeebc5403f02b0cb71d
4
- data.tar.gz: e1090389330d2de0bfd8b0c58cd0768f1a9bc58bb220d80c9d74aea6cb9ec738
3
+ metadata.gz: dc2ae518cdfe5d3872000bf12bd1083a4a8540a3e6857e50573b1b8011604605
4
+ data.tar.gz: 25917d69a7a912ace45ccaa43f5bc0a5202e37e66532e1eabf30a09df0478c2e
5
5
  SHA512:
6
- metadata.gz: a4b3e71c139193309e919b6dc344f4fb200521b07a90fd3e45384d272549632af96eec9852416fdc8ddbfda31c9675f5ab107ae4cb1b2ff61b98dbeba0979457
7
- data.tar.gz: 16b98a9dae45d273b70600565f8130d43d592e4097b517f49a69177c367a8c88a5841e4eb238a9629f9a45d1ca12ce7addfb5118c27f52d45a9c4a0ae3e56f61
6
+ metadata.gz: 4691cfaffdc82bc4580e08ede833788371b0c9df3b4013bb96a777b2c8b35f92889d466274b301370daa0014a087ad0006d3f92a1de62de9cbea3f5e6e2d78e4
7
+ data.tar.gz: 2c7e5a0d2c3d5c503346d0a208f6dc9efc4b7b5b8470655e0bf29ea89ed4cbe4d8f4cf2e002738d62cb5b74dfd8b175ed99d01dfaf02fa0cc24c3e85029532c0
@@ -0,0 +1,23 @@
1
+ name: Ruby CI
2
+
3
+ on: [pull_request]
4
+
5
+ jobs:
6
+ build:
7
+ runs-on: ubuntu-latest
8
+
9
+ strategy:
10
+ fail-fast: false
11
+ matrix:
12
+ ruby: [2.7, "3.0"]
13
+
14
+ steps:
15
+ - uses: actions/checkout@v2
16
+ - name: Set up Ruby
17
+ uses: ruby/setup-ruby@v1
18
+ with:
19
+ ruby-version: ${{ matrix.ruby }}
20
+ bundler-cache: true
21
+ - name: Build and test with Rake
22
+ run: |
23
+ bundle exec rake
data/README.md CHANGED
@@ -1,5 +1,6 @@
1
1
  # misp-rb
2
2
 
3
+ [![Gem Version](https://badge.fury.io/rb/misp.svg)](https://badge.fury.io/rb/misp)
3
4
  [![Build Status](https://travis-ci.com/ninoseki/misp-rb.svg?branch=master)](https://travis-ci.com/ninoseki/misp-rb)
4
5
  [![Coverage Status](https://coveralls.io/repos/github/ninoseki/misp-rb/badge.svg?branch=master)](https://coveralls.io/github/ninoseki/misp-rb?branch=master)
5
6
  [![CodeFactor](https://www.codefactor.io/repository/github/ninoseki/misp-rb/badge)](https://www.codefactor.io/repository/github/ninoseki/misp-rb)
@@ -20,8 +21,8 @@ gem install misp
20
21
 
21
22
  By default, it tries to load configurations from environmental variables:
22
23
 
23
- - MISP API endpoint from ENV["MISP_API_ENDPOINT"]
24
- - MISP API key from ENV["MISP_API_KEY"]
24
+ - `MISP_API_ENDPOINT`: MISP API endpoint (e.g. https://misppriv.circl.lu)
25
+ - `MISP_API_KEY`: MISP API key
25
26
 
26
27
  Also, you can configure them manually.
27
28
 
@@ -60,7 +61,7 @@ event.update
60
61
  event = MISP::Event.get(17)
61
62
  event.add_attribute(value: "8.8.8.8", type: "ip-dst")
62
63
  # or
63
- attribute = MISP::Attribute(value: "1.1.1.1", type: "ip-dst")
64
+ attribute = MISP::Attribute.new(value: "1.1.1.1", type: "ip-dst")
64
65
  event.add_attribute attribute
65
66
  event.update
66
67
  ```
@@ -109,6 +110,14 @@ event.tags << MISP::Tag.new(name: "my event-level tag")
109
110
  event.create
110
111
  ```
111
112
 
113
+ ### Search for events / attributes
114
+
115
+ ```ruby
116
+ events = MISP::Event.search(info: "test")
117
+
118
+ attributes = MISP::Attribute.search(type: "ip-dst")
119
+ ```
120
+
112
121
  ## Acknowledgement
113
122
 
114
123
  The implementation design of this gem is highly influenced by [FloatingGhost/mispex](https://github.com/FloatingGhost/mispex).
@@ -2,48 +2,70 @@
2
2
 
3
3
  module MISP
4
4
  class Attribute < Base
5
+ # @return [String]
5
6
  attr_reader :id
7
+ # @return [String]
6
8
  attr_accessor :type
9
+ # @return [String]
7
10
  attr_accessor :category
11
+ # @return [Boolean]
8
12
  attr_accessor :to_ids
13
+ # @return [String]
9
14
  attr_reader :uuid
15
+ # @return [String]
10
16
  attr_reader :event_id
17
+ # @return [String]
11
18
  attr_accessor :distribution
19
+ # @return [String]
12
20
  attr_accessor :timestamp
21
+ # @return [String]
13
22
  attr_accessor :comment
23
+ # @return [String]
14
24
  attr_accessor :sharing_group_id
25
+ # @return [Boolean]
15
26
  attr_accessor :deleted
27
+ # @return [Boolean]
16
28
  attr_accessor :disable_correlation
29
+ # @return [String]
17
30
  attr_accessor :value
31
+ # @return [String]
18
32
  attr_accessor :data
19
33
 
34
+ # @return [Array<MISP::SharingGroup>]
20
35
  attr_accessor :sharing_groups
36
+ # @return [Array<MISP::Attribute>]
21
37
  attr_accessor :shadow_attributes
38
+ # @return [Array<MISP::Tag>]
22
39
  attr_accessor :tags
23
40
 
24
41
  def initialize(**attributes)
25
- attributes = normalize_attributes(attributes)
26
-
27
- @id = attributes.dig(:id)
28
- @type = attributes.dig(:type)
29
- @category = attributes.dig(:category)
30
- @to_ids = attributes.dig(:to_ids)
31
- @uuid = attributes.dig(:uuid)
32
- @event_id = attributes.dig(:event_id)
33
- @distribution = attributes.dig(:distribution)
34
- @timestamp = attributes.dig(:timestamp)
35
- @comment = attributes.dig(:comment)
36
- @sharing_group_id = attributes.dig(:sharing_group_id)
37
- @deleted = attributes.dig(:deleted)
38
- @disable_correlation = attributes.dig(:disable_correlation)
39
- @value = attributes.dig(:value)
40
- @data = attributes.dig(:data)
41
-
42
- @sharing_groups = build_plural_attribute(items: attributes.dig(:SharingGroup), klass: SharingGroup)
43
- @shadow_attributes = build_plural_attribute(items: attributes.dig(:ShadowAttribute), klass: Attribute )
44
- @tags = build_plural_attribute(items: attributes.dig(:Tag), klass: Tag)
42
+ attributes = normalize_attributes(**attributes)
43
+
44
+ @id = attributes[:id]
45
+ @type = attributes[:type]
46
+ @category = attributes[:category]
47
+ @to_ids = attributes[:to_ids]
48
+ @uuid = attributes[:uuid]
49
+ @event_id = attributes[:event_id]
50
+ @distribution = attributes[:distribution]
51
+ @timestamp = attributes[:timestamp]
52
+ @comment = attributes[:comment]
53
+ @sharing_group_id = attributes[:sharing_group_id]
54
+ @deleted = attributes[:deleted]
55
+ @disable_correlation = attributes[:disable_correlation]
56
+ @value = attributes[:value]
57
+ @data = attributes[:data]
58
+
59
+ @sharing_groups = build_plural_attribute(items: attributes[:SharingGroup], klass: SharingGroup)
60
+ @shadow_attributes = build_plural_attribute(items: attributes[:ShadowAttribute], klass: Attribute )
61
+ @tags = build_plural_attribute(items: attributes[:Tag], klass: Tag)
45
62
  end
46
63
 
64
+ #
65
+ # Returns a hash representation of the attribute data.
66
+ #
67
+ # @return [Hash]
68
+ #
47
69
  def to_h
48
70
  {
49
71
  id: id,
@@ -66,36 +88,53 @@ module MISP
66
88
  }.compact
67
89
  end
68
90
 
91
+ #
92
+ # Get an attribute
93
+ #
94
+ # @return [MISP::Attribute]
95
+ #
69
96
  def get
70
- _get("/attributes/#{id}") { |attribute| Attribute.new symbolize_keys(attribute) }
71
- end
72
-
73
- def self.get(id)
74
- new(id: id).get
97
+ _get("/attributes/#{id}") { |attribute| Attribute.new attribute }
75
98
  end
76
99
 
100
+ #
101
+ # Delete an attribute
102
+ #
103
+ # @return [Hash]
104
+ #
77
105
  def delete
78
106
  _post("/attributes/delete/#{id}") { |json| json }
79
107
  end
80
108
 
81
- def self.delete(id)
82
- new(id: id).delete
83
- end
84
-
109
+ #
110
+ # Create an attribute
111
+ #
112
+ # @return [MISP::Attribute]
113
+ #
85
114
  def create(event_id)
86
- _post("/attributes/add/#{event_id}", wrap(to_h)) { |attribute| Attribute.new symbolize_keys(attribute) }
87
- end
88
-
89
- def self.create(event_id, **attributes)
90
- new(attributes).create(event_id)
115
+ _post("/attributes/add/#{event_id}", wrap(to_h)) { |attribute| Attribute.new(**attribute) }
91
116
  end
92
117
 
118
+ #
119
+ # Update an attribute
120
+ #
121
+ # @param [Hash] **attrs attributes
122
+ #
123
+ # @return [MISP::Attribute]
124
+ #
93
125
  def update(**attrs)
94
126
  payload = to_h.merge(attrs)
95
127
  payload[:timestamp] = nil
96
- _post("/attributes/edit/#{id}", wrap(payload)) { |json| Attribute.new symbolize_keys(json.dig("response", "Attribute")) }
128
+ _post("/attributes/edit/#{id}", wrap(payload)) { |json| Attribute.new(**json.dig(:response, :Attribute)) }
97
129
  end
98
130
 
131
+ #
132
+ # Search for attributes
133
+ #
134
+ # @param [Hash] **params parameters
135
+ #
136
+ # @return [Array<MISP::Attributes>]
137
+ #
99
138
  def search(**params)
100
139
  base = {
101
140
  returnFormat: "json",
@@ -104,25 +143,53 @@ module MISP
104
143
  }
105
144
 
106
145
  _post("/attributes/restSearch", base.merge(params)) do |json|
107
- attributes = json.dig("response", "Attribute") || []
108
- attributes.map { |attribute| Attribute.new symbolize_keys(attribute) }
146
+ attributes = json.dig(:response, :Attribute) || []
147
+ attributes.map { |attribute| Attribute.new(**attribute) }
109
148
  end
110
149
  end
111
150
 
112
- def self.search(**params)
113
- new.search params
114
- end
115
-
151
+ #
152
+ # Add a tag to an attribute
153
+ #
154
+ # @param [MISP::Tag, Hash] tag
155
+ #
156
+ # @return [MISP::Tag]
157
+ #
116
158
  def add_tag(tag)
117
- tag = Tag.new(symbolize_keys(tag)) unless tag.is_a?(MISP::Tag)
159
+ tag = Tag.new(**tag) unless tag.is_a?(MISP::Tag)
118
160
  payload = { uuid: uuid, tag: tag.name }
119
- _post("/tags/attachTagToObject", payload) { |json| Tag.new symbolize_keys(json) }
161
+ _post("/tags/attachTagToObject", payload) { |json| Tag.new(**json) }
120
162
  end
121
163
 
164
+ #
165
+ # Remove a tag from an attribute
166
+ #
167
+ # @param [MISP::Tag, Hash] tag
168
+ #
169
+ # @return [Hash]
170
+ #
122
171
  def remove_tag(tag)
123
- tag = Tag.new(symbolize_keys(tag)) unless tag.is_a?(MISP::Tag)
172
+ tag = Tag.new(**tag) unless tag.is_a?(MISP::Tag)
124
173
  payload = { uuid: uuid, tag: tag.name }
125
174
  _post("/tags/removeTagFromObject", payload) { |json| json }
126
175
  end
176
+
177
+ class << self
178
+ def get(id)
179
+ new(id: id).get
180
+ end
181
+
182
+ def delete(id)
183
+ new(id: id).delete
184
+ end
185
+
186
+ def create(event_id, **attributes)
187
+ new(**attributes).create(event_id)
188
+ end
189
+
190
+ def search(**params)
191
+ new.search(**params)
192
+ end
193
+ end
127
194
  end
128
195
  end
data/lib/misp/base.rb CHANGED
@@ -6,40 +6,67 @@ require "uri"
6
6
 
7
7
  module MISP
8
8
  class Base
9
+ private
10
+
11
+ #
12
+ # API endpoint
13
+ #
14
+ # @return [URI]
15
+ #
9
16
  def api_endpoint
10
17
  @api_endpoint ||= URI(MISP.configuration.api_endpoint)
11
18
  end
12
19
 
20
+ #
21
+ # API key
22
+ #
23
+ # @return [String]
24
+ #
13
25
  def api_key
14
26
  @api_key ||= MISP.configuration.api_key
15
27
  end
16
28
 
17
- private
18
-
29
+ #
30
+ # Build an instance of a class
31
+ #
32
+ # @param [Hash] item
33
+ # @param [Class] klass
34
+ #
35
+ # @return [Class]
36
+ #
19
37
  def build_attribute(item:, klass:)
20
38
  return nil unless item
21
39
 
22
- klass.new symbolize_keys(item)
40
+ klass.new(**item)
23
41
  end
24
42
 
43
+ #
44
+ # Build an array of an instance of a class
45
+ #
46
+ # @param [Array<Hash>] items
47
+ # @param [Class] klass
48
+ #
49
+ # @return [Arra<Class>]
50
+ #
25
51
  def build_plural_attribute(items:, klass:)
26
52
  (items || []).map do |item|
27
- klass.new symbolize_keys(item)
53
+ klass.new(**item)
28
54
  end
29
55
  end
30
56
 
31
- def symbolize_keys(hash)
32
- hash.map { |k, v| [k.to_sym, v] }.to_h
33
- end
34
-
57
+ #
58
+ # a name of the class
59
+ #
60
+ # @return [String]
61
+ #
35
62
  def class_name
36
63
  self.class.to_s.split("::").last.to_s
37
64
  end
38
65
 
39
- def normalize_attributes(attributes)
66
+ def normalize_attributes(**attributes)
40
67
  klass = class_name.to_sym
41
68
 
42
- attributes.key?(klass) ? symbolize_keys(attributes.dig(klass)) : attributes
69
+ attributes.key?(klass) ? attributes[klass] : attributes
43
70
  end
44
71
 
45
72
  def wrap(params)
@@ -99,7 +126,7 @@ module MISP
99
126
  end
100
127
 
101
128
  def parse_body(body)
102
- JSON.parse body.to_s
129
+ JSON.parse body.to_s, symbolize_names: true
103
130
  rescue JSON::ParserError => _e
104
131
  body.to_s
105
132
  end
@@ -122,9 +149,9 @@ module MISP
122
149
 
123
150
  def default_headers
124
151
  {
125
- "Content-Type": "application/json",
126
- "Accept": "application/json",
127
- "Authorization": api_key
152
+ 'Content-Type': "application/json",
153
+ Accept: "application/json",
154
+ Authorization: api_key
128
155
  }
129
156
  end
130
157
 
@@ -14,15 +14,15 @@ module MISP
14
14
  end
15
15
  end
16
16
 
17
- def self.configuration
18
- @configuration ||= Configuration.new
19
- end
17
+ class << self
18
+ def configuration
19
+ @configuration ||= Configuration.new
20
+ end
20
21
 
21
- def self.configuration=(config)
22
- @configuration = config
23
- end
22
+ attr_writer :configuration
24
23
 
25
- def self.configure
26
- yield configuration
24
+ def configure
25
+ yield configuration
26
+ end
27
27
  end
28
28
  end