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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d234e1b4ade6f71981fd1abafbf8f58a46714149f61d0e6a12ba8ed12e9bc18e
4
- data.tar.gz: 00d2b291531cb3c7cb4d44ec4fb806940b8dc554c2efafbbac505b3c2cfe701a
3
+ metadata.gz: ee740db0824f6917fb06f4001fa9f01fa6f612a0e7e5c290341774a6fd81b600
4
+ data.tar.gz: d62ed242fa1d2df3e39d8af24baafeb8c3725b613f0e4b725f8cafca26c3f26f
5
5
  SHA512:
6
- metadata.gz: 73afb2cf54396ed6fe3bb128d068a96327bfb7a83b21d4dd7621ecbf0ad7f2682309b6da868bc285572075eda281d247f415510539dadb1e9cd7db9527d66e75
7
- data.tar.gz: 7fe5ab79f639286b1e15d1d4dd64a1722a55469f975340ce9c80c598b5cf01b507a7117b5a765ea0b54a14868f059d2492e05976dccd4e5bfbae6cde0950c91c
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 and Tags</summary>
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. Extract keywords from the parsed ruleset to use it for matching keywords before regex operation.
33
- # 2. Build and Compile rule regex patterns obtained from the ruleset with +DEFAULT_PATTERN_MATCHER_TAGS+
34
- # tags. Raises +RulesetCompilationError+ in case the regex pattern compilation fails.
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
- @default_keyword_matcher = build_keyword_matcher(
40
- tags: DEFAULT_PATTERN_MATCHER_TAGS,
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+:: Array of tag values to filter from the default ruleset when determining the rules used for the scan.
64
- # For example: Add `gitlab_blocking` to include only rules for Push Protection. Defaults to
65
- # [`gitlab_blocking`] (+DEFAULT_PATTERN_MATCHER_TAGS+).
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: DEFAULT_PATTERN_MATCHER_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
- Timeout.timeout(timeout) do
99
- keyword_matcher = build_keyword_matcher(tags:)
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
- tags:,
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, :default_pattern_matcher, :default_keyword_matcher, :default_rules
146
+ attr_reader :logger, :rules, :keywords, :pattern_matcher, :keyword_matcher
150
147
 
151
- # Builds RE2::Set pattern matcher for the given combination of rules
152
- # and tags. It also allows a choice(via `include_missing_tags`) to consider rules
153
- # for pattern matching that do not have `tags` property defined. If the given tags
154
- # are same as +DEFAULT_PATTERN_MATCHER_TAGS+ then returns the eagerly loaded default
155
- # pattern matcher created during initialization.
156
- def build_pattern_matcher(tags:, include_missing_tags: false)
157
- if tags.eql?(DEFAULT_PATTERN_MATCHER_TAGS) && !default_pattern_matcher.nil?
158
- logger.info(
159
- message: "Given tags input matches default matcher tags, using pre-defined RE2 Pattern Matcher"
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 [default_pattern_matcher, default_rules]
178
+ return
162
179
  end
163
180
 
164
181
  logger.info(
165
- message: "Creating a new RE2 Pattern Matcher with given tags",
166
- tags:,
167
- include_missing_tags:
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
- active_rules = []
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
- rule_tags = rule[:tags]
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
- [matcher, active_rules]
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
- def build_keyword_matcher(tags:, include_missing_tags: false)
214
- if tags.eql?(DEFAULT_PATTERN_MATCHER_TAGS) && !default_keyword_matcher.nil?
215
- logger.info(
216
- message: "Given tags input matches default tags, using pre-defined RE2 Keyword Matcher"
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 a new RE2 Keyword Matcher..",
223
- tags:,
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 a match with given rule tags, returning empty RE2 Keyword Matcher"
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
- exclusions: {},
294
- rules: [])
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
- exclusions: {},
329
- rules: []
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: {}, rules: @default_rules)
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.42.1"
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.42.1
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-06-25 00:00:00.000000000 Z
13
+ date: 2026-07-09 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: grpc