Skip to content

Commit

Permalink
Support content security policy in Web Extensions.
Browse files Browse the repository at this point in the history
https://webkit.org/b/246490
rdar://problem/114823298

Reviewed by Brent Fulgham.

* Source/WebCore/en.lproj/Localizable.strings: Updated.
* Source/WebKit/UIProcess/Extensions/Cocoa/WebExtensionCocoa.mm:
(WebKit::toAPI):
(WebKit::WebExtension::createError):
(WebKit::WebExtension::errors):
(WebKit::WebExtension::contentSecurityPolicy): Added.
(WebKit::WebExtension::populateContentSecurityPolicyStringsIfNeeded): Added.
* Source/WebKit/UIProcess/Extensions/Cocoa/WebExtensionContextCocoa.mm:
(WebKit::WebExtensionContext::webViewConfiguration): Set _contentSecurityPolicyModeForExtension.
* Source/WebKit/UIProcess/Extensions/Cocoa/WebExtensionURLSchemeHandlerCocoa.mm:
(WebKit::WebExtensionURLSchemeHandler::platformStartTask): Set Content-Security-Policy header.
* Source/WebKit/UIProcess/Extensions/WebExtension.h:
* Tools/TestWebKitAPI/Tests/WebKitCocoa/WKWebExtension.mm:
(TestWebKitAPI::TEST):
* Tools/TestWebKitAPI/Tests/WebKitCocoa/WKWebExtensionController.mm:
(TestWebKitAPI::TEST):

Canonical link: https://commits.webkit.org/270225@main
  • Loading branch information
xeenon committed Nov 4, 2023
1 parent 9f73743 commit 8e6019e
Show file tree
Hide file tree
Showing 11 changed files with 254 additions and 63 deletions.
3 changes: 3 additions & 0 deletions Source/WebCore/en.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -481,6 +481,9 @@
/* WKWebExtensionErrorInvalidManifestEntry description for content_scripts */
"Empty or invalid `content_scripts` manifest entry." = "Empty or invalid `content_scripts` manifest entry.";

/* WKWebExtensionErrorInvalidManifestEntry description for content_security_policy */
"Empty or invalid `content_security_policy` manifest entry." = "Empty or invalid `content_security_policy` manifest entry.";

/* WKWebExtensionErrorInvalidManifestEntry description for default_icon in action only */
"Empty or invalid `default_icon` for the `action` manifest entry." = "Empty or invalid `default_icon` for the `action` manifest entry.";

Expand Down
44 changes: 44 additions & 0 deletions Source/WebKit/UIProcess/Extensions/Cocoa/WebExtensionCocoa.mm
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,9 @@
static NSString * const browserURLOverridesManifestKey = @"browser_url_overrides";
static NSString * const newTabManifestKey = @"newtab";

static NSString * const contentSecurityPolicyManifestKey = @"content_security_policy";
static NSString * const contentSecurityPolicyExtensionPagesManifestKey = @"extension_pages";

WebExtension::WebExtension(NSBundle *appExtensionBundle, NSError **outError)
: m_bundle(appExtensionBundle)
, m_resourceBaseURL(appExtensionBundle.resourceURL.URLByStandardizingPath.absoluteURL)
Expand Down Expand Up @@ -452,6 +455,8 @@ static _WKWebExtensionError toAPI(WebExtension::Error error)
return _WKWebExtensionErrorInvalidManifestEntry;
case WebExtension::Error::InvalidContentScripts:
return _WKWebExtensionErrorInvalidManifestEntry;
case WebExtension::Error::InvalidContentSecurityPolicy:
return _WKWebExtensionErrorInvalidManifestEntry;
case WebExtension::Error::InvalidDeclarativeNetRequest:
return _WKWebExtensionErrorInvalidDeclarativeNetRequestEntry;
case WebExtension::Error::InvalidDescription:
Expand Down Expand Up @@ -528,6 +533,10 @@ static _WKWebExtensionError toAPI(WebExtension::Error error)
localizedDescription = WEB_UI_STRING("Empty or invalid `content_scripts` manifest entry.", "WKWebExtensionErrorInvalidManifestEntry description for content_scripts");
break;

case Error::InvalidContentSecurityPolicy:
localizedDescription = WEB_UI_STRING("Empty or invalid `content_security_policy` manifest entry.", "WKWebExtensionErrorInvalidManifestEntry description for content_security_policy");
break;

case Error::InvalidDeclarativeNetRequest:
ALLOW_NONLITERAL_FORMAT_BEGIN
if (NSString *debugDescription = underlyingError.userInfo[NSDebugDescriptionErrorKey])
Expand Down Expand Up @@ -653,6 +662,7 @@ static _WKWebExtensionError toAPI(WebExtension::Error error)
populateContentScriptPropertiesIfNeeded();
populatePermissionsPropertiesIfNeeded();
populatePagePropertiesIfNeeded();
populateContentSecurityPolicyStringsIfNeeded();

return [m_errors copy] ?: @[ ];
}
Expand Down Expand Up @@ -746,6 +756,40 @@ static _WKWebExtensionError toAPI(WebExtension::Error error)
recordError(createError(Error::InvalidDescription));
}

NSString *WebExtension::contentSecurityPolicy()
{
populateContentSecurityPolicyStringsIfNeeded();
return m_contentSecurityPolicy.get();
}

void WebExtension::populateContentSecurityPolicyStringsIfNeeded()
{
if (!manifestParsedSuccessfully())
return;

if (m_parsedManifestContentSecurityPolicyStrings)
return;

m_parsedManifestContentSecurityPolicyStrings = true;

// Documentation: https://developer.mozilla.org/docs/Mozilla/Add-ons/WebExtensions/manifest.json/content_security_policy

if (supportsManifestVersion(3)) {
if (auto *policyDictionary = objectForKey<NSDictionary>(m_manifest, contentSecurityPolicyManifestKey, false)) {
m_contentSecurityPolicy = objectForKey<NSString>(policyDictionary, contentSecurityPolicyExtensionPagesManifestKey);
if (!m_contentSecurityPolicy && (!policyDictionary.count || policyDictionary[contentSecurityPolicyExtensionPagesManifestKey]))
recordError(createError(Error::InvalidContentSecurityPolicy));
}
} else {
m_contentSecurityPolicy = objectForKey<NSString>(m_manifest, contentSecurityPolicyManifestKey);
if (!m_contentSecurityPolicy && [m_manifest objectForKey:contentSecurityPolicyManifestKey])
recordError(createError(Error::InvalidContentSecurityPolicy));
}

if (!m_contentSecurityPolicy)
m_contentSecurityPolicy = @"script-src 'self'";
}

CocoaImage *WebExtension::icon(CGSize size)
{
if (!manifestParsedSuccessfully())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1850,7 +1850,10 @@ static _WKWebExtensionContextError toAPI(WebExtensionContext::Error error)
if (!isLoaded())
return nil;

bool isManifestVersion3 = extension().supportsManifestVersion(3);

WKWebViewConfiguration *configuration = [extensionController()->configuration().webViewConfiguration() copy];
configuration._contentSecurityPolicyModeForExtension = isManifestVersion3 ? _WKContentSecurityPolicyModeForExtensionManifestV3 : _WKContentSecurityPolicyModeForExtensionManifestV2;
configuration._corsDisablingPatterns = corsDisablingPatterns();
configuration._crossOriginAccessControlCheckEnabled = NO;
configuration._processDisplayName = processDisplayName();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -129,9 +129,9 @@
fileData = [stylesheetContents dataUsingEncoding:NSUTF8StringEncoding];
}

// FIXME: <https://webkit.org/b/246490> Include the Content-Security-Policy header for the extension.
NSHTTPURLResponse *urlResponse = [[NSHTTPURLResponse alloc] initWithURL:requestURL statusCode:200 HTTPVersion:nil headerFields:@{
@"Access-Control-Allow-Origin": @"*",
@"Content-Security-Policy": extensionContext->extension().contentSecurityPolicy(),
@"Content-Length": [NSString stringWithFormat:@"%zu", (size_t)fileData.length],
@"Content-Type": mimeType
}];
Expand Down
11 changes: 9 additions & 2 deletions Source/WebKit/UIProcess/Extensions/WebExtension.h
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ class WebExtension : public API::ObjectImpl<API::Object::Type::WebExtension>, pu
InvalidBackgroundContent,
InvalidBackgroundPersistence,
InvalidContentScripts,
InvalidContentSecurityPolicy,
InvalidDeclarativeNetRequest,
InvalidDescription,
InvalidExternallyConnectable,
Expand Down Expand Up @@ -175,6 +176,8 @@ class WebExtension : public API::ObjectImpl<API::Object::Type::WebExtension>, pu
NSString *displayDescription();
NSString *version();

NSString *contentSecurityPolicy();

CocoaImage *icon(CGSize idealSize);

CocoaImage *actionIcon(CGSize idealSize);
Expand Down Expand Up @@ -241,6 +244,7 @@ class WebExtension : public API::ObjectImpl<API::Object::Type::WebExtension>, pu
void populateContentScriptPropertiesIfNeeded();
void populatePermissionsPropertiesIfNeeded();
void populatePagePropertiesIfNeeded();
void populateContentSecurityPolicyStringsIfNeeded();

InjectedContentVector m_staticInjectedContents;

Expand Down Expand Up @@ -277,18 +281,21 @@ class WebExtension : public API::ObjectImpl<API::Object::Type::WebExtension>, pu
RetainPtr<NSString> m_displayActionLabel;
RetainPtr<NSString> m_actionPopupPath;

RetainPtr<NSString> m_contentSecurityPolicy;

RetainPtr<NSArray> m_backgroundScriptPaths;
RetainPtr<NSString> m_backgroundPagePath;
RetainPtr<NSString> m_backgroundServiceWorkerPath;
RetainPtr<NSString> m_generatedBackgroundContent;
bool m_backgroundContentIsPersistent : 1 { false };
bool m_backgroundPageUsesModules : 1 { false };

RetainPtr<NSString> m_optionsPagePath;
RetainPtr<NSString> m_overrideNewTabPagePath;

bool m_backgroundContentIsPersistent : 1 { false };
bool m_backgroundPageUsesModules : 1 { false };
bool m_parsedManifest : 1 { false };
bool m_parsedManifestDisplayStrings : 1 { false };
bool m_parsedManifestContentSecurityPolicyStrings : 1 { false };
bool m_parsedManifestActionProperties : 1 { false };
bool m_parsedManifestBackgroundProperties : 1 { false };
bool m_parsedManifestContentScriptProperties : 1 { false };
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ static inline void parseTargetInjectionOptions(NSDictionary *targetInfo, WebExte
Vector<WebExtensionFrameIdentifier> frames;
for (NSNumber *frameID in frameIDs) {
auto identifier = toWebExtensionFrameIdentifier(frameID.doubleValue);
if (!identifier) {
if (!isValid(identifier)) {
*outExceptionString = toErrorString(nil, frameIDsKey, @"'%@' is not a frame identifier", frameID);
return;
}
Expand Down Expand Up @@ -302,7 +302,7 @@ static inline void parseCSSInjectionOptions(NSDictionary *cssInfo, WebExtensionS

for (NSNumber *frameID in targetInfo[frameIDsKey]) {
auto identifier = toWebExtensionFrameIdentifier(frameID.doubleValue);
if (!identifier) {
if (!isValid(identifier)) {
*outExceptionString = toErrorString(nil, frameIDsKey, @"'%@' is not a frame identifier", frameID);
return false;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -462,9 +462,9 @@
if (!validateDictionary(options, sourceKey, nil, types, outExceptionString))
return false;

if (NSNumber *frameNumber = objectForKey<NSNumber>(options, frameIdKey)) {
auto identifier = toWebExtensionFrameIdentifier(frameNumber.doubleValue);
if (!identifier) {
if (NSNumber *frameID = objectForKey<NSNumber>(options, frameIdKey)) {
auto identifier = toWebExtensionFrameIdentifier(frameID.doubleValue);
if (!isValid(identifier)) {
*outExceptionString = toErrorString(nil, frameIdKey, @"it is not a frame identifier");
return false;
}
Expand Down Expand Up @@ -524,7 +524,7 @@

if (NSNumber *frameID = options[frameIdKey]) {
auto frameIdentifier = toWebExtensionFrameIdentifier(frameID.doubleValue);
if (!frameIdentifier) {
if (!isValid(frameIdentifier)) {
*outExceptionString = toErrorString(nil, frameIdKey, @"it is not a frame identifier");
return false;
}
Expand Down

0 comments on commit 8e6019e

Please sign in to comment.