gitlab-secret_detection 0.42.1 → 0.43.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/README.md +1 -4
- data/lib/gitlab/secret_detection/core/scanner.rb +77 -77
- data/lib/gitlab/secret_detection/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: ee740db0824f6917fb06f4001fa9f01fa6f612a0e7e5c290341774a6fd81b600
|
|
4
|
+
data.tar.gz: d62ed242fa1d2df3e39d8af24baafeb8c3725b613f0e4b725f8cafca26c3f26f
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: a56e54761be8a6cf2b3885a7b072e1f65684aa4b0da542bf514834d388bfde6b3b840a7d45fb25e68c77f04c95f8a0d74484cb4be981eeb179d81a6f379c44db
|
|
7
|
+
data.tar.gz: ee2763b6627a6258caf220b4b2ca8cb68b24c6ff1161de107a75b0b0aeae65ecd201c5f71a52e8bf5c80bb86a985c3e0baa4289c6f7aa542aca3a5737f143110
|
data/README.md
CHANGED
|
@@ -202,7 +202,7 @@ You should see the following response as a result:
|
|
|
202
202
|
</details>
|
|
203
203
|
|
|
204
204
|
|
|
205
|
-
<details><summary>Example: Using Exclusions
|
|
205
|
+
<details><summary>Example: Using Exclusions</summary>
|
|
206
206
|
|
|
207
207
|
```shell
|
|
208
208
|
$ grpcurl -d @ \
|
|
@@ -233,9 +233,6 @@ $ grpcurl -d @ \
|
|
|
233
233
|
"exclusion_type": "EXCLUSION_TYPE_RAW_VALUE",
|
|
234
234
|
"value": "glrt-12345123451234512345"
|
|
235
235
|
}
|
|
236
|
-
],
|
|
237
|
-
"tags": [
|
|
238
|
-
"gitlab_blocking"
|
|
239
236
|
]
|
|
240
237
|
}
|
|
241
238
|
EOM
|
|
@@ -15,8 +15,6 @@ module Gitlab
|
|
|
15
15
|
DEFAULT_SCAN_TIMEOUT_SECS = 180 # 3 minutes
|
|
16
16
|
# default time limit(in seconds) for running the scan operation on a single payload
|
|
17
17
|
DEFAULT_PAYLOAD_TIMEOUT_SECS = 30 # 30 seconds
|
|
18
|
-
# Tags used for creating default pattern matcher
|
|
19
|
-
DEFAULT_PATTERN_MATCHER_TAGS = ['gitlab_blocking'].freeze
|
|
20
18
|
# Max no of child processes to spawn per request
|
|
21
19
|
# ref: https://gitlab.com/gitlab-org/gitlab/-/issues/430160
|
|
22
20
|
MAX_PROCS_PER_REQUEST = 5
|
|
@@ -29,21 +27,17 @@ module Gitlab
|
|
|
29
27
|
DEFAULT_MAX_FINDINGS_LIMIT = 999
|
|
30
28
|
|
|
31
29
|
# Initializes the instance with logger along with following operations:
|
|
32
|
-
# 1.
|
|
33
|
-
#
|
|
34
|
-
#
|
|
30
|
+
# 1. Filter the parsed ruleset down to rules applicable to push protection based on
|
|
31
|
+
# their structured `scanningCapabilities` field.
|
|
32
|
+
# 2. Extract keywords from the applicable rules to use for matching keywords before regex operation.
|
|
33
|
+
# 3. Build and Compile rule regex patterns obtained from the applicable rules.
|
|
34
|
+
# Raises +RulesetCompilationError+ in case the regex pattern compilation fails.
|
|
35
35
|
def initialize(rules:, logger: Logger.new($stdout))
|
|
36
36
|
@logger = logger
|
|
37
|
-
@rules = rules
|
|
38
|
-
@keywords = create_keywords(rules)
|
|
39
|
-
@
|
|
40
|
-
|
|
41
|
-
include_missing_tags: false
|
|
42
|
-
)
|
|
43
|
-
@default_pattern_matcher, @default_rules = build_pattern_matcher(
|
|
44
|
-
tags: DEFAULT_PATTERN_MATCHER_TAGS,
|
|
45
|
-
include_missing_tags: false
|
|
46
|
-
) # includes only gitlab_blocking rules
|
|
37
|
+
@rules = select_push_protection_rules(rules)
|
|
38
|
+
@keywords = create_keywords(@rules)
|
|
39
|
+
@keyword_matcher = build_keyword_matcher(@rules)
|
|
40
|
+
@pattern_matcher = build_pattern_matcher(@rules)
|
|
47
41
|
end
|
|
48
42
|
|
|
49
43
|
# Runs Secret Detection scan on the list of given payloads. Both the total scan duration and
|
|
@@ -60,9 +54,9 @@ module Gitlab
|
|
|
60
54
|
# :rule - Exclusions in the :rule array are the rules to exclude from the ruleset used for the scan.
|
|
61
55
|
# Each rule is represented by its ID. For example: `gitlab_personal_access_token`
|
|
62
56
|
# for representing Gitlab Personal Access Token. By default, no rule is excluded from the ruleset.
|
|
63
|
-
# +tags+::
|
|
64
|
-
#
|
|
65
|
-
#
|
|
57
|
+
# +tags+:: Deprecated and ignored. Rules are selected via their structured
|
|
58
|
+
# `scanningCapabilities` field instead of tags. The argument is kept only
|
|
59
|
+
# for backward compatibility with existing callers and will be removed.
|
|
66
60
|
# +max_findings_limit+:: Integer to limit the number of findings to be returned in the scan. Defaults
|
|
67
61
|
# to 999 (+DEFAULT_MAX_FINDINGS_LIMIT+).
|
|
68
62
|
#
|
|
@@ -84,7 +78,7 @@ module Gitlab
|
|
|
84
78
|
timeout: DEFAULT_SCAN_TIMEOUT_SECS,
|
|
85
79
|
payload_timeout: DEFAULT_PAYLOAD_TIMEOUT_SECS,
|
|
86
80
|
exclusions: {},
|
|
87
|
-
tags:
|
|
81
|
+
tags: [],
|
|
88
82
|
subprocess: RUN_IN_SUBPROCESS,
|
|
89
83
|
max_findings_limit: DEFAULT_MAX_FINDINGS_LIMIT
|
|
90
84
|
)
|
|
@@ -93,24 +87,27 @@ module Gitlab
|
|
|
93
87
|
# assign defaults since grpc passing zero timeout value to `Timeout.timeout(..)` makes it effectively useless.
|
|
94
88
|
timeout = DEFAULT_SCAN_TIMEOUT_SECS unless timeout.positive?
|
|
95
89
|
payload_timeout = DEFAULT_PAYLOAD_TIMEOUT_SECS unless payload_timeout.positive?
|
|
96
|
-
tags = DEFAULT_PATTERN_MATCHER_TAGS if tags.empty?
|
|
97
90
|
|
|
98
|
-
|
|
99
|
-
|
|
91
|
+
unless tags.nil? || tags.empty? || @tags_deprecation_warned
|
|
92
|
+
@tags_deprecation_warned = true
|
|
93
|
+
logger.warn(
|
|
94
|
+
message: "The `tags` argument is deprecated and ignored. Rules are selected via their " \
|
|
95
|
+
"`scanningCapabilities` field instead.",
|
|
96
|
+
given_tags: tags
|
|
97
|
+
)
|
|
98
|
+
end
|
|
100
99
|
|
|
100
|
+
Timeout.timeout(timeout) do
|
|
101
101
|
matched_payloads = filter_by_keywords(keyword_matcher, payloads)
|
|
102
102
|
|
|
103
103
|
next Core::Response.new(status: Core::Status::NOT_FOUND) if matched_payloads.empty?
|
|
104
104
|
|
|
105
|
-
# the pattern matcher will filter rules by tags so we use the filtered rule list
|
|
106
|
-
pattern_matcher, active_rules = build_pattern_matcher(tags:)
|
|
107
|
-
|
|
108
105
|
scan_args = {
|
|
109
106
|
payloads: matched_payloads,
|
|
110
107
|
payload_timeout:,
|
|
111
108
|
pattern_matcher:,
|
|
109
|
+
rules:,
|
|
112
110
|
exclusions:,
|
|
113
|
-
rules: active_rules,
|
|
114
111
|
max_findings_limit:
|
|
115
112
|
}.freeze
|
|
116
113
|
|
|
@@ -120,7 +117,7 @@ module Gitlab
|
|
|
120
117
|
payload_timeout:,
|
|
121
118
|
given_total_payloads: payloads.length,
|
|
122
119
|
scannable_payloads_post_keyword_filter: matched_payloads.length,
|
|
123
|
-
|
|
120
|
+
active_rules: rules.length,
|
|
124
121
|
run_in_subprocess: subprocess,
|
|
125
122
|
max_findings_limit:,
|
|
126
123
|
given_exclusions: format_exclusions_hash(exclusions)
|
|
@@ -146,44 +143,57 @@ module Gitlab
|
|
|
146
143
|
|
|
147
144
|
private
|
|
148
145
|
|
|
149
|
-
attr_reader :logger, :rules, :keywords, :
|
|
146
|
+
attr_reader :logger, :rules, :keywords, :pattern_matcher, :keyword_matcher
|
|
150
147
|
|
|
151
|
-
#
|
|
152
|
-
#
|
|
153
|
-
#
|
|
154
|
-
#
|
|
155
|
-
#
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
148
|
+
# A rule applies to push protection when its structured `scanningCapabilities`
|
|
149
|
+
# field includes `pushProtection`. Rules without the field are included for
|
|
150
|
+
# backward compatibility: ruleset artifacts released before the structured
|
|
151
|
+
# fields refactor (https://gitlab.com/gitlab-org/gitlab/-/work_items/592827)
|
|
152
|
+
# do not carry it and are already limited to push protection rules when the
|
|
153
|
+
# artifact is generated.
|
|
154
|
+
def push_protection_rule?(rule)
|
|
155
|
+
capabilities = rule[:scanningCapabilities]
|
|
156
|
+
|
|
157
|
+
capabilities.nil? || capabilities[:pushProtection] == true
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
# Filters the given ruleset down to push protection rules and logs how
|
|
161
|
+
# it was narrowed, so that a malformed ruleset artifact is visible in
|
|
162
|
+
# the logs at startup.
|
|
163
|
+
def select_push_protection_rules(rules)
|
|
164
|
+
selected = rules.select { |rule| push_protection_rule?(rule) }.freeze
|
|
165
|
+
log_selected_rules(selected, total_rules_given: rules.length)
|
|
166
|
+
selected
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
# An empty selection is logged as an error because every scan would
|
|
170
|
+
# then return no findings.
|
|
171
|
+
def log_selected_rules(selected_rules, total_rules_given:)
|
|
172
|
+
if selected_rules.empty?
|
|
173
|
+
logger.error(
|
|
174
|
+
message: "No rules with the pushProtection scanning capability found in the given ruleset. " \
|
|
175
|
+
"All scans will return no findings.",
|
|
176
|
+
total_rules_given:
|
|
160
177
|
)
|
|
161
|
-
return
|
|
178
|
+
return
|
|
162
179
|
end
|
|
163
180
|
|
|
164
181
|
logger.info(
|
|
165
|
-
message: "
|
|
166
|
-
|
|
167
|
-
|
|
182
|
+
message: "Selected push protection rules from the given ruleset",
|
|
183
|
+
total_rules_given:,
|
|
184
|
+
push_protection_rules_selected: selected_rules.length,
|
|
185
|
+
selected_without_scanning_capabilities: selected_rules.count { |rule| rule[:scanningCapabilities].nil? }
|
|
168
186
|
)
|
|
169
|
-
|
|
187
|
+
end
|
|
170
188
|
|
|
189
|
+
# Builds RE2::Set pattern matcher for the given rules. The matcher's
|
|
190
|
+
# pattern indices align with the order of the given rules.
|
|
191
|
+
def build_pattern_matcher(rules)
|
|
171
192
|
matcher = RE2::Set.new
|
|
172
193
|
|
|
173
194
|
begin
|
|
174
195
|
rules.each do |rule|
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
include_rule = if tags.empty?
|
|
178
|
-
true
|
|
179
|
-
elsif rule_tags
|
|
180
|
-
tags.intersect?(rule_tags)
|
|
181
|
-
else
|
|
182
|
-
include_missing_tags
|
|
183
|
-
end
|
|
184
|
-
|
|
185
|
-
active_rules << rule if include_rule
|
|
186
|
-
matcher.add(rule[:regex]) if include_rule
|
|
196
|
+
matcher.add(rule[:regex])
|
|
187
197
|
end
|
|
188
198
|
rescue StandardError => e
|
|
189
199
|
logger.error "Failed to add regex secret detection ruleset in RE::Set: #{e.message}"
|
|
@@ -196,7 +206,7 @@ module Gitlab
|
|
|
196
206
|
raise Core::Ruleset::RulesetCompilationError
|
|
197
207
|
end
|
|
198
208
|
|
|
199
|
-
|
|
209
|
+
matcher
|
|
200
210
|
end
|
|
201
211
|
|
|
202
212
|
# Creates and returns the unique set of rule matching keywords
|
|
@@ -210,34 +220,24 @@ module Gitlab
|
|
|
210
220
|
secrets_keywords.freeze
|
|
211
221
|
end
|
|
212
222
|
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
)
|
|
218
|
-
return default_keyword_matcher
|
|
219
|
-
end
|
|
220
|
-
|
|
223
|
+
# Builds RE2 keyword matcher from the keywords of the given rules. Returns
|
|
224
|
+
# nil when the rules define no keywords, in which case the keyword-based
|
|
225
|
+
# payload prefilter is skipped.
|
|
226
|
+
def build_keyword_matcher(rules)
|
|
221
227
|
logger.info(
|
|
222
|
-
message: "Creating
|
|
223
|
-
|
|
224
|
-
include_missing_tags:
|
|
228
|
+
message: "Creating RE2 Keyword Matcher",
|
|
229
|
+
rules_count: rules.length
|
|
225
230
|
)
|
|
226
231
|
|
|
227
232
|
include_keywords = Set.new
|
|
228
233
|
|
|
229
234
|
rules.each do |rule|
|
|
230
|
-
rule_tags = rule.fetch(:tags, [])
|
|
231
|
-
|
|
232
|
-
next if rule_tags.empty? && !include_missing_tags
|
|
233
|
-
next unless rule_tags.intersect?(tags)
|
|
234
|
-
|
|
235
235
|
include_keywords.merge(rule[:keywords]) unless rule[:keywords].nil?
|
|
236
236
|
end
|
|
237
237
|
|
|
238
238
|
if include_keywords.empty?
|
|
239
239
|
logger.error(
|
|
240
|
-
message: "No rule keywords found
|
|
240
|
+
message: "No rule keywords found in the given rules, returning empty RE2 Keyword Matcher"
|
|
241
241
|
)
|
|
242
242
|
return nil
|
|
243
243
|
end
|
|
@@ -290,8 +290,8 @@ module Gitlab
|
|
|
290
290
|
payload_timeout:,
|
|
291
291
|
pattern_matcher:,
|
|
292
292
|
max_findings_limit:,
|
|
293
|
-
|
|
294
|
-
|
|
293
|
+
rules:,
|
|
294
|
+
exclusions: {})
|
|
295
295
|
all_applied_exclusions = Set.new
|
|
296
296
|
|
|
297
297
|
logger.info(
|
|
@@ -325,8 +325,8 @@ module Gitlab
|
|
|
325
325
|
payload_timeout:,
|
|
326
326
|
pattern_matcher:,
|
|
327
327
|
max_findings_limit:,
|
|
328
|
-
|
|
329
|
-
|
|
328
|
+
rules:,
|
|
329
|
+
exclusions: {}
|
|
330
330
|
)
|
|
331
331
|
all_applied_exclusions = Set.new
|
|
332
332
|
|
|
@@ -383,7 +383,7 @@ module Gitlab
|
|
|
383
383
|
# Finds secrets in the given payload guarded with a timeout as a circuit breaker. It accepts
|
|
384
384
|
# literal values to exclude from the input before the scan, also SD rules to exclude during
|
|
385
385
|
# the scan.
|
|
386
|
-
def find_secrets_in_payload(payload:, pattern_matcher:, exclusions: {}
|
|
386
|
+
def find_secrets_in_payload(payload:, pattern_matcher:, rules:, exclusions: {})
|
|
387
387
|
findings = []
|
|
388
388
|
applied_exclusions = Set.new
|
|
389
389
|
|
|
@@ -5,7 +5,7 @@ module Gitlab
|
|
|
5
5
|
class Gem
|
|
6
6
|
# Ensure to maintain the same version in CHANGELOG file.
|
|
7
7
|
# More details available under 'Release Process' section in the README.md file.
|
|
8
|
-
VERSION = "0.
|
|
8
|
+
VERSION = "0.43.0"
|
|
9
9
|
|
|
10
10
|
# SD_ENV env var is used to determine which environment the
|
|
11
11
|
# server is running. This var is defined in `.runway/env-<env>.yml` files.
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: gitlab-secret_detection
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.43.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- group::secret detection
|
|
@@ -10,7 +10,7 @@ authors:
|
|
|
10
10
|
autorequire:
|
|
11
11
|
bindir: bin
|
|
12
12
|
cert_chain: []
|
|
13
|
-
date: 2026-
|
|
13
|
+
date: 2026-07-09 00:00:00.000000000 Z
|
|
14
14
|
dependencies:
|
|
15
15
|
- !ruby/object:Gem::Dependency
|
|
16
16
|
name: grpc
|