wss_agent 0.0.15
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +14 -0
- data/.rspec +2 -0
- data/.travis.yml +6 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +201 -0
- data/README.md +73 -0
- data/Rakefile +10 -0
- data/bin/wss_agent +13 -0
- data/lib/config/custom_default.yml +5 -0
- data/lib/config/default.yml +11 -0
- data/lib/wss_agent/cli.rb +49 -0
- data/lib/wss_agent/client.rb +59 -0
- data/lib/wss_agent/configure.rb +90 -0
- data/lib/wss_agent/gem_sha1.rb +76 -0
- data/lib/wss_agent/project.rb +45 -0
- data/lib/wss_agent/response.rb +57 -0
- data/lib/wss_agent/response_inventory.rb +28 -0
- data/lib/wss_agent/response_policies.rb +76 -0
- data/lib/wss_agent/specifications.rb +150 -0
- data/lib/wss_agent/version.rb +4 -0
- data/lib/wss_agent.rb +40 -0
- data/spec/fixtures/vcr_cassettes/WssAgent_CLI/update/when_not_found_token/should_display_error_message.yml +2984 -0
- data/spec/fixtures/vcr_cassettes/WssAgent_Client/_update/server_error/response_should_be_success.yml +2984 -0
- data/spec/fixtures/vcr_cassettes/WssAgent_Client/_update/server_error/should_response_json_data.yml +2984 -0
- data/spec/fixtures/vcr_cassettes/WssAgent_Client/_update/server_error/should_return_message_response.yml +2984 -0
- data/spec/fixtures/vcr_cassettes/WssAgent_Client/_update/server_error/should_return_status_of_response.yml +2984 -0
- data/spec/fixtures/vcr_cassettes/WssAgent_Client/_update/server_timeout/response_should_be_success.yml +2984 -0
- data/spec/fixtures/vcr_cassettes/WssAgent_Client/_update/server_timeout/should_response_json_data.yml +2984 -0
- data/spec/fixtures/vcr_cassettes/WssAgent_Client/_update/server_timeout/should_return_message_response.yml +2984 -0
- data/spec/fixtures/vcr_cassettes/WssAgent_Client/_update/server_timeout/should_return_status_of_response.yml +2984 -0
- data/spec/fixtures/vcr_cassettes/WssAgent_Client/_update/success/response_should_be_success.yml +2984 -0
- data/spec/fixtures/vcr_cassettes/WssAgent_Client/_update/success/should_response_json_data.yml +2984 -0
- data/spec/fixtures/vcr_cassettes/WssAgent_Client/_update/success/should_return_message_response.yml +2984 -0
- data/spec/fixtures/vcr_cassettes/WssAgent_Client/_update/success/should_return_status_of_response.yml +2984 -0
- data/spec/fixtures/vcr_cassettes/WssAgent_Specifications/_check_policies/should_check_policies.yml +50 -0
- data/spec/fixtures/vcr_cassettes/WssAgent_Specifications/_update/should_update_list_gems_on_server.yml +50 -0
- data/spec/fixtures/vcr_cassettes/WssAgent_Specifications/_update/when_check_policies_is_true/and_check_policies_return_a_violation/should_not_update_inventory.yml +2984 -0
- data/spec/fixtures/vcr_cassettes/WssAgent_Specifications/_update/when_check_policies_is_true/and_check_policies_returns_without_a_violation/should_update_inventory.yml +2984 -0
- data/spec/fixtures/wss_agent.yml +9 -0
- data/spec/spec_helper.rb +35 -0
- data/spec/wss_agent/cli_spec.rb +57 -0
- data/spec/wss_agent/client_spec.rb +137 -0
- data/spec/wss_agent/configure_spec.rb +148 -0
- data/spec/wss_agent/specifications_spec.rb +137 -0
- data/wss_agent.gemspec +38 -0
- metadata +390 -0
@@ -0,0 +1,57 @@
|
|
1
|
+
module WssAgent
|
2
|
+
class Response
|
3
|
+
SUCCESS_STATUS = 1
|
4
|
+
BAD_REQUEST_STATUS = 2
|
5
|
+
SERVER_ERROR_STATUS = 3
|
6
|
+
|
7
|
+
attr_reader :response, :status, :message, :response_data, :data
|
8
|
+
|
9
|
+
def initialize(response)
|
10
|
+
@response = response
|
11
|
+
if response.is_a?(Faraday::Error::ClientError)
|
12
|
+
parse_error
|
13
|
+
else
|
14
|
+
parse_response
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def parse_error
|
19
|
+
@status = SERVER_ERROR_STATUS
|
20
|
+
@message = response.message
|
21
|
+
end
|
22
|
+
|
23
|
+
def parse_response
|
24
|
+
if response.success?
|
25
|
+
begin
|
26
|
+
@response_data = Oj.load(response.body)
|
27
|
+
@status = @response_data['status'].to_i
|
28
|
+
@message = @response_data['message']
|
29
|
+
rescue
|
30
|
+
@status = SERVER_ERROR_STATUS
|
31
|
+
@message = response.body
|
32
|
+
end
|
33
|
+
else
|
34
|
+
@status = SERVER_ERROR_STATUS
|
35
|
+
@message = response.body
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def response_success?
|
40
|
+
if response.is_a?(Faraday::Error::ClientError)
|
41
|
+
false
|
42
|
+
else
|
43
|
+
response.success?
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def success?
|
48
|
+
response_success? && status == SUCCESS_STATUS
|
49
|
+
end
|
50
|
+
|
51
|
+
def data
|
52
|
+
@data ||= Oj.load(response_data['data'])
|
53
|
+
rescue
|
54
|
+
response_data && response_data.key?('data') ? response_data['data'] : nil
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module WssAgent
|
2
|
+
class ResponseInventory < Response
|
3
|
+
|
4
|
+
def message
|
5
|
+
if success?
|
6
|
+
@message = "White Source update results: \n"
|
7
|
+
@message << " White Source organization: #{data['organization']} \n"
|
8
|
+
|
9
|
+
unless data['createdProjects'].empty?
|
10
|
+
@message << " #{data['createdProjects'].size} newly created projects: "
|
11
|
+
@message << data['createdProjects'].join(' ')
|
12
|
+
else
|
13
|
+
@message << " No new projects found \n"
|
14
|
+
end
|
15
|
+
|
16
|
+
unless data['updatedProjects'].empty?
|
17
|
+
@message << " #{data['updatedProjects'].size} existing projects were updated: "
|
18
|
+
@message << data['updatedProjects'].join(' ')
|
19
|
+
else
|
20
|
+
@message << "\n No projects were updated \n"
|
21
|
+
end
|
22
|
+
@message
|
23
|
+
else
|
24
|
+
super
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
module WssAgent
|
2
|
+
class ResponsePolicies < Response
|
3
|
+
REJECT_ACTION = 'Reject'
|
4
|
+
|
5
|
+
def parse_response
|
6
|
+
if response.success?
|
7
|
+
begin
|
8
|
+
@response_data = Oj.load(response.body)
|
9
|
+
@status = @response_data['status'].to_i
|
10
|
+
@message = @response_data['message']
|
11
|
+
check_new_projects
|
12
|
+
check_existing_projects
|
13
|
+
rescue
|
14
|
+
@status = SERVER_ERROR_STATUS
|
15
|
+
@message = response.body
|
16
|
+
end
|
17
|
+
else
|
18
|
+
@status = SERVER_ERROR_STATUS
|
19
|
+
@message = response.body
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
|
24
|
+
def message
|
25
|
+
if success?
|
26
|
+
if policy_violations?
|
27
|
+
@message = [
|
28
|
+
'Some dependencies do not conform with open source policies',
|
29
|
+
'List of violations:'
|
30
|
+
]
|
31
|
+
@message << policy_violations.each_with_index.map { |j, i|
|
32
|
+
"#{i+1}. Package: #{j['resource']['displayName']} - #{j['policy']['displayName']}"
|
33
|
+
}.join("\n")
|
34
|
+
@message.join("\n")
|
35
|
+
else
|
36
|
+
"All dependencies conform with open source policies"
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def policy_violations
|
42
|
+
@policy_violations || []
|
43
|
+
end
|
44
|
+
|
45
|
+
def policy_violations?
|
46
|
+
!policy_violations.nil? &&
|
47
|
+
!policy_violations.empty? &&
|
48
|
+
policy_violations.size > 0
|
49
|
+
end
|
50
|
+
|
51
|
+
def check_existing_projects
|
52
|
+
data['existingProjects'].each { |_proj_name, resource| check(resource) }
|
53
|
+
end
|
54
|
+
|
55
|
+
def check_new_projects
|
56
|
+
data['newProjects'].each { |_proj_name, resource| check(resource) }
|
57
|
+
end
|
58
|
+
|
59
|
+
def add_resource(resource)
|
60
|
+
@policy_violations ||= []
|
61
|
+
@policy_violations << resource
|
62
|
+
end
|
63
|
+
|
64
|
+
def check(resource)
|
65
|
+
if resource.key?('resource') && resource.key?('policy') &&
|
66
|
+
(resource['policy']['actionType'] == REJECT_ACTION)
|
67
|
+
add_resource({'resource' => resource['resource'],
|
68
|
+
'policy' => resource['policy']})
|
69
|
+
end
|
70
|
+
|
71
|
+
if resource.key?('children') && resource['children'].is_a?(Array)
|
72
|
+
resource['children'].each { |j| check(j) }
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,150 @@
|
|
1
|
+
require "digest"
|
2
|
+
|
3
|
+
module WssAgent
|
4
|
+
class Specifications
|
5
|
+
|
6
|
+
class << self
|
7
|
+
# Get dependencies
|
8
|
+
#
|
9
|
+
# @param [Hash]
|
10
|
+
# @option options [Boolean] 'all' if true then get all dependencies (include development dependencies)
|
11
|
+
# @option options [String] 'excludes' list gem name which need to exclude from end list
|
12
|
+
def specs(options = {})
|
13
|
+
list_gems = Bundler.load.specs.to_a
|
14
|
+
if options['all']
|
15
|
+
# get all gems
|
16
|
+
list = {}
|
17
|
+
list_gems.each { |j| list[j.name] = j }
|
18
|
+
list_gems.each { |j|
|
19
|
+
list = gem_dependencies(list, j.dependencies, options)
|
20
|
+
}
|
21
|
+
|
22
|
+
list_gems = list.values
|
23
|
+
end
|
24
|
+
list_gems
|
25
|
+
end
|
26
|
+
|
27
|
+
# Display list dependencies
|
28
|
+
#
|
29
|
+
# @param (see Specifications#specs)
|
30
|
+
def list(options = {})
|
31
|
+
new(specs(options)).call
|
32
|
+
end
|
33
|
+
|
34
|
+
# Send gem list to server
|
35
|
+
#
|
36
|
+
# @param (see Specifications#specs)
|
37
|
+
def update(options = {})
|
38
|
+
wss_client = WssAgent::Client.new
|
39
|
+
if WssAgent::Configure['check_policies']
|
40
|
+
policy_results = wss_client.check_policies(WssAgent::Specifications.list(options))
|
41
|
+
if policy_results.success? && policy_results.policy_violations?
|
42
|
+
puts policy_results.message
|
43
|
+
return false
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
result = wss_client.update(WssAgent::Specifications.list(options))
|
48
|
+
if result.success?
|
49
|
+
WssAgent.logger.debug result.data
|
50
|
+
puts result.message
|
51
|
+
else
|
52
|
+
ap "synchronization errors occur: status: #{result.status}, message: #{result.message}"
|
53
|
+
end
|
54
|
+
|
55
|
+
result.success?
|
56
|
+
end
|
57
|
+
|
58
|
+
# checking dependencies that they conforms with company policy.
|
59
|
+
#
|
60
|
+
# @param (see Specifications#specs)
|
61
|
+
def check_policies(options = {})
|
62
|
+
wss_client = WssAgent::Client.new
|
63
|
+
result = wss_client.check_policies(WssAgent::Specifications.list(options))
|
64
|
+
if result.success?
|
65
|
+
WssAgent.logger.debug result.data
|
66
|
+
puts result.message
|
67
|
+
else
|
68
|
+
ap "check policies errors occur: #{result.status}, message: #{result.message}, data: #{result.data}"
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
# Get all dependencies includes development
|
73
|
+
#
|
74
|
+
# @param [Array<Spec>] array for gems
|
75
|
+
# @param [Array<Dependencies>]
|
76
|
+
# @param [Hash]
|
77
|
+
# @options options [String] :excludes list gems to exclude
|
78
|
+
#
|
79
|
+
# @return [Array<Spec>] list
|
80
|
+
def gem_dependencies(list, gem_dependencies, options = {})
|
81
|
+
gem_dependencies.each do |gd|
|
82
|
+
if options['excludes'] && options['excludes'].to_s.split(',').include?(gd.name)
|
83
|
+
next
|
84
|
+
end
|
85
|
+
gs = gd.matching_specs.first
|
86
|
+
if gs
|
87
|
+
unless list[gs.name]
|
88
|
+
list[gs.name] = gs
|
89
|
+
unless gs.dependencies.empty?
|
90
|
+
list = gem_dependencies(list, gs.dependencies, options)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
else
|
94
|
+
unless list[gd.name]
|
95
|
+
list[gd.name] = Gem::Specification.new(gd.name,
|
96
|
+
gd.requirements_list.last.scan(/[\d\.\w]+/).first)
|
97
|
+
rm_dep = remote_dependencies(gd.name, gd.requirements_list.last)
|
98
|
+
unless rm_dep.empty?
|
99
|
+
list = gem_dependencies(list, rm_dep, options)
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
list
|
106
|
+
end
|
107
|
+
|
108
|
+
# Load dependencies from rubygems
|
109
|
+
#
|
110
|
+
# @param gem_name [String] name gem
|
111
|
+
# @params version [String] version gem
|
112
|
+
#
|
113
|
+
# @return [Array<Gem::Dependency>] list gem dependencies
|
114
|
+
def remote_dependencies(gem_name, version)
|
115
|
+
conn = Faraday.new(url: 'https://rubygems.org') do |h|
|
116
|
+
h.headers[:content_type] = 'application/x-www-form-urlencoded'
|
117
|
+
h.request :url_encoded
|
118
|
+
h.adapter :excon
|
119
|
+
end
|
120
|
+
response = conn.get("/api/v1/gems/#{gem_name}.json")
|
121
|
+
dep_list = Oj.load(response.body)
|
122
|
+
dep_list['dependencies'].values.flatten.
|
123
|
+
map { |j| Gem::Dependency.new(j['name'], Gem::Requirement.new(j['requirements'].split(','))) }
|
124
|
+
end
|
125
|
+
end # end class << self
|
126
|
+
|
127
|
+
def initialize(gem_specs)
|
128
|
+
@gem_specs = gem_specs
|
129
|
+
end
|
130
|
+
|
131
|
+
def call
|
132
|
+
@gem_specs.map do |spec|
|
133
|
+
next if spec.name == WssAgent::NAME
|
134
|
+
gem_item(spec)
|
135
|
+
end.compact
|
136
|
+
end
|
137
|
+
|
138
|
+
def gem_item(spec)
|
139
|
+
{
|
140
|
+
'groupId' => spec.name,
|
141
|
+
'artifactId' => spec.file_name,
|
142
|
+
'version' => spec.version.to_s,
|
143
|
+
'sha1' => GemSha1.new(spec).sha1,
|
144
|
+
'optional' => false,
|
145
|
+
'children' => [],
|
146
|
+
'exclusions' => []
|
147
|
+
}
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
data/lib/wss_agent.rb
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'thor'
|
2
|
+
require 'net/http'
|
3
|
+
require 'awesome_print'
|
4
|
+
require 'yaml'
|
5
|
+
require 'oj'
|
6
|
+
require 'faraday'
|
7
|
+
require 'faraday_middleware'
|
8
|
+
require 'yell'
|
9
|
+
require 'wss_agent/version'
|
10
|
+
require 'wss_agent/specifications'
|
11
|
+
require 'wss_agent/configure'
|
12
|
+
require 'wss_agent/cli'
|
13
|
+
require 'wss_agent/response'
|
14
|
+
require 'wss_agent/response_policies'
|
15
|
+
require 'wss_agent/response_inventory'
|
16
|
+
require 'wss_agent/client'
|
17
|
+
require 'wss_agent/gem_sha1'
|
18
|
+
require 'wss_agent/project'
|
19
|
+
|
20
|
+
|
21
|
+
module WssAgent
|
22
|
+
# Your code goes here...
|
23
|
+
|
24
|
+
class WssAgentError < StandardError
|
25
|
+
def self.status_code(code)
|
26
|
+
define_method(:status_code) { code }
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
class TokenNotFound < WssAgentError; status_code(10) ; end
|
31
|
+
class ApiUrlNotFound < WssAgentError; status_code(11) ; end
|
32
|
+
|
33
|
+
def self.logger
|
34
|
+
@logger ||= Yell.new STDOUT, level: [:info]
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.enable_debug!
|
38
|
+
@logger ||= Yell.new STDOUT, level: [:debug, :info, :warn, :error, :fatal, :unknown]
|
39
|
+
end
|
40
|
+
end
|