gitlab-secret_detection 0.12.0 → 0.13.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
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f72d7d8262749305fdf1811df74637af4f7ba4bbf693fc41d2cdb34d1ca25238
|
4
|
+
data.tar.gz: 6896b4eb32dc734640c0dc125dd75e79b765f27aa618f73fec81c83c0c20df93
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2d35dfa136b9524f851a6ed20885aa6d50b9334f12166fd9d131ff523ed38bed1264bad953fd6fa58e63fe2c5f9c62be129d3be231258eef25990803b9b67d4e
|
7
|
+
data.tar.gz: 331a74ba7a779f275717c769c0f63785d7e74d25b07da5d07f44c08a1a0c7f343eec3ba7fd8285d549d298fac4d93bdc3f2bc122ea7cb30bd567e4c75e43efdb
|
@@ -7,14 +7,16 @@ module Gitlab
|
|
7
7
|
#
|
8
8
|
# +status+:: One of values from Gitlab::SecretDetection::Core::Status indicating the scan operation's status
|
9
9
|
# +results+:: Array of Gitlab::SecretDetection::Core::Finding values. Default value is nil.
|
10
|
-
# +metadata+:: Hash object containing additional meta information about the response. It is currently used
|
11
10
|
# to embed more information on error.
|
11
|
+
# +applied_exclusions+:: Array of Exclusions that were applied during this scan.
|
12
|
+
# +metadata+:: Hash object containing additional meta information about the response. It is currently used
|
12
13
|
class Response
|
13
|
-
attr_reader :status, :results, :metadata
|
14
|
+
attr_reader :status, :results, :applied_exclusions, :metadata
|
14
15
|
|
15
|
-
def initialize(status
|
16
|
+
def initialize(status:, results: [], applied_exclusions: [], metadata: {})
|
16
17
|
@status = status
|
17
18
|
@results = results
|
19
|
+
@applied_exclusions = applied_exclusions
|
18
20
|
@metadata = metadata
|
19
21
|
end
|
20
22
|
|
@@ -25,15 +27,21 @@ module Gitlab
|
|
25
27
|
def to_h
|
26
28
|
{
|
27
29
|
status:,
|
28
|
-
|
29
|
-
|
30
|
+
results: results&.map(&:to_h),
|
31
|
+
applied_exclusions:,
|
32
|
+
metadata:
|
30
33
|
}
|
31
34
|
end
|
32
35
|
|
33
36
|
protected
|
34
37
|
|
35
38
|
def state
|
36
|
-
[
|
39
|
+
[
|
40
|
+
status,
|
41
|
+
results,
|
42
|
+
applied_exclusions,
|
43
|
+
metadata
|
44
|
+
]
|
37
45
|
end
|
38
46
|
end
|
39
47
|
end
|
@@ -90,7 +90,7 @@ module Gitlab
|
|
90
90
|
subprocess: RUN_IN_SUBPROCESS
|
91
91
|
)
|
92
92
|
|
93
|
-
return Core::Response.new(Core::Status::INPUT_ERROR) unless validate_scan_input(payloads)
|
93
|
+
return Core::Response.new(status: Core::Status::INPUT_ERROR) unless validate_scan_input(payloads)
|
94
94
|
|
95
95
|
# assign defaults since grpc passing zero timeout value to `Timeout.timeout(..)` makes it effectively useless.
|
96
96
|
timeout = DEFAULT_SCAN_TIMEOUT_SECS unless timeout.positive?
|
@@ -102,24 +102,26 @@ module Gitlab
|
|
102
102
|
|
103
103
|
matched_payloads = filter_by_keywords(keyword_matcher, payloads)
|
104
104
|
|
105
|
-
next Core::Response.new(Core::Status::NOT_FOUND) if matched_payloads.empty?
|
105
|
+
next Core::Response.new(status: Core::Status::NOT_FOUND) if matched_payloads.empty?
|
106
106
|
|
107
107
|
scan_args = {
|
108
|
-
payloads: matched_payloads,
|
108
|
+
payloads: matched_payloads,
|
109
|
+
payload_timeout:,
|
109
110
|
pattern_matcher: build_pattern_matcher(tags:),
|
110
|
-
raw_value_exclusions:,
|
111
|
+
raw_value_exclusions:,
|
112
|
+
rule_exclusions:
|
111
113
|
}
|
112
114
|
|
113
|
-
secrets = subprocess ? run_scan_within_subprocess(**scan_args) : run_scan(**scan_args)
|
115
|
+
secrets, applied_exclusions = subprocess ? run_scan_within_subprocess(**scan_args) : run_scan(**scan_args)
|
114
116
|
|
115
117
|
scan_status = overall_scan_status(secrets)
|
116
118
|
|
117
|
-
Core::Response.new(scan_status, secrets)
|
119
|
+
Core::Response.new(status: scan_status, results: secrets, applied_exclusions:)
|
118
120
|
end
|
119
121
|
rescue Timeout::Error => e
|
120
122
|
logger.error "Secret detection operation timed out: #{e}"
|
121
123
|
|
122
|
-
Core::Response.new(Core::Status::SCAN_TIMEOUT)
|
124
|
+
Core::Response.new(status: Core::Status::SCAN_TIMEOUT)
|
123
125
|
end
|
124
126
|
|
125
127
|
private
|
@@ -208,25 +210,42 @@ module Gitlab
|
|
208
210
|
# literal values to exclude from the input before the scan, also SD rules to exclude during
|
209
211
|
# the scan when performed on the payloads.
|
210
212
|
def run_scan(
|
211
|
-
payloads:,
|
212
|
-
|
213
|
+
payloads:,
|
214
|
+
payload_timeout:,
|
215
|
+
pattern_matcher:,
|
216
|
+
raw_value_exclusions: [],
|
217
|
+
rule_exclusions: []
|
218
|
+
)
|
219
|
+
all_applied_exclusions = Set.new
|
220
|
+
|
221
|
+
all_findings = payloads.flat_map do |payload|
|
213
222
|
Timeout.timeout(payload_timeout) do
|
214
|
-
find_secrets_in_payload(
|
223
|
+
findings, applied_exclusions = find_secrets_in_payload(
|
215
224
|
payload:,
|
216
225
|
pattern_matcher:,
|
217
|
-
raw_value_exclusions:,
|
226
|
+
raw_value_exclusions:,
|
227
|
+
rule_exclusions:
|
218
228
|
)
|
229
|
+
all_applied_exclusions.merge(applied_exclusions)
|
230
|
+
findings
|
219
231
|
end
|
220
232
|
rescue Timeout::Error => e
|
221
233
|
logger.error "Secret Detection scan timed out on the payload(id:#{payload.id}): #{e}"
|
234
|
+
|
222
235
|
Core::Finding.new(payload.id,
|
223
236
|
Core::Status::PAYLOAD_TIMEOUT)
|
224
237
|
end
|
238
|
+
[all_findings.freeze, all_applied_exclusions.to_a.freeze]
|
225
239
|
end
|
226
240
|
|
227
241
|
def run_scan_within_subprocess(
|
228
|
-
payloads:,
|
229
|
-
|
242
|
+
payloads:,
|
243
|
+
payload_timeout:,
|
244
|
+
pattern_matcher:,
|
245
|
+
raw_value_exclusions: [],
|
246
|
+
rule_exclusions: []
|
247
|
+
)
|
248
|
+
all_applied_exclusions = Set.new
|
230
249
|
payload_sizes = payloads.map(&:size)
|
231
250
|
grouped_payload_indices = group_by_chunk_size(payload_sizes)
|
232
251
|
|
@@ -239,19 +258,22 @@ module Gitlab
|
|
239
258
|
) do |grouped_payload|
|
240
259
|
grouped_payload.flat_map do |payload|
|
241
260
|
Timeout.timeout(payload_timeout) do
|
242
|
-
find_secrets_in_payload(
|
261
|
+
findings, applied_exclusions = find_secrets_in_payload(
|
243
262
|
payload:,
|
244
263
|
pattern_matcher:,
|
245
264
|
raw_value_exclusions:, rule_exclusions:
|
246
265
|
)
|
266
|
+
all_applied_exclusions.merge(applied_exclusions)
|
267
|
+
findings
|
247
268
|
end
|
248
269
|
rescue Timeout::Error => e
|
249
270
|
logger.error "Secret Detection scan timed out on the payload(id:#{payload.id}): #{e}"
|
271
|
+
|
250
272
|
Core::Finding.new(payload.id, Core::Status::PAYLOAD_TIMEOUT)
|
251
273
|
end
|
252
274
|
end
|
253
275
|
|
254
|
-
found_secrets.freeze
|
276
|
+
[found_secrets.freeze, all_applied_exclusions.to_a.freeze]
|
255
277
|
end
|
256
278
|
|
257
279
|
# Finds secrets in the given payload guarded with a timeout as a circuit breaker. It accepts
|
@@ -259,6 +281,7 @@ module Gitlab
|
|
259
281
|
# the scan.
|
260
282
|
def find_secrets_in_payload(payload:, pattern_matcher:, raw_value_exclusions: [], rule_exclusions: [])
|
261
283
|
findings = []
|
284
|
+
applied_exclusions = Set.new
|
262
285
|
|
263
286
|
payload_offset = payload.respond_to?(:offset) ? payload.offset : 0
|
264
287
|
|
@@ -268,6 +291,7 @@ module Gitlab
|
|
268
291
|
unless raw_value_exclusions.empty?
|
269
292
|
raw_value_exclusions.each do |value|
|
270
293
|
line.gsub!(value, '') # replace input that doesn't contain allowed value in it
|
294
|
+
applied_exclusions << value # TODO we need the id of the exclusion
|
271
295
|
end
|
272
296
|
end
|
273
297
|
|
@@ -284,19 +308,30 @@ module Gitlab
|
|
284
308
|
matches.each do |match_idx|
|
285
309
|
rule = rules[match_idx]
|
286
310
|
|
287
|
-
next if
|
311
|
+
next if applied_rule_exclusion?(rule[:id], rule_exclusions, applied_exclusions)
|
288
312
|
|
289
313
|
title = rule[:title].nil? ? rule[:description] : rule[:title]
|
290
|
-
|
291
|
-
|
314
|
+
|
315
|
+
findings << Core::Finding.new(
|
316
|
+
payload.id,
|
317
|
+
Core::Status::FOUND,
|
318
|
+
line_no,
|
319
|
+
rule[:id],
|
320
|
+
title
|
321
|
+
)
|
292
322
|
end
|
293
323
|
end
|
294
324
|
|
295
|
-
findings.freeze
|
325
|
+
[findings.freeze, applied_exclusions]
|
296
326
|
rescue StandardError => e
|
297
327
|
logger.error "Secret Detection scan failed on the payload(id:#{payload.id}): #{e}"
|
298
328
|
|
299
|
-
Core::Finding.new(payload.id, Core::Status::SCAN_ERROR)
|
329
|
+
[[Core::Finding.new(payload.id, Core::Status::SCAN_ERROR)], []]
|
330
|
+
end
|
331
|
+
|
332
|
+
def applied_rule_exclusion?(type, rule_exclusions, applied_exclusions)
|
333
|
+
applied_exclusion = rule_exclusions&.find { |rule_exclusion| rule_exclusion == type }
|
334
|
+
applied_exclusion && (applied_exclusions << applied_exclusion)
|
300
335
|
end
|
301
336
|
|
302
337
|
# Validates the given payloads by verifying the type and
|
@@ -17,10 +17,11 @@ module Gitlab
|
|
17
17
|
# Time to wait for the response from the service
|
18
18
|
REQUEST_TIMEOUT_SECONDS = 10 # 10 seconds
|
19
19
|
|
20
|
-
def initialize(host, secure: false, compression: true)
|
20
|
+
def initialize(host, secure: false, compression: true, logger: nil)
|
21
21
|
@host = host
|
22
22
|
@secure = secure
|
23
23
|
@compression = compression
|
24
|
+
@logger = logger.nil? ? LOGGER : logger
|
24
25
|
end
|
25
26
|
|
26
27
|
# Triggers Secret Detection service's `/Scan` gRPC endpoint. To keep it consistent with SDS gem interface,
|
@@ -116,14 +117,18 @@ module Gitlab
|
|
116
117
|
def with_rescued_errors
|
117
118
|
yield
|
118
119
|
rescue ::GRPC::Unauthenticated
|
119
|
-
SecretDetection::Core::Response.new(SecretDetection::Core::Status::AUTH_ERROR)
|
120
|
+
SecretDetection::Core::Response.new(status: SecretDetection::Core::Status::AUTH_ERROR)
|
120
121
|
rescue ::GRPC::InvalidArgument => e
|
121
122
|
SecretDetection::Core::Response.new(
|
122
|
-
SecretDetection::Core::Status::INPUT_ERROR,
|
123
|
+
status: SecretDetection::Core::Status::INPUT_ERROR,
|
124
|
+
results: nil,
|
125
|
+
metadata: { message: e.details, **e.metadata }
|
123
126
|
)
|
124
127
|
rescue ::GRPC::Unknown, ::GRPC::BadStatus => e
|
125
128
|
SecretDetection::Core::Response.new(
|
126
|
-
SecretDetection::Core::Status::SCAN_ERROR,
|
129
|
+
status: SecretDetection::Core::Status::SCAN_ERROR,
|
130
|
+
results: nil,
|
131
|
+
metadata: { message: e.details }
|
127
132
|
)
|
128
133
|
end
|
129
134
|
|
@@ -131,13 +136,13 @@ module Gitlab
|
|
131
136
|
response = grpc_response.to_h
|
132
137
|
|
133
138
|
SecretDetection::Core::Response.new(
|
134
|
-
response[:status],
|
135
|
-
response[:results],
|
136
|
-
response[:metadata]
|
139
|
+
status: response[:status],
|
140
|
+
results: response[:results],
|
141
|
+
metadata: response[:metadata]
|
137
142
|
)
|
138
143
|
rescue StandardError => e
|
139
|
-
logger.error("Failed to convert to core response: #{e}")
|
140
|
-
SecretDetection::Core::Response.new(SecretDetection::Core::Status::SCAN_ERROR)
|
144
|
+
@logger.error("Failed to convert to core response: #{e}")
|
145
|
+
SecretDetection::Core::Response.new(status: SecretDetection::Core::Status::SCAN_ERROR)
|
141
146
|
end
|
142
147
|
end
|
143
148
|
end
|