hachi 0.3.1 → 2.0.0
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 +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
|