зеркало из https://github.com/electron/electron.git
fix: `chrome.tabs` 'url' and 'title' are privileged information (#39595)
fix: tabs url and title are privileged information
This commit is contained in:
Родитель
bfa58df7c9
Коммит
e1d63794e5
|
@ -301,6 +301,7 @@ ExtensionFunction::ResponseAction TabsQueryFunction::Run() {
|
||||||
|
|
||||||
tabs::Tab tab;
|
tabs::Tab tab;
|
||||||
tab.id = contents->ID();
|
tab.id = contents->ID();
|
||||||
|
tab.title = base::UTF16ToUTF8(wc->GetTitle());
|
||||||
tab.url = wc->GetLastCommittedURL().spec();
|
tab.url = wc->GetLastCommittedURL().spec();
|
||||||
tab.active = contents->IsFocused();
|
tab.active = contents->IsFocused();
|
||||||
tab.audible = contents->IsCurrentlyAudible();
|
tab.audible = contents->IsCurrentlyAudible();
|
||||||
|
@ -322,12 +323,18 @@ ExtensionFunction::ResponseAction TabsGetFunction::Run() {
|
||||||
return RespondNow(Error("No such tab"));
|
return RespondNow(Error("No such tab"));
|
||||||
|
|
||||||
tabs::Tab tab;
|
tabs::Tab tab;
|
||||||
|
|
||||||
tab.id = tab_id;
|
tab.id = tab_id;
|
||||||
// TODO(nornagon): in Chrome, the tab URL is only available to extensions
|
|
||||||
// that have the "tabs" (or "activeTab") permission. We should do the same
|
// "title" and "url" properties are considered privileged data and can
|
||||||
// permission check here.
|
// only be checked if the extension has the "tabs" permission or it has
|
||||||
tab.url = contents->web_contents()->GetLastCommittedURL().spec();
|
// access to the WebContents's origin.
|
||||||
|
auto* wc = contents->web_contents();
|
||||||
|
if (extension()->permissions_data()->HasAPIPermissionForTab(
|
||||||
|
contents->ID(), mojom::APIPermissionID::kTab) ||
|
||||||
|
extension()->permissions_data()->HasHostPermission(wc->GetURL())) {
|
||||||
|
tab.url = wc->GetLastCommittedURL().spec();
|
||||||
|
tab.title = base::UTF16ToUTF8(wc->GetTitle());
|
||||||
|
}
|
||||||
|
|
||||||
tab.active = contents->IsFocused();
|
tab.active = contents->IsFocused();
|
||||||
|
|
||||||
|
@ -609,10 +616,16 @@ ExtensionFunction::ResponseValue TabsUpdateFunction::GetResult() {
|
||||||
auto* api_web_contents = electron::api::WebContents::From(web_contents_);
|
auto* api_web_contents = electron::api::WebContents::From(web_contents_);
|
||||||
tab.id = (api_web_contents ? api_web_contents->ID() : -1);
|
tab.id = (api_web_contents ? api_web_contents->ID() : -1);
|
||||||
|
|
||||||
// TODO(nornagon): in Chrome, the tab URL is only available to extensions
|
// "title" and "url" properties are considered privileged data and can
|
||||||
// that have the "tabs" (or "activeTab") permission. We should do the same
|
// only be checked if the extension has the "tabs" permission or it has
|
||||||
// permission check here.
|
// access to the WebContents's origin.
|
||||||
|
if (extension()->permissions_data()->HasAPIPermissionForTab(
|
||||||
|
api_web_contents->ID(), mojom::APIPermissionID::kTab) ||
|
||||||
|
extension()->permissions_data()->HasHostPermission(
|
||||||
|
web_contents_->GetURL())) {
|
||||||
tab.url = web_contents_->GetLastCommittedURL().spec();
|
tab.url = web_contents_->GetLastCommittedURL().spec();
|
||||||
|
tab.title = base::UTF16ToUTF8(web_contents_->GetTitle());
|
||||||
|
}
|
||||||
|
|
||||||
if (api_web_contents)
|
if (api_web_contents)
|
||||||
tab.active = api_web_contents->IsFocused();
|
tab.active = api_web_contents->IsFocused();
|
||||||
|
|
|
@ -20,5 +20,11 @@
|
||||||
"extension_types": [
|
"extension_types": [
|
||||||
"extension"
|
"extension"
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
"tabs": {
|
||||||
|
"channel": "stable",
|
||||||
|
"extension_types": [
|
||||||
|
"extension"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -39,6 +39,8 @@ constexpr APIPermissionInfo::InitInfo permissions_to_register[] = {
|
||||||
{mojom::APIPermissionID::kPdfViewerPrivate, "pdfViewerPrivate"},
|
{mojom::APIPermissionID::kPdfViewerPrivate, "pdfViewerPrivate"},
|
||||||
#endif
|
#endif
|
||||||
{mojom::APIPermissionID::kManagement, "management"},
|
{mojom::APIPermissionID::kManagement, "management"},
|
||||||
|
{mojom::APIPermissionID::kTab, "tabs",
|
||||||
|
APIPermissionInfo::kFlagRequiresManagementUIWarning},
|
||||||
};
|
};
|
||||||
base::span<const APIPermissionInfo::InitInfo> GetPermissionInfos() {
|
base::span<const APIPermissionInfo::InitInfo> GetPermissionInfos() {
|
||||||
return base::make_span(permissions_to_register);
|
return base::make_span(permissions_to_register);
|
||||||
|
|
|
@ -842,15 +842,14 @@ describe('chrome extensions', () => {
|
||||||
|
|
||||||
before(async () => {
|
before(async () => {
|
||||||
customSession = session.fromPartition(`persist:${uuid.v4()}`);
|
customSession = session.fromPartition(`persist:${uuid.v4()}`);
|
||||||
await customSession.loadExtension(path.join(fixtures, 'extensions', 'tabs-api-async'));
|
await customSession.loadExtension(path.join(fixtures, 'extensions', 'chrome-tabs', 'api-async'));
|
||||||
});
|
});
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
w = new BrowserWindow({
|
w = new BrowserWindow({
|
||||||
show: false,
|
show: false,
|
||||||
webPreferences: {
|
webPreferences: {
|
||||||
session: customSession,
|
session: customSession
|
||||||
nodeIntegration: true
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -913,7 +912,8 @@ describe('chrome extensions', () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('get', async () => {
|
describe('get', () => {
|
||||||
|
it('returns tab properties', async () => {
|
||||||
await w.loadURL(url);
|
await w.loadURL(url);
|
||||||
|
|
||||||
const message = { method: 'get' };
|
const message = { method: 'get' };
|
||||||
|
@ -922,6 +922,8 @@ describe('chrome extensions', () => {
|
||||||
const [,, responseString] = await once(w.webContents, 'console-message');
|
const [,, responseString] = await once(w.webContents, 'console-message');
|
||||||
|
|
||||||
const response = JSON.parse(responseString);
|
const response = JSON.parse(responseString);
|
||||||
|
expect(response).to.have.property('url').that.is.a('string');
|
||||||
|
expect(response).to.have.property('title').that.is.a('string');
|
||||||
expect(response).to.have.property('active').that.is.a('boolean');
|
expect(response).to.have.property('active').that.is.a('boolean');
|
||||||
expect(response).to.have.property('autoDiscardable').that.is.a('boolean');
|
expect(response).to.have.property('autoDiscardable').that.is.a('boolean');
|
||||||
expect(response).to.have.property('discarded').that.is.a('boolean');
|
expect(response).to.have.property('discarded').that.is.a('boolean');
|
||||||
|
@ -932,10 +934,35 @@ describe('chrome extensions', () => {
|
||||||
expect(response).to.have.property('index').that.is.a('number');
|
expect(response).to.have.property('index').that.is.a('number');
|
||||||
expect(response).to.have.property('pinned').that.is.a('boolean');
|
expect(response).to.have.property('pinned').that.is.a('boolean');
|
||||||
expect(response).to.have.property('selected').that.is.a('boolean');
|
expect(response).to.have.property('selected').that.is.a('boolean');
|
||||||
expect(response).to.have.property('url').that.is.a('string');
|
|
||||||
expect(response).to.have.property('windowId').that.is.a('number');
|
expect(response).to.have.property('windowId').that.is.a('number');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('does not return privileged properties without tabs permission', async () => {
|
||||||
|
const noPrivilegeSes = session.fromPartition(`persist:${uuid.v4()}`);
|
||||||
|
await noPrivilegeSes.loadExtension(path.join(fixtures, 'extensions', 'chrome-tabs', 'no-privileges'));
|
||||||
|
|
||||||
|
w = new BrowserWindow({ show: false, webPreferences: { session: noPrivilegeSes } });
|
||||||
|
await w.loadURL(url);
|
||||||
|
|
||||||
|
w.webContents.executeJavaScript('window.postMessage(\'{}\', \'*\')');
|
||||||
|
const [,, responseString] = await once(w.webContents, 'console-message');
|
||||||
|
const response = JSON.parse(responseString);
|
||||||
|
expect(response).not.to.have.property('url');
|
||||||
|
expect(response).not.to.have.property('title');
|
||||||
|
expect(response).to.have.property('active').that.is.a('boolean');
|
||||||
|
expect(response).to.have.property('autoDiscardable').that.is.a('boolean');
|
||||||
|
expect(response).to.have.property('discarded').that.is.a('boolean');
|
||||||
|
expect(response).to.have.property('groupId').that.is.a('number');
|
||||||
|
expect(response).to.have.property('highlighted').that.is.a('boolean');
|
||||||
|
expect(response).to.have.property('id').that.is.a('number');
|
||||||
|
expect(response).to.have.property('incognito').that.is.a('boolean');
|
||||||
|
expect(response).to.have.property('index').that.is.a('number');
|
||||||
|
expect(response).to.have.property('pinned').that.is.a('boolean');
|
||||||
|
expect(response).to.have.property('selected').that.is.a('boolean');
|
||||||
|
expect(response).to.have.property('windowId').that.is.a('number');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('reload', async () => {
|
it('reload', async () => {
|
||||||
await w.loadURL(url);
|
await w.loadURL(url);
|
||||||
|
|
||||||
|
@ -960,6 +987,19 @@ describe('chrome extensions', () => {
|
||||||
const [,, responseString] = await once(w.webContents, 'console-message');
|
const [,, responseString] = await once(w.webContents, 'console-message');
|
||||||
const response = JSON.parse(responseString);
|
const response = JSON.parse(responseString);
|
||||||
|
|
||||||
|
expect(response).to.have.property('url').that.is.a('string');
|
||||||
|
expect(response).to.have.property('title').that.is.a('string');
|
||||||
|
expect(response).to.have.property('active').that.is.a('boolean');
|
||||||
|
expect(response).to.have.property('autoDiscardable').that.is.a('boolean');
|
||||||
|
expect(response).to.have.property('discarded').that.is.a('boolean');
|
||||||
|
expect(response).to.have.property('groupId').that.is.a('number');
|
||||||
|
expect(response).to.have.property('highlighted').that.is.a('boolean');
|
||||||
|
expect(response).to.have.property('id').that.is.a('number');
|
||||||
|
expect(response).to.have.property('incognito').that.is.a('boolean');
|
||||||
|
expect(response).to.have.property('index').that.is.a('number');
|
||||||
|
expect(response).to.have.property('pinned').that.is.a('boolean');
|
||||||
|
expect(response).to.have.property('selected').that.is.a('boolean');
|
||||||
|
expect(response).to.have.property('windowId').that.is.a('number');
|
||||||
expect(response).to.have.property('mutedInfo').that.is.a('object');
|
expect(response).to.have.property('mutedInfo').that.is.a('object');
|
||||||
const { mutedInfo } = response;
|
const { mutedInfo } = response;
|
||||||
expect(mutedInfo).to.deep.eq({
|
expect(mutedInfo).to.deep.eq({
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
{
|
{
|
||||||
"name": "tabs-api-async",
|
"name": "api-async",
|
||||||
"version": "1.0",
|
"version": "1.0",
|
||||||
"content_scripts": [
|
"content_scripts": [
|
||||||
{
|
{
|
||||||
|
@ -8,6 +8,7 @@
|
||||||
"run_at": "document_start"
|
"run_at": "document_start"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"permissions": ["tabs"],
|
||||||
"background": {
|
"background": {
|
||||||
"service_worker": "background.js"
|
"service_worker": "background.js"
|
||||||
},
|
},
|
|
@ -0,0 +1,6 @@
|
||||||
|
/* global chrome */
|
||||||
|
|
||||||
|
chrome.runtime.onMessage.addListener((_request, sender, sendResponse) => {
|
||||||
|
chrome.tabs.get(sender.tab.id).then(sendResponse);
|
||||||
|
return true;
|
||||||
|
});
|
|
@ -0,0 +1,11 @@
|
||||||
|
/* global chrome */
|
||||||
|
|
||||||
|
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
|
||||||
|
sendResponse(request);
|
||||||
|
});
|
||||||
|
|
||||||
|
window.addEventListener('message', () => {
|
||||||
|
chrome.runtime.sendMessage({}, response => {
|
||||||
|
console.log(JSON.stringify(response));
|
||||||
|
});
|
||||||
|
}, false);
|
|
@ -0,0 +1,19 @@
|
||||||
|
{
|
||||||
|
"name": "no-privileges",
|
||||||
|
"version": "1.0",
|
||||||
|
"content_scripts": [
|
||||||
|
{
|
||||||
|
"matches": [
|
||||||
|
"<all_urls>"
|
||||||
|
],
|
||||||
|
"js": [
|
||||||
|
"main.js"
|
||||||
|
],
|
||||||
|
"run_at": "document_start"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"background": {
|
||||||
|
"service_worker": "background.js"
|
||||||
|
},
|
||||||
|
"manifest_version": 3
|
||||||
|
}
|
Загрузка…
Ссылка в новой задаче