Loading src/main/java/it/inaf/oats/vospace/datamodel/NodeUtils.java +38 −3 Original line number Diff line number Diff line package it.inaf.oats.vospace.datamodel; import java.net.URLDecoder; import java.net.URLEncoder; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.regex.Pattern; import java.util.stream.Collectors; import net.ivoa.xml.vospace.v2.Node; public class NodeUtils { /** * Forbidden path chars are non printable characters and some symbols that * could create issues to scripts that manipulates files. Other UTF-8 * characters are allowed. Front end needs to pay attention to other allowed * characters like & and parenthesis in any case, also to avoid XSS attacks. */ private static final Pattern FORBIDDEN_CHARS = Pattern.compile("[\\x00\\x08\\x0B\\x0C\\x0E-\\x1F" + Pattern.quote("<>?\":\\|'*") + "]"); /** * Slash is a special character in defining REST endpoints and trying to * define a PathVariable containing slashes doesn't work, so the endpoint * has been defined using "/nodes/**" instead of "/nodes/{path}" and the * path is extracted manually parsing the request URL. * path is extracted manually parsing the request URL. Proper URL encoding * handling is needed, considering also that slashes mustn't be escaped. */ public static String getPathFromRequestURLString(String requestUrlString) { return getPathFromRequestURLString(requestUrlString, "/nodes/"); } String[] split = requestUrlString.split("/nodes/"); public static String getPathFromRequestURLString(String requestUrlString, String prefix) { String[] split = requestUrlString.split(prefix); String path = "/"; if (split.length == 2) { path += split[1]; String[] parts = split[1].split("/"); path += String.join("/", Arrays.stream(parts) .map(p -> { String decoded = URLDecoder.decode(p, StandardCharsets.UTF_8); if (FORBIDDEN_CHARS.matcher(decoded).find()) { throw new IllegalArgumentException("Path segment " + decoded + " contains an illegal character"); } return decoded; }) .collect(Collectors.toList())); } return path; } public static String urlEncodePath(String path) { String[] parts = path.split("/"); return String.join("/", Arrays.stream(parts) .map(p -> URLEncoder.encode(p, StandardCharsets.UTF_8)) .collect(Collectors.toList())); } // This method assumes that URL is in the format /node1/node2/... // multiple slashes as a single separator are allowed // But the output has only single slash separators Loading src/test/java/it/inaf/oats/vospace/datamodel/NodeUtilsTest.java 0 → 100644 +54 −0 Original line number Diff line number Diff line package it.inaf.oats.vospace.datamodel; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; import org.junit.jupiter.api.Test; public class NodeUtilsTest { @Test public void testGetPathFromRequestURLString() { String requestUrl = "http://localhost/vospace/nodes/a/b/c/"; assertEquals("/a/b/c", NodeUtils.getPathFromRequestURLString(requestUrl)); } @Test public void testGetPathWithSpacesFromRequestURLString() { String requestUrl = "http://localhost/vospace/nodes/a/b/c%20d%20%C3%A4.pdf"; assertEquals("/a/b/c d ä.pdf", NodeUtils.getPathFromRequestURLString(requestUrl)); } @Test public void testEncodePathSpecialChars() { String specialChars = "ä è#+ /other/+-ò@"; assertEquals("%C3%A4+%C3%A8%23%2B+/other/%2B-%C3%B2%40", NodeUtils.urlEncodePath(specialChars)); } @Test public void testIllegalBrakets() { testIllegalChars("<no>.pdf"); } @Test public void testIllegalQuestionMark() { testIllegalChars("???.pdf"); } @Test public void testIllegalQuotes() { testIllegalChars("\"'.pdf"); } private void testIllegalChars(String illegalString) { boolean exception = false; try { NodeUtils.getPathFromRequestURLString("http://localhost/vospace/nodes/path/to/" + illegalString); } catch (IllegalArgumentException ex) { exception = true; } assertTrue(exception); } } Loading
src/main/java/it/inaf/oats/vospace/datamodel/NodeUtils.java +38 −3 Original line number Diff line number Diff line package it.inaf.oats.vospace.datamodel; import java.net.URLDecoder; import java.net.URLEncoder; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.regex.Pattern; import java.util.stream.Collectors; import net.ivoa.xml.vospace.v2.Node; public class NodeUtils { /** * Forbidden path chars are non printable characters and some symbols that * could create issues to scripts that manipulates files. Other UTF-8 * characters are allowed. Front end needs to pay attention to other allowed * characters like & and parenthesis in any case, also to avoid XSS attacks. */ private static final Pattern FORBIDDEN_CHARS = Pattern.compile("[\\x00\\x08\\x0B\\x0C\\x0E-\\x1F" + Pattern.quote("<>?\":\\|'*") + "]"); /** * Slash is a special character in defining REST endpoints and trying to * define a PathVariable containing slashes doesn't work, so the endpoint * has been defined using "/nodes/**" instead of "/nodes/{path}" and the * path is extracted manually parsing the request URL. * path is extracted manually parsing the request URL. Proper URL encoding * handling is needed, considering also that slashes mustn't be escaped. */ public static String getPathFromRequestURLString(String requestUrlString) { return getPathFromRequestURLString(requestUrlString, "/nodes/"); } String[] split = requestUrlString.split("/nodes/"); public static String getPathFromRequestURLString(String requestUrlString, String prefix) { String[] split = requestUrlString.split(prefix); String path = "/"; if (split.length == 2) { path += split[1]; String[] parts = split[1].split("/"); path += String.join("/", Arrays.stream(parts) .map(p -> { String decoded = URLDecoder.decode(p, StandardCharsets.UTF_8); if (FORBIDDEN_CHARS.matcher(decoded).find()) { throw new IllegalArgumentException("Path segment " + decoded + " contains an illegal character"); } return decoded; }) .collect(Collectors.toList())); } return path; } public static String urlEncodePath(String path) { String[] parts = path.split("/"); return String.join("/", Arrays.stream(parts) .map(p -> URLEncoder.encode(p, StandardCharsets.UTF_8)) .collect(Collectors.toList())); } // This method assumes that URL is in the format /node1/node2/... // multiple slashes as a single separator are allowed // But the output has only single slash separators Loading
src/test/java/it/inaf/oats/vospace/datamodel/NodeUtilsTest.java 0 → 100644 +54 −0 Original line number Diff line number Diff line package it.inaf.oats.vospace.datamodel; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; import org.junit.jupiter.api.Test; public class NodeUtilsTest { @Test public void testGetPathFromRequestURLString() { String requestUrl = "http://localhost/vospace/nodes/a/b/c/"; assertEquals("/a/b/c", NodeUtils.getPathFromRequestURLString(requestUrl)); } @Test public void testGetPathWithSpacesFromRequestURLString() { String requestUrl = "http://localhost/vospace/nodes/a/b/c%20d%20%C3%A4.pdf"; assertEquals("/a/b/c d ä.pdf", NodeUtils.getPathFromRequestURLString(requestUrl)); } @Test public void testEncodePathSpecialChars() { String specialChars = "ä è#+ /other/+-ò@"; assertEquals("%C3%A4+%C3%A8%23%2B+/other/%2B-%C3%B2%40", NodeUtils.urlEncodePath(specialChars)); } @Test public void testIllegalBrakets() { testIllegalChars("<no>.pdf"); } @Test public void testIllegalQuestionMark() { testIllegalChars("???.pdf"); } @Test public void testIllegalQuotes() { testIllegalChars("\"'.pdf"); } private void testIllegalChars(String illegalString) { boolean exception = false; try { NodeUtils.getPathFromRequestURLString("http://localhost/vospace/nodes/path/to/" + illegalString); } catch (IllegalArgumentException ex) { exception = true; } assertTrue(exception); } }