apexsoqlsnykssrf

Sanitising SOQL response for inclusing in HttpRequest


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.


Solution

  • 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);