Skip to content

Commit

Permalink
#21400 updates tempfileAPI (#21402)
Browse files Browse the repository at this point in the history
* #21400 updates tempfileAPI

* #21400 updates tempfileAPI
  • Loading branch information
wezell committed Dec 14, 2021
1 parent 3f412f0 commit ccad093
Show file tree
Hide file tree
Showing 3 changed files with 135 additions and 8 deletions.
55 changes: 47 additions & 8 deletions dotCMS/src/main/java/com/dotcms/rest/api/v1/temp/TempFileAPI.java
Expand Up @@ -9,6 +9,7 @@
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.URL;
import java.nio.file.Files;
import java.util.ArrayList;
Expand All @@ -18,19 +19,23 @@
import java.util.Optional;

import javax.servlet.http.HttpServletRequest;

import org.xbill.DNS.Address;
import org.xbill.DNS.ExtendedResolver;
import org.xbill.DNS.Resolver;
import com.dotcms.http.CircuitBreakerUrl;
import com.dotcms.http.CircuitBreakerUrl.Method;
import com.dotcms.util.CloseUtils;
import com.dotcms.util.ConversionUtils;
import com.dotcms.util.SecurityUtils;
import com.dotcms.util.network.IPUtils;
import com.dotmarketing.business.APILocator;
import com.dotmarketing.business.DotStateException;
import com.dotmarketing.business.UserAPI;
import com.dotmarketing.business.web.WebAPILocator;
import com.dotmarketing.exception.DotRuntimeException;
import com.dotmarketing.exception.DotSecurityException;
import com.dotmarketing.util.Config;
import com.dotmarketing.util.DNSUtil;
import com.dotmarketing.util.FileUtil;
import com.dotmarketing.util.Logger;
import com.dotmarketing.util.SecurityLogger;
Expand Down Expand Up @@ -60,6 +65,9 @@ public class TempFileAPI {
public static final String TEMP_RESOURCE_PREFIX = "temp_";

private static final String WHO_CAN_USE_TEMP_FILE = "whoCanUse.tmp";
private static final String TEMP_RESOURCE_BY_URL_ADMIN_ONLY="TEMP_RESOURCE_BY_URL_ADMIN_ONLY";



/**
* Returns an empty TempFile of a unique id and file handle that can be used to write and access a
Expand Down Expand Up @@ -200,14 +208,45 @@ public DotTempFile createTempFileFromUrl(final String incomingFileName,
final DotTempFile dotTempFile = createEmptyTempFile(fileName, request);
final File tempFile = dotTempFile.file;

final OutputStream out = new BoundedOutputStream(maxFileSize(request),
Files.newOutputStream(tempFile.toPath()));

final CircuitBreakerUrl urlGetter =
CircuitBreakerUrl.builder().setMethod(Method.GET).setUrl(url.toString())
.setTimeout(timeoutSeconds * 1000).build();

final boolean tempFilesByUrlAdminOnly = Config
.getBooleanProperty(TEMP_RESOURCE_BY_URL_ADMIN_ONLY, false);


/**
* If url requested is on a private subnet, block by default
*/
if(IPUtils.isIpPrivateSubnet(url.getHost())) {
throw new DotRuntimeException("Unable to load file by url:" + url);
}


/**
* by adding the source IP give visibility to the
* remote server of who initiatied the reuqest
*/
final String sourceIpAddress = request.getRemoteAddr();
final String finalUrl = url.toString().contains("?") ? url.toString() + "&sourceIp=" + sourceIpAddress : url.toString() + "?sourceIp=" + sourceIpAddress ;



/**
* Only allow admins to use the URL functionality
*/
User user = PortalUtil.getUser(request);
if(user == null || tempFilesByUrlAdminOnly && !user.isAdmin()) {
throw new DotRuntimeException("Only Admins can import a file by URL");
}

try(final OutputStream out = new BoundedOutputStream(maxFileSize(request),
Files.newOutputStream(tempFile.toPath()))){

urlGetter.doOut(out);
final CircuitBreakerUrl urlGetter =
CircuitBreakerUrl.builder().setMethod(Method.GET).setUrl(finalUrl)
.setTimeout(timeoutSeconds * 1000).build();

urlGetter.doOut(out);
}

return dotTempFile;

Expand Down
53 changes: 53 additions & 0 deletions dotCMS/src/main/java/com/dotcms/util/network/IPUtils.java
@@ -1,7 +1,11 @@
package com.dotcms.util.network;

import java.net.InetAddress;
import java.util.Objects;
import org.xbill.DNS.Address;
import com.dotcms.repackage.org.apache.commons.net.util.SubnetUtils;
import com.dotmarketing.exception.DotRuntimeException;
import com.dotmarketing.util.Logger;
import com.dotmarketing.util.UtilMethods;
import io.vavr.control.Try;

Expand Down Expand Up @@ -38,4 +42,53 @@ public static boolean isIpInCIDR(final String ip, final String CIDR) {


}

final static private String[] privateSubnets = {"10.0.0.0/8","172.16.0.0/12", "192.168.0.0/16"};



/**
* It is important when we allow calling to remote endpoints that we verify
* that the remote endpoint is not in our corprate or private network.
* This method checks if the ip or hostname passed in is on the private network
* which can be blocked if needed.
* @param ipOrHostName
* @return
*/
public static boolean isIpPrivateSubnet(final String ipOrHostName) {

if (ipOrHostName == null) {
return true;
}

try {
InetAddress addr = Address.getByName(ipOrHostName);

final String ip = addr.getHostAddress();

if ("127.0.0.1".equals(ip)) {
return true;
}

if ("localhost".equals(ip)) {
return true;
}

for (String subnet : privateSubnets) {
if (isIpInCIDR(ip, subnet)) {
return true;
}
}
} catch (Exception e) {
Logger.warn(IPUtils.class, "unable to resolve hostname, assuming the worst:" + ipOrHostName + " "+ e.getMessage());
return true;
}
return false;



}



}
35 changes: 35 additions & 0 deletions dotCMS/src/test/java/com/dotcms/util/network/IPUtilsTest.java
Expand Up @@ -56,4 +56,39 @@ public void test_false_cases() {
}

}


final static String[] ipsOnPrivateSubnets= {
"192.168.1.255",
"10.0.0.4",
"127.0.0.1",
"172.16.3.5",
"172.16.3.0",
"localhost"
};

final static String[] ipsOnPublicSubnets= {
"2.2.2.2",
"3.22.136.122",
"142.251.32.110",
"74.6.231.21",
"dotcms.com",
"193.252.133.20"
};

@Test
public void test_ip_private_subnets() {
for(String testCase : ipsOnPrivateSubnets) {
assertTrue( IPUtils.isIpPrivateSubnet(testCase));
}
}
@Test

public void test_ip_public_subnets() {
for(String testCase : ipsOnPublicSubnets) {
assertFalse( IPUtils.isIpPrivateSubnet(testCase));
}
}


}

0 comments on commit ccad093

Please sign in to comment.