Compare commits

..

2 Commits

Author SHA1 Message Date
Sefa Ilkimen
fcd142a70b release v2.0.5 2019-02-25 13:08:25 +01:00
Sefa Ilkimen
807400bc63 Fix #185: need more detailed SSL error message 2019-02-22 19:20:12 +01:00
8 changed files with 6383 additions and 26 deletions

View File

@@ -1,5 +1,9 @@
# Changelog
## 2.0.5
- Fixed #185: need more detailed SSL error message
## 2.0.4
- Fixed #179: sending empty string with utf8 serializer throws an exception

6345
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
{
"name": "cordova-plugin-advanced-http",
"version": "2.0.4",
"version": "2.0.5",
"description": "Cordova / Phonegap plugin for communicating with HTTP servers using SSL pinning",
"scripts": {
"updatecert": "node ./scripts/update-test-cert.js",

View File

@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<plugin xmlns="http://www.phonegap.com/ns/plugins/1.0" xmlns:android="http://schemas.android.com/apk/res/android" id="cordova-plugin-advanced-http" version="2.0.4">
<plugin xmlns="http://www.phonegap.com/ns/plugins/1.0" xmlns:android="http://schemas.android.com/apk/res/android" id="cordova-plugin-advanced-http" version="2.0.5">
<name>Advanced HTTP plugin</name>
<description>
Cordova / Phonegap plugin for communicating with HTTP servers using SSL pinning

View File

@@ -270,11 +270,11 @@ abstract class CordovaHttp {
protected void handleHttpRequestException(HttpRequestException e) {
if (e.getCause() instanceof UnknownHostException) {
this.respondWithError(0, "The host could not be resolved");
this.respondWithError(0, "The host could not be resolved: " + e.getMessage());
} else if (e.getCause() instanceof SocketTimeoutException) {
this.respondWithError(1, "The request timed out");
this.respondWithError(1, "The request timed out: " + e.getMessage());
} else if (e.getCause() instanceof SSLHandshakeException) {
this.respondWithError("SSL handshake failed");
this.respondWithError(-2, "SSL handshake failed: " + e.getMessage());
} else {
this.respondWithError("There was an error with the request: " + e.getMessage());
}

View File

@@ -392,11 +392,11 @@ static NSString * const AFNSURLSessionTaskDidSuspendNotification = @"com.alamofi
- On iOS 7, `__NSCFLocalSessionTask` and `__NSCFURLSessionTask` are the only two classes that have their own implementations of `resume` and `suspend`, and `__NSCFLocalSessionTask` DOES NOT CALL SUPER. This means both classes need to be swizzled.
- On iOS 8, `NSURLSessionTask` is the only class that implements `resume` and `suspend`. This means this is the only class that needs to be swizzled.
- Because `NSURLSessionTask` is not involved in the class hierarchy for every version of iOS, its easier to add the swizzled methods to a dummy class and manage them there.
Some Assumptions:
- No implementations of `resume` or `suspend` call super. If this were to change in a future version of iOS, we'd need to handle it.
- No background task classes override `resume` or `suspend`
The current solution:
1) Grab an instance of `__NSCFLocalDataTask` by asking an instance of `NSURLSession` for a data task.
2) Grab a pointer to the original implementation of `af_resume`
@@ -415,7 +415,7 @@ static NSString * const AFNSURLSessionTaskDidSuspendNotification = @"com.alamofi
#pragma clang diagnostic pop
IMP originalAFResumeIMP = method_getImplementation(class_getInstanceMethod([self class], @selector(af_resume)));
Class currentClass = [localDataTask class];
while (class_getInstanceMethod(currentClass, @selector(resume))) {
Class superClass = [currentClass superclass];
IMP classResumeIMP = method_getImplementation(class_getInstanceMethod(currentClass, @selector(resume)));
@@ -426,7 +426,7 @@ static NSString * const AFNSURLSessionTaskDidSuspendNotification = @"com.alamofi
}
currentClass = [currentClass superclass];
}
[localDataTask cancel];
[session finishTasksAndInvalidate];
}
@@ -454,7 +454,7 @@ static NSString * const AFNSURLSessionTaskDidSuspendNotification = @"com.alamofi
NSAssert([self respondsToSelector:@selector(state)], @"Does not respond to state");
NSURLSessionTaskState state = [self state];
[self af_resume];
if (state != NSURLSessionTaskStateRunning) {
[[NSNotificationCenter defaultCenter] postNotificationName:AFNSURLSessionTaskDidResumeNotification object:self];
}
@@ -464,7 +464,7 @@ static NSString * const AFNSURLSessionTaskDidSuspendNotification = @"com.alamofi
NSAssert([self respondsToSelector:@selector(state)], @"Does not respond to state");
NSURLSessionTaskState state = [self state];
[self af_suspend];
if (state != NSURLSessionTaskStateSuspended) {
[[NSNotificationCenter defaultCenter] postNotificationName:AFNSURLSessionTaskDidSuspendNotification object:self];
}
@@ -978,7 +978,7 @@ didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge
disposition = NSURLSessionAuthChallengePerformDefaultHandling;
}
} else {
disposition = NSURLSessionAuthChallengeCancelAuthenticationChallenge;
disposition = NSURLSessionAuthChallengeRejectProtectionSpace;
}
} else {
disposition = NSURLSessionAuthChallengePerformDefaultHandling;
@@ -1025,7 +1025,7 @@ didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge
disposition = NSURLSessionAuthChallengeUseCredential;
credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
} else {
disposition = NSURLSessionAuthChallengeCancelAuthenticationChallenge;
disposition = NSURLSessionAuthChallengeRejectProtectionSpace;
}
} else {
disposition = NSURLSessionAuthChallengePerformDefaultHandling;

View File

@@ -104,6 +104,9 @@
case -1009:
// no connection
return [NSNumber numberWithInt:3];
case -1202:
// untrusted SSL certificate
return [NSNumber numberWithInt:-2];
default:
return [NSNumber numberWithInt:-1];
}

View File

@@ -37,46 +37,51 @@ const helpers = {
}
};
const messageFactory = {
sslTrustAnchor: function() { return 'SSL handshake failed: javax.net.ssl.SSLHandshakeException: java.security.cert.CertPathValidatorException: Trust anchor for certification path not found.' },
invalidCertificate: function(domain) { return 'The certificate for this server is invalid. You might be connecting to a server that is pretending to be “' + domain + '” which could put your confidential information at risk.' }
}
const tests = [
{
description: 'should reject self signed cert (GET)',
expected: 'rejected: {"status":-1,"error":"cancelled"}',
expected: 'rejected: {"status":-2, ...',
func: function(resolve, reject) { cordova.plugin.http.get('https://self-signed.badssl.com/', {}, {}, resolve, reject); },
validationFunc: function(driver, result, targetInfo) {
result.type.should.be.equal('rejected');
result.data.should.be.eql({ status: -1, error: targetInfo.isAndroid ? 'SSL handshake failed' : 'cancelled' });
result.data.should.be.eql({ status: -2, error: targetInfo.isAndroid ? messageFactory.sslTrustAnchor() : messageFactory.invalidCertificate('self-signed.badssl.com') });
}
},{
description: 'should reject self signed cert (PUT)',
expected: 'rejected: {"status":-1,"error":"cancelled"}',
expected: 'rejected: {"status":-2, ...',
func: function(resolve, reject) { cordova.plugin.http.put('https://self-signed.badssl.com/', { test: 'testString' }, {}, resolve, reject); },
validationFunc: function(driver, result, targetInfo) {
result.type.should.be.equal('rejected');
result.data.should.be.eql({ status: -1, error: targetInfo.isAndroid ? 'SSL handshake failed' : 'cancelled' });
result.data.should.be.eql({ status: -2, error: targetInfo.isAndroid ? messageFactory.sslTrustAnchor() : messageFactory.invalidCertificate('self-signed.badssl.com') });
}
},{
description: 'should reject self signed cert (POST)',
expected: 'rejected: {"status":-1,"error":"cancelled"}',
expected: 'rejected: {"status":-2, ...',
func: function(resolve, reject) { cordova.plugin.http.post('https://self-signed.badssl.com/', { test: 'testString' }, {}, resolve, reject); },
validationFunc: function(driver, result, targetInfo) {
result.type.should.be.equal('rejected');
result.data.should.be.eql({ status: -1, error: targetInfo.isAndroid ? 'SSL handshake failed' : 'cancelled' });
result.data.should.be.eql({ status: -2, error: targetInfo.isAndroid ? messageFactory.sslTrustAnchor() : messageFactory.invalidCertificate('self-signed.badssl.com') });
}
},{
description: 'should reject self signed cert (PATCH)',
expected: 'rejected: {"status":-1,"error":"cancelled"}',
expected: 'rejected: {"status":-2, ...',
func: function(resolve, reject) { cordova.plugin.http.patch('https://self-signed.badssl.com/', { test: 'testString' }, {}, resolve, reject); },
validationFunc: function(driver, result, targetInfo) {
result.type.should.be.equal('rejected');
result.data.should.be.eql({ status: -1, error: targetInfo.isAndroid ? 'SSL handshake failed' : 'cancelled' });
result.data.should.be.eql({ status: -2, error: targetInfo.isAndroid ? messageFactory.sslTrustAnchor() : messageFactory.invalidCertificate('self-signed.badssl.com') });
}
},{
description: 'should reject self signed cert (DELETE)',
expected: 'rejected: {"status":-1,"error":"cancelled"}',
expected: 'rejected: {"status":-2, ...',
func: function(resolve, reject) { cordova.plugin.http.delete('https://self-signed.badssl.com/', {}, {}, resolve, reject); },
validationFunc: function(driver, result, targetInfo) {
result.type.should.be.equal('rejected');
result.data.should.be.eql({ status: -1, error: targetInfo.isAndroid ? 'SSL handshake failed' : 'cancelled' });
result.data.should.be.eql({ status: -2, error: targetInfo.isAndroid ? messageFactory.sslTrustAnchor() : messageFactory.invalidCertificate('self-signed.badssl.com') });
}
},{
description: 'should accept bad cert (GET)',
@@ -284,7 +289,7 @@ const tests = [
JSON
.parse(result.data.data)
.url
.should.be.equal('http://httpbin.org/get?myArray[]=val1&myArray[]=val2&myArray[]=val3&myString=testString');
.should.include('httpbin.org/get?myArray[]=val1&myArray[]=val2&myArray[]=val3&myString=testString');
}
},{
description: 'should throw on non-string values in local header object #54',
@@ -439,14 +444,14 @@ const tests = [
}
},{
description: 'should reject when pinned cert does not match received server cert (GET)',
expected: 'rejected: {"status": -1 ...',
expected: 'rejected: {"status": -2 ...',
before: helpers.setPinnedCertMode,
func: function(resolve, reject) {
cordova.plugin.http.get('https://sha512.badssl.com/', {}, {}, resolve, reject);
},
validationFunc: function(driver, result, targetInfo) {
result.type.should.be.equal('rejected');
result.data.should.be.eql({ status: -1, error: targetInfo.isAndroid ? 'SSL handshake failed' : 'cancelled' });
result.data.should.be.eql({ status: -2, error: targetInfo.isAndroid ? messageFactory.sslTrustAnchor() : messageFactory.invalidCertificate('sha512.badssl.com') });
}
},{
description: 'should send deeply structured JSON object correctly (POST) #65',