Skip to content

Commit 6ffed06

Browse files
jacek-manko-redspenap
authored andcommitted
Implement mixed content whitelist
API is exposed via WebKitWebExtension, similiar to the CORS whitelist. Wildcards can be used, with '*' replacing any string
1 parent cc3e02d commit 6ffed06

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
@@ -48,7 +48,60 @@
4848

4949
namespace WebCore {
5050

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

129182
RefPtr document = frame.document();
183+
if (isWhitelisted(document->securityOrigin().toString(), url.protocolHostAndPort())) {
184+
logConsoleWarning(frame, true, "display"_s, url);
185+
return true;
186+
}
187+
130188
if (!document->checkedContentSecurityPolicy()->allowRunningOrDisplayingInsecureContent(url))
131189
return false;
132190

@@ -149,6 +207,11 @@ bool MixedContentChecker::frameAndAncestorsCanRunInsecureContent(LocalFrame& fra
149207
if (!foundMixedContentInFrameTree(frame, url))
150208
return true;
151209

210+
if (isWhitelisted(securityOrigin.toString(), url.protocolHostAndPort())) {
211+
logConsoleWarning(frame, true, "run"_s, url);
212+
return true;
213+
}
214+
152215
RefPtr document = frame.document();
153216
if (!document->checkedContentSecurityPolicy()->allowRunningOrDisplayingInsecureContent(url))
154217
return false;
@@ -260,5 +323,25 @@ void MixedContentChecker::checkFormForMixedContent(LocalFrame& frame, const URL&
260323

261324
frame.protectedLoader()->client().didDisplayInsecureContent();
262325
}
326+
void MixedContentChecker::addMixedContentWhitelistEntry(const String& origin, const String& domain)
327+
{
328+
m_whitelist.append(makeKeyValuePair(origin, domain));
329+
}
330+
331+
void MixedContentChecker::removeMixedContentWhitelistEntry(const String& origin, const String& domain)
332+
{
333+
for (size_t i = 0; i < m_whitelist.size(); i++) {
334+
if (m_whitelist[i].key == origin && m_whitelist[i].value == domain) {
335+
m_whitelist.removeAt(i);
336+
break;
337+
}
338+
}
339+
}
263340

341+
void MixedContentChecker::resetMixedContentWhitelist()
342+
{
343+
m_whitelist.clear();
344+
345+
}
264346
} // namespace WebCore
347+

Source/WebCore/loader/MixedContentChecker.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@
3232
#include "FetchOptions.h"
3333
#include "ResourceLoaderOptions.h"
3434
#include <wtf/Forward.h>
35+
#include <wtf/KeyValuePair.h>
36+
#include <wtf/Vector.h>
3537

3638
namespace WebCore {
3739

@@ -58,5 +60,9 @@ void checkFormForMixedContent(LocalFrame&, const URL&);
5860

5961
WEBCORE_EXPORT bool canModifyRequest(const URL&, FetchOptions::Destination, Initiator);
6062

63+
void addMixedContentWhitelistEntry(const String& origin, const String& domain);
64+
void removeMixedContentWhitelistEntry(const String& origin, const String& domain);
65+
void resetMixedContentWhitelist();
66+
6167
} // namespace MixedContentChecker
6268
} // 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
@@ -78,6 +78,8 @@
7878
#include <wtf/ProcessPrivilege.h>
7979
#include <wtf/SystemTracing.h>
8080

81+
#include <WebCore/MixedContentChecker.h>
82+
8183
#if ENABLE(NOTIFICATIONS)
8284
#include "WebNotificationManager.h"
8385
#endif
@@ -158,6 +160,20 @@ void InjectedBundle::resetOriginAccessAllowLists()
158160
WebProcess::singleton().ensureNetworkProcessConnection().connection().send(Messages::NetworkConnectionToWebProcess::ResetOriginAccessAllowLists { }, 0);
159161
}
160162

163+
void InjectedBundle::addMixedContentWhitelistEntry(const String& origin, const String& domain)
164+
{
165+
MixedContentChecker::addMixedContentWhitelistEntry(origin, domain);
166+
}
167+
168+
void InjectedBundle::removeMixedContentWhitelistEntry(const String& origin, const String& domain)
169+
{
170+
MixedContentChecker::removeMixedContentWhitelistEntry(origin, domain);
171+
}
172+
void InjectedBundle::resetMixedContentWhitelist()
173+
{
174+
MixedContentChecker::resetMixedContentWhitelist();
175+
}
176+
161177
void InjectedBundle::setAsynchronousSpellCheckingEnabled(bool enabled)
162178
{
163179
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
@@ -94,6 +94,9 @@ class InjectedBundle : public API::ObjectImpl<API::Object::Type::Bundle> {
9494
void addOriginAccessAllowListEntry(const String&, const String&, const String&, bool);
9595
void removeOriginAccessAllowListEntry(const String&, const String&, const String&, bool);
9696
void resetOriginAccessAllowLists();
97+
void addMixedContentWhitelistEntry(const String&, const String&);
98+
void removeMixedContentWhitelistEntry(const String&, const String&);
99+
void resetMixedContentWhitelist();
97100
void setAsynchronousSpellCheckingEnabled(bool);
98101
int numberOfPages(WebFrame*, double, double);
99102
int pageNumberForElementById(WebFrame*, const String&, double, double);

0 commit comments

Comments
 (0)