Repositorio del curso CCOM4030 el semestre B91 del proyecto Artesanías con el Instituto de Cultura

CDVWhitelist.m 11KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285
  1. /*
  2. Licensed to the Apache Software Foundation (ASF) under one
  3. or more contributor license agreements. See the NOTICE file
  4. distributed with this work for additional information
  5. regarding copyright ownership. The ASF licenses this file
  6. to you under the Apache License, Version 2.0 (the
  7. "License"); you may not use this file except in compliance
  8. with the License. You may obtain a copy of the License at
  9. http://www.apache.org/licenses/LICENSE-2.0
  10. Unless required by applicable law or agreed to in writing,
  11. software distributed under the License is distributed on an
  12. "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  13. KIND, either express or implied. See the License for the
  14. specific language governing permissions and limitations
  15. under the License.
  16. */
  17. #import "CDVWhitelist.h"
  18. NSString* const kCDVDefaultWhitelistRejectionString = @"ERROR whitelist rejection: url='%@'";
  19. NSString* const kCDVDefaultSchemeName = @"cdv-default-scheme";
  20. @interface CDVWhitelistPattern : NSObject {
  21. @private
  22. NSRegularExpression* _scheme;
  23. NSRegularExpression* _host;
  24. NSNumber* _port;
  25. NSRegularExpression* _path;
  26. }
  27. + (NSString*)regexFromPattern:(NSString*)pattern allowWildcards:(bool)allowWildcards;
  28. - (id)initWithScheme:(NSString*)scheme host:(NSString*)host port:(NSString*)port path:(NSString*)path;
  29. - (bool)matches:(NSURL*)url;
  30. @end
  31. @implementation CDVWhitelistPattern
  32. + (NSString*)regexFromPattern:(NSString*)pattern allowWildcards:(bool)allowWildcards
  33. {
  34. NSString* regex = [NSRegularExpression escapedPatternForString:pattern];
  35. if (allowWildcards) {
  36. regex = [regex stringByReplacingOccurrencesOfString:@"\\*" withString:@".*"];
  37. /* [NSURL path] has the peculiarity that a trailing slash at the end of a path
  38. * will be omitted. This regex tweak compensates for that.
  39. */
  40. if ([regex hasSuffix:@"\\/.*"]) {
  41. regex = [NSString stringWithFormat:@"%@(\\/.*)?", [regex substringToIndex:([regex length] - 4)]];
  42. }
  43. }
  44. return [NSString stringWithFormat:@"%@$", regex];
  45. }
  46. - (id)initWithScheme:(NSString*)scheme host:(NSString*)host port:(NSString*)port path:(NSString*)path
  47. {
  48. self = [super init]; // Potentially change "self"
  49. if (self) {
  50. if ((scheme == nil) || [scheme isEqualToString:@"*"]) {
  51. _scheme = nil;
  52. } else {
  53. _scheme = [NSRegularExpression regularExpressionWithPattern:[CDVWhitelistPattern regexFromPattern:scheme allowWildcards:NO] options:NSRegularExpressionCaseInsensitive error:nil];
  54. }
  55. if ([host isEqualToString:@"*"] || host == nil) {
  56. _host = nil;
  57. } else if ([host hasPrefix:@"*."]) {
  58. _host = [NSRegularExpression regularExpressionWithPattern:[NSString stringWithFormat:@"([a-z0-9.-]*\\.)?%@", [CDVWhitelistPattern regexFromPattern:[host substringFromIndex:2] allowWildcards:false]] options:NSRegularExpressionCaseInsensitive error:nil];
  59. } else {
  60. _host = [NSRegularExpression regularExpressionWithPattern:[CDVWhitelistPattern regexFromPattern:host allowWildcards:NO] options:NSRegularExpressionCaseInsensitive error:nil];
  61. }
  62. if ((port == nil) || [port isEqualToString:@"*"]) {
  63. _port = nil;
  64. } else {
  65. _port = [[NSNumber alloc] initWithInteger:[port integerValue]];
  66. }
  67. if ((path == nil) || [path isEqualToString:@"/*"]) {
  68. _path = nil;
  69. } else {
  70. _path = [NSRegularExpression regularExpressionWithPattern:[CDVWhitelistPattern regexFromPattern:path allowWildcards:YES] options:0 error:nil];
  71. }
  72. }
  73. return self;
  74. }
  75. - (bool)matches:(NSURL*)url
  76. {
  77. return (_scheme == nil || [_scheme numberOfMatchesInString:[url scheme] options:NSMatchingAnchored range:NSMakeRange(0, [[url scheme] length])]) &&
  78. (_host == nil || ([url host] != nil && [_host numberOfMatchesInString:[url host] options:NSMatchingAnchored range:NSMakeRange(0, [[url host] length])])) &&
  79. (_port == nil || [[url port] isEqualToNumber:_port]) &&
  80. (_path == nil || [_path numberOfMatchesInString:[url path] options:NSMatchingAnchored range:NSMakeRange(0, [[url path] length])])
  81. ;
  82. }
  83. @end
  84. @interface CDVWhitelist ()
  85. @property (nonatomic, readwrite, strong) NSMutableArray* whitelist;
  86. @property (nonatomic, readwrite, strong) NSMutableSet* permittedSchemes;
  87. - (void)addWhiteListEntry:(NSString*)pattern;
  88. @end
  89. @implementation CDVWhitelist
  90. @synthesize whitelist, permittedSchemes, whitelistRejectionFormatString;
  91. - (id)initWithArray:(NSArray*)array
  92. {
  93. self = [super init];
  94. if (self) {
  95. self.whitelist = [[NSMutableArray alloc] init];
  96. self.permittedSchemes = [[NSMutableSet alloc] init];
  97. self.whitelistRejectionFormatString = kCDVDefaultWhitelistRejectionString;
  98. for (NSString* pattern in array) {
  99. [self addWhiteListEntry:pattern];
  100. }
  101. }
  102. return self;
  103. }
  104. - (BOOL)isIPv4Address:(NSString*)externalHost
  105. {
  106. // an IPv4 address has 4 octets b.b.b.b where b is a number between 0 and 255.
  107. // for our purposes, b can also be the wildcard character '*'
  108. // we could use a regex to solve this problem but then I would have two problems
  109. // anyways, this is much clearer and maintainable
  110. NSArray* octets = [externalHost componentsSeparatedByString:@"."];
  111. NSUInteger num_octets = [octets count];
  112. // quick check
  113. if (num_octets != 4) {
  114. return NO;
  115. }
  116. // restrict number parsing to 0-255
  117. NSNumberFormatter* numberFormatter = [[NSNumberFormatter alloc] init];
  118. [numberFormatter setMinimum:[NSNumber numberWithUnsignedInteger:0]];
  119. [numberFormatter setMaximum:[NSNumber numberWithUnsignedInteger:255]];
  120. // iterate through each octet, and test for a number between 0-255 or if it equals '*'
  121. for (NSUInteger i = 0; i < num_octets; ++i) {
  122. NSString* octet = [octets objectAtIndex:i];
  123. if ([octet isEqualToString:@"*"]) { // passes - check next octet
  124. continue;
  125. } else if ([numberFormatter numberFromString:octet] == nil) { // fails - not a number and not within our range, return
  126. return NO;
  127. }
  128. }
  129. return YES;
  130. }
  131. - (void)addWhiteListEntry:(NSString*)origin
  132. {
  133. if (self.whitelist == nil) {
  134. return;
  135. }
  136. if ([origin isEqualToString:@"*"]) {
  137. NSLog(@"Unlimited access to network resources");
  138. self.whitelist = nil;
  139. self.permittedSchemes = nil;
  140. } else { // specific access
  141. NSRegularExpression* parts = [NSRegularExpression regularExpressionWithPattern:@"^((\\*|[A-Za-z-]+):/?/?)?(((\\*\\.)?[^*/:]+)|\\*)?(:(\\d+))?(/.*)?" options:0 error:nil];
  142. NSTextCheckingResult* m = [parts firstMatchInString:origin options:NSMatchingAnchored range:NSMakeRange(0, [origin length])];
  143. if (m != nil) {
  144. NSRange r;
  145. NSString* scheme = nil;
  146. r = [m rangeAtIndex:2];
  147. if (r.location != NSNotFound) {
  148. scheme = [origin substringWithRange:r];
  149. }
  150. NSString* host = nil;
  151. r = [m rangeAtIndex:3];
  152. if (r.location != NSNotFound) {
  153. host = [origin substringWithRange:r];
  154. }
  155. // Special case for two urls which are allowed to have empty hosts
  156. if (([scheme isEqualToString:@"file"] || [scheme isEqualToString:@"content"]) && (host == nil)) {
  157. host = @"*";
  158. }
  159. NSString* port = nil;
  160. r = [m rangeAtIndex:7];
  161. if (r.location != NSNotFound) {
  162. port = [origin substringWithRange:r];
  163. }
  164. NSString* path = nil;
  165. r = [m rangeAtIndex:8];
  166. if (r.location != NSNotFound) {
  167. path = [origin substringWithRange:r];
  168. }
  169. if (scheme == nil) {
  170. // XXX making it stupid friendly for people who forget to include protocol/SSL
  171. [self.whitelist addObject:[[CDVWhitelistPattern alloc] initWithScheme:@"http" host:host port:port path:path]];
  172. [self.whitelist addObject:[[CDVWhitelistPattern alloc] initWithScheme:@"https" host:host port:port path:path]];
  173. } else {
  174. [self.whitelist addObject:[[CDVWhitelistPattern alloc] initWithScheme:scheme host:host port:port path:path]];
  175. }
  176. if (self.permittedSchemes != nil) {
  177. if ([scheme isEqualToString:@"*"]) {
  178. self.permittedSchemes = nil;
  179. } else if (scheme != nil) {
  180. [self.permittedSchemes addObject:scheme];
  181. }
  182. }
  183. }
  184. }
  185. }
  186. - (BOOL)schemeIsAllowed:(NSString*)scheme
  187. {
  188. if ([scheme isEqualToString:@"http"] ||
  189. [scheme isEqualToString:@"https"] ||
  190. [scheme isEqualToString:@"ftp"] ||
  191. [scheme isEqualToString:@"ftps"]) {
  192. return YES;
  193. }
  194. return (self.permittedSchemes == nil) || [self.permittedSchemes containsObject:scheme];
  195. }
  196. - (BOOL)URLIsAllowed:(NSURL*)url
  197. {
  198. return [self URLIsAllowed:url logFailure:YES];
  199. }
  200. - (BOOL)URLIsAllowed:(NSURL*)url logFailure:(BOOL)logFailure
  201. {
  202. // Shortcut acceptance: Are all urls whitelisted ("*" in whitelist)?
  203. if (whitelist == nil) {
  204. return YES;
  205. }
  206. // Shortcut rejection: Check that the scheme is supported
  207. NSString* scheme = [[url scheme] lowercaseString];
  208. if (![self schemeIsAllowed:scheme]) {
  209. if (logFailure) {
  210. NSLog(@"%@", [self errorStringForURL:url]);
  211. }
  212. return NO;
  213. }
  214. // http[s] and ftp[s] should also validate against the common set in the kCDVDefaultSchemeName list
  215. if ([scheme isEqualToString:@"http"] || [scheme isEqualToString:@"https"] || [scheme isEqualToString:@"ftp"] || [scheme isEqualToString:@"ftps"]) {
  216. NSURL* newUrl = [NSURL URLWithString:[NSString stringWithFormat:@"%@://%@%@", kCDVDefaultSchemeName, [url host], [[url path] stringByAddingPercentEncodingWithAllowedCharacters:NSCharacterSet.URLPathAllowedCharacterSet]]];
  217. // If it is allowed, we are done. If not, continue to check for the actual scheme-specific list
  218. if ([self URLIsAllowed:newUrl logFailure:NO]) {
  219. return YES;
  220. }
  221. }
  222. // Check the url against patterns in the whitelist
  223. for (CDVWhitelistPattern* p in self.whitelist) {
  224. if ([p matches:url]) {
  225. return YES;
  226. }
  227. }
  228. if (logFailure) {
  229. NSLog(@"%@", [self errorStringForURL:url]);
  230. }
  231. // if we got here, the url host is not in the white-list, do nothing
  232. return NO;
  233. }
  234. - (NSString*)errorStringForURL:(NSURL*)url
  235. {
  236. return [NSString stringWithFormat:self.whitelistRejectionFormatString, [url absoluteString]];
  237. }
  238. @end