fluent-plugin-jfrog-siem 0.1.8 → 2.0.1

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: 4e222c0afdaf25a0aa38e4167236c2f06b5c7f9c889ca544f3a05677609c2c1e
4
- data.tar.gz: '097fd2e7d1b8b0b8394e11efa9abbcfa5d0e67412d3284bdd5c5fe47badde641'
3
+ metadata.gz: ea8dc8e85e1874da646a83a7aea1ab32a63057cfaedd0cce5b30a63461898b9c
4
+ data.tar.gz: 4d38c3858e8432b13cd25e1fde6a82619d037274037173fd0825b3b1236e811e
5
5
  SHA512:
6
- metadata.gz: 18b38f238bef87f6e015aa6e6eda87ba58586e6e85ef5f29d58392b91ad1fc5c12da33777ce7b477f222d966df2c5eb7082d1cbe757cc712703adb4511945057
7
- data.tar.gz: 49a13e9f1aeec783f9e7f35ca9750d3d3c4398d1e6facfa029dbfa6dec8a27d083941e995722cdb7ffa66937fb747bc079fe439e534e5dc23023e7543d56dc4b
6
+ metadata.gz: e9289b75176b973729f922e61b8a1c17481ad896f1bed3e9baae687f6387ecf8d968523ec229da36d22f6d6ef9b052ab8e9cab5d652310e2f4c8d0dfb8ef81f8
7
+ data.tar.gz: 9bf33d52a6265a39dceb9bb8e1c99d395fcc0e654b2586d180379c390ac76e86b0dcf680985d01cecf9daaa3237d34e7e9848a2d2a0f6b4461c3e59065a5ff2f
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --require spec_helper
data/Gemfile.lock ADDED
@@ -0,0 +1,90 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ fluent-plugin-jfrog-siem (1.0.0)
5
+ concurrent-ruby (~> 1.1.8)
6
+ concurrent-ruby-edge
7
+ fluentd (>= 0.14.10, < 2)
8
+
9
+ GEM
10
+ remote: https://rubygems.org/
11
+ specs:
12
+ concurrent-ruby (1.1.8)
13
+ concurrent-ruby-edge (0.6.0)
14
+ concurrent-ruby (~> 1.1.6)
15
+ cool.io (1.7.1)
16
+ diff-lcs (1.4.4)
17
+ domain_name (0.5.20190701)
18
+ unf (>= 0.0.5, < 1.0.0)
19
+ fluentd (1.13.1)
20
+ bundler
21
+ cool.io (>= 1.4.5, < 2.0.0)
22
+ http_parser.rb (>= 0.5.1, < 0.7.0)
23
+ msgpack (>= 1.3.1, < 2.0.0)
24
+ serverengine (>= 2.2.2, < 3.0.0)
25
+ sigdump (~> 0.2.2)
26
+ strptime (>= 0.2.2, < 1.0.0)
27
+ tzinfo (>= 1.0, < 3.0)
28
+ tzinfo-data (~> 1.0)
29
+ webrick (>= 1.4.2, < 1.8.0)
30
+ yajl-ruby (~> 1.0)
31
+ http-accept (1.7.0)
32
+ http-cookie (1.0.3)
33
+ domain_name (~> 0.5)
34
+ http_parser.rb (0.6.0)
35
+ mime-types (3.3.1)
36
+ mime-types-data (~> 3.2015)
37
+ mime-types-data (3.2021.0225)
38
+ msgpack (1.4.2)
39
+ netrc (0.11.0)
40
+ power_assert (2.0.0)
41
+ rake (12.3.3)
42
+ rest-client (2.1.0)
43
+ http-accept (>= 1.7.0, < 2.0)
44
+ http-cookie (>= 1.0.2, < 2.0)
45
+ mime-types (>= 1.16, < 4.0)
46
+ netrc (~> 0.8)
47
+ rspec (3.10.0)
48
+ rspec-core (~> 3.10.0)
49
+ rspec-expectations (~> 3.10.0)
50
+ rspec-mocks (~> 3.10.0)
51
+ rspec-core (3.10.1)
52
+ rspec-support (~> 3.10.0)
53
+ rspec-expectations (3.10.1)
54
+ diff-lcs (>= 1.2.0, < 2.0)
55
+ rspec-support (~> 3.10.0)
56
+ rspec-mocks (3.10.2)
57
+ diff-lcs (>= 1.2.0, < 2.0)
58
+ rspec-support (~> 3.10.0)
59
+ rspec-support (3.10.2)
60
+ serverengine (2.2.4)
61
+ sigdump (~> 0.2.2)
62
+ sigdump (0.2.4)
63
+ strptime (0.2.5)
64
+ test-unit (3.4.2)
65
+ power_assert
66
+ tzinfo (2.0.4)
67
+ concurrent-ruby (~> 1.0)
68
+ tzinfo-data (1.2021.1)
69
+ tzinfo (>= 1.0.0)
70
+ unf (0.1.4)
71
+ unf_ext
72
+ unf_ext (0.0.7.7)
73
+ webrick (1.7.0)
74
+ yajl-ruby (1.4.1)
75
+
76
+ PLATFORMS
77
+ x86_64-darwin-20
78
+
79
+ DEPENDENCIES
80
+ bundler (~> 2.0)
81
+ concurrent-ruby (~> 1.1.8)
82
+ concurrent-ruby-edge
83
+ fluent-plugin-jfrog-siem!
84
+ rake (~> 12.0)
85
+ rest-client (~> 2.0)
86
+ rspec (~> 3.10.0)
87
+ test-unit (~> 3.0)
88
+
89
+ BUNDLED WITH
90
+ 2.2.3
data/README.md CHANGED
@@ -87,7 +87,8 @@ wget https://raw.githubusercontent.com/jfrog/log-analytics-datadog/master/siem/d
87
87
  Integration is done by setting up Xray. Obtain JPD url and access token for API. Configure the source directive parameters specified below
88
88
  * **tag** (string) (required): The value is the tag assigned to the generated events.
89
89
  * **jpd_url** (string) (required): JPD url required to pull Xray SIEM violations
90
- * **access_token** (string) (required): [Access token](https://www.jfrog.com/confluence/display/JFROG/Access+Tokens) to authenticate Xray
90
+ * **apikey** (string) (required): API Key is the [Artifactory API Key](https://www.jfrog.com/confluence/display/JFROG/User+Profile#UserProfile-APIKey) for authentication
91
+ * **username** (string) (required): USER is the Artifactory username for authentication
91
92
  * **pos_file** (string) (required): Position file to record last SIEM violation pulled
92
93
  * **batch_size** (integer) (optional): Batch size for processing violations
93
94
  * Default value: `25`
data/Rakefile CHANGED
@@ -4,7 +4,7 @@ Bundler::GemHelper.install_tasks
4
4
  require "rake/testtask"
5
5
 
6
6
  Rake::TestTask.new(:test) do |t|
7
- t.libs.push("lib", "test")
7
+ t.libs.push("lib", "test", 'lib/fluent/plugin')
8
8
  t.test_files = FileList["test/**/test_*.rb"]
9
9
  t.verbose = true
10
10
  t.warning = false
@@ -3,13 +3,13 @@ $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.8"
7
- spec.authors = ["John Peterson", "Mahitha Byreddy"]
8
- spec.email = ["johnp@jfrog.com", "mahithab@jfrog.com"]
6
+ spec.version = "2.0.1"
7
+ spec.authors = ["Mahitha Byreddy", "Sudhindra Rao"]
8
+ spec.email = ["mahithab@jfrog.com", "sudhindrar@jfrog.com"]
9
9
 
10
10
  spec.summary = %q{JFrog SIEM fluent input plugin will send the SIEM events from JFrog Xray to Fluentd}
11
11
  spec.description = %q{JFrog SIEM fluent input plugin will send the SIEM events from JFrog Xray to Fluentd which can then be delivered to whatever output plugin specified}
12
- spec.homepage = "https://github.com/jfrog/log-analytics"
12
+ spec.homepage = "https://github.com/jfrog/fluent-plugin-jfrog-siem"
13
13
  spec.license = "Apache-2.0"
14
14
 
15
15
  test_files, files = `git ls-files -z`.split("\x0").partition do |f|
@@ -24,7 +24,12 @@ 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
+ spec.add_development_dependency "concurrent-ruby", "~> 1.1.8"
28
+ spec.add_development_dependency "concurrent-ruby-edge", '>= 0'
29
+ spec.add_development_dependency 'rspec', '~> 3.10.0'
30
+
31
+ spec.add_runtime_dependency "rest-client", "~> 2.0"
32
+ spec.add_runtime_dependency "concurrent-ruby", "~> 1.1.8"
33
+ spec.add_runtime_dependency "concurrent-ruby-edge", '>= 0'
29
34
  spec.add_runtime_dependency "fluentd", [">= 0.14.10", "< 2"]
30
35
  end
@@ -14,10 +14,10 @@
14
14
  # limitations under the License.
15
15
  require "fluent/plugin/input"
16
16
  require "rest-client"
17
- require "thread/pool"
18
- require "json"
19
17
  require "date"
20
18
  require "uri"
19
+ require "fluent/plugin/xray"
20
+ require "fluent/plugin/position_file"
21
21
 
22
22
  module Fluent
23
23
  module Plugin
@@ -32,11 +32,10 @@ module Fluent
32
32
  config_param :jpd_url, :string, default: ""
33
33
  config_param :username, :string, default: ""
34
34
  config_param :apikey, :string, default: ""
35
- config_param :pos_file, :string, default: ""
36
35
  config_param :batch_size, :integer, default: 25
37
- config_param :thread_count, :integer, default: 5
38
36
  config_param :wait_interval, :integer, default: 60
39
-
37
+ config_param :from_date, :string, default: ""
38
+ config_param :pos_file_path, :string, default: ""
40
39
 
41
40
  # `configure` is called before `start`.
42
41
  # 'conf' is a `Hash` that includes the configuration parameters.
@@ -59,22 +58,14 @@ module Fluent
59
58
  raise Fluent::ConfigError, "Must define the API Key to use for authentication."
60
59
  end
61
60
 
62
- if @pos_file == ""
63
- raise Fluent::ConfigError, "Must define a position file to record last SIEM violation pulled."
64
- end
65
-
66
- if @thread_count < 1
67
- raise Fluent::ConfigError, "Must define at least one thread to process violation details."
68
- end
69
-
70
- if @thread_count > @batch_size
71
- raise Fluent::ConfigError, "Violation detail url thread count exceeds batch size."
72
- end
73
-
74
61
  if @wait_interval < 1
75
62
  raise Fluent::ConfigError, "Wait interval must be greater than 1 to wait between pulling new events."
76
63
  end
77
64
 
65
+ if @from_date == ""
66
+ puts "From date not specified, so getting violations from current date if pos_file doesn't exist"
67
+ end
68
+
78
69
  end
79
70
 
80
71
 
@@ -94,102 +85,19 @@ module Fluent
94
85
 
95
86
 
96
87
  def run
97
- call_home(@jpd_url)
98
- # runs the violation pull
99
- last_created_date_string = get_last_item_create_date()
100
- begin
101
- last_created_date = DateTime.parse(last_created_date_string).strftime("%Y-%m-%dT%H:%M:%SZ")
102
- rescue
103
- last_created_date = DateTime.parse("1970-01-01T00:00:00Z").strftime("%Y-%m-%dT%H:%M:%SZ")
104
- end
105
- offset_count=1
106
- left_violations=0
107
- waiting_for_violations = false
108
- xray_json={"filters": { "created_from": last_created_date }, "pagination": {"order_by": "created","limit": @batch_size ,"offset": offset_count } }
109
-
110
- while true
111
- # Grab the batch of records
112
- resp=get_xray_violations(xray_json, @jpd_url)
113
- number_of_violations = JSON.parse(resp)['total_violations']
114
- if left_violations <= 0
115
- left_violations = number_of_violations
116
- end
117
-
118
- xray_violation_urls_list = []
119
- for index in 0..JSON.parse(resp)['violations'].length-1 do
120
- # Get the violation
121
- item = JSON.parse(resp)['violations'][index]
122
-
123
- # Get the created date and check if we should skip (already processed) or process this record.
124
- created_date_string = item['created']
125
- created_date = DateTime.parse(created_date_string).strftime("%Y-%m-%dT%H:%M:%SZ")
88
+ # call_home(@jpd_url)
126
89
 
127
- # Determine if we need to persist this record or not
128
- persistItem = true
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
139
- end
90
+ last_created_date = get_last_item_create_date()
140
91
 
141
- # Publish the record to fluentd
142
- if persistItem
143
-
144
- now = Fluent::Engine.now
145
- router.emit(@tag, now, item)
146
-
147
- # write to the pos_file created_date_string
148
- open(@pos_file, 'a') do |f|
149
- f << "#{created_date_string}\n"
150
- end
151
-
152
- # Mark this as the last record successfully processed
153
- last_created_date_string = created_date_string
154
- last_created_date = created_date
155
-
156
- # Grab violation detail url and add to url list to process w/ thread pool
157
- xray_violation_details_url=item['violation_details_url']
158
- xray_violation_urls_list.append(xray_violation_details_url)
159
- end
160
- end
161
-
162
- # iterate over url array adding to thread pool each url.
163
- # limit max workers to thread count to prevent overloading xray.
164
- thread_pool = Thread.pool(thread_count)
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
- }
170
- thread_pool.shutdown
171
-
172
- # reduce left violations by jump size (not all batches have full item count??)
173
- left_violations = left_violations - @batch_size
174
- if left_violations <= 0
175
- waiting_for_violations = true
176
- sleep(@wait_interval)
177
- else
178
- # Grab the next record to process for the violation details url
179
- waiting_for_violations = false
180
- offset_count = offset_count + 1
181
- xray_json={"filters": { "created_from": last_created_date_string }, "pagination": {"order_by": "created","limit": @batch_size , "offset": offset_count } }
182
- end
183
- end
184
- end
185
-
186
-
187
- # pull the last item create date from the pos_file return created_date_string
188
- def get_last_item_create_date()
189
- if(!(File.exist?(@pos_file)))
190
- @pos_file = File.new(@pos_file, "w")
92
+ if (@from_date != "")
93
+ last_created_date = DateTime.parse(@from_date).strftime("%Y-%m-%dT%H:%M:%SZ")
191
94
  end
192
- return IO.readlines(@pos_file).last
95
+ date_since = last_created_date
96
+ puts "Getting queries from #{date_since}"
97
+ xray = Xray.new(@jpd_url, @username, @apikey, @wait_interval, @batch_size, @pos_file_path, router, @tag)
98
+ violations_channel = xray.violations(date_since)
99
+ xray.violation_details(violations_channel)
100
+ sleep 100
193
101
  end
194
102
 
195
103
  #call home functionality
@@ -203,119 +111,27 @@ module Fluent
203
111
  :password => @apikey,
204
112
  :headers => { :accept => :json, :content_type => :json}
205
113
  ).execute do |response, request, result|
206
- puts "Posting call home information"
114
+ puts "Posting call home information"
207
115
  end
208
116
  end
209
117
 
210
- # queries the xray API for violations based upon the input json
211
- def get_xray_violations_detail(xray_violation_detail_url)
212
- response = RestClient::Request.new(
213
- :method => :get,
214
- :url => xray_violation_detail_url,
215
- :user => @username,
216
- :password => @apikey
217
- ).execute do |response, request, result|
218
- case response.code
219
- when 200
220
- return response.to_str
221
- else
222
- raise Fluent::ConfigError, "Cannot reach Artifactory URL to pull Xray SIEM violations."
223
- end
224
- end
225
- end
226
-
227
-
228
- # queries the xray API for violations based upon the input json
229
- def get_xray_violations(xray_json, jpd_url)
230
- response = RestClient::Request.new(
231
- :method => :post,
232
- :url => jpd_url + "/xray/api/v1/violations",
233
- :payload => xray_json.to_json,
234
- :user => @username,
235
- :password => @apikey,
236
- :headers => { :accept => :json, :content_type => :json}
237
- ).execute do |response, request, result|
238
- case response.code
239
- when 200
240
- return response.to_str
241
- else
242
- raise Fluent::ConfigError, "Cannot reach Artifactory URL to pull Xray SIEM violations."
243
- end
244
- end
245
- end
246
-
247
- # normalizes Xray data according to common information models for all log-vendors
248
- def data_normalization(detailResp)
249
- detailResp_json = JSON.parse(detailResp)
250
- cve = []
251
- cvss_v2_list = []
252
- cvss_v3_list = []
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
268
- end
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
277
- end
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
293
- end
294
- detailResp_json['policies'] = policy_list
295
- detailResp_json['rules'] = rule_list
296
- end
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)
118
+ # pull the last item create date from the pos_file return created_date_string
119
+ def get_last_item_create_date()
120
+ recent_pos_file = get_recent_pos_file()
121
+ if recent_pos_file != nil
122
+ last_created_date_string = IO.readlines(recent_pos_file).last
123
+ return DateTime.parse(last_created_date_string).strftime("%Y-%m-%dT%H:%M:%SZ")
124
+ else
125
+ return DateTime.now.strftime("%Y-%m-%dT%H:%M:%SZ")
303
126
  end
304
- detailResp_json['impacted_artifacts_url'] = impacted_artifact_url_list
305
- return detailResp_json
306
127
  end
307
128
 
308
- def pull_violation_details(xray_violation_detail_url)
309
- begin
310
- detailResp=get_xray_violations_detail(xray_violation_detail_url)
311
- time = Fluent::Engine.now
312
- detailResp_json = data_normalization(detailResp)
313
- router.emit(@tag, time, detailResp_json)
314
- rescue
315
- raise Fluent::ConfigError, "Error pulling violation details url #{xray_violation_detail_url}"
316
- end
129
+ def get_recent_pos_file()
130
+ pos_file = @pos_file_path + "*.siem.pos"
131
+ return Dir.glob(pos_file).sort.last
317
132
  end
318
133
 
319
134
  end
320
135
  end
321
136
  end
137
+