rubocop-dev_doc 0.9.0 → 0.10.1
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/config/default.yml +69 -2
- data/lib/dev_doc/test/lints/http_driven_controller_tests.rb +27 -9
- data/lib/rubocop/cop/dev_doc/{migration → rails}/avoid_bypassing_validation.rb +1 -1
- data/lib/rubocop/cop/dev_doc/rails/avoid_raw_sql.rb +3 -3
- data/lib/rubocop/cop/dev_doc/rails/no_block_predicate_on_relation.rb +133 -78
- data/lib/rubocop/cop/dev_doc/style/prefer_public_send.rb +1 -1
- data/lib/rubocop/cop/dev_doc/test/justification_header.rb +80 -0
- data/lib/rubocop/cop/dev_doc/test/no_persistence_in_mailer_previews.rb +94 -0
- data/lib/rubocop/cop/dev_doc/test/require_unit_test_justification.rb +103 -77
- data/lib/rubocop/cop/dev_doc/test/test_file_placement.rb +74 -0
- data/lib/rubocop/cop/dev_doc/view/prefer_prop_over_name.rb +64 -11
- data/lib/rubocop/dev_doc/version.rb +1 -1
- data/lib/rubocop/dev_doc.rb +1 -0
- metadata +6 -4
- data/lib/rubocop/cop/dev_doc/style/avoid_send.rb +0 -95
|
@@ -1,95 +0,0 @@
|
|
|
1
|
-
module RuboCop
|
|
2
|
-
module Cop
|
|
3
|
-
module DevDoc
|
|
4
|
-
module Style
|
|
5
|
-
# Avoid 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
|
-
# **Literal symbol arguments are exempt** — the method name is fixed at
|
|
14
|
-
# code-write time and visible to reviewers, equivalent to a direct call.
|
|
15
|
-
#
|
|
16
|
-
# ## Safer alternatives
|
|
17
|
-
#
|
|
18
|
-
# **a) For model attributes — use bracket notation instead.**
|
|
19
|
-
# `@model[column_name]` only accesses database columns, so it cannot
|
|
20
|
-
# accidentally invoke methods like `destroy`.
|
|
21
|
-
#
|
|
22
|
-
# ❌ Dangerous — method_name could be :destroy or any other method
|
|
23
|
-
# @user.send(method_name)
|
|
24
|
-
#
|
|
25
|
-
# ✔️ Safe — only accesses database columns
|
|
26
|
-
# @user[method_name]
|
|
27
|
-
#
|
|
28
|
-
# **b) For non-model objects — use a prefix to restrict callable methods.**
|
|
29
|
-
# By interpolating the dynamic part into a fixed prefix, only methods
|
|
30
|
-
# with that prefix (e.g. `export_csv`, `export_pdf`) can be invoked,
|
|
31
|
-
# preventing accidental calls to unintended methods.
|
|
32
|
-
#
|
|
33
|
-
# ❌ Unrestricted — any method can be called
|
|
34
|
-
# obj.send(method_name)
|
|
35
|
-
#
|
|
36
|
-
# ✔️ Restricted — only methods with the prefix can be called
|
|
37
|
-
# obj.send("export_#{method_name}")
|
|
38
|
-
#
|
|
39
|
-
# NOTE: A prefix narrows the callable surface but does not eliminate it —
|
|
40
|
-
# an attacker-controlled suffix can still reach any method sharing the
|
|
41
|
-
# prefix (e.g. `"export_#{x}"` could hit `export_and_destroy`). Use the
|
|
42
|
-
# narrowest prefix that fits, and prefer an explicit whitelist when the
|
|
43
|
-
# set of targets is small.
|
|
44
|
-
#
|
|
45
|
-
# @example
|
|
46
|
-
# # bad — dynamic method name from a variable
|
|
47
|
-
# @user.send(method_name)
|
|
48
|
-
# obj.public_send(action)
|
|
49
|
-
#
|
|
50
|
-
# # bad — interpolation with no static prefix restricts nothing
|
|
51
|
-
# obj.send("#{x}")
|
|
52
|
-
# obj.send("#{x}_run")
|
|
53
|
-
#
|
|
54
|
-
# # good — literal symbol: method name is statically visible
|
|
55
|
-
# instance.send(:private_helper, arg)
|
|
56
|
-
#
|
|
57
|
-
# # good — bracket notation for model attributes
|
|
58
|
-
# @user[attribute_name]
|
|
59
|
-
#
|
|
60
|
-
# # good — static prefix restricts the callable methods
|
|
61
|
-
# obj.send("export_#{method_name}")
|
|
62
|
-
class AvoidSend < Base
|
|
63
|
-
MSG = "Avoid dynamic `%<method>s` — use bracket notation for model attributes, " \
|
|
64
|
-
"or a prefix (`obj.send(\"export_\#{x}\")`) to restrict callable methods.".freeze
|
|
65
|
-
# `__send__` is the canonical alias — omitting it would leave a
|
|
66
|
-
# zero-cost dodge for the exact dynamic dispatch this cop restricts.
|
|
67
|
-
RESTRICT_ON_SEND = %i[send public_send __send__].freeze
|
|
68
|
-
|
|
69
|
-
def on_send(node)
|
|
70
|
-
return if node.receiver.nil?
|
|
71
|
-
|
|
72
|
-
arg = node.first_argument
|
|
73
|
-
return if arg&.sym_type?
|
|
74
|
-
return if prefixed_dynamic_method?(arg)
|
|
75
|
-
|
|
76
|
-
add_offense(node.loc.selector, message: format(MSG, method: node.method_name))
|
|
77
|
-
end
|
|
78
|
-
|
|
79
|
-
private
|
|
80
|
-
|
|
81
|
-
# A dynamic string/symbol that begins with a static prefix (e.g.
|
|
82
|
-
# `"export_#{x}"`) restricts the callable surface to methods sharing
|
|
83
|
-
# that prefix, so it is exempt. Pure interpolation (`"#{x}"`) or a
|
|
84
|
-
# trailing prefix (`"#{x}_run"`) restricts nothing and is still flagged.
|
|
85
|
-
def prefixed_dynamic_method?(arg)
|
|
86
|
-
return false unless arg&.type?(:dstr, :dsym)
|
|
87
|
-
|
|
88
|
-
first = arg.children.first
|
|
89
|
-
first&.str_type? && !first.value.empty?
|
|
90
|
-
end
|
|
91
|
-
end
|
|
92
|
-
end
|
|
93
|
-
end
|
|
94
|
-
end
|
|
95
|
-
end
|