Skip to content

Commit 301bff4

Browse files
the-chenergyJonWBedard
authored andcommitted
A compromised Web Content process should not be able to start Web Inspector
rdar://98891055 https://bugs.webkit.org/show_bug.cgi?id=283092 Reviewed by Ryosuke Niwa and BJ Burg. There currently exists a message WebInspectorUIProxy::OpenLocalInspectorFrontend, which the web process sends to the UI process to show Web Inspector for the current web page. This introduces security risks as a compromised website may find its way to send arbitrary messages to the UI process, opening Web Inspector and weakening the web content sandbox. The reason this message exists is because there are useful ways the web process needs to open Web Inspector with initiative. Normally, Web Inspector is opened via one of the Develop menu's items, which is controlled by the UI process. However, Web Inspector can also be opened without being prompted by the UI process first, in these places: 1. In a web page's context menu, the "Inspect Element" option 2. Inside Web Inspector, if the Debug UI is enabled, on the top right corner, a button to open inspector^2 3. In WebKitTestRunner, via the TestRunner::showWebInspector function This patch makes it so that web process can no longer send a message to a UI process to open Web Inspector. This means web process cannot open Web Inspector at will -- it must be either due to the UI process's demand, or it's in one of the above three cases. More details below. I have tested that this change preserves the above three special cases and does prevent the web page from opening Web Inspector at will. - Cases #1 and #2 can be tested from the UI. - Case #3 can be tested with a WebKit test involving Web Inspector. I ran the test LayoutTests/inspector/console/js-completions.html, where I saw the test crashing without special treatment for this case. - To verify that the web page can't open Web Inspector, I followed the reproduction steps from the Radar and saw Web Inspector no longer opens, and opening the external URL also failed as expected. * Source/WebKit/UIProcess/Inspector/WebInspectorUIProxy.messages.in: * Source/WebKit/UIProcess/Inspector/WebInspectorUIProxy.h: * Source/WebKit/UIProcess/Inspector/WebInspectorUIProxy.cpp: (WebKit::WebInspectorUIProxy::connect): - If the UI process wants to open Web Inspector, it sends a WebInspector::Show command to the web process. This patch makes that command take an async reply, so that the anticipated WebInspectorUIProxy::OpenLocalInspectorFrontend message from the web process can now be delivered through that async reply instead. This ensures that OpenLocalInspectorFrontend can only be done when initiated from the UI process (due to user interaction). (WebKit::WebInspectorUIProxy::markAsUnderTest): (WebKit::WebInspectorUIProxy::openLocalInspectorFrontend): (WebKit::WebInspectorUIProxy::closeFrontendPageAndWindow): - To avoid relying on the web process for potentially sensitive parameters, I reworked and removed the canAttach and underTest arguments from openLocalInspectorFrontend. These two values are now stored and managed in the UI process instead, instead of being passed from the web process all the time. - For canAttach, I noticed that the WebInspectorUIProxyMac::platformCanAttach method already implements the same logic as the web process's WebInspector::canAttachWindow. I filed https://webkit.org/b/283435 as a follow-up to clean up the webProcessCanAttach parameter, the canAttachWindow function in the web process, and potentially the m_attached field too, which all become obsolete due to this change. - I couldn't figure out what the `if (m_attached)` in canAttachWindow check does, and to me it had no effect, as this function is not called while inspector is open. - For underTest, I'm now letting the test runner directly set the flag on the WebInspectorUIProxy, as part of my fix to address case #3 from above. (WebKit::WebInspectorUIProxy::showConsole): (WebKit::WebInspectorUIProxy::showResources): (WebKit::WebInspectorUIProxy::showMainResourceForFrame): (WebKit::WebInspectorUIProxy::togglePageProfiling): - As the web process can longer call OpenLocalInspectorFrontend, call show/connect/openLocalInspectorFrontend here in the UI process instead. (WebKit::WebInspectorUIProxy::requestOpenLocalInspectorFrontend): - To preserve the open inspector^2 button (case #2 from above), we still maintain this message, but we ignore it unless it's for opening inspector^2, thus renaming the message as a request. This is all assuming that the Web Inspector is not a compromised web process, so we allow that message from it to come through. * Source/WebKit/WebProcess/Inspector/WebInspector.messages.in: * Source/WebKit/WebProcess/Inspector/WebInspector.h: * Source/WebKit/WebProcess/Inspector/WebInspector.cpp: (WebKit::WebInspector::show): - The Show message now takes an async reply, which is used to replace sending WebInspectorUIProxy::OpenLocalInspectorFrontend later. (WebKit::WebInspector::showConsole): (WebKit::WebInspector::showResources): (WebKit::WebInspector::showMainResourceForFrame): (WebKit::WebInspector::startPageProfiling): (WebKit::WebInspector::stopPageProfiling): - Calling inspectorController()->show() no longer does anything, since it's now the UI process's job to show Web Inspector first, for these functions to merely switch to the appropriate tabs. * Source/WebKit/WebProcess/Inspector/WebInspector.cpp: (WebKit::WebInspector::openLocalInspectorFrontend): * Source/WebKit/WebProcess/Inspector/WebInspectorClient.cpp: (WebKit::WebInspectorClient::openLocalFrontend): - Adapt to the command's reworked version. - This is maintained to allow the opening of inspector^2 from the web process (case #2 from above). For opening inspector^1, this message will be ignored by the UI process. * Source/WebKit/UIProcess/WebPageProxy.cpp: (WebKit::WebPageProxy::contextMenuItemSelected): - When the "Inspect Element" context menu item is selected (case #1 from above), since the web process may not be privileged to open Web Inspector, handle the showing of inspector here in UI process. * Tools/WebKitTestRunner/InjectedBundle/TestRunner.cpp: (WTR::TestRunner::showWebInspector): * Tools/WebKitTestRunner/TestInvocation.cpp: (WTR::TestInvocation::didReceiveMessageFromInjectedBundle): * Source/WebKit/UIProcess/API/C/WKPagePrivate.h: * Source/WebKit/UIProcess/API/C/WKPage.cpp: (WKPageShowWebInspectorForTesting): - Preserve letting the WebKitTestRunner open Web Inspector (case #3 from above). - Adapt to the change that we now also let the UI process know about the underTest flag for case #3, rather than letting UI process rely on the value reported by the web process. * Source/WebKit/WebProcess/InjectedBundle/API/c/WKBundlePage.h: * Source/WebKit/WebProcess/InjectedBundle/API/c/WKBundlePage.cpp: (WKBundlePageShowInspectorForTest): Deleted. - No longer used due to my special fix for case #3. Originally-landed-as: 283286.537@safari-7620-branch (694a9b5). rdar://144667626 Canonical link: https://commits.webkit.org/290260@main
1 parent ff08cab commit 301bff4

14 files changed

+59
-34
lines changed

Source/WebKit/UIProcess/API/C/WKPage.cpp

+7
Original file line numberDiff line numberDiff line change
@@ -2952,6 +2952,13 @@ void WKPageSetAllowsRemoteInspection(WKPageRef pageRef, bool allow)
29522952
#endif
29532953
}
29542954

2955+
void WKPageShowWebInspectorForTesting(WKPageRef pageRef)
2956+
{
2957+
RefPtr<WebInspectorUIProxy> inspector = toImpl(pageRef)->inspector();
2958+
inspector->markAsUnderTest();
2959+
inspector->show();
2960+
}
2961+
29552962
void WKPageSetMediaVolume(WKPageRef pageRef, float volume)
29562963
{
29572964
CRASH_IF_SUSPENDED;

Source/WebKit/UIProcess/API/C/WKPagePrivate.h

+2
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,8 @@ WK_EXPORT void WKPageSetControlledByAutomation(WKPageRef page, bool controlled);
9494
WK_EXPORT bool WKPageGetAllowsRemoteInspection(WKPageRef page);
9595
WK_EXPORT void WKPageSetAllowsRemoteInspection(WKPageRef page, bool allow);
9696

97+
WK_EXPORT void WKPageShowWebInspectorForTesting(WKPageRef page);
98+
9799
WK_EXPORT void WKPageSetMediaVolume(WKPageRef page, float volume);
98100
WK_EXPORT void WKPageSetMayStartMediaWhenInWindow(WKPageRef page, bool mayStartMedia);
99101

Source/WebKit/UIProcess/Inspector/WebInspectorUIProxy.cpp

+25-8
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,12 @@ void WebInspectorUIProxy::connect()
156156

157157
Ref legacyMainFrameProcess = inspectedPage->legacyMainFrameProcess();
158158
legacyMainFrameProcess->send(Messages::WebInspectorInterruptDispatcher::NotifyNeedDebuggerBreak(), 0);
159-
legacyMainFrameProcess->send(Messages::WebInspector::Show(), m_inspectedPage->webPageIDInMainFrameProcess());
159+
legacyMainFrameProcess->sendWithAsyncReply(
160+
Messages::WebInspector::Show(),
161+
[this, protectedThis = Ref { *this }] {
162+
openLocalInspectorFrontend();
163+
},
164+
m_inspectedPage->webPageIDInMainFrameProcess());
160165
}
161166

162167
void WebInspectorUIProxy::show()
@@ -265,7 +270,7 @@ void WebInspectorUIProxy::showConsole()
265270
if (!inspectedPage)
266271
return;
267272

268-
createFrontendPage();
273+
show();
269274

270275
inspectedPage->protectedLegacyMainFrameProcess()->send(Messages::WebInspector::ShowConsole(), inspectedPage->webPageIDInMainFrameProcess());
271276
}
@@ -276,7 +281,7 @@ void WebInspectorUIProxy::showResources()
276281
if (!inspectedPage)
277282
return;
278283

279-
createFrontendPage();
284+
show();
280285

281286
inspectedPage->protectedLegacyMainFrameProcess()->send(Messages::WebInspector::ShowResources(), inspectedPage->webPageIDInMainFrameProcess());
282287
}
@@ -287,7 +292,7 @@ void WebInspectorUIProxy::showMainResourceForFrame(WebCore::FrameIdentifier fram
287292
if (!inspectedPage)
288293
return;
289294

290-
createFrontendPage();
295+
show();
291296

292297
inspectedPage->sendToProcessContainingFrame(frameID, Messages::WebInspector::ShowMainResourceForFrame(frameID));
293298
}
@@ -392,6 +397,8 @@ void WebInspectorUIProxy::togglePageProfiling()
392397
if (!m_inspectedPage)
393398
return;
394399

400+
show();
401+
395402
RefPtr inspectedPage = m_inspectedPage.get();
396403
if (m_isProfilingPage)
397404
inspectedPage->protectedLegacyMainFrameProcess()->send(Messages::WebInspector::StopPageProfiling(), inspectedPage->webPageIDInMainFrameProcess());
@@ -453,7 +460,16 @@ void WebInspectorUIProxy::createFrontendPage()
453460
#endif
454461
}
455462

456-
void WebInspectorUIProxy::openLocalInspectorFrontend(bool canAttach, bool underTest)
463+
void WebInspectorUIProxy::requestOpenLocalInspectorFrontend()
464+
{
465+
// Prevent a compromised malicious web page from opening Web Inspector at will.
466+
if (!m_underTest && inspectionLevel() < 2)
467+
return;
468+
469+
openLocalInspectorFrontend();
470+
}
471+
472+
void WebInspectorUIProxy::openLocalInspectorFrontend()
457473
{
458474
RefPtr inspectedPage = m_inspectedPage.get();
459475
if (!inspectedPage)
@@ -467,7 +483,6 @@ void WebInspectorUIProxy::openLocalInspectorFrontend(bool canAttach, bool underT
467483
return;
468484
}
469485

470-
m_underTest = underTest;
471486
createFrontendPage();
472487

473488
RefPtr inspectorPage = m_inspectorPage.get();
@@ -482,7 +497,10 @@ void WebInspectorUIProxy::openLocalInspectorFrontend(bool canAttach, bool underT
482497
inspectedPage->inspectorController().connectFrontend(*this);
483498

484499
if (!m_underTest) {
485-
m_canAttach = platformCanAttach(canAttach);
500+
// FIXME <https://webkit.org/b/283435>: Remove the webProcessCanAttach argument from platformCanAttach.
501+
// The value canAttach in the web process is no longer used or respected.
502+
const bool webProcessCanAttach = false;
503+
m_canAttach = platformCanAttach(webProcessCanAttach);
486504
m_isAttached = shouldOpenAttached();
487505
m_attachmentSide = static_cast<AttachmentSide>(protectedInspectorPagePreferences()->inspectorAttachmentSide());
488506

@@ -607,7 +625,6 @@ void WebInspectorUIProxy::closeFrontendPageAndWindow()
607625

608626
m_isAttached = false;
609627
m_canAttach = false;
610-
m_underTest = false;
611628

612629
platformCloseFrontendPageAndWindow();
613630
}

Source/WebKit/UIProcess/Inspector/WebInspectorUIProxy.h

+3-1
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,7 @@ class WebInspectorUIProxy
199199
void toggleElementSelection();
200200

201201
bool isUnderTest() const { return m_underTest; }
202+
void markAsUnderTest() { m_underTest = true; }
202203

203204
void setDiagnosticLoggingAvailable(bool);
204205

@@ -264,9 +265,10 @@ class WebInspectorUIProxy
264265
#endif
265266

266267
// Called by WebInspectorUIProxy messages
267-
void openLocalInspectorFrontend(bool canAttach, bool underTest);
268+
void requestOpenLocalInspectorFrontend();
268269
void setFrontendConnection(IPC::Connection::Handle&&);
269270

271+
void openLocalInspectorFrontend();
270272
void sendMessageToBackend(const String&);
271273
void frontendLoaded();
272274
void didClose();

Source/WebKit/UIProcess/Inspector/WebInspectorUIProxy.messages.in

+1-1
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
DispatchedTo=UI
2727
]
2828
messages -> WebInspectorUIProxy {
29-
OpenLocalInspectorFrontend(bool canAttach, bool underTest)
29+
RequestOpenLocalInspectorFrontend()
3030
SetFrontendConnection(IPC::ConnectionHandle connectionHandle)
3131

3232
SendMessageToBackend(String message)

Source/WebKit/UIProcess/WebPageProxy.cpp

+6
Original file line numberDiff line numberDiff line change
@@ -10056,6 +10056,12 @@ void WebPageProxy::contextMenuItemSelected(const WebContextMenuItemData& item)
1005610056
case ContextMenuItemTagShowColors:
1005710057
showColorPanel();
1005810058
return;
10059+
10060+
case ContextMenuItemTagInspectElement:
10061+
// The web process can no longer demand Web Inspector to show, so handle that part here.
10062+
m_inspector->show();
10063+
// The actual element-selection is still handled in the web process, so we break instead of return.
10064+
break;
1005910065
#endif // PLATFORM(MAC)
1006010066

1006110067
case ContextMenuItemTagShowSpellingPanel:

Source/WebKit/WebProcess/InjectedBundle/API/c/WKBundlePage.cpp

-5
Original file line numberDiff line numberDiff line change
@@ -438,11 +438,6 @@ void WKBundlePageListenForLayoutMilestones(WKBundlePageRef pageRef, WKLayoutMile
438438
WebKit::toImpl(pageRef)->listenForLayoutMilestones(WebKit::toLayoutMilestones(milestones));
439439
}
440440

441-
void WKBundlePageShowInspectorForTest(WKBundlePageRef page)
442-
{
443-
WebKit::toImpl(page)->inspector()->show();
444-
}
445-
446441
void WKBundlePageCloseInspectorForTest(WKBundlePageRef page)
447442
{
448443
WebKit::toImpl(page)->inspector()->close();

Source/WebKit/WebProcess/InjectedBundle/API/c/WKBundlePage.h

-1
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,6 @@ WK_EXPORT double WKBundlePageGetBackingScaleFactor(WKBundlePageRef page);
9999

100100
WK_EXPORT void WKBundlePageListenForLayoutMilestones(WKBundlePageRef page, WKLayoutMilestones milestones);
101101

102-
WK_EXPORT void WKBundlePageShowInspectorForTest(WKBundlePageRef page);
103102
WK_EXPORT void WKBundlePageCloseInspectorForTest(WKBundlePageRef page);
104103
WK_EXPORT void WKBundlePageEvaluateScriptInInspectorForTest(WKBundlePageRef page, WKStringRef script);
105104

Source/WebKit/WebProcess/Inspector/WebInspector.messages.in

+1-1
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
DispatchedTo=WebContent
2626
]
2727
messages -> WebInspector {
28-
Show()
28+
Show() -> () Async
2929
Close()
3030

3131
SetAttached(bool attached)

Source/WebKit/WebProcess/Inspector/WebInspectorClient.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ void WebInspectorClient::frontendCountChanged(unsigned count)
102102
Inspector::FrontendChannel* WebInspectorClient::openLocalFrontend(InspectorController* controller)
103103
{
104104
if (RefPtr page = m_page.get())
105-
page->inspector()->openLocalInspectorFrontend(controller->isUnderTest());
105+
page->inspector()->openLocalInspectorFrontend();
106106
return nullptr;
107107
}
108108

Source/WebKit/WebProcess/Inspector/WebInspectorInternal.cpp

+6-13
Original file line numberDiff line numberDiff line change
@@ -75,9 +75,9 @@ WebPage* WebInspector::page() const
7575
return m_page.get();
7676
}
7777

78-
void WebInspector::openLocalInspectorFrontend(bool underTest)
78+
void WebInspector::openLocalInspectorFrontend()
7979
{
80-
WebProcess::singleton().parentProcessConnection()->send(Messages::WebInspectorUIProxy::OpenLocalInspectorFrontend(canAttachWindow(), underTest), m_page->identifier());
80+
WebProcess::singleton().parentProcessConnection()->send(Messages::WebInspectorUIProxy::RequestOpenLocalInspectorFrontend(), m_page->identifier());
8181
}
8282

8383
void WebInspector::setFrontendConnection(IPC::Connection::Handle&& connectionHandle)
@@ -132,12 +132,13 @@ void WebInspector::whenFrontendConnectionEstablished(Function<void()>&& callback
132132
}
133133

134134
// Called by WebInspector messages
135-
void WebInspector::show()
135+
void WebInspector::show(CompletionHandler<void()>&& completionHandler)
136136
{
137137
if (!m_page->corePage())
138138
return;
139139

140140
m_page->corePage()->inspectorController().show();
141+
completionHandler();
141142
}
142143

143144
void WebInspector::close()
@@ -165,8 +166,6 @@ void WebInspector::showConsole()
165166
if (!m_page->corePage())
166167
return;
167168

168-
m_page->corePage()->inspectorController().show();
169-
170169
whenFrontendConnectionEstablished([=, this] {
171170
m_frontendConnection->send(Messages::WebInspectorUI::ShowConsole(), 0);
172171
});
@@ -177,8 +176,6 @@ void WebInspector::showResources()
177176
if (!m_page->corePage())
178177
return;
179178

180-
m_page->corePage()->inspectorController().show();
181-
182179
whenFrontendConnectionEstablished([=, this] {
183180
m_frontendConnection->send(Messages::WebInspectorUI::ShowResources(), 0);
184181
});
@@ -193,8 +190,6 @@ void WebInspector::showMainResourceForFrame(WebCore::FrameIdentifier frameIdenti
193190
if (!m_page->corePage())
194191
return;
195192

196-
m_page->corePage()->inspectorController().show();
197-
198193
String inspectorFrameIdentifier = m_page->corePage()->inspectorController().ensurePageAgent().frameId(frame->coreLocalFrame());
199194

200195
whenFrontendConnectionEstablished([=, this] {
@@ -207,8 +202,6 @@ void WebInspector::startPageProfiling()
207202
if (!m_page->corePage())
208203
return;
209204

210-
m_page->corePage()->inspectorController().show();
211-
212205
whenFrontendConnectionEstablished([=, this] {
213206
m_frontendConnection->send(Messages::WebInspectorUI::StartPageProfiling(), 0);
214207
});
@@ -219,8 +212,6 @@ void WebInspector::stopPageProfiling()
219212
if (!m_page->corePage())
220213
return;
221214

222-
m_page->corePage()->inspectorController().show();
223-
224215
whenFrontendConnectionEstablished([=, this] {
225216
m_frontendConnection->send(Messages::WebInspectorUI::StopPageProfiling(), 0);
226217
});
@@ -270,6 +261,8 @@ void WebInspector::setEmulatedConditions(std::optional<int64_t>&& bytesPerSecond
270261

271262
#endif // ENABLE(INSPECTOR_NETWORK_THROTTLING)
272263

264+
// FIXME <https://webkit.org/b/283435>: Remove this unused canAttachWindow function. Its return value is no longer used
265+
// or respected by the UI process.
273266
bool WebInspector::canAttachWindow()
274267
{
275268
if (!m_page->corePage())

Source/WebKit/WebProcess/Inspector/WebInspectorInternal.h

+2-2
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ class WebInspector : public ThreadSafeRefCounted<WebInspector>, private IPC::Con
5858
void didClose(IPC::Connection&) override { close(); }
5959
void didReceiveInvalidMessage(IPC::Connection&, IPC::MessageName, int32_t indexOfObjectFailingDecoding) override { close(); }
6060

61-
void show();
61+
void show(CompletionHandler<void()>&&);
6262
void close();
6363

6464
void canAttachWindow(bool& result);
@@ -97,7 +97,7 @@ class WebInspector : public ThreadSafeRefCounted<WebInspector>, private IPC::Con
9797
bool canAttachWindow();
9898

9999
// Called from WebInspectorClient
100-
void openLocalInspectorFrontend(bool underTest);
100+
void openLocalInspectorFrontend();
101101
void closeFrontendConnection();
102102

103103
void bringToFront();

Tools/WebKitTestRunner/InjectedBundle/TestRunner.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -491,7 +491,7 @@ void TestRunner::makeWindowObject(JSContextRef context)
491491

492492
void TestRunner::showWebInspector()
493493
{
494-
WKBundlePageShowInspectorForTest(page());
494+
postMessage("ShowWebInspector");
495495
}
496496

497497
void TestRunner::closeWebInspector()

Tools/WebKitTestRunner/TestInvocation.cpp

+4
Original file line numberDiff line numberDiff line change
@@ -714,6 +714,10 @@ void TestInvocation::didReceiveMessageFromInjectedBundle(WKStringRef messageName
714714

715715
if (WKStringIsEqualToUTF8CString(messageName, "RequestExitFullscreenFromUIProcess")) {
716716
TestController::singleton().requestExitFullscreenFromUIProcess(TestController::singleton().mainWebView()->page());
717+
}
718+
719+
if (WKStringIsEqualToUTF8CString(messageName, "ShowWebInspector")) {
720+
WKPageShowWebInspectorForTesting(TestController::singleton().mainWebView()->page());
717721
return;
718722
}
719723

0 commit comments

Comments
 (0)