fluent-plugin-jfrog-siem 0.1.3 → 0.1.8
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/Gemfile +1 -0
- data/README.md +8 -2
- data/fluent-plugin-jfrog-siem.gemspec +3 -1
- data/lib/fluent/plugin/in_jfrog_siem.rb +91 -43
- data/test/plugin/test_in_jfrog_siem.rb +3 -2
- metadata +30 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4e222c0afdaf25a0aa38e4167236c2f06b5c7f9c889ca544f3a05677609c2c1e
|
4
|
+
data.tar.gz: '097fd2e7d1b8b0b8394e11efa9abbcfa5d0e67412d3284bdd5c5fe47badde641'
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 18b38f238bef87f6e015aa6e6eda87ba58586e6e85ef5f29d58392b91ad1fc5c12da33777ce7b477f222d966df2c5eb7082d1cbe757cc712703adb4511945057
|
7
|
+
data.tar.gz: 49a13e9f1aeec783f9e7f35ca9750d3d3c4398d1e6facfa029dbfa6dec8a27d083941e995722cdb7ffa66937fb747bc079fe439e534e5dc23023e7543d56dc4b
|
data/Gemfile
CHANGED
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.8"
|
7
7
|
spec.authors = ["John Peterson", "Mahitha Byreddy"]
|
8
8
|
spec.email = ["johnp@jfrog.com", "mahithab@jfrog.com"]
|
9
9
|
|
@@ -24,5 +24,7 @@ Gem::Specification.new do |spec|
|
|
24
24
|
spec.add_development_dependency "rake", "~> 12.0"
|
25
25
|
spec.add_development_dependency "test-unit", "~> 3.0"
|
26
26
|
spec.add_development_dependency "rest-client", "~> 2.0"
|
27
|
+
spec.add_development_dependency "thread", "~> 0.2.2"
|
28
|
+
spec.add_runtime_dependency "thread", "~> 0.2.2"
|
27
29
|
spec.add_runtime_dependency "fluentd", [">= 0.14.10", "< 2"]
|
28
30
|
end
|
@@ -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,16 @@ 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
|
+
persistItem = false
|
133
|
+
end
|
134
|
+
else
|
135
|
+
if created_date < last_created_date
|
136
|
+
# "persisting everything"
|
137
|
+
persistItem = true
|
138
|
+
end
|
125
139
|
end
|
126
140
|
|
127
141
|
# Publish the record to fluentd
|
@@ -148,20 +162,21 @@ module Fluent
|
|
148
162
|
# iterate over url array adding to thread pool each url.
|
149
163
|
# limit max workers to thread count to prevent overloading xray.
|
150
164
|
thread_pool = Thread.pool(thread_count)
|
151
|
-
|
152
|
-
|
153
|
-
pull_violation_details(xray_violation_url
|
154
|
-
|
155
|
-
|
156
|
-
|
165
|
+
thread_pool.process {
|
166
|
+
for xray_violation_url in xray_violation_urls_list do
|
167
|
+
pull_violation_details(xray_violation_url)
|
168
|
+
end
|
169
|
+
}
|
157
170
|
thread_pool.shutdown
|
158
171
|
|
159
172
|
# reduce left violations by jump size (not all batches have full item count??)
|
160
173
|
left_violations = left_violations - @batch_size
|
161
174
|
if left_violations <= 0
|
175
|
+
waiting_for_violations = true
|
162
176
|
sleep(@wait_interval)
|
163
177
|
else
|
164
178
|
# Grab the next record to process for the violation details url
|
179
|
+
waiting_for_violations = false
|
165
180
|
offset_count = offset_count + 1
|
166
181
|
xray_json={"filters": { "created_from": last_created_date_string }, "pagination": {"order_by": "created","limit": @batch_size , "offset": offset_count } }
|
167
182
|
end
|
@@ -178,48 +193,53 @@ module Fluent
|
|
178
193
|
end
|
179
194
|
|
180
195
|
#call home functionality
|
181
|
-
def call_home(jpd_url
|
196
|
+
def call_home(jpd_url)
|
182
197
|
call_home_json = { "productId": "jfrogLogAnalytics/v0.5.1", "features": [ { "featureId": "Platform/Xray" }, { "featureId": "Channel/xrayeventsiem" } ] }
|
183
198
|
response = RestClient::Request.new(
|
184
199
|
:method => :post,
|
185
200
|
:url => jpd_url + "/artifactory/api/system/usage",
|
186
201
|
:payload => call_home_json.to_json,
|
187
|
-
:
|
202
|
+
:user => @username,
|
203
|
+
:password => @apikey,
|
204
|
+
:headers => { :accept => :json, :content_type => :json}
|
188
205
|
).execute do |response, request, result|
|
189
206
|
puts "Posting call home information"
|
190
207
|
end
|
191
208
|
end
|
192
209
|
|
193
210
|
# queries the xray API for violations based upon the input json
|
194
|
-
def get_xray_violations_detail(xray_violation_detail_url
|
211
|
+
def get_xray_violations_detail(xray_violation_detail_url)
|
195
212
|
response = RestClient::Request.new(
|
196
213
|
:method => :get,
|
197
214
|
:url => xray_violation_detail_url,
|
198
|
-
|
215
|
+
:user => @username,
|
216
|
+
:password => @apikey
|
199
217
|
).execute do |response, request, result|
|
200
218
|
case response.code
|
201
219
|
when 200
|
202
220
|
return response.to_str
|
203
221
|
else
|
204
|
-
raise Fluent::
|
222
|
+
raise Fluent::ConfigError, "Cannot reach Artifactory URL to pull Xray SIEM violations."
|
205
223
|
end
|
206
224
|
end
|
207
225
|
end
|
208
226
|
|
209
227
|
|
210
228
|
# queries the xray API for violations based upon the input json
|
211
|
-
def get_xray_violations(xray_json, jpd_url
|
229
|
+
def get_xray_violations(xray_json, jpd_url)
|
212
230
|
response = RestClient::Request.new(
|
213
231
|
:method => :post,
|
214
232
|
:url => jpd_url + "/xray/api/v1/violations",
|
215
233
|
:payload => xray_json.to_json,
|
216
|
-
:
|
234
|
+
:user => @username,
|
235
|
+
:password => @apikey,
|
236
|
+
:headers => { :accept => :json, :content_type => :json}
|
217
237
|
).execute do |response, request, result|
|
218
238
|
case response.code
|
219
239
|
when 200
|
220
240
|
return response.to_str
|
221
241
|
else
|
222
|
-
raise Fluent::
|
242
|
+
raise Fluent::ConfigError, "Cannot reach Artifactory URL to pull Xray SIEM violations."
|
223
243
|
end
|
224
244
|
end
|
225
245
|
end
|
@@ -227,44 +247,72 @@ module Fluent
|
|
227
247
|
# normalizes Xray data according to common information models for all log-vendors
|
228
248
|
def data_normalization(detailResp)
|
229
249
|
detailResp_json = JSON.parse(detailResp)
|
230
|
-
properties = detailResp_json['properties']
|
231
250
|
cve = []
|
232
251
|
cvss_v2_list = []
|
233
252
|
cvss_v3_list = []
|
234
|
-
|
235
|
-
|
236
|
-
|
253
|
+
policy_list = []
|
254
|
+
rule_list = []
|
255
|
+
impacted_artifact_url_list = []
|
256
|
+
if detailResp_json.key?('properties')
|
257
|
+
properties = detailResp_json['properties']
|
258
|
+
for index in 0..properties.length-1 do
|
259
|
+
if properties[index].key?('cve')
|
260
|
+
cve.push(properties[index]['cve'])
|
261
|
+
end
|
262
|
+
if properties[index].key?('cvss_v2')
|
263
|
+
cvss_v2_list.push(properties[index]['cvss_v2'])
|
264
|
+
end
|
265
|
+
if properties[index].key?('cvss_v3')
|
266
|
+
cvss_v3_list.push(properties[index]['cvss_v3'])
|
267
|
+
end
|
237
268
|
end
|
238
|
-
|
239
|
-
|
269
|
+
|
270
|
+
detailResp_json["cve"] = cve.sort.reverse[0]
|
271
|
+
cvss_v2 = cvss_v2_list.sort.reverse[0]
|
272
|
+
cvss_v3 = cvss_v3_list.sort.reverse[0]
|
273
|
+
if !cvss_v3.nil?
|
274
|
+
cvss = cvss_v3
|
275
|
+
elsif !cvss_v2.nil?
|
276
|
+
cvss = cvss_v2
|
240
277
|
end
|
241
|
-
|
242
|
-
|
278
|
+
cvss_score = cvss[0..2]
|
279
|
+
cvss_version = cvss.split(':')[1][0..2]
|
280
|
+
detailResp_json["cvss_score"] = cvss_score
|
281
|
+
detailResp_json["cvss_version"] = cvss_version
|
282
|
+
end
|
283
|
+
|
284
|
+
if detailResp_json.key?('matched_policies')
|
285
|
+
matched_policies = detailResp_json['matched_policies']
|
286
|
+
for index in 0..matched_policies.length-1 do
|
287
|
+
if matched_policies[index].key?('policy')
|
288
|
+
policy_list.push(matched_policies[index]['policy'])
|
289
|
+
end
|
290
|
+
if matched_policies[index].key?('rule')
|
291
|
+
rule_list.push(matched_policies[index]['rule'])
|
292
|
+
end
|
243
293
|
end
|
294
|
+
detailResp_json['policies'] = policy_list
|
295
|
+
detailResp_json['rules'] = rule_list
|
244
296
|
end
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
cvss = cvss_v2
|
297
|
+
|
298
|
+
impacted_artifacts = detailResp_json['impacted_artifacts']
|
299
|
+
for impacted_artifact in impacted_artifacts do
|
300
|
+
matchdata = impacted_artifact.match /default\/(?<repo_name>[^\/]*)\/(?<path>.*)/
|
301
|
+
impacted_artifact_url = matchdata['repo_name'] + ":" + matchdata['path'] + " "
|
302
|
+
impacted_artifact_url_list.append(impacted_artifact_url)
|
252
303
|
end
|
253
|
-
|
254
|
-
cvss_version = cvss.split(':')[1][0..2]
|
255
|
-
detailResp_json["cvss_score"] = cvss_score
|
256
|
-
detailResp_json["cvss_version"] = cvss_version
|
304
|
+
detailResp_json['impacted_artifacts_url'] = impacted_artifact_url_list
|
257
305
|
return detailResp_json
|
258
306
|
end
|
259
307
|
|
260
|
-
def pull_violation_details(xray_violation_detail_url
|
308
|
+
def pull_violation_details(xray_violation_detail_url)
|
261
309
|
begin
|
262
|
-
detailResp=get_xray_violations_detail(xray_violation_detail_url
|
310
|
+
detailResp=get_xray_violations_detail(xray_violation_detail_url)
|
263
311
|
time = Fluent::Engine.now
|
264
312
|
detailResp_json = data_normalization(detailResp)
|
265
313
|
router.emit(@tag, time, detailResp_json)
|
266
314
|
rescue
|
267
|
-
raise Fluent::
|
315
|
+
raise Fluent::ConfigError, "Error pulling violation details url #{xray_violation_detail_url}"
|
268
316
|
end
|
269
317
|
end
|
270
318
|
|
@@ -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 JPD_URL
|
17
|
+
username USER
|
18
|
+
apikey API_KEY
|
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.8
|
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-10 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: bundler
|
@@ -67,6 +67,34 @@ dependencies:
|
|
67
67
|
- - "~>"
|
68
68
|
- !ruby/object:Gem::Version
|
69
69
|
version: '2.0'
|
70
|
+
- !ruby/object:Gem::Dependency
|
71
|
+
name: thread
|
72
|
+
requirement: !ruby/object:Gem::Requirement
|
73
|
+
requirements:
|
74
|
+
- - "~>"
|
75
|
+
- !ruby/object:Gem::Version
|
76
|
+
version: 0.2.2
|
77
|
+
type: :development
|
78
|
+
prerelease: false
|
79
|
+
version_requirements: !ruby/object:Gem::Requirement
|
80
|
+
requirements:
|
81
|
+
- - "~>"
|
82
|
+
- !ruby/object:Gem::Version
|
83
|
+
version: 0.2.2
|
84
|
+
- !ruby/object:Gem::Dependency
|
85
|
+
name: thread
|
86
|
+
requirement: !ruby/object:Gem::Requirement
|
87
|
+
requirements:
|
88
|
+
- - "~>"
|
89
|
+
- !ruby/object:Gem::Version
|
90
|
+
version: 0.2.2
|
91
|
+
type: :runtime
|
92
|
+
prerelease: false
|
93
|
+
version_requirements: !ruby/object:Gem::Requirement
|
94
|
+
requirements:
|
95
|
+
- - "~>"
|
96
|
+
- !ruby/object:Gem::Version
|
97
|
+
version: 0.2.2
|
70
98
|
- !ruby/object:Gem::Dependency
|
71
99
|
name: fluentd
|
72
100
|
requirement: !ruby/object:Gem::Requirement
|