Testing a specific github pull request

I often need to test specific github pull requests. All you need to know is that PR refs on github are of the form “refs/pull/pr/head” (pr is the pull request number).

For example, here’s how you do it (for hypothetical PR 1234):

git fetch origin refs/pull/1234/head
git show FETCH_HEAD # see the patch
git cherry-pick FETCH_HEAD # apply patch to current branch
twitter
Filed under git

How remote debugging works in Chrome

Chrome for Android allows pages to be debugged remotely from the desktop. The wire protocol for debugging is specified here. This post gives an overview on how the bridge is setup between desktop chrome and android chrome.

10,000 foot view
When the Android phone is in Developer mode and USB debugging is enabled, Chrome for Android allows itself to be debugged through Unix domain sockets. One can connect to this socket and obtain a list of open pages. Each page has a corresponding websocket debug URL which can be used to debug the page.

Finding the unix domain socket
/proc/net/unix contains the list of unix domain sockets. Chrome creates sockets with _devtools_remote somewhere in the name of the socket (see devtools_adb_bridge.cc).

You can view it by using the following command on the host:

adb shell cat /proc/shell/unix | grep _devtools_remote

The output should be something like this:

00000000: 00000002 00000000 00010000 0001 01 107909 @chrome_devtools_remote_8985
00000000: 00000002 00000000 00010000 0001 01 87977 @chrome_devtools_remote

There are two sockets listed because I have Chrome Beta and Chrome running at the same time. The ‘@’ in front of the socket name implies an abstract socket.

Setup adb forwarding
Next step is to connect to the abstract socket and find out the list of open pages. We setup forwarding through adb.

adb forward tcp:4000 localabstract:chrome_devtools_remote_8985

The port 4000 was chosen at random. The above line makes adb forward any connections to localhost TCP port 4000 to the abstract socket named chrome_devtools_remote_8985 over USB.

Getting list of pages
We can now connect to port 4000 to get the list of pages.

curl localhost:4000/json

You should see output like so:

[ {
   "description": "",
   "devtoolsFrontendUrl": "http://chrome-devtools-frontend.appspot.com/serve_rev/@165554/devtools.html?ws=localhost:4000/devtools/page/0",
   "id": "0",
   "title": "https://www.google.com/url?sa=t&source=web&rct=j&ei=aEPpUq3iIMrFoATNqYDwDQ&url=http://m.youtube.com/watch%3Fv%3DoHg5SJYRHA0&cd=3&ved=0CC8QtwIwAg&usg=AFQjCNE6GowB7mL72VHPMCbZco5bTpnXbA&sig2=GBopoUdf_D-kofy7N2c80w",
   "type": "page",
   "url": "https://www.google.com/url?sa=t&source=web&rct=j&ei=aEPpUq3iIMrFoATNqYDwDQ&url=http://m.youtube.com/watch%3Fv%3DoHg5SJYRHA0&cd=3&ved=0CC8QtwIwAg&usg=AFQjCNE6GowB7mL72VHPMCbZco5bTpnXbA&sig2=GBopoUdf_D-kofy7N2c80w",
   "webSocketDebuggerUrl": "ws://localhost:4000/devtools/page/0"
} ]

The webSocketDebuggerUrl above gives you the URL you can talk the remote debug protocol!

Connecting to the page

wscat allows you to talk to web sockets.

wscat -c ws://localhost:4000/devtools/page/0

You can now type away json commands. The remote debugging protocol is best understood with some source code. Which leads us to devtools ui.

Installing devtools
The DevTools UI that is part of chrome is just plain ol’ HTML/CSS/JS. You can just download it and ask it to connect to the websocket above!.

Download Devtools from here (The code is part of Blink). Extract and serve up the contents using a web server (I use serve).

In your browser, http://localhost:3000/front_end/inspector.html?ws=localhost:4000/devtools/page/0. Here, localhost:3000 is the port of your local webserver. localhost:4000 is our bridge to Chrome Android. That’s it!

 

twitter

Cordova plugin for UDP messages

I pushed a Cordova 3.x plugin today to send and receive UDP datagrams. It supports both unicast and multicast addressing. It supports only Android at the moment, I expect to iOS support at some point.

You can find it here. Do

cordova plugin add https://github.com/gramakri/cordova-plugin-datagram

in your cordova project to install the plugin.

twitter

JavaScript misfeature

Today a friend of mine checked in code that read:

    var stream = new fs.createReadStream(filename);

The ‘new’ in the code was not intended. Our expansive auto tests passed without a glitch. In fact, the ‘stream’ variable was correctly set to the stream – exactly like if the ‘new’ keyword was not present.

It turns out that in JavaScript constructor functions can return values. If a constructor function returns a value that is of Object type, the ‘new’ operator discards the object it created and returns that value instead. This misfeature is part of the spec in 13.2.2.

So, what happens above is that the ‘new’ operator creates a new object. It runs the fs.createReadStream() like a constructor on this new object. But since fs.createReadStream() returned a stream which is of type Object, it discards the new object it created and returns the stream.

twitter

Introducing Safety Dance (node module)

I don’t get exceptions. There’s this theory that they should be raised for ‘exceptional’ cases. But in reality lots of API uses it as a way to just pass back the error code. Joel has already said whatever I want to say about exceptions over a decade ago, so I won’t repeat his article here.

The best abuse of exceptions can be seen in APIs like JSON.parse, JSON.stringify and most node.js sync functions. It’s annoying because the cases are hardly exceptional. And to simply check if a parse succeeded, you have to surround your code with try/catch.

Safety Dance

To remedy the situation, I have a written a module called Safety Dance. The idea is simple – you can use the safeCall() function to wrap exception throwing functions. If safeCall() catches an exception, it will make the function return null or false (or whatever error value makes sense for that API call).

For example,

var safetydance = require('safetydance'), safeCall = safetydance.safeCall;
var result = safeCall(function () { return 1 + 2; }); // will return 3
result = safeCall(function () { throw new Error('bad'); }); // will return null
safetydance.error; // contains the error 'bad'

There are convenience wrappers for some API. For example,

try {
    var obj = JSON.parse(str);
} catch (e) {
}

becomes

var obj = safe.JSON.parse(str);
if (obj === null) {
    // safe.error contains the actual error
}

At this point, someone will point out that null is valid JSON and they would be correct. But I am yet to see any REST API that uses null as a valid API result. In any case, you can write safe.safeCall(function() { JSON.parse(str); }, undefined);. The second parameter is the value to return if safeCall() catches an exception.

twitter

My XSRF exploit of build.phonegap.com

PhoneGap Build is a nice service from Adobe. You upload your PhoneGap application and it builds Android, iOS packages on the cloud. PhoneGap Build also provides an API for it’s service. I used the API to create pgbuild to automate the upload of the PhoneGap application source and download the built packages.

The exploit I found has to do with the JSONP API (scroll to the end of the api page). Using the exploit, one can read a logged in user’s registered apps, initiate a rebuild of the apps etc.

The exploit itself is fairly trivial, if you understand how browsers work. If you just want to know the exploit scroll to the very end. The goal of this post is to provide a high level overview of how browser security and xsrf-style exploits work. Let’s start with Same Origin Policy.

Same Origin Policy

Browsers are very anal (for good reasons) about what resources a web page can access. A web page served from foo.com can only access resources from foo.com. This principle is called Same-Origin Policy. The policy is applied to JavaScript, HTML, file:// protocol and each has it’s exceptions. For HTML, the img tag, for example, can have url’s that point to an arbitrary website. The script tag and style tag can have the src attribute set to content from arbitrary websites.

Cookies

Whenever the browser makes a request, it sends across the cookies that have been set for that domain. This is true even for the cross-domain img and script tags. For example, let’s assume that you were logged into your bank. Without logging out, if you navigate to evil.com which has img src=”http://bank.com” somewhere inside it, your browser will make a request to bank.com with the cookies of bank.com set. The bank might provide personal information about you (since cookies have been provided) and dutifully serve the web page. It’s as if you had typed bank.com in the url bar of the browser (smart browsers will set the HTTP accept header when requesting an image and smart servers will check that header, but that is another story). Thankfully, the response from bank.com is most likely some HTML and the browser will fail to load it as an image. Your bank content is thus not introspectable by evil.com. The same goes for the script tag. Since HTML cannot be executed as a script, the browser just ignores the response.

Same-Origin policy is restrictive

One of the main reasons tags like img, script are excused from same-origin policy is for ease of development of the web. It’s nice to be able to link to external images and to reuse scripts from another domain that one owns. However, the same-origin policy is restrictive for sharing “data” between domains. Data is usually offered by web services as XML or JSON. Unfortunately, the popular way for web pages to fetch data – XHR (aka AJAX), also follows the same origin policy.

JSONP – Sharing data across domains

JSONP (JSON with padding) takes advantage of the fact that script tags can point to different domains. A web page author issues a cross-domain API call that returns JSON data as part of the script tag. For example,

<script src="service.com/api/get_posts_as_json"> </script>

The result of the above call is JSON which not valid JavaScript. The browser will ignore the above just like it ignored HTML in the previous bank example. We need to somehow make the output of above a valid JavaScript. With JSONP, one writes:

function processPosts(result) { ... }

<script src="service.com/api/get_posts_as_json?callback=processPosts"></script>

service.com sees the callback parameter and responds with processPosts(json_result) which is valid JavaScript. All we have to do is to provide an implementation of processPosts function somewhere in our web page.

The exploit

First thing to notice about the build.phonegap.com’s JSONP API is that it uses build.phonegap.com as it’s domain. This is interesting because the main website is also hosted on build.phonegap.com. This means that if a user is logged into the build.phonegap.com, a evil web page can issue JSONP API calls with the user’s credentials.

Usually, “APIs” are hosted in a domain separate from the website. For example, api.service.com for API access and www.service.com for the website. This means that api.service.com can safely have JSONP support since the cookies from www.service.com and api.service.com won’t mix.

If APIs and the websites are hosted in the same domain distinguished only by path, then one needs to be more careful. For example, http://build.phonegap.com/api provides API access and http://build.phonegap.com/apps provides the website. Cookies do have a ‘path’ attribute which can be used to tell the browser that different paths in same domain don’t mix. However, http://build.phonegap.com/apps set the cookies in the ‘/’ path. This meant that cookies will be provided for http://build.phonegap.com/api too.

Do you see the exploit now? All I had to do was to use the JSONP api to access user information. In case you were wondering, exploits where evil websites steal credentials of a user or impersonate the unwitting user are called Cross-Site Request Forgery attacks (CSRF or XSRF).

Sample exploit code

Once you are logged onto build.phonegap.com, navigate to the links below:
Example 1 – List Your Apps
Example 2 – Rebuild Your Apps (WARNING: Don’t Click unless you know what you are doing)

Possible fixes

Since I don’t have access to build.phonegap.com code, I can only guess a few possible fixes

  • Strip out cookies when processing JSONP and rely on the HTTP auth header or the API token.
  • Make the website set cookies with path as /apps.
  • Remove JSONP support and add support for CORS.

Move api access to a separate domain.

Current status

I informed Adobe before this blog post and they quickly fixed the problem (I haven’t checked how).

Update

Adobe has acknowledged me on their web site, thanks Adobe!

twitter

Qt Quick Best Practices

I noticed that the video and slides for my Qt Dev Days 2011 talk in Munich are now online. Here’s the video and here are the slides.

I couldn’t attend the event in San Francisco because I had to attend to some personal matters. Johannes and Donald covered for me there on very short notice (thanks guys!). For that matter, I have been mostly ‘offline’ for the last 2 months or so and will continue to be offline for atleast end of this month. So, if you sent me mail, I will get back at some point:)

twitter

Sharing wifi connection over ethernet

I have a wireless router at home which is physically far from the place I actually work. I required an internet connection to update my Arch machine. I could use wpa_supplicant to connect to internet using wifi but I thought I would explore the option of connecting my laptop back to back with another that already had a wifi connection.

Let’s call the computer with wifi connected to the router as I (as in connected to Internet). Let’s call the other computer C.

1. Connect I and C using normal ethernet cable. You don’t really need a cross-over cable since most ethernet cards these days are smart enough to do automatic cross-over.

2. Select a IP range for the ethernet connection between I and C. I chose 192.168.10.x.

3. On computer I, set the ip : sudo ip addr add 192.168.10.1/24 dev eth0

4. On computer I, Enable ip forwarding and setup iptables to masquerate (nat) the wifi connection.

sysctl -w net.ipv4.ip_forward=1
iptables -t nat -A POSTROUTING -o wlan0 -j MASQUERADE

5. On Computer I, install and configure dnsmasq. dnsmasq is a dhcp and dns server. The only configuration I needed after installing dnsmasq was adding a line “dhcp-range=192.168.10.100,192.168.10.150,12h” in /etc/dnsmasq.conf. That line specifes the ip range for dhcp leased addresses and the validity time. dns support is enabled by default, so nothing to configure there.

6. Run dnsmasq. Just “sudo dnsmasq”. I actually used “dnsmasq –no-daemon” which lets me the see the debug output on the console.

That’s it. Computer C should not get an IP address and be able to access the internet. You can use ‘dhclient eth0′ or ‘dhcpcd eth0′ to get an IP address through DHCP.

twitter

Qt DevDays 2011

I am just about to catch my flight to Munich to attend Qt Developer Days 2011 where I will be giving a talk on Qt Quick Best Practices. I have been working with QML exclusively for over a year now and this talk is in essence a summary of all the things I have learnt about it. This will also be my 6th developer days as attendee and third time as a speaker.

Am really looking forward to interesting conversations about Qt5, Qt Quick and the Qt project 🙂

twitter