- PVSM.RU - https://www.pvsm.ru -
Владельцам расширений (а также приложений) для Хрома уже пора бы задуматься над поддержкой второй версии манифеста.
Если кто не в курсе, то не так давно были объявлены новые изменения и нововведения в разработку расширений для браузера.
Далее будет выборочный перевод двух [1] страниц [2] и мой способ использования шаблонизатора изнутри песочницы.
* Далее старыми расширениями буду называть расширения и приложения с версией манифеста 1 (или вообще без версии).
`script-src 'self'; object-src 'self'. Это по сути самое важное обновление. О нем немного позже.chrome-extension://[PACKAGE ID]/[PATH]. Т.е. вы не сможете подключить скрипт или картинку с расширения с других страниц кроме самого расширения. Но чтобы обойти этот недостаток появилось свойство web_accessible_resources [5], в котором можно указать массив с путями к нужным ресурсам.background_page (которое было строкой), теперь пишем background [6], которое должно содержать объект со свойством scripts или page.browser_actions заменено на browser_action, а API chrome.browserActions — на chrome.browserAction.icons из browser_action. Вместо него нужно использовать default_icon или chrome.browserAction.setIcon.name из browser_action. Вместо него нужно использовать default_title или chrome.browserAction.setTitle.popup из browser_action. Вместо него нужно использовать default_popup или chrome.browserAction.setPopup.default_popup в browser_action должно быть строкой, а не объектом
page_actions заменено на page_action, а API chrome.pageActions — на chrome.pageAction.icons из page_action. Вместо него нужно использовать default_icon или chrome.pageAction.setIcon.name из page_action. Вместо него нужно использовать default_title или chrome.pageAction.setTitle.popup из page_action. Вместо него нужно использовать default_popup или chrome.pageAction.setPopup.default_popup в page_action должно быть строкой, а не объектом.chrome.self из API, теперь нужно использовать chrome.extension.
chrome.extension.getTabContentses и chrome.extension.getExtensionTabs. Вместо них нужно использовать chrome.extension.getViews({ "type": "tab" }) [7].
Port.tab используем Port.sender.Чтобы расширения были менее подвержены XSS уязвимостям были внедрены общие принципы CSP. В общем CSP являет собой механизм белых и черных списков касательно ресурсов, которые загружаются и выполняются расширением. С помощью CSP можно установить только необходимые разрешения для расширения и таким образом повысить его безопасность.
Эта политика является дополнительным уровнем защиты над правами на доступ к ресурсам (host permissions [8])
Установить политику безопасности можно в строковом параметре content_security_policy в manifest.json.
Если не установлена версия манифеста (manifest_version), то по умолчанию нет никакой политики безопасности контента. Но для второй версии манифеста она установлена по умолчанию со значением script-src 'self'; object-src 'self'. Поэтому есть некоторые ограничения. Например, функция eval выполнятся не будет. Так же не будут выполнятся инлайновые <script> блоки и инлайновые обработчики событий (<button onclick="...">). Если вы в коде по какой-то причине передавали строку в качестве первого аргумента функций setTimeout и setInterval, то это тоже придется исправить.
Так же скрипты, подключаемые с других ресурсов (с CDN, например), нужно сохранить локально.
К сожалению нет способа смягчить ограничения на выполнение инлайновых скриптов. Но есть возможность подключать сторонние скрипты (правда, только по https). Для этого нужно указать домен в content_security_policy, например так:
{
...,
"content_security_policy": "script-src 'self' https://example.com; object-src 'self'",
...
}
Сначала вы можете подумать — зачем мне eval? Но эта функция нужна почти для всех шаблонизаторов (заметьте, new Function() тоже не работает). Здесь нам поможет песочница (sandbox [10]).
Можно создать отдельную страницу, где будут выполнятся все небезопасные операции (например, eval) и запускать ее изнутри песочницы (на песочницу по умолчанию CSP не распространяется).
Так же есть возможность передавать данные между расширением и песочницей через метод postMessage().
sandboxed/template-renderer.html с таким вот контентом
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Sandboxed Template Renderer</title>
<script src="/js/libs/underscore/underscore-min.js"></script>
</head>
<body>
<script>
var templates = {};
window.addEventListener('message', function (event) {
var template;
if (typeof templates[event.data.templateName] == 'undefined') {
template = _.template(event.data.template);
templates[event.data.templateName] = template;
} else {
template = templates[event.data.templateName];
}
event.source.postMessage({
id: event.data.id,
result: template(event.data.context)
}, event.origin);
});
</script>
</html>
{
...,
"sandbox": {
"pages": ["sandboxed/template-renderer.html"]
},
...
}
var getTemplate = (function(){
var iframe = document.createElement('iframe'),
callbacks = [];
iframe.src = 'sandboxed/template-renderer.html';
iframe.style.display = 'none';
document.body.appendChild(iframe);
window.addEventListener('message', function (event) {
callbacks.forEach(function (item, idx) {
if (item && item.id == event.data.id) {
item.callback(event.data.result);
delete callbacks[idx];
}
});
});
return function (templateName, template) {
return function (context, callback) {
var id = Math.random();
callbacks.push({
id: id,
callback: callback
});
iframe.contentWindow.postMessage({
id: id,
templateName: templateName,
template: template,
context: context
}, '*');
};
};
}());
// получаем функцию, которая будет рендерить шаблон в зависимости от контекста
var template = getTemplate('templateId', templateContent);
// одно плохо - теперь результат получаем асинхронно
template({text: 'Hello world'}, function (html) {
// выводим html на страницу
// можно было передать $('body').html в качестве второго параметра,
// но решил написать так для большей наглядности
$('body').html(html);
});
Это решение — первое что пришло в голову. Наверное есть способ сделать это лучше (красивее). Буду рад увидеть предложения в комментариях.
И на последок, мой manifest.json:
{
"name": "Twittext",
"description": "A lightweight Google Chrome extension for Twitter",
"background": {
"page": "background.html"
},
"manifest_version": 2,
"browser_action": {
"default_icon": "img/icon_19.png",
"default_title": "Twittext",
"default_popup": "popup.html"
},
"icons": {
"128": "img/icon_128.png",
"19": "img/icon_19.png",
"48": "img/icon_48.png"
},
"options_page": "options.html",
"version": "1.6.1",
"permissions": [
"tabs",
"background",
"https://api.twitter.com/",
"https://userstream.twitter.com/"
],
"sandbox": {
"pages": ["sandboxed/template-renderer.html"]
}
}
Автор: utf
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/chrome/13494
Ссылки в тексте:
[1] двух: http://developer.chrome.com/extensions/manifestVersion.html
[2] страниц: http://developer.chrome.com/extensions/contentSecurityPolicy.html
[3] планах Гугла: http://developer.chrome.com/extensions/manifestVersion.html#schedule
[4] Веб-сторе: https://chrome.google.com/webstore
[5] web_accessible_resources: http://developer.chrome.com/extensions/manifest.html#web_accessible_resources
[6] background: http://developer.chrome.com/extensions/background_pages.html
[7] chrome.extension.getViews({ "type": "tab" }): http://developer.chrome.com/extensions/extension.html#method-getViews
[8] host permissions: http://developer.chrome.com/extensions/manifest.html#permissions
[9] Смягчение ограничений политики безопасности: http://developer.chrome.com/extensions/contentSecurityPolicy.html#H2-3
[10] sandbox: http://developer.chrome.com/extensions/manifest.html#sandbox
[11] underscore шаблонизатор: http://underscorejs.org/#template
Нажмите здесь для печати.