Implement actual policy
This commit is contained in:
@@ -91,7 +91,7 @@ extension BrowserViewController: WKNavigationDelegate, WKUIDelegate
|
|||||||
var allowJavaScript = tab.javaScriptEnabled
|
var allowJavaScript = tab.javaScriptEnabled
|
||||||
if !allowJavaScript, let host = navigationAction.request.url?.host {
|
if !allowJavaScript, let host = navigationAction.request.url?.host {
|
||||||
// Check origin policy
|
// Check origin policy
|
||||||
allowJavaScript = policyManager.allowedOriginsForScriptResources().contains(host)
|
allowJavaScript = policyManager.scriptPolicy(forOrigin: host).allowsEmbeddedJavaScript()
|
||||||
}
|
}
|
||||||
|
|
||||||
preferences.allowsContentJavaScript = allowJavaScript
|
preferences.allowsContentJavaScript = allowJavaScript
|
||||||
|
|||||||
@@ -84,9 +84,11 @@
|
|||||||
_processPool = [[WKProcessPool alloc] _initWithConfiguration:poolConfiguration];
|
_processPool = [[WKProcessPool alloc] _initWithConfiguration:poolConfiguration];
|
||||||
|
|
||||||
// Initialize allowed origins now
|
// Initialize allowed origins now
|
||||||
|
NSDictionary<NSString *, NSNumber *> *policies = [_policyDataSource scriptPolicyTypeByOrigin];
|
||||||
NSArray<NSString *> *allowedOrigins = [[_policyDataSource allowedOriginsForScriptResources] allObjects];
|
NSArray<NSString *> *allowedOrigins = [[_policyDataSource allowedOriginsForScriptResources] allObjects];
|
||||||
[_processPool _setObject:allowedOrigins forBundleParameter:SBRGetAllowedOriginsKey()];
|
[_processPool _setObject:allowedOrigins forBundleParameter:SBRGetAllowedOriginsKey()];
|
||||||
[_processPool _setObject:@(_allowAllScripts) forBundleParameter:SBRGetAllScriptsAllowedKey()];
|
[_processPool _setObject:@(_allowAllScripts) forBundleParameter:SBRGetAllScriptsAllowedKey()];
|
||||||
|
[_processPool _setObject:policies forBundleParameter:SBRGetPolicyTypeByOriginKey()];
|
||||||
|
|
||||||
webViewConfiguration.processPool = _processPool;
|
webViewConfiguration.processPool = _processPool;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,6 +35,12 @@ NS_SWIFT_NAME(ScriptPolicy)
|
|||||||
|
|
||||||
- (instancetype)initWithSecurityOrigin:(NSString *)origin policyType:(SBRScriptOriginPolicyType)policyType;
|
- (instancetype)initWithSecurityOrigin:(NSString *)origin policyType:(SBRScriptOriginPolicyType)policyType;
|
||||||
|
|
||||||
|
/// Returns YES if policy type allows embedded (<script>) JavaScript for the given origin.
|
||||||
|
- (BOOL)allowsEmbeddedJavaScript;
|
||||||
|
|
||||||
|
/// Returns YES if policy allows loading external JavaScript resource with the security origin `resourceOrigin`.
|
||||||
|
- (BOOL)allowsExternalJavaScriptResourceOrigin:(NSString *)resourceOrigin;
|
||||||
|
|
||||||
- (instancetype)init NS_UNAVAILABLE;
|
- (instancetype)init NS_UNAVAILABLE;
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
|||||||
@@ -12,6 +12,32 @@
|
|||||||
|
|
||||||
@implementation SBRScriptPolicy
|
@implementation SBRScriptPolicy
|
||||||
|
|
||||||
|
+ (NSSet<NSString *> *)_commonCDNOrigins {
|
||||||
|
static dispatch_once_t onceToken;
|
||||||
|
static NSSet<NSString *> *whitelist = nil;
|
||||||
|
dispatch_once(&onceToken, ^{
|
||||||
|
whitelist = [NSSet setWithArray:@[
|
||||||
|
// JSDelivr: Used by bootstrap
|
||||||
|
@"cdn.jsdelivr.net",
|
||||||
|
|
||||||
|
// SourceForge
|
||||||
|
@"fsdn.net",
|
||||||
|
|
||||||
|
// Open Source CDNs
|
||||||
|
@"cdnjs.com",
|
||||||
|
@"osscdn.com",
|
||||||
|
|
||||||
|
// JQuery
|
||||||
|
@"code.jquery.com",
|
||||||
|
|
||||||
|
// Bootstrap
|
||||||
|
@"bootstrapcdn.com",
|
||||||
|
]];
|
||||||
|
});
|
||||||
|
|
||||||
|
return whitelist;
|
||||||
|
}
|
||||||
|
|
||||||
+ (NSString *)titleForPolicyType:(SBRScriptOriginPolicyType)policyType
|
+ (NSString *)titleForPolicyType:(SBRScriptOriginPolicyType)policyType
|
||||||
{
|
{
|
||||||
switch (policyType) {
|
switch (policyType) {
|
||||||
@@ -118,4 +144,62 @@
|
|||||||
[coder encodeObject:[NSNumber numberWithInteger:_policyType] forKey:@"policyType"];
|
[coder encodeObject:[NSNumber numberWithInteger:_policyType] forKey:@"policyType"];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (BOOL)allowsEmbeddedJavaScript
|
||||||
|
{
|
||||||
|
return _policyType > SBRScriptOriginPolicyTypeAlpha;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (BOOL)allowsExternalJavaScriptResourceOrigin:(NSString *)resourceOrigin
|
||||||
|
{
|
||||||
|
switch (_policyType) {
|
||||||
|
case SBRScriptOriginPolicyTypeAlpha:
|
||||||
|
// No scripts allowed whatsoever.
|
||||||
|
return NO;
|
||||||
|
case SBRScriptOriginPolicyTypeBravo:
|
||||||
|
// Only allows on-page scripts, no external scripts whatsoever.
|
||||||
|
return NO;
|
||||||
|
case SBRScriptOriginPolicyTypeCharlie:
|
||||||
|
// Only allow scripts from the exact same origin as the host.
|
||||||
|
return [_origin isEqualToString:resourceOrigin];
|
||||||
|
case SBRScriptOriginPolicyTypeDelta: {
|
||||||
|
// Allow scripts from common CDNs, or CDNs that *appear* to belong to the host.
|
||||||
|
if ([[[self class] _commonCDNOrigins] containsObject:resourceOrigin]) {
|
||||||
|
return YES;
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOL looksLikeCDN = NO;
|
||||||
|
NSArray<NSString *> *hostOriginComponents = [_origin componentsSeparatedByString:@"."];
|
||||||
|
if ([hostOriginComponents count] > 1) {
|
||||||
|
// Assume our "family" name is just before the TLD.
|
||||||
|
NSString *hostFamilyName = [hostOriginComponents objectAtIndex:(hostOriginComponents.count - 2)];
|
||||||
|
looksLikeCDN = [resourceOrigin containsString:hostFamilyName];
|
||||||
|
|
||||||
|
if (!looksLikeCDN) {
|
||||||
|
NSArray<NSString *> *resourceOriginComponents = [resourceOrigin componentsSeparatedByString:@"."];
|
||||||
|
if ([resourceOriginComponents count] > 1) {
|
||||||
|
NSString *resourceFamilyName = [resourceOriginComponents objectAtIndex:(resourceOriginComponents.count - 2)];
|
||||||
|
|
||||||
|
// Check and see if the subdomain is "cdn", but also make sure we're not accidentally allowing an ad network CDN.
|
||||||
|
looksLikeCDN = [[resourceOriginComponents firstObject] isEqualToString:@"cdn"];
|
||||||
|
|
||||||
|
BOOL looksLikeAdNetwork = (
|
||||||
|
[resourceFamilyName hasSuffix:@"ad"] ||
|
||||||
|
[resourceFamilyName hasSuffix:@"ads"] ||
|
||||||
|
[resourceFamilyName containsString:@"analy"] // "analy-tics"
|
||||||
|
);
|
||||||
|
looksLikeCDN = looksLikeCDN && !looksLikeAdNetwork;
|
||||||
|
|
||||||
|
NSLog(@"SBRProcessPlugin: [%@] CDN:%@ AD:%@", resourceOriginComponents, looksLikeCDN ? @"YES" : @"NO", looksLikeAdNetwork ? @"YES" : @"NO");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return looksLikeCDN;
|
||||||
|
}
|
||||||
|
case SBRScriptOriginPolicyTypeEcho:
|
||||||
|
// Anything goes.
|
||||||
|
return YES;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|||||||
@@ -59,6 +59,7 @@
|
|||||||
CD853BD124E778B800D2BDCC /* History.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = CD853BCF24E778B800D2BDCC /* History.xcdatamodeld */; };
|
CD853BD124E778B800D2BDCC /* History.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = CD853BCF24E778B800D2BDCC /* History.xcdatamodeld */; };
|
||||||
CD853BD424E77BF900D2BDCC /* HistoryItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD853BD324E77BF900D2BDCC /* HistoryItem.swift */; };
|
CD853BD424E77BF900D2BDCC /* HistoryItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD853BD324E77BF900D2BDCC /* HistoryItem.swift */; };
|
||||||
CD97CF9225D5BE6F00288FEE /* NavigationControlsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD97CF9125D5BE6F00288FEE /* NavigationControlsView.swift */; };
|
CD97CF9225D5BE6F00288FEE /* NavigationControlsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD97CF9125D5BE6F00288FEE /* NavigationControlsView.swift */; };
|
||||||
|
CD9B88C2272201E900DAAB7E /* SBRScriptPolicy.m in Sources */ = {isa = PBXBuildFile; fileRef = CD361CF5271A3718006E9CA5 /* SBRScriptPolicy.m */; };
|
||||||
CDAD9CE8263A2DF200FF7199 /* DocumentControlsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CDAD9CE7263A2DF200FF7199 /* DocumentControlsView.swift */; };
|
CDAD9CE8263A2DF200FF7199 /* DocumentControlsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CDAD9CE7263A2DF200FF7199 /* DocumentControlsView.swift */; };
|
||||||
CDAD9CEA263A318F00FF7199 /* ShareableURL.swift in Sources */ = {isa = PBXBuildFile; fileRef = CDAD9CE9263A318F00FF7199 /* ShareableURL.swift */; };
|
CDAD9CEA263A318F00FF7199 /* ShareableURL.swift in Sources */ = {isa = PBXBuildFile; fileRef = CDAD9CE9263A318F00FF7199 /* ShareableURL.swift */; };
|
||||||
CDC4A1CF25E9D8F7007D33C6 /* Tagger.js in Resources */ = {isa = PBXBuildFile; fileRef = CDC4A1CE25E9D8F7007D33C6 /* Tagger.js */; };
|
CDC4A1CF25E9D8F7007D33C6 /* Tagger.js in Resources */ = {isa = PBXBuildFile; fileRef = CDC4A1CE25E9D8F7007D33C6 /* Tagger.js */; };
|
||||||
@@ -631,6 +632,7 @@
|
|||||||
isa = PBXSourcesBuildPhase;
|
isa = PBXSourcesBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
|
CD9B88C2272201E900DAAB7E /* SBRScriptPolicy.m in Sources */,
|
||||||
1ADFF4AA24C8D477006DC7AE /* SBRProcessPlugin.m in Sources */,
|
1ADFF4AA24C8D477006DC7AE /* SBRProcessPlugin.m in Sources */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
|||||||
@@ -9,11 +9,13 @@
|
|||||||
|
|
||||||
#import "SBRWebProcessDelegate.h"
|
#import "SBRWebProcessDelegate.h"
|
||||||
#import "SBRWebProcessProxy.h"
|
#import "SBRWebProcessProxy.h"
|
||||||
|
#import "SBRScriptPolicy.h"
|
||||||
|
|
||||||
#import <WebKit/_WKRemoteObjectInterface.h>
|
#import <WebKit/_WKRemoteObjectInterface.h>
|
||||||
#import <WebKit/_WKRemoteObjectRegistry.h>
|
#import <WebKit/_WKRemoteObjectRegistry.h>
|
||||||
#import <WebKit/WKWebProcessPlugInBrowserContextController.h>
|
#import <WebKit/WKWebProcessPlugInBrowserContextController.h>
|
||||||
#import <WebKit/WKWebProcessPlugInBrowserContextControllerPrivate.h>
|
#import <WebKit/WKWebProcessPlugInBrowserContextControllerPrivate.h>
|
||||||
|
#import <WebKit/WKWebProcessPlugInFrame.h>
|
||||||
#import <WebKit/WKWebProcessPlugInLoadDelegate.h>
|
#import <WebKit/WKWebProcessPlugInLoadDelegate.h>
|
||||||
|
|
||||||
@interface SBRProcessPlugin () <WKWebProcessPlugInLoadDelegate, SBRWebProcessProxy>
|
@interface SBRProcessPlugin () <WKWebProcessPlugInLoadDelegate, SBRWebProcessProxy>
|
||||||
@@ -61,6 +63,7 @@
|
|||||||
{
|
{
|
||||||
_allowedResourceOrigins = [[plugInController parameters] valueForKey:SBRGetAllowedOriginsKey()];
|
_allowedResourceOrigins = [[plugInController parameters] valueForKey:SBRGetAllowedOriginsKey()];
|
||||||
_allScriptsAllowed = [[[plugInController parameters] valueForKey:SBRGetAllScriptsAllowedKey()] boolValue];
|
_allScriptsAllowed = [[[plugInController parameters] valueForKey:SBRGetAllScriptsAllowedKey()] boolValue];
|
||||||
|
_policyTypeByOrigin = [[plugInController parameters] valueForKey:SBRGetPolicyTypeByOriginKey()];
|
||||||
NSLog(@"SBRProcessPlugin: %lu origins allowed, all scripts allowed: %@ ", (unsigned long)_allowedResourceOrigins.count, _allScriptsAllowed ? @"YES" : @"NO");
|
NSLog(@"SBRProcessPlugin: %lu origins allowed, all scripts allowed: %@ ", (unsigned long)_allowedResourceOrigins.count, _allScriptsAllowed ? @"YES" : @"NO");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -87,15 +90,20 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
NSURL *requestURL = [request URL];
|
NSURL *requestURL = [request URL];
|
||||||
NSString *originString = [requestURL host];
|
NSString *resourceOrigin = [requestURL host];
|
||||||
NSString *requestExtension = [requestURL pathExtension];
|
NSString *requestExtension = [requestURL pathExtension];
|
||||||
|
NSString *hostOrigin = [[[controller mainFrame] URL] host];
|
||||||
if (requestExtension.length > 0 && [requestExtension isEqualToString:@"js"]) {
|
if (requestExtension.length > 0 && [requestExtension isEqualToString:@"js"]) {
|
||||||
if ([self allScriptsAllowed] || [_allowedResourceOrigins containsObject:originString]) {
|
NSNumber *policyType = [_policyTypeByOrigin objectForKey:hostOrigin];
|
||||||
NSLog(@"SBRProcessPlugin: Allowing whitelisted requestURL: %@", requestURL);
|
NSLog(@"SBRProcessPlugin: Policy type for %@: %@", hostOrigin, policyType);
|
||||||
[[self processDelegate] webProcessDidAllowScriptWithOrigin:originString];
|
|
||||||
|
SBRScriptPolicy *policy = [[SBRScriptPolicy alloc] initWithSecurityOrigin:hostOrigin policyType:[policyType integerValue]];
|
||||||
|
if ([self allScriptsAllowed] || [policy allowsExternalJavaScriptResourceOrigin:resourceOrigin]) {
|
||||||
|
NSLog(@"SBRProcessPlugin: Policy allows script requestURL: %@", requestURL);
|
||||||
|
[[self processDelegate] webProcessDidAllowScriptWithOrigin:resourceOrigin];
|
||||||
} else {
|
} else {
|
||||||
NSLog(@"SBRProcessPlugin: Blocking requestURL: %@", requestURL);
|
NSLog(@"SBRProcessPlugin: Policy disallows script requestURL: %@", requestURL);
|
||||||
[[self processDelegate] webProcessDidBlockScriptWithOrigin:originString];
|
[[self processDelegate] webProcessDidBlockScriptWithOrigin:resourceOrigin];
|
||||||
|
|
||||||
request = nil;
|
request = nil;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user