Commit a9ec1449 authored by Sonia Zorba's avatar Sonia Zorba
Browse files

Handled special characters and illegal characters on node paths

parent 5a20bd0a
Loading
Loading
Loading
Loading
Loading
+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
+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);
    }
}