gitlab-secret_detection 0.11.1 → 0.39.2
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/README.md +34 -26
- data/lib/gitlab/secret_detection/core/response.rb +16 -6
- data/lib/gitlab/secret_detection/core/ruleset.rb +30 -3
- data/lib/gitlab/secret_detection/core/scanner.rb +308 -77
- data/lib/gitlab/secret_detection/core/secret_push_protection_rules.toml +1072 -0
- data/lib/gitlab/secret_detection/core/status.rb +34 -0
- data/lib/gitlab/secret_detection/grpc/client/grpc_client.rb +50 -19
- data/lib/gitlab/secret_detection/grpc/generated/secret_detection_pb.rb +1 -1
- data/lib/gitlab/secret_detection/grpc/integrated_error_tracking.rb +64 -0
- data/lib/gitlab/secret_detection/grpc/scanner_service.rb +35 -16
- data/lib/gitlab/secret_detection/grpc.rb +1 -0
- data/lib/gitlab/secret_detection/utils/masker.rb +43 -0
- data/lib/gitlab/secret_detection/utils.rb +1 -0
- data/lib/gitlab/secret_detection/version.rb +3 -17
- data/proto/secret_detection.proto +3 -0
- metadata +209 -19
- data/lib/gitlab.rb +0 -6
|
@@ -5,6 +5,8 @@ module Gitlab
|
|
|
5
5
|
module Core
|
|
6
6
|
# All the possible statuses emitted by the scan operation
|
|
7
7
|
class Status
|
|
8
|
+
# These values must stay in-sync with the GRPC::ScanResponse::Status values
|
|
9
|
+
UNSPECIFIED = 0 # to match the GRPC::Status values
|
|
8
10
|
FOUND = 1 # When scan operation completes with one or more findings
|
|
9
11
|
FOUND_WITH_ERRORS = 2 # When scan operation completes with one or more findings along with some errors
|
|
10
12
|
SCAN_TIMEOUT = 3 # When the scan operation runs beyond given time out
|
|
@@ -13,6 +15,38 @@ module Gitlab
|
|
|
13
15
|
INPUT_ERROR = 6 # When the scan operation fails due to invalid input
|
|
14
16
|
NOT_FOUND = 7 # When scan operation completes with zero findings
|
|
15
17
|
AUTH_ERROR = 8 # When authentication fails
|
|
18
|
+
|
|
19
|
+
# Maps values to constants
|
|
20
|
+
@values_map = {}
|
|
21
|
+
|
|
22
|
+
# Using class instance variables and singleton methods
|
|
23
|
+
class << self
|
|
24
|
+
attr_reader :values_map
|
|
25
|
+
|
|
26
|
+
# Register constants and their values in the map
|
|
27
|
+
def const_set(name, value)
|
|
28
|
+
const = super
|
|
29
|
+
@values_map[value] = name
|
|
30
|
+
const
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# Look up a constant by its value
|
|
34
|
+
def find_by_value(value)
|
|
35
|
+
const_name = @values_map[value]
|
|
36
|
+
const_name ? const_get(const_name) : nil
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# Get the name of a constant by its value
|
|
40
|
+
def name_by_value(value)
|
|
41
|
+
@values_map[value]
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# Initialize the values map with existing constants
|
|
46
|
+
constants.each do |const_name|
|
|
47
|
+
const_value = const_get(const_name)
|
|
48
|
+
@values_map[const_value] = const_name
|
|
49
|
+
end
|
|
16
50
|
end
|
|
17
51
|
end
|
|
18
52
|
end
|
|
@@ -17,10 +17,14 @@ module Gitlab
|
|
|
17
17
|
# Time to wait for the response from the service
|
|
18
18
|
REQUEST_TIMEOUT_SECONDS = 10 # 10 seconds
|
|
19
19
|
|
|
20
|
-
|
|
20
|
+
# Total payload size limit allowed per scan request
|
|
21
|
+
MAX_PAYLOAD_SIZE_PER_REQUEST = 4_000_000 # 3.8MiB (0.2MiB buffer for other request props)
|
|
22
|
+
|
|
23
|
+
def initialize(host, secure: false, compression: true, logger: nil)
|
|
21
24
|
@host = host
|
|
22
25
|
@secure = secure
|
|
23
26
|
@compression = compression
|
|
27
|
+
@logger = logger.nil? ? LOGGER : logger
|
|
24
28
|
end
|
|
25
29
|
|
|
26
30
|
# Triggers Secret Detection service's `/Scan` gRPC endpoint. To keep it consistent with SDS gem interface,
|
|
@@ -29,13 +33,27 @@ module Gitlab
|
|
|
29
33
|
# +Gitlab::SecretDetection::Core::Response+ type by assiging a appropriate +status+ value to it.
|
|
30
34
|
def run_scan(request:, auth_token:, extra_headers: {})
|
|
31
35
|
with_rescued_errors do
|
|
36
|
+
payload_size = calculate_payload_size(request)
|
|
37
|
+
if payload_size >= MAX_PAYLOAD_SIZE_PER_REQUEST
|
|
38
|
+
@logger.info(
|
|
39
|
+
message: "Skipping to send Scan Request to Secret Detection server due to request size overlimit",
|
|
40
|
+
payload_size:
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
next Gitlab::SecretDetection::GRPC::ScanResponse.new(
|
|
44
|
+
results: [],
|
|
45
|
+
status: SecretDetection::Core::Status::INPUT_ERROR,
|
|
46
|
+
applied_exclusions: []
|
|
47
|
+
)
|
|
48
|
+
end
|
|
49
|
+
|
|
32
50
|
grpc_response = stub.scan(
|
|
33
51
|
request,
|
|
34
52
|
metadata: build_metadata(auth_token, extra_headers),
|
|
35
53
|
deadline: request_deadline
|
|
36
54
|
)
|
|
37
55
|
|
|
38
|
-
|
|
56
|
+
grpc_response
|
|
39
57
|
end
|
|
40
58
|
end
|
|
41
59
|
|
|
@@ -51,16 +69,28 @@ module Gitlab
|
|
|
51
69
|
request_stream = Gitlab::SecretDetection::GRPC::StreamRequestEnumerator.new(requests)
|
|
52
70
|
results = []
|
|
53
71
|
with_rescued_errors do
|
|
72
|
+
has_oversized_request = requests.any? do |request|
|
|
73
|
+
payload_size = calculate_payload_size(request)
|
|
74
|
+
payload_size >= MAX_PAYLOAD_SIZE_PER_REQUEST
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
if has_oversized_request
|
|
78
|
+
@logger.info("Skipping to send Scan Request to Secret Detection server due to request size overlimit")
|
|
79
|
+
response = Gitlab::SecretDetection::GRPC::ScanResponse.new(
|
|
80
|
+
status: SecretDetection::Core::Status::INPUT_ERROR
|
|
81
|
+
)
|
|
82
|
+
next (block_given? ? response : [response])
|
|
83
|
+
end
|
|
84
|
+
|
|
54
85
|
stub.scan_stream(
|
|
55
86
|
request_stream.each_item,
|
|
56
87
|
metadata: build_metadata(auth_token, extra_headers),
|
|
57
88
|
deadline: request_deadline
|
|
58
89
|
).each do |grpc_response|
|
|
59
|
-
response = convert_to_core_response(grpc_response)
|
|
60
90
|
if block_given?
|
|
61
|
-
yield
|
|
91
|
+
yield grpc_response
|
|
62
92
|
else
|
|
63
|
-
results <<
|
|
93
|
+
results << grpc_response
|
|
64
94
|
end
|
|
65
95
|
end
|
|
66
96
|
results
|
|
@@ -116,28 +146,29 @@ module Gitlab
|
|
|
116
146
|
def with_rescued_errors
|
|
117
147
|
yield
|
|
118
148
|
rescue ::GRPC::Unauthenticated
|
|
119
|
-
SecretDetection::Core::Response.new(SecretDetection::Core::Status::AUTH_ERROR)
|
|
149
|
+
SecretDetection::Core::Response.new(status: SecretDetection::Core::Status::AUTH_ERROR)
|
|
120
150
|
rescue ::GRPC::InvalidArgument => e
|
|
121
151
|
SecretDetection::Core::Response.new(
|
|
122
|
-
SecretDetection::Core::Status::INPUT_ERROR,
|
|
152
|
+
status: SecretDetection::Core::Status::INPUT_ERROR,
|
|
153
|
+
results: nil,
|
|
154
|
+
metadata: { message: e.details, **e.metadata }
|
|
155
|
+
)
|
|
156
|
+
rescue ::GRPC::ResourceExhausted => e
|
|
157
|
+
@logger.error(message: "Secret Detection Server resource exhausted: #{e.details}", **e.metadata)
|
|
158
|
+
SecretDetection::Core::Response.new(
|
|
159
|
+
status: SecretDetection::Core::Status::SCAN_ERROR,
|
|
160
|
+
metadata: { message: e.details, **e.metadata }
|
|
123
161
|
)
|
|
124
162
|
rescue ::GRPC::Unknown, ::GRPC::BadStatus => e
|
|
125
163
|
SecretDetection::Core::Response.new(
|
|
126
|
-
SecretDetection::Core::Status::SCAN_ERROR,
|
|
164
|
+
status: SecretDetection::Core::Status::SCAN_ERROR,
|
|
165
|
+
results: nil,
|
|
166
|
+
metadata: { message: e.details, **e.metadata }
|
|
127
167
|
)
|
|
128
168
|
end
|
|
129
169
|
|
|
130
|
-
def
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
SecretDetection::Core::Response.new(
|
|
134
|
-
response[:status],
|
|
135
|
-
response[:results],
|
|
136
|
-
response[:metadata]
|
|
137
|
-
)
|
|
138
|
-
rescue StandardError => e
|
|
139
|
-
logger.error("Failed to convert to core response: #{e}")
|
|
140
|
-
SecretDetection::Core::Response.new(SecretDetection::Core::Status::SCAN_ERROR)
|
|
170
|
+
def calculate_payload_size(request)
|
|
171
|
+
request&.payloads&.reduce(0) { |total, p| total + p.data.size + p.id.size }
|
|
141
172
|
end
|
|
142
173
|
end
|
|
143
174
|
end
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
require 'google/protobuf'
|
|
6
6
|
|
|
7
7
|
|
|
8
|
-
descriptor_data = "\n\x16secret_detection.proto\x12\x17gitlab.secret_detection\"Z\n\tExclusion\x12>\n\x0e\x65xclusion_type\x18\x01 \x01(\x0e\x32&.gitlab.secret_detection.ExclusionType\x12\r\n\x05value\x18\x02 \x01(\t\"\xc0\x02\n\x0bScanRequest\x12>\n\x08payloads\x18\x01 \x03(\x0b\x32,.gitlab.secret_detection.ScanRequest.Payload\x12\x19\n\x0ctimeout_secs\x18\x02 \x01(\x02H\x00\x88\x01\x01\x12!\n\x14payload_timeout_secs\x18\x03 \x01(\x02H\x01\x88\x01\x01\x12\x36\n\nexclusions\x18\x04 \x03(\x0b\x32\".gitlab.secret_detection.Exclusion\x12\x0c\n\x04tags\x18\x05 \x03(\t\x1a\x43\n\x07Payload\x12\n\n\x02id\x18\x01 \x01(\t\x12\x0c\n\x04\x64\x61ta\x18\x02 \x01(\t\x12\x13\n\x06offset\x18\x03 \x01(\x05H\x00\x88\x01\x01\x42\t\n\x07_offsetB\x0f\n\r_timeout_secsB\x17\n\x15_payload_timeout_secs\"\xa2\x04\n\x0cScanResponse\x12>\n\x07results\x18\x01 \x03(\x0b\x32-.gitlab.secret_detection.ScanResponse.Finding\x12\x0e\n\x06status\x18\x02 \x01(\x05\x12>\n\x12\x61pplied_exclusions\x18\x03 \x03(\x0b\x32\".gitlab.secret_detection.Exclusion\x1a\x9d\x01\n\x07\x46inding\x12\x12\n\npayload_id\x18\x01 \x01(\t\x12\x0e\n\x06status\x18\x02 \x01(\x05\x12\x11\n\x04type\x18\x03 \x01(\tH\x00\x88\x01\x01\x12\x18\n\x0b\x64\x65scription\x18\x04 \x01(\tH\x01\x88\x01\x01\x12\x18\n\x0bline_number\x18\x05 \x01(\x05H\x02\x88\x01\x01\x42\x07\n\x05_typeB\x0e\n\x0c_descriptionB\x0e\n\x0c_line_number\"\xe1\x01\n\x06Status\x12\x16\n\x12STATUS_UNSPECIFIED\x10\x00\x12\x10\n\x0cSTATUS_FOUND\x10\x01\x12\x1c\n\x18STATUS_FOUND_WITH_ERRORS\x10\x02\x12\x17\n\x13STATUS_SCAN_TIMEOUT\x10\x03\x12\x1a\n\x16STATUS_PAYLOAD_TIMEOUT\x10\x04\x12\x15\n\x11STATUS_SCAN_ERROR\x10\x05\x12\x16\n\x12STATUS_INPUT_ERROR\x10\x06\x12\x14\n\x10STATUS_NOT_FOUND\x10\x07\x12\x15\n\x11STATUS_AUTH_ERROR\x10\x08
|
|
8
|
+
descriptor_data = "\n\x16secret_detection.proto\x12\x17gitlab.secret_detection\"Z\n\tExclusion\x12>\n\x0e\x65xclusion_type\x18\x01 \x01(\x0e\x32&.gitlab.secret_detection.ExclusionType\x12\r\n\x05value\x18\x02 \x01(\t\"\xc0\x02\n\x0bScanRequest\x12>\n\x08payloads\x18\x01 \x03(\x0b\x32,.gitlab.secret_detection.ScanRequest.Payload\x12\x19\n\x0ctimeout_secs\x18\x02 \x01(\x02H\x00\x88\x01\x01\x12!\n\x14payload_timeout_secs\x18\x03 \x01(\x02H\x01\x88\x01\x01\x12\x36\n\nexclusions\x18\x04 \x03(\x0b\x32\".gitlab.secret_detection.Exclusion\x12\x0c\n\x04tags\x18\x05 \x03(\t\x1a\x43\n\x07Payload\x12\n\n\x02id\x18\x01 \x01(\t\x12\x0c\n\x04\x64\x61ta\x18\x02 \x01(\t\x12\x13\n\x06offset\x18\x03 \x01(\x05H\x00\x88\x01\x01\x42\t\n\x07_offsetB\x0f\n\r_timeout_secsB\x17\n\x15_payload_timeout_secs\"\xa2\x04\n\x0cScanResponse\x12>\n\x07results\x18\x01 \x03(\x0b\x32-.gitlab.secret_detection.ScanResponse.Finding\x12\x0e\n\x06status\x18\x02 \x01(\x05\x12>\n\x12\x61pplied_exclusions\x18\x03 \x03(\x0b\x32\".gitlab.secret_detection.Exclusion\x1a\x9d\x01\n\x07\x46inding\x12\x12\n\npayload_id\x18\x01 \x01(\t\x12\x0e\n\x06status\x18\x02 \x01(\x05\x12\x11\n\x04type\x18\x03 \x01(\tH\x00\x88\x01\x01\x12\x18\n\x0b\x64\x65scription\x18\x04 \x01(\tH\x01\x88\x01\x01\x12\x18\n\x0bline_number\x18\x05 \x01(\x05H\x02\x88\x01\x01\x42\x07\n\x05_typeB\x0e\n\x0c_descriptionB\x0e\n\x0c_line_number\"\xe1\x01\n\x06Status\x12\x16\n\x12STATUS_UNSPECIFIED\x10\x00\x12\x10\n\x0cSTATUS_FOUND\x10\x01\x12\x1c\n\x18STATUS_FOUND_WITH_ERRORS\x10\x02\x12\x17\n\x13STATUS_SCAN_TIMEOUT\x10\x03\x12\x1a\n\x16STATUS_PAYLOAD_TIMEOUT\x10\x04\x12\x15\n\x11STATUS_SCAN_ERROR\x10\x05\x12\x16\n\x12STATUS_INPUT_ERROR\x10\x06\x12\x14\n\x10STATUS_NOT_FOUND\x10\x07\x12\x15\n\x11STATUS_AUTH_ERROR\x10\x08*\xa1\x01\n\rExclusionType\x12\x1e\n\x1a\x45XCLUSION_TYPE_UNSPECIFIED\x10\x00\x12\x17\n\x13\x45XCLUSION_TYPE_RULE\x10\x01\x12\x1c\n\x18\x45XCLUSION_TYPE_RAW_VALUE\x10\x02\x12\x17\n\x13\x45XCLUSION_TYPE_PATH\x10\x03\x12 \n\x1c\x45XCLUSION_TYPE_REGEX_PATTERN\x10\x04\x32\xc1\x01\n\x07Scanner\x12U\n\x04Scan\x12$.gitlab.secret_detection.ScanRequest\x1a%.gitlab.secret_detection.ScanResponse\"\x00\x12_\n\nScanStream\x12$.gitlab.secret_detection.ScanRequest\x1a%.gitlab.secret_detection.ScanResponse\"\x00(\x01\x30\x01\x42 \xea\x02\x1dGitlab::SecretDetection::GRPCb\x06proto3"
|
|
9
9
|
|
|
10
10
|
pool = Google::Protobuf::DescriptorPool.generated_pool
|
|
11
11
|
pool.add_serialized_file(descriptor_data)
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'sentry-ruby'
|
|
4
|
+
|
|
5
|
+
require_relative '../../../../lib/gitlab/secret_detection/core/ruleset'
|
|
6
|
+
|
|
7
|
+
module Gitlab
|
|
8
|
+
module SecretDetection
|
|
9
|
+
module GRPC
|
|
10
|
+
module IntegratedErrorTracking
|
|
11
|
+
extend self
|
|
12
|
+
|
|
13
|
+
def track_exception(exception, args = {})
|
|
14
|
+
unless Sentry.initialized?
|
|
15
|
+
logger.warn(message: "Cannot track exception in Error Tracking as Sentry is not initialized")
|
|
16
|
+
return
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
args[:ruleset_version] = ruleset_version
|
|
20
|
+
|
|
21
|
+
Sentry.capture_exception(exception, **args)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def setup(logger: Logger.new($stdout))
|
|
25
|
+
if Sentry.initialized?
|
|
26
|
+
logger.warn(message: "Sentry is already initialized, skipping re-setup")
|
|
27
|
+
return
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
logger.info(message: "Initializing Sentry SDK for Integrated Error Tracking..")
|
|
31
|
+
|
|
32
|
+
unless can_setup_sentry?
|
|
33
|
+
logger.warn(message: "Integrated Error Tracking not available, skipping Sentry SDK initialization")
|
|
34
|
+
return false
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
Sentry.init do |config|
|
|
38
|
+
config.dsn = ENV.fetch('SD_TRACKING_DSN')
|
|
39
|
+
config.environment = ENV.fetch('SD_ENV')
|
|
40
|
+
config.release = Gitlab::SecretDetection::Gem::VERSION
|
|
41
|
+
config.send_default_pii = true
|
|
42
|
+
config.send_modules = false
|
|
43
|
+
config.traces_sample_rate = 0.2 if ENV.fetch('ENABLE_SENTRY_PERFORMANCE_MONITORING', 'false') == 'true'
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
Sentry.set_context('ruleset', { version: ruleset_version })
|
|
47
|
+
|
|
48
|
+
true
|
|
49
|
+
rescue StandardError => e
|
|
50
|
+
logger.error(message: "Failed to initialize Sentry SDK for Integrated Error Tracking: #{e}")
|
|
51
|
+
raise e
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def ruleset_version
|
|
55
|
+
@ruleset_version ||= Gitlab::SecretDetection::Core::Ruleset.new.extract_ruleset_version || 'unknown'
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def can_setup_sentry?
|
|
59
|
+
ENV.fetch('SD_ENV', '') == 'production' && ENV.fetch('SD_TRACKING_DSN', '') != ''
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
@@ -32,6 +32,7 @@ module Gitlab
|
|
|
32
32
|
module GRPC
|
|
33
33
|
class ScannerService < Scanner::Service
|
|
34
34
|
include SDLogger
|
|
35
|
+
include IntegratedErrorTracking
|
|
35
36
|
|
|
36
37
|
# Maximum timeout value that can be given as the input. This guards
|
|
37
38
|
# against the misuse of timeouts.
|
|
@@ -45,46 +46,63 @@ module Gitlab
|
|
|
45
46
|
}.freeze
|
|
46
47
|
|
|
47
48
|
# Implementation for /Scan RPC method
|
|
48
|
-
def scan(request,
|
|
49
|
-
scan_request_action(request)
|
|
49
|
+
def scan(request, call)
|
|
50
|
+
scan_request_action(request, call)
|
|
50
51
|
end
|
|
51
52
|
|
|
52
53
|
# Implementation for /ScanStream RPC method
|
|
53
|
-
def scan_stream(requests,
|
|
54
|
-
request_action = ->(r) { scan_request_action(r) }
|
|
54
|
+
def scan_stream(requests, call)
|
|
55
|
+
request_action = ->(r) { scan_request_action(r, call) }
|
|
55
56
|
StreamEnumerator.new(requests, request_action).each_item
|
|
56
57
|
end
|
|
57
58
|
|
|
58
59
|
private
|
|
59
60
|
|
|
60
|
-
def scan_request_action(request)
|
|
61
|
+
def scan_request_action(request, call)
|
|
62
|
+
if request.nil?
|
|
63
|
+
logger.error(
|
|
64
|
+
message: "FATAL: Secret Detection gRPC scan request is `nil`",
|
|
65
|
+
deadline: call.deadline,
|
|
66
|
+
cancelled: call.cancelled?
|
|
67
|
+
)
|
|
68
|
+
return Gitlab::SecretDetection::GRPC::ScanResponse.new(
|
|
69
|
+
results: [],
|
|
70
|
+
status: Gitlab::SecretDetection::GRPC::ScanResponse::Status::STATUS_INPUT_ERROR,
|
|
71
|
+
applied_exclusions: []
|
|
72
|
+
)
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
logger.info(message: "Secret Detection gRPC scan request received")
|
|
76
|
+
|
|
61
77
|
validate_request(request)
|
|
62
78
|
|
|
63
79
|
payloads = request.payloads.to_a
|
|
80
|
+
exclusions = { raw_value: [], rule: [], path: [] }
|
|
64
81
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
request.exclusions&.each do |exclusion|
|
|
69
|
-
case exclusion.type
|
|
82
|
+
request.exclusions.each do |exclusion|
|
|
83
|
+
case exclusion.exclusion_type
|
|
70
84
|
when :EXCLUSION_TYPE_RAW_VALUE
|
|
71
|
-
|
|
85
|
+
exclusions[:raw_value] << exclusion
|
|
72
86
|
when :EXCLUSION_TYPE_RULE
|
|
73
|
-
|
|
87
|
+
exclusions[:rule] << exclusion
|
|
88
|
+
when :EXCLUSION_TYPE_PATH
|
|
89
|
+
exclusions[:path] << exclusion
|
|
90
|
+
else
|
|
91
|
+
logger.warn("Unknown exclusion type #{exclusion.exclusion_type}")
|
|
74
92
|
end
|
|
75
93
|
end
|
|
76
94
|
|
|
77
95
|
begin
|
|
78
96
|
result = scanner.secrets_scan(
|
|
79
97
|
payloads,
|
|
80
|
-
|
|
81
|
-
rule_exclusions:,
|
|
98
|
+
exclusions:,
|
|
82
99
|
tags: request.tags.to_a,
|
|
83
100
|
timeout: request.timeout_secs,
|
|
84
101
|
payload_timeout: request.payload_timeout_secs
|
|
85
102
|
)
|
|
86
103
|
rescue StandardError => e
|
|
87
|
-
logger.error("Failed to run the scan:
|
|
104
|
+
logger.error(message: "Failed to run the secret detection scan", exception: e.message)
|
|
105
|
+
track_exception(e)
|
|
88
106
|
raise ::GRPC::Unknown, e.message
|
|
89
107
|
end
|
|
90
108
|
|
|
@@ -94,7 +112,8 @@ module Gitlab
|
|
|
94
112
|
|
|
95
113
|
Gitlab::SecretDetection::GRPC::ScanResponse.new(
|
|
96
114
|
results: findings,
|
|
97
|
-
status: result.status
|
|
115
|
+
status: result.status,
|
|
116
|
+
applied_exclusions: result.applied_exclusions
|
|
98
117
|
)
|
|
99
118
|
end
|
|
100
119
|
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Gitlab
|
|
4
|
+
module SecretDetection
|
|
5
|
+
module Utils
|
|
6
|
+
class Masker
|
|
7
|
+
DEFAULT_VISIBLE_CHAR_COUNT = 3
|
|
8
|
+
DEFAULT_MASK_CHAR_COUNT = 5
|
|
9
|
+
DEFAULT_MASK_CHAR = '*'
|
|
10
|
+
|
|
11
|
+
class << self
|
|
12
|
+
def mask_secret(
|
|
13
|
+
raw_secret_value,
|
|
14
|
+
mask_char: DEFAULT_MASK_CHAR,
|
|
15
|
+
visible_chars_count: DEFAULT_VISIBLE_CHAR_COUNT,
|
|
16
|
+
mask_chars_count: DEFAULT_MASK_CHAR_COUNT
|
|
17
|
+
)
|
|
18
|
+
return '' if raw_secret_value.nil? || raw_secret_value.empty?
|
|
19
|
+
return raw_secret_value if raw_secret_value.length <= visible_chars_count # Too short to mask
|
|
20
|
+
|
|
21
|
+
chars = raw_secret_value.chars
|
|
22
|
+
position = 0
|
|
23
|
+
|
|
24
|
+
while position < chars.length
|
|
25
|
+
# Show 'visible_chars_count' characters
|
|
26
|
+
position += visible_chars_count
|
|
27
|
+
|
|
28
|
+
# Mask next 'mask_chars' characters if available
|
|
29
|
+
mask_chars_count.times do
|
|
30
|
+
break if position >= chars.length
|
|
31
|
+
|
|
32
|
+
chars[position] = mask_char
|
|
33
|
+
position += 1
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
chars.join
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
@@ -3,23 +3,9 @@
|
|
|
3
3
|
module Gitlab
|
|
4
4
|
module SecretDetection
|
|
5
5
|
class Gem
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
def self.get_release_version
|
|
11
|
-
release_version = ENV.fetch("SD_GEM_RELEASE_VERSION", "")
|
|
12
|
-
|
|
13
|
-
if release_version.empty?
|
|
14
|
-
raise LoadError("Missing SD_GEM_RELEASE_VERSION environment variable.") unless local_env?
|
|
15
|
-
|
|
16
|
-
"#{DEFAULT_VERSION}-debug"
|
|
17
|
-
elsif release_version.match?(SEMVER_REGEX)
|
|
18
|
-
release_version
|
|
19
|
-
else
|
|
20
|
-
"#{DEFAULT_VERSION}-#{release_version}"
|
|
21
|
-
end
|
|
22
|
-
end
|
|
6
|
+
# Ensure to maintain the same version in CHANGELOG file.
|
|
7
|
+
# More details available under 'Release Process' section in the README.md file.
|
|
8
|
+
VERSION = "0.39.2"
|
|
23
9
|
|
|
24
10
|
# SD_ENV env var is used to determine which environment the
|
|
25
11
|
# server is running. This var is defined in `.runway/env-<env>.yml` files.
|
|
@@ -17,6 +17,8 @@ enum ExclusionType {
|
|
|
17
17
|
EXCLUSION_TYPE_UNSPECIFIED = 0;
|
|
18
18
|
EXCLUSION_TYPE_RULE = 1; // Rule ID to exclude
|
|
19
19
|
EXCLUSION_TYPE_RAW_VALUE = 2; // Raw value to exclude
|
|
20
|
+
EXCLUSION_TYPE_PATH = 3; // Specific file path to exclude
|
|
21
|
+
EXCLUSION_TYPE_REGEX_PATTERN = 4; // Regular expression to exclude
|
|
20
22
|
}
|
|
21
23
|
|
|
22
24
|
/* Request arg for triggering Scan/ScanStream method */
|
|
@@ -39,6 +41,7 @@ message ScanRequest {
|
|
|
39
41
|
}
|
|
40
42
|
|
|
41
43
|
/* Response from Scan/ScanStream method */
|
|
44
|
+
/* Any changes to these definitions must be matched by changes in the Core classes that correspond to them */
|
|
42
45
|
message ScanResponse {
|
|
43
46
|
// Represents a secret finding identified within a payload
|
|
44
47
|
message Finding {
|