Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

Add JSONP helper #2

Closed
wants to merge 1 commit into from

2 participants

Serg Podtynnyi Konstantin Haase
Serg Podtynnyi

Extracted from my sinatra-jsonp extension

Konstantin Haase
Owner

Isn't jsonp obsolete and super insecure?

Serg Podtynnyi

It's only way to do crossdomain ineraction and jQuery like it.))
1,454 total downloads now on rubygems for sinatra-jsonp.

Konstantin Haase
Owner

A quick google for CORS jquery reveals plenty of plugins and AFAIK you only need those if you want to support IE.

Serg Podtynnyi

Sure CORS is the future, but it's not working on IE7,IE6 and Opera => it's for example 17% of our visitors on our company site.

Konstantin Haase
Owner

Yes, I'm not saying your plugin is of no use, to the contrary. I'm just wondering if we should include it in sinatra-contrib. And I have not rejected the pull request, I want this to be discussed. After all, it is a major security risk.

Serg Podtynnyi

Ok, maybe we can include it not in Common section. For example: rack-contrib have jsonp. Also I can add checks for js to prevent xss as it done in rack-contrib.

Konstantin Haase
Owner

Yes, we could do that. Any reason not to use Rack::JSONP, though? Esp. since it is handling some XSS attacks. All you would have to do is returning JSON.

Serg Podtynnyi Add Jsonp helper 910692c
Serg Podtynnyi

I just don't like the way they handle callback names. I moved helper to the custom section and updated commit.

Konstantin Haase
Owner

Have you considered submitting a pull request to rack-contrib?
What's your take, @ohhgabriel?

Serg Podtynnyi

Nope, they use automatic content-type based response and there is no way to set custom callback params and callback name, also I think bad_request idea is wrong way.

Konstantin Haase
Owner

Yeah, I meant submitting a patch for callback name/params.
bad_request is part of the XSS prevention. What would you do instead?

I've not used JSONP myself, @mtodd, if you have a minute, a quick review would be great.

Serg Podtynnyi

I would simply return JSON just like no callback passed. Also Sinatra checks URI and returns URI::InvalidURIError

Konstantin Haase
Owner

But this is not produced by an invalid URI. It's more that you could provide a callback like jquery = myobject; real_callback. The main reason I'm pushing towards the middleware approach is that it plays well with other extensions, like respond_with/respond_to.

Serg Podtynnyi

I rewrite tests to rspec and add XSS checks

 describe "should handle properly XSS vulnerability attempts" do
    context "with XSS vulnerability attempts" do
      def request(callback)
        get "/method?#{callback}"
        body.should == '["hello","hi","hallo"]'
      end
    end

   # Fail with URI::InvalidURIError:bad URI(is not URI?): foo<bar>baz()$
   it "should return bad request for callback with invalid characters" do
      request("foo<bar>baz()$")
    end

    # Fail with URI::InvalidURIError: bad URI(is not URI?): foo<script>alert(1)</script>
    it "should return bad request for callbacks with <script> tags" do
      request("foo<script>alert(1)</script>")
    end
   # Pass and return only json
    it "should return json for callbacks with multiple statements" do
      request("foo%3balert(1)//")
    end
  end

2 of 3 test will fail on sinatra without additiional checks.
Also I have no clue how to add helper method to sinatra via rack middleware approach and if I want to use some plugins from sinatra-contrib I have to use rack-contrib for jsonp?

Konstantin Haase
Owner

They fail because you don't escape them properly. Sinatra tries to unescape them and thereby raises the exceptions. This is not a security feature. You don't have to use rack-contrib, only if you want to use middleware offered by it. If you want to add a helper, you'll have to do it the other way around: Add the helper to Sinatra and let that helper use the middleware. But with middleware you usually don't need helpers at all.

Serg Podtynnyi

Ok, I got your point and you can close this pull request because of it's uselessness. I just realised that Rack::JSONP is OK for 99% of use cases and thanks for your time.))

Konstantin Haase rkh closed this
Konstantin Haase rkh referenced this pull request from a commit
Konstantin Haase rkh update for travis #2 f6be961
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Apr 26, 2011
  1. Add Jsonp helper

    Serg Podtynnyi authored
This page is out of date. Refresh to see the latest.
Showing with 110 additions and 0 deletions.
  1. +1 −0  lib/sinatra/contrib.rb
  2. +72 −0 lib/sinatra/jsonp.rb
  3. +37 −0 spec/jsonp_spec.rb
1  lib/sinatra/contrib.rb
View
@@ -23,6 +23,7 @@ module Common
module Custom
# register :Compass
register :Decompile
+ helpers :Jsonp
end
##
72 lib/sinatra/jsonp.rb
View
@@ -0,0 +1,72 @@
+require 'sinatra/base'
+require 'json'
+
+module Sinatra
+ ##
+ # JSONP output helper for Sinatra. Automatically detects callback params
+ # and returns proper JSONP output.
+ # If no callback params where detected it returns plain JSON.
+ # Works with jQuery jQuery.getJSON method out of the box.
+ module Jsonp
+
+ # Examples
+
+ # Classic:
+
+ # require "sinatra"
+ # require "sinatra/jsonp"
+
+ # get '/hello' do
+ # data = ["hello","hi","hallo"]
+ # JSONP data # JSONP is an alias for jsonp method
+ # end
+
+ # # define your own callback as second string param
+ # get '/hi' do
+ # data = ["hello","hi","hallo"]
+ # jsonp data, 'functionA'
+ # end
+
+ # # same with symbol param
+ # get '/hallo' do
+ # data = ["hello","hi","hallo"]
+ # jsonp data, :functionB
+ # end
+
+ # Modular:
+
+ # require "sinatra/base"
+ # require "sinatra/jsonp"
+
+ # class Foo < Sinatra::Base
+ # helpers Sinatra::Jsonp
+
+ # get '/' do
+ # data = ["hello","hi","hallo"]
+ # jsonp data
+ # end
+ # end
+ #
+ def jsonp(*args)
+ if args.size > 0
+ data = args[0].to_json
+ if args.size > 1
+ callback = args[1].to_s
+ else
+ ['callback','jscallback','jsonp','jsoncallback'].each do |x|
+ callback = params.delete(x) unless callback
+ end
+ end
+ if callback
+ content_type :js
+ response = "#{callback}(#{data})"
+ else
+ response = data
+ end
+ response
+ end
+ end
+ alias JSONP jsonp
+ end
+ helpers Jsonp
+end
37 spec/jsonp_spec.rb
View
@@ -0,0 +1,37 @@
+require "backports"
+require_relative 'spec_helper'
+
+describe Sinatra::Jsonp do
+ before do
+ mock_app do
+ helpers Sinatra::Jsonp
+
+ get '/method' do
+ data = ["hello","hi","hallo"]
+ jsonp data
+ end
+ get '/method_with_params' do
+ data = ["hello","hi","hallo"]
+ jsonp data, :functionA
+ end
+ end
+ end
+
+ it "should return JSON if no callback passed" do
+ get '/method'
+ body.should == '["hello","hi","hallo"]'
+ end
+ it "should return JSONP if callback passed via GET param" do
+ get '/method?callback=functionA'
+ body.should == 'functionA(["hello","hi","hallo"])'
+ end
+ it "should return JSONP if callback passed via method param" do
+ get '/method_with_params'
+ body.should == 'functionA(["hello","hi","hallo"])'
+ end
+ it "should return JSONP with callback passed via method params even if it passed via GET param" do
+ get '/method_with_params?callback=functionB'
+ body.should == 'functionA(["hello","hi","hallo"])'
+ end
+end
+
Something went wrong with that request. Please try again.