Bug 911864 - Make in-content XBL event handlers allowuntrusted=false by default. r=smaug

This commit is contained in:
Bobby Holley 2013-11-01 15:31:58 +01:00
parent c8fd7ea7d1
commit c3012b9053
10 changed files with 105 additions and 10 deletions

View File

@ -154,8 +154,9 @@ nsXBLService::getClass(nsCStringKey *k)
// Constructors/Destructors
nsXBLBinding::nsXBLBinding(nsXBLPrototypeBinding* aBinding)
: mMarkedForDeath(false),
mPrototypeBinding(aBinding)
: mMarkedForDeath(false)
, mUsingXBLScope(false)
, mPrototypeBinding(aBinding)
{
NS_ASSERTION(mPrototypeBinding, "Must have a prototype binding!");
// Grab a ref to the document info so the prototype binding won't die
@ -297,6 +298,20 @@ nsXBLBinding::SetBoundElement(nsIContent* aElement)
mBoundElement = aElement;
if (mNextBinding)
mNextBinding->SetBoundElement(aElement);
if (!mBoundElement) {
return;
}
// Compute whether we're using an XBL scope.
//
// We disable XBL scopes for remote XUL, where we care about compat more
// than security. So we need to know whether we're using an XBL scope so that
// we can decide what to do about untrusted events when "allowuntrusted"
// is not given in the handler declaration.
nsCOMPtr<nsIGlobalObject> go = mBoundElement->OwnerDoc()->GetScopeObject();
NS_ENSURE_TRUE_VOID(go && go->GetGlobalJSObject());
mUsingXBLScope = xpc::UseXBLScope(js::GetObjectCompartment(go->GetGlobalJSObject()));
}
bool
@ -530,7 +545,7 @@ nsXBLBinding::InstallEventHandlers()
bool hasAllowUntrustedAttr = curr->HasAllowUntrustedAttr();
if ((hasAllowUntrustedAttr && curr->AllowUntrustedEvents()) ||
(!hasAllowUntrustedAttr && !isChromeDoc)) {
(!hasAllowUntrustedAttr && !isChromeDoc && !mUsingXBLScope)) {
flags.mAllowUntrustedEvents = true;
}
@ -546,6 +561,7 @@ nsXBLBinding::InstallEventHandlers()
for (i = 0; i < keyHandlers->Count(); ++i) {
nsXBLKeyEventHandler* handler = keyHandlers->ObjectAt(i);
handler->SetIsBoundToChrome(isChromeDoc);
handler->SetUsingXBLScope(mUsingXBLScope);
nsAutoString type;
handler->GetEventName(type);

View File

@ -162,6 +162,7 @@ public:
protected:
bool mMarkedForDeath;
bool mUsingXBLScope;
nsXBLPrototypeBinding* mPrototypeBinding; // Weak, but we're holding a ref to the docinfo
nsCOMPtr<nsIContent> mContent; // Strong. Our anonymous content stays around with us.

View File

@ -70,7 +70,8 @@ nsXBLKeyEventHandler::nsXBLKeyEventHandler(nsIAtom* aEventType, uint8_t aPhase,
: mEventType(aEventType),
mPhase(aPhase),
mType(aType),
mIsBoundToChrome(false)
mIsBoundToChrome(false),
mUsingXBLScope(false)
{
}
@ -96,7 +97,7 @@ nsXBLKeyEventHandler::ExecuteMatchedHandlers(nsIDOMKeyEvent* aKeyEvent,
bool hasAllowUntrustedAttr = handler->HasAllowUntrustedAttr();
if ((trustedEvent ||
(hasAllowUntrustedAttr && handler->AllowUntrustedEvents()) ||
(!hasAllowUntrustedAttr && !mIsBoundToChrome)) &&
(!hasAllowUntrustedAttr && !mIsBoundToChrome && !mUsingXBLScope)) &&
handler->KeyEventMatched(aKeyEvent, aCharCode, aIgnoreShiftKey)) {
handler->ExecuteHandler(target, aKeyEvent);
executed = true;

View File

@ -85,6 +85,12 @@ public:
{
mIsBoundToChrome = aIsBoundToChrome;
}
void SetUsingXBLScope(bool aUsingXBLScope)
{
mUsingXBLScope = aUsingXBLScope;
}
private:
nsXBLKeyEventHandler();
bool ExecuteMatchedHandlers(nsIDOMKeyEvent* aEvent, uint32_t aCharCode,
@ -95,6 +101,7 @@ private:
uint8_t mPhase;
uint8_t mType;
bool mIsBoundToChrome;
bool mUsingXBLScope;
};
nsresult

View File

@ -103,7 +103,10 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=821850
</property>
</implementation>
<handlers>
<handler event="testevent" action="ok(true, 'called event handler'); finish();"/>
<handler event="testevent" action="ok(true, 'called event handler'); finish();" allowuntrusted="true"/>
<handler event="testtrusted" action="ok(true, 'called trusted handler'); window.wrappedJSObject.triggeredTrustedHandler = true;"/>
<handler event="keyup" action="ok(true, 'called untrusted key handler'); window.wrappedJSObject.triggeredUntrustedKeyHandler = true;" allowuntrusted="true"/>
<handler event="keydown" action="ok(true, 'called trusted key handler'); window.wrappedJSObject.triggeredTrustedKeyHandler = true;"/>
</handlers>
</binding>
</bindings>
@ -206,6 +209,58 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=821850
Object.defineProperty(bound, 'prop', {value: "redefined"});
bound.primitiveField = 321;
// We need a chrome window to create trusted events. This isn't really doable
// in child processes, so let's just skip if that's the case.
if (SpecialPowers.isMainProcess()) {
var Ci = SpecialPowers.Ci;
var chromeWin = SpecialPowers.wrap(window.top)
.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIWebNavigation)
.QueryInterface(Ci.nsIDocShell)
.chromeEventHandler.ownerDocument.defaultView;
// Untrusted events should not trigger event handlers without
// exposeToUntrustedContent=true.
window.triggeredTrustedHandler = false;
var untrustedEvent = new CustomEvent('testtrusted');
ok(!untrustedEvent.isTrusted, "Created an untrusted event");
bound.dispatchEvent(untrustedEvent);
ok(!window.triggeredTrustedHandler, "untrusted events should not trigger trusted handler");
var trustedEvent = new chromeWin.CustomEvent('testtrusted');
ok(trustedEvent.isTrusted, "Created a trusted event");
SpecialPowers.wrap(bound).dispatchEvent(trustedEvent);
ok(window.triggeredTrustedHandler, "trusted events should trigger trusted handler");
//
// We check key events as well, since they're implemented differently.
//
// NB: We don't check isTrusted on the events we create here, because
// according to smaug, old-style event initialization doesn't mark the
// event as trusted until it's dispatched.
//
window.triggeredUntrustedKeyHandler = false;
window.triggeredTrustedKeyHandler = false;
// Untrusted event, permissive handler.
var untrustedKeyEvent = document.createEvent('KeyboardEvent');
untrustedKeyEvent.initEvent('keyup', true, true);
bound.dispatchEvent(untrustedKeyEvent);
ok(window.triggeredUntrustedKeyHandler, "untrusted key events should trigger untrusted handler");
// Untrusted event, strict handler.
var fakeTrustedKeyEvent = document.createEvent('KeyboardEvent');
fakeTrustedKeyEvent.initEvent('keydown', true, true);
bound.dispatchEvent(fakeTrustedKeyEvent);
ok(!window.triggeredTrustedKeyHandler, "untrusted key events should not trigger trusted handler");
// Trusted event, strict handler.
var trustedKeyEvent = chromeWin.document.createEvent('KeyboardEvent');
trustedKeyEvent.initEvent('keydown', true, true);
SpecialPowers.wrap(bound).dispatchEvent(trustedKeyEvent);
ok(window.triggeredTrustedKeyHandler, "trusted key events should trigger trusted handler");
}
// Hand control back to the XBL scope by dispatching an event on the bound element.
bound.dispatchEvent(new CustomEvent('testevent'));
}

View File

@ -9,7 +9,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=403162
<bindings xmlns="http://www.mozilla.org/xbl">
<binding id="test">
<handlers>
<handler event="foo" action="XPCNativeWrapper.unwrap(window).triggerCount++"/>
<handler event="foo" action="XPCNativeWrapper.unwrap(window).triggerCount++" allowuntrusted="true"/>
</handlers>
</binding>
</bindings>

View File

@ -9,9 +9,9 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=403162
<bindings xmlns="http://www.mozilla.org/xbl">
<binding id="test">
<handlers>
<handler event="DOMMouseScroll" action="XPCNativeWrapper.unwrap(window).triggerCount++"/>
<handler event="DOMMouseScroll" modifiers="shift" action="XPCNativeWrapper.unwrap(window).shiftCount++"/>
<handler event="DOMMouseScroll" modifiers="control" action="XPCNativeWrapper.unwrap(window).controlCount++"/>
<handler event="DOMMouseScroll" action="XPCNativeWrapper.unwrap(window).triggerCount++" allowuntrusted="true"/>
<handler event="DOMMouseScroll" modifiers="shift" action="XPCNativeWrapper.unwrap(window).shiftCount++" allowuntrusted="true"/>
<handler event="DOMMouseScroll" modifiers="control" action="XPCNativeWrapper.unwrap(window).controlCount++" allowuntrusted="true"/>
</handlers>
</binding>
</bindings>

View File

@ -298,6 +298,13 @@ bool AllowXBLScope(JSCompartment *c)
XPCWrappedNativeScope *scope = EnsureCompartmentPrivate(c)->scope;
return scope && scope->AllowXBLScope();
}
bool UseXBLScope(JSCompartment *c)
{
XPCWrappedNativeScope *scope = EnsureCompartmentPrivate(c)->scope;
return scope && scope->UseXBLScope();
}
} /* namespace xpc */
XPCWrappedNativeScope::~XPCWrappedNativeScope()

View File

@ -1369,6 +1369,7 @@ public:
bool IsXBLScope() { return mIsXBLScope; }
bool AllowXBLScope();
bool UseXBLScope() { return mUseXBLScope; }
protected:
virtual ~XPCWrappedNativeScope();

View File

@ -54,6 +54,13 @@ GetXBLScope(JSContext *cx, JSObject *contentScope);
bool
AllowXBLScope(JSCompartment *c);
// Returns whether we will use an XBL scope for this compartment. This is
// semantically equivalent to comparing global != GetXBLScope(global), but it
// does not have the side-effect of eagerly creating the XBL scope if it does
// not already exist.
bool
UseXBLScope(JSCompartment *c);
bool
IsSandboxPrototypeProxy(JSObject *obj);