fluent-plugin-jfrog-siem 2.0.5 → 2.0.6

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 602f632100f31aa06677f7f5ce98def8d1e51aa43d883cc18e6c8495f80b79dc
4
- data.tar.gz: 8d28142729182b919f69ce6893928477e605aa658ae09f816f0d3f6e5dedace6
3
+ metadata.gz: fa44f9a815aa280de6977f666e2da6ea8be2bae24cc8c7365430cbfd6bc3496b
4
+ data.tar.gz: ad2eec36ca931720158e21e9abcf7d99e4c32618365ef300ca465df59339e307
5
5
  SHA512:
6
- metadata.gz: 3b1e9b8c84c67e39bcc5eb935ff498bda659f1ee360caf2967b062e3b24a1874308beb831f9fcd57b985c0eb7dd865b286064eeba1ccc7f93b70f2864a87dc51
7
- data.tar.gz: 4955cc838617021f7c3c84268af0840cee892ce17fdeafbaa41e67127fd08685cb0d20961c57d47c9b96a76b3d6371ad6ddb9b7ec5de27439a402439c97ff5b1
6
+ metadata.gz: f7cca01dc886183ad29fb99151f0c25f56900cce252f4c17eb88c5a4ec2e9c4dc9571049540b2dc5e1d36b5db612f4ee347d063e6c26a3d2506586a35b04ebf0
7
+ data.tar.gz: 7691c4e9dd2bdb3c6df6e562bd497842de79b086c46f28af0924b19c021350effa2b382e3cb8ab948af1a30ea6a52e470ba4467e4e9adcea38157fde9cb65dde
@@ -3,7 +3,7 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3
3
 
4
4
  Gem::Specification.new do |spec|
5
5
  spec.name = "fluent-plugin-jfrog-siem"
6
- spec.version = "2.0.5"
6
+ spec.version = "2.0.6"
7
7
  spec.authors = ["Mahitha Byreddy", "Sudhindra Rao","Giridharan Ramasamy"]
8
8
  spec.email = ["mahithab@jfrog.com", "sudhindrar@jfrog.com", "girir@jfrog.com"]
9
9
 
@@ -29,7 +29,7 @@ Gem::Specification.new do |spec|
29
29
  spec.add_development_dependency 'rspec', '~> 3.10.0'
30
30
 
31
31
  spec.add_runtime_dependency "rest-client", "~> 2.0"
32
- spec.add_runtime_dependency "concurrent-ruby", "~> 1.1.8"
32
+ spec.add_runtime_dependency "concurrent-ruby", "~> 1.1.8" , "< 1.1.10"
33
33
  spec.add_runtime_dependency "concurrent-ruby-edge", '>= 0'
34
34
  spec.add_runtime_dependency "fluentd", [">= 0.14.10", "< 2"]
35
35
  end
@@ -118,9 +118,11 @@ module Fluent
118
118
  def get_last_item_create_date()
119
119
  recent_pos_file = get_recent_pos_file()
120
120
  if recent_pos_file != nil
121
+ puts "Position file already exists so pulling the latest create_date from it"
121
122
  last_created_date_string = IO.readlines(recent_pos_file).last
122
123
  return DateTime.parse(last_created_date_string).strftime("%Y-%m-%dT%H:%M:%SZ")
123
124
  else
125
+ puts "Position file doesn't exist so fetching current DateTime to form a new position file"
124
126
  return DateTime.now.strftime("%Y-%m-%dT%H:%M:%SZ")
125
127
  end
126
128
  end
@@ -1,186 +1,195 @@
1
- require 'concurrent'
2
- require 'concurrent-edge'
3
- require 'json'
4
- require "fluent/plugin/position_file"
5
-
6
- class Xray
7
- def initialize(jpd_url, username, api_key, token, wait_interval, batch_size, pos_file_path, router, tag)
8
- @jpd_url = jpd_url
9
- @username = username
10
- @api_key = api_key
11
- @token = token
12
- @wait_interval = wait_interval
13
- @batch_size = batch_size
14
- @pos_file_path = pos_file_path
15
- @router = router
16
- @tag = tag
17
- end
18
-
19
- def violations(date_since)
20
- violations_channel = Concurrent::Channel.new(capacity: @batch_size)
21
- page_number = 1
22
- timer_task = Concurrent::TimerTask.new(execution_interval: @wait_interval, timeout_interval: 30) do
23
- xray_json = {"filters": { "created_from": date_since }, "pagination": {"order_by": "created","limit": @batch_size ,"offset": page_number } }
24
- resp = get_violations(xray_json)
25
- page_violation_count = resp['violations'].length
26
- puts "Total violations count is #{resp['total_violations']}"
27
- if resp['total_violations'] > 0
28
- puts "Number of Violations in page #{page_number} are #{page_violation_count}"
29
- resp['violations'].each {|v| violations_channel = process(v, violations_channel) }
30
- page_number += 1 if next_page?(page_violation_count)
31
- end
32
- end
33
- timer_task.execute
34
-
35
- violations_channel
36
- end
37
-
38
- def violation_details(violations_channel)
39
- violations_channel.each do |v|
40
- Concurrent::Promises.future(v) do |v|
41
- pull_violation_details(v['violation_details_url'])
42
- pos_file = PositionFile.new(@pos_file_path)
43
- pos_file.write(v)
44
- end
45
- end
46
- end
47
-
48
- def pull_violation_details(xray_violation_detail_url)
49
- begin
50
- detailResp_json = data_normalization(get_violations_detail(xray_violation_detail_url))
51
- time = Fluent::Engine.now
52
- puts detailResp_json
53
- @router.emit(@tag, time, detailResp_json)
54
- rescue => e
55
- puts "error: #{e}"
56
- raise Fluent::ConfigError, "Error pulling violation details url #{xray_violation_detail_url}: #{e}"
57
- end
58
- end
59
-
60
- def get_violations_detail(xray_violation_detail_url)
61
- if !@token.nil? && @token != ''
62
- response = RestClient::Request.new(
63
- :method => :get,
64
- :url => @jpd_url + xray_violation_detail_url[xray_violation_detail_url.index('/xray/'),xray_violation_detail_url.length],
65
- :headers => { :accept => :json, :content_type => :json, Authorization:'Bearer ' + @token }
66
- )
67
- elsif !@api_key.nil? && @api_key != ''
68
- response = RestClient::Request.new(
69
- :method => :get,
70
- :url => @jpd_url + xray_violation_detail_url[xray_violation_detail_url.index('/xray/'),xray_violation_detail_url.length],
71
- :user => @username,
72
- :password => @api_key
73
- )
74
- end
75
-
76
- response.execute do |response, request, result|
77
- case response.code
78
- when 200
79
- return JSON.parse(response.to_s)
80
- else
81
- puts "error: #{response.to_json}"
82
- raise Fluent::ConfigError, "Cannot reach Artifactory URL to pull Xray SIEM violations details."
83
- end
84
- end
85
- end
86
-
87
- def data_normalization(detailResp_json)
88
- cve = []
89
- cvss_v2_list = []
90
- cvss_v3_list = []
91
- policy_list = []
92
- rule_list = []
93
- impacted_artifact_url_list = []
94
- if detailResp_json.key?('properties')
95
- properties = detailResp_json['properties']
96
- for index in 0..properties.length-1 do
97
- if properties[index].key?('cve')
98
- cve.push(properties[index]['cve'])
99
- end
100
- if properties[index].key?('cvss_v2')
101
- cvss_v2_list.push(properties[index]['cvss_v2'])
102
- end
103
- if properties[index].key?('cvss_v3')
104
- cvss_v3_list.push(properties[index]['cvss_v3'])
105
- end
106
- end
107
-
108
- detailResp_json["cve"] = cve.sort.reverse[0]
109
- cvss_v2 = cvss_v2_list.sort.reverse[0]
110
- cvss_v3 = cvss_v3_list.sort.reverse[0]
111
- if !cvss_v3.nil?
112
- cvss = cvss_v3
113
- elsif !cvss_v2.nil?
114
- cvss = cvss_v2
115
- end
116
- cvss_score = cvss[0..2]
117
- cvss_version = cvss.split(':')[1][0..2]
118
- detailResp_json["cvss_score"] = cvss_score
119
- detailResp_json["cvss_version"] = cvss_version
120
- end
121
-
122
- if detailResp_json.key?('matched_policies')
123
- matched_policies = detailResp_json['matched_policies']
124
- for index in 0..matched_policies.length-1 do
125
- if matched_policies[index].key?('policy')
126
- policy_list.push(matched_policies[index]['policy'])
127
- end
128
- if matched_policies[index].key?('rule')
129
- rule_list.push(matched_policies[index]['rule'])
130
- end
131
- end
132
- detailResp_json['policies'] = policy_list
133
- detailResp_json['rules'] = rule_list
134
- end
135
-
136
- detailResp_json['impacted_artifacts'].each do |impacted_artifact|
137
- matchdata = impacted_artifact.match /default\/(?<repo_name>[^\/]*)\/(?<path>.*)/
138
- impacted_artifact_url = matchdata['repo_name'] + ":" + matchdata['path'] + " "
139
- impacted_artifact_url_list.append(impacted_artifact_url)
140
- end
141
- detailResp_json['impacted_artifacts_url'] = impacted_artifact_url_list
142
- return detailResp_json
143
- end
144
-
145
- def process(violation, violations_channel)
146
- pos_file = PositionFile.new(@pos_file_path)
147
- violations_channel << violation unless pos_file.processed?(violation)
148
- violations_channel
149
- end
150
-
151
- private
152
- def get_violations(xray_json)
153
- if !@token.nil? && @token != ''
154
- response = RestClient::Request.new(
155
- :method => :post,
156
- :url => @jpd_url + "/xray/api/v1/violations",
157
- :payload => xray_json.to_json,
158
- :headers => { :accept => :json, :content_type => :json, Authorization:'Bearer ' + @token }
159
- )
160
- elsif !@api_key.nil? && @api_key != ''
161
- response = RestClient::Request.new(
162
- :method => :post,
163
- :url => @jpd_url + "/xray/api/v1/violations",
164
- :payload => xray_json.to_json,
165
- :user => @username,
166
- :password => @api_key,
167
- :headers => { :accept => :json, :content_type => :json }
168
- )
169
- end
170
- response.execute do |response, request, result|
171
- case response.code
172
- when 200
173
- return JSON.parse(response.to_str)
174
- else
175
- puts "error: #{response.to_json}"
176
- raise Fluent::ConfigError, "Cannot reach Artifactory URL to pull Xray SIEM violations. #{response.to_json}"
177
- end
178
- end
179
- end
180
-
181
- def next_page?(count)
182
- count == @batch_size
183
- end
184
-
185
- end
186
-
1
+ require 'concurrent'
2
+ require 'concurrent-edge'
3
+ require 'json'
4
+ require "fluent/plugin/position_file"
5
+
6
+ class Xray
7
+ def initialize(jpd_url, username, api_key, token, wait_interval, batch_size, pos_file_path, router, tag)
8
+ @jpd_url = jpd_url
9
+ @username = username
10
+ @api_key = api_key
11
+ @token = token
12
+ @wait_interval = wait_interval
13
+ @batch_size = batch_size
14
+ @pos_file_path = pos_file_path
15
+ @router = router
16
+ @tag = tag
17
+ end
18
+
19
+ def violations(date_since)
20
+ violations_channel = Concurrent::Channel.new(capacity: @batch_size)
21
+ page_number = 1
22
+ timer_task = Concurrent::TimerTask.new(execution_interval: @wait_interval, timeout_interval: 30) do
23
+ xray_json = {"filters": { "created_from": date_since }, "pagination": {"order_by": "created","limit": @batch_size ,"offset": page_number } }
24
+ puts "Fetching Xray Violations with #{xray_json} parameters"
25
+ resp = get_violations(xray_json)
26
+ page_violation_count = resp['violations'].length
27
+ puts "Total violations count is #{resp['total_violations']}"
28
+ if resp['total_violations'] > 0
29
+ puts "Number of Violations in page #{page_number} are #{page_violation_count}"
30
+ resp['violations'].each {|v| violations_channel = process(v, violations_channel) }
31
+ page_number += 1 if next_page?(page_violation_count)
32
+ end
33
+ end
34
+ timer_task.execute
35
+
36
+ violations_channel
37
+ end
38
+
39
+ def violation_details(violations_channel)
40
+ violations_channel.each do |v|
41
+ Concurrent::Promises.future(v) do |v|
42
+ process_violation_details(v['violation_details_url'])
43
+ pos_file = PositionFile.new(@pos_file_path)
44
+ puts "Adding issue #{v['issue_id']} to position file at #{@pos_file_path}"
45
+ pos_file.write(v)
46
+ end
47
+ end
48
+ end
49
+
50
+ def process_violation_details(xray_violation_detail_url)
51
+ begin
52
+ detailResp_json = data_normalization(get_violations_detail(xray_violation_detail_url))
53
+ time = Fluent::Engine.now
54
+ puts "Emitting normalized Xray Violation #{detailResp_json['issue_id']}"
55
+ @router.emit(@tag, time, detailResp_json)
56
+ rescue => e
57
+ puts "Process Violation details error: #{e}"
58
+ raise Fluent::ConfigError, "Process Violation details error: #{e}"
59
+ end
60
+ end
61
+
62
+ def get_violations_detail(xray_violation_detail_url)
63
+ if !@token.nil? && @token != ''
64
+ response = RestClient::Request.new(
65
+ :method => :get,
66
+ :url => @jpd_url + xray_violation_detail_url[xray_violation_detail_url.index('/xray/'),xray_violation_detail_url.length],
67
+ :headers => { :accept => :json, :content_type => :json, Authorization:'Bearer ' + @token }
68
+ )
69
+ elsif !@api_key.nil? && @api_key != ''
70
+ response = RestClient::Request.new(
71
+ :method => :get,
72
+ :url => @jpd_url + xray_violation_detail_url[xray_violation_detail_url.index('/xray/'),xray_violation_detail_url.length],
73
+ :user => @username,
74
+ :password => @api_key
75
+ )
76
+ end
77
+
78
+ response.execute do |response, request, result|
79
+ case response.code
80
+ when 200
81
+ return JSON.parse(response.to_s)
82
+ else
83
+ puts "Validation failed error (cannot reach Artifactory to pull Xray Violation details): #{response.to_json}"
84
+ raise Fluent::ConfigError, "Validation failed error (cannot reach Artifactory to pull Xray Violation details): #{response.to_json}"
85
+ end
86
+ end
87
+ end
88
+
89
+ def data_normalization(detailResp_json)
90
+ cve = []
91
+ cvss_v2_list = []
92
+ cvss_v3_list = []
93
+ policy_list = []
94
+ rule_list = []
95
+ impacted_artifact_url_list = []
96
+ if detailResp_json.key?('properties')
97
+ properties = detailResp_json['properties']
98
+ for index in 0..properties.length-1 do
99
+ if properties[index].key?('cve')
100
+ cve.push(properties[index]['cve'])
101
+ end
102
+ if properties[index].key?('cvss_v2')
103
+ cvss_v2_list.push(properties[index]['cvss_v2'])
104
+ end
105
+ if properties[index].key?('cvss_v3')
106
+ cvss_v3_list.push(properties[index]['cvss_v3'])
107
+ end
108
+ end
109
+
110
+ detailResp_json["cve"] = cve.sort.reverse[0]
111
+ cvss_v2 = cvss_v2_list.sort.reverse[0]
112
+ cvss_v3 = cvss_v3_list.sort.reverse[0]
113
+ if !cvss_v3.nil?
114
+ cvss = cvss_v3
115
+ elsif !cvss_v2.nil?
116
+ cvss = cvss_v2
117
+ end
118
+ cvss_score = cvss[0..2]
119
+ cvss_version = cvss.split(':')[1][0..2]
120
+ detailResp_json["cvss_score"] = cvss_score
121
+ detailResp_json["cvss_version"] = cvss_version
122
+ end
123
+
124
+ if detailResp_json.key?('matched_policies')
125
+ matched_policies = detailResp_json['matched_policies']
126
+ for index in 0..matched_policies.length-1 do
127
+ if matched_policies[index].key?('policy')
128
+ policy_list.push(matched_policies[index]['policy'])
129
+ end
130
+ if matched_policies[index].key?('rule')
131
+ rule_list.push(matched_policies[index]['rule'])
132
+ end
133
+ end
134
+ detailResp_json['policies'] = policy_list
135
+ detailResp_json['rules'] = rule_list
136
+ end
137
+
138
+ detailResp_json['impacted_artifacts'].each do |impacted_artifact|
139
+ matchdata = impacted_artifact.match /default\/(?<repo_name>[^\/]*)\/(?<path>.*)/
140
+ impacted_artifact_url = matchdata['repo_name'] + ":" + matchdata['path'] + " "
141
+ impacted_artifact_url_list.append(impacted_artifact_url)
142
+ end
143
+ detailResp_json['impacted_artifacts_url'] = impacted_artifact_url_list
144
+ return detailResp_json
145
+ end
146
+
147
+ def process(violation, violations_channel)
148
+ pos_file = PositionFile.new(@pos_file_path)
149
+ unless pos_file.processed?(violation)
150
+ violations_channel << violation
151
+ else
152
+ puts "Violation #{violation['issue_id']} is already processed"
153
+ end
154
+ #violations_channel << violation unless pos_file.processed?(violation)
155
+ violations_channel
156
+ end
157
+
158
+ private
159
+ def get_violations(xray_json)
160
+ if !@token.nil? && @token != ''
161
+ puts "Validating JPD access token and fetching violations"
162
+ response = RestClient::Request.new(
163
+ :method => :post,
164
+ :url => @jpd_url + "/xray/api/v1/violations",
165
+ :payload => xray_json.to_json,
166
+ :headers => { :accept => :json, :content_type => :json, Authorization:'Bearer ' + @token }
167
+ )
168
+ elsif !@api_key.nil? && @api_key != ''
169
+ puts "Validating JPD API Key and fetching violations"
170
+ response = RestClient::Request.new(
171
+ :method => :post,
172
+ :url => @jpd_url + "/xray/api/v1/violations",
173
+ :payload => xray_json.to_json,
174
+ :user => @username,
175
+ :password => @api_key,
176
+ :headers => { :accept => :json, :content_type => :json }
177
+ )
178
+ end
179
+ response.execute do |response, request, result|
180
+ case response.code
181
+ when 200
182
+ return JSON.parse(response.to_str)
183
+ else
184
+ puts "Validation failed error (cannot reach Artifactory to pull Xray Violations): #{response.to_json}"
185
+ raise Fluent::ConfigError, "Validation failed error (cannot reach Artifactory to pull Xray Violations): #{response.to_json}"
186
+ end
187
+ end
188
+ end
189
+
190
+ def next_page?(count)
191
+ count == @batch_size
192
+ end
193
+
194
+ end
195
+
@@ -1,41 +1,41 @@
1
- require "helper"
2
- require "fluent/plugin/in_jfrog_siem.rb"
3
-
4
- class JfrogSiemInputTest < Test::Unit::TestCase
5
- setup do
6
- Fluent::Test.setup
7
- end
8
-
9
- test "failure" do
10
- #flunk
11
- end
12
-
13
- # Default configuration for tests
14
- CONFIG = %[
15
- tag "jfrog.xray.siem.vulnerabilities"
16
- jpd_url "JPDURL"
17
- username "admin"
18
- apikey "APIKEY"
19
- pos_file_path "#{ENV['JF_PRODUCT_DATA_INTERNAL']}/log/"
20
- wait_interval 10
21
- from_date "2016-01-01"
22
- batch_size 25
23
- ]
24
-
25
- private
26
-
27
- def create_driver(conf = CONFIG)
28
- Fluent::Test::Driver::Input.new(Fluent::Plugin::JfrogSiemInput).configure(conf)
29
- end
30
-
31
- sub_test_case 'Testing' do
32
- test 'Testing plugin in_jfrog_siem' do
33
- d = create_driver(CONFIG)
34
- begin
35
- d.run
36
- rescue => e
37
- raise "Test failed due to #{e}"
38
- end
39
- end
40
- end
41
- end
1
+ require "helper"
2
+ require "fluent/plugin/in_jfrog_siem.rb"
3
+
4
+ class JfrogSiemInputTest < Test::Unit::TestCase
5
+ setup do
6
+ Fluent::Test.setup
7
+ end
8
+
9
+ test "failure" do
10
+ #flunk
11
+ end
12
+
13
+ # Default configuration for tests
14
+ CONFIG = %[
15
+ tag "jfrog.xray.siem.vulnerabilities"
16
+ jpd_url "JPDURL"
17
+ username "admin"
18
+ apikey "APIKEY"
19
+ pos_file_path "#{ENV['JF_PRODUCT_DATA_INTERNAL']}/log/"
20
+ wait_interval 10
21
+ from_date "2016-01-01"
22
+ batch_size 25
23
+ ]
24
+
25
+ private
26
+
27
+ def create_driver(conf = CONFIG)
28
+ Fluent::Test::Driver::Input.new(Fluent::Plugin::JfrogSiemInput).configure(conf)
29
+ end
30
+
31
+ sub_test_case 'Testing' do
32
+ test 'Testing plugin in_jfrog_siem' do
33
+ d = create_driver(CONFIG)
34
+ begin
35
+ d.run
36
+ rescue => e
37
+ raise "Test failed due to #{e}"
38
+ end
39
+ end
40
+ end
41
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fluent-plugin-jfrog-siem
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.5
4
+ version: 2.0.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mahitha Byreddy
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2022-12-06 00:00:00.000000000 Z
13
+ date: 2023-03-10 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: bundler
@@ -137,6 +137,9 @@ dependencies:
137
137
  - - "~>"
138
138
  - !ruby/object:Gem::Version
139
139
  version: 1.1.8
140
+ - - "<"
141
+ - !ruby/object:Gem::Version
142
+ version: 1.1.10
140
143
  type: :runtime
141
144
  prerelease: false
142
145
  version_requirements: !ruby/object:Gem::Requirement
@@ -144,6 +147,9 @@ dependencies:
144
147
  - - "~>"
145
148
  - !ruby/object:Gem::Version
146
149
  version: 1.1.8
150
+ - - "<"
151
+ - !ruby/object:Gem::Version
152
+ version: 1.1.10
147
153
  - !ruby/object:Gem::Dependency
148
154
  name: concurrent-ruby-edge
149
155
  requirement: !ruby/object:Gem::Requirement