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.
@@ -0,0 +1,177 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'scan_target'
4
+
5
+ module Zanshin
6
+ module SDK
7
+ # Zanshin SDK Organization Scan Target
8
+ module OrganizationScanTargets
9
+ ###################################################
10
+ # Organization Scan Targets
11
+ ###################################################
12
+
13
+ # Scan Targets Enumerator of an organization
14
+ # [#reference](https://api.zanshin.tenchisecurity.com/#operation/getOrganizationScanTargets)
15
+ #
16
+ # @param organization_id [UUID] of the organization
17
+ #
18
+ # @return an Scan Targets Enumerator object
19
+ def iter_organization_scan_targets(organization_id)
20
+ Enumerator.new do |yielder|
21
+ @http.request('GET', "/organizations/#{validate_uuid(organization_id)}/scantargets").each do |e|
22
+ yielder.yield e
23
+ end
24
+ end
25
+ end
26
+
27
+ # Create a new scan target in organization
28
+ # [#reference](https://api.zanshin.tenchisecurity.com/#operation/createOrganizationScanTargets)
29
+ #
30
+ # @param organization_id [UUID] of the organization
31
+ # @param name [UUID] of the organization
32
+ # @param credential [ScanTarget::AWS, ScanTarget::Azure, ScanTarget::GCP, ScanTarget::HUAWEI, ScanTarget::DOMAIN]
33
+ # to access the cloud account to be scanned
34
+ # @param schedule [Cron] in cron format
35
+ #
36
+ # @return an Object representing the newly created scan target
37
+ def create_organization_scan_target(organization_id, name, credential, schedule = '0 0 * * *')
38
+ unless [ScanTarget::AWS, ScanTarget::Azure, ScanTarget::GCP,
39
+ ScanTarget::HUAWEI, ScanTarget::DOMAIN].include? credential.class
40
+ raise "#{credential.class} is invalid instance of Zanshin::SDK::ScanTarget classes"
41
+ end
42
+
43
+ body = {
44
+ 'kind' => credential.class::KIND, 'name' => name,
45
+ 'credential' => credential.to_json, 'schedule' => schedule
46
+ }
47
+ @http.request('POST', "/organizations/#{validate_uuid(organization_id)}/scantargets", body)
48
+ end
49
+
50
+ # Get scan target details of organization
51
+ # [#reference](https://api.zanshin.tenchisecurity.com/#operation/getOrganizationScanTargetById)
52
+ #
53
+ # @param organization_id [UUID] of the organization
54
+ # @param scan_target_id [UUID] of the scan target
55
+ #
56
+ # @return a Object representing the scan target
57
+ def get_organization_scan_target(organization_id, scan_target_id)
58
+ @http.request('GET',
59
+ "/organizations/#{validate_uuid(organization_id)}/scantargets/#{validate_uuid(scan_target_id)}")
60
+ end
61
+
62
+ # Update scan target of organization
63
+ # [#reference](https://api.zanshin.tenchisecurity.com/#operation/editOrganizationMembersById)
64
+ #
65
+ # @param organization_id [UUID] of the organization
66
+ # @param scan_target_id [UUID] of the scan target
67
+ # @param name [String] of the scan target
68
+ # @param schedule [Cron] of the schedule
69
+ #
70
+ # @return a Object representing the scan target updated
71
+ def update_organization_scan_target(organization_id, scan_target_id, name = nil, schedule = nil)
72
+ body = {
73
+ 'name' => name,
74
+ 'schedule' => schedule
75
+ }
76
+
77
+ @http.request('PUT',
78
+ "/organizations/#{validate_uuid(organization_id)}/scantargets/#{validate_uuid(scan_target_id)}",
79
+ body)
80
+ end
81
+
82
+ # Delete scan target of organization
83
+ # [#reference](https://api.zanshin.tenchisecurity.com/#operation/deleteOrganizationScanTargetById)
84
+ #
85
+ # @param organization_id [UUID] of the organization
86
+ # @param scan_target_id [UUID] of the scan target
87
+ #
88
+ # @return a Boolean with result
89
+ def delete_organization_scan_target(organization_id, scan_target_id)
90
+ @http.request('DELETE',
91
+ "/organizations/#{validate_uuid(organization_id)}/scantargets/#{validate_uuid(scan_target_id)}")
92
+ end
93
+
94
+ # Starts a scan on the specified scan target
95
+ # [#reference](https://api.zanshin.tenchisecurity.com/#operation/scanOrganizationScanTarget)
96
+ #
97
+ # @param organization_id [UUID] of the organization
98
+ # @param scan_target_id [UUID] of the scan target
99
+ #
100
+ # @return a Boolean with result
101
+ def start_organization_scan_target_scan(organization_id, scan_target_id)
102
+ @http.request(
103
+ 'POST',
104
+ "/organizations/#{validate_uuid(organization_id)}/scantargets/#{validate_uuid(scan_target_id)}/scan"
105
+ )
106
+ end
107
+
108
+ # TODO: API documentation is not up to date, does not have this endpoint
109
+ # After updating you will need to add the reference here
110
+
111
+ # Stop a scan on the specific scan target
112
+ #
113
+ # @param organization_id [UUID] of the organization
114
+ # @param scan_target_id [UUID] of the scan target
115
+ #
116
+ # @return a Boolean with result
117
+ def stop_organization_scan_target_scan(organization_id, scan_target_id)
118
+ @http.request(
119
+ 'POST',
120
+ "/organizations/#{validate_uuid(organization_id)}/scantargets/#{validate_uuid(scan_target_id)}/stop"
121
+ )
122
+ end
123
+
124
+ # Check scan target
125
+ # [#reference](https://api.zanshin.tenchisecurity.com/#operation/checkOrganizationScanTarget)
126
+ #
127
+ # @param organization_id [UUID] of the organization
128
+ # @param scan_target_id [UUID] of the scan target
129
+ #
130
+ # @return a Object representing the scan target
131
+ def check_organization_scan_target(organization_id, scan_target_id)
132
+ @http.request(
133
+ 'POST',
134
+ "/organizations/#{validate_uuid(organization_id)}/scantargets/#{validate_uuid(scan_target_id)}/check"
135
+ )
136
+ end
137
+
138
+ ###################################################
139
+ # Organization Scan Target Scan
140
+ ###################################################
141
+
142
+ # Scans Enumerator of an Scan Target
143
+ # [#reference](https://api.zanshin.tenchisecurity.com/#operation/getOrganizationScanTargetScans)
144
+ #
145
+ # @param organization_id [UUID] of the organization
146
+ # @param scan_target_id [UUID] of the scan target
147
+ #
148
+ # @return an Scans Enumerator object
149
+ def iter_organization_scan_target_scans(organization_id, scan_target_id)
150
+ Enumerator.new do |yielder|
151
+ @http.request(
152
+ 'GET',
153
+ "/organizations/#{validate_uuid(organization_id)}/scantargets/#{validate_uuid(scan_target_id)}/scans"
154
+ ).each do |e|
155
+ yielder.yield e
156
+ end
157
+ end
158
+ end
159
+
160
+ # Get scan of scan target
161
+ # [#reference](https://api.zanshin.tenchisecurity.com/#operation/getOrganizationScanTargetScanSlot)
162
+ #
163
+ # @param organization_id [UUID] of the organization
164
+ # @param scan_target_id [UUID] of the scan target
165
+ # @param scan_id [UUID] of the scan
166
+ #
167
+ # @return a Object representing the scan
168
+ def get_organization_scan_target_scan(organization_id, scan_target_id, scan_id)
169
+ @http.request(
170
+ 'GET',
171
+ "/organizations/#{validate_uuid(organization_id)}/scantargets/#{
172
+ validate_uuid(scan_target_id)}/scans/#{validate_uuid(scan_id)}"
173
+ )
174
+ end
175
+ end
176
+ end
177
+ end
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Zanshin
4
+ module SDK
5
+ # Zanshin SDK Organization
6
+ module Organizations
7
+ ###################################################
8
+ # Organizations
9
+ ###################################################
10
+
11
+ # Organizations Enumerator of current logged user
12
+ # [#reference](https://api.zanshin.tenchisecurity.com/#operation/getOrganizations)
13
+ #
14
+ # @return an Organizations Enumerator object
15
+ def iter_organizations
16
+ Enumerator.new do |yielder|
17
+ @http.request('GET', '/organizations').each do |e|
18
+ yielder.yield e
19
+ end
20
+ end
21
+ end
22
+
23
+ # Gets an organization given its ID
24
+ # [#reference](https://api.zanshin.tenchisecurity.com/#operation/getOrganizationById)
25
+ #
26
+ # @param organization_id [UUID] of the organization
27
+ #
28
+ # @return an Object representing the organization
29
+ def get_organization(organization_id)
30
+ @http.request('GET', "/organizations/#{validate_uuid(organization_id)}")
31
+ end
32
+
33
+ # Gets an organization given its ID
34
+ # [#reference](https://api.zanshin.tenchisecurity.com/#operation/editOrganizationById)
35
+ #
36
+ # @param organization_id [UUID] of the organization
37
+ # @param name [String] Optional name of the organization
38
+ # @param picture [String] Optional picture URL of the organization, accepted formats: jpg, jpeg, png, svg
39
+ # @param email [String] Optional e-mail contact of the organization
40
+ #
41
+ # @return an Object representing the organization
42
+ def update_organization(organization_id, name: nil, picture: nil, email: nil)
43
+ body = {
44
+ 'name' => name,
45
+ 'picture' => picture,
46
+ 'email' => email
47
+ }
48
+ @http.request('PUT', "/organizations/#{validate_uuid(organization_id)}", body)
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'net/http'
4
+ require 'zanshin/request/zanshin_request_error'
5
+ require 'json'
6
+
7
+ module Zanshin
8
+ module SDK
9
+ # Zanshin SDK Request
10
+ module Request
11
+ # Zanshin SDK request HTTPClient
12
+ class HTTPClient
13
+ attr_accessor :headers, :http_client
14
+
15
+ # Initialize a new HTTP connection to the Zanshin API
16
+ # @overload initialize(api_key, api_url, user_agent, proxy_url)
17
+ # @param api_key [String] API key to use
18
+ # @param api_url [String] URL of the Zanshin API
19
+ # @param user_agent [String] User agent to use in requests performed
20
+ # @param proxy_url [String] Optional URL indicating which proxy server to use
21
+ def initialize(api_key, api_url, user_agent, proxy_url = nil)
22
+ @headers = { 'Authorization' => "Bearer #{api_key}",
23
+ 'User-Agent' => user_agent,
24
+ 'Content-Type' => 'application/json' }
25
+ # HACK: Needs to figure out why Net::HTTP is not automatically decoded `gzip` and `deflate` encoding
26
+ # 'Accept-Encoding' => 'gzip, deflate'
27
+
28
+ uri = URI.parse(api_url)
29
+ proxy = URI.parse(proxy_url || '')
30
+
31
+ @http_client = Net::HTTP.new(uri.host, uri.port, proxy.host, proxy.port, proxy.user, proxy.password)
32
+ @http_client.use_ssl = true
33
+ end
34
+
35
+ # Request to Zanshin
36
+ # @overload initialize(api_key, api_url, user_agent, proxy_url)
37
+ # @param method ['GET', 'POST', 'PUT', 'DELETE'] HTTP method to pass along to `:HTTPClient`
38
+ # @param path [String] API path to access
39
+ # @param body [Object] request body to pass along to `:HTTPClient`
40
+ def request(method, path, body = nil)
41
+ body = body.to_json if body
42
+ response = @http_client.send_request(method, path, body, @headers)
43
+ result = JSON.parse(response.body)
44
+ raise ZanshinError.new(response.code, result['message'] || result['errorName']) if response.code.to_i >= 400
45
+
46
+ result
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Zanshin
4
+ module SDK
5
+ module Request
6
+ # Zanshin SDK request ZanshinError
7
+ class ZanshinError < StandardError
8
+ attr_reader :code, :body
9
+
10
+ # Initialize a new Zanshin Error
11
+ # @overload initialize(code, body)
12
+ # @param code [String] of the Error
13
+ # @param body [String] message of the error
14
+ def initialize(code, body)
15
+ @code = code
16
+ @body = body
17
+ super("Error [#{code}] #{body}")
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,114 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'json'
4
+
5
+ module Zanshin
6
+ module SDK
7
+ # Zanshin SDK Scan Target
8
+ #
9
+ # This module contains the classes for each provider that Zanshin supports, and which can be used
10
+ # to create a scan target
11
+ module ScanTarget
12
+ # AWS scan target
13
+ class AWS
14
+ # Type of provider that will be sent to the API
15
+ KIND = 'AWS'
16
+
17
+ attr_accessor :account
18
+
19
+ def initialize(account)
20
+ @account = account
21
+ end
22
+
23
+ # Convert AWS class to json
24
+ def to_json(*_args)
25
+ {
26
+ 'account' => @account
27
+ }
28
+ end
29
+ end
30
+
31
+ # Azure scan target
32
+ class Azure
33
+ # Type of provider that will be sent to the API
34
+ KIND = 'Azure'
35
+
36
+ attr_accessor :application_id, :subscription_id, :directory_id, :secret
37
+
38
+ def initialize(application_id, subscription_id, directory_id, secret)
39
+ @application_id = application_id
40
+ @subscription_id = subscription_id
41
+ @directory_id = directory_id
42
+ @secret = secret
43
+ end
44
+
45
+ # Convert Azure class to json
46
+ def to_json(*_args)
47
+ {
48
+ 'application_id' => @application_id,
49
+ 'subscription_id' => @subscription_id,
50
+ 'directory_id' => @directory_id,
51
+ 'secret' => @secret
52
+ }
53
+ end
54
+ end
55
+
56
+ # GCP scan target
57
+ class GCP
58
+ # Type of provider that will be sent to the API
59
+ KIND = 'GCP'
60
+
61
+ attr_accessor :project_id
62
+
63
+ def initialize(project_id)
64
+ @project_id = project_id
65
+ end
66
+
67
+ # Convert GCP class to json
68
+ def to_json(*_args)
69
+ {
70
+ 'project_id' => @project_id
71
+ }
72
+ end
73
+ end
74
+
75
+ # HUAWEI scan target
76
+ class HUAWEI
77
+ # Type of provider that will be sent to the API
78
+ KIND = 'HUAWEI'
79
+
80
+ attr_accessor :account_id
81
+
82
+ def initialize(account_id)
83
+ @account_id = account_id
84
+ end
85
+
86
+ # Convert HUAWEI class to json
87
+ def to_json(*_args)
88
+ {
89
+ 'account_id' => @account_id
90
+ }
91
+ end
92
+ end
93
+
94
+ # DOMAIN scan target
95
+ class DOMAIN
96
+ # Type of provider that will be sent to the API
97
+ KIND = 'DOMAIN'
98
+
99
+ attr_accessor :domain
100
+
101
+ def initialize(domain)
102
+ @domain = domain
103
+ end
104
+
105
+ # Convert DOMAIN class to json
106
+ def to_json(*_args)
107
+ {
108
+ 'domain' => @domain
109
+ }
110
+ end
111
+ end
112
+ end
113
+ end
114
+ end
@@ -0,0 +1,80 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Zanshin
4
+ module SDK
5
+ # Zanshin SDK Summary
6
+ module Summaries
7
+ ###################################################
8
+ # Summaries
9
+ ###################################################
10
+
11
+ # Gets a summary of the current state of alerts for an organization, both in total and broken down by scan target
12
+ # [#reference](https://api.zanshin.tenchisecurity.com/#operation/alertSummary)
13
+ #
14
+ # @param organization_id [UUID] of the organization whose alert summaries are desired
15
+ # @param scan_target_ids [Array<UUID>] optional list of scan target IDs to summarize alerts from, defaults to all
16
+ #
17
+ # @return a Object representing the alert summaries
18
+ def get_alert_summaries(organization_id, scan_target_ids = [])
19
+ body = {
20
+ 'organizationId' => validate_uuid(organization_id),
21
+ 'scanTargetIds' => scan_target_ids.each { |scan_target_id| validate_uuid(scan_target_id) }
22
+ }
23
+
24
+ @http.request('POST', '/alerts/summaries', body)
25
+ end
26
+
27
+ # Gets a summary of the current state of alerts for followed organizations
28
+ # [#reference](https://api.zanshin.tenchisecurity.com/#operation/alertFollowingSummary)
29
+ #
30
+ # @param organization_id [UUID] of the organization
31
+ # @param following_ids [Array<UUID>] list of IDs of organizations being followed to summarize alerts from
32
+ #
33
+ # @return a Object representing the alert following summaries
34
+ def get_following_alert_summaries(organization_id, following_ids = [])
35
+ body = {
36
+ 'organizationId' => validate_uuid(organization_id),
37
+ 'followingIds' => following_ids.each { |following_id| validate_uuid(following_id) }
38
+ }
39
+
40
+ @http.request('POST', '/alerts/summaries/following', body)
41
+ end
42
+
43
+ # Returns summaries of scan results over a period of time, summarizing number of alerts that changed states
44
+ # [#reference](https://api.zanshin.tenchisecurity.com/#operation/scanSummary)
45
+ #
46
+ # @param organization_id [UUID] of the organization whose alert summaries are desired
47
+ # @param scan_target_ids [Array<UUID>] optional list of scan target IDs to summarize alerts from, defaults to all
48
+ # @param days [Integer] number of days to go back in time in historical search
49
+ #
50
+ # @return a Object representing the scan summaries
51
+ def get_scan_summaries(organization_id, scan_target_ids = [], days = 7)
52
+ body = {
53
+ 'organizationId' => validate_uuid(organization_id),
54
+ 'scanTargetIds' => scan_target_ids.each { |scan_target_id| validate_uuid(scan_target_id) },
55
+ 'daysBefore' => days
56
+ }
57
+
58
+ @http.request('POST', '/alerts/summaries/scans', body)
59
+ end
60
+
61
+ # Gets a summary of the current state of alerts for followed organizations
62
+ # [#reference](https://api.zanshin.tenchisecurity.com/#operation/scanSummaryFollowing)
63
+ #
64
+ # @param organization_id [UUID] of the organization whose alert summaries are desired
65
+ # @param following_ids [Array<UUID>] optional list of IDs of organizations being followed to summarize alerts from
66
+ # @param days [Integer] number of days to go back in time in historical search
67
+ #
68
+ # @return a Object representing the scan summaries
69
+ def get_following_scan_summaries(organization_id, following_ids = [], days = 7)
70
+ body = {
71
+ 'organizationId' => validate_uuid(organization_id),
72
+ 'followingIds' => following_ids.each { |following_id| validate_uuid(following_id) },
73
+ 'daysBefore' => days
74
+ }
75
+
76
+ @http.request('POST', '/alerts/summaries/scans/following', body)
77
+ end
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Zanshin
4
+ module SDK
5
+ # The Zanshin SDK version
6
+ VERSION = '1.0.1'
7
+ end
8
+ end
data/lib/zanshin.rb ADDED
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'zanshin/client'
4
+ require_relative 'zanshin/version'
5
+
6
+ # {include:file:README.md}
7
+ module Zanshin
8
+ # Zanshin SDK
9
+ module SDK
10
+ # The default endpoint for Zanshin API
11
+ ZANSHIN_API = 'https://api.zanshin.tenchi-dev.com'
12
+ # The directory that Zanshin uses to store/get private settings
13
+ CONFIG_DIR = "#{Dir.home}/.tenchi"
14
+ # The file that Zanshin uses to store/get private settings
15
+ CONFIG_FILE = "#{CONFIG_DIR}/config"
16
+ end
17
+ end