Fixed: <rdar://problem/4098786> sync. XMLHttpRequest works w/o AllowNetworkAccess...
authorcblu <cblu@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 20 May 2005 20:50:20 +0000 (20:50 +0000)
committercblu <cblu@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 20 May 2005 20:50:20 +0000 (20:50 +0000)
Synchronous loads did not cause the willSendRequest method on the resource load delegate to be called. This is the method that Dashboard uses to enforce AllowNetworkAccess and this must be called to avoid exploits.

        Reviewed by sullivan.

        * WebCoreSupport.subproj/WebBridge.m:
        (-[WebBridge objectLoadedFromCacheWithURL:response:data:]): call [WebFrame _requestFromDelegateForRequest:identifier:error:] then
[WebFrame _saveResourceAndSendRemainingDelegateMessagesWithRequest:identifier:response:data:error:] so synthetic resource load delegate
methods are called and the data is saved as a WebResource for resources in the WebCore cache.

        (-[WebBridge syncLoadResourceWithURL:customHeaders:postData:finalURL:responseHeaders:statusCode:]): call [WebFrame _requestFromDelegateForRequest:identifier:error:],
respect its result, do the load and then call [WebFrame _saveResourceAndSendRemainingDelegateMessagesWithRequest:identifier:response:data:error:]
for synchronous loads

        * WebView.subproj/WebFrame.m:
        (-[WebFrame _opened]): call [WebFrame _requestFromDelegateForRequest:identifier:error:] then
[WebFrame _sendRemainingDelegateMessagesWithIdentifier:response:length:error:] so synthetic resource load delegate methods are called
for subresrources in the page cache

(-[WebFrame _requestFromDelegateForRequest:identifier:error:]): new, was part of the removed _sendResourceLoadDelegateMessagesForURL:::
This method calls identifierForInitialRequest and willSendRequest.

        (-[WebFrame _sendRemainingDelegateMessagesWithIdentifier:response:length:error:]): new, was part of the removed _sendResourceLoadDelegateMessagesForURL:::
This method calls the remaining resource load delegate messages.

        (-[WebFrame _saveResourceAndSendRemainingDelegateMessagesWithRequest:identifier:response:data:error:]): new, saves the resource and calls
[WebFrame _sendRemainingDelegateMessagesWithIdentifier:response:length:error:]

        * WebView.subproj/WebFrameInternal.h:

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@9183 268f45cc-cd09-0410-ab3c-d52691b4dbfc

WebKit/ChangeLog
WebKit/WebCoreSupport.subproj/WebBridge.m
WebKit/WebView.subproj/WebFrame.m
WebKit/WebView.subproj/WebFrameInternal.h

index 00c057b51c8576066502d8ba0374d127a8705a26..47722dab87469da95e0f7d3b0ab960a38e29607e 100644 (file)
@@ -1,3 +1,36 @@
+2005-05-20  Chris Blumenberg  <cblu@apple.com>
+
+       Fixed: <rdar://problem/4098786> sync. XMLHttpRequest works w/o AllowNetworkAccess key because load delegate is not consulted
+
+       Synchronous loads did not cause the willSendRequest method on the resource load delegate to be called. This is the method that Dashboard uses to enforce AllowNetworkAccess and this must be called to avoid exploits.
+
+        Reviewed by sullivan.
+
+        * WebCoreSupport.subproj/WebBridge.m:
+        (-[WebBridge objectLoadedFromCacheWithURL:response:data:]): call [WebFrame _requestFromDelegateForRequest:identifier:error:] then 
+       [WebFrame _saveResourceAndSendRemainingDelegateMessagesWithRequest:identifier:response:data:error:] so synthetic resource load delegate 
+       methods are called and the data is saved as a WebResource for resources in the WebCore cache.
+
+        (-[WebBridge syncLoadResourceWithURL:customHeaders:postData:finalURL:responseHeaders:statusCode:]): call [WebFrame _requestFromDelegateForRequest:identifier:error:],
+       respect its result, do the load and then call [WebFrame _saveResourceAndSendRemainingDelegateMessagesWithRequest:identifier:response:data:error:] 
+       for synchronous loads
+
+        * WebView.subproj/WebFrame.m:
+        (-[WebFrame _opened]): call [WebFrame _requestFromDelegateForRequest:identifier:error:] then 
+       [WebFrame _sendRemainingDelegateMessagesWithIdentifier:response:length:error:] so synthetic resource load delegate methods are called 
+       for subresrources in the page cache
+        
+       (-[WebFrame _requestFromDelegateForRequest:identifier:error:]): new, was part of the removed _sendResourceLoadDelegateMessagesForURL:::
+       This method calls identifierForInitialRequest and willSendRequest.
+
+        (-[WebFrame _sendRemainingDelegateMessagesWithIdentifier:response:length:error:]): new, was part of the removed _sendResourceLoadDelegateMessagesForURL:::
+       This method calls the remaining resource load delegate messages.
+
+        (-[WebFrame _saveResourceAndSendRemainingDelegateMessagesWithRequest:identifier:response:data:error:]): new, saves the resource and calls
+       [WebFrame _sendRemainingDelegateMessagesWithIdentifier:response:length:error:]
+
+        * WebView.subproj/WebFrameInternal.h:
+
 2005-05-17  Chris Blumenberg  <cblu@apple.com>
        
        Fixed: <rdar://problem/4119282> clicking a link in an RTF file opens the link with NSWorkspace without the usual security checks or WebView delegate control
index 4253e073c3fd74a83d5b3e2b18368ae098215f67..88e46de8b7b594be83c13b4466cb5b154beeff40 100644 (file)
@@ -458,12 +458,13 @@ NSString *WebPluginContainerKey =   @"WebPluginContainer";
 
 - (void)objectLoadedFromCacheWithURL:(NSURL *)URL response:(NSURLResponse *)response data:(NSData *)data
 {
-    WebResource *resource = [[WebResource alloc] _initWithData:data URL:URL response:response];
-    ASSERT(resource != nil);
-    [[self dataSource] addSubresource:resource];
-    [resource release];
-    
-    [_frame _sendResourceLoadDelegateMessagesForURL:URL response:response length:[data length]];    
+    // FIXME: If the WebKit client changes or cancels the request, WebCore does not respect this and continues the load.
+    NSError *error;
+    NSString *identifier;
+    NSURLRequest *request = [[NSURLRequest alloc] initWithURL:URL];
+    [_frame _requestFromDelegateForRequest:request identifier:&identifier error:&error];    
+    [_frame _saveResourceAndSendRemainingDelegateMessagesWithRequest:request identifier:identifier response:response data:data error:error];
+    [request release];
 }
 
 - (NSData *)syncLoadResourceWithURL:(NSURL *)URL customHeaders:(NSDictionary *)requestHeaders postData:(NSArray *)postData finalURL:(NSURL **)finalURL responseHeaders:(NSDictionary **)responseHeaderDict statusCode:(int *)statusCode
@@ -473,32 +474,39 @@ NSString *WebPluginContainerKey =   @"WebPluginContainer";
     BOOL hideReferrer;
     [self canLoadURL:URL fromReferrer:[self referrer] hideReferrer:&hideReferrer];
 
-    NSMutableURLRequest *newRequest = [[NSMutableURLRequest alloc] initWithURL:URL];
+    NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:URL];
 
     if (postData) {
-        [newRequest setHTTPMethod:@"POST"];
-        webSetHTTPBody(newRequest, postData);
+        [request setHTTPMethod:@"POST"];
+        webSetHTTPBody(request, postData);
     }
 
     NSEnumerator *e = [requestHeaders keyEnumerator];
     NSString *key;
     while ((key = (NSString *)[e nextObject]) != nil) {
-        [newRequest addValue:[requestHeaders objectForKey:key] forHTTPHeaderField:key];
+        [request addValue:[requestHeaders objectForKey:key] forHTTPHeaderField:key];
     }
     
     // Never use cached data for these requests (xmlhttprequests).
-    [newRequest setCachePolicy:[[[self dataSource] request] cachePolicy]];
+    [request setCachePolicy:[[[self dataSource] request] cachePolicy]];
     if (!hideReferrer)
-        [newRequest setHTTPReferrer:[self referrer]];
+        [request setHTTPReferrer:[self referrer]];
     
     WebView *webView = [_frame webView];
-    [newRequest setMainDocumentURL:[[[[webView mainFrame] dataSource] request] URL]];
-    [newRequest setHTTPUserAgent:[webView userAgentForURL:[newRequest URL]]];
-
-    NSURLResponse *response = nil;
+    [request setMainDocumentURL:[[[[webView mainFrame] dataSource] request] URL]];
+    [request setHTTPUserAgent:[webView userAgentForURL:[request URL]]];
+    
     NSError *error = nil;
-    NSData *result = [NSURLConnection sendSynchronousRequest:newRequest returningResponse:&response error:&error];
-
+    NSString *identifier = nil;    
+    NSURLRequest *newRequest = [_frame _requestFromDelegateForRequest:request identifier:&identifier error:&error];
+    
+    NSURLResponse *response = nil;
+    NSData *result = nil;
+    if (error == nil) {
+        ASSERT(newRequest != nil);
+        result = [NSURLConnection sendSynchronousRequest:newRequest returningResponse:&response error:&error];
+    }
+    
     if (error == nil) {
         *finalURL = [response URL];
         if ([response isKindOfClass:[NSHTTPURLResponse class]]) {
@@ -509,16 +517,19 @@ NSString *WebPluginContainerKey =   @"WebPluginContainer";
             *responseHeaderDict = [NSDictionary dictionary];
             *statusCode = 200;
         }
-
-        // notify the delegates
-        // FIXME: Bridge method name "loaded from cache" doesn't make any sense here.
-        [self objectLoadedFromCacheWithURL:URL response:response data:result];
     } else {
         *finalURL = URL;
         *responseHeaderDict = [NSDictionary dictionary];
-        *statusCode = 404;
+        if ([error domain] == NSURLErrorDomain) {
+            *statusCode = [error code];
+        } else {
+            *statusCode = 404;
+        }
     }
-
+    
+    [_frame _saveResourceAndSendRemainingDelegateMessagesWithRequest:newRequest identifier:identifier response:response data:result error:error];
+    [request release];
+    
     return result;
 }
 
index a6f0c8279957964383b1607034d3fe438faf25b6..94aa82f14cde51f36a3b416b5acc7e483e0e08ff 100644 (file)
@@ -1053,9 +1053,13 @@ static CFAbsoluteTime _timeOfLastCompletedLoad;
         int i, count = [responses count];
         for (i = 0; i < count; i++){
             response = [responses objectAtIndex: i];
-            [self _sendResourceLoadDelegateMessagesForURL:[response URL]
-                                                 response:response
-                                                   length:[response expectedContentLength]];
+            // FIXME: If the WebKit client changes or cancels the request, this is not respected.
+            NSError *error;
+            NSString *identifier;
+            NSURLRequest *request = [[NSURLRequest alloc] initWithURL:[response URL]];
+            [self _requestFromDelegateForRequest:request identifier:&identifier error:&error];
+            [self _sendRemainingDelegateMessagesWithIdentifier:identifier response:response length:[response expectedContentLength] error:error];
+            [request release];
         }
         
         // Release the resources kept in the page cache.  They will be
@@ -2551,42 +2555,89 @@ static CFAbsoluteTime _timeOfLastCompletedLoad;
     return _private->internalLoadDelegate;
 }
 
-- (void)_sendResourceLoadDelegateMessagesForURL:(NSURL *)URL response:(NSURLResponse *)response length:(unsigned)length
+- (NSURLRequest *)_requestFromDelegateForRequest:(NSURLRequest *)request identifier:(NSString **)identifier error:(NSError **)error
 {
-    ASSERT(response != nil);
+    ASSERT(request != nil);
     
-    NSURLRequest *request = [[NSURLRequest alloc] initWithURL:URL];
     WebView *wv = [self webView];
     id delegate = [wv resourceLoadDelegate];
     id sharedDelegate = [WebDefaultResourceLoadDelegate sharedResourceLoadDelegate];
-    id identifier;
     WebResourceDelegateImplementationCache implementations = [wv _resourceLoadDelegateImplementations];
     WebDataSource *dataSource = [self dataSource];
     
-    // No chance for delegate to modify request, so we don't send a willSendRequest:redirectResponse: message.
-    if (implementations.delegateImplementsIdentifierForRequest)
-        identifier = [delegate webView:wv identifierForInitialRequest: request fromDataSource:dataSource];
-    else
-        identifier = [sharedDelegate webView:wv identifierForInitialRequest:request fromDataSource:dataSource];
-    
-    if (implementations.delegateImplementsDidReceiveResponse)
-        [delegate webView:wv resource: identifier didReceiveResponse: response fromDataSource:dataSource];
-    else
-        [sharedDelegate webView:wv resource: identifier didReceiveResponse: response fromDataSource:dataSource];
+    if (implementations.delegateImplementsIdentifierForRequest) {
+        *identifier = [delegate webView:wv identifierForInitialRequest:request fromDataSource:dataSource];
+    } else {
+        *identifier = [sharedDelegate webView:wv identifierForInitialRequest:request fromDataSource:dataSource];
+    }
+        
+    NSURLRequest *newRequest;
+    if (implementations.delegateImplementsWillSendRequest) {
+        newRequest = [delegate webView:wv resource:*identifier willSendRequest:request redirectResponse:nil fromDataSource:dataSource];
+    } else {
+        newRequest = [sharedDelegate webView:wv resource:*identifier willSendRequest:request redirectResponse:nil fromDataSource:dataSource];
+    }
     
-    if (implementations.delegateImplementsDidReceiveContentLength)
-        [delegate webView:wv resource: identifier didReceiveContentLength:length fromDataSource:dataSource];
-    else
-        [sharedDelegate webView:wv resource: identifier didReceiveContentLength:length fromDataSource:dataSource];
+    if (newRequest == nil) {
+        *error = [NSError _webKitErrorWithDomain:NSURLErrorDomain code:NSURLErrorCancelled URL:[request URL]];
+    } else {
+        *error = nil;
+    }
     
-    if (implementations.delegateImplementsDidFinishLoadingFromDataSource)
-        [delegate webView:wv resource: identifier didFinishLoadingFromDataSource:dataSource];
-    else
-        [sharedDelegate webView:wv resource: identifier didFinishLoadingFromDataSource:dataSource];
+    return newRequest;
+}
+
+- (void)_sendRemainingDelegateMessagesWithIdentifier:(NSString *)identifier response:(NSURLResponse *)response length:(unsigned)length error:(NSError *)error 
+{    
+    WebView *wv = [self webView];
+    id delegate = [wv resourceLoadDelegate];
+    id sharedDelegate = [WebDefaultResourceLoadDelegate sharedResourceLoadDelegate];
+    WebResourceDelegateImplementationCache implementations = [wv _resourceLoadDelegateImplementations];
+    WebDataSource *dataSource = [self dataSource];
+        
+    if (response != nil) {
+        if (implementations.delegateImplementsDidReceiveResponse) {
+            [delegate webView:wv resource:identifier didReceiveResponse:response fromDataSource:dataSource];
+        } else {
+            [sharedDelegate webView:wv resource:identifier didReceiveResponse:response fromDataSource:dataSource];
+        }
+    }
     
-    [wv _finishedLoadingResourceFromDataSource:dataSource];
+    if (length > 0) {
+        if (implementations.delegateImplementsDidReceiveContentLength) {
+            [delegate webView:wv resource:identifier didReceiveContentLength:length fromDataSource:dataSource];
+        } else {
+            [sharedDelegate webView:wv resource:identifier didReceiveContentLength:length fromDataSource:dataSource];
+        }
+    }
     
-    [request release];
+    if (error == nil) {
+        if (implementations.delegateImplementsDidFinishLoadingFromDataSource) {
+            [delegate webView:wv resource:identifier didFinishLoadingFromDataSource:dataSource];
+        } else {
+            [sharedDelegate webView:wv resource:identifier didFinishLoadingFromDataSource:dataSource];
+        }
+        [wv _finishedLoadingResourceFromDataSource:dataSource];
+    } else {
+        [[wv _resourceLoadDelegateForwarder] webView:wv resource:identifier didFailLoadingWithError:error fromDataSource:dataSource];
+    }
+}
+
+- (void)_saveResourceAndSendRemainingDelegateMessagesWithRequest:(NSURLRequest *)request
+                                                      identifier:(NSString *)identifier 
+                                                        response:(NSURLResponse *)response 
+                                                            data:(NSData *)data
+                                                           error:(NSError *)error
+{
+    unsigned length = [data length];
+    if (length > 0 && error == nil) {
+        ASSERT(request != nil);
+        WebResource *resource = [[WebResource alloc] _initWithData:data URL:[request URL] response:response];
+        ASSERT(resource != nil);    
+        [[self dataSource] addSubresource:resource];
+        [resource release];
+    }
+    [self _sendRemainingDelegateMessagesWithIdentifier:identifier response:response length:length error:error];
 }
 
 - (void)_unmarkAllMisspellings
index d71301f838b71a13a6351525a51078b67dad3e73..2cad9bfd998a5346ec29ee8edb1e65ddd60ada00 100644 (file)
@@ -7,9 +7,15 @@
 - (void)_updateDrawsBackground;
 - (void)_setInternalLoadDelegate:(id)internalLoadDelegate;
 - (id)_internalLoadDelegate;
-- (void)_sendResourceLoadDelegateMessagesForURL:(NSURL *)URL response:(NSURLResponse *)response length:(unsigned)length;
 - (void)_unmarkAllMisspellings;
 
+- (NSURLRequest *)_requestFromDelegateForRequest:(NSURLRequest *)request identifier:(NSString **)identifier error:(NSError **)error;
+- (void)_sendRemainingDelegateMessagesWithIdentifier:(NSString *)identifier response:(NSURLResponse *)response length:(unsigned)length error:(NSError *)error;
+- (void)_saveResourceAndSendRemainingDelegateMessagesWithRequest:(NSURLRequest *)request
+                                                      identifier:(NSString *)identifier 
+                                                        response:(NSURLResponse *)response 
+                                                            data:(NSData *)data
+                                                           error:(NSError *)error;
 @end
 
 @interface NSObject (WebInternalFrameLoadDelegate)