Files
Attractor/App/Web Process Bundle Bridge/SBRScriptPolicy.m
2021-10-21 13:24:58 -07:00

206 lines
6.9 KiB
Objective-C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//
// SBRScriptPolicy.m
// App
//
// Created by James Magahern on 10/15/21.
//
#import "SBRScriptPolicy.h"
// For icon drawing
#import <UIKit/UIKit.h>
@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
{
switch (policyType) {
case SBRScriptOriginPolicyTypeAlpha:
return @"Alpha";
case SBRScriptOriginPolicyTypeBravo:
return @"Bravo";
case SBRScriptOriginPolicyTypeCharlie:
return @"Charlie";
case SBRScriptOriginPolicyTypeDelta:
return @"Delta";
case SBRScriptOriginPolicyTypeEcho:
return @"Echo";
}
}
+ (NSString *)localizedDescriptionForPolicyType:(SBRScriptOriginPolicyType)policyType
{
switch (policyType) {
case SBRScriptOriginPolicyTypeAlpha:
return @"All scripts blocked.";
case SBRScriptOriginPolicyTypeBravo:
return @"Scripts on page are allowed.";
case SBRScriptOriginPolicyTypeCharlie:
return @"Allow scripts from the same security origin.";
case SBRScriptOriginPolicyTypeDelta:
return @"Allow scripts from common and host CDNs.";
case SBRScriptOriginPolicyTypeEcho:
return @"All scripts are allowed.";
}
}
+ (UIImage *)iconRepresentationForPolicyType:(SBRScriptOriginPolicyType)policyType withSize:(CGSize)size
{
UIFont *font = [UIFont boldSystemFontOfSize:size.height - 2];
NSDictionary<NSAttributedStringKey, id> *attrs = @{
NSFontAttributeName : font,
NSForegroundColorAttributeName : [UIColor whiteColor]
};
const CGRect rect = (CGRect) { .origin = CGPointZero, .size = size };
UIGraphicsBeginImageContextWithOptions(size, NO, UIScreen.mainScreen.scale);
UIBezierPath *backgroundPath = [UIBezierPath bezierPathWithRoundedRect:rect cornerRadius:4.0];
[backgroundPath fill];
NSString *character = @"";
switch (policyType) {
case SBRScriptOriginPolicyTypeAlpha:
character = @"𝝰"; break;
case SBRScriptOriginPolicyTypeBravo:
character = @"𝝱"; break;
case SBRScriptOriginPolicyTypeCharlie:
character = @"𝝲"; break;
case SBRScriptOriginPolicyTypeDelta:
character = @"𝝳"; break;
case SBRScriptOriginPolicyTypeEcho:
character = @"𝝴"; break;
}
const CGSize charSize = [character sizeWithAttributes:attrs];
const CGRect charRect = (CGRect) {
.origin = (CGPoint) {
.x = (size.width - charSize.width) / 2,
.y = -(charSize.height - size.height) / 2,
},
.size = charSize
};
[character drawInRect:charRect withAttributes:attrs];
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return image;
}
+ (BOOL)supportsSecureCoding
{
return YES;
}
- (instancetype)initWithSecurityOrigin:(NSString *)origin policyType:(SBRScriptOriginPolicyType)policyType
{
if (self = [super init]) {
_origin = [origin copy];
_policyType = policyType;
}
return self;
}
- (instancetype)initWithCoder:(NSCoder *)coder
{
NSString *origin = [coder decodeObjectForKey:@"origin"];
NSNumber *policyTypeNumber = [coder decodeObjectForKey:@"policyType"];
return [self initWithSecurityOrigin:origin policyType:[policyTypeNumber integerValue]];
}
- (void)encodeWithCoder:(NSCoder *)coder
{
[coder encodeObject:_origin forKey:@"origin"];
[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