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 +4 -4
- data/fluent-plugin-jfrog-siem.gemspec +2 -2
- data/lib/fluent/plugin/in_jfrog_siem.rb +2 -0
- data/lib/fluent/plugin/xray.rb +195 -186
- data/test/plugin/test_in_jfrog_siem.rb +41 -41
- metadata +8 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: fa44f9a815aa280de6977f666e2da6ea8be2bae24cc8c7365430cbfd6bc3496b
|
4
|
+
data.tar.gz: ad2eec36ca931720158e21e9abcf7d99e4c32618365ef300ca465df59339e307
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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.
|
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
|
data/lib/fluent/plugin/xray.rb
CHANGED
@@ -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
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
pos_file.
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
:
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
:
|
72
|
-
:
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
cvss =
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
:
|
164
|
-
:
|
165
|
-
:
|
166
|
-
:
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
end
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
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.
|
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:
|
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
|