Escape URL in rails - ruby-on-rails

I have a commenting system where people can leave a comment together with their website. Since rails now escapes everything by default I don't really do anything to avoid XSS and it works find - almost. For some reason the URL isn't escaped.
In order to display the username I have a simple helper:
def display_name(name, site)
if !site.blank?
return link_to(name, site)
else
return name
end
end
But if you put something like javascript:alert(1) into the website field it get injected directly into the page - any idea how to escape this?

Even if you escape javascript, malicous users could still create URLs which point to, say, delete urls that could potentially affect a user's data. Why not verify the URL as such when you collect it?
validates :attribute, :url => true
I'd recommend using Thong Kuah's UrlValidator.

Related

Changing urls in ruby on rails depending on different conditions

I'm new to ruby on rails....I wanted to know if there is a way to change the URL displayed depending on the client's response. I mean... here's an example:
I'm making a project showing listings in various places...
Now in general I have a home page, a search page, and a detail page for listings. So, respective URLs are officespace/home, officespace/search?conditions, officespace/detailpage?id=(controller-officespace)[&Conditions eg.---price,size,place,type...]
So, every time the client makes a request for search, the same URL is shown, of course with the given conditions.
Now I want that if the client asks for only the place and mentions nothing about size, price, etc., the url should be /listing/location_name.
If he mentions other conditions, then it'll be listing/(office_type)/size(x sq feet)_office_for_rent_in_locationname)
B.t.w. (I already have a controller named listings and its purpose is something else.)
And so on ........... Actually, I want to change URLs for a number of things. Anyway, please help me. And please don't refer me to the manuals. I've already read them and they didn't give any direct help.
This is an interesting routing challenge. Essentially, your goal is to create a special expression that will match the kinds of URL's you want to display in the user's browser. These expressions will be used in match formulas in config/routes.rb. Then, you'll need to make sure the form actions and links on relevant search pages link to those specialized URL's and NOT the default pages. Here's an example to get started:
routes.rb
match "/listing/:officeType/size/:squarefeet/office_for/:saleOrRent/in/:locationName" => "searches#index"
match "/listing/*locationName" => "searches#index"
resources :searches
Since you explicitly mentioned that your listings controller is for something else, I just named our new controller searches. Inside the code for the index method for this controller, you have to decide how you want to collect the relevant data to pass along to your view. Everything marked with a : in the match expressions above will be passed to the controller in the params hash as if it were an HTTP GET query string parameter. Thus we can do the following:
searches_controller.rb
def index
if params[:squarefeet] && params[:officeType] && params[:locationName]
#listings = Listing.where("squarefeet >= ?", params[:squarefeet].to_i).
where(:officeType => params[:officeType],
:locationName => params[:locationName])
elsif params[:locationName]
#listings = Listing.where(:locationName => params[:locationName])
else
#listings = Listing.all
end
end
And to send the user to one of those links:
views/searches/index.html.erb
<%= link_to "Click here for a great office!", "/listing/corporate/size/3200/office_for/rent/in/Dallas" %>
The above example would only work if your Listing model is set up exactly the same way as my arbitrary guess, but hopefully you can work from there to figure out what your code needs to look like. Note that I wasn't able to get the underscores in there. The routes only match segments separated by slashes as far as I can tell. Keep working on it and you may find a way past that.

How can I make rails route helpers always use to_param to generate the path, even when I just pass in an ActiveRecord ID?

So, I'm implementing a pretty/SEO-friendly URL scheme for my rails app. I have a model called Artist, and I would like the Rails artist_path helper to always generate the friendly version of the path.
In my routes.rb file, I have the following line:
get 'artists/:id(/:slug)', :to => 'artists#show', :as => 'artist'
If the slug is left out, or is incorrect (it's calculated by the artist name), the controller 301 redirects to the correct URL. However, for SEO reasons, I want to ensure that all links internal to my site have the correct URL to start with.
The Artist model has the two following (very simple) functions to allow this to work:
def slug
name.parameterize
end
def to_param
"#{id}/#{slug}"
end
If I call artist_path with an artist object, this works as intended:
> app.artist_path(Artist.find 1234)
=> "/artists/1234/artist-name"
However, when I use call it with just the ID, it does not seem to use to_param at all:
> app.artist_path(id: 1234)
=> "/artists/1234"
tl;dr: How can I force Rails to always instantiate the object and call to_param on it when artist_path is called, even when only the ID is specified?
As far as I'm aware, the reason why what you're asking won't work is because when you pass in values to the built-in/automatic URL helpers (like an ID, per your example), those values just get used to "fill in the blanks" in the route URL.
If you pass an array, the values from the array will get used in order until the URL is filled in. If you pass a hash, those properties will get replaced into the URL. An object, like your model, will use it's to_param method to fill in the values... etc.
I understand your concern regarding "having knowledge of the limitations of that model," however, this behavior is standard in Rails and I don't believe it would really throw anyone. When you pass in an ID, as you do in your example, you're not telling the URL helper to "lookup a record via the model using this ID," you're simply telling it to "replace ':id' in the URL string with the ID you're providing." I'm fairly certain the built-in URL helpers wouldn't even know how to lookup the record - what model to use, etc. - other than maybe inferring from the route/controller name.
My best suggestion is to always use the instantiated model/record. If you were hoping the URL Helper would look that up for you, then there's no extra overhead as far as querying the database goes. If there's some additional reason you want to avoid instantiating the record yourself, I'd be glad to hear it and possibly provide other suggestions.

There is a common practice to reject "malicious" user-input to a rails application

I am using Ruby on Rails 3.1.0 and I would like to know what is a common practice to prevent to store "malicious" values in the database.
For example, I have a database table column means to store URLs. A user, passing the validation (just a length check), can submit a URL like http://<script>alert('hello!');</script>. I would like to do not permit to store links like the above... how can I make that?
The proper thing to do is use URI to parse the supposed URL and then check each component:
validate :url_check
def url_check
u = URI.parse(self.url)
# check u.scheme, u.userinfo, etc. and call errors.add(:url, '...')
# if something is invalid.
rescue URI::InvalidURIError
errors.add(:url, 'You are being naughty.')
end
While those links are in the database, they do no harm. Problems might occur when you try to render them. Rails does a good job in escaping most things that you output (I didn't dare to say "everything", 'cause I don't know for sure).
You can be extra sure and escape a string yourself:
CGI.escape your_link_url
Link: CGI.escape
You can use regex to validate it's a valid url without the '<', '>' url. And HTML encode it where it applies.

Append query string to request.request_uri without knowing if there was a query string

When a user arrives to my site on an iPhone I want to present them with a page with 2 options:
1) Go get our app or
2) Continue on to the requested URL
I do this by checking the user agent in my application controller and rendering a view.
For option 2 to work properly on that view I want the link to preserve the original URL and append a query string parameter fullsite=1. I look for that query string parameter param in a before_filter in my application controller and set a cookie so that the user is not prompted again.
What is the cleanest way to append a query string parameter to a request.request_uri knowing that that request url may or may not have had a query string to start with? I know it is straight forward to check if the request had parameters but I'm wondering if there is a helper to append a parameter cleanly in either case (no params or with existing params) without evaluating things on my own. Seeking the correct "railsy" way to do this.
Probably the best way to do this is to add your flag to the params hash, which will already have all the other information you need to build that link (presuming you catch iPhones and render in a before_filter), and then build your link from the modified params hash:
In your before_filter:
params[:fullsite] = 1
And then in your view:
<%= link_to('Continue to the Full Site', params) %>
This way Rails gets to do all the messy work of escaping and appending your query strings - both :fullsite and whatever the original query string contained.
If you're just using :fullsite as a way to avoid redirecting iPhones through your iPhone portal multiple times, though, it seems like either of these approaches would work better:
Set :fulltext in the session instead, or
Only redirect users if they're on iPhones AND request.referer is something other than your site.
Hope this helps!

how do I make the URL's in Ruby on Rails SEO friendly knowing a #vendor.name?

My application is in RoR
I have an action/view called showsummary where the ID has been passed into the URL, and the controller has used that to instantiate #vendor where #vendor.name is the name of a company.
I would like the URL to be, rather than showsummary/1/ to have /vendor-name in the URL instead.
How do I do that?
All of these solutions use find_by_name, which would definitely require having an index on that column and require they are unique. A better solution that we have used, sacrificing a small amount of beauty, is to use prefix the vendor name with its ID. This means that you dont have to have an index on your name column and/or require uniqueness.
vendor.rb
def to_param
normalized_name = name.gsub(' ', '-').gsub(/[^a-zA-Z0-9\_\-\.]/, '')
"#{self.id}-#{normalized_name}"
end
So this would give you URLs like
/1-Acme
/19-Safeway
etc
Then in your show action you can still use
Vendor.find(params[:id])
as that method will implicitly call .to_i on its argument, and calling to_i on such a string will always return the numerical prefix and drop the remaining text- its all fluff at that point.
The above assumes you are using the default route of /:controller/:action/:id, which would make your URLs look like
/vendors/show/1-Acme
But if you want them to just look
/1-Acme
Then have a route like
map.show_vendor '/:id', :controller => 'vendors', :action => 'show'
This would imply that that it would pretty much swallow alot of URLs that you probably wouldnt want it too. Take warning.
I thought I'd mention String#parameterize, as a supplement to the tagged answer.
def to_param
"#{id}-#{name.parameterize}"
end
It'll filter out hyphenated characters, replace spaces with dashes etc.
Ryan Bates has a great screencast on this very subject.
Basically you overload the to_param method in the Vendor model.
def to_param
permalink
end
Then when you look up the resource in your controller you do something like this:
#vender = Vender.find_by_name(params[:id])
But the problem with this is that you'll have to make sure that the vendors' names are unique. If they can't be then do the other solution that Ryan suggests where he prepends the the id to the name and then parses the resulting uri to find the item id.
You do this by modifying the routes that are used to access those URL's and changing them to use :name, rather than :id. This will probably mean that you have to write the routes yourself rather than relying on resources.
For instance add this to the routes.rb file:
map.with_options :controller => "vendor" do |vendor|
vendor.connect "/vendor/:name", :action => "show"
# more routes here for update, delete, new, etc as required
end
The other change that will be required is that now you'll have to find the vendor object in the database by the name not the id, so:
#vendor = Vendor.find_by_name(params[:name])
Internally (at least to my knowledge/experimentation) whatever parameter name is not specified in the URL part of the route (i.e. not within the "/Controller/Action/:id" part of it) is tacked on to the end as a parameter.
Friendly ID
http://github.com/norman/friendly_id/blob/26b373414eba639a773e61ac595bb9c1424f6c0b/README.rdoc
I'd have to experiment a bit to get it right, but there's two primary parts to the solution.
1) Add a route.
in config/routes, add a line that sends requests of the form baseurl/controller/:vendor-name to the action showsummary, (or maybe a new action, show_summary_by_vendor_name)
[also, if you planned on using baseurl/:vendorname, that's fine too]
For convenience, make sure the parameter is something like :vendor-name, not the default :id
2) Write the controller action.
In the controller file, either edit your showsummary action to differentiate based on whether it's called with an id or with a vendorname, or just write a show_summary_by_vendor_name. (depending on best practices, and what route you wrote in 1. I don't know off the top of my head which is preferable)
You can then do
#vendor = Vendors.find_by_name(params[:vendor_name])
or something like that, and then render it the way you would in regular showsummary.
3) Use that as the link.
Once you confirm that baseurl[/controller?]/vendor-name works, and shows the summary, make sure all the links in your application, and elsewhere, use that link. Off the top of my head, I can't remember how difficult it is to integrate a custom route into link_to, but I think it's doable. Most search engines [google] rely heavily on links, so good SEO will have you using those named links, not the numbered ones. I think. I don't know much about SEO.
Take a look also at this quck start to SEO for Rails

Resources