Discrepancy between Rubular regex and Rails regex - ruby-on-rails

I want to validate U.S. phone numbers. On Rubular, the regex:
/^\(?([2-9][0-8][0-9])\)?[-.●]?([2-9][0-9]{2})[-.●]?([0-9]{4})$/
does not match the string:
'6463976583!'
as I expect. However, my Rails validator that uses the regex:
validates :phone, presence: true, format: {with: /^\(?([2-9][0-8][0-9])\)?[-.●]?([2-9][0-9]{2})[-.●]?([0-9]{4})$/}
seems to allow the same string.
Any thoughts as to where I'm going wrong?

pry(main)> '6463976583!'[/^\(?([2-9][0-8][0-9])\)?[-.]?([2-9][0-9]{2})[-.]?([0-9]{4})$/]?([0-9]{4})$/]
=> nil
pry(main)> '6463976583'[/^\(?([2-9][0-8][0-9])\)?[-.]?([2-9][0-9]{2})[-.]?([0-9]{4})$/]
=> "6463976583"
Perhaps try \A and \Z for your ActiveRecord validation: https://apidock.com/rails/ActiveModel/Validations/ClassMethods/validates_format_of
The documentation mentions it matches start and end of a string versus start and end of a line. I believe that Rails or Rubocop one, throws warnings if you don't use ^ and $ instead of \A or \Z
If you can't get the Rails validator to work, you can create a custom validator method and call your regex there

Related

Regex, devise & brakeman. Ruby on rails 5.2.2

I'm using the given regex expression to validate the username:
validates_format_of :username, with: /^[a-zA-Z0-9_\.]*$/, :multiline => true
By running brakeman gem i'm having the following warning.
== Warnings ==
Confidence: High
Category: Format Validation
Check: ValidationRegex
Message: Insufficient validation for `username` using `/^[a-zA-Z0-9_\.]*$/`. Use `\A` and `\z` as anchors
File: app/models/user.rb
Line: 16
What would be the optimal regex expression to correctly assign those anchors and keeping the same character permissions?
Regex is yet a myth for me!
Best regards.
From ruby on rails guides regular expressions
To fix the regular expression, \A and \z should be used instead of ^ and $, like
/\A[a-zA-Z0-9_\.]*\z/

Validate Numericality Conditions on English & Arabic Numbers

I'm using the following code to validate numericality in Rails:
validates :number, :numericality => {:greater_than_or_equal_to => 500}
And it works well, but then I added regex to allow Arabic numbers like this:
validates :number, :format =>{:with => /[0-9\u0660-\u0669]*/}
Here it accepts Arabic numbers but the condition greater_than_or_equal_to => 500 is working only on English numbers and I need it to support Arabic numbers too.
Alright, the digit can be defined as [0-9] and combined with Arabic ones, [0-9\u0660-\u0669]. We want to match 500 up to 999 first. We need to combine [5-9] with [\u0665-\u0669] -> [5-9\u0665-\u0669] that will match 5 to 9 in both ASCII and Arabic notations. After 5-9, there can be 2 more any digits, so we need to append [0-9\u0660-\u0669]{2} to it.
Next, we need to allow numbers more than 999, that is, 1000 and more. So, we need to add an alternative branch [0-9\u0660-\u066‌​9]{4,}.
The whole result is
/\A([5-9\u0665-\u0669][0-9\u0660-\u0669]{2}|[0-9\u0660-\u066‌​9]{4,})\z/
Where \A matches the start of string, \z matches the end of the string and (Branch_1|Branch_2) is a grouping construct that may be turned into a non-capturing one by adding ?: after the initial (:
/\A(?:[5-9\u0665-\u0669][0-9\u0660-\u0669]{2}|[0-9\u0660-\u066‌​9]{4,})\z/
See the regex demo
For ROR users who want to avoid using regex in such case to allow Arabic numbers to be working with Integer DB field (not string), I've found here a good example to convert Unicode numbers to Integer before validation, so it's still possible to use such condition:
validates :number, :numericality => {:greater_than_or_equal_to => 500}

Regular expression trouble in rails 4

I've the following regex in my model
validates :profile_name, presence:true,
uniqueness:true,
format:
{
with: /^[a-zA-Z0-9_-]+$/,
message: "Must be formatted correctly"
}
My tests are not passing because of regex. I am validating the profile name.
Here is my error log:
The provided regular expression is using multiline anchors (^ or $),
which may present a security risk. Did you mean to use \A and \z, or
forgot to add the :multiline => true option? (ArgumentError)
I think am doing right, still it is not passing. Please Help me to fix this.
Use, \A and \z instead of ^ and $ for regex:
validates :profile_name, presence:true,
uniqueness:true,
format:
{
with: /\A[a-zA-Z0-9_-]+\z/,
message: "Must be formatted correctly"
}
Read more on Regular Expressions as to why.

Regular expressions with validations in RoR 4

There is the following code:
class Product < ActiveRecord::Base
validates :title, :description, :image_url, presence: true
validates :price, numericality: {greater_than_or_equal_to: 0.01}
validates :title, uniqueness: true
validates :image_url, allow_blank: true, format: {
with: %r{\.(gif|jpg|png)$}i,
message: 'URL must point to GIT/JPG/PNG pictures'
}
end
It works, but when I try to test it using "rake test" I'll catch this message:
rake aborted!
The provided regular expression is using multiline anchors (^ or $), which may present a security risk. Did you mean to use \A and \z, or forgot to add the :multiline => true option?
What does it mean? How can I fix it?
^ and $ are Start of Line and End of Line anchors. While \A and \z are Permanent Start of String and End of String anchors.
See the difference:
string = "abcde\nzzzz"
# => "abcde\nzzzz"
/^abcde$/ === string
# => true
/\Aabcde\z/ === string
# => false
So Rails is telling you, "Are you sure you want to use ^ and $? Don't you want to use \A and \z instead?"
There is more on the rails security concern that generates this warning here.
This warning raises because your validation rule is vulnerable for javascript injection.
In your case \.(gif|jpg|png)$ matches till the end of the line. So your rule will validate this value pic.png\nalert(1); as true:
"test.png\n<script>alert(1)</script>" === /\.(gif|jpg|png)$/i
# => true
"test.png\n<script>alert(1)</script>" === /\.(gif|jpg|png)\z/i
# => false
Read the acticles:
http://caiustheory.com/validating-data-with-regular-expressions-in-ruby
http://guides.rubyonrails.org/security.html#regular-expressions
The problem regexp is not in devise, but rather lives in config/initializers/devise.rb. Change:
# Regex to use to validate the email address
config.email_regexp = /^([\w\.%\+\-]+)#([\w\-]+\.)+([\w]{2,})$/i
to:
# Regex to use to validate the email address
config.email_regexp = /\A([\w\.%\+\-]+)#([\w\-]+\.)+([\w]{2,})\Z/i
The warning is telling you that strings like the following will pass validation, but it is probably not what you want:
test = "image.gif\nthis is not an image"
re = /\.(gif|jpg|png)$/i
re.match(test) #=> #<MatchData ".gif" 1:"gif">
Both ^ and $ matches the start/end of any line, not the start/end of the string. \A and \z matches the start and the end of the full string, respectively.
re = /\.(gif|jpg|png)\z/i
re.match(test) #=> nil
The second part of the warning (“or forgot to add the :multiline => true option”) is telling you that if you actually want the behaviour of ^ and $ you can simply silence the warning passing the :multiline option.
If Ruby wants to see \z instead of the $ symbol sign, for security, you need to give it to him, then the code would look like this :
validates :image_url, allow_blank: true, format: {with: %r{\.(gif|jpg|png)\z}i, message: 'URL must point to GIF, JPG, PNG.'}

In Rails 3, how to validate string matches exactly only several strings

I'm creating a model in Rails 3 as follows:
class Foo < ActiveRecord::Base
attr_accessible :bar
type_regex = /(dog)|(cat)|(bird)/
validates :bar, :presence => true,
:format => { :with => type_regex }
end
I need the type to match either dog, cat or bird exactly. In the above class, however, type_regex will match doggy, cathrine, etc.
How do I make the validation match those strings exactly?
Thank you
You'll need to add anchors to the regex. Specifically you need to add \A to the beginning and \Z to the end of the regex (\A and \Z meaning beginning- and end-of-string respectively).
Be careful with precedence though: due to the low precedence of | you'll either have to put a non-capturing group around the alternation or anchor each of the words separately. I.e. you have to do /\A(?:(dog)|(cat)|(bird))\Z/. Doing /\A(dog)|(cat)|(bird)\Z/ will not work because that way the \A will only apply to dog and the \Z only to bird.
The same is true if you use ^ and $ instead of \A and \Z. On that subject let me explain the difference between those: ^ and $ match the beginning and end of a line, not the string. So the string "dog\ngy" would still match the regex /^dog$/, but not the regex /\Adog\Z/.
The regex you need is:
type_regex = /(^dog$)|(^cat$)|(^bird$)/
The other suggestion by Augusto's will still match 'catherine', 'doggie'.
add $ at the end. For example:
type_regex = /[(dog)|(cat)|(bird)]$/
(you might need to escape the $). And to validate the start of string use ^
type_regex = /^[(dog)|(cat)|(bird)]/
so this is what you want... I think :)
type_regex = /^[(dog)|(cat)|(bird)]$/

Resources