fluent-plugin-jfrog-siem 2.0.5 → 2.0.6

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.
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