I have a block of APEX code like this:
MyType mdt = [
SELECT Token_Endpoint__c
FROM Google_OAuth2__mdt
LIMIT 1
];
String tokenEndpoint = mdt.Token_Endpoint__c;
HttpRequest req = new HttpRequest();
req.setEndpoint(tokenEndpoint);
Snyk code test identifies the req.setEndpoint
call as introducing a server side request forgery vulnerability, which is valid, but I am struggling to find a way to sanitise the input.
Unsanitized input from a SOQL statement flows into setendpoint, where it is used as an URL to perform a request. This may result in a Server-Side Request Forgery vulnerability.
ā [High] Server-Side Request Forgery (SSRF)
I have tried creating an allowList Set and throwing an exception if the URL is not found.
String tokenEndpoint = mdt.Token_Endpoint__c;
Set<String> allowList = new Set<String>{'https://foo/...'}
if (!allowList.contains('https://foo/...')) {
throw new IllegalArgumentException('uh oh')
}
HttpRequest req = new HttpRequest();
req.setEndpoint(tokenEndpoint);
And also to try and lookup the URL in a map
String tokenEndpoint = mdt.Token_Endpoint__c;
Map<String, String> allowList = new Map<String, String>{'https://foo/...' => 'https://foo/...'}
if (!allowList.containsKey('https://foo/...')) {
throw new IllegalArgumentException('uh oh')
}
HttpRequest req = new HttpRequest();
req.setEndpoint(allowList.get('https://foo/...'));
But Snyk alerts on both cases. The only thing I have found that convince Snyk that the URL is safe is to hard code it in the APEX class itself. But that is not an option for me.
Seems that the following works, breaking the URL into it's composite pieces (scheme, host, path etc) and validating each according to a restrictive ruleset, then reassembling a URL from the fragments:
MyType mdt = [
SELECT Token_Endpoint__c
FROM Google_OAuth2__mdt
LIMIT 1
];
String tokenEndpoint = mdt.Token_Endpoint__c;
Url urlRaw = new Url(tokenEndpoint);
String scheme = EncodingUtil.urlEncode(urlRaw.getProtocol(), 'UTF-8');
String host = EncodingUtil.urlEncode(urlRaw.getHost(), 'UTF-8');
String path = urlRaw.getFile().replaceAll('[^\\w\\/]', '');
String sanitized = new Url(scheme, host, path).toExternalForm();
HttpRequest req = new HttpRequest();
req.setEndpoint(sanitized);