hachi 0.3.1 → 2.0.0
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.yml +23 -0
- data/.gitignore +5 -1
- data/README.md +12 -76
- data/Rakefile +1 -1
- data/hachi.gemspec +7 -5
- data/lib/hachi/api.rb +37 -5
- data/lib/hachi/awrence/methods.rb +66 -0
- data/lib/hachi/clients/alert.rb +9 -119
- data/lib/hachi/clients/artifact.rb +3 -38
- data/lib/hachi/clients/base.rb +72 -106
- data/lib/hachi/clients/case.rb +9 -102
- data/lib/hachi/clients/observable.rb +53 -0
- data/lib/hachi/clients/query.rb +18 -0
- data/lib/hachi/clients/user.rb +6 -16
- data/lib/hachi/version.rb +1 -1
- data/lib/hachi.rb +15 -6
- data/renovate.json +5 -0
- metadata +50 -27
- data/.travis.yml +0 -7
- data/lib/hachi/models/alert.rb +0 -74
- data/lib/hachi/models/artifact.rb +0 -44
- data/lib/hachi/models/base.rb +0 -31
- data/lib/hachi/models/case.rb +0 -61
- data/lib/hachi/models/user.rb +0 -39
- data/samples/01_create_an_alert.rb +0 -17
- data/samples/02_search_artifacts.rb +0 -16
- data/samples/03_list_cases.rb +0 -16
- data/samples/04_merge_alerts.rb +0 -17
data/lib/hachi/clients/base.rb
CHANGED
@@ -7,18 +7,83 @@ require "uri"
|
|
7
7
|
module Hachi
|
8
8
|
module Clients
|
9
9
|
class Base
|
10
|
+
# @return [String]
|
10
11
|
attr_reader :api_endpoint
|
12
|
+
|
13
|
+
# @return [String]
|
11
14
|
attr_reader :api_key
|
12
15
|
|
13
|
-
|
16
|
+
# @return [String, nil]
|
17
|
+
attr_reader :api_version
|
18
|
+
|
19
|
+
include Hachi::Awrence::Methods
|
20
|
+
|
21
|
+
def initialize(api_endpoint:, api_key:, api_version:)
|
14
22
|
@api_endpoint = URI(api_endpoint)
|
15
23
|
@api_key = api_key
|
24
|
+
@api_version = api_version
|
25
|
+
end
|
26
|
+
|
27
|
+
def get(path, params: {}, &block)
|
28
|
+
url = url_for(path)
|
29
|
+
url.query = URI.encode_www_form(params) unless params.empty?
|
30
|
+
|
31
|
+
get = Net::HTTP::Get.new(url)
|
32
|
+
get.add_field "Authorization", "Bearer #{api_key}"
|
33
|
+
request(get, &block)
|
34
|
+
end
|
35
|
+
|
36
|
+
def post(path, params: {}, json: {}, &block)
|
37
|
+
url = url_for(path)
|
38
|
+
url.query = URI.encode_www_form(params) unless params.empty?
|
39
|
+
|
40
|
+
json = to_camelback_keys(json.compact) if json.is_a?(Hash)
|
41
|
+
|
42
|
+
post = Net::HTTP::Post.new(url)
|
43
|
+
post.body = json.is_a?(Hash) ? json.to_json : json.to_s
|
44
|
+
|
45
|
+
post.add_field "Content-Type", "application/json"
|
46
|
+
post.add_field "Authorization", "Bearer #{api_key}"
|
47
|
+
|
48
|
+
request(post, &block)
|
49
|
+
end
|
50
|
+
|
51
|
+
def delete(path, params: {}, json: {}, &block)
|
52
|
+
url = url_for(path)
|
53
|
+
url.query = URI.encode_www_form(params) unless params.empty?
|
54
|
+
|
55
|
+
json = to_camelback_keys(json.compact) if json.is_a?(Hash)
|
56
|
+
|
57
|
+
delete = Net::HTTP::Delete.new(url)
|
58
|
+
delete.body = json.is_a?(Hash) ? json.to_json : json.to_s
|
59
|
+
|
60
|
+
delete.add_field "Authorization", "Bearer #{api_key}"
|
61
|
+
request(delete, &block)
|
62
|
+
end
|
63
|
+
|
64
|
+
def patch(path, params: {}, json: {}, &block)
|
65
|
+
url = url_for(path)
|
66
|
+
url.query = URI.encode_www_form(params) unless params.empty?
|
67
|
+
|
68
|
+
json = to_camelback_keys(json.compact) if json.is_a?(Hash)
|
69
|
+
|
70
|
+
patch = Net::HTTP::Patch.new(url)
|
71
|
+
patch.body = json.is_a?(Hash) ? json.to_json : json.to_s
|
72
|
+
|
73
|
+
patch.add_field "Content-Type", "application/json"
|
74
|
+
patch.add_field "Authorization", "Bearer #{api_key}"
|
75
|
+
|
76
|
+
request(patch, &block)
|
16
77
|
end
|
17
78
|
|
18
79
|
private
|
19
80
|
|
20
81
|
def base_url
|
21
|
-
|
82
|
+
if api_version.nil? || api_version.to_s.empty?
|
83
|
+
"#{api_endpoint.scheme}://#{api_endpoint.hostname}:#{api_endpoint.port}/api"
|
84
|
+
else
|
85
|
+
"#{api_endpoint.scheme}://#{api_endpoint.hostname}:#{api_endpoint.port}/api/#{api_version}"
|
86
|
+
end
|
22
87
|
end
|
23
88
|
|
24
89
|
def url_for(path)
|
@@ -28,13 +93,13 @@ module Hachi
|
|
28
93
|
def https_options
|
29
94
|
return nil if api_endpoint.scheme != "https"
|
30
95
|
|
31
|
-
if proxy = ENV
|
96
|
+
if proxy = ENV.fetch("HTTPS_PROXY") { ENV.fetch("https_proxy", nil) }
|
32
97
|
uri = URI(proxy)
|
33
98
|
{
|
34
99
|
proxy_address: uri.hostname,
|
35
100
|
proxy_port: uri.port,
|
36
101
|
proxy_from_env: false,
|
37
|
-
use_ssl: true
|
102
|
+
use_ssl: true
|
38
103
|
}
|
39
104
|
else
|
40
105
|
{ use_ssl: true }
|
@@ -42,12 +107,12 @@ module Hachi
|
|
42
107
|
end
|
43
108
|
|
44
109
|
def http_options
|
45
|
-
if proxy = ENV
|
110
|
+
if proxy = ENV.fetch("HTTP_PROXY") { ENV.fetch("http_proxy", nil) }
|
46
111
|
uri = URI(proxy)
|
47
112
|
{
|
48
113
|
proxy_address: uri.hostname,
|
49
114
|
proxy_port: uri.port,
|
50
|
-
proxy_from_env: false
|
115
|
+
proxy_from_env: false
|
51
116
|
}
|
52
117
|
else
|
53
118
|
{}
|
@@ -74,107 +139,8 @@ module Hachi
|
|
74
139
|
end
|
75
140
|
end
|
76
141
|
|
77
|
-
def get(path, params = {}, &block)
|
78
|
-
url = url_for(path)
|
79
|
-
url.query = URI.encode_www_form(params) unless params.empty?
|
80
|
-
|
81
|
-
get = Net::HTTP::Get.new(url)
|
82
|
-
get.add_field "Authorization", "Bearer #{api_key}"
|
83
|
-
request(get, &block)
|
84
|
-
end
|
85
|
-
|
86
|
-
def post(path, params = {}, &block)
|
87
|
-
url = url_for(path)
|
88
|
-
|
89
|
-
post = Net::HTTP::Post.new(url)
|
90
|
-
post.body = params.is_a?(Hash) ? params.to_json : params.to_s
|
91
|
-
|
92
|
-
post.add_field "Content-Type", "application/json"
|
93
|
-
post.add_field "Authorization", "Bearer #{api_key}"
|
94
|
-
|
95
|
-
request(post, &block)
|
96
|
-
end
|
97
|
-
|
98
|
-
def delete(path, params = {}, &block)
|
99
|
-
url = url_for(path)
|
100
|
-
url.query = URI.encode_www_form(params) unless params.empty?
|
101
|
-
|
102
|
-
delete = Net::HTTP::Delete.new(url)
|
103
|
-
delete.add_field "Authorization", "Bearer #{api_key}"
|
104
|
-
request(delete, &block)
|
105
|
-
end
|
106
|
-
|
107
|
-
def patch(path, params = {}, &block)
|
108
|
-
url = url_for(path)
|
109
|
-
|
110
|
-
patch = Net::HTTP::Patch.new(url)
|
111
|
-
patch.body = params.is_a?(Hash) ? params.to_json : params.to_s
|
112
|
-
|
113
|
-
patch.add_field "Content-Type", "application/json"
|
114
|
-
patch.add_field "Authorization", "Bearer #{api_key}"
|
115
|
-
|
116
|
-
request(patch, &block)
|
117
|
-
end
|
118
|
-
|
119
|
-
def validate_range(range)
|
120
|
-
return true if range == "all"
|
121
|
-
raise ArgumentError, "range should be 'all' or `from-to`" unless range.match?(/(\d+)-(\d+)/)
|
122
|
-
|
123
|
-
from, to = range.split("-").map(&:to_i)
|
124
|
-
return true if from < to
|
125
|
-
|
126
|
-
raise ArgumentError, "from should be smaller than to"
|
127
|
-
end
|
128
|
-
|
129
|
-
def _search(path, attributes:, range: "all", sort: nil)
|
130
|
-
validate_range range
|
131
|
-
|
132
|
-
attributes = normalize_attributes(attributes)
|
133
|
-
conditions = attributes.map do |key, value|
|
134
|
-
if key == :data && value.is_a?(Array)
|
135
|
-
{ _or: decompose_data(value) }
|
136
|
-
else
|
137
|
-
{ _string: "#{key}:\"#{value}\"" }
|
138
|
-
end
|
139
|
-
end
|
140
|
-
|
141
|
-
default_conditions = {
|
142
|
-
_and: [
|
143
|
-
{ _not: { status: "Deleted" } },
|
144
|
-
{ _not: { _in: { _field: "_type", _values: ["dashboard", "data", "user", "analyzer", "caseTemplate", "reportTemplate", "action"] } } },
|
145
|
-
],
|
146
|
-
}
|
147
|
-
|
148
|
-
query = {
|
149
|
-
_and: [conditions, default_conditions].flatten,
|
150
|
-
}
|
151
|
-
|
152
|
-
query_string = build_query_string(range: range, sort: sort)
|
153
|
-
|
154
|
-
post("#{path}?#{query_string}", query: query) { |json| json }
|
155
|
-
end
|
156
|
-
|
157
|
-
def decompose_data(data)
|
158
|
-
data.map do |elem|
|
159
|
-
{ _field: "data", _value: elem }
|
160
|
-
end
|
161
|
-
end
|
162
|
-
|
163
|
-
def normalize_attributes(attributes)
|
164
|
-
h = {}
|
165
|
-
attributes.each do |key, value|
|
166
|
-
h[camelize(key).to_sym] = value
|
167
|
-
end
|
168
|
-
h
|
169
|
-
end
|
170
|
-
|
171
|
-
def camelize(string)
|
172
|
-
head, *others = string.to_s.split("_")
|
173
|
-
[head, others.map(&:capitalize)].flatten.join
|
174
|
-
end
|
175
|
-
|
176
142
|
def build_query_string(params)
|
177
|
-
URI.encode_www_form(params.
|
143
|
+
URI.encode_www_form(params.compact)
|
178
144
|
end
|
179
145
|
end
|
180
146
|
end
|
data/lib/hachi/clients/case.rb
CHANGED
@@ -3,15 +3,6 @@
|
|
3
3
|
module Hachi
|
4
4
|
module Clients
|
5
5
|
class Case < Base
|
6
|
-
#
|
7
|
-
# List cases
|
8
|
-
#
|
9
|
-
# @return [Array]
|
10
|
-
#
|
11
|
-
def list
|
12
|
-
get("/api/case") { |json| json }
|
13
|
-
end
|
14
|
-
|
15
6
|
#
|
16
7
|
# Get a case
|
17
8
|
#
|
@@ -20,7 +11,7 @@ module Hachi
|
|
20
11
|
# @return [Hash]
|
21
12
|
#
|
22
13
|
def get_by_id(id)
|
23
|
-
get("/
|
14
|
+
get("/case/#{id}") { |json| json }
|
24
15
|
end
|
25
16
|
|
26
17
|
#
|
@@ -31,114 +22,30 @@ module Hachi
|
|
31
22
|
# @return [String]
|
32
23
|
#
|
33
24
|
def delete_by_id(id)
|
34
|
-
delete("/
|
25
|
+
delete("/case/#{id}") { |json| json }
|
35
26
|
end
|
36
27
|
|
37
28
|
#
|
38
29
|
# Create a case
|
39
30
|
#
|
40
|
-
# @param [
|
41
|
-
# @param [String, nil] description
|
42
|
-
# @param [Integer, nil] severity
|
43
|
-
# @param [String, nil] start_date
|
44
|
-
# @param [String, nil] owner
|
45
|
-
# @param [Boolean, nil] flag
|
46
|
-
# @param [Intege, nil] tlp
|
47
|
-
# @param [String, nil] tags
|
48
|
-
#
|
49
|
-
# @return [Hash]
|
50
|
-
#
|
51
|
-
def create(title:, description:, severity: nil, start_date: nil, owner: nil, flag: nil, tlp: nil, tags: nil)
|
52
|
-
kase = Models::Case.new(
|
53
|
-
title: title,
|
54
|
-
description: description,
|
55
|
-
severity: severity,
|
56
|
-
start_date: start_date,
|
57
|
-
owner: owner,
|
58
|
-
flag: flag,
|
59
|
-
tlp: tlp,
|
60
|
-
tags: tags,
|
61
|
-
)
|
62
|
-
|
63
|
-
post("/api/case", kase.payload) { |json| json }
|
64
|
-
end
|
65
|
-
|
66
|
-
#
|
67
|
-
# Find cases
|
68
|
-
#
|
69
|
-
# @param [Hash] attributes
|
70
|
-
# @param [String] range
|
71
|
-
#
|
72
|
-
# @return [Hash]
|
73
|
-
#
|
74
|
-
def search(attributes, range: "all")
|
75
|
-
_search("/api/case/_search", attributes: attributes, range: range) { |json| json }
|
76
|
-
end
|
77
|
-
|
78
|
-
#
|
79
|
-
# Get list of cases linked to this case
|
80
|
-
#
|
81
|
-
# @param [String] id Case ID
|
82
|
-
#
|
83
|
-
# @return [Array]
|
84
|
-
#
|
85
|
-
def links(id)
|
86
|
-
get("/api/case/#{id}/links") { |json| json }
|
87
|
-
end
|
88
|
-
|
89
|
-
#
|
90
|
-
# Merge two cases
|
91
|
-
#
|
92
|
-
# @param [String] id1 Case ID
|
93
|
-
# @param [String] id2 Case ID
|
31
|
+
# @param [Hash] payload
|
94
32
|
#
|
95
33
|
# @return [Hash]
|
96
34
|
#
|
97
|
-
def
|
98
|
-
post("/
|
35
|
+
def create(**payload)
|
36
|
+
post("/case", json: payload) { |json| json }
|
99
37
|
end
|
100
38
|
|
101
39
|
#
|
102
40
|
# Update a case
|
103
41
|
#
|
104
|
-
# @param [String
|
105
|
-
# @param [
|
106
|
-
# @param [String, nil] description
|
107
|
-
# @param [String, nil] severity
|
108
|
-
# @param [String, nil] start_date
|
109
|
-
# @param [String, nil] owner
|
110
|
-
# @param [Boolean, nil] flag
|
111
|
-
# @param [Integer, nil] tlp
|
112
|
-
# @param [String, nil] tags
|
113
|
-
# @param [String, nil] status
|
114
|
-
# @param [String, nil] resolution_status
|
115
|
-
# @param [String, nil] impact_status
|
116
|
-
# @param [String, nil] summary
|
117
|
-
# @param [String, nil] end_date
|
118
|
-
# @param [String, nil] metrics
|
119
|
-
# @param [String, nil] custom_fields
|
42
|
+
# @param [String] id
|
43
|
+
# @param [Hash] payload
|
120
44
|
#
|
121
45
|
# @return [Hash]
|
122
46
|
#
|
123
|
-
def update(id,
|
124
|
-
|
125
|
-
title: title,
|
126
|
-
description: description,
|
127
|
-
severity: severity,
|
128
|
-
startDate: start_date,
|
129
|
-
owner: owner,
|
130
|
-
flag: flag,
|
131
|
-
tlp: tlp,
|
132
|
-
tags: tags,
|
133
|
-
status: status,
|
134
|
-
resolutionStatus: resolution_status,
|
135
|
-
impactStatus: impact_status,
|
136
|
-
summary: summary,
|
137
|
-
endDate: end_date,
|
138
|
-
metrics: metrics,
|
139
|
-
customFields: custom_fields
|
140
|
-
}.compact
|
141
|
-
patch("/api/case/#{id}", attributes) { |json| json }
|
47
|
+
def update(id, **payload)
|
48
|
+
patch("/case/#{id}", json: payload) { |json| json }
|
142
49
|
end
|
143
50
|
end
|
144
51
|
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Hachi
|
4
|
+
module Clients
|
5
|
+
class Observable < Base
|
6
|
+
#
|
7
|
+
# Create an observable in a case
|
8
|
+
#
|
9
|
+
# @param [String] case_id Observable ID
|
10
|
+
# @param [Hash] payload
|
11
|
+
#
|
12
|
+
# @return [Hash]
|
13
|
+
#
|
14
|
+
def create_in_case(case_id, **payload)
|
15
|
+
post("/case/#{case_id}/observable", json: payload) { |json| json }
|
16
|
+
end
|
17
|
+
|
18
|
+
#
|
19
|
+
# Create an observable in an alert
|
20
|
+
#
|
21
|
+
# @param [String] alert_id Observable ID
|
22
|
+
# @param [Hash] payload
|
23
|
+
#
|
24
|
+
# @return [Hash]
|
25
|
+
#
|
26
|
+
def create_in_alert(alert_id, **payload)
|
27
|
+
post("/alert/#{alert_id}/observable", json: payload) { |json| json }
|
28
|
+
end
|
29
|
+
|
30
|
+
#
|
31
|
+
# Get an observable
|
32
|
+
#
|
33
|
+
# @param [String] id observable ID
|
34
|
+
#
|
35
|
+
# @return [Hash]
|
36
|
+
#
|
37
|
+
def get_by_id(id)
|
38
|
+
get("/observable/#{id}") { |json| json }
|
39
|
+
end
|
40
|
+
|
41
|
+
#
|
42
|
+
# Delete an observable
|
43
|
+
#
|
44
|
+
# @param [String] id observable ID
|
45
|
+
#
|
46
|
+
# @return [String]
|
47
|
+
#
|
48
|
+
def delete_by_id(id)
|
49
|
+
delete("/observable/#{id}") { |json| json }
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Hachi
|
4
|
+
module Clients
|
5
|
+
class Query < Base
|
6
|
+
#
|
7
|
+
# Query
|
8
|
+
#
|
9
|
+
# @param [Hash] payload
|
10
|
+
#
|
11
|
+
# @return [Hash]
|
12
|
+
#
|
13
|
+
def query(**payload)
|
14
|
+
post("/query", json: payload) { |json| json }
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
data/lib/hachi/clients/user.rb
CHANGED
@@ -9,7 +9,7 @@ module Hachi
|
|
9
9
|
# @return [Hash]
|
10
10
|
#
|
11
11
|
def current
|
12
|
-
get("/
|
12
|
+
get("/user/current") { |json| json }
|
13
13
|
end
|
14
14
|
|
15
15
|
#
|
@@ -20,7 +20,7 @@ module Hachi
|
|
20
20
|
# @return [Hash]
|
21
21
|
#
|
22
22
|
def get_by_id(id)
|
23
|
-
get("/
|
23
|
+
get("/user/#{id}") { |json| json }
|
24
24
|
end
|
25
25
|
|
26
26
|
#
|
@@ -31,28 +31,18 @@ module Hachi
|
|
31
31
|
# @return [String]
|
32
32
|
#
|
33
33
|
def delete_by_id(id)
|
34
|
-
delete("/
|
34
|
+
delete("/user/#{id}") { |json| json }
|
35
35
|
end
|
36
36
|
|
37
37
|
#
|
38
38
|
# Create a user
|
39
39
|
#
|
40
|
-
# @param [
|
41
|
-
# @param [String] name
|
42
|
-
# @param [Array<String>] roles
|
43
|
-
# @param [String] password
|
40
|
+
# @param [Hash] payload
|
44
41
|
#
|
45
42
|
# @return [Hash]
|
46
43
|
#
|
47
|
-
def create(
|
48
|
-
user
|
49
|
-
login: login,
|
50
|
-
name: name,
|
51
|
-
roles: roles,
|
52
|
-
password: password
|
53
|
-
)
|
54
|
-
|
55
|
-
post("/api/user", user.payload) { |json| json }
|
44
|
+
def create(**payload)
|
45
|
+
post("/user", json: payload) { |json| json }
|
56
46
|
end
|
57
47
|
end
|
58
48
|
end
|
data/lib/hachi/version.rb
CHANGED
data/lib/hachi.rb
CHANGED
@@ -2,20 +2,29 @@
|
|
2
2
|
|
3
3
|
require "hachi/version"
|
4
4
|
|
5
|
-
require "hachi/
|
5
|
+
require "hachi/awrence/methods"
|
6
6
|
|
7
|
-
require "hachi/
|
8
|
-
require "hachi/models/alert"
|
9
|
-
require "hachi/models/artifact"
|
10
|
-
require "hachi/models/case"
|
11
|
-
require "hachi/models/user"
|
7
|
+
require "hachi/api"
|
12
8
|
|
13
9
|
require "hachi/clients/base"
|
10
|
+
|
14
11
|
require "hachi/clients/alert"
|
15
12
|
require "hachi/clients/artifact"
|
16
13
|
require "hachi/clients/case"
|
14
|
+
require "hachi/clients/observable"
|
15
|
+
require "hachi/clients/query"
|
17
16
|
require "hachi/clients/user"
|
18
17
|
|
19
18
|
module Hachi
|
19
|
+
module Awrence
|
20
|
+
class << self
|
21
|
+
attr_writer :acronyms
|
22
|
+
|
23
|
+
def acronyms
|
24
|
+
@acronyms ||= {}
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
20
29
|
class Error < StandardError; end
|
21
30
|
end
|