zanshin 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
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