Possible unprotected redirect Open
redirect_to visibility_redirect_to(params)
- Read upRead up
- Exclude checks
Unvalidated redirects and forwards are #10 on the OWASP Top Ten.
Redirects which rely on user-supplied values can be used to "spoof" websites or hide malicious links in otherwise harmless-looking URLs. They can also allow access to restricted areas of a site if the destination is not validated.
Brakeman will raise warnings whenever redirect_to
appears to be used with a user-supplied value that may allow them to change the :host
option.
For example,
redirect_to params.merge(:action => :home)
will create a warning like
Possible unprotected redirect near line 46: redirect_to(params)
This is because params
could contain :host => 'evilsite.com'
which would redirect away from your site and to a malicious site.
If the first argument to redirect_to
is a hash, then adding :only_path => true
will limit the redirect to the current host. Another option is to specify the host explicitly.
redirect_to params.merge(:only_path => true)
redirect_to params.merge(:host => 'myhost.com')
If the first argument is a string, then it is possible to parse the string and extract the path:
redirect_to URI.parse(some_url).path
If the URL does not contain a protocol (e.g., http://
), then you will probably get unexpected results, as redirect_to
will prepend the current host name and a protocol.
Method has too many lines. [14/10] Open
def visibility
@smart_village.query <<~GRAPHQL
mutation {
changeVisibility (
id: #{params[:id]},
- Read upRead up
- Exclude checks
This cop checks if the length of a method exceeds some maximum value. Comment lines can optionally be ignored. The maximum allowed length is configurable.
Method has too many lines. [13/10] Open
def update
auth_server = SmartVillageApi.auth_server_url
uri = Addressable::URI.parse("#{auth_server}/data_provider/update.json")
data_to_send = {
data_provider: data_provider_params,
- Read upRead up
- Exclude checks
This cop checks if the length of a method exceeds some maximum value. Comment lines can optionally be ignored. The maximum allowed length is configurable.
DataProviderController#update has approx 7 statements Open
def update
- Read upRead up
- Exclude checks
A method with Too Many Statements
is any method that has a large number of lines.
Too Many Statements
warns about any method that has more than 5 statements. Reek's smell detector for Too Many Statements
counts +1 for every simple statement in a method and +1 for every statement within a control structure (if
, else
, case
, when
, for
, while
, until
, begin
, rescue
) but it doesn't count the control structure itself.
So the following method would score +6 in Reek's statement-counting algorithm:
def parse(arg, argv, &error)
if !(val = arg) and (argv.empty? or /\A-/ =~ (val = argv[0]))
return nil, block, nil # +1
end
opt = (val = parse_arg(val, &error))[1] # +2
val = conv_arg(*val) # +3
if opt and !arg
argv.shift # +4
else
val[0] = nil # +5
end
val # +6
end
(You might argue that the two assigments within the first @if@ should count as statements, and that perhaps the nested assignment should count as +2.)
DataProviderController#edit has approx 6 statements Open
def edit
- Read upRead up
- Exclude checks
A method with Too Many Statements
is any method that has a large number of lines.
Too Many Statements
warns about any method that has more than 5 statements. Reek's smell detector for Too Many Statements
counts +1 for every simple statement in a method and +1 for every statement within a control structure (if
, else
, case
, when
, for
, while
, until
, begin
, rescue
) but it doesn't count the control structure itself.
So the following method would score +6 in Reek's statement-counting algorithm:
def parse(arg, argv, &error)
if !(val = arg) and (argv.empty? or /\A-/ =~ (val = argv[0]))
return nil, block, nil # +1
end
opt = (val = parse_arg(val, &error))[1] # +2
val = conv_arg(*val) # +3
if opt and !arg
argv.shift # +4
else
val[0] = nil # +5
end
val # +6
end
(You might argue that the two assigments within the first @if@ should count as statements, and that perhaps the nested assignment should count as +2.)
DataProviderController assumes too much for instance variable '@current_user' Open
class DataProviderController < ApplicationController
- Read upRead up
- Exclude checks
Classes should not assume that instance variables are set or present outside of the current class definition.
Good:
class Foo
def initialize
@bar = :foo
end
def foo?
@bar == :foo
end
end
Good as well:
class Foo
def foo?
bar == :foo
end
def bar
@bar ||= :foo
end
end
Bad:
class Foo
def go_foo!
@bar = :foo
end
def foo?
@bar == :foo
end
end
Example
Running Reek on:
class Dummy
def test
@ivar
end
end
would report:
[1]:InstanceVariableAssumption: Dummy assumes too much for instance variable @ivar
Note that this example would trigger this smell warning as well:
class Parent
def initialize(omg)
@omg = omg
end
end
class Child < Parent
def foo
@omg
end
end
The way to address the smell warning is that you should create an attr_reader
to use @omg
in the subclass and not access @omg
directly like this:
class Parent
attr_reader :omg
def initialize(omg)
@omg = omg
end
end
class Child < Parent
def foo
omg
end
end
Directly accessing instance variables is considered a smell because it breaks encapsulation and makes it harder to reason about code.
If you don't want to expose those methods as public API just make them private like this:
class Parent
def initialize(omg)
@omg = omg
end
private
attr_reader :omg
end
class Child < Parent
def foo
omg
end
end
Current Support in Reek
An instance variable must:
- be set in the constructor
- or be accessed through a method with lazy initialization / memoization.
If not, Instance Variable Assumption will be reported.
DataProviderController has no descriptive comment Open
class DataProviderController < ApplicationController
- Read upRead up
- Exclude checks
Classes and modules are the units of reuse and release. It is therefore considered good practice to annotate every class and module with a brief comment outlining its responsibilities.
Example
Given
class Dummy
# Do things...
end
Reek would emit the following warning:
test.rb -- 1 warning:
[1]:Dummy has no descriptive comment (IrresponsibleModule)
Fixing this is simple - just an explaining comment:
# The Dummy class is responsible for ...
class Dummy
# Do things...
end
DataProviderController#visibility_redirect_to calls 'params[:item_type]' 3 times Open
return "/events" if params[:item_type] === "EventRecord"
return "/surveys/#{params[:parent_id]}/comments" if params[:item_type] === "Survey_Comment"
"/#{params[:item_type].underscore.pluralize}"
- Read upRead up
- Exclude checks
Duplication occurs when two fragments of code look nearly identical, or when two fragments of code have nearly identical effects at some conceptual level.
Reek implements a check for Duplicate Method Call.
Example
Here's a very much simplified and contrived example. The following method will report a warning:
def double_thing()
@other.thing + @other.thing
end
One quick approach to silence Reek would be to refactor the code thus:
def double_thing()
thing = @other.thing
thing + thing
end
A slightly different approach would be to replace all calls of double_thing
by calls to @other.double_thing
:
class Other
def double_thing()
thing + thing
end
end
The approach you take will depend on balancing other factors in your code.
DataProviderController#edit calls 'result.body' 2 times Open
@data_provider = OpenStruct.new(JSON.parse(result.body))
else
flash[:error] = "Achtung! Verbindungsproblem zum Server, bitte versuchen Sie es später noch einmal (#{result.body})"
- Read upRead up
- Exclude checks
Duplication occurs when two fragments of code look nearly identical, or when two fragments of code have nearly identical effects at some conceptual level.
Reek implements a check for Duplicate Method Call.
Example
Here's a very much simplified and contrived example. The following method will report a warning:
def double_thing()
@other.thing + @other.thing
end
One quick approach to silence Reek would be to refactor the code thus:
def double_thing()
thing = @other.thing
thing + thing
end
A slightly different approach would be to replace all calls of double_thing
by calls to @other.double_thing
:
class Other
def double_thing()
thing + thing
end
end
The approach you take will depend on balancing other factors in your code.
DataProviderController assumes too much for instance variable '@smart_village' Wontfix
class DataProviderController < ApplicationController
- Read upRead up
- Exclude checks
Classes should not assume that instance variables are set or present outside of the current class definition.
Good:
class Foo
def initialize
@bar = :foo
end
def foo?
@bar == :foo
end
end
Good as well:
class Foo
def foo?
bar == :foo
end
def bar
@bar ||= :foo
end
end
Bad:
class Foo
def go_foo!
@bar = :foo
end
def foo?
@bar == :foo
end
end
Example
Running Reek on:
class Dummy
def test
@ivar
end
end
would report:
[1]:InstanceVariableAssumption: Dummy assumes too much for instance variable @ivar
Note that this example would trigger this smell warning as well:
class Parent
def initialize(omg)
@omg = omg
end
end
class Child < Parent
def foo
@omg
end
end
The way to address the smell warning is that you should create an attr_reader
to use @omg
in the subclass and not access @omg
directly like this:
class Parent
attr_reader :omg
def initialize(omg)
@omg = omg
end
end
class Child < Parent
def foo
omg
end
end
Directly accessing instance variables is considered a smell because it breaks encapsulation and makes it harder to reason about code.
If you don't want to expose those methods as public API just make them private like this:
class Parent
def initialize(omg)
@omg = omg
end
private
attr_reader :omg
end
class Child < Parent
def foo
omg
end
end
Current Support in Reek
An instance variable must:
- be set in the constructor
- or be accessed through a method with lazy initialization / memoization.
If not, Instance Variable Assumption will be reported.
DataProviderController#visibility_record_type calls 'params[:item_type]' 4 times Open
return "Survey::Poll" if params[:item_type] === "Survey"
return "Survey::Comment" if params[:item_type] === "Survey_Comment"
return "GenericItem" if params[:item_type] === "Noticeboard"
params[:item_type]
- Read upRead up
- Exclude checks
Duplication occurs when two fragments of code look nearly identical, or when two fragments of code have nearly identical effects at some conceptual level.
Reek implements a check for Duplicate Method Call.
Example
Here's a very much simplified and contrived example. The following method will report a warning:
def double_thing()
@other.thing + @other.thing
end
One quick approach to silence Reek would be to refactor the code thus:
def double_thing()
thing = @other.thing
thing + thing
end
A slightly different approach would be to replace all calls of double_thing
by calls to @other.double_thing
:
class Other
def double_thing()
thing + thing
end
end
The approach you take will depend on balancing other factors in your code.
DataProviderController#visibility_redirect_to doesn't depend on instance state (maybe move it to another class?) Open
def visibility_redirect_to(params)
- Read upRead up
- Exclude checks
A Utility Function is any instance method that has no dependency on the state of the instance.
DataProviderController#visibility_record_type doesn't depend on instance state (maybe move it to another class?) Open
def visibility_record_type(params)
- Read upRead up
- Exclude checks
A Utility Function is any instance method that has no dependency on the state of the instance.
Avoid the use of the case equality operator ===
. Open
return "Survey::Poll" if params[:item_type] === "Survey"
- Read upRead up
- Exclude checks
This cop checks for uses of the case equality operator(===).
Example:
# bad
Array === something
(1..100) === 7
/something/ === some_string
# good
something.is_a?(Array)
(1..100).include?(7)
some_string =~ /something/
Line is too long. [122/100] Open
flash[:error] = "Achtung! Verbindungsproblem zum Server, bitte versuchen Sie es später noch einmal (#{result.body})"
- Exclude checks
Avoid the use of the case equality operator ===
. Open
return "Survey::Comment" if params[:item_type] === "Survey_Comment"
- Read upRead up
- Exclude checks
This cop checks for uses of the case equality operator(===).
Example:
# bad
Array === something
(1..100) === 7
/something/ === some_string
# good
something.is_a?(Array)
(1..100).include?(7)
some_string =~ /something/
Missing top-level class documentation comment. Open
class DataProviderController < ApplicationController
- Read upRead up
- Exclude checks
This cop checks for missing top-level documentation of classes and modules. Classes with no body are exempt from the check and so are namespace modules - modules that have nothing in their bodies except classes, other modules, or constant definitions.
The documentation requirement is annulled if the class or module has a "#:nodoc:" comment next to it. Likewise, "#:nodoc: all" does the same for all its children.
Example:
# bad
class Person
# ...
end
# good
# Description/Explanation of Person class
class Person
# ...
end
Avoid the use of the case equality operator ===
. Open
return "/events" if params[:item_type] === "EventRecord"
- Read upRead up
- Exclude checks
This cop checks for uses of the case equality operator(===).
Example:
# bad
Array === something
(1..100) === 7
/something/ === some_string
# good
something.is_a?(Array)
(1..100).include?(7)
some_string =~ /something/
Avoid the use of the case equality operator ===
. Open
return "GenericItem" if params[:item_type] === "Noticeboard"
- Read upRead up
- Exclude checks
This cop checks for uses of the case equality operator(===).
Example:
# bad
Array === something
(1..100) === 7
/something/ === some_string
# good
something.is_a?(Array)
(1..100).include?(7)
some_string =~ /something/
Avoid the use of the case equality operator ===
. Open
return "/surveys/#{params[:parent_id]}/comments" if params[:item_type] === "Survey_Comment"
- Read upRead up
- Exclude checks
This cop checks for uses of the case equality operator(===).
Example:
# bad
Array === something
(1..100) === 7
/something/ === some_string
# good
something.is_a?(Array)
(1..100).include?(7)
some_string =~ /something/