Why does the Rails sanitizer remove hrefs that contain capitalized URLs? - ruby-on-rails

An example:
[Dev]> ActionController::Base.helpers.sanitize('test')
=> 'test'
[Dev]> ActionController::Base.helpers.sanitize('test')
=> '<a>test</a>'
Extremely frustrating!

This seems to be a bug in the method contains_bad_protocols? in action_controller/vendor/html-scanner/html/sanitizer.rb. This method is defined as:
def contains_bad_protocols?(attr_name, value)
uri_attributes.include?(attr_name) &&
(value =~ /(^[^\/:]*):|(&#0*58)|(&#x70)|(%|%)3A/
&& !allowed_protocols.include?(value.split(protocol_separator).first))
end
And allowed_protocols as:
self.allowed_protocols = Set.new(%w(ed2k ftp http https irc mailto news gopher nntp
telnet webcal xmpp callto feed svn urn aim rsync tag ssh sftp rtsp afs))
Thus:
allowed_protocols.include? 'http' => true
allowed_protocols.include? 'Http' => false

Related

Rails 4 - Parse URL

I'm parsing a URL in my Rails application. I want to get the domain example.com stripped of any protocol, subdomain, directories, etc.
My method:
def clean_host(url)
uri = URI.parse(url)
return uri
end
What I actually get for example.com/ is:
scheme:
user:
password:
host:
port:
path: example.com/
query:
opaque:
registry:
fragment:
parser:
What I eventually want is to get the domain example.com stripped of any protocol, subdomain, directories, etc.
I've looked into domainatrix but it wont bundle with my project. Is there a way to do this with Ruby's URI.parse? Or will I have to look into other avenues?
The problem is that example.com/ isn't a full URL. URI rightly assumes it's a path. If it were me and you're going to have a lot of these fragments I'd just brute force it.
> "example.com/".sub(%r{^.*?://}, '').sub(%r{/.*$}, '')
=> "example.com"
> "http://subdomain.example.com/path/here".sub(%r{^.*?://}, '').sub(%r{/.*$}, '')
=> "subdomain.example.com"
Stripping the subdomain off is another ball of wax as you'd need to example the TLD to determine what is appropriate so you don't end up with say "com.uk"
You should just be able to do request.domain assuming you are doing this in a controller.
You can also use request.path and request.subdomain
For example: http://www.domain.com/users/123
request.subdomain => 'www'
request.domain => 'domain.com'
request.path => '/users/123'

Rails, Backbone, PhoneGap, CORS (not allowed by Access-Control-Allow-Origin error)

I'm building a Phonegap app in that uses backbone.js and a Rails backend. While creating new users, I am getting a CORS related error:
I am running my PhoneGap web app on http://0.0.0.0:8000 ($ python -m SimpleHTTPServer), and running my Rails app in webrick on http://0.0.0.0:3000 ($ rails server).
The problem happens when trying to create a new "Spot" in Backbone like this (chrome js console):
> s = new App.Models.Spot()
(creates Spot)
> s.save()
(returns error Object)
OPTIONS http://0.0.0.0:3000/spots.json 404 (Not Found) jquery-1.8.2.js:8416
XMLHttpRequest cannot load http://0.0.0.0:3000/spots.json. Origin http://0.0.0.0:8000 is not allowed by Access-Control-Allow-Origin.
Here is my application controller:
def set_access_control_headers
headers['Access-Control-Allow-Origin'] = 'http://0.0.0.0:8000'
headers['Access-Control-Request-Method'] = 'POST, GET'
end
I have read numerous articles, and the furthest along I could get was when modifying my routes.rb to include this:
match '*all' => 'application#cor', :constraints => {:method => 'OPTIONS'}
And in my application_controller.rb
def cor
headers["Access-Control-Allow-Origin"] = "*"
headers["Access-Control-Allow-Methods"] = %w{GET POST PUT DELETE OPTIONS}.join(",")
headers["Access-Control-Allow-Headers"] = %w{Origin Accept Content-Type X-Requested-With X-CSRF-Token}.join(",")
head(:ok) if request.request_method == "OPTIONS"
end
Adding the route and the 'cor' method will then allow me to make saves, but, I will have the same error when deleting records.
I get the basic idea of CORS, I can't access a server with a different domain than that of the origin of my request. But how exactly to set this up with Rails, Backbone, Phonegap, is not clear to me. Any help would be awesome, thanks!
I've follow this article (section "CORS in Rails") and it works for me.
I change if request.method == :options to if request.method == 'OPTIONS' and add methods PUT, DELETE to headers['Access-Control-Allow-Methods']. Insted of '*' in Access-Control-Allow-Origin I have localhost:8080 (I'm running my app on nginx).
Hope this will help.
PD: Do you have filters (hooks) in your application controller?

Rails Facebook avatar to data-uri

I'm trying to pull a facebook avatar via auth. Here's what i'm doing:
def image_uri
require 'net/http'
image = URI.parse(params[:image]) # https://graph.facebook.com/565515262/picture
fetch = Net::HTTP.get_response(image)
based = 'data:image/jpg;base64,' << Base64.encode64(fetch)
render :text => based
end
I'm getting the following error (new error — edited):
Connection reset by peer
I've tried googling about, I can't seem to get a solution, any ideas?
I'm basically looking for the exact functioning of PHP's file_get_contents()
Try escaping the URI before parsing:
URI.parse URI.escape(params[:image])
Make sure that params[:image] does contain the uri you want to parse... I would instead pass the userid and interpolate it into the uri.
URI.parse URI.escape("https://graph.facebook.com/#{params[:image]}/picture)"
Does it throw the same error when you use a static string "https://graph.facebook.com/565515262/picture"
What does it say when you do
render :text => params[:image]
If both of the above don't answer your question then please try specifying the use of HTTPS-
uri = URI('https://secure.example.com/some_path?query=string')
Net::HTTP.start(uri.host, uri.port, :use_ssl => uri.scheme == 'https').start do |http|
request = Net::HTTP::Get.new uri.request_uri
response = http.request request # Net::HTTPResponse object
end
Presuming you are on ruby < 1.9.3, you will also have to
require 'net/https'
If you are on ruby 1.9.3 you don't have to do anything.
Edit
If you are on the latest version, you can simply do:
open(params[:image]) # http://graph.facebook.com/#{#user.facebook_id}/picture

invalid URI - How to prevent, URI::InvalidURIError errors?

I got the following back from delayed_job:
[Worker(XXXXXX pid:3720)] Class#XXXXXXX failed with URI::InvalidURIError: bad URI(is not URI?): https://s3.amazonaws.com/cline-local-dev/2/attachments/542/original/mac-os-x[1].jpeg?AWSAccessKeyId=xxxxxxxx&Expires=1295403309&Signature=xxxxxxx%3D - 3 failed attempts
The way this URI comes from in my app is.
In my user_mailer I do:
#comment.attachments.each do |a|
attachments[a.attachment_file_name] = open(a.authenticated_url()) {|f| f.read }
end
Then in my attachments model:
def authenticated_url(style = nil, expires_in = 90.minutes)
AWS::S3::S3Object.url_for(attachment.path(style || attachment.default_style), attachment.bucket_name, :expires_in => expires_in, :use_ssl => attachment.s3_protocol == 'https')
end
That being said, is there some type of URI.encode or parsing I can do to prevent a valid URI (as I checked the URL works in my browser) for erroring and killing delayed_job in rails 3?
Thank you!
Ruby has (at least) two modules for dealing with URIs.
URI is part of the standard library.
Addressable::URI, is a separate gem, and more comprehensive, and claims to conform to the spec.
Parse a URL with either one, modify any parameters using the gem's methods, then convert it using to_s before passing it on, and you should be good to go.
I tried ' open( URI.parse(URI.encode( a.authenticated_url() )) ' but that errord with OpenURI::HTTPError: 403 Forbidden
If you navigated to that page via a browser and it succeeded, then later failed going to it directly via code, it's likely there is a cookie or session state that is missing. You might need to use something like Mechanize, which will maintain that state while allowing you to navigate through a site.
EDIT:
require 'addressable/uri'
url = 'http://www.example.com'
uri = Addressable::URI.parse(url)
uri.query_values = {
:foo => :bar,
:q => '"one two"'
}
uri.to_s # => "http://www.example.com?foo=bar&q=%22one%20two%22"

Rails: generate a full URL in an ActionMailer view

I'm using ActionMailer to send a sign up confirmation email. The email needs to contain a link back to the site to verify the user, but I can't persuade Rails to generate a full URL (including the domain etc).
I'm using:
<%= url_for :controller => 'login', :action => 'verify', :guid => #user.new_user.guid, :only_path => false, :host => 'http://plantality.com' %>
in my view
Part b:
In development mode Rails gripes if I don't specify the host explicilty in the link above. But I don't want to do this in production. Any solutions?
To solve the problem to pass a host for generating URLs in ActionMailer, check out this plugin and the reason why I wrote it.
To solve the first issue, use named routes when applicable. Instead of
<%= url_for :controller => 'login', :action => 'verify', :guid => #user.new_user.guid, :only_path => false, :host => 'http://plantality.com' %>
assuming the route is called login, use
<%= login_url(:guid => #user.new_user.guid) %>
Note, I'm using login_url, not login_path.
I'm not sure if it is what you want but in config/environments/development.rb you can specify default options for mailer urls
config.action_mailer.default_url_options = {
:host => "your.host.org",
:port => 3000
}
you can do the same in config/environments/production.rb
I don't know why the previous solutions seem so complicated, but since I'm here why not give my 2 cents...
Go to /config/environments and add:
config.absolute_site_url = 'your site url'
for the respective environment (ie. in development.rb, test.rb, or production.rb). Restart web server.
This allows you to call Rails.application.config.absolute_site_url to get the desired URL. No need for plugins or weird cheat, just store the site url as an application wide variable.
I think its not 100% correct way but this can also be a solution :
See the Using asset hosts section in the documentation. You need to specify an asset_host. You can also construct it dynamically from the request chaining "#{request.protocol}#{request.host_with_port}/login/?guid=#{#user.new_user.guid}"
To generate url, try this
Rails.application.routes.url_helpers.user_url(User.first.id, host: 'yourhost.io')
this will generate url like this:
http://yourhost.io/users/1
As well you can pass some params
expires = Time.now + 2.days
params = {expires: expires}
u = User.first.id
Rails.application.routes.url_helpers.user_url(u, params, host: 'host.com')
will generate:
http://yourhost.io/users/1.expires=2018-08-12+15%253A52%253A15+%252B0300
so you can werifi in action if link is not expired

Resources