New filtering in MouseHole

Kevin Ballard kevin at sb.org
Sun Sep 4 05:11:54 EDT 2005


Ok, I've hacked on MouseHole a bit to make it possible for JavaScript  
to have access to MouseHole. I've done it in a fairly restrictive  
manner. Basically, I've made it possible for a script to handle any  
URL whose path starts with /.mouseHole/, matching based on domain and  
path. Because of naming issues, I decided to rename rewrite to filter  
and call this new feature rewrite (after all, the existing rewrite is  
really just filtering). But to preserve compatibility I've made  
rewrite with no args act like the new filter.

How you use the new feature is you simply add a call to rewrite(). It  
takes 2 args. The first is a regex to match against host:port, so you  
might use /^(www\.)?google\.com:80/ to match against google. The  
second is a regex to match against the path after /.mouseHole/, so to  
match http://www.google.com/.mouseHole/ping you would use %r{^/ping 
$}. On a request to the proxy, if the request method is GET/POST/PUT/ 
HEAD and the path starts with /.mouseHole/, it will match against all  
existing rewriting, and the first one it finds that works it calls.  
The rewrite block should set both req['Content-Type'] and req.body to  
appropriate values. req['Content-Length'] will be filled in for you  
based on req.body. The scripts are matched in whatever order  
each_fresh_script uses and the individual rewrites will be matched in  
order of definition. I would recommend that the path match be  
something fairly unique, so multiple generic scripts (i.e. ones not  
limited to a specific domain) don't try to use the same path. I  
thought about sticking a script identifier into the matched path, but  
I wasn't sure what to use.

The reason I restricted the rewriting to /.mouseHole/ is because it  
makes writing scripts a bit easier (so you don't end up trying to  
rewrite way too many pages when you meant to rewrite a specific page)  
and faster (because it doesn't have to match all scripts for every  
request). Also because this feature is meant primarily for JavaScript  
XMLHttpRequest calls, although you could use this to toss in a  
<script> or <img> tag or the like.

The patch can be found at <http://kevin.sb.org/files/ 
mousehole-1.patch>. It was created against CVS TOT right now (check  
the email time if you care).

An example script is the following. It adds a new button to google's  
index page that simply asks MouseHole for a random number. If you  
don't care, do keep reading. I talk more after the script.

---------------
MouseHole.script do
   # declaration
   name "Rewriting Test"
   namespace "kevin at sb.org"
   description "Tests the new rewriting stuff."
   include_match %r{^http://(www\.)?google\.com(/(index\.html)?)?$}
   version "0.1"

   filter do |req, res|
     document.elements['//head'].add_element 'script', 'type' =>  
'text/javascript', 'src' => '/.mouseHole/test.js'
     lucky = document.elements['//input[@name="btnI"]']
     lucky.parent.add_element 'input', 'type' => 'submit', 'value' =>  
'Random', 'onclick' => 'pingMouseHole(); return false;'
   end

   rewrite(%r{^(www\.)?google\.com:80$}, %r{^/test\.js$}) do |uri,  
req, res|
     res['Content-Type'] = 'text/javascript'
     res.body = <<-EOF
function createRequestObject() {
   var ro;
   var browser = navigator.appName;
   if(browser == "Microsoft Internet Explorer"){
     ro = new ActiveXObject("Microsoft.XMLHTTP");
   }else{
     ro = new XMLHttpRequest();
   }
   return ro;
}

var http = createRequestObject();

function sndReq(action, handler) {
   http.open('get', action);
   http.onreadystatechange = function() {
     if(http.readyState == 4){
       handler(http.responseText);
     }
   };
   http.send(null);
}

function pingMouseHole() {
   sndReq('/.mouseHole/ping', function(txt) {
     alert(txt);
   })
}
     EOF
   end

   rewrite(%r{^(www\.)google\.com:80$}, %r{^/ping$}) do |uri, req, res|
     res['Content-Type'] = 'text/plain'
     res.body = uri.to_s + ": " + rand(100).to_s
   end
end
-------------------

On Sep 5, 2005, at 2:31 AM, why the lucky stiff wrote:

> * Javascript access to MouseHole: A MouseHole script injects  
> Javascript into a page and that Javascript needs to store data back  
> inside MouseHole.  Uhh, this exposes MouseHole to possible attack  
> from other Javascripts.  What APIs do we allow Javascript to  
> access.  (You don't want a rogue Javascript deactivating all your  
> scripts or wiping out your configurations, right?)

Well, this provides the requested functionality. The only APIs  
accessible from javascript are the ones that your user scripts  
provide, so as long as they behave appropriately, there should be no  
problem. Feel free to add some $SAFE magick'ry if you wish around  
this, I don't have any experience twiddling $SAFE so I just ignored it.

> * Javascript cross-site access: A MouseHole script injects  
> Javascript into a page and that Javascript needs to pass data to  
> another domain (normally a JS failure.)
> ** So, if we're on boingboing and we want to access metafilter..  
> http://boingboing.net/http://metafilter.com/api/post.  That opens a  
> security hole (the same one Greasemonkey just bumped up on.)
> ** Instead, we could have the MouseHole script register URLs before  
> hand: register_url("http://metafilter.com/api/post")
> ** Then, the script is given a hash to access that domain.  (Or is  
> allowed access to it through http://boingboing.net/http:// 
> metafilter.com/api/post.)
> ** Or, just a whitelist with regexps.

With this new filtering, you just ask MouseHole to do the cross- 
domain request. Only vulnerabilities are again what the script itself  
provides.

Thoughts or comments?

Oh, and I don't suppose MouseHole could be hosted in an SVN repo  
instead of a CVS repo? I'm using SVK to wrap it (because I hate using  
CVS), but it's slow to sync changes in the repo.

-- 
Kevin Ballard
kevin at sb.org
http://www.tildesoft.com
http://kevin.sb.org

-------------- next part --------------
A non-text attachment was scrubbed...
Name: smime.p7s
Type: application/pkcs7-signature
Size: 2378 bytes
Desc: not available
Url : http://rubyforge.org/pipermail/mousehole-scripters/attachments/20050904/e8086156/smime.bin


More information about the Mousehole-scripters mailing list