Skip to content

Commit 3fd2ed5

Browse files
Implement mixed content whitelist
API is exposed via WebKitWebExtension, similiar to the CORS whitelist. Wildcards can be used, with '*' replacing any string
1 parent 695c76a commit 3fd2ed5

6 files changed

Lines changed: 143 additions & 1 deletion

File tree

Source/WebCore/loader/MixedContentChecker.cpp

Lines changed: 84 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,60 @@
4747

4848
namespace WebCore {
4949

50-
static bool isMixedContent(const Document& document, const URL& url)
50+
static WTF::Vector<WTF::KeyValuePair<WTF::String, WTF::String>> m_whitelist = {};
51+
52+
static bool wildcardMatch(const String& pattern, const String& url)
53+
{
54+
int patternLen = pattern.length();
55+
int patternPos = 0;
56+
int urlLen = url.length();
57+
int urlPos = 0;
58+
int wildcardPos = -1;
59+
int wildcardMatchEnd = 0;
60+
61+
while (urlPos < urlLen) {
62+
if (patternPos < patternLen && pattern[patternPos] == url[urlPos]) {
63+
// characters match
64+
patternPos++;
65+
urlPos++;
66+
} else if (patternPos < patternLen && pattern[patternPos] == '*') {
67+
// mark wildcard position, start matching the rest of the pattern
68+
wildcardPos = patternPos;
69+
wildcardMatchEnd = urlPos;
70+
patternPos++;
71+
} else if (wildcardPos != -1) {
72+
// no match, but we have a wildcard - assume wildcard handles a match to this position,
73+
// revert patternPos to after last *
74+
patternPos = wildcardPos + 1;
75+
wildcardMatchEnd++;
76+
urlPos = wildcardMatchEnd;
77+
} else {
78+
// no match, no wildcard - pattern does not match
79+
return false;
80+
}
81+
}
82+
83+
// url matches so far, and we're at the end of it
84+
// skip any remaining wildcards
85+
while (patternPos < patternLen && pattern[patternPos] == '*') {
86+
patternPos++;
87+
}
88+
// if we're at the end of pattern, that's a match
89+
// otherwise, the remaining part of the pattern can't be matched
90+
return patternPos == patternLen;
91+
}
92+
93+
static bool isWhitelisted(const String& origin, const String& domain)
94+
{
95+
for (auto kvPair : m_whitelist) {
96+
if (wildcardMatch(kvPair.key, origin) && wildcardMatch(kvPair.value, domain)) {
97+
return true;
98+
}
99+
}
100+
return false;
101+
}
102+
103+
static bool isMixedContent(const Document& document, const WTF::URL& url)
51104
{
52105
// FIXME: Use document.isSecureContext(), instead of comparing against "https" scheme, when all ports stop using loopback in LayoutTests
53106
// sandboxed iframes have an opaque origin so we should perform the mixed content check considering the origin
@@ -124,6 +177,11 @@ static bool frameAndAncestorsCanDisplayInsecureContent(LocalFrame& frame, MixedC
124177
return true;
125178

126179
RefPtr document = frame.document();
180+
if (isWhitelisted(document->securityOrigin().toString(), url.protocolHostAndPort())) {
181+
logConsoleWarning(frame, true, "display"_s, url);
182+
return true;
183+
}
184+
127185
if (!document->checkedContentSecurityPolicy()->allowRunningOrDisplayingInsecureContent(url))
128186
return false;
129187

@@ -146,6 +204,11 @@ bool MixedContentChecker::frameAndAncestorsCanRunInsecureContent(LocalFrame& fra
146204
if (!foundMixedContentInFrameTree(frame, url))
147205
return true;
148206

207+
if (isWhitelisted(securityOrigin.toString(), url.protocolHostAndPort())) {
208+
logConsoleWarning(frame, true, "run"_s, url);
209+
return true;
210+
}
211+
149212
RefPtr document = frame.document();
150213
if (!document->checkedContentSecurityPolicy()->allowRunningOrDisplayingInsecureContent(url))
151214
return false;
@@ -242,5 +305,25 @@ void MixedContentChecker::checkFormForMixedContent(LocalFrame& frame, const URL&
242305

243306
frame.checkedLoader()->client().didDisplayInsecureContent();
244307
}
308+
void MixedContentChecker::addMixedContentWhitelistEntry(const String& origin, const String& domain)
309+
{
310+
m_whitelist.append(makeKeyValuePair(origin, domain));
311+
}
312+
313+
void MixedContentChecker::removeMixedContentWhitelistEntry(const String& origin, const String& domain)
314+
{
315+
for (size_t i = 0; i < m_whitelist.size(); i++) {
316+
if (m_whitelist[i].key == origin && m_whitelist[i].value == domain) {
317+
m_whitelist.remove(i);
318+
break;
319+
}
320+
}
321+
}
245322

323+
void MixedContentChecker::resetMixedContentWhitelist()
324+
{
325+
m_whitelist.clear();
326+
327+
}
246328
} // namespace WebCore
329+

Source/WebCore/loader/MixedContentChecker.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@
3131

3232
#include "FetchOptions.h"
3333
#include <wtf/Forward.h>
34+
#include <wtf/KeyValuePair.h>
35+
#include <wtf/Vector.h>
3436

3537
namespace WebCore {
3638

@@ -55,5 +57,9 @@ bool shouldBlockRequestForDisplayableContent(LocalFrame&, const URL&, ContentTyp
5557
bool shouldBlockRequestForRunnableContent(LocalFrame&, SecurityOrigin&, const URL&, ShouldLogWarning = ShouldLogWarning::Yes);
5658
void checkFormForMixedContent(LocalFrame&, const URL&);
5759

60+
void addMixedContentWhitelistEntry(const String& origin, const String& domain);
61+
void removeMixedContentWhitelistEntry(const String& origin, const String& domain);
62+
void resetMixedContentWhitelist();
63+
5864
} // namespace MixedContentChecker
5965
} // namespace WebCore

Source/WebKit/WebProcess/InjectedBundle/API/glib/WebKitWebExtension.cpp

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -274,6 +274,27 @@ void webkit_web_extension_reset_origin_access_whitelists(WebKitWebExtension* ext
274274
extension->priv->bundle->resetOriginAccessAllowLists();
275275
}
276276

277+
void webkit_web_extension_add_mixed_content_whitelist_entry(WebKitWebExtension *extension, const gchar* origin, const gchar* domain)
278+
{
279+
g_return_if_fail(WEBKIT_IS_WEB_EXTENSION(extension));
280+
281+
extension->priv->bundle->addMixedContentWhitelistEntry(String::fromUTF8(origin), String::fromUTF8(domain));
282+
}
283+
284+
void webkit_web_extension_remove_mixed_content_whitelist_entry(WebKitWebExtension *extension, const gchar* origin, const gchar* domain)
285+
{
286+
g_return_if_fail(WEBKIT_IS_WEB_EXTENSION(extension));
287+
288+
extension->priv->bundle->removeMixedContentWhitelistEntry(String::fromUTF8(origin), String::fromUTF8(domain));
289+
}
290+
291+
void webkit_web_extension_reset_mixed_content_whitelist_entry(WebKitWebExtension *extension)
292+
{
293+
g_return_if_fail(WEBKIT_IS_WEB_EXTENSION(extension));
294+
295+
extension->priv->bundle->resetMixedContentWhitelist();
296+
}
297+
277298
/**
278299
* webkit_web_extension_send_message_to_context:
279300
* @extension: a #WebKitWebExtension

Source/WebKit/WebProcess/InjectedBundle/API/glib/WebKitWebExtension.h.in

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,19 @@ webkit_web_extension_remove_origin_access_whitelist_entry (WebKitWebExtension
9292
WEBKIT_API void
9393
webkit_web_extension_reset_origin_access_whitelists (WebKitWebExtension *extension);
9494

95+
WEBKIT_API void
96+
webkit_web_extension_add_mixed_content_whitelist_entry (WebKitWebExtension *extension,
97+
const gchar *origin,
98+
const gchar *domain);
99+
100+
WEBKIT_API void
101+
webkit_web_extension_remove_mixed_content_whitelist_entry (WebKitWebExtension *extension,
102+
const gchar *origin,
103+
const gchar *domain);
104+
105+
WEBKIT_API void
106+
webkit_web_extension_reset_mixed_content_whitelist_entry (WebKitWebExtension *extension);
107+
95108
WEBKIT_API void
96109
webkit_web_extension_send_message_to_context (WebKitWebExtension *extension,
97110
WebKitUserMessage *message,

Source/WebKit/WebProcess/InjectedBundle/InjectedBundle.cpp

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,8 @@
8080
#include <wtf/ProcessPrivilege.h>
8181
#include <wtf/SystemTracing.h>
8282

83+
#include <WebCore/MixedContentChecker.h>
84+
8385
#if ENABLE(NOTIFICATIONS)
8486
#include "WebNotificationManager.h"
8587
#endif
@@ -165,6 +167,20 @@ void InjectedBundle::resetOriginAccessAllowLists()
165167
WebProcess::singleton().ensureNetworkProcessConnection().connection().send(Messages::NetworkConnectionToWebProcess::ResetOriginAccessAllowLists { }, 0);
166168
}
167169

170+
void InjectedBundle::addMixedContentWhitelistEntry(const String& origin, const String& domain)
171+
{
172+
MixedContentChecker::addMixedContentWhitelistEntry(origin, domain);
173+
}
174+
175+
void InjectedBundle::removeMixedContentWhitelistEntry(const String& origin, const String& domain)
176+
{
177+
MixedContentChecker::removeMixedContentWhitelistEntry(origin, domain);
178+
}
179+
void InjectedBundle::resetMixedContentWhitelist()
180+
{
181+
MixedContentChecker::resetMixedContentWhitelist();
182+
}
183+
168184
void InjectedBundle::setAsynchronousSpellCheckingEnabled(bool enabled)
169185
{
170186
Page::forEachPage([enabled](Page& page) {

Source/WebKit/WebProcess/InjectedBundle/InjectedBundle.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,9 @@ class InjectedBundle : public API::ObjectImpl<API::Object::Type::Bundle> {
9797
void addOriginAccessAllowListEntry(const String&, const String&, const String&, bool);
9898
void removeOriginAccessAllowListEntry(const String&, const String&, const String&, bool);
9999
void resetOriginAccessAllowLists();
100+
void addMixedContentWhitelistEntry(const String&, const String&);
101+
void removeMixedContentWhitelistEntry(const String&, const String&);
102+
void resetMixedContentWhitelist();
100103
void setAsynchronousSpellCheckingEnabled(bool);
101104
int numberOfPages(WebFrame*, double, double);
102105
int pageNumberForElementById(WebFrame*, const String&, double, double);

0 commit comments

Comments
 (0)