rubocop-dev_doc 0.8.0 → 0.9.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: 7d3924b0184e741e19b3f29854b6f472a9bd54667ddfef1e59ccc1aa000e5073
4
- data.tar.gz: d59338919f825edec8ed8bbc717d26f19782fbc88411d90e1a8e0c110d07235d
3
+ metadata.gz: 726cbfc87e097508ea26f7cb598c61e4f743e9979d688b340f277836899a0913
4
+ data.tar.gz: 221eb62a0484ac2695d0ae1c06f340192e6741118676f2b30cbd14296f3b6e8f
5
5
  SHA512:
6
- metadata.gz: 606dad217da6a65ddc8eb66040f2f8747be5fed37aa27764f2ae74a2fa2266158e848d780b2ee10a7d690f20832b0c36b3a5b3b16d766c433c99088226b78c8e
7
- data.tar.gz: 49cac436e0dfef59326c94b89187c5d6001578972d0faa76f2fca683cfd93b9dc1e5866093351884a49e0ad58cb0f6f768a4711275f57e6bf85d24efc913137f
6
+ metadata.gz: 3ea55c9934463e771ad0f465b691268d743f0598e07a758b84d8ca66b2a54af4ce290f137f64161232aab20f32ca274b762c49b66089a3ded781826be5c0d249
7
+ data.tar.gz: edc9a58999125d644baa1db29d9bf89a5de47f72c864ee717460053c2f5e0104670bdbbb56f4744ca7f2c44ab085d18d75a4a68b77f01952a35e82ab759af0d7
data/config/default.yml CHANGED
@@ -195,8 +195,8 @@ DevDoc/Style/NoUnscopedMethodDefinitions:
195
195
  # included by default.
196
196
  SafeDSLReceivers: []
197
197
 
198
- DevDoc/Style/AvoidSend:
199
- Description: "Avoid `send`/`public_send` with an explicit receiver; prefer direct calls or safer alternatives."
198
+ DevDoc/Style/AvoidInsecureSend:
199
+ Description: "Avoid insecure dynamic `send`/`public_send` `send` is always flagged; `public_send` requires a prefix or trusted source (validator/mailer)."
200
200
  Enabled: true
201
201
 
202
202
  DevDoc/Style/PreferPublicSend:
@@ -388,9 +388,22 @@ Rails/StrongParametersExpect:
388
388
  # Narrower replacement for Rails/StrongParametersExpect — flags only the
389
389
  # hash-form rewrite (`params.require(:foo).permit(...)` → `params.expect(foo: [...])`).
390
390
  # Does not flag scalar `params[:foo]` in any context.
391
+ #
392
+ # SafeAutoCorrect is false because `expect` is NOT drop-in for require+permit
393
+ # (verified against a production suite; see the cop docs for full detail):
394
+ # 1. `expect` raises ParameterMissing (400) when the key is present but every
395
+ # value filters out — permit returned an empty slice and callers often rely
396
+ # on that ("slice-style" param methods probed on every request).
397
+ # 2. `expect` uses explicit_arrays: collection-valued nested attributes
398
+ # (numeric-keyed fields_for hashes) need the double-array `[[...]]`
399
+ # declaration; the flat `[...]` this autocorrect emits SILENTLY filters
400
+ # collections to empty. Audit every *_attributes key after correcting:
401
+ # has_many nests need `[[...]]`, has_one nests stay flat.
402
+ # Run with `-A` only alongside a test suite that exercises the forms.
391
403
  DevDoc/Rails/StrongParametersExpect:
392
404
  Description: "Use `params.expect(foo: [...])` instead of `params.require(:foo).permit(...)`."
393
405
  Enabled: true
406
+ SafeAutoCorrect: false
394
407
  Include:
395
408
  - "app/controllers/**/*.rb"
396
409
 
@@ -412,10 +425,32 @@ Rails/SaveBang:
412
425
  Exclude:
413
426
  - "app/controllers/**/*"
414
427
 
428
+ # Superseded by DevDoc/Style/CaseElseDecision below: same detection, but the
429
+ # offense message carries the raise/report/fall-through decision framework
430
+ # instead of upstream's "Missing else statement" (which teaches the wrong
431
+ # reflex — add an else, any else). Running both double-flags every offense.
415
432
  Style/MissingElse:
416
- Enabled: true
433
+ Enabled: false
417
434
  EnforcedStyle: case
418
435
 
436
+ # The point is NOT to add a silent `else` — it is to force a decision about
437
+ # what no-branch-matching MEANS at each case. Three legitimate outcomes:
438
+ # 1. The else genuinely happens in normal operation -> `else` + a comment
439
+ # stating why fall-through is correct (requires Style/EmptyElse off).
440
+ # 2. The else indicates a bug and continuing is unsafe -> `else raise`
441
+ # (fail fast; unexpected enum/mode values must not proceed silently).
442
+ # 3. The else indicates a bug but users can safely continue -> report to the
443
+ # error tracker and degrade gracefully (e.g. log an error, then return the
444
+ # neutral value). Prefer this over raising in user-facing render paths.
445
+ # Never pick 1 by reflex: if you cannot write down why the else legitimately
446
+ # happens, it belongs in bucket 2 or 3. Also weigh WHERE the case runs — a
447
+ # raise on user-supplied input that executes before authorization turns
448
+ # garbage requests into 500s, and error-tracker reports on pre-auth,
449
+ # scanner-reachable paths become noise rather than signal.
450
+ DevDoc/Style/CaseElseDecision:
451
+ Description: "Every `case` needs an `else` that decides what a non-match means: raise (unsafe to continue), report + degrade (bug but tolerable), or a comment justifying fall-through. Supersedes Style/MissingElse."
452
+ Enabled: true
453
+
419
454
  # Core default is `Max: 2`, which permits `a&.b&.c`. Tightened to `Max: 1`:
420
455
  # in `a&.b&.c`, the second `&.` is ambiguous — the reader can't tell whether
421
456
  # it's there because `b.c` can genuinely be nil on its own, or merely because
@@ -436,6 +471,12 @@ DevDoc/Test/AvoidGlibTravelFreeze:
436
471
  Include:
437
472
  - "test/**/*.rb"
438
473
  - "spec/**/*.rb"
474
+ # Mailer previews aren't tests: freeze_time there exists to render stable
475
+ # dev-tool output, glib's test helpers aren't available in preview classes,
476
+ # and the "frozen time masks timing bugs" rationale doesn't apply.
477
+ Exclude:
478
+ - "test/mailers/previews/**/*"
479
+ - "spec/mailers/previews/**/*"
439
480
 
440
481
  DevDoc/Test/RequireGlibTravelBlock:
441
482
  Description: "`glib_travel` must be called with a block — the bare anchor form leaks frozen time past the test. A bare duration argument (`glib_travel(61.seconds)`) that advances the clock is the accepted non-block form."
@@ -38,6 +38,31 @@ module RuboCop
38
38
  # Scalar `params[:foo]` in any context — leave that to per-project
39
39
  # decision or the upstream cop.
40
40
  #
41
+ # ## Autocorrect is UNSAFE (SafeAutoCorrect: false)
42
+ # `expect` is not a drop-in for require+permit; both deltas below were
43
+ # verified against a production suite (81 test failures before manual
44
+ # fix-ups):
45
+ #
46
+ # 1. **Empty-slice 400**: `expect` raises ParameterMissing when the key
47
+ # is present but every value filters out — `permit` returned an
48
+ # empty slice and proceeded. Bites "slice-style" param methods that
49
+ # probe for whichever keys arrived (e.g. a publishing_params called
50
+ # unconditionally in update). Keep those tolerant with
51
+ # `params.fetch(:key, {}).permit(...)` + an inline reason.
52
+ # 2. **Silent collection loss**: `expect` filters with explicit_arrays,
53
+ # so collection-valued nested attributes (numeric-keyed fields_for /
54
+ # dynamic-group hashes, `{"0"=>{...}}`) require the double-array
55
+ # `[[...]]` declaration. The flat `[...]` this autocorrect emits
56
+ # SILENTLY filters such collections to empty — no error, no data.
57
+ # After correcting, audit every *_attributes key: has_many nests
58
+ # need `[[...]]`, has_one nests stay flat.
59
+ #
60
+ # The corrector also deliberately skips permit calls whose arguments
61
+ # are computed (e.g. `permit([...static...] + extra)`): copying the
62
+ # expression into `expect(key: [<expr>])` would produce a one-element
63
+ # outer array — which IS expect's array-of-hashes syntax, silently
64
+ # changing semantics. Those sites are flagged without a correction.
65
+ #
41
66
  # @example
42
67
  # # bad
43
68
  # params.require(:user).permit(:name, :email)
@@ -57,9 +82,9 @@ module RuboCop
57
82
  extend AutoCorrector
58
83
 
59
84
  MSG_REQUIRE_PERMIT = 'Use `params.expect(%<key>s: [...])` instead of ' \
60
- '`params.require(:%<key>s).permit(...)`.'
85
+ '`params.require(:%<key>s).permit(...)`.'.freeze
61
86
  MSG_PERMIT_REQUIRE = 'Use `params.expect(%<key>s: ...)` instead of ' \
62
- '`params.permit(%<key>s: ...).require(:%<key>s)`.'
87
+ '`params.permit(%<key>s: ...).require(:%<key>s)`.'.freeze
63
88
 
64
89
  RESTRICT_ON_SEND = %i[permit require].freeze
65
90
 
@@ -73,25 +98,33 @@ module RuboCop
73
98
  # Match: params.require(:foo).permit(...)
74
99
  def check_require_permit(permit_node)
75
100
  require_node = permit_node.receiver
76
- return unless require_node&.send_type? && require_node.method_name == :require
77
- return unless params_receiver?(require_node.receiver)
78
- return unless require_node.arguments.one? && require_node.first_argument.sym_type?
101
+ return unless chained_call?(require_node, :require) && params_receiver?(require_node.receiver)
102
+ return unless single_sym_arg?(require_node)
79
103
 
80
104
  key = require_node.first_argument.value
81
105
  add_offense(permit_node, message: format(MSG_REQUIRE_PERMIT, key: key)) do |corrector|
82
- replacement = build_require_permit_replacement(
83
- require_node.receiver, key, permit_node.arguments
84
- )
85
- corrector.replace(permit_node, replacement)
106
+ autocorrect_require_permit(corrector, permit_node, require_node, key)
86
107
  end
87
108
  end
88
109
 
110
+ # Computed args (`permit([...] + extra)`, `permit(some_method)`) must
111
+ # not be source-copied into the expect array: a single computed-array
112
+ # element becomes `expect(key: [expr])` — expect's array-of-hashes
113
+ # syntax — silently changing semantics. Flag without correcting.
114
+ def autocorrect_require_permit(corrector, permit_node, require_node, key)
115
+ return unless literal_permit_args?(permit_node)
116
+
117
+ corrector.replace(
118
+ permit_node,
119
+ build_require_permit_replacement(require_node.receiver, key, permit_node.arguments)
120
+ )
121
+ end
122
+
89
123
  # Match: params.permit(foo: ...).require(:foo)
90
124
  def check_permit_require(require_node)
91
125
  permit_node = require_node.receiver
92
- return unless permit_node&.send_type? && permit_node.method_name == :permit
93
- return unless params_receiver?(permit_node.receiver)
94
- return unless require_node.arguments.one? && require_node.first_argument.sym_type?
126
+ return unless chained_call?(permit_node, :permit) && params_receiver?(permit_node.receiver)
127
+ return unless single_sym_arg?(require_node)
95
128
 
96
129
  key = require_node.first_argument.value
97
130
  pair = permit_hash_pair_for_key(permit_node, key)
@@ -103,6 +136,23 @@ module RuboCop
103
136
  end
104
137
  end
105
138
 
139
+ def chained_call?(node, method_name)
140
+ node&.send_type? && node.method_name == method_name
141
+ end
142
+
143
+ def single_sym_arg?(node)
144
+ node.arguments.one? && node.first_argument.sym_type?
145
+ end
146
+
147
+ # Source-copying is only safe for literal permit arguments; any
148
+ # computed expression (method call, `+`, splat, const) changes
149
+ # meaning inside `expect`'s array literal.
150
+ def literal_permit_args?(permit_node)
151
+ permit_node.arguments.all? do |arg|
152
+ arg.sym_type? || arg.str_type? || arg.hash_type? || arg.array_type?
153
+ end
154
+ end
155
+
106
156
  def params_receiver?(node)
107
157
  node&.send_type? && node.method_name == :params && node.receiver.nil?
108
158
  end
@@ -0,0 +1,159 @@
1
+ module RuboCop
2
+ module Cop
3
+ module DevDoc
4
+ module Style
5
+ # Avoid insecure dynamic `send` and `public_send` with an explicit receiver.
6
+ #
7
+ # ## Rationale
8
+ # `send()` can call *any* method, including destructive ones like
9
+ # `destroy`. The risk is specifically with **dynamic** method names —
10
+ # when the argument is a variable or interpolated string, a crafted
11
+ # value could invoke methods the developer never intended to expose.
12
+ #
13
+ # `public_send` respects method visibility, but still allows calling
14
+ # any public method. When the method name is dynamic, a prefix restricts
15
+ # the callable surface to methods sharing that prefix.
16
+ #
17
+ # ## Rules
18
+ #
19
+ # **`send()` with a dynamic argument is always flagged.** There is no
20
+ # safe use case for dynamic `send` — use `public_send` instead, and
21
+ # disable with a justification when bypassing visibility is intentional.
22
+ #
23
+ # **`public_send()` with a dynamic argument is allowed when:**
24
+ # - The argument is a **literal symbol or string** (method name is fixed at code-write time).
25
+ # - The call uses a **prefix pattern** (`"prefix_#{method_name}"`) that restricts callable methods.
26
+ # - The call is in a **validator** file (`app/validators/`) — method names come from model DSL, not user input.
27
+ # - The call is a **mailer dispatch** — method names are trusted internal symbols.
28
+ #
29
+ # **`public_send()` with a dynamic argument is flagged when:**
30
+ # - The argument is an unrestricted variable or interpolation with no static prefix.
31
+ #
32
+ # ## Safer alternatives
33
+ #
34
+ # **a) For model attributes — use bracket notation instead.**
35
+ # `@model[column_name]` only accesses database columns, so it cannot
36
+ # accidentally invoke methods like `destroy`.
37
+ #
38
+ # ❌ Dangerous — method_name could be :destroy or any other method
39
+ # @user.send(method_name)
40
+ #
41
+ # ✔️ Safe — only accesses database columns
42
+ # @user[method_name]
43
+ #
44
+ # **b) For non-model objects — use a prefix to restrict callable methods.**
45
+ # By interpolating the dynamic part into a fixed prefix, only methods
46
+ # with that prefix (e.g. `export_csv`, `export_pdf`) can be invoked,
47
+ # preventing accidental calls to unintended methods.
48
+ #
49
+ # ❌ Unrestricted — any method can be called
50
+ # obj.public_send(method_name)
51
+ #
52
+ # ✔️ Restricted — only methods with the prefix can be called
53
+ # obj.public_send("export_#{method_name}")
54
+ #
55
+ # NOTE: A prefix narrows the callable surface but does not eliminate it —
56
+ # an attacker-controlled suffix can still reach any method sharing the
57
+ # prefix (e.g. `"export_#{x}"` could hit `export_and_destroy`). Use the
58
+ # narrowest prefix that fits, and prefer an explicit whitelist when the
59
+ # set of targets is small.
60
+ #
61
+ # @example
62
+ # # bad — dynamic method name from a variable
63
+ # @user.send(method_name)
64
+ # obj.public_send(action)
65
+ #
66
+ # # bad — interpolation with no static prefix restricts nothing
67
+ # obj.send("#{x}")
68
+ # obj.send("#{x}_run")
69
+ #
70
+ # # good — literal symbol: method name is statically visible
71
+ # instance.public_send(:email)
72
+ #
73
+ # # good — bracket notation for model attributes
74
+ # @user[attribute_name]
75
+ #
76
+ # # good — static prefix restricts the callable methods
77
+ # obj.public_send("export_#{method_name}")
78
+ #
79
+ # # good — validator pattern (trusted source)
80
+ # record.public_send(attribute)
81
+ #
82
+ # # good — mailer dispatch (trusted source)
83
+ # mailer.public_send(action)
84
+ class AvoidInsecureSend < Base
85
+ MSG_SEND = "Avoid dynamic `send` — use `public_send` instead, or " \
86
+ "bracket notation for model attributes. Disable with a " \
87
+ "justification when bypassing visibility is intentional.".freeze
88
+
89
+ MSG_PUBLIC_SEND = "Avoid unrestricted dynamic `public_send` — use a " \
90
+ "prefix (`obj.public_send(\"export_\#{x}\")`) to " \
91
+ "restrict callable methods, or bracket notation for " \
92
+ "model attributes.".freeze
93
+
94
+ # `__send__` is the canonical alias — omitting it would leave a
95
+ # zero-cost dodge for the exact dynamic dispatch this cop restricts.
96
+ RESTRICT_ON_SEND = %i[send public_send __send__].freeze
97
+
98
+ def on_send(node)
99
+ return if node.receiver.nil?
100
+ return if literal_argument?(node)
101
+ return if prefixed_dynamic_method?(node)
102
+
103
+ check_send(node)
104
+ end
105
+
106
+ private
107
+
108
+ def literal_argument?(node)
109
+ node.first_argument&.sym_type?
110
+ end
111
+
112
+ def prefixed_dynamic_method?(node)
113
+ arg = node.first_argument
114
+ return false unless arg&.type?(:dstr, :dsym)
115
+
116
+ first = arg.children.first
117
+ first&.str_type? && !first.value.empty?
118
+ end
119
+
120
+ def check_send(node)
121
+ if node.method?(:send) || node.method?(:__send__)
122
+ add_offense(node.loc.selector, message: MSG_SEND)
123
+ else
124
+ check_public_send(node)
125
+ end
126
+ end
127
+
128
+ def check_public_send(node)
129
+ return if validator_file?
130
+ return if mailer_dispatch?(node)
131
+
132
+ add_offense(node.loc.selector, message: MSG_PUBLIC_SEND)
133
+ end
134
+
135
+ # Validator files are exempt — method names come from model DSL
136
+ # (developer-controlled), not user input.
137
+ def validator_file?
138
+ processed_source.file_path.to_s.include?('app/validators/')
139
+ end
140
+
141
+ # Mailer dispatch is exempt — method names are trusted internal symbols
142
+ # (constructor args, method params), not user input.
143
+ #
144
+ # Heuristic: the receiver is a mailer class or a parameterized mailer,
145
+ # and the method is a mailer action (not a private helper).
146
+ def mailer_dispatch?(node)
147
+ receiver = node.receiver
148
+ return false unless receiver
149
+
150
+ receiver_source = receiver.source
151
+ receiver_source.match?(/Mailer\b/) ||
152
+ receiver_source.match?(/\.with\(/) ||
153
+ receiver_source.match?(/parameterized_mailer/)
154
+ end
155
+ end
156
+ end
157
+ end
158
+ end
159
+ end
@@ -0,0 +1,87 @@
1
+ module RuboCop
2
+ module Cop
3
+ module DevDoc
4
+ module Style
5
+ # Every `case` must carry an `else` that DECIDES what a non-matching
6
+ # value means — this cop supersedes `Style/MissingElse`
7
+ # (EnforcedStyle: case), whose "Missing `else` statement" message
8
+ # teaches the wrong reflex: add an else, any else.
9
+ #
10
+ # ## Rationale
11
+ # A `case` without `else` makes "exhaustive dispatch" and "deliberate
12
+ # partial match" indistinguishable — an unanticipated value silently
13
+ # evaluates to nil. The fix is not a bare `else`; it is a decision.
14
+ # Three legitimate outcomes:
15
+ #
16
+ # 1. **Fall-through genuinely happens in normal operation** — add
17
+ # `else` with a comment stating WHY it is correct (requires
18
+ # `Style/EmptyElse` to be disabled).
19
+ # 2. **A non-match is a bug and continuing is unsafe** — `else raise`
20
+ # (fail fast; unexpected enum/mode values must not proceed).
21
+ # 3. **A non-match is a bug but users can safely continue** — report
22
+ # to the error tracker, then degrade gracefully (return the neutral
23
+ # value). Prefer this over raising in user-facing render paths.
24
+ #
25
+ # Never pick 1 by reflex: if you cannot write down why the else
26
+ # legitimately happens, it belongs in bucket 2 or 3. Also weigh WHERE
27
+ # the case runs: raising on user-supplied input that executes before
28
+ # authorization turns garbage requests into 500s, and error-tracker
29
+ # reports on pre-auth, scanner-reachable paths are noise, not signal.
30
+ #
31
+ # ## Interaction with Style/MissingElse
32
+ # Same detection surface (case statements and `case/in` pattern
33
+ # matches; `if` is exempt) — running both double-flags every offense.
34
+ # Disable `Style/MissingElse` when enabling this cop:
35
+ #
36
+ # Style/MissingElse:
37
+ # Enabled: false
38
+ #
39
+ # @example
40
+ # # bad — a value nobody anticipated silently becomes nil
41
+ # case status
42
+ # when :active then process
43
+ # when :archived then skip
44
+ # end
45
+ #
46
+ # # good — closed set: fail fast
47
+ # case status
48
+ # when :active then process
49
+ # when :archived then skip
50
+ # else
51
+ # raise "Unexpected status: #{status}"
52
+ # end
53
+ #
54
+ # # good — bug, but users can continue: report and degrade
55
+ # case status
56
+ # when :active then process
57
+ # when :archived then skip
58
+ # else
59
+ # ErrorTracker.error("Unexpected status: #{status}")
60
+ # nil
61
+ # end
62
+ #
63
+ # # good — legitimate fall-through, reason stated
64
+ # case action_name.to_sym
65
+ # when :new, :create then load_import
66
+ # else
67
+ # # No resource setup needed for the remaining actions.
68
+ # end
69
+ class CaseElseDecision < Base
70
+ MSG = '`case` has no `else` — decide what a non-match means: ' \
71
+ '`raise` if continuing is unsafe (closed set); report to the ' \
72
+ 'error tracker and degrade gracefully if users can continue; ' \
73
+ 'or `else` + a comment stating why fall-through is ' \
74
+ 'legitimate. Never add a bare `else` by reflex.'.freeze
75
+
76
+ def on_case(node)
77
+ add_offense(node.loc.keyword) unless node.else?
78
+ end
79
+
80
+ def on_case_match(node)
81
+ add_offense(node.loc.keyword) unless node.else?
82
+ end
83
+ end
84
+ end
85
+ end
86
+ end
87
+ end
@@ -21,16 +21,16 @@ module RuboCop
21
21
  # meant a public-API call. `send` leaves it ambiguous whether
22
22
  # private access was wanted.
23
23
  #
24
- # ## Relationship to AvoidSend
25
- # `DevDoc/Style/AvoidSend` flags *dynamic* dispatch (both `send` and
24
+ # ## Relationship to AvoidInsecureSend
25
+ # `DevDoc/Style/AvoidInsecureSend` flags *dynamic* dispatch (both `send` and
26
26
  # `public_send`) as risky. This cop is orthogonal: it flags `send`
27
- # specifically, including the cases AvoidSend exempts (literal-symbol
27
+ # specifically, including the cases AvoidInsecureSend exempts (literal-symbol
28
28
  # args, prefix-string args). The friction asymmetry is intentional —
29
29
  # `send` is the deeper exception, so it costs an extra disable:
30
30
  #
31
- # obj.public_send(method_name) # AvoidSend: 1 disable
31
+ # obj.public_send(method_name) # AvoidInsecureSend: 1 disable
32
32
  # obj.public_send(:foo) # clean
33
- # obj.send(method_name) # AvoidSend + this cop: 2 disables
33
+ # obj.send(method_name) # AvoidInsecureSend + this cop: 2 disables
34
34
  # obj.send(:foo) # this cop only: 1 disable
35
35
  #
36
36
  # ## When `send` IS the right choice
@@ -1,5 +1,5 @@
1
1
  module RuboCop
2
2
  module DevDoc
3
- VERSION = "0.8.0".freeze
3
+ VERSION = "0.9.0".freeze
4
4
  end
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rubocop-dev_doc
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.8.0
4
+ version: 0.9.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - dev-doc contributors
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2026-07-04 00:00:00.000000000 Z
11
+ date: 2026-07-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -130,8 +130,10 @@ files:
130
130
  - lib/rubocop/cop/dev_doc/route/resource_name_number.rb
131
131
  - lib/rubocop/cop/dev_doc/route/resources_require_only.rb
132
132
  - lib/rubocop/cop/dev_doc/style/avoid_head_response.rb
133
+ - lib/rubocop/cop/dev_doc/style/avoid_insecure_send.rb
133
134
  - lib/rubocop/cop/dev_doc/style/avoid_options_hash.rb
134
135
  - lib/rubocop/cop/dev_doc/style/avoid_send.rb
136
+ - lib/rubocop/cop/dev_doc/style/case_else_decision.rb
135
137
  - lib/rubocop/cop/dev_doc/style/literal_operator_in_condition.rb
136
138
  - lib/rubocop/cop/dev_doc/style/literal_or_in_when_clause.rb
137
139
  - lib/rubocop/cop/dev_doc/style/minimize_variable_scope.rb