Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -138,11 +138,26 @@ private void parseAssay(XHTMLContentHandler xhtml, Metadata metadata, ParseConte
xhtml.startElement("div");
xhtml.element("h3", "ASSAY " + assayFileName);
// location starts with "/C:" on windows, can't use Paths.get()
try (InputStream stream = TikaInputStream.get(new File(this.location + assayFileName).toPath()))
{
File assayFile = resolveWithinLocation(assayFileName);
try (InputStream stream = TikaInputStream.get(assayFile.toPath())) {
ISATabUtils.parseAssay(stream, xhtml, metadata, context);
}
xhtml.endElement("div");
}
}

/**
* Resolves an assay file name taken from the (untrusted) investigation file against
* the ISA-Tab location, rejecting names that escape that directory via "../" or a
* symlink.
*/
private File resolveWithinLocation(String fileName) throws IOException, TikaException {
File candidate = new File(this.location + fileName);
String locationCanonical = new File(this.location).getCanonicalPath();
if (!candidate.getCanonicalPath().startsWith(locationCanonical + File.separator)) {
throw new TikaException(
"assay file name '" + fileName + "' resolves outside the ISA-Tab directory");
}
return candidate;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,19 @@
package org.apache.tika.parser.isatab;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertThrows;

import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
import org.xml.sax.ContentHandler;

import org.apache.tika.TikaTest;
import org.apache.tika.exception.TikaException;
import org.apache.tika.io.TikaInputStream;
import org.apache.tika.metadata.Metadata;
import org.apache.tika.metadata.TikaCoreProperties;
Expand Down Expand Up @@ -68,4 +76,32 @@ public void testParseArchive() throws Exception {
assertEquals("Stephen", metadata.get("Investigation Person First Name"),
"Invalid Investigation Person First Name");
}

@Test
public void testAssayPathTraversal(@TempDir Path root) throws Exception {
Path secret = root.resolve("secret.txt");
Files.write(secret, "SUPERSECRETTOKEN12345".getBytes(StandardCharsets.UTF_8));

Path isaDir = Files.createDirectory(root.resolve("isa"));
Files.write(isaDir.resolve("i_test.txt"),
("STUDY\n"
+ "Study File Name\t\"s_test.txt\"\n"
+ "Study Assay File Name\t\"../secret.txt\"\n")
.getBytes(StandardCharsets.UTF_8));
Path study = isaDir.resolve("s_test.txt");
Files.write(study, "\"Source Name\"\n\"culture1\"\n".getBytes(StandardCharsets.UTF_8));

Parser parser = new ISArchiveParser(isaDir.toString());
ContentHandler handler = new BodyContentHandler();
Metadata metadata = new Metadata();
metadata.set(TikaCoreProperties.RESOURCE_NAME_KEY, "s_test.txt");
ParseContext context = new ParseContext();

try (TikaInputStream tis = TikaInputStream.get(study)) {
assertThrows(TikaException.class,
() -> parser.parse(tis, handler, metadata, context));
}
assertFalse(handler.toString().contains("SUPERSECRETTOKEN12345"),
"assay reader escaped the ISA-Tab directory");
}
}