zanshin 1.0.1

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