zanshin 1.0.1

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 1e4c4177cdf64faab3875bd1fe144c21284530f211812969e0bd3ec5ab438d56
4
+ data.tar.gz: abd2455541e409bb6a018f12ba3e644fd32132ebd221f927f257a26d1e81e3b1
5
+ SHA512:
6
+ metadata.gz: 0ca6218cf76d9642937df5f15b68c2bec389cd147bc68af25aa2969dcd6a42e3c2722d1fe56a54db17844de2e9222b29b10e6386d2fa152ee243e952001330c9
7
+ data.tar.gz: fea40647b2780b2059188a477e4644426903164429bc5ae951f69950d78697cb080ca0634da6b6d306e16396fda12012b905b83d1e3d15b2d0e03f246c6d96d1
data/LICENSE.md ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2022 Tenchi Security.
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,135 @@
1
+ [![Gem Version](https://badge.fury.io/rb/zanshin.svg)](https://badge.fury.io/rb/zanshin)
2
+
3
+ # Zanshin Ruby SDK
4
+
5
+ This Ruby gem contains an SDK to interact with the [API of the Zanshin](https://api.zanshin.tenchisecurity.com) from [Tenchi Security](https://www.tenchisecurity.com).
6
+
7
+ ## Installation
8
+
9
+ Install the gem and add to the application's Gemfile by executing:
10
+
11
+ ```shell
12
+ bundle add zanshin
13
+ ```
14
+
15
+ If bundler is not being used to manage dependencies, install the gem by executing:
16
+
17
+ ```shell
18
+ gem install zanshin
19
+ ```
20
+
21
+ ## Setting up Credentials
22
+
23
+ There are three ways that the SDK handles credentials. The order of evaluation is:
24
+ - [**1st** Client Parameters](#client-parameters)
25
+ - [**2nd** Environment Variables](#environment-variables)
26
+ - [**3rd** Config File](#config-file)
27
+
28
+ ### Client Parameters
29
+
30
+ When calling the `Client` class, you can pass the values API Key(`api_key`), API URL(`api_url`), User Agent(`user_agent`) and Proxy URL(`proxy_url`) you want to use as below:
31
+
32
+ ```ruby
33
+ client = Zanshin::SDK::Client.new(api_key: "my_zanshin_api_key")
34
+ puts client.get_me
35
+ ```
36
+
37
+ > These values will overwrite anything you set as Environment Variables or in the Config File.
38
+
39
+ ### Environment Variables
40
+
41
+ You can use the following Environment Variables to configure Zanshin SDK:
42
+ - `ZANSHIN_API_KEY`: Will setup your Zanshin credentials
43
+ - `ZANSHIN_API_URL`: Will define the API URL. Default is `https://api.zanshin.tenchisecurity.com`
44
+ - `ZANSHIN_USER_AGENT`: If you want to overwrite the User Agent when calling Zanshin API (The value passed will be concatenated to the SDK's default value `Zanshin Ruby SDK 1.0.0`)
45
+ - `HTTP_PROXY | HTTPS_PROXY`: Zanshin SDK uses `Net::HTTP` under the hood, checkout the [Net::HTTP RDoc](https://ruby-doc.org/stdlib-3.1.2/libdoc/net/http/rdoc/Net/HTTP.html) section of their documentation for more use cases
46
+
47
+ #### Example
48
+
49
+ ```shell
50
+ export ZANSHIN_API_KEY="my_zanshin_api_key"
51
+ ```
52
+
53
+ > These Environment Variables will overwrite anything you set on the Config File.
54
+
55
+ ### Config File
56
+
57
+ This `Ruby SDK` was built to be used under the same conditions as the [Python SDK](https://github.com/tenchi-security/zanshin-sdk-python), so the configuration file is in the format created by the Python [RawConfigParser](https://docs.python.org/3/library/configparser.html#configparser.RawConfigParser) class.
58
+
59
+ The file is located at `~/.tenchi/config`, where `~` is the [current user's home directory](https://docs.python.org/3/library/pathlib.html#pathlib.Path.home).
60
+
61
+ Each section is treated as a configuration profile, and the SDK will look for a section called `default` if another is not explicitly selected.
62
+
63
+ These are the supported options:
64
+
65
+ * `api_key` (required) which contains the Zanshin API key obtained at the [Zanshin web portal](https://zanshin.tenchisecurity.com/my-profile).
66
+ * `api_url` (optional) directs the SDK to use a different API endpoint than the default (https://api.zanshin.tenchisecurity.com).
67
+ * `user_agent` (optional) allows you to override the default user-agent header used by the SDK when making API requests (The value passed will be concatenated to the SDK's default value `Zanshin Ruby SDK 1.0.0`).
68
+ * `proxy_url` (optional) directs the SDK to use a Proxy.
69
+
70
+ This is what a minimal configuration file looks like:
71
+ ```ini
72
+ [default]
73
+ api_key=my_zanshin_api_key
74
+ ```
75
+
76
+ ## The SDK
77
+
78
+ ```ruby
79
+ client = Zanshin::SDK::Client.new
80
+ client.get_me
81
+ ```
82
+
83
+ ## Usage
84
+
85
+ ### Create instance
86
+ ```ruby
87
+ client = Zanshin::SDK::Client.new # loads API key from the "default" profile in ~/.tenchi/config
88
+ ```
89
+
90
+ ### Get logged user info
91
+ ```ruby
92
+ client.get_me # calls /me API endpoint
93
+ ```
94
+
95
+ ### Get logged user info
96
+
97
+ Methods with prefix `iter_*` return an [Enumerator](https://ruby-doc.org/core-2.6/Enumerator.html), you can use it however you want, in this example we use `to_a` to convert the Enumerator into an Array.
98
+ ```ruby
99
+ client.iter_organizations.to_a # calls /organizations API endpoint
100
+ ```
101
+
102
+ ## Support
103
+
104
+ If you are a Zanshin customer and have any questions regarding the use of the service, its API or this SDK package, please get in touch via e-mail at `support@tenchisecurity.com` or via the support widget on the [Zanshin Portal](https://zanshin.tenchisecurity.com).
105
+
106
+ ## Development
107
+
108
+ After checking out the repo, run `bundle install` to install dependencies. Then, run `bundle exec rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
109
+
110
+ ## Contributing
111
+
112
+ Bug reports and pull requests are welcome on GitHub at `https://github.com/tenchi-security/zanshin-sdk-ruby`. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/tenchi-security/zanshin-sdk-ruby/blob/main/CODE_OF_CONDUCT.md).
113
+
114
+ ## Unit Test
115
+
116
+ Currently, the Unit Test only checks if the API call was made in the way it should, after implementing the input validations the tests will be better.
117
+
118
+ ## TODO
119
+
120
+ - Input validation
121
+ - Coverage badge
122
+ - File Persistent Alerts Iterator
123
+ - Onboard Scan Targets (AWS)
124
+
125
+ ## License
126
+
127
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
128
+
129
+ ## Code of Conduct
130
+
131
+ Everyone interacting in the `Zanshin Ruby SDK` project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/tenchi-security/zanshin-sdk-ruby/blob/main/CODE_OF_CONDUCT.md).
132
+
133
+ ## Changelog
134
+
135
+ See [CHANGELOG](https://github.com/tenchi-security/zanshin-sdk-ruby/blob/main/CHANGELOG.md) for a list of changes.
@@ -0,0 +1,96 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Zanshin
4
+ module SDK
5
+ # Zanshin SDK Account
6
+ module Account
7
+ ###################################################
8
+ # Account
9
+ ###################################################
10
+
11
+ # Returns the details of the user account that owns the API key used by this Connection instance
12
+ # [#reference](https://api.zanshin.tenchisecurity.com/#operation/getMe)
13
+ #
14
+ # @return an Object representing the user
15
+ def get_me
16
+ @http.request('GET', '/me')
17
+ end
18
+
19
+ ###################################################
20
+ # Account Invites
21
+ ###################################################
22
+
23
+ # Invites Enumerator of current logged user
24
+ # [#reference](https://api.zanshin.tenchisecurity.com/#operation/getInvites)
25
+ #
26
+ # @return an Invites Enumerator object
27
+ def iter_invites
28
+ Enumerator.new do |yielder|
29
+ @http.request('GET', '/me/invites').each do |e|
30
+ yielder.yield e
31
+ end
32
+ end
33
+ end
34
+
35
+ # Gets a specific invitation details, it only works if the invitation was made for the current logged user
36
+ # [#reference](https://api.zanshin.tenchisecurity.com/#operation/getInviteById)
37
+ #
38
+ # @param invite_id [UUID] of the invite
39
+ #
40
+ # @return an Object representing the invite
41
+ def get_invite(invite_id)
42
+ @http.request('GET', "/me/invites/#{validate_uuid(invite_id)}")
43
+ end
44
+
45
+ # Accepts an invitation with the informed ID, it only works if the user accepting the invitation is the user that
46
+ # received the invitation
47
+ # [#reference](https://api.zanshin.tenchisecurity.com/#operation/acceptInviteById)
48
+ #
49
+ # @param invite_id [UUID] of the invite
50
+ #
51
+ # @return an Object representing the organization of this invite
52
+ def accept_invite(invite_id)
53
+ @http.request('POST', "/me/invites/#{validate_uuid(invite_id)}/accept")
54
+ end
55
+
56
+ ###################################################
57
+ # Account API key
58
+ ###################################################
59
+
60
+ # API keys Enumerator of current logged user
61
+ # [#reference](https://api.zanshin.tenchisecurity.com/#operation/getMyApiKeys)
62
+ #
63
+ # @return an API keys Enumerator object
64
+ def iter_api_keys
65
+ Enumerator.new do |yielder|
66
+ @http.request('GET', '/me/apikeys').each do |e|
67
+ yielder.yield e
68
+ end
69
+ end
70
+ end
71
+
72
+ # Creates a new API key for the current logged user, API Keys can be used to interact with the zanshin api
73
+ # directly on behalf of that user
74
+ # [#reference](https://api.zanshin.tenchisecurity.com/#operation/createApiKeys)
75
+ #
76
+ # @param name [String] of your new API key
77
+ #
78
+ # @return an Object representing the user api key
79
+ def create_api_key(name)
80
+ body = {}
81
+ body['name'] = name
82
+ @http.request('POST', '/me/apikeys', body)
83
+ end
84
+
85
+ # Deletes a given API key by its id, it will only work if the informed ID belongs to the current logged user
86
+ # [#reference](https://api.zanshin.tenchisecurity.com/#operation/deleteApiKey)
87
+ #
88
+ # @param api_key_id [UUID] of your new API key
89
+ #
90
+ # @return a Boolean with result
91
+ def delete_api_key(api_key_id)
92
+ @http.request('DELETE', "/me/apikeys/#{validate_uuid(api_key_id)}")
93
+ end
94
+ end
95
+ end
96
+ end
@@ -0,0 +1,368 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Zanshin
4
+ module SDK
5
+ # Zanshin SDK Alerts
6
+ module Alerts
7
+ ###################################################
8
+ # Alerts
9
+ ###################################################
10
+
11
+ # Alerts Enumerator of an organization by loading them, transparently paginating on the API
12
+ # [#reference](https://api.zanshin.tenchisecurity.com/#operation/listAllAlert)
13
+ #
14
+ # @param organization_id [UUID] of the organization
15
+ # @param scan_target_ids [Array<UUID>] Optional list of scan target IDs to list alerts from, defaults to all
16
+ # @param rule [String] to filter alerts from (rule), not passing the field will fetch all
17
+ # @param states [Array<'OPEN', 'ACTIVE', 'IN_PROGRESS', 'RISK_ACCEPTED', 'CLOSED'>] Optional list of states to
18
+ # filter returned alerts, defaults to all
19
+ # @param severities [Array<'CRITICAL', 'HIGH', 'MEDIUM', 'LOW', 'INFO'>] optional list of severities to filter
20
+ # returned alerts, defaults to all
21
+ # @param page_size [Integer] the number of alerts to load from the API at a time
22
+ # @param language ['en-US', 'pt-BR'] Language of the rule will be returned
23
+ # @param created_at_start [String] Search alerts by creation date - greater or equals than
24
+ # @param created_at_end [String] Search alerts by creation date - less or equals than
25
+ # @param updated_at_start [String] Search alerts by update date - greater or equals than
26
+ # @param updated_at_end [String] Search alerts by update date - less or equals than
27
+ #
28
+ # @return an Alerts Enumerator object
29
+ def iter_alerts(organization_id,
30
+ scan_target_ids: [],
31
+ rule: nil,
32
+ states: nil,
33
+ severities: nil,
34
+ page_size: 100,
35
+ language: nil,
36
+ created_at_start: nil,
37
+ created_at_end: nil,
38
+ updated_at_start: nil,
39
+ updated_at_end: nil)
40
+ body = {
41
+ 'organizationId' => validate_uuid(organization_id),
42
+ 'page' => 0,
43
+ 'pageSize' => page_size,
44
+ 'scanTargetIds' => scan_target_ids.each { |scan_target_id| validate_uuid(scan_target_id) },
45
+ 'rule' => rule,
46
+ 'states' => states,
47
+ 'severities' => severities,
48
+ 'lang' => language,
49
+ 'CreatedAtStart' => created_at_start,
50
+ 'CreatedAtEnd' => created_at_end,
51
+ 'UpdatedAtStart' => updated_at_start,
52
+ 'UpdatedAtEnd' => updated_at_end
53
+ }
54
+
55
+ Enumerator.new do |yielder|
56
+ loop do
57
+ body['page'] += 1
58
+ data = @http.request('POST', '/alerts', body.compact)
59
+ data['data'].each do |e|
60
+ yielder.yield e
61
+ end
62
+ break if body['page'] == (data['total'] / body['pageSize']).ceil
63
+ end
64
+ end
65
+ end
66
+
67
+ # Alerts Following Enumerator over the following alerts froms organizations being followed by
68
+ # transparently paginating on the API
69
+ # [#reference](https://api.zanshin.tenchisecurity.com/#operation/listFollowingAlerts)
70
+ #
71
+ # @param organization_id [UUID] of the organization
72
+ # @param following_ids [Array<UUID>] Optional list of IDs of organizations you are following to
73
+ # list alerts from, defaults to all
74
+ # @param rule [String] to filter alerts from (rule), not passing the field will fetch all
75
+ # @param states [Array<'OPEN', 'ACTIVE', 'IN_PROGRESS', 'RISK_ACCEPTED', 'CLOSED'>] Optional list of states to
76
+ # filter returned alerts, defaults to all
77
+ # @param severities [Array<'CRITICAL', 'HIGH', 'MEDIUM', 'LOW', 'INFO'>] optional list of severities to filter
78
+ # returned alerts, defaults to all
79
+ # @param page_size [Integer] the number of alerts to load from the API at a time
80
+ # @param language ['en-US', 'pt-BR'] Language of the rule will be returned
81
+ # @param created_at_start [String] Search alerts by creation date - greater or equals than
82
+ # @param created_at_end [String] Search alerts by creation date - less or equals than
83
+ # @param updated_at_start [String] Search alerts by update date - greater or equals than
84
+ # @param updated_at_end [String] Search alerts by update date - less or equals than
85
+ #
86
+ # @return an Alerts Enumerator object
87
+ def iter_following_alerts(organization_id,
88
+ following_ids: [],
89
+ rule: nil,
90
+ states: nil,
91
+ severities: nil,
92
+ page_size: 100,
93
+ language: nil,
94
+ created_at_start: nil,
95
+ created_at_end: nil,
96
+ updated_at_start: nil,
97
+ updated_at_end: nil)
98
+ body = {
99
+ 'organizationId' => validate_uuid(organization_id),
100
+ 'page' => 0,
101
+ 'pageSize' => page_size,
102
+ 'followingIds' => following_ids.each { |following_id| validate_uuid(following_id) },
103
+ 'rule' => rule,
104
+ 'states' => states,
105
+ 'severities' => severities,
106
+ 'lang' => language,
107
+ 'CreatedAtStart' => created_at_start,
108
+ 'CreatedAtEnd' => created_at_end,
109
+ 'UpdatedAtStart' => updated_at_start,
110
+ 'UpdatedAtEnd' => updated_at_end
111
+ }
112
+
113
+ Enumerator.new do |yielder|
114
+ loop do
115
+ body['page'] += 1
116
+ data = @http.request('POST', '/alerts/following', body.compact)
117
+ data['data'].each do |e|
118
+ yielder.yield e
119
+ end
120
+ break if body['page'] == (data['total'] / body['pageSize']).ceil
121
+ end
122
+ end
123
+ end
124
+
125
+ # Alerts History Enumerator of an organization by loading them, transparently paginating on the API
126
+ # [#reference](https://api.zanshin.tenchisecurity.com/#operation/listAllAlertsHistory)
127
+ #
128
+ # @param organization_id [UUID] of the organization
129
+ # @param scan_target_ids [Array<UUID>] Optional list of scan target IDs to list alerts from, defaults to all
130
+ # @param page_size [Integer] the number of alerts to load from the API at a time
131
+ # @param language ['en-US', 'pt-BR'] Language of the rule will be returned
132
+ # @param cursor [String] Alert Cursor of the last alert consumed, when this value is passed, subsequent
133
+ # alert histories will be returned
134
+ #
135
+ # @return an Alerts History Enumerator object
136
+ def iter_alerts_history(organization_id,
137
+ scan_target_ids: [],
138
+ page_size: 100,
139
+ language: nil,
140
+ cursor: nil)
141
+ body = {
142
+ 'organizationId' => validate_uuid(organization_id),
143
+ 'page' => 0,
144
+ 'pageSize' => page_size,
145
+ 'scanTargetIds' => scan_target_ids.each { |scan_target_id| validate_uuid(scan_target_id) },
146
+ 'lang' => language,
147
+ 'cursor' => cursor
148
+ }
149
+
150
+ Enumerator.new do |yielder|
151
+ loop do
152
+ body['page'] += 1
153
+ data = @http.request('POST', '/alerts/history', body.compact)
154
+ break if data['data'].empty?
155
+
156
+ body['cursor'] = data['data'].last['cursor']
157
+ data['data'].each do |e|
158
+ yielder.yield e
159
+ end
160
+ end
161
+ end
162
+ end
163
+
164
+ # Alerts Following History Enumerator of an organization by loading them, transparently paginating on the API
165
+ # [#reference](https://api.zanshin.tenchisecurity.com/#operation/listAllAlertsHistoryFollowing)
166
+ #
167
+ # @param organization_id [UUID] of the organization
168
+ # @param following_ids [Array<UUID>] Optional list of IDs of organizations you are following to
169
+ # list alerts from, defaults to all
170
+ # @param page_size [Integer] the number of alerts to load from the API at a time
171
+ # @param language ['en-US', 'pt-BR'] Language of the rule will be returned
172
+ # @param cursor [String] Alert Cursor of the last alert consumed, when this value is passed, subsequent
173
+ # alert histories will be returned
174
+ #
175
+ # @return an Alerts Following History Enumerator object
176
+ def iter_alerts_following_history(organization_id,
177
+ following_ids: [],
178
+ page_size: 100,
179
+ language: nil,
180
+ cursor: nil)
181
+ body = {
182
+ 'organizationId' => validate_uuid(organization_id),
183
+ 'page' => 0,
184
+ 'pageSize' => page_size,
185
+ 'followingIds' => following_ids.each { |following_id| validate_uuid(following_id) },
186
+ 'lang' => language,
187
+ 'cursor' => cursor
188
+ }
189
+
190
+ Enumerator.new do |yielder|
191
+ loop do
192
+ body['page'] += 1
193
+ data = @http.request('POST', '/alerts/history/following', body.compact)
194
+ break if data['data'].empty?
195
+
196
+ body['cursor'] = data['data'].last['cursor']
197
+ data['data'].each do |e|
198
+ yielder.yield e
199
+ end
200
+ end
201
+ end
202
+ end
203
+
204
+ # Grouped Alerts Enumerator of an organization by loading them, transparently paginating on the API
205
+ # [#reference](https://api.zanshin.tenchisecurity.com/#operation/listAllAlertRules)
206
+ #
207
+ # @param organization_id [UUID] of the organization
208
+ # @param scan_target_ids [Array<UUID>] Optional list of scan target IDs to list alerts from, defaults to all
209
+ # @param states [Array<'OPEN', 'ACTIVE', 'IN_PROGRESS', 'RISK_ACCEPTED', 'CLOSED'>] Optional list of states to
210
+ # filter returned alerts, defaults to all
211
+ # @param severities [Array<'CRITICAL', 'HIGH', 'MEDIUM', 'LOW', 'INFO'>] optional list of severities to filter
212
+ # returned alerts, defaults to all
213
+ # @param page_size [Integer] the number of alerts to load from the API at a time
214
+ #
215
+ # @return an Grouped Alerts Enumerator object
216
+ def iter_grouped_alerts(organization_id,
217
+ scan_target_ids: [],
218
+ states: nil,
219
+ severities: nil,
220
+ page_size: 100)
221
+ body = {
222
+ 'organizationId' => validate_uuid(organization_id),
223
+ 'page' => 0,
224
+ 'pageSize' => page_size,
225
+ 'scanTargetIds' => scan_target_ids.each { |scan_target_id| validate_uuid(scan_target_id) },
226
+ 'states' => states,
227
+ 'severities' => severities
228
+ }
229
+
230
+ Enumerator.new do |yielder|
231
+ loop do
232
+ body['page'] += 1
233
+ data = @http.request('POST', '/alerts/rules', body.compact)
234
+ data['data'].each do |e|
235
+ yielder.yield e
236
+ end
237
+ break if body['page'] == (data['total'] / body['pageSize']).ceil
238
+ end
239
+ end
240
+ end
241
+
242
+ # Grouped Alerts Following Enumerator of an organization by loading them, transparently paginating on the API
243
+ # [#reference](https://api.zanshin.tenchisecurity.com/#operation/listAllAlertRulesFollowing)
244
+ #
245
+ # @param organization_id [UUID] of the organization
246
+ # @param following_ids [Array<UUID>] Optional list of IDs of organizations you are following to
247
+ # list alerts from, defaults to all
248
+ # @param states [Array<'OPEN', 'ACTIVE', 'IN_PROGRESS', 'RISK_ACCEPTED', 'CLOSED'>] Optional list of states to
249
+ # filter returned alerts, defaults to all
250
+ # @param severities [Array<'CRITICAL', 'HIGH', 'MEDIUM', 'LOW', 'INFO'>] optional list of severities to filter
251
+ # returned alerts, defaults to all
252
+ # @param page_size [Integer] the number of alerts to load from the API at a time
253
+ #
254
+ # @return an Grouped Alerts Following Enumerator object
255
+ def iter_grouped_following_alerts(organization_id,
256
+ following_ids: [],
257
+ states: nil,
258
+ severities: nil,
259
+ page_size: 100)
260
+ body = {
261
+ 'organizationId' => validate_uuid(organization_id),
262
+ 'page' => 0,
263
+ 'pageSize' => page_size,
264
+ 'followingIds' => following_ids.each { |following_id| validate_uuid(following_id) },
265
+ 'states' => states,
266
+ 'severities' => severities
267
+ }
268
+
269
+ Enumerator.new do |yielder|
270
+ loop do
271
+ body['page'] += 1
272
+ data = @http.request('POST', '/alerts/rules/following', body.compact)
273
+ data['data'].each do |e|
274
+ yielder.yield e
275
+ end
276
+ break if body['page'] == (data['total'] / body['pageSize']).ceil
277
+ end
278
+ end
279
+ end
280
+
281
+ # Returns the detailed object that describes an alert
282
+ # [#reference](https://api.zanshin.tenchisecurity.com/#operation/getAlertById)
283
+ #
284
+ # @param alert_id [UUID] of the alert
285
+ #
286
+ # @return a Object representing the alert
287
+ def get_alert(alert_id)
288
+ @http.request('GET', "/alerts/#{validate_uuid(alert_id)}")
289
+ end
290
+
291
+ # Alert History Enumerator over the history of an alert
292
+ # [#reference](https://api.zanshin.tenchisecurity.com/#operation/listAllAlertHistory)
293
+ #
294
+ # @param alert_id [UUID] of the alert
295
+ #
296
+ # @return an Alert History Enumerator object
297
+ def iter_alert_history(alert_id)
298
+ Enumerator.new do |yielder|
299
+ @http.request('GET', "/alerts/#{validate_uuid(alert_id)}/history").each do |e|
300
+ yielder.yield e
301
+ end
302
+ end
303
+ end
304
+
305
+ # Alert Comments Enumerator over the comment of an alert
306
+ # [#reference](https://api.zanshin.tenchisecurity.com/#operation/listAllAlertComments)
307
+ #
308
+ # @param alert_id [UUID] of the alert
309
+ #
310
+ # @return an Alert Comments Enumerator object
311
+ def iter_alert_comments(alert_id)
312
+ Enumerator.new do |yielder|
313
+ @http.request('GET', "/alerts/#{validate_uuid(alert_id)}/comments").each do |e|
314
+ yielder.yield e
315
+ end
316
+ end
317
+ end
318
+
319
+ # Update alert
320
+ # [#reference](https://api.zanshin.tenchisecurity.com/#operation/editOrganizationScanTargetAlertById)
321
+ #
322
+ # @param organization_id [UUID] of the organization
323
+ # @param scan_target_id [UUID] of the scan target
324
+ # @param alert_id [UUID] of the alert
325
+ # @param state ['OPEN', 'ACTIVE', 'IN_PROGRESS', 'RISK_ACCEPTED']
326
+ # @param labels [Array<String>] Optional labels to alert
327
+ # @param comment [String] Accepted and required only when change :state to 'IN_PROGRESS'
328
+ #
329
+ # @return a Object representing the alert updated
330
+ def update_alert(organization_id, scan_target_id, alert_id, state = nil, labels = nil, comment = nil)
331
+ body = {
332
+ 'state' => state,
333
+ 'labels' => labels,
334
+ 'comment' => comment
335
+ }
336
+
337
+ @http.request(
338
+ 'PUT',
339
+ "/organizations/#{validate_uuid(organization_id)}/scantargets/#{
340
+ validate_uuid(scan_target_id)}/alerts/#{validate_uuid(alert_id)}",
341
+ body.compact
342
+ )
343
+ end
344
+
345
+ # Create comment in alert
346
+ # [#reference](https://api.zanshin.tenchisecurity.com/#operation/listAllAlertComments)
347
+ #
348
+ # @param organization_id [UUID] of the organization
349
+ # @param scan_target_id [UUID] of the scan target
350
+ # @param alert_id [UUID] of the alert
351
+ # @param comment [String] in HTML format
352
+ #
353
+ # @return a Object representing the alert comment
354
+ def create_alert_comment(organization_id, scan_target_id, alert_id, comment)
355
+ body = {
356
+ 'comment' => comment
357
+ }
358
+
359
+ @http.request(
360
+ 'POST',
361
+ "/organizations/#{validate_uuid(organization_id)}/scantargets/#{
362
+ validate_uuid(scan_target_id)}/alerts/#{validate_uuid(alert_id)}/comments",
363
+ body.compact
364
+ )
365
+ end
366
+ end
367
+ end
368
+ end