Do permission check when calling guest window methods
This commit is contained in:
		
					parent
					
						
							
								a1dfdbdde4
							
						
					
				
			
			
				commit
				
					
						a1066617a8
					
				
			
		
					 2 changed files with 38 additions and 3 deletions
				
			
		| 
						 | 
					@ -9,9 +9,11 @@
 | 
				
			||||||
#include "atom/common/api/remote_callback_freer.h"
 | 
					#include "atom/common/api/remote_callback_freer.h"
 | 
				
			||||||
#include "atom/common/api/remote_object_freer.h"
 | 
					#include "atom/common/api/remote_object_freer.h"
 | 
				
			||||||
#include "atom/common/native_mate_converters/content_converter.h"
 | 
					#include "atom/common/native_mate_converters/content_converter.h"
 | 
				
			||||||
 | 
					#include "atom/common/native_mate_converters/gurl_converter.h"
 | 
				
			||||||
#include "atom/common/node_includes.h"
 | 
					#include "atom/common/node_includes.h"
 | 
				
			||||||
#include "base/hash.h"
 | 
					#include "base/hash.h"
 | 
				
			||||||
#include "native_mate/dictionary.h"
 | 
					#include "native_mate/dictionary.h"
 | 
				
			||||||
 | 
					#include "url/origin.h"
 | 
				
			||||||
#include "v8/include/v8-profiler.h"
 | 
					#include "v8/include/v8-profiler.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace std {
 | 
					namespace std {
 | 
				
			||||||
| 
						 | 
					@ -97,6 +99,10 @@ void RequestGarbageCollectionForTesting(v8::Isolate* isolate) {
 | 
				
			||||||
    v8::Isolate::GarbageCollectionType::kFullGarbageCollection);
 | 
					    v8::Isolate::GarbageCollectionType::kFullGarbageCollection);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool IsSameOrigin(const GURL& l, const GURL& r) {
 | 
				
			||||||
 | 
					  return url::Origin(l).IsSameOriginWith(url::Origin(r));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void Initialize(v8::Local<v8::Object> exports, v8::Local<v8::Value> unused,
 | 
					void Initialize(v8::Local<v8::Object> exports, v8::Local<v8::Value> unused,
 | 
				
			||||||
                v8::Local<v8::Context> context, void* priv) {
 | 
					                v8::Local<v8::Context> context, void* priv) {
 | 
				
			||||||
  mate::Dictionary dict(context->GetIsolate(), exports);
 | 
					  mate::Dictionary dict(context->GetIsolate(), exports);
 | 
				
			||||||
| 
						 | 
					@ -112,6 +118,7 @@ void Initialize(v8::Local<v8::Object> exports, v8::Local<v8::Value> unused,
 | 
				
			||||||
                 &atom::api::KeyWeakMap<std::pair<int64_t, int32_t>>::Create);
 | 
					                 &atom::api::KeyWeakMap<std::pair<int64_t, int32_t>>::Create);
 | 
				
			||||||
  dict.SetMethod("requestGarbageCollectionForTesting",
 | 
					  dict.SetMethod("requestGarbageCollectionForTesting",
 | 
				
			||||||
                 &RequestGarbageCollectionForTesting);
 | 
					                 &RequestGarbageCollectionForTesting);
 | 
				
			||||||
 | 
					  dict.SetMethod("isSameOrigin", &IsSameOrigin);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
}  // namespace
 | 
					}  // namespace
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,6 +1,7 @@
 | 
				
			||||||
'use strict'
 | 
					'use strict'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const {BrowserWindow, ipcMain, webContents} = require('electron')
 | 
					const {BrowserWindow, ipcMain, webContents} = require('electron')
 | 
				
			||||||
 | 
					const {isSameOrigin} = process.atomBinding('v8_util')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const hasProp = {}.hasOwnProperty
 | 
					const hasProp = {}.hasOwnProperty
 | 
				
			||||||
const frameToGuest = {}
 | 
					const frameToGuest = {}
 | 
				
			||||||
| 
						 | 
					@ -151,6 +152,22 @@ const getGuestWindow = function (guestId) {
 | 
				
			||||||
  return guestWindow
 | 
					  return guestWindow
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Checks whether |sender| can access the |target|:
 | 
				
			||||||
 | 
					// 1. Check whether |sender| is the parent of |target|.
 | 
				
			||||||
 | 
					// 2. Check whether |sender| has node integration, if so it is allowed to
 | 
				
			||||||
 | 
					//    do anything it wants.
 | 
				
			||||||
 | 
					// 3. Check whether the origins match.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// However it allows a child window without node integration but with same
 | 
				
			||||||
 | 
					// origin to do anything it wants, when its opener window has node integration.
 | 
				
			||||||
 | 
					// The W3C does not have anything on this, but from my understanding of the
 | 
				
			||||||
 | 
					// security model of |window.opener|, this should be fine.
 | 
				
			||||||
 | 
					const canAccessWindow = function (sender, target) {
 | 
				
			||||||
 | 
					  return (target.getWebPreferences().openerId === sender.webContents.id) ||
 | 
				
			||||||
 | 
					         (sender.getWebPreferences().nodeIntegration === true) ||
 | 
				
			||||||
 | 
					         isSameOrigin(sender.getURL(), target.getURL())
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Routed window.open messages.
 | 
					// Routed window.open messages.
 | 
				
			||||||
ipcMain.on('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_OPEN', function (event, url, frameName,
 | 
					ipcMain.on('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_OPEN', function (event, url, frameName,
 | 
				
			||||||
                                                                  disposition, options,
 | 
					                                                                  disposition, options,
 | 
				
			||||||
| 
						 | 
					@ -171,18 +188,27 @@ ipcMain.on('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_OPEN', function (event, url, fr
 | 
				
			||||||
 | 
					
 | 
				
			||||||
ipcMain.on('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_CLOSE', function (event, guestId) {
 | 
					ipcMain.on('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_CLOSE', function (event, guestId) {
 | 
				
			||||||
  const guestWindow = getGuestWindow(guestId)
 | 
					  const guestWindow = getGuestWindow(guestId)
 | 
				
			||||||
  if (guestWindow != null) guestWindow.destroy()
 | 
					  if (guestWindow != null && canAccessWindow(event.sender, guestWindow.webContents)) {
 | 
				
			||||||
 | 
					    guestWindow.destroy()
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
})
 | 
					})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
ipcMain.on('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_METHOD', function (event, guestId, method, ...args) {
 | 
					ipcMain.on('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_METHOD', function (event, guestId, method, ...args) {
 | 
				
			||||||
  const guestWindow = getGuestWindow(guestId)
 | 
					  const guestWindow = getGuestWindow(guestId)
 | 
				
			||||||
  event.returnValue = guestWindow != null ? guestWindow[method].apply(guestWindow, args) : null
 | 
					  if (guestWindow != null && canAccessWindow(event.sender, guestWindow.webContents)) {
 | 
				
			||||||
 | 
					    event.returnValue = guestWindow[method].apply(guestWindow, args)
 | 
				
			||||||
 | 
					  } else {
 | 
				
			||||||
 | 
					    event.returnValue = null
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
})
 | 
					})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
ipcMain.on('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_POSTMESSAGE', function (event, guestId, message, targetOrigin, sourceOrigin) {
 | 
					ipcMain.on('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_POSTMESSAGE', function (event, guestId, message, targetOrigin, sourceOrigin) {
 | 
				
			||||||
  const guestContents = webContents.fromId(guestId)
 | 
					  const guestContents = webContents.fromId(guestId)
 | 
				
			||||||
  if (guestContents == null) return
 | 
					  if (guestContents == null) return
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // The W3C does not seem to have word on how postMessage should work when the
 | 
				
			||||||
 | 
					  // origins do not match, so we do not do |canAccessWindow| check here since
 | 
				
			||||||
 | 
					  // postMessage across origins is usefull and not harmful.
 | 
				
			||||||
  if (guestContents.getURL().indexOf(targetOrigin) === 0 || targetOrigin === '*') {
 | 
					  if (guestContents.getURL().indexOf(targetOrigin) === 0 || targetOrigin === '*') {
 | 
				
			||||||
    const sourceId = event.sender.id
 | 
					    const sourceId = event.sender.id
 | 
				
			||||||
    guestContents.send('ELECTRON_GUEST_WINDOW_POSTMESSAGE', sourceId, message, sourceOrigin)
 | 
					    guestContents.send('ELECTRON_GUEST_WINDOW_POSTMESSAGE', sourceId, message, sourceOrigin)
 | 
				
			||||||
| 
						 | 
					@ -191,5 +217,7 @@ ipcMain.on('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_POSTMESSAGE', function (event,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
ipcMain.on('ELECTRON_GUEST_WINDOW_MANAGER_WEB_CONTENTS_METHOD', function (event, guestId, method, ...args) {
 | 
					ipcMain.on('ELECTRON_GUEST_WINDOW_MANAGER_WEB_CONTENTS_METHOD', function (event, guestId, method, ...args) {
 | 
				
			||||||
  const guestContents = webContents.fromId(guestId)
 | 
					  const guestContents = webContents.fromId(guestId)
 | 
				
			||||||
  if (guestContents != null) guestContents[method].apply(guestContents, args)
 | 
					  if (guestContents != null && canAccessWindow(event.sender, guestContents)) {
 | 
				
			||||||
 | 
					    guestContents[method].apply(guestContents, args)
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
})
 | 
					})
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue