On Sep 21, 2012, at 12:50 PM, Philipp Pirozhkov wrote:
Hi all,
I would like to describe my concerns on current exception handling below.
How it is handled currently:
def a
some_method_that_doesnt_exist
end
begin
cxt['a'] = method :a
cxt.eval('a')
rescue V8::JSError => e
if e.in_ruby?
puts "some ruby error'
else
puts "some JS error'
end
end
Doesn't look so good, right?
My second concern is that there's no sane way to understand what kind of exception we got from ruby:
require 'v8'
class Ex < StandardError
end
class A
def c
puts "raising"
raise Ex.new "i'm a specific ruby exception"
end
end
c = V8::Context.new
c['a'] = A.new
begin
c.eval('a.c')
rescue Ex => e
puts "rescued ex: #{e}"
rescue V8::JSError => e2
puts "rescued js: #{e2.inspect}"
puts e2.in_ruby?
rescue StandardError => e3
puts "rescued se: #{e3}"
end
Guess, which one is rescued?
V8::JSError, and `in_ruby?` is returning true.
If we are passing a wrapper for Net::HTTP to JS code, how do we distinguish between Net::HTTPBadResponse and NoMethodError?
In first case there's an error with networking, and it is recoverable in JS code (try another host, report, log et c.).
The second case is quite different and means there's an error in evaluated JS.
How do we distinguish these two?
Does the new error handling in 0.11.0 address these concerns?
Let's move to my third concern.
JS is known to be a safe sandbox. If JS is trying to do something malicious, we should be notified.
require 'v8'
class MaliciousScript < Exception
end
class A
def a path
raise MaliciousScript.new "hey, the script is trying to `rm -rf /`!" if path =~ /\//
end
end
c = V8::Context.new
c['a'] = A.new
begin
c.eval 'a.a("my")'
puts "ok"
c.eval <<-EOF
try {
a.a("/")
} catch(e){
// Hey let's hack those bastards!
}
EOF
puts "No exception!"
rescue MaliciousScript => e
puts "rescued maliciuos script: #{e}"
rescue V8::JSError => e2
puts "rescued js: #{e2.inspect}"
puts e2.in_ruby?
rescue Exception => e3
puts "rescued se: #{e3}"
end
Guess what?
\
This is definitely NOT good. Thanks for spotting this.
My proposals here abridged:
1. Split V8::JSError into two, JS specific and Ruby specific
2. Let Ruby catch ruby specific exceptions in a straightforward way
we could mix in modules to the V8::Error instance indicating whether the root cause was in ruby or Javascript
module V8::Error:JS
end
module V8::Error::Ruby
end
that way, you could distinguish (optionally) in your rescue
begin
rescue V8::Error::JavaScriptError => e
#root cause from JS
rescue V8::Error::RubyError => e
#root cause was from Ruby
rescue V8::Error
#don't care which language originated the exception, only that one did occur during JS evaluation
end
3. Restrict JS from catching exceptions it's not intended to catch
This is tichy, need to dig into V8 to see how to do this, but it is critical for sandboxing. This is easier in Rhino, I think.
Please let me know what you think.
PS Just for the case you may have questions regarding what i'm doing. I'm working on a platform which allows users to host and execute JS, and it's Ruby controlled/based. I provide an API to JS, and would like to do so in a controlled way.
I'd love to hear your thoughts on this. If you want to take a stab at time boxing scripts in V8 (like you can already do with Rhino) I'd be happy to help you with it.
PPS Should I crosspost/move this to javascript-and-friends? Not sure if it's common to therubyrhino or execjs.
Since therubyracer and therubyrhino share a common api, I think it is wise. I'll do so.
BR, Phil