fluent-plugin-jfrog-siem 1.0.0 → 2.0.0
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/.rspec +1 -0
- data/Gemfile.lock +90 -0
- data/Rakefile +1 -1
- data/fluent-plugin-jfrog-siem.gemspec +8 -3
- data/lib/fluent/plugin/in_jfrog_siem.rb +31 -221
- data/lib/fluent/plugin/position_file.rb +32 -0
- data/lib/fluent/plugin/violations.json +380 -0
- data/lib/fluent/plugin/xray.rb +164 -0
- data/spec/position_file_spec.rb +56 -0
- data/spec/spec_helper.rb +111 -0
- data/spec/xray_spec.rb +135 -0
- data/test/helper.rb +1 -0
- data/test/plugin/test_in_jfrog_siem.rb +31 -6
- metadata +75 -8
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: e34dd9d1fb60e95b931f7c67bb508e635057b9e2f349f681eb2b54cb182c2396
|
|
4
|
+
data.tar.gz: b0acda337195accb81ce6bf3ee7bf05ef7e79630793dbc7553c0df5e541e26a3
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 9116c506cf562af1566e6001748f5464752b2c07aace2d9f6fe04573c5a97db4cbf0e638d048dea536387559a71fa493fad882dc55650f1597c4030ab13be962
|
|
7
|
+
data.tar.gz: 2aaeaa3f4a22753db0b97c35b93938c4049cb9037f56f3cc6199dc222034a9d26609e1906973c0b9f11960f856227b2cf93ab000e8bd677aee9045df0bd684b0
|
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/Rakefile
CHANGED
|
@@ -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 = "
|
|
6
|
+
spec.version = "2.0.0"
|
|
7
7
|
spec.authors = ["John Peterson", "Mahitha Byreddy"]
|
|
8
8
|
spec.email = ["johnp@jfrog.com", "mahithab@jfrog.com"]
|
|
9
9
|
|
|
@@ -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 "
|
|
28
|
-
spec.
|
|
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,108 +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
|
-
|
|
128
|
-
persistItem = true
|
|
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
|
|
145
|
-
end
|
|
90
|
+
last_created_date = get_last_item_create_date()
|
|
146
91
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
now = Fluent::Engine.now
|
|
151
|
-
router.emit(@tag, now, item)
|
|
152
|
-
|
|
153
|
-
# write to the pos_file created_date_string
|
|
154
|
-
open(@pos_file, 'a') do |f|
|
|
155
|
-
f << "#{created_date_string}\n"
|
|
156
|
-
end
|
|
157
|
-
|
|
158
|
-
# Mark this as the last record successfully processed
|
|
159
|
-
last_created_date_string = created_date_string
|
|
160
|
-
last_created_date = created_date
|
|
161
|
-
|
|
162
|
-
# Grab violation detail url and add to url list to process w/ thread pool
|
|
163
|
-
xray_violation_details_url=item['violation_details_url']
|
|
164
|
-
xray_violation_urls_list.append(xray_violation_details_url)
|
|
165
|
-
end
|
|
166
|
-
end
|
|
167
|
-
|
|
168
|
-
# iterate over url array adding to thread pool each url.
|
|
169
|
-
# limit max workers to thread count to prevent overloading xray.
|
|
170
|
-
thread_pool = Thread.pool(thread_count)
|
|
171
|
-
thread_pool.process {
|
|
172
|
-
for xray_violation_url in xray_violation_urls_list do
|
|
173
|
-
pull_violation_details(xray_violation_url)
|
|
174
|
-
end
|
|
175
|
-
}
|
|
176
|
-
thread_pool.shutdown
|
|
177
|
-
|
|
178
|
-
# reduce left violations by jump size (not all batches have full item count??)
|
|
179
|
-
left_violations = left_violations - @batch_size
|
|
180
|
-
if left_violations <= 0
|
|
181
|
-
waiting_for_violations = true
|
|
182
|
-
sleep(@wait_interval)
|
|
183
|
-
else
|
|
184
|
-
# Grab the next record to process for the violation details url
|
|
185
|
-
waiting_for_violations = false
|
|
186
|
-
offset_count = offset_count + 1
|
|
187
|
-
xray_json={"filters": { "created_from": last_created_date_string }, "pagination": {"order_by": "created","limit": @batch_size , "offset": offset_count } }
|
|
188
|
-
end
|
|
189
|
-
end
|
|
190
|
-
end
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
# pull the last item create date from the pos_file return created_date_string
|
|
194
|
-
def get_last_item_create_date()
|
|
195
|
-
if(!(File.exist?(@pos_file)))
|
|
196
|
-
@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")
|
|
197
94
|
end
|
|
198
|
-
|
|
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
|
|
199
101
|
end
|
|
200
102
|
|
|
201
103
|
#call home functionality
|
|
@@ -209,119 +111,27 @@ module Fluent
|
|
|
209
111
|
:password => @apikey,
|
|
210
112
|
:headers => { :accept => :json, :content_type => :json}
|
|
211
113
|
).execute do |response, request, result|
|
|
212
|
-
|
|
114
|
+
puts "Posting call home information"
|
|
213
115
|
end
|
|
214
116
|
end
|
|
215
117
|
|
|
216
|
-
#
|
|
217
|
-
def
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
case response.code
|
|
225
|
-
when 200
|
|
226
|
-
return response.to_str
|
|
227
|
-
else
|
|
228
|
-
raise Fluent::ConfigError, "Cannot reach Artifactory URL to pull Xray SIEM violations."
|
|
229
|
-
end
|
|
230
|
-
end
|
|
231
|
-
end
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
# queries the xray API for violations based upon the input json
|
|
235
|
-
def get_xray_violations(xray_json, jpd_url)
|
|
236
|
-
response = RestClient::Request.new(
|
|
237
|
-
:method => :post,
|
|
238
|
-
:url => jpd_url + "/xray/api/v1/violations",
|
|
239
|
-
:payload => xray_json.to_json,
|
|
240
|
-
:user => @username,
|
|
241
|
-
:password => @apikey,
|
|
242
|
-
:headers => { :accept => :json, :content_type => :json}
|
|
243
|
-
).execute do |response, request, result|
|
|
244
|
-
case response.code
|
|
245
|
-
when 200
|
|
246
|
-
return response.to_str
|
|
247
|
-
else
|
|
248
|
-
raise Fluent::ConfigError, "Cannot reach Artifactory URL to pull Xray SIEM violations."
|
|
249
|
-
end
|
|
250
|
-
end
|
|
251
|
-
end
|
|
252
|
-
|
|
253
|
-
# normalizes Xray data according to common information models for all log-vendors
|
|
254
|
-
def data_normalization(detailResp)
|
|
255
|
-
detailResp_json = JSON.parse(detailResp)
|
|
256
|
-
cve = []
|
|
257
|
-
cvss_v2_list = []
|
|
258
|
-
cvss_v3_list = []
|
|
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
|
|
274
|
-
end
|
|
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
|
|
283
|
-
end
|
|
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
|
|
299
|
-
end
|
|
300
|
-
detailResp_json['policies'] = policy_list
|
|
301
|
-
detailResp_json['rules'] = rule_list
|
|
302
|
-
end
|
|
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)
|
|
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")
|
|
309
126
|
end
|
|
310
|
-
detailResp_json['impacted_artifacts_url'] = impacted_artifact_url_list
|
|
311
|
-
return detailResp_json
|
|
312
127
|
end
|
|
313
128
|
|
|
314
|
-
def
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
time = Fluent::Engine.now
|
|
318
|
-
detailResp_json = data_normalization(detailResp)
|
|
319
|
-
router.emit(@tag, time, detailResp_json)
|
|
320
|
-
rescue
|
|
321
|
-
raise Fluent::ConfigError, "Error pulling violation details url #{xray_violation_detail_url}"
|
|
322
|
-
end
|
|
129
|
+
def get_recent_pos_file()
|
|
130
|
+
pos_file = @pos_file_path + "*.siem.pos"
|
|
131
|
+
return Dir.glob(pos_file).sort.last
|
|
323
132
|
end
|
|
324
133
|
|
|
325
134
|
end
|
|
326
135
|
end
|
|
327
136
|
end
|
|
137
|
+
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
class PositionFile
|
|
2
|
+
|
|
3
|
+
def initialize(pos_file_path)
|
|
4
|
+
@pos_file_path = pos_file_path
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
def processed?(violation)
|
|
8
|
+
File.exist?(pos_file_name(violation)) && found?(violation)
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def write(violation)
|
|
12
|
+
File.open(pos_file_name(violation), 'a') do |f|
|
|
13
|
+
f << violation_entry(violation)
|
|
14
|
+
f << "\n"
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
private
|
|
19
|
+
def found?(violation)
|
|
20
|
+
return File.open(pos_file_name(violation)) { |f| f.find { |line| line.include? violation_entry(violation) } }
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def violation_entry(violation)
|
|
24
|
+
created_date = DateTime.parse(violation['created']).strftime("%Y-%m-%dT%H:%M:%SZ")
|
|
25
|
+
[created_date, violation['watch_name'], violation['issue_id']].join(',')
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def pos_file_name(violation)
|
|
29
|
+
pos_file_date = DateTime.parse(violation['created']).strftime("%Y-%m-%d")
|
|
30
|
+
@pos_file_path + "jfrog_siem_log_#{pos_file_date}.siem.pos"
|
|
31
|
+
end
|
|
32
|
+
end
|