fluent-plugin-jfrog-siem 0.1.3 → 0.1.8

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: cd2cc7532e266f9d58b4dbbf18479c1f34fd975649f7aa5ab9e5e671f449025b
4
- data.tar.gz: fa8943afc154c4d6136ac1605f5bf85a240ce6ac58bf3ee61f42eb162f447aa6
3
+ metadata.gz: 4e222c0afdaf25a0aa38e4167236c2f06b5c7f9c889ca544f3a05677609c2c1e
4
+ data.tar.gz: '097fd2e7d1b8b0b8394e11efa9abbcfa5d0e67412d3284bdd5c5fe47badde641'
5
5
  SHA512:
6
- metadata.gz: b47739ce004b1d48fddd026188aed933e4397b3730eb1d9bde1511f8bd623dc687246fa0ad85a91e814aa46175e5b0aee44213e12f18be97f078fb71b44904f7
7
- data.tar.gz: a377d04e8b41cb108402036158d82632390db472cc4e7a97655110465e79a939e2bc90a25945b4932463f0561294c6f4af46fe70fa730141a403e1665a960a6e
6
+ metadata.gz: 18b38f238bef87f6e015aa6e6eda87ba58586e6e85ef5f29d58392b91ad1fc5c12da33777ce7b477f222d966df2c5eb7082d1cbe757cc712703adb4511945057
7
+ data.tar.gz: 49a13e9f1aeec783f9e7f35ca9750d3d3c4398d1e6facfa029dbfa6dec8a27d083941e995722cdb7ffa66937fb747bc079fe439e534e5dc23023e7543d56dc4b
data/Gemfile CHANGED
@@ -1,2 +1,3 @@
1
1
  source "https://rubygems.org"
2
+
2
3
  gemspec
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.3"
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 :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,16 @@ 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
+ 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
- for xray_violation_url in xray_violation_urls_list do
152
- thread_pool.process {
153
- pull_violation_details(xray_violation_url, @access_token)
154
- }
155
- end
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, access_token)
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
- :headers => { :accept => :json, :content_type => :json, Authorization:'Bearer ' + access_token }
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, access_token)
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
- headers: {Authorization:'Bearer ' + access_token}
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::StandardError, "Cannot reach Artifactory URL to pull Xray SIEM violations."
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, access_token)
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
- :headers => { :accept => :json, :content_type => :json, Authorization:'Bearer ' + access_token }
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::StandardError, "Cannot reach Artifactory URL to pull Xray SIEM violations."
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
- for index in 0..properties.length-1 do
235
- if properties[index].key?('cve')
236
- cve.push(properties[index]['cve'])
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
- if properties[index].key?('cvss_v2')
239
- cvss_v2_list.push(properties[index]['cvss_v2'])
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
- if properties[index].key?('cvss_v3')
242
- cvss_v3_list.push(properties[index]['cvss_v3'])
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
- detailResp_json["cve"] = cve.sort.reverse[0]
246
- cvss_v2 = cvss_v2_list.sort.reverse[0]
247
- cvss_v3 = cvss_v3_list.sort.reverse[0]
248
- if cvss_v3.length() > 0
249
- cvss = cvss_v3
250
- elsif cvss_v2.length() > 0
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
- cvss_score = cvss[0..2]
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, access_token)
308
+ def pull_violation_details(xray_violation_detail_url)
261
309
  begin
262
- detailResp=get_xray_violations_detail(xray_violation_detail_url, access_token)
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::StandardError, "Error pulling violation details url #{xray_violation_detail_url}"
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 <jpd_url>
17
- access_token <access_token>
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.3
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-01-21 00:00:00.000000000 Z
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