Skip to content
176 changes: 176 additions & 0 deletions src/test/java/com/twilio/security/RequestValidatorTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -114,4 +114,180 @@ public void testValidateAddsPortHttp() {
Assert.assertTrue("Validator did not add port 80 to http url", isValid);
}


@Test
public void testValidateWithEmptyParams() {
// Test with no form parameters - empty map
Map<String, String> emptyParams = new HashMap<>();
String expectedSignature = "zYQTYrRWXE7LtzbG4PfP7/bkkGo="; // hash of URL only
boolean isValid = validator.validate(url, emptyParams, expectedSignature);

Assert.assertTrue("Validation should succeed with empty parameters", isValid);
}

@Test
public void testValidateWithNullParams() {
// Test with null parameters
String expectedSignature = "zYQTYrRWXE7LtzbG4PfP7/bkkGo="; // hash of URL only
boolean isValid = validator.validate(url, (Map<String, String>) null, expectedSignature);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what if we dont type-cast null, we should expect a NPE?


Assert.assertTrue("Validation should succeed with null parameters", isValid);
}

@Test
public void testValidateWithNoQueryParams() {
// Test with URL that has no query parameters
String urlNoQuery = "https://mycompany.com/myapp.php";
String expectedSignature = "1xaxFBvyzXYvWplzNOSs28HWO94=";
boolean isValid = validator.validate(urlNoQuery, params, expectedSignature);

Assert.assertTrue("Validation should succeed with URL without query parameters", isValid);
}

@Test
public void testValidateIgnoresNonTwilioQueryParams() {
// Test that non-Twilio query parameters in the URL are included in signature generation
// (they are NOT ignored - they're part of the URL that gets signed)
String urlWithExtraParams = "https://mycompany.com/myapp.php?foo=1&bar=2&type=thiswontbeusedinthesignaturegeneratorandsoshouldbeignored";
String expectedSignature = "hYkNsTBttoNGkyCS0p+wWmQoRTg=";
boolean isValid = validator.validate(urlWithExtraParams, params, expectedSignature);

Assert.assertTrue("Validation should succeed - query params are part of URL signature", isValid);
}

@Test
public void testValidateWithUrlEncodedQueryParams() {
// Test with URL encoded query parameters
String urlEncoded = "https://mycompany.com/myapp.php?foo=hello%20world&bar=test%2Bvalue";
String expectedSignature = "er2tvAP1Wx+iuTTE2BPgvEaN1cs=";
boolean isValid = validator.validate(urlEncoded, params, expectedSignature);

Assert.assertTrue("Validation should succeed with URL encoded query parameters", isValid);
}

@Test
public void testValidateWithSpecialCharactersInParams() {
// Test with special characters in parameter values
Map<String, String> specialParams = new HashMap<>();
specialParams.put("Message", "Hello & goodbye! @#$%^*()");
specialParams.put("From", "+1 (415) 867-5309");
specialParams.put("Special", "unicode: ñáéíóú");

String expectedSignature = "dCPiR4WtQ6QFN6pJh81CtlCcWLQ=";
boolean isValid = validator.validate(url, specialParams, expectedSignature);

Assert.assertTrue("Validation should succeed with special characters in parameters", isValid);
}

@Test
public void testValidateWithEmptyParameterValues() {
// Test with empty string parameter values
Map<String, String> emptyValueParams = new HashMap<>();
emptyValueParams.put("Digits", "");
emptyValueParams.put("CallSid", "CA1234567890ABCDE");
emptyValueParams.put("EmptyParam", "");

String expectedSignature = "9HHp/OqQMEwdrVebHDdA/+tqjX8=";
boolean isValid = validator.validate(url, emptyValueParams, expectedSignature);

Assert.assertTrue("Validation should succeed with empty parameter values", isValid);
}

@Test
public void testValidateWithNullParameterValues() {
// Test with null parameter values (treated as empty strings)
Map<String, String> nullValueParams = new HashMap<>();
nullValueParams.put("Digits", null);
nullValueParams.put("CallSid", "CA1234567890ABCDE");
nullValueParams.put("NullParam", null);

String expectedSignature = "L9lTTwIlxM1xGAtLRwhOSZggRhM=";
boolean isValid = validator.validate(url, nullValueParams, expectedSignature);

Assert.assertTrue("Validation should succeed with null parameter values", isValid);
}

@Test
public void testValidateParameterKeySorting() {
// Test that parameter keys are properly sorted alphabetically
Map<String, String> unsortedParams = new HashMap<>();
unsortedParams.put("Zebra", "last");
unsortedParams.put("Alpha", "first");
unsortedParams.put("Beta", "second");
unsortedParams.put("Gamma", "third");

String expectedSignature = "enveUe73ZTVLAekHxez3Qx6JuuQ=";
boolean isValid = validator.validate(url, unsortedParams, expectedSignature);

Assert.assertTrue("Validation should succeed with alphabetically sorted parameters", isValid);
}

@Test
public void testValidateWithInternationalCharactersInUrl() {
// Test with international characters in URL
String intlUrl = "https://mycompany.com/webhook/España?año=2023";
String expectedSignature = "G/Hx6mqqnjUNlPAoi2Sp9bGst0g=";
boolean isValid = validator.validate(intlUrl, params, expectedSignature);

Assert.assertTrue("Validation should succeed with international characters in URL", isValid);
}

@Test
public void testValidateWithCaseSensitiveParameterKeys() {
// Test that parameter keys are case sensitive
Map<String, String> caseParams = new HashMap<>();
caseParams.put("callSid", "lowercase");
caseParams.put("CallSid", "uppercase"); // Different from callSid
caseParams.put("CALLSID", "allcaps");

String expectedSignature = "jDK7WKT8wiNrwggd4ceTv4e1MJ4=";
boolean isValid = validator.validate(url, caseParams, expectedSignature);

Assert.assertTrue("Validation should succeed with case-sensitive parameter keys", isValid);
}

@Test
public void testValidateWithMalformedUrlStillWorks() {
// Test that validation handles URLs gracefully even with unusual formatting
String malformedUrl = "https://mycompany.com:443//myapp.php?foo=1&bar=2";
String expectedSignature = "A9lH3u16NjyiiM/+wylw5rirFUs="; // Signature for the malformed URL as-is
boolean isValid = validator.validate(malformedUrl, params, expectedSignature);

Assert.assertTrue("Validation should handle malformed URLs gracefully", isValid);
}

@Test
public void testValidateWithDifferentProtocols() {
// Test validation with different protocols
String httpUrl = url.replace("https", "http");
String expectedSignature = "0ZXoZLH/DfblKGATFgpif+LLRf4=";
boolean isValid = validator.validate(httpUrl, params, expectedSignature);

Assert.assertTrue("Validation should work with HTTP protocol", isValid);
}

@Test
public void testValidateFailsWithWrongSignature() {
// Test that validation properly fails with incorrect signature
String wrongSignature = "IncorrectSignatureValue123=";
boolean isValid = validator.validate(url, params, wrongSignature);

Assert.assertFalse("Validation should fail with incorrect signature", isValid);
}

@Test
public void testValidateFailsWithNullSignature() {
// Test that validation properly fails with null signature
boolean isValid = validator.validate(url, params, null);

Assert.assertFalse("Validation should fail with null signature", isValid);
}

@Test
public void testValidateFailsWithEmptySignature() {
// Test that validation properly fails with empty signature
boolean isValid = validator.validate(url, params, "");
Assert.assertFalse("Validation should fail with empty signature", isValid);
}

}