misp 0.1.0 → 0.1.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/test.yaml +23 -0
- data/README.md +12 -3
- data/lib/misp/attribute.rb +111 -44
- data/lib/misp/base.rb +41 -14
- data/lib/misp/configuration.rb +8 -8
- data/lib/misp/event.rb +140 -65
- data/lib/misp/feed.rb +82 -36
- data/lib/misp/galaxy.rb +40 -16
- data/lib/misp/galaxy_cluster.rb +28 -12
- data/lib/misp/org.rb +12 -4
- data/lib/misp/orgc.rb +12 -4
- data/lib/misp/server.rb +22 -7
- data/lib/misp/sharing_group.rb +67 -27
- data/lib/misp/sharing_group_org.rb +17 -7
- data/lib/misp/sharing_group_server.rb +17 -7
- data/lib/misp/tag.rb +71 -28
- data/lib/misp/version.rb +1 -1
- data/misp.gemspec +6 -6
- data/renovate.json +5 -0
- metadata +21 -20
- data/.travis.yml +0 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: dc2ae518cdfe5d3872000bf12bd1083a4a8540a3e6857e50573b1b8011604605
|
4
|
+
data.tar.gz: 25917d69a7a912ace45ccaa43f5bc0a5202e37e66532e1eabf30a09df0478c2e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
24
|
-
- 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).
|
data/lib/misp/attribute.rb
CHANGED
@@ -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
|
28
|
-
@type = attributes
|
29
|
-
@category = attributes
|
30
|
-
@to_ids = attributes
|
31
|
-
@uuid = attributes
|
32
|
-
@event_id = attributes
|
33
|
-
@distribution = attributes
|
34
|
-
@timestamp = attributes
|
35
|
-
@comment = attributes
|
36
|
-
@sharing_group_id = attributes
|
37
|
-
@deleted = attributes
|
38
|
-
@disable_correlation = attributes
|
39
|
-
@value = attributes
|
40
|
-
@data = attributes
|
41
|
-
|
42
|
-
@sharing_groups = build_plural_attribute(items: attributes
|
43
|
-
@shadow_attributes = build_plural_attribute(items: attributes
|
44
|
-
@tags = build_plural_attribute(items: attributes
|
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
|
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
|
-
|
82
|
-
|
83
|
-
|
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
|
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
|
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(
|
108
|
-
attributes.map { |attribute| Attribute.new
|
146
|
+
attributes = json.dig(:response, :Attribute) || []
|
147
|
+
attributes.map { |attribute| Attribute.new(**attribute) }
|
109
148
|
end
|
110
149
|
end
|
111
150
|
|
112
|
-
|
113
|
-
|
114
|
-
|
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(
|
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
|
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(
|
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
|
-
|
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
|
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
|
53
|
+
klass.new(**item)
|
28
54
|
end
|
29
55
|
end
|
30
56
|
|
31
|
-
|
32
|
-
|
33
|
-
|
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) ?
|
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
|
-
|
126
|
-
|
127
|
-
|
152
|
+
'Content-Type': "application/json",
|
153
|
+
Accept: "application/json",
|
154
|
+
Authorization: api_key
|
128
155
|
}
|
129
156
|
end
|
130
157
|
|
data/lib/misp/configuration.rb
CHANGED
@@ -14,15 +14,15 @@ module MISP
|
|
14
14
|
end
|
15
15
|
end
|
16
16
|
|
17
|
-
|
18
|
-
|
19
|
-
|
17
|
+
class << self
|
18
|
+
def configuration
|
19
|
+
@configuration ||= Configuration.new
|
20
|
+
end
|
20
21
|
|
21
|
-
|
22
|
-
@configuration = config
|
23
|
-
end
|
22
|
+
attr_writer :configuration
|
24
23
|
|
25
|
-
|
26
|
-
|
24
|
+
def configure
|
25
|
+
yield configuration
|
26
|
+
end
|
27
27
|
end
|
28
28
|
end
|