mirror of
https://github.com/electron/electron.git
synced 2026-04-10 03:01:51 -04:00
fix: propagate requesting frame through sync permission checks (#50686)
WebContentsPermissionHelper::CheckPermission was hardcoding GetPrimaryMainFrame() and deriving the requesting origin from web_contents_->GetLastCommittedURL(), so the setPermissionCheckHandler callback always received the top frame's origin and details.isMainFrame/details.requestingUrl always reflected the main frame, even when a cross-origin subframe with allow="serial" or allow="camera; microphone" triggered the check. Thread the requesting RenderFrameHost through CheckPermission, CheckSerialAccessPermission, and CheckMediaAccessPermission so the permission manager receives the real requesting frame. Update the serial delegate and WebContents::CheckMediaAccessPermission callers to pass the frame they already have. Adds a regression test that loads a cross-origin iframe with allow="camera; microphone", calls enumerateDevices() from within the iframe, and asserts the permission check handler receives the iframe origin for requestingOrigin, isMainFrame, and requestingUrl. Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Sam Attard <sattard@anthropic.com>
This commit is contained in:
@@ -1767,7 +1767,8 @@ bool WebContents::CheckMediaAccessPermission(
|
||||
content::WebContents::FromRenderFrameHost(render_frame_host);
|
||||
auto* permission_helper =
|
||||
WebContentsPermissionHelper::FromWebContents(web_contents);
|
||||
return permission_helper->CheckMediaAccessPermission(security_origin, type);
|
||||
return permission_helper->CheckMediaAccessPermission(render_frame_host,
|
||||
security_origin, type);
|
||||
}
|
||||
|
||||
void WebContents::RequestMediaAccessPermission(
|
||||
|
||||
@@ -51,8 +51,7 @@ bool ElectronSerialDelegate::CanRequestPortPermission(
|
||||
auto* web_contents = content::WebContents::FromRenderFrameHost(frame);
|
||||
auto* permission_helper =
|
||||
WebContentsPermissionHelper::FromWebContents(web_contents);
|
||||
return permission_helper->CheckSerialAccessPermission(
|
||||
frame->GetLastCommittedOrigin());
|
||||
return permission_helper->CheckSerialAccessPermission(frame);
|
||||
}
|
||||
|
||||
bool ElectronSerialDelegate::HasPortPermission(
|
||||
|
||||
@@ -228,14 +228,14 @@ void WebContentsPermissionHelper::RequestPermission(
|
||||
}
|
||||
|
||||
bool WebContentsPermissionHelper::CheckPermission(
|
||||
content::RenderFrameHost* requesting_frame,
|
||||
blink::PermissionType permission,
|
||||
base::DictValue details) const {
|
||||
auto* rfh = web_contents_->GetPrimaryMainFrame();
|
||||
auto* permission_manager = static_cast<ElectronPermissionManager*>(
|
||||
web_contents_->GetBrowserContext()->GetPermissionControllerDelegate());
|
||||
auto origin = web_contents_->GetLastCommittedURL();
|
||||
return permission_manager->CheckPermissionWithDetails(permission, rfh, origin,
|
||||
std::move(details));
|
||||
auto origin = requesting_frame->GetLastCommittedOrigin().GetURL();
|
||||
return permission_manager->CheckPermissionWithDetails(
|
||||
permission, requesting_frame, origin, std::move(details));
|
||||
}
|
||||
|
||||
void WebContentsPermissionHelper::RequestFullscreenPermission(
|
||||
@@ -313,6 +313,7 @@ void WebContentsPermissionHelper::RequestOpenExternalPermission(
|
||||
}
|
||||
|
||||
bool WebContentsPermissionHelper::CheckMediaAccessPermission(
|
||||
content::RenderFrameHost* requesting_frame,
|
||||
const url::Origin& security_origin,
|
||||
blink::mojom::MediaStreamType type) const {
|
||||
base::DictValue details;
|
||||
@@ -321,14 +322,16 @@ bool WebContentsPermissionHelper::CheckMediaAccessPermission(
|
||||
auto blink_type = type == blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE
|
||||
? blink::PermissionType::AUDIO_CAPTURE
|
||||
: blink::PermissionType::VIDEO_CAPTURE;
|
||||
return CheckPermission(blink_type, std::move(details));
|
||||
return CheckPermission(requesting_frame, blink_type, std::move(details));
|
||||
}
|
||||
|
||||
bool WebContentsPermissionHelper::CheckSerialAccessPermission(
|
||||
const url::Origin& embedding_origin) const {
|
||||
content::RenderFrameHost* requesting_frame) const {
|
||||
base::DictValue details;
|
||||
details.Set("securityOrigin", embedding_origin.GetURL().spec());
|
||||
return CheckPermission(blink::PermissionType::SERIAL, std::move(details));
|
||||
details.Set("securityOrigin",
|
||||
requesting_frame->GetLastCommittedOrigin().GetURL().spec());
|
||||
return CheckPermission(requesting_frame, blink::PermissionType::SERIAL,
|
||||
std::move(details));
|
||||
}
|
||||
|
||||
WEB_CONTENTS_USER_DATA_KEY_IMPL(WebContentsPermissionHelper);
|
||||
|
||||
@@ -47,9 +47,11 @@ class WebContentsPermissionHelper
|
||||
const GURL& url);
|
||||
|
||||
// Synchronous Checks
|
||||
bool CheckMediaAccessPermission(const url::Origin& security_origin,
|
||||
bool CheckMediaAccessPermission(content::RenderFrameHost* requesting_frame,
|
||||
const url::Origin& security_origin,
|
||||
blink::mojom::MediaStreamType type) const;
|
||||
bool CheckSerialAccessPermission(const url::Origin& embedding_origin) const;
|
||||
bool CheckSerialAccessPermission(
|
||||
content::RenderFrameHost* requesting_frame) const;
|
||||
|
||||
private:
|
||||
explicit WebContentsPermissionHelper(content::WebContents* web_contents);
|
||||
@@ -61,7 +63,8 @@ class WebContentsPermissionHelper
|
||||
bool user_gesture = false,
|
||||
base::DictValue details = {});
|
||||
|
||||
bool CheckPermission(blink::PermissionType permission,
|
||||
bool CheckPermission(content::RenderFrameHost* requesting_frame,
|
||||
blink::PermissionType permission,
|
||||
base::DictValue details) const;
|
||||
|
||||
// TODO(clavin): refactor to use the WebContents provided by the
|
||||
|
||||
@@ -1941,6 +1941,60 @@ describe('session module', () => {
|
||||
expect(handlerDetails!.isMainFrame).to.be.false();
|
||||
expect(handlerDetails!.embeddingOrigin).to.equal('file:///');
|
||||
});
|
||||
|
||||
it('provides iframe origin as requestingOrigin for media check from cross-origin subFrame', async () => {
|
||||
const w = new BrowserWindow({
|
||||
show: false,
|
||||
webPreferences: {
|
||||
partition: 'very-temp-permission-handler-media'
|
||||
}
|
||||
});
|
||||
const ses = w.webContents.session;
|
||||
const iframeUrl = 'https://myfakesite/';
|
||||
let capturedOrigin: string | undefined;
|
||||
let capturedIsMainFrame: boolean | undefined;
|
||||
let capturedRequestingUrl: string | undefined;
|
||||
let capturedSecurityOrigin: string | undefined;
|
||||
|
||||
ses.protocol.interceptStringProtocol('https', (req, cb) => {
|
||||
cb('<html><body>iframe</body></html>');
|
||||
});
|
||||
|
||||
ses.setPermissionCheckHandler((wc, permission, requestingOrigin, details) => {
|
||||
if (permission === 'media') {
|
||||
capturedOrigin = requestingOrigin;
|
||||
capturedIsMainFrame = details.isMainFrame;
|
||||
capturedRequestingUrl = details.requestingUrl;
|
||||
capturedSecurityOrigin = (details as any).securityOrigin;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
try {
|
||||
await w.loadFile(path.join(fixtures, 'api', 'blank.html'));
|
||||
w.webContents.executeJavaScript(`
|
||||
var iframe = document.createElement('iframe');
|
||||
iframe.src = '${iframeUrl}';
|
||||
iframe.allow = 'camera; microphone';
|
||||
document.body.appendChild(iframe);
|
||||
null;
|
||||
`);
|
||||
const [,, frameProcessId, frameRoutingId] = await once(w.webContents, 'did-frame-finish-load');
|
||||
const frame = webFrameMain.fromId(frameProcessId, frameRoutingId)!;
|
||||
await frame.executeJavaScript(
|
||||
'navigator.mediaDevices.enumerateDevices().then(() => {}).catch(() => {});',
|
||||
true
|
||||
);
|
||||
|
||||
expect(capturedOrigin).to.equal(iframeUrl);
|
||||
expect(capturedIsMainFrame).to.be.false();
|
||||
expect(capturedRequestingUrl).to.equal(iframeUrl);
|
||||
expect(capturedSecurityOrigin).to.equal(iframeUrl);
|
||||
} finally {
|
||||
ses.protocol.uninterceptProtocol('https');
|
||||
ses.setPermissionCheckHandler(null);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe('ses.isPersistent()', () => {
|
||||
|
||||
Reference in New Issue
Block a user