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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7a6d55c86435d39f27aa0deaf46748366ac837ad4a2417f5a89bfe3711f62c3a
4
- data.tar.gz: 8fc1d3286dab511cd5b6c2cb5358f4edc782dc694d0da052c191858de42057d6
3
+ metadata.gz: e45847f8ff6faf6103f383101c21b8534f1b0bc7c9d8626e8f3a5a172631fb9e
4
+ data.tar.gz: 3f45070df06e498421d9e64ea25a793b700bb6efab6b7a5e878a206214927889
5
5
  SHA512:
6
- metadata.gz: 2fe48f82b2911228d5bf3073ed92d6d4991ee89a5e66c1d56516f6e63a8fe7b4037ec374cb4012b59630cb796435fb02db696582af472cb598ecdf304b670d4a
7
- data.tar.gz: 0c8fa4396ccba483ac3d8931cecbfb6996d65c847a7fdfac749de7156be6d0f2dda4cc8a0e51a6672d210c57d683a5b2cb6d787eb1646ecd1c7efc006e2bc3bd
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/fluentd/plugins/input/fluent-plugin-jfrog-siem/splunk.conf
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/fluentd/plugins/input/fluent-plugin-jfrog-siem/elastic.conf
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.4"
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 :access_token, :string, default: ""
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 @access_token == ""
54
- raise Fluent::ConfigError, "Must define the access token to use for authentication."
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, @access_token)
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, @access_token)
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 created_date <= last_created_date
124
- persistItem = false
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, @access_token)
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, access_token)
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
- :headers => { :accept => :json, :content_type => :json, Authorization:'Bearer ' + access_token }
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, access_token)
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
- headers: {Authorization:'Bearer ' + access_token}
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::StandardError, "Cannot reach Artifactory URL to pull Xray SIEM violations."
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, access_token)
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
- :headers => { :accept => :json, :content_type => :json, Authorization:'Bearer ' + access_token }
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::StandardError, "Cannot reach Artifactory URL to pull Xray SIEM violations."
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
- for index in 0..properties.length-1 do
234
- if properties[index].key?('cve')
235
- cve.push(properties[index]['cve'])
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
- if properties[index].key?('cvss_v2')
238
- cvss_v2_list.push(properties[index]['cvss_v2'])
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
- if properties[index].key?('cvss_v3')
241
- cvss_v3_list.push(properties[index]['cvss_v3'])
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
- detailResp_json["cve"] = cve.sort.reverse[0]
245
- cvss_v2 = cvss_v2_list.sort.reverse[0]
246
- cvss_v3 = cvss_v3_list.sort.reverse[0]
247
- if cvss_v3.length() > 0
248
- cvss = cvss_v3
249
- elsif cvss_v2.length() > 0
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
- cvss_score = cvss[0..2]
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, access_token)
314
+ def pull_violation_details(xray_violation_detail_url)
260
315
  begin
261
- detailResp=get_xray_violations_detail(xray_violation_detail_url, access_token)
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::StandardError, "Error pulling violation details url #{xray_violation_detail_url}"
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 <jpd_url>
17
- access_token <access_token>
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
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-02-02 00:00:00.000000000 Z
12
+ date: 2021-05-17 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: bundler