fluent-plugin-jfrog-siem 0.1.4 → 0.1.9
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/README.md +8 -2
- data/fluent-plugin-jfrog-siem.gemspec +1 -1
- data/lib/fluent/plugin/in_jfrog_siem.rb +93 -38
- data/test/plugin/test_in_jfrog_siem.rb +3 -2
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e45847f8ff6faf6103f383101c21b8534f1b0bc7c9d8626e8f3a5a172631fb9e
|
4
|
+
data.tar.gz: 3f45070df06e498421d9e64ea25a793b700bb6efab6b7a5e878a206214927889
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 69f51b464049166d1930578447cd7ca2dd5bd273e013ea94e1eb682b2e664e83e1694db11358c929fda408c714cf3f5c2d3d272aee5b4bee88104c55441e556e
|
7
|
+
data.tar.gz: 0a94d81ed3741bbbffd41aa47e5781becb956337675261bb00eea4f4de2c9bcc41809fe212c6abb21a817b5626f9ff03db5be07e262ce0dde9093efa9c68c17a
|
data/README.md
CHANGED
@@ -68,13 +68,19 @@ Splunk:
|
|
68
68
|
|
69
69
|
Splunk setup can be found at [README.](https://github.com/jfrog/log-analytics-splunk/blob/master/README.md)
|
70
70
|
````text
|
71
|
-
wget https://raw.githubusercontent.com/jfrog/log-analytics/master/
|
71
|
+
wget https://raw.githubusercontent.com/jfrog/log-analytics-splunk/master/siem/splunk_siem.conf
|
72
72
|
````
|
73
73
|
Elasticsearch:
|
74
74
|
|
75
75
|
Elasticsearch Kibana setup can be found at [README.](https://github.com/jfrog/log-analytics-elastic/blob/master/README.md)
|
76
76
|
````text
|
77
|
-
wget https://raw.githubusercontent.com/jfrog/log-analytics/master/
|
77
|
+
wget https://raw.githubusercontent.com/jfrog/log-analytics-elastic/master/siem/elastic_siem.conf
|
78
|
+
````
|
79
|
+
Datadog:
|
80
|
+
|
81
|
+
Datadog setup can be found at [README.](https://github.com/jfrog/log-analytics-datadog/blob/master/README.md)
|
82
|
+
````text
|
83
|
+
wget https://raw.githubusercontent.com/jfrog/log-analytics-datadog/master/siem/datadog_siem.conf
|
78
84
|
````
|
79
85
|
|
80
86
|
#### Configuration parameters
|
@@ -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 = "0.1.
|
6
|
+
spec.version = "0.1.9"
|
7
7
|
spec.authors = ["John Peterson", "Mahitha Byreddy"]
|
8
8
|
spec.email = ["johnp@jfrog.com", "mahithab@jfrog.com"]
|
9
9
|
|
@@ -30,7 +30,8 @@ module Fluent
|
|
30
30
|
# `:default` means that the parameter is optional.
|
31
31
|
config_param :tag, :string, default: ""
|
32
32
|
config_param :jpd_url, :string, default: ""
|
33
|
-
config_param :
|
33
|
+
config_param :username, :string, default: ""
|
34
|
+
config_param :apikey, :string, default: ""
|
34
35
|
config_param :pos_file, :string, default: ""
|
35
36
|
config_param :batch_size, :integer, default: 25
|
36
37
|
config_param :thread_count, :integer, default: 5
|
@@ -50,8 +51,12 @@ module Fluent
|
|
50
51
|
raise Fluent::ConfigError, "Must define the JPD URL to pull Xray SIEM violations."
|
51
52
|
end
|
52
53
|
|
53
|
-
if @
|
54
|
-
raise Fluent::ConfigError, "Must define the
|
54
|
+
if @username == ""
|
55
|
+
raise Fluent::ConfigError, "Must define the username to use for authentication."
|
56
|
+
end
|
57
|
+
|
58
|
+
if @apikey == ""
|
59
|
+
raise Fluent::ConfigError, "Must define the API Key to use for authentication."
|
55
60
|
end
|
56
61
|
|
57
62
|
if @pos_file == ""
|
@@ -89,7 +94,7 @@ module Fluent
|
|
89
94
|
|
90
95
|
|
91
96
|
def run
|
92
|
-
call_home(@jpd_url
|
97
|
+
call_home(@jpd_url)
|
93
98
|
# runs the violation pull
|
94
99
|
last_created_date_string = get_last_item_create_date()
|
95
100
|
begin
|
@@ -99,11 +104,12 @@ module Fluent
|
|
99
104
|
end
|
100
105
|
offset_count=1
|
101
106
|
left_violations=0
|
107
|
+
waiting_for_violations = false
|
102
108
|
xray_json={"filters": { "created_from": last_created_date }, "pagination": {"order_by": "created","limit": @batch_size ,"offset": offset_count } }
|
103
109
|
|
104
110
|
while true
|
105
111
|
# Grab the batch of records
|
106
|
-
resp=get_xray_violations(xray_json, @jpd_url
|
112
|
+
resp=get_xray_violations(xray_json, @jpd_url)
|
107
113
|
number_of_violations = JSON.parse(resp)['total_violations']
|
108
114
|
if left_violations <= 0
|
109
115
|
left_violations = number_of_violations
|
@@ -120,8 +126,22 @@ module Fluent
|
|
120
126
|
|
121
127
|
# Determine if we need to persist this record or not
|
122
128
|
persistItem = true
|
123
|
-
if
|
124
|
-
|
129
|
+
if waiting_for_violations
|
130
|
+
if created_date <= last_created_date
|
131
|
+
# "not persisting it - waiting for violations"
|
132
|
+
# waiting and same last timestamp (left violations in batch)
|
133
|
+
persistItem = false
|
134
|
+
end
|
135
|
+
if created_date > last_created_date
|
136
|
+
# new violation while waiting
|
137
|
+
persistItem = true
|
138
|
+
waiting_for_violations = false
|
139
|
+
end
|
140
|
+
else
|
141
|
+
if created_date < last_created_date
|
142
|
+
# "persisting everything"
|
143
|
+
persistItem = true
|
144
|
+
end
|
125
145
|
end
|
126
146
|
|
127
147
|
# Publish the record to fluentd
|
@@ -150,7 +170,7 @@ module Fluent
|
|
150
170
|
thread_pool = Thread.pool(thread_count)
|
151
171
|
thread_pool.process {
|
152
172
|
for xray_violation_url in xray_violation_urls_list do
|
153
|
-
pull_violation_details(xray_violation_url
|
173
|
+
pull_violation_details(xray_violation_url)
|
154
174
|
end
|
155
175
|
}
|
156
176
|
thread_pool.shutdown
|
@@ -158,9 +178,11 @@ module Fluent
|
|
158
178
|
# reduce left violations by jump size (not all batches have full item count??)
|
159
179
|
left_violations = left_violations - @batch_size
|
160
180
|
if left_violations <= 0
|
181
|
+
waiting_for_violations = true
|
161
182
|
sleep(@wait_interval)
|
162
183
|
else
|
163
184
|
# Grab the next record to process for the violation details url
|
185
|
+
waiting_for_violations = false
|
164
186
|
offset_count = offset_count + 1
|
165
187
|
xray_json={"filters": { "created_from": last_created_date_string }, "pagination": {"order_by": "created","limit": @batch_size , "offset": offset_count } }
|
166
188
|
end
|
@@ -177,48 +199,53 @@ module Fluent
|
|
177
199
|
end
|
178
200
|
|
179
201
|
#call home functionality
|
180
|
-
def call_home(jpd_url
|
202
|
+
def call_home(jpd_url)
|
181
203
|
call_home_json = { "productId": "jfrogLogAnalytics/v0.5.1", "features": [ { "featureId": "Platform/Xray" }, { "featureId": "Channel/xrayeventsiem" } ] }
|
182
204
|
response = RestClient::Request.new(
|
183
205
|
:method => :post,
|
184
206
|
:url => jpd_url + "/artifactory/api/system/usage",
|
185
207
|
:payload => call_home_json.to_json,
|
186
|
-
:
|
208
|
+
:user => @username,
|
209
|
+
:password => @apikey,
|
210
|
+
:headers => { :accept => :json, :content_type => :json}
|
187
211
|
).execute do |response, request, result|
|
188
212
|
puts "Posting call home information"
|
189
213
|
end
|
190
214
|
end
|
191
215
|
|
192
216
|
# queries the xray API for violations based upon the input json
|
193
|
-
def get_xray_violations_detail(xray_violation_detail_url
|
217
|
+
def get_xray_violations_detail(xray_violation_detail_url)
|
194
218
|
response = RestClient::Request.new(
|
195
219
|
:method => :get,
|
196
220
|
:url => xray_violation_detail_url,
|
197
|
-
|
221
|
+
:user => @username,
|
222
|
+
:password => @apikey
|
198
223
|
).execute do |response, request, result|
|
199
224
|
case response.code
|
200
225
|
when 200
|
201
226
|
return response.to_str
|
202
227
|
else
|
203
|
-
raise Fluent::
|
228
|
+
raise Fluent::ConfigError, "Cannot reach Artifactory URL to pull Xray SIEM violations."
|
204
229
|
end
|
205
230
|
end
|
206
231
|
end
|
207
232
|
|
208
233
|
|
209
234
|
# queries the xray API for violations based upon the input json
|
210
|
-
def get_xray_violations(xray_json, jpd_url
|
235
|
+
def get_xray_violations(xray_json, jpd_url)
|
211
236
|
response = RestClient::Request.new(
|
212
237
|
:method => :post,
|
213
238
|
:url => jpd_url + "/xray/api/v1/violations",
|
214
239
|
:payload => xray_json.to_json,
|
215
|
-
:
|
240
|
+
:user => @username,
|
241
|
+
:password => @apikey,
|
242
|
+
:headers => { :accept => :json, :content_type => :json}
|
216
243
|
).execute do |response, request, result|
|
217
244
|
case response.code
|
218
245
|
when 200
|
219
246
|
return response.to_str
|
220
247
|
else
|
221
|
-
raise Fluent::
|
248
|
+
raise Fluent::ConfigError, "Cannot reach Artifactory URL to pull Xray SIEM violations."
|
222
249
|
end
|
223
250
|
end
|
224
251
|
end
|
@@ -226,44 +253,72 @@ module Fluent
|
|
226
253
|
# normalizes Xray data according to common information models for all log-vendors
|
227
254
|
def data_normalization(detailResp)
|
228
255
|
detailResp_json = JSON.parse(detailResp)
|
229
|
-
properties = detailResp_json['properties']
|
230
256
|
cve = []
|
231
257
|
cvss_v2_list = []
|
232
258
|
cvss_v3_list = []
|
233
|
-
|
234
|
-
|
235
|
-
|
259
|
+
policy_list = []
|
260
|
+
rule_list = []
|
261
|
+
impacted_artifact_url_list = []
|
262
|
+
if detailResp_json.key?('properties')
|
263
|
+
properties = detailResp_json['properties']
|
264
|
+
for index in 0..properties.length-1 do
|
265
|
+
if properties[index].key?('cve')
|
266
|
+
cve.push(properties[index]['cve'])
|
267
|
+
end
|
268
|
+
if properties[index].key?('cvss_v2')
|
269
|
+
cvss_v2_list.push(properties[index]['cvss_v2'])
|
270
|
+
end
|
271
|
+
if properties[index].key?('cvss_v3')
|
272
|
+
cvss_v3_list.push(properties[index]['cvss_v3'])
|
273
|
+
end
|
236
274
|
end
|
237
|
-
|
238
|
-
|
275
|
+
|
276
|
+
detailResp_json["cve"] = cve.sort.reverse[0]
|
277
|
+
cvss_v2 = cvss_v2_list.sort.reverse[0]
|
278
|
+
cvss_v3 = cvss_v3_list.sort.reverse[0]
|
279
|
+
if !cvss_v3.nil?
|
280
|
+
cvss = cvss_v3
|
281
|
+
elsif !cvss_v2.nil?
|
282
|
+
cvss = cvss_v2
|
239
283
|
end
|
240
|
-
|
241
|
-
|
284
|
+
cvss_score = cvss[0..2]
|
285
|
+
cvss_version = cvss.split(':')[1][0..2]
|
286
|
+
detailResp_json["cvss_score"] = cvss_score
|
287
|
+
detailResp_json["cvss_version"] = cvss_version
|
288
|
+
end
|
289
|
+
|
290
|
+
if detailResp_json.key?('matched_policies')
|
291
|
+
matched_policies = detailResp_json['matched_policies']
|
292
|
+
for index in 0..matched_policies.length-1 do
|
293
|
+
if matched_policies[index].key?('policy')
|
294
|
+
policy_list.push(matched_policies[index]['policy'])
|
295
|
+
end
|
296
|
+
if matched_policies[index].key?('rule')
|
297
|
+
rule_list.push(matched_policies[index]['rule'])
|
298
|
+
end
|
242
299
|
end
|
300
|
+
detailResp_json['policies'] = policy_list
|
301
|
+
detailResp_json['rules'] = rule_list
|
243
302
|
end
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
cvss = cvss_v2
|
303
|
+
|
304
|
+
impacted_artifacts = detailResp_json['impacted_artifacts']
|
305
|
+
for impacted_artifact in impacted_artifacts do
|
306
|
+
matchdata = impacted_artifact.match /default\/(?<repo_name>[^\/]*)\/(?<path>.*)/
|
307
|
+
impacted_artifact_url = matchdata['repo_name'] + ":" + matchdata['path'] + " "
|
308
|
+
impacted_artifact_url_list.append(impacted_artifact_url)
|
251
309
|
end
|
252
|
-
|
253
|
-
cvss_version = cvss.split(':')[1][0..2]
|
254
|
-
detailResp_json["cvss_score"] = cvss_score
|
255
|
-
detailResp_json["cvss_version"] = cvss_version
|
310
|
+
detailResp_json['impacted_artifacts_url'] = impacted_artifact_url_list
|
256
311
|
return detailResp_json
|
257
312
|
end
|
258
313
|
|
259
|
-
def pull_violation_details(xray_violation_detail_url
|
314
|
+
def pull_violation_details(xray_violation_detail_url)
|
260
315
|
begin
|
261
|
-
detailResp=get_xray_violations_detail(xray_violation_detail_url
|
316
|
+
detailResp=get_xray_violations_detail(xray_violation_detail_url)
|
262
317
|
time = Fluent::Engine.now
|
263
318
|
detailResp_json = data_normalization(detailResp)
|
264
319
|
router.emit(@tag, time, detailResp_json)
|
265
320
|
rescue
|
266
|
-
raise Fluent::
|
321
|
+
raise Fluent::ConfigError, "Error pulling violation details url #{xray_violation_detail_url}"
|
267
322
|
end
|
268
323
|
end
|
269
324
|
|
@@ -13,8 +13,9 @@ class JfrogSiemInputTest < Test::Unit::TestCase
|
|
13
13
|
# Default configuration for tests
|
14
14
|
CONFIG = %[
|
15
15
|
tag "test_tag"
|
16
|
-
jpd_url
|
17
|
-
|
16
|
+
jpd_url "http://dd-siem-unified-15-rt.jfrog.tech"
|
17
|
+
username "admin"
|
18
|
+
apikey "AKCp8jQ8tAb2tqC6jXH7J1qLShXnjorD5XhFkvqUhN78WMLmvfTx5GNuD9B8uEXnGyruBCzYk"
|
18
19
|
pos_file "test_pos.txt"
|
19
20
|
]
|
20
21
|
|
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: 0.1.
|
4
|
+
version: 0.1.9
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- John Peterson
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2021-
|
12
|
+
date: 2021-05-17 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: bundler
|