CustomEvent.detail“受污染”? [英] CustomEvent.detail "tainted"?

查看:166
本文介绍了CustomEvent.detail“受污染”?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在开发Chrome扩展程序,以便为网站添加便利。

我可以访问该页面的DOM,但我也需要与该页面上的第一方JS进行交互,我可以在我的扩展中做到这一点。



我可以在页面中注入任意标签(最显着的也是< script> tags),但是由于转义字符串像

  {
html:'< div onclick =doSomething这是\'someName\')>< / div>'
}





我尝试在页面中注入事件监听器以获取JS变量来自页面,但遇到了问题。

似乎如果一个 CustomEvent 从扩展传递到网站或返回,并且if CustomEvent.detail 包含特定类型的对象(至少是函数和错误),整个 CustomEvent.d etail 将被清除,即设置为空。



示例



脚本(extension.js):

 (function()
{
var script = document.createElement('script');
script.innerHTML = [
window.addEventListener('xyz',function(ev),
{,
console.log('dispatch:');,
console.log ($'$');
document.head.appendChild(script);
/ / JSON序列化数据
var e = new CustomEvent('xyz',{detail:{x:42,name:'Schroedinger'}});
console.log('dispatch:'之前)
console.log(e.detail);
window.dispatchEvent(e);
//非JSON序列化数据
var detail = {x:42,name: 'Schroedinger'};
detail.detail = detail; //创建循环引用
e = new CustomEvent('xyz',{detail:详细信息});
console.log('dispatch:'之前)
console.log(e.detail);
window.dispatchEvent(e);
//函数
e = new CustomEvent('xyz',{detail:{x:42,name:'Schroedinger',func:function(){}}});
console.log('dispatch:'之前);
console.log(e.detail);
window.dispatchEvent(e);
//带错误对象的数据
e = new CustomEvent('xyz',{detail:{x:42,name:'Schroedinger',err:new Error()}});
console.log('dispatch:'之前);
console.log(e.detail);
window.dispatchEvent(e);
})();

输出(为了便于阅读,分数):

 在发货前:
Object {x:42,name:Schroedinger}
发货后:
Object {x :42,名称:Schroedinger}

在发货前:
Object {x:42,name:Schroedinger,detail:Object}
发货后:
对象{x:42,名称:Schroedinger,detail:Object}

在发送之前:
Object {x:42,name:Schroedinger,func:function(){ }}
在发送之后:
null

在发送之前:
Object {x:42,name:Schroedinger,err:铬扩展中的错误:/ / ...}
发货后:
null

我最初认为JSON -serializability是个问题,但循环引用在事件中传递的很好,当它们在JSON序列化时会中断。

感觉像某些对象以同样的方式事件细节异常,并终止整个结构化克隆算法。




解决方法



我最终使用了一个相当简单的解决方案(尽管不是很漂亮):

在Chrome中,没有相当于 mozIJSSubScriptLoader ,但是您可以从您的内部追加< script> 标签到页面扩展名(你不允许在FF中这样做)。
chrome.extension.getURL 一起,可以用来运行JS文件打包在页面的上下文中:

 (function()
{
var script = document.createElement('script');
script.src = chrome.extension.getURL('extension.js');
document.head.appendChild(script);
})();

当然,这要求

 web_accessible_resources:[extension.js] 

已设置在 manifest.json 中,这并不美观,但不应该是一个实际的问题。

缺点当然,从 extension.js 中,您不再可以访问您的扩展可以访问的任何chrome API,但在我的情况下,我并不需要那。因此,通过 CustomEvent 设置代理不会太困难,因为Chrome API的最大部分只需要并返回可克隆。


I'm developing a Chrome extension to add convenience to a website.
I have access to the page's DOM, but I also need to interact with the "first-party" JS on that page, which I cannot do from my extension.

I can inject arbitrary tags into the page (most notably also <script> tags), but since escaping strings like

{
    html: '<div onclick="doSomething(this, \'someName\')"></div>'
}

is a real pain, I'd like to keep the injected code at an absolute minimum.

I tried injecting event listeners into the page in order to fetch JS variables from the page, but ran into a problem.
It seems that if a CustomEvent is passed from an extension to a website or back, and if CustomEvent.detail contains certain types of objects (at least functions and errors) somewhere, the entire CustomEvent.detail will be purged, i.e. set to null.

Example

Script (extension.js):

(function()
{
    var script = document.createElement('script');
    script.innerHTML = [
"window.addEventListener('xyz', function(ev)",
"    {                                      ",
"        console.log('after dispatch:');    ",
"        console.log(ev.detail);            ",
"    });                                    ",
    ].join('\n');
    document.head.appendChild(script);
    // JSON-serializable data
    var e = new CustomEvent('xyz', { detail: { x: 42, name: 'Schroedinger' } });
    console.log('before dispatch:')
    console.log(e.detail);
    window.dispatchEvent(e);
    // non-JSON-serializable data
    var detail = { x: 42, name: 'Schroedinger' };
    detail.detail = detail; // Create circular reference
    e = new CustomEvent('xyz', { detail: detail });
    console.log('before dispatch:')
    console.log(e.detail);
    window.dispatchEvent(e);
    // data with function
    e = new CustomEvent('xyz', { detail: { x: 42, name: 'Schroedinger', func: function(){} } });
    console.log('before dispatch:');
    console.log(e.detail);
    window.dispatchEvent(e);
    // data with error object
    e = new CustomEvent('xyz', { detail: { x: 42, name: 'Schroedinger', err: new Error() } });
    console.log('before dispatch:');
    console.log(e.detail);
    window.dispatchEvent(e);
})();

Output (paragraphed for readability):

before dispatch:
Object {x: 42, name: "Schroedinger"}
after dispatch:
Object {x: 42, name: "Schroedinger"}

before dispatch:
Object {x: 42, name: "Schroedinger", detail: Object}
after dispatch:
Object {x: 42, name: "Schroedinger", detail: Object}

before dispatch:
Object {x: 42, name: "Schroedinger", func: function (){}}
after dispatch:
null

before dispatch:
Object {x: 42, name: "Schroedinger", err: Error at chrome-extension://...}
after dispatch:
null

I initially thought JSON-serializability was the issue, but circular references pass just fine in events, when they would break if JSON-serialized.
It feels like certain objects "taint" the event detail the same way non-crossorigin images taint canvases, except there's nothing in the console.

I was unable to find any documentation regarding this behaviour, and (as Paul S. suggested), there does not seem to be a "privilege" for that on the Chrome permissions list.

Tested in Chrome 40.0.2214.115m, 43.0.2357.124m and 48.0.2547.0-dev.

解决方案

What I found out

I initially thought this was a security feature, mostly because Firefox behaves that way.

In ran an equivalent test in Firefox by putting the event listener in a separate file that could be loaded via mozIJSSubScriptLoader:

test.js:

(function()
{
    window.addEventListener('xyz', function(ev)
    {
        console.log('after dispatch:');
        console.log(ev.detail);
    });
})();

firefox.js:

(function()
{
    var mozIJSSubScriptLoader = Components.classes["@mozilla.org/moz/jssubscript-loader;1"].getService(Components.interfaces.mozIJSSubScriptLoader);
    window.addEventListener('load', function load(event)
    {
        window.removeEventListener('load', load);
        window.gBrowser.addEventListener('DOMContentLoaded', function(event)
        {
            mozIJSSubScriptLoader.loadSubScript('chrome://my-extension/content/test.js', window.content, 'UTF-8');
            // JSON-serializable data
            var e = new CustomEvent('xyz', { detail: { x: 42, name: 'Schroedinger' } });
            console.log('before dispatch:')
            console.log(e.detail);
            window.content.dispatchEvent(e);
            // non-JSON-serializable data
            e = new CustomEvent('xyz', { detail: { x: 42, name: 'Schroedinger', func: function(){} } });
            console.log('before dispatch:');
            console.log(e.detail);
            window.content.dispatchEvent(e);
        });
    });
})();

Result:

(Note that the error occurs twice.)

So in Firefox it doesn't even matter what detail contains - as long as it comes from an extension, the page is not allowed to access it.
Looks like a security feature to me.

The reason I put the above in a quote is because this is somewhat different in Chrome!

After some deeper investigation it looks like although the extension and the page share the DOM tree, they exist in two different contexts.
I don't know whether this is actually a security feature or just a technical consequence, but this, of course, has the consequence that only clonable objects can be passed back and forth.

What puzzles me though is the fact that the operation silently fails, when, according to the HTML standard, §2.7.5 (structured clone), the entire operation should fail with an error:

↪ If input is another native object type (e.g. Error, Function)
↪ If input is a host object (e.g. a DOM node)
            Throw a DataCloneError exception and abort the overall structured clone algorithm.

Workaround

I ended up using a fairly easy (although not so pretty) workaround:
In Chrome, there's no equivalent to mozIJSSubScriptLoader, but you're allowed to append <script> tags to a page from within your extension (you're not allowed to do that in FF).
Together with chrome.extension.getURL, that can be used to run a JS file packaged with the extension in the context of the page:

(function()
{
    var script = document.createElement('script');
    script.src = chrome.extension.getURL('extension.js');
    document.head.appendChild(script);
})();

Of course that requires that

"web_accessible_resources": [ "extension.js" ]

is set in manifest.json, which isn't pretty, but shouldn't be an actual problem.

The drawback of this is, of course, that from within extension.js you no longer have access to any chrome API your extension has access to, but in my case I didn't need that. It wouldn't be too difficult to set up a proxy via CustomEvent for that though, as the biggest part of the Chrome API only requires and returns data that is clonable.

这篇关于CustomEvent.detail“受污染”?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆