Brakeman insufficient validation warning of regex anchors - ruby-on-rails

I'm trying to implement a validation in a model like this.
validates_format_of :field, with: /[0-9]/, message: 'must have at least one number (0-9)'
Brakeman detects this a Format Validation security issue and it recommends to add the anchors between the regular expression.
Insufficient validation for 'field' using /[0-9]/. Use \A and \z as anchors near line 54
If I add those anchors, the regular expression stops working, so I don't know what to do in this case. Here's the tests I made using rails c.
"asdf1234".match(/\A[0-9]\z/) # => nil
"foobar1".match(/\A[0-9]\z/) # => nil
I need that the method return #<MatchData "1"> in both cases.
Any ideas?
Thanks.

If you need to match a string that has at least 1 digit inside, and any other chars before and after, you may use
/\A[^0-9]*[0-9].*\z/m
or just
/\A.*[0-9].*\z/m
Details
\A - start of string
[^0-9]* - zero or more chars other than an ASCII digit
[0-9] - an ASCII digit
.* - any 0+ chars, as many as possible, up to the
\z - end of string.
The m modifier makes . match any char, including a line break char.
Actually, /\A.*[0-9].*\z/m will be a bit slower, as the first .* will grab all the string at once and then will backtrack to find the last digit. The first one is more optimized.

Related

Why is my expression matching something that clearly doesn't meet the expression?

I'm using Rails 4.2.7. I want to match the pattern, numbers, an arbitrary number of spaces, and a potential "+" at the end. So I wrote
2.3.0 :013 > /\d+\s*\+?/.match("40+")
=> #<MatchData "40+">
However, this is also matching
2.3.0 :012 > /\d+\s*\+?/.match("40-50")
=> #<MatchData "40">
What gives? The string "40-50" doesn't match the expression provided, but clearly I'm not doing something right in my regex.
If you want it to match only the full string, use the \A and \z markers. Like this:
/\A\d+\s*\+\z$/.match("40-50")
This will force the regular expression to match only if the full string match it.
Otherwise, the way you have it now, it will stop as soon as it find a match anywhere in your string.
\d+ means one digit or more.
\s* means an optional space.
\+? means an optional + sign
So 40 does match those conditions.
The ? Quantifier is Your Problem
You want the following instead:
/\d+\s*\+/.match("40-50")
If you use \+? instead of \+ without a quantifier, the question mark modifies the statement to mean "zero or one of the preceding atoms." With the quantifier, 40-50 matches since it has zero + characters in the string.

Insufficient validation for 'name' using / +\w/. Use \A and \z as anchors

I used brakeman for generating scanning reports in my application. It generated a Format Validation security warning with High Confidence in my model page:
Insufficient validation for 'name' using / +\w/. Use \A and \z as
anchors near line 54
This is the line in my model where I am facing error:
validates_format_of :name, :with => / +\w/, :message => "must be your first and last name."
If there is no space in the name field, I am showing above validation. How can show it \A and\z format?
Brakeman is recommending the use of \A and \z-anchors in your validation regex. They define where the match has to start at (\A) and end at (\z). Without them, the regex can simply match to any part of the name. Let's transform your regex to encompass the whole string:
/\A\w+ +\w+\z/
You can remove the + after the space if you only want to accept one space.
Using the normal regex line begin (^) and end ($) anchors is also not recommended, because it would be enough to have one line which matches, and all other lines could be invalid.

Regex pattern for checking name with limited characters

I have a requirement to validate username with some special characters in it('-) and white space in it. I am able to achieve this with the help of the following regex -
^[a-zA-z]+([ '-][a-zA-Z]+)*$
But I am unable to add a limit to the maximum number of characters that user can enter say 25 to this particular regex. So can any one please explain how to do the same for the above regex? Thanks.
You may add a negative lookahead at the beginning disallowing 26 or more chars:
^(?!.{26})[a-zA-Z]+([ '-][a-zA-Z]+)*$
^^^^ ^
You also have a typo [A-z], it must be [A-Z]. See Difference between regex [A-z] and [a-zA-Z].
The negative lookahead (the (?!...) construct above) is anchored at the start of the string (meaning it is placed right after ^), and the length check is performed only once, right before parsing with the main, consuming pattern part.
You can also see more on how a negative lookahead works here.
Just add a lookahead at the beginning to match only for a max of 25 characters by adding a (?=.{1,25}$).
Your new regex will be: ^(?=.{1,25}$)[a-zA-z]+([ '-][a-zA-Z]+)*$

validates_format_of in rails (only numbers, commas and white spaces)

I have a field "floors" and I want it to accept only numbers, commas and white spaces.
I'm using a validates_format_of :floors, :with => /[0-9\,\s]+/ right now, but it works bad because it accepts a string like "1, 2, abc".
Please help me to find my mistake.
Your regex matches 1, 2, inside 1, 2, abc, it is a partial match. To disallow partial matches, use start-of-string and end-of-string anchors.
In Ruby, to match the start of the string you need to use \A anchor. The end-of-string anchor is \z. Thus, use
/\A[0-9,\s]+\z/
See regex demo
Also note that , is not a special regex metacharacter and does not need escaping.
If you need to start with a number, you can use
/\A\d[\d,\s]*\z/
Here, \d will require a digit to appear in the beginning and then it can be followed with digits, whitespace and commas, zero or more occurrences. Another way of restricting the generic character class is using a lookahead: \A(?=\d)[\d,\s]+\z.
Going further, you can match numbers like 1,300,567.567 or 1 300 567.567 with
/\A\d{1,3}(?:[,\s]\d{3})*(?:\.\d+)?\z/
See another demo

username regex in rails

I am trying to find a regex to limit what a person can use for a username on my site. I don't need to have it check to see how many characters there are in it, as another validation does this. Basically all I need to make it do is make sure that it allows: letters (capital and lowercase) numbers, dashes and underscores.
I came across this: /^[-a-z]+$/i
But it doesn't seem to allow numbers.
What am I missing?
The regex you're looking for is
/\A[a-z0-9\-_]+\z/i
Meaning one or more characters of range a-z, range 0-9, - (needs to be escaped with a backslash) and _, case insensitive (the i qualifier)
Use
/\A[\w-]+\z$/
\w is shorthand for letters, digits and underscore.
\A matches at the start of the string, \z matches at the end of the string. These tokens are called anchors, and Ruby is a bit special with regard to them: Most regex engines use ^ and $ as start/end-of-string anchors by default, whereas in Ruby they can also match at the start/end of lines (which matters if you're working with multiline strings). Therefore, it's safer (as #JustMichael pointed out) to use \A and \z because there is no such ambiguity.
Your regular expression contains a character class [-a-z] that allows the characters - (dash) and a through z. In order to expand the range of characters allowed by this character class, you will need to add more characters within the [].
Please see Character Classes or Character Sets for further information and examples.

Resources