Additional zip-slip tests (#1162)
* Additional zip-slip tests * Fix windows path test
This commit is contained in:
parent
f49eee623b
commit
909f7e64fe
|
@ -471,9 +471,13 @@ public class SimpleWorkerContext extends BaseWorkerContext implements IWorkerCon
|
|||
|
||||
private void loadFromStream(InputStream stream, IContextResourceLoader loader) throws IOException, FHIRException {
|
||||
ZipInputStream zip = new ZipInputStream(stream);
|
||||
ZipEntry ze;
|
||||
while ((ze = zip.getNextEntry()) != null) {
|
||||
loadDefinitionItem(ze.getName(), zip, loader, null, null);
|
||||
ZipEntry zipEntry;
|
||||
while ((zipEntry = zip.getNextEntry()) != null) {
|
||||
String entryName = zipEntry.getName();
|
||||
if (entryName.contains("..")) {
|
||||
throw new RuntimeException("Entry with an illegal path: " + entryName);
|
||||
}
|
||||
loadDefinitionItem(entryName, zip, loader, null, null);
|
||||
zip.closeEntry();
|
||||
}
|
||||
zip.close();
|
||||
|
|
|
@ -37,6 +37,8 @@ import org.hl7.fhir.utilities.xhtml.HierarchicalTableGenerator.Row;
|
|||
import org.hl7.fhir.utilities.xhtml.HierarchicalTableGenerator.TableModel;
|
||||
import org.hl7.fhir.utilities.xhtml.XhtmlNode;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
public class QuestionnaireRenderer extends TerminologyRenderer {
|
||||
public static final String EXT_QUESTIONNAIRE_ITEM_TYPE_ORIGINAL = "http://hl7.org/fhir/tools/StructureDefinition/original-item-type";
|
||||
|
||||
|
@ -251,28 +253,28 @@ public class QuestionnaireRenderer extends TerminologyRenderer {
|
|||
Cell flags = gen.new Cell();
|
||||
r.getCells().add(flags);
|
||||
if (i.getReadOnly()) {
|
||||
flags.addPiece(gen.new Piece(Utilities.pathURL(context.getSpecificationLink(), "questionnaire-definitions.html#Questionnaire.item.readOnly"), null, "Is Readonly").addHtml(new XhtmlNode(NodeType.Element, "img").attribute("src", Utilities.path(context.getLocalPrefix(), "icon-qi-readonly.png"))));
|
||||
flags.addPiece(gen.new Piece(Utilities.pathURL(context.getSpecificationLink(), "questionnaire-definitions.html#Questionnaire.item.readOnly"), null, "Is Readonly").addHtml(new XhtmlNode(NodeType.Element, "img").attribute("src", getImgPath("icon-qi-readonly.png"))));
|
||||
}
|
||||
if (ToolingExtensions.readBoolExtension(i, "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-isSubject")) {
|
||||
flags.addPiece(gen.new Piece(getSDCLink("StructureDefinition-sdc-questionnaire-isSubject.html"), null, "Can change the subject of the questionnaire").addHtml(new XhtmlNode(NodeType.Element, "img").attribute("src", Utilities.path(context.getLocalPrefix(), "icon-qi-subject.png"))));
|
||||
flags.addPiece(gen.new Piece(getSDCLink("StructureDefinition-sdc-questionnaire-isSubject.html"), null, "Can change the subject of the questionnaire").addHtml(new XhtmlNode(NodeType.Element, "img").attribute("src", getImgPath("icon-qi-subject.png"))));
|
||||
}
|
||||
if (ToolingExtensions.readBoolExtension(i, "http://hl7.org/fhir/StructureDefinition/questionnaire-hidden")) {
|
||||
flags.addPiece(gen.new Piece(getSpecLink("extension-questionnaire-hidden.html"), null, "Is a hidden item").addHtml(new XhtmlNode(NodeType.Element, "img").attribute("src", Utilities.path(context.getLocalPrefix(), "icon-qi-hidden.png"))));
|
||||
flags.addPiece(gen.new Piece(getSpecLink("extension-questionnaire-hidden.html"), null, "Is a hidden item").addHtml(new XhtmlNode(NodeType.Element, "img").attribute("src", getImgPath("icon-qi-hidden.png"))));
|
||||
}
|
||||
if (ToolingExtensions.readBoolExtension(i, "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-optionalDisplay")) {
|
||||
flags.addPiece(gen.new Piece(getSDCLink("StructureDefinition-sdc-questionnaire-optionalDisplay.html"), null, "Is optional to display").addHtml(new XhtmlNode(NodeType.Element, "img").attribute("src", Utilities.path(context.getLocalPrefix(), "icon-qi-optional.png"))));
|
||||
flags.addPiece(gen.new Piece(getSDCLink("StructureDefinition-sdc-questionnaire-optionalDisplay.html"), null, "Is optional to display").addHtml(new XhtmlNode(NodeType.Element, "img").attribute("src", getImgPath("icon-qi-optional.png"))));
|
||||
}
|
||||
if (i.hasExtension("http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-observationLinkPeriod")) {
|
||||
flags.addPiece(gen.new Piece(getSDCLink("StructureDefinition-sdc-questionnaire-observationLinkPeriod.html"), null, "Is linked to an observation").addHtml(new XhtmlNode(NodeType.Element, "img").attribute("src", Utilities.path(context.getLocalPrefix(), "icon-qi-observation.png"))));
|
||||
flags.addPiece(gen.new Piece(getSDCLink("StructureDefinition-sdc-questionnaire-observationLinkPeriod.html"), null, "Is linked to an observation").addHtml(new XhtmlNode(NodeType.Element, "img").attribute("src", getImgPath("icon-qi-observation.png"))));
|
||||
}
|
||||
if (i.hasExtension("http://hl7.org/fhir/StructureDefinition/questionnaire-choiceOrientation")) {
|
||||
String code = ToolingExtensions.readStringExtension(i, "http://hl7.org/fhir/StructureDefinition/questionnaire-choiceOrientation");
|
||||
flags.addPiece(gen.new Piece(getSpecLink("extension-questionnaire-choiceorientation.html"), null, "Orientation: "+code).addHtml(new XhtmlNode(NodeType.Element, "img").attribute("src", Utilities.path(context.getLocalPrefix(), "icon-qi-"+code+".png"))));
|
||||
flags.addPiece(gen.new Piece(getSpecLink("extension-questionnaire-choiceorientation.html"), null, "Orientation: "+code).addHtml(new XhtmlNode(NodeType.Element, "img").attribute("src", getImgPath("icon-qi-" + code + ".png"))));
|
||||
}
|
||||
if (i.hasExtension("http://hl7.org/fhir/StructureDefinition/questionnaire-displayCategory")) {
|
||||
CodeableConcept cc = i.getExtensionByUrl("http://hl7.org/fhir/StructureDefinition/questionnaire-displayCategory").getValueCodeableConcept();
|
||||
String code = cc.getCode("http://hl7.org/fhir/questionnaire-display-category");
|
||||
flags.addPiece(gen.new Piece(getSDCLink("StructureDefinition-sdc-questionnaire-displayCategory.html"), null, "Category: "+code).addHtml(new XhtmlNode(NodeType.Element, "img").attribute("src", Utilities.path(context.getLocalPrefix(), "icon-qi-"+code+".png"))));
|
||||
flags.addPiece(gen.new Piece(getSDCLink("StructureDefinition-sdc-questionnaire-displayCategory.html"), null, "Category: "+code).addHtml(new XhtmlNode(NodeType.Element, "img").attribute("src", getImgPath("icon-qi-" + code + ".png"))));
|
||||
}
|
||||
}
|
||||
Cell defn = gen.new Cell();
|
||||
|
@ -687,26 +689,26 @@ public class QuestionnaireRenderer extends TerminologyRenderer {
|
|||
|
||||
if (ToolingExtensions.readBoolExtension(i, "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-isSubject")) {
|
||||
hasFlag = true;
|
||||
flags.ah(getSDCLink("http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-isSubject"), "Can change the subject of the questionnaire").img(Utilities.path(context.getLocalPrefix(), "icon-qi-subject.png"), "icon");
|
||||
flags.ah(getSDCLink("http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-isSubject"), "Can change the subject of the questionnaire").img(getImgPath("icon-qi-subject.png"), "icon");
|
||||
}
|
||||
if (ToolingExtensions.readBoolExtension(i, "http://hl7.org/fhir/StructureDefinition/questionnaire-hidden")) {
|
||||
hasFlag = true;
|
||||
flags.ah(Utilities.pathURL(context.getSpecificationLink(), "extension-questionnaire-hidden.html"), "Is a hidden item").img(Utilities.path(context.getLocalPrefix(), "icon-qi-hidden.png"), "icon");
|
||||
flags.ah(Utilities.pathURL(context.getSpecificationLink(), "extension-questionnaire-hidden.html"), "Is a hidden item").img(getImgPath("icon-qi-hidden.png"), "icon");
|
||||
d.style("background-color: #eeeeee");
|
||||
}
|
||||
if (ToolingExtensions.readBoolExtension(i, "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-optionalDisplay")) {
|
||||
hasFlag = true;
|
||||
flags.ah(getSDCLink("http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-optionalDisplay"), "Is optional to display").img(Utilities.path(context.getLocalPrefix(), "icon-qi-optional.png"), "icon");
|
||||
flags.ah(getSDCLink("http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-optionalDisplay"), "Is optional to display").img(getImgPath("icon-qi-optional.png"), "icon");
|
||||
}
|
||||
if (i.hasExtension("http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-observationLinkPeriod")) {
|
||||
hasFlag = true;
|
||||
flags.ah(getSDCLink("http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-observationLinkPeriod"), "Is linked to an observation").img(Utilities.path(context.getLocalPrefix(), "icon-qi-observation.png"), "icon");
|
||||
flags.ah(getSDCLink("http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-observationLinkPeriod"), "Is linked to an observation").img(getImgPath("icon-qi-observation.png"), "icon");
|
||||
}
|
||||
if (i.hasExtension("http://hl7.org/fhir/StructureDefinition/questionnaire-displayCategory")) {
|
||||
CodeableConcept cc = i.getExtensionByUrl("http://hl7.org/fhir/StructureDefinition/questionnaire-displayCategory").getValueCodeableConcept();
|
||||
String code = cc.getCode("http://hl7.org/fhir/questionnaire-display-category");
|
||||
hasFlag = true;
|
||||
flags.ah(getSDCLink("http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-displayCategory"), "Category: "+code).img(Utilities.path(context.getLocalPrefix(), "icon-qi-"+code+".png"), "icon");
|
||||
flags.ah(getSDCLink("http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-displayCategory"), "Category: "+code).img(getImgPath("icon-qi-" + code + ".png"), "icon");
|
||||
}
|
||||
|
||||
if (i.hasMaxLength()) {
|
||||
|
@ -788,6 +790,13 @@ public class QuestionnaireRenderer extends TerminologyRenderer {
|
|||
return hasExt;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
private String getImgPath(String code) throws IOException {
|
||||
return context.getLocalPrefix().length() > 0
|
||||
? Utilities.path(context.getLocalPrefix(), code)
|
||||
: Utilities.path(code);
|
||||
}
|
||||
|
||||
private void item(XhtmlNode ul, String name, String value, String valueLink) {
|
||||
if (!Utilities.noString(value)) {
|
||||
ul.li().style("font-size: 10px").ah(valueLink).tx(name+": "+value);
|
||||
|
@ -862,7 +871,7 @@ public class QuestionnaireRenderer extends TerminologyRenderer {
|
|||
boolean ext = false;
|
||||
XhtmlNode td = tbl.tr().td("structure").colspan("2").span(null, null).attribute("class", "self-link-parent");
|
||||
td.an(q.getId());
|
||||
td.img(Utilities.path(context.getLocalPrefix(), "icon_q_root.gif"), "icon");
|
||||
td.img(getImgPath("icon_q_root.gif"), "icon");
|
||||
td.tx(" Questionnaire ");
|
||||
td.b().tx(q.getId());
|
||||
|
||||
|
@ -915,10 +924,10 @@ public class QuestionnaireRenderer extends TerminologyRenderer {
|
|||
XhtmlNode td = tbl.tr().td("structure").colspan("2").span(null, null).attribute("class", "self-link-parent");
|
||||
td.an("item."+qi.getLinkId());
|
||||
for (QuestionnaireItemComponent p : parents) {
|
||||
td.ah("#item."+p.getLinkId()).img(Utilities.path(context.getLocalPrefix(), "icon_q_item.png"), "icon");
|
||||
td.ah("#item."+p.getLinkId()).img(getImgPath("icon_q_item.png"), "icon");
|
||||
td.tx(" > ");
|
||||
}
|
||||
td.img(Utilities.path(context.getLocalPrefix(), "icon_q_item.png"), "icon");
|
||||
td.img(getImgPath("icon_q_item.png"), "icon");
|
||||
td.tx(" Item ");
|
||||
td.b().tx(qi.getLinkId());
|
||||
|
||||
|
|
|
@ -97,17 +97,17 @@ public class TerminologyCacheManager {
|
|||
public static void unzip(InputStream is, String targetDir) throws IOException {
|
||||
try (ZipInputStream zipIn = new ZipInputStream(is)) {
|
||||
for (ZipEntry ze; (ze = zipIn.getNextEntry()) != null; ) {
|
||||
String path = Path.of(Utilities.path(targetDir, ze.getName())).normalize().toFile().getAbsolutePath();
|
||||
|
||||
if (!path.startsWith(targetDir)) {
|
||||
Path path = Path.of(Utilities.path(targetDir, ze.getName())).normalize();
|
||||
String pathString = path.toFile().getAbsolutePath();
|
||||
if (!path.startsWith(Path.of(targetDir).normalize())) {
|
||||
// see: https://snyk.io/research/zip-slip-vulnerability
|
||||
throw new RuntimeException("Entry with an illegal path: " + ze.getName());
|
||||
}
|
||||
if (ze.isDirectory()) {
|
||||
Utilities.createDirectory(path);
|
||||
Utilities.createDirectory(pathString);
|
||||
} else {
|
||||
Utilities.createDirectory(Utilities.getDirectoryForFile(path));
|
||||
TextFile.streamToFileNoClose(zipIn, path);
|
||||
Utilities.createDirectory(Utilities.getDirectoryForFile(pathString));
|
||||
TextFile.streamToFileNoClose(zipIn, pathString);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -146,61 +146,6 @@ public class ToolsHelper {
|
|||
// }
|
||||
}
|
||||
|
||||
private Map<String, byte[]> getDefinitions(String definitions) throws IOException, FHIRException {
|
||||
Map<String, byte[]> results = new HashMap<String, byte[]>();
|
||||
readDefinitions(results, loadDefinitions(definitions));
|
||||
return results;
|
||||
}
|
||||
|
||||
private void readDefinitions(Map<String, byte[]> map, byte[] defn) throws IOException {
|
||||
ZipInputStream zip = new ZipInputStream(new ByteArrayInputStream(defn));
|
||||
ZipEntry ze;
|
||||
while ((ze = zip.getNextEntry()) != null) {
|
||||
if (!ze.getName().endsWith(".zip") && !ze.getName().endsWith(".jar") ) { // skip saxon .zip
|
||||
String name = ze.getName();
|
||||
InputStream in = zip;
|
||||
ByteArrayOutputStream b = new ByteArrayOutputStream();
|
||||
int n;
|
||||
byte[] buf = new byte[1024];
|
||||
while ((n = in.read(buf, 0, 1024)) > -1) {
|
||||
b.write(buf, 0, n);
|
||||
}
|
||||
map.put(name, b.toByteArray());
|
||||
}
|
||||
zip.closeEntry();
|
||||
}
|
||||
zip.close();
|
||||
}
|
||||
|
||||
private byte[] loadDefinitions(String definitions) throws FHIRException, IOException {
|
||||
byte[] defn;
|
||||
// if (Utilities.noString(definitions)) {
|
||||
// defn = loadFromUrl(MASTER_SOURCE);
|
||||
// } else
|
||||
if (definitions.startsWith("https:") || definitions.startsWith("http:")) {
|
||||
defn = loadFromUrl(definitions);
|
||||
} else if (new File(definitions).exists()) {
|
||||
defn = loadFromFile(definitions);
|
||||
} else
|
||||
throw new FHIRException("Unable to find FHIR validation Pack (source = "+definitions+")");
|
||||
return defn;
|
||||
}
|
||||
|
||||
private byte[] loadFromUrl(String src) throws IOException {
|
||||
URL url = new URL(src);
|
||||
byte[] str = IOUtils.toByteArray(url.openStream());
|
||||
return str;
|
||||
}
|
||||
|
||||
private byte[] loadFromFile(String src) throws IOException {
|
||||
FileInputStream in = new FileInputStream(src);
|
||||
byte[] b = new byte[in.available()];
|
||||
in.read(b);
|
||||
in.close();
|
||||
return b;
|
||||
}
|
||||
|
||||
|
||||
protected XmlPullParser loadXml(InputStream stream) throws XmlPullParserException, IOException {
|
||||
BufferedInputStream input = new BufferedInputStream(stream);
|
||||
XmlPullParserFactory factory = XmlPullParserFactory.newInstance(System.getProperty(XmlPullParserFactory.PROPERTY_NAME), null);
|
||||
|
|
|
@ -1,341 +0,0 @@
|
|||
package org.hl7.fhir.r4b.utils;
|
||||
|
||||
import java.io.BufferedOutputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipInputStream;
|
||||
|
||||
import org.hl7.fhir.r4b.model.CodeType;
|
||||
import org.hl7.fhir.r4b.model.SearchParameter;
|
||||
import org.hl7.fhir.r4b.utils.IntegrityChecker.SearchParameterNode;
|
||||
import org.hl7.fhir.r4b.utils.IntegrityChecker.SearchParameterNodeSorter;
|
||||
import org.hl7.fhir.r4b.utils.IntegrityChecker.SearchParameterParamNode;
|
||||
import org.hl7.fhir.r4b.utils.IntegrityChecker.SearchParameterParamNodeSorter;
|
||||
import org.hl7.fhir.exceptions.FHIRFormatError;
|
||||
import org.hl7.fhir.r4b.model.ElementDefinition;
|
||||
import org.hl7.fhir.r4b.formats.JsonParser;
|
||||
import org.hl7.fhir.r4b.formats.XmlParser;
|
||||
import org.hl7.fhir.r4b.model.StructureDefinition;
|
||||
import org.hl7.fhir.r4b.model.StructureDefinition.TypeDerivationRule;
|
||||
import org.hl7.fhir.r4b.utils.IntegrityChecker.StructureDefinitionNode;
|
||||
import org.hl7.fhir.r4b.utils.IntegrityChecker.StructureDefinitionNodeComparer;
|
||||
import org.hl7.fhir.utilities.Utilities;
|
||||
import org.hl7.fhir.utilities.npm.NpmPackage;
|
||||
|
||||
public class IntegrityChecker {
|
||||
|
||||
public class SearchParameterNodeSorter implements Comparator<SearchParameterNode> {
|
||||
|
||||
@Override
|
||||
public int compare(SearchParameterNode o1, SearchParameterNode o2) {
|
||||
return o1.name.compareTo(o2.name);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public class SearchParameterParamNodeSorter implements Comparator<SearchParameterParamNode> {
|
||||
|
||||
@Override
|
||||
public int compare(SearchParameterParamNode o1, SearchParameterParamNode o2) {
|
||||
return o1.sp.getCode().compareTo(o2.sp.getCode());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public class SearchParameterParamNode {
|
||||
SearchParameter sp;
|
||||
boolean only;
|
||||
public SearchParameterParamNode(SearchParameter sp, boolean only) {
|
||||
super();
|
||||
this.sp = sp;
|
||||
this.only = only;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
public class SearchParameterNode {
|
||||
|
||||
private String name;
|
||||
private List<SearchParameterParamNode> params = new ArrayList<>();
|
||||
|
||||
public SearchParameterNode(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public class StructureDefinitionNodeComparer implements Comparator<StructureDefinitionNode> {
|
||||
|
||||
@Override
|
||||
public int compare(StructureDefinitionNode arg0, StructureDefinitionNode arg1) {
|
||||
if ( arg0.sd.getType().equals(arg1.sd.getType())) {
|
||||
return arg0.sd.getName().compareTo(arg1.sd.getName());
|
||||
} else {
|
||||
return arg0.sd.getType().compareTo(arg1.sd.getType());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public class StructureDefinitionNode {
|
||||
StructureDefinition sd;
|
||||
List<StructureDefinitionNode> children = new ArrayList<>();
|
||||
|
||||
public StructureDefinitionNode(StructureDefinition sd) {
|
||||
this.sd = sd;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private NpmPackage npm;
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
IntegrityChecker check = new IntegrityChecker();
|
||||
check.load(args[0]);
|
||||
check.check(args[1]);
|
||||
}
|
||||
|
||||
private void check(String dst) throws IOException {
|
||||
dumpSD(new FileWriter("/Users/grahamegrieve/temp/r4b-dump.txt"));
|
||||
// checkSD();
|
||||
// checkSP();
|
||||
// checkExamplesXml(dst);
|
||||
// checkExamplesJson(dst);
|
||||
}
|
||||
|
||||
|
||||
private void dumpSD(FileWriter w) throws FHIRFormatError, IOException {
|
||||
Map<String, StructureDefinition> map = new HashMap<>();
|
||||
for (String sdn : npm.listResources("StructureDefinition")) {
|
||||
InputStream s = npm.load(sdn);
|
||||
StructureDefinition sd = (StructureDefinition) new JsonParser().parse(s);
|
||||
map.put(sd.getUrl(), sd);
|
||||
}
|
||||
msg("Loaded "+map.size()+" Structures");
|
||||
List<String> structures = new ArrayList<>();
|
||||
for (StructureDefinition sd : map.values()) {
|
||||
structures.add(sd.getUrl());
|
||||
}
|
||||
Collections.sort(structures);
|
||||
|
||||
for (String sdn : structures) {
|
||||
dumpSD(map.get(sdn), map, w);
|
||||
}
|
||||
}
|
||||
|
||||
private void dumpSD(StructureDefinition sd, Map<String, StructureDefinition> map, FileWriter w) throws IOException {
|
||||
if (sd.getDerivation() == TypeDerivationRule.SPECIALIZATION) {
|
||||
StructureDefinition base = sd.hasBaseDefinition() ? map.get(sd.getBaseDefinition()) : null;
|
||||
System.out.println(sd.getType()+(base == null ? "" : " : "+base.getType()));
|
||||
w.append(sd.getType()+(base == null ? "" : " : "+base.getType())+"\r\n");
|
||||
for (ElementDefinition ed : sd.getSnapshot().getElement()) {
|
||||
w.append(" "+Utilities.padLeft("", ' ', Utilities.charCount(ed.getPath(), '.'))+tail(ed.getPath())+" : "+ed.typeSummary()+" ["+ed.getMin()+".."+ed.getMax()+"]"+"\r\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private String tail(String path) {
|
||||
return path.contains(".") ? path.substring(path.lastIndexOf('.')+1) : path;
|
||||
}
|
||||
|
||||
private Map<String, byte[]> loadZip(InputStream stream) throws IOException {
|
||||
Map<String, byte[]> res = new HashMap<String, byte[]>();
|
||||
ZipInputStream zip = new ZipInputStream(stream);
|
||||
ZipEntry ze;
|
||||
while ((ze = zip.getNextEntry()) != null) {
|
||||
int size;
|
||||
byte[] buffer = new byte[2048];
|
||||
|
||||
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
|
||||
BufferedOutputStream bos = new BufferedOutputStream(bytes, buffer.length);
|
||||
|
||||
while ((size = zip.read(buffer, 0, buffer.length)) != -1) {
|
||||
bos.write(buffer, 0, size);
|
||||
}
|
||||
bos.flush();
|
||||
bos.close();
|
||||
res.put(ze.getName(), bytes.toByteArray());
|
||||
|
||||
zip.closeEntry();
|
||||
}
|
||||
zip.close();
|
||||
return res;
|
||||
}
|
||||
|
||||
private void checkExamplesJson(String dst) throws FileNotFoundException, IOException {
|
||||
Map<String, byte[]> files = loadZip(new FileInputStream(Utilities.path(dst, "examples-json.zip")));
|
||||
for (Entry<String, byte[]> t : files.entrySet()) {
|
||||
try {
|
||||
new JsonParser().parse(t.getValue());
|
||||
System.out.print(".");
|
||||
} catch (Exception e) {
|
||||
System.out.println("");
|
||||
System.out.println("Error parsing "+t.getKey()+": "+e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void checkExamplesXml(String dst) throws FileNotFoundException, IOException {
|
||||
Map<String, byte[]> files = loadZip(new FileInputStream(Utilities.path(dst, "examples.zip")));
|
||||
for (Entry<String, byte[]> t : files.entrySet()) {
|
||||
try {
|
||||
new XmlParser().parse(t.getValue());
|
||||
System.out.print(".");
|
||||
} catch (Exception e) {
|
||||
System.out.println("");
|
||||
System.out.println("Error parsing "+t.getKey()+": "+e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void checkSP() throws IOException {
|
||||
List<SearchParameter> list = new ArrayList<>();
|
||||
for (String sdn : npm.listResources("SearchParameter")) {
|
||||
InputStream s = npm.load(sdn);
|
||||
SearchParameter sp = (SearchParameter) new JsonParser().parse(s);
|
||||
list.add(sp);
|
||||
}
|
||||
msg("Loaded "+list.size()+" resources");
|
||||
Map<String, SearchParameterNode> map = new HashMap<>();
|
||||
for (SearchParameter sp : list) {
|
||||
for (CodeType c : sp.getBase()) {
|
||||
String s = c.primitiveValue();
|
||||
if (!map.containsKey(s)) {
|
||||
map.put(s, new SearchParameterNode(s));
|
||||
}
|
||||
addNode(sp, sp.getBase().size() == 1, map.get(s));
|
||||
}
|
||||
}
|
||||
for (SearchParameterNode node : sort(map.values())) {
|
||||
dump(node);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void dump(SearchParameterNode node) {
|
||||
msg(node.name);
|
||||
for (SearchParameterParamNode p : sortP(node.params)) {
|
||||
String exp = p.sp.getExperimental() ? " **exp!" : "";
|
||||
if (p.only) {
|
||||
msg(" "+p.sp.getCode()+exp);
|
||||
} else {
|
||||
msg(" *"+p.sp.getCode()+exp);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private List<SearchParameterParamNode> sortP(List<SearchParameterParamNode> params) {
|
||||
List<SearchParameterParamNode> res = new ArrayList<>();
|
||||
res.addAll(params);
|
||||
Collections.sort(res, new SearchParameterParamNodeSorter());
|
||||
return res;
|
||||
}
|
||||
|
||||
private List<SearchParameterNode> sort(Collection<SearchParameterNode> values) {
|
||||
List<SearchParameterNode> res = new ArrayList<>();
|
||||
res.addAll(values);
|
||||
Collections.sort(res, new SearchParameterNodeSorter());
|
||||
return res;
|
||||
}
|
||||
|
||||
private void addNode(SearchParameter sp, boolean b, SearchParameterNode node) {
|
||||
node.params.add(new SearchParameterParamNode(sp, b));
|
||||
}
|
||||
|
||||
private void checkSD() throws IOException {
|
||||
Map<String, StructureDefinition> map = new HashMap<>();
|
||||
for (String sdn : npm.listResources("StructureDefinition")) {
|
||||
InputStream s = npm.load(sdn);
|
||||
StructureDefinition sd = (StructureDefinition) new JsonParser().parse(s);
|
||||
map.put(sd.getUrl(), sd);
|
||||
}
|
||||
msg("Loaded "+map.size()+" resources");
|
||||
List<StructureDefinitionNode> roots = new ArrayList<>();
|
||||
for (StructureDefinition sd : map.values()) {
|
||||
if (sd.getBaseDefinition() == null || !map.containsKey(sd.getBaseDefinition())) {
|
||||
StructureDefinitionNode root = new StructureDefinitionNode(sd);
|
||||
roots.add(root);
|
||||
analyse(root, map);
|
||||
}
|
||||
}
|
||||
sort(roots);
|
||||
for (StructureDefinitionNode root : roots) {
|
||||
describe(root, 0);
|
||||
}
|
||||
}
|
||||
|
||||
private void sort(List<StructureDefinitionNode> list) {
|
||||
Collections.sort(list, new StructureDefinitionNodeComparer());
|
||||
|
||||
}
|
||||
|
||||
private void analyse(StructureDefinitionNode node, Map<String, StructureDefinition> map) {
|
||||
for (StructureDefinition sd : map.values()) {
|
||||
if (node.sd.getUrl().equals(sd.getBaseDefinition())) {
|
||||
StructureDefinitionNode c = new StructureDefinitionNode(sd);
|
||||
node.children.add(c);
|
||||
analyse(c, map);
|
||||
}
|
||||
}
|
||||
sort(node.children);
|
||||
}
|
||||
|
||||
private void describe(StructureDefinitionNode node, int level) {
|
||||
describe(node.sd, level);
|
||||
for (StructureDefinitionNode c : node.children) {
|
||||
describe(c, level+1);
|
||||
}
|
||||
}
|
||||
|
||||
private void describe(StructureDefinition sd, int level) {
|
||||
String exp = sd.getExperimental() ? " **exp!" : "";
|
||||
if (sd.getDerivation() == TypeDerivationRule.CONSTRAINT) {
|
||||
msg(Utilities.padLeft("", ' ', level)+sd.getType()+" / "+sd.getName()+" ("+sd.getUrl()+")"+exp);
|
||||
} else {
|
||||
msg(Utilities.padLeft("", ' ', level)+sd.getType()+" : "+sd.getKind()+exp);
|
||||
}
|
||||
}
|
||||
|
||||
// private int analyse(Map<String, StructureDefinition> map, List<StructureDefinition> list, StructureDefinition sd) {
|
||||
// if (!list.contains(sd)) {
|
||||
// int level = 0;
|
||||
// if (sd.hasBaseDefinition()) {
|
||||
// StructureDefinition p = map.get(sd.getBaseDefinition());
|
||||
// if (p == null) {
|
||||
// msg("Can't find parent "+sd.getBaseDefinition()+" for "+sd.getUrl());
|
||||
// } else {
|
||||
// level = analyse(map, list, p) + 1;
|
||||
// }
|
||||
// }
|
||||
// list.add(sd);
|
||||
// sd.setUserData("level", level);
|
||||
// }
|
||||
// }
|
||||
|
||||
private void msg(String string) {
|
||||
System.out.println(string);
|
||||
}
|
||||
|
||||
private void load(String folder) throws IOException {
|
||||
msg("Loading resources from "+folder);
|
||||
npm = NpmPackage.fromFolder(folder);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
package org.hl7.fhir.r4b.context;
|
||||
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.Arguments;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
public class SimpleWorkerContextTests {
|
||||
public static Stream<Arguments> zipSlipData() {
|
||||
|
||||
return Stream.of(
|
||||
Arguments.of("zip-slip/zip-slip.zip", "Entry with an illegal path: ../evil.txt"),
|
||||
Arguments.of("zip-slip/zip-slip-2.zip", "Entry with an illegal path: child/../../evil.txt"),
|
||||
Arguments.of("zip-slip/zip-slip-peer.zip", "Entry with an illegal path: ../childpeer/evil.txt"),
|
||||
Arguments.of("zip-slip/zip-slip-win.zip", "Entry with an illegal path: ../evil.txt")
|
||||
);
|
||||
}
|
||||
|
||||
@ParameterizedTest(name = "{index}: file {0}")
|
||||
@MethodSource("zipSlipData")
|
||||
public void testLoadFromClasspathZipSlip(String classPath, String expectedMessage) {
|
||||
RuntimeException thrown = Assertions.assertThrows(RuntimeException.class, () -> {SimpleWorkerContext.fromClassPath(classPath);});
|
||||
assertNotNull(thrown);
|
||||
assertEquals(expectedMessage, thrown.getMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLoadFromClasspathBinaries() throws IOException {
|
||||
SimpleWorkerContext simpleWorkerContext = SimpleWorkerContext.fromClassPath("zip-slip/zip-normal.zip");
|
||||
|
||||
final String testPath = "zip-normal/depth1/test.txt";
|
||||
assertTrue(simpleWorkerContext.binaries.containsKey(testPath));
|
||||
String testFileContent = new String(simpleWorkerContext.binaries.get(testPath), StandardCharsets.UTF_8);
|
||||
assertEquals("dummy file content", testFileContent);
|
||||
}
|
||||
}
|
|
@ -5,11 +5,15 @@ import org.junit.jupiter.api.Assertions;
|
|||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.TestInstance;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.Arguments;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
|
@ -17,24 +21,17 @@ import static org.junit.jupiter.api.Assertions.assertNotNull;
|
|||
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
||||
public class TerminologyCacheManagerTests implements ResourceLoaderTests {
|
||||
|
||||
public static final String ZIP_NORMAL_ZIP = "zip-normal.zip";
|
||||
public static final String ZIP_SLIP_ZIP = "zip-slip.zip";
|
||||
|
||||
public static final String ZIP_SLIP_2_ZIP = "zip-slip-2.zip";
|
||||
|
||||
public static final String ZIP_SLIP_WIN_ZIP = "zip-slip-win.zip";
|
||||
Path tempDir;
|
||||
|
||||
@BeforeAll
|
||||
public void beforeAll() throws IOException {
|
||||
tempDir = Files.createTempDirectory("terminology-cache-manager");
|
||||
tempDir.resolve("child").toFile().mkdir();
|
||||
getResourceAsInputStream("terminologyCacheManager", ZIP_SLIP_ZIP);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNormalZip() throws IOException {
|
||||
InputStream normalInputStream = getResourceAsInputStream( "terminologyCacheManager", ZIP_NORMAL_ZIP);
|
||||
InputStream normalInputStream = getResourceAsInputStream("zip-slip", "zip-normal.zip");
|
||||
TerminologyCacheManager.unzip( normalInputStream, tempDir.toFile().getAbsolutePath());
|
||||
|
||||
Path expectedFilePath = tempDir.resolve("zip-normal").resolve("depth1").resolve("test.txt");
|
||||
|
@ -42,36 +39,25 @@ public class TerminologyCacheManagerTests implements ResourceLoaderTests {
|
|||
assertEquals("dummy file content", actualContent);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSlipZip() throws IOException {
|
||||
RuntimeException thrown = Assertions.assertThrows(RuntimeException.class, () -> {
|
||||
InputStream slipInputStream = getResourceAsInputStream( "terminologyCacheManager", ZIP_SLIP_ZIP);
|
||||
TerminologyCacheManager.unzip( slipInputStream, tempDir.toFile().getAbsolutePath());
|
||||
//Code under test
|
||||
});
|
||||
assertNotNull(thrown);
|
||||
assertEquals("Entry with an illegal path: ../evil.txt", thrown.getMessage());
|
||||
public static Stream<Arguments> zipSlipData() {
|
||||
|
||||
return Stream.of(
|
||||
Arguments.of("zip-slip.zip", "../evil.txt"),
|
||||
Arguments.of("zip-slip-2.zip", "child/../../evil.txt"),
|
||||
Arguments.of("zip-slip-peer.zip", "../childpeer/evil.txt"),
|
||||
Arguments.of("zip-slip-win.zip", "../evil.txt")
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSlip2Zip() throws IOException {
|
||||
@ParameterizedTest(name = "{index}: file {0}")
|
||||
@MethodSource("zipSlipData")
|
||||
public void testLoadFromClasspathZipSlip(String fileName, String expectedMessage) {
|
||||
RuntimeException thrown = Assertions.assertThrows(RuntimeException.class, () -> {
|
||||
InputStream slipInputStream = getResourceAsInputStream( "terminologyCacheManager", ZIP_SLIP_2_ZIP);
|
||||
InputStream slipInputStream = getResourceAsInputStream("zip-slip", fileName);
|
||||
TerminologyCacheManager.unzip( slipInputStream, tempDir.toFile().getAbsolutePath());
|
||||
//Code under test
|
||||
});
|
||||
assertNotNull(thrown);
|
||||
assertEquals("Entry with an illegal path: child/../../evil.txt", thrown.getMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSlipZipWin() throws IOException {
|
||||
RuntimeException thrown = Assertions.assertThrows(RuntimeException.class, () -> {
|
||||
InputStream slipInputStream = getResourceAsInputStream( "terminologyCacheManager", ZIP_SLIP_WIN_ZIP);
|
||||
TerminologyCacheManager.unzip( slipInputStream, tempDir.toFile().getAbsolutePath());
|
||||
//Code under test
|
||||
});
|
||||
assertNotNull(thrown);
|
||||
assertEquals("Entry with an illegal path: ../evil.txt", thrown.getMessage());
|
||||
Assertions.assertTrue(thrown.getMessage().endsWith(expectedMessage));
|
||||
}
|
||||
}
|
||||
|
|
Binary file not shown.
|
@ -520,9 +520,13 @@ public class SimpleWorkerContext extends BaseWorkerContext implements IWorkerCon
|
|||
|
||||
private void loadFromStream(InputStream stream, IContextResourceLoader loader) throws IOException, FHIRException {
|
||||
ZipInputStream zip = new ZipInputStream(stream);
|
||||
ZipEntry ze;
|
||||
while ((ze = zip.getNextEntry()) != null) {
|
||||
loadDefinitionItem(ze.getName(), zip, loader, null, null);
|
||||
ZipEntry zipEntry;
|
||||
while ((zipEntry = zip.getNextEntry()) != null) {
|
||||
String entryName = zipEntry.getName();
|
||||
if (entryName.contains("..")) {
|
||||
throw new RuntimeException("Entry with an illegal path: " + entryName);
|
||||
}
|
||||
loadDefinitionItem(entryName, zip, loader, null, null);
|
||||
zip.closeEntry();
|
||||
}
|
||||
zip.close();
|
||||
|
|
|
@ -37,6 +37,8 @@ import org.hl7.fhir.utilities.xhtml.HierarchicalTableGenerator.TableModel;
|
|||
import org.hl7.fhir.utilities.xhtml.NodeType;
|
||||
import org.hl7.fhir.utilities.xhtml.XhtmlNode;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
public class QuestionnaireRenderer extends TerminologyRenderer {
|
||||
public static final String EXT_QUESTIONNAIRE_ITEM_TYPE_ORIGINAL = "http://hl7.org/fhir/tools/StructureDefinition/original-item-type";
|
||||
|
||||
|
@ -255,28 +257,28 @@ public class QuestionnaireRenderer extends TerminologyRenderer {
|
|||
Cell flags = gen.new Cell();
|
||||
r.getCells().add(flags);
|
||||
if (i.getReadOnly()) {
|
||||
flags.addPiece(gen.new Piece(Utilities.pathURL(context.getLink(KnownLinkType.SPEC), "questionnaire-definitions.html#Questionnaire.item.readOnly"), null, "Is Readonly").addHtml(new XhtmlNode(NodeType.Element, "img").attribute("alt", "icon").attribute("src", Utilities.path(context.getLocalPrefix(), "icon-qi-readonly.png"))));
|
||||
flags.addPiece(gen.new Piece(Utilities.pathURL(context.getLink(KnownLinkType.SPEC), "questionnaire-definitions.html#Questionnaire.item.readOnly"), null, "Is Readonly").addHtml(new XhtmlNode(NodeType.Element, "img").attribute("alt", "icon").attribute("src", getImgPath("icon-qi-readonly.png"))));
|
||||
}
|
||||
if (ToolingExtensions.readBoolExtension(i, "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-isSubject")) {
|
||||
flags.addPiece(gen.new Piece(getSDCLink("StructureDefinition-sdc-questionnaire-isSubject.html"), null, "Can change the subject of the questionnaire").addHtml(new XhtmlNode(NodeType.Element, "img").attribute("alt", "icon").attribute("src", Utilities.path(context.getLocalPrefix(), "icon-qi-subject.png"))));
|
||||
flags.addPiece(gen.new Piece(getSDCLink("StructureDefinition-sdc-questionnaire-isSubject.html"), null, "Can change the subject of the questionnaire").addHtml(new XhtmlNode(NodeType.Element, "img").attribute("alt", "icon").attribute("src", getImgPath("icon-qi-subject.png"))));
|
||||
}
|
||||
if (ToolingExtensions.readBoolExtension(i, ToolingExtensions.EXT_Q_HIDDEN)) {
|
||||
flags.addPiece(gen.new Piece(getSpecLink("extension-questionnaire-hidden.html"), null, "Is a hidden item").addHtml(new XhtmlNode(NodeType.Element, "img").attribute("alt", "icon").attribute("src", Utilities.path(context.getLocalPrefix(), "icon-qi-hidden.png"))));
|
||||
flags.addPiece(gen.new Piece(getSpecLink("extension-questionnaire-hidden.html"), null, "Is a hidden item").addHtml(new XhtmlNode(NodeType.Element, "img").attribute("alt", "icon").attribute("src", getImgPath("icon-qi-hidden.png"))));
|
||||
}
|
||||
if (ToolingExtensions.readBoolExtension(i, ToolingExtensions.EXT_Q_OTP_DISP)) {
|
||||
flags.addPiece(gen.new Piece(getSDCLink("StructureDefinition-sdc-questionnaire-optionalDisplay.html"), null, "Is optional to display").addHtml(new XhtmlNode(NodeType.Element, "img").attribute("alt", "icon").attribute("src", Utilities.path(context.getLocalPrefix(), "icon-qi-optional.png"))));
|
||||
flags.addPiece(gen.new Piece(getSDCLink("StructureDefinition-sdc-questionnaire-optionalDisplay.html"), null, "Is optional to display").addHtml(new XhtmlNode(NodeType.Element, "img").attribute("alt", "icon").attribute("src", getImgPath("icon-qi-optional.png"))));
|
||||
}
|
||||
if (i.hasExtension("http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-observationLinkPeriod")) {
|
||||
flags.addPiece(gen.new Piece(getSDCLink("StructureDefinition-sdc-questionnaire-observationLinkPeriod.html"), null, "Is linked to an observation").addHtml(new XhtmlNode(NodeType.Element, "img").attribute("alt", "icon").attribute("src", Utilities.path(context.getLocalPrefix(), "icon-qi-observation.png"))));
|
||||
flags.addPiece(gen.new Piece(getSDCLink("StructureDefinition-sdc-questionnaire-observationLinkPeriod.html"), null, "Is linked to an observation").addHtml(new XhtmlNode(NodeType.Element, "img").attribute("alt", "icon").attribute("src", getImgPath("icon-qi-observation.png"))));
|
||||
}
|
||||
if (i.hasExtension(ToolingExtensions.EXT_Q_CHOICE_ORIENT)) {
|
||||
String code = ToolingExtensions.readStringExtension(i, ToolingExtensions.EXT_Q_CHOICE_ORIENT);
|
||||
flags.addPiece(gen.new Piece(getSpecLink("extension-questionnaire-choiceorientation.html"), null, "Orientation: "+code).addHtml(new XhtmlNode(NodeType.Element, "img").attribute("alt", "icon").attribute("src", Utilities.path(context.getLocalPrefix(), "icon-qi-"+code+".png"))));
|
||||
flags.addPiece(gen.new Piece(getSpecLink("extension-questionnaire-choiceorientation.html"), null, "Orientation: "+code).addHtml(new XhtmlNode(NodeType.Element, "img").attribute("alt", "icon").attribute("src", getImgPath("icon-qi-" + code + ".png"))));
|
||||
}
|
||||
if (i.hasExtension(ToolingExtensions.EXT_Q_DISPLAY_CAT)) {
|
||||
CodeableConcept cc = i.getExtensionByUrl(ToolingExtensions.EXT_Q_DISPLAY_CAT).getValueCodeableConcept();
|
||||
String code = cc.getCode("http://hl7.org/fhir/questionnaire-display-category");
|
||||
flags.addPiece(gen.new Piece(getSDCLink("StructureDefinition-sdc-questionnaire-displayCategory.html"), null, "Category: "+code).addHtml(new XhtmlNode(NodeType.Element, "img").attribute("alt", "icon").attribute("src", Utilities.path(context.getLocalPrefix(), "icon-qi-"+code+".png"))));
|
||||
flags.addPiece(gen.new Piece(getSDCLink("StructureDefinition-sdc-questionnaire-displayCategory.html"), null, "Category: "+code).addHtml(new XhtmlNode(NodeType.Element, "img").attribute("alt", "icon").attribute("src", getImgPath("icon-qi-" + code + ".png"))));
|
||||
}
|
||||
}
|
||||
Cell defn = gen.new Cell();
|
||||
|
@ -690,26 +692,26 @@ public class QuestionnaireRenderer extends TerminologyRenderer {
|
|||
|
||||
if (ToolingExtensions.readBoolExtension(i, "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-isSubject")) {
|
||||
hasFlag = true;
|
||||
flags.ah(getSDCLink("http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-isSubject"), "Can change the subject of the questionnaire").img(Utilities.path(context.getLocalPrefix(), "icon-qi-subject.png"), "icon");
|
||||
flags.ah(getSDCLink("http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-isSubject"), "Can change the subject of the questionnaire").img(getImgPath("icon-qi-subject.png"), "icon");
|
||||
}
|
||||
if (ToolingExtensions.readBoolExtension(i, ToolingExtensions.EXT_Q_HIDDEN)) {
|
||||
hasFlag = true;
|
||||
flags.ah(Utilities.pathURL(context.getLink(KnownLinkType.SPEC), "extension-questionnaire-hidden.html"), "Is a hidden item").img(Utilities.path(context.getLocalPrefix(), "icon-qi-hidden.png"), "icon");
|
||||
flags.ah(Utilities.pathURL(context.getLink(KnownLinkType.SPEC), "extension-questionnaire-hidden.html"), "Is a hidden item").img(getImgPath("icon-qi-hidden.png"), "icon");
|
||||
d.style("background-color: #eeeeee");
|
||||
}
|
||||
if (ToolingExtensions.readBoolExtension(i, ToolingExtensions.EXT_Q_OTP_DISP)) {
|
||||
hasFlag = true;
|
||||
flags.ah(getSDCLink(ToolingExtensions.EXT_Q_OTP_DISP), "Is optional to display").img(Utilities.path(context.getLocalPrefix(), "icon-qi-optional.png"), "icon");
|
||||
flags.ah(getSDCLink(ToolingExtensions.EXT_Q_OTP_DISP), "Is optional to display").img(getImgPath("icon-qi-optional.png"), "icon");
|
||||
}
|
||||
if (i.hasExtension("http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-observationLinkPeriod")) {
|
||||
hasFlag = true;
|
||||
flags.ah(getSDCLink("http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-observationLinkPeriod"), "Is linked to an observation").img(Utilities.path(context.getLocalPrefix(), "icon-qi-observation.png"), "icon");
|
||||
flags.ah(getSDCLink("http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-observationLinkPeriod"), "Is linked to an observation").img(getImgPath("icon-qi-observation.png"), "icon");
|
||||
}
|
||||
if (i.hasExtension(ToolingExtensions.EXT_Q_DISPLAY_CAT)) {
|
||||
CodeableConcept cc = i.getExtensionByUrl(ToolingExtensions.EXT_Q_DISPLAY_CAT).getValueCodeableConcept();
|
||||
String code = cc.getCode("http://hl7.org/fhir/questionnaire-display-category");
|
||||
hasFlag = true;
|
||||
flags.ah(getSDCLink("http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-displayCategory"), "Category: "+code).img(Utilities.path(context.getLocalPrefix(), "icon-qi-"+code+".png"), "icon");
|
||||
flags.ah(getSDCLink("http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-displayCategory"), "Category: "+code).img(getImgPath("icon-qi-" + code + ".png"), "icon");
|
||||
}
|
||||
|
||||
if (i.hasMaxLength()) {
|
||||
|
@ -791,6 +793,13 @@ public class QuestionnaireRenderer extends TerminologyRenderer {
|
|||
return hasExt;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
private String getImgPath(String code) throws IOException {
|
||||
return context.getLocalPrefix().length() > 0
|
||||
? Utilities.path(context.getLocalPrefix(), code)
|
||||
: Utilities.path(code);
|
||||
}
|
||||
|
||||
private void item(XhtmlNode ul, String name, String value, String valueLink) {
|
||||
if (!Utilities.noString(value)) {
|
||||
ul.li().style("font-size: 10px").ah(valueLink).tx(name+": "+value);
|
||||
|
@ -865,7 +874,7 @@ public class QuestionnaireRenderer extends TerminologyRenderer {
|
|||
boolean ext = false;
|
||||
XhtmlNode td = tbl.tr().td("structure").colspan("2").span(null, null).attribute("class", "self-link-parent");
|
||||
td.an(q.getId());
|
||||
td.img(Utilities.path(context.getLocalPrefix(), "icon_q_root.gif"), "icon");
|
||||
td.img(getImgPath("icon_q_root.gif"), "icon");
|
||||
td.tx(" Questionnaire ");
|
||||
td.b().tx(q.getId());
|
||||
|
||||
|
@ -918,10 +927,10 @@ public class QuestionnaireRenderer extends TerminologyRenderer {
|
|||
XhtmlNode td = tbl.tr().td("structure").colspan("2").span(null, null).attribute("class", "self-link-parent");
|
||||
td.an("item."+qi.getLinkId());
|
||||
for (QuestionnaireItemComponent p : parents) {
|
||||
td.ah("#item."+p.getLinkId()).img(Utilities.path(context.getLocalPrefix(), "icon_q_item.png"), "icon");
|
||||
td.ah("#item."+p.getLinkId()).img(getImgPath("icon_q_item.png"), "icon");
|
||||
td.tx(" > ");
|
||||
}
|
||||
td.img(Utilities.path(context.getLocalPrefix(), "icon_q_item.png"), "icon");
|
||||
td.img(getImgPath("icon_q_item.png"), "icon");
|
||||
td.tx(" Item ");
|
||||
td.b().tx(qi.getLinkId());
|
||||
|
||||
|
|
|
@ -97,16 +97,17 @@ public class TerminologyCacheManager {
|
|||
public static void unzip(InputStream is, String targetDir) throws IOException {
|
||||
try (ZipInputStream zipIn = new ZipInputStream(is)) {
|
||||
for (ZipEntry ze; (ze = zipIn.getNextEntry()) != null; ) {
|
||||
String path = Path.of(Utilities.path(targetDir, ze.getName())).normalize().toFile().getAbsolutePath();
|
||||
if (!path.startsWith(targetDir)) {
|
||||
Path path = Path.of(Utilities.path(targetDir, ze.getName())).normalize();
|
||||
String pathString = path.toFile().getAbsolutePath();
|
||||
if (!path.startsWith(Path.of(targetDir).normalize())) {
|
||||
// see: https://snyk.io/research/zip-slip-vulnerability
|
||||
throw new RuntimeException("Entry with an illegal path: " + ze.getName());
|
||||
}
|
||||
if (ze.isDirectory()) {
|
||||
Utilities.createDirectory(path);
|
||||
Utilities.createDirectory(pathString);
|
||||
} else {
|
||||
Utilities.createDirectory(Utilities.getDirectoryForFile(path));
|
||||
TextFile.streamToFileNoClose(zipIn, path);
|
||||
Utilities.createDirectory(Utilities.getDirectoryForFile(pathString));
|
||||
TextFile.streamToFileNoClose(zipIn, pathString);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -146,61 +146,6 @@ public class ToolsHelper {
|
|||
// }
|
||||
}
|
||||
|
||||
private Map<String, byte[]> getDefinitions(String definitions) throws IOException, FHIRException {
|
||||
Map<String, byte[]> results = new HashMap<String, byte[]>();
|
||||
readDefinitions(results, loadDefinitions(definitions));
|
||||
return results;
|
||||
}
|
||||
|
||||
private void readDefinitions(Map<String, byte[]> map, byte[] defn) throws IOException {
|
||||
ZipInputStream zip = new ZipInputStream(new ByteArrayInputStream(defn));
|
||||
ZipEntry ze;
|
||||
while ((ze = zip.getNextEntry()) != null) {
|
||||
if (!ze.getName().endsWith(".zip") && !ze.getName().endsWith(".jar") ) { // skip saxon .zip
|
||||
String name = ze.getName();
|
||||
InputStream in = zip;
|
||||
ByteArrayOutputStream b = new ByteArrayOutputStream();
|
||||
int n;
|
||||
byte[] buf = new byte[1024];
|
||||
while ((n = in.read(buf, 0, 1024)) > -1) {
|
||||
b.write(buf, 0, n);
|
||||
}
|
||||
map.put(name, b.toByteArray());
|
||||
}
|
||||
zip.closeEntry();
|
||||
}
|
||||
zip.close();
|
||||
}
|
||||
|
||||
private byte[] loadDefinitions(String definitions) throws FHIRException, IOException {
|
||||
byte[] defn;
|
||||
// if (Utilities.noString(definitions)) {
|
||||
// defn = loadFromUrl(MASTER_SOURCE);
|
||||
// } else
|
||||
if (definitions.startsWith("https:") || definitions.startsWith("http:")) {
|
||||
defn = loadFromUrl(definitions);
|
||||
} else if (new File(definitions).exists()) {
|
||||
defn = loadFromFile(definitions);
|
||||
} else
|
||||
throw new FHIRException("Unable to find FHIR validation Pack (source = "+definitions+")");
|
||||
return defn;
|
||||
}
|
||||
|
||||
private byte[] loadFromUrl(String src) throws IOException {
|
||||
URL url = new URL(src);
|
||||
byte[] str = IOUtils.toByteArray(url.openStream());
|
||||
return str;
|
||||
}
|
||||
|
||||
private byte[] loadFromFile(String src) throws IOException {
|
||||
FileInputStream in = new FileInputStream(src);
|
||||
byte[] b = new byte[in.available()];
|
||||
in.read(b);
|
||||
in.close();
|
||||
return b;
|
||||
}
|
||||
|
||||
|
||||
protected XmlPullParser loadXml(InputStream stream) throws XmlPullParserException, IOException {
|
||||
BufferedInputStream input = new BufferedInputStream(stream);
|
||||
XmlPullParserFactory factory = XmlPullParserFactory.newInstance(System.getProperty(XmlPullParserFactory.PROPERTY_NAME), null);
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
package org.hl7.fhir.r5.context;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyBoolean;
|
||||
import static org.mockito.ArgumentMatchers.argThat;
|
||||
|
@ -9,9 +9,9 @@ import static org.mockito.Mockito.mock;
|
|||
import static org.mockito.Mockito.times;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.*;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.hl7.fhir.r5.model.CapabilityStatement;
|
||||
import org.hl7.fhir.r5.model.CodeableConcept;
|
||||
|
@ -27,9 +27,13 @@ import org.hl7.fhir.r5.terminologies.ValueSetExpanderSimple;
|
|||
import org.hl7.fhir.r5.utils.validation.ValidationContextCarrier;
|
||||
import org.hl7.fhir.utilities.ToolingClientLogger;
|
||||
import org.hl7.fhir.utilities.validation.ValidationOptions;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.Arguments;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
import org.mockito.ArgumentMatcher;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.Mockito;
|
||||
|
@ -423,4 +427,31 @@ public class SimpleWorkerContextTests {
|
|||
Mockito.verify(context).setTxCaps(terminologyCapabilities);
|
||||
}
|
||||
|
||||
public static Stream<Arguments> zipSlipData() {
|
||||
|
||||
return Stream.of(
|
||||
Arguments.of("zip-slip/zip-slip.zip", "Entry with an illegal path: ../evil.txt"),
|
||||
Arguments.of("zip-slip/zip-slip-2.zip", "Entry with an illegal path: child/../../evil.txt"),
|
||||
Arguments.of("zip-slip/zip-slip-peer.zip", "Entry with an illegal path: ../childpeer/evil.txt"),
|
||||
Arguments.of("zip-slip/zip-slip-win.zip", "Entry with an illegal path: ../evil.txt")
|
||||
);
|
||||
}
|
||||
|
||||
@ParameterizedTest(name = "{index}: file {0}")
|
||||
@MethodSource("zipSlipData")
|
||||
public void testLoadFromClasspathZipSlip(String classPath, String expectedMessage) {
|
||||
RuntimeException thrown = Assertions.assertThrows(RuntimeException.class, () -> {new SimpleWorkerContext.SimpleWorkerContextBuilder().fromClassPath(classPath);});
|
||||
assertNotNull(thrown);
|
||||
assertEquals(expectedMessage, thrown.getMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLoadFromClasspathBinaries() throws IOException {
|
||||
SimpleWorkerContext simpleWorkerContext = new SimpleWorkerContext.SimpleWorkerContextBuilder().fromClassPath("zip-slip/zip-normal.zip");
|
||||
|
||||
final String testPath = "zip-normal/depth1/test.txt";
|
||||
assertTrue(simpleWorkerContext.getBinaryKeysAsSet().contains(testPath));
|
||||
String testFileContent = new String(simpleWorkerContext.getBinaryForKey(testPath), StandardCharsets.UTF_8);
|
||||
assertEquals("dummy file content", testFileContent);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,12 +5,16 @@ import org.junit.jupiter.api.Assertions;
|
|||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.TestInstance;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.Arguments;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
|
@ -18,24 +22,17 @@ import static org.junit.jupiter.api.Assertions.assertNotNull;
|
|||
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
||||
public class TerminologyCacheManagerTests implements ResourceLoaderTests {
|
||||
|
||||
public static final String ZIP_NORMAL_ZIP = "zip-normal.zip";
|
||||
public static final String ZIP_SLIP_ZIP = "zip-slip.zip";
|
||||
|
||||
public static final String ZIP_SLIP_2_ZIP = "zip-slip-2.zip";
|
||||
|
||||
public static final String ZIP_SLIP_WIN_ZIP = "zip-slip-win.zip";
|
||||
Path tempDir;
|
||||
|
||||
@BeforeAll
|
||||
public void beforeAll() throws IOException {
|
||||
tempDir = Files.createTempDirectory("terminology-cache-manager");
|
||||
tempDir.resolve("child").toFile().mkdir();
|
||||
getResourceAsInputStream("terminologyCacheManager", ZIP_SLIP_ZIP);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNormalZip() throws IOException {
|
||||
InputStream normalInputStream = getResourceAsInputStream( "terminologyCacheManager", ZIP_NORMAL_ZIP);
|
||||
InputStream normalInputStream = getResourceAsInputStream( "zip-slip", "zip-normal.zip");
|
||||
TerminologyCacheManager.unzip( normalInputStream, tempDir.toFile().getAbsolutePath());
|
||||
|
||||
Path expectedFilePath = tempDir.resolve("zip-normal").resolve("depth1").resolve("test.txt");
|
||||
|
@ -43,36 +40,26 @@ public class TerminologyCacheManagerTests implements ResourceLoaderTests {
|
|||
assertEquals("dummy file content", actualContent);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSlipZip() throws IOException {
|
||||
RuntimeException thrown = Assertions.assertThrows(RuntimeException.class, () -> {
|
||||
InputStream slipInputStream = getResourceAsInputStream( "terminologyCacheManager", ZIP_SLIP_ZIP);
|
||||
TerminologyCacheManager.unzip( slipInputStream, tempDir.toFile().getAbsolutePath());
|
||||
//Code under test
|
||||
});
|
||||
assertNotNull(thrown);
|
||||
assertEquals("Entry with an illegal path: ../evil.txt", thrown.getMessage());
|
||||
public static Stream<Arguments> zipSlipData() {
|
||||
|
||||
return Stream.of(
|
||||
Arguments.of("zip-slip.zip", "../evil.txt"),
|
||||
Arguments.of("zip-slip-2.zip", "child/../../evil.txt"),
|
||||
Arguments.of("zip-slip-peer.zip", "../childpeer/evil.txt"),
|
||||
Arguments.of("zip-slip-win.zip", "../evil.txt")
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSlip2Zip() throws IOException {
|
||||
@ParameterizedTest(name = "{index}: file {0}")
|
||||
@MethodSource("zipSlipData")
|
||||
public void testLoadFromClasspathZipSlip(String fileName, String expectedMessage) {
|
||||
RuntimeException thrown = Assertions.assertThrows(RuntimeException.class, () -> {
|
||||
InputStream slipInputStream = getResourceAsInputStream( "terminologyCacheManager", ZIP_SLIP_2_ZIP);
|
||||
InputStream slipInputStream = getResourceAsInputStream( "zip-slip", fileName);
|
||||
TerminologyCacheManager.unzip( slipInputStream, tempDir.toFile().getAbsolutePath());
|
||||
//Code under test
|
||||
});
|
||||
assertNotNull(thrown);
|
||||
assertEquals("Entry with an illegal path: child/../../evil.txt", thrown.getMessage());
|
||||
Assertions.assertTrue(thrown.getMessage().endsWith(expectedMessage));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSlipZipWin() throws IOException {
|
||||
RuntimeException thrown = Assertions.assertThrows(RuntimeException.class, () -> {
|
||||
InputStream slipInputStream = getResourceAsInputStream( "terminologyCacheManager", ZIP_SLIP_WIN_ZIP);
|
||||
TerminologyCacheManager.unzip( slipInputStream, tempDir.toFile().getAbsolutePath());
|
||||
//Code under test
|
||||
});
|
||||
assertNotNull(thrown);
|
||||
assertEquals("Entry with an illegal path: ../evil.txt", thrown.getMessage());
|
||||
}
|
||||
}
|
||||
|
|
Binary file not shown.
|
@ -613,39 +613,33 @@ public class Utilities {
|
|||
return s.toString();
|
||||
}
|
||||
|
||||
|
||||
private static boolean isPathRoot(String pathString) {
|
||||
boolean actual;
|
||||
Path path = Path.of(pathString);
|
||||
Path normalizedPath = path.normalize();
|
||||
actual = normalizedPath.equals(path.getRoot());
|
||||
return actual;
|
||||
}
|
||||
public static String path(String... args) throws IOException {
|
||||
StringBuilder s = new StringBuilder();
|
||||
boolean d = false;
|
||||
boolean first = true;
|
||||
boolean argIsNotEmptyOrNull = false;
|
||||
|
||||
if (args[0] == null || noString(args[0].trim())) {
|
||||
throw new RuntimeException("First entry cannot be null or empty");
|
||||
}
|
||||
|
||||
if (isPathRoot(args[0])) {
|
||||
throw new RuntimeException("First entry cannot be root: " + args[0]);
|
||||
}
|
||||
|
||||
for (String arg : args) {
|
||||
if (first && arg == null)
|
||||
continue;
|
||||
first = false;
|
||||
if (!d)
|
||||
d = !noString(arg);
|
||||
if (!argIsNotEmptyOrNull)
|
||||
argIsNotEmptyOrNull = !noString(arg);
|
||||
else if (!s.toString().endsWith(File.separator))
|
||||
s.append(File.separator);
|
||||
String a = arg;
|
||||
if (s.length() == 0) {
|
||||
if ("[tmp]".equals(a)) {
|
||||
if (hasCTempDir()) {
|
||||
a = C_TEMP_DIR;
|
||||
} else if (ToolGlobalSettings.hasTempPath()) {
|
||||
a = ToolGlobalSettings.getTempPath();
|
||||
} else {
|
||||
a = System.getProperty("java.io.tmpdir");
|
||||
}
|
||||
} else if ("[user]".equals(a)) {
|
||||
a = System.getProperty("user.home");
|
||||
} else if (a.startsWith("[") && a.endsWith("]")) {
|
||||
String ev = System.getenv(a.replace("[", "").replace("]", ""));
|
||||
if (ev != null) {
|
||||
a = ev;
|
||||
} else {
|
||||
a = "null";
|
||||
}
|
||||
}
|
||||
a = replaceVariables(a);
|
||||
}
|
||||
a = a.replace("\\", File.separator);
|
||||
a = a.replace("/", File.separator);
|
||||
|
@ -671,9 +665,34 @@ public class Utilities {
|
|||
} else
|
||||
s.append(a);
|
||||
}
|
||||
if (!Path.of(s.toString()).normalize().startsWith(Path.of(replaceVariables(args[0])).normalize())) {
|
||||
throw new RuntimeException("Computed path does not start with first element: " + String.join(", ", args));
|
||||
}
|
||||
return s.toString();
|
||||
}
|
||||
|
||||
private static String replaceVariables(String a) {
|
||||
if ("[tmp]".equals(a)) {
|
||||
if (hasCTempDir()) {
|
||||
return C_TEMP_DIR;
|
||||
} else if (ToolGlobalSettings.hasTempPath()) {
|
||||
return ToolGlobalSettings.getTempPath();
|
||||
} else {
|
||||
return System.getProperty("java.io.tmpdir");
|
||||
}
|
||||
} else if ("[user]".equals(a)) {
|
||||
return System.getProperty("user.home");
|
||||
} else if (a.startsWith("[") && a.endsWith("]")) {
|
||||
String ev = System.getenv(a.replace("[", "").replace("]", ""));
|
||||
if (ev != null) {
|
||||
return ev;
|
||||
} else {
|
||||
return "null";
|
||||
}
|
||||
}
|
||||
return a;
|
||||
}
|
||||
|
||||
private static boolean hasCTempDir() {
|
||||
if (!System.getProperty("os.name").toLowerCase().contains("win")) {
|
||||
return false;
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package org.hl7.fhir.utilities;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
import java.io.File;
|
||||
|
@ -7,11 +8,17 @@ import java.io.IOException;
|
|||
import java.nio.file.Paths;
|
||||
import java.text.MessageFormat;
|
||||
import java.util.Random;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.apache.commons.lang3.SystemUtils;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.condition.EnabledOnOs;
|
||||
import org.junit.jupiter.api.condition.OS;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.Arguments;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
|
||||
class UtilitiesTest {
|
||||
|
||||
|
@ -39,22 +46,22 @@ class UtilitiesTest {
|
|||
@DisplayName("Test Utilities.path maps temp directory correctly")
|
||||
public void testTempDirPath() throws IOException {
|
||||
if (ToolGlobalSettings.hasTempPath()) {
|
||||
Assertions.assertEquals(Utilities.path("[tmp]", TEST_TXT), ToolGlobalSettings.getTempPath() +File.separator+ TEST_TXT);
|
||||
assertEquals(Utilities.path("[tmp]", TEST_TXT), ToolGlobalSettings.getTempPath() +File.separator+ TEST_TXT);
|
||||
} else {
|
||||
Assertions.assertEquals(Utilities.path("[tmp]", TEST_TXT), getTempDirectory() + TEST_TXT);
|
||||
assertEquals(Utilities.path("[tmp]", TEST_TXT), getTempDirectory() + TEST_TXT);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Test Utilities.path maps user directory correctly")
|
||||
public void testUserDirPath() throws IOException {
|
||||
Assertions.assertEquals(Utilities.path("[user]", TEST_TXT), getUserDirectory() + TEST_TXT);
|
||||
assertEquals(Utilities.path("[user]", TEST_TXT), getUserDirectory() + TEST_TXT);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Test Utilities.path maps JAVA_HOME correctly")
|
||||
public void testJavaHomeDirPath() throws IOException {
|
||||
Assertions.assertEquals(Utilities.path("[JAVA_HOME]", TEST_TXT), getJavaHomeDirectory() + TEST_TXT);
|
||||
assertEquals(Utilities.path("[JAVA_HOME]", TEST_TXT), getJavaHomeDirectory() + TEST_TXT);
|
||||
}
|
||||
|
||||
private String getJavaHomeDirectory() {
|
||||
|
@ -171,24 +178,24 @@ class UtilitiesTest {
|
|||
@Test
|
||||
@DisplayName("Decimal Reasoning Tests")
|
||||
void testDecimalRoutines() {
|
||||
Assertions.assertEquals("-0.500000", Utilities.lowBoundaryForDecimal("0", 6));
|
||||
Assertions.assertEquals("0.50000000", Utilities.lowBoundaryForDecimal("1", 8));
|
||||
Assertions.assertEquals("0.950000", Utilities.lowBoundaryForDecimal("1.0", 6));
|
||||
Assertions.assertEquals("0.95", Utilities.lowBoundaryForDecimal("1.0", 2));
|
||||
Assertions.assertEquals("-1.05000000", Utilities.lowBoundaryForDecimal("-1.0", 8));
|
||||
Assertions.assertEquals("1.23", Utilities.lowBoundaryForDecimal("1.234", 2));
|
||||
Assertions.assertEquals("1.57", Utilities.lowBoundaryForDecimal("1.567", 2));
|
||||
assertEquals("-0.500000", Utilities.lowBoundaryForDecimal("0", 6));
|
||||
assertEquals("0.50000000", Utilities.lowBoundaryForDecimal("1", 8));
|
||||
assertEquals("0.950000", Utilities.lowBoundaryForDecimal("1.0", 6));
|
||||
assertEquals("0.95", Utilities.lowBoundaryForDecimal("1.0", 2));
|
||||
assertEquals("-1.05000000", Utilities.lowBoundaryForDecimal("-1.0", 8));
|
||||
assertEquals("1.23", Utilities.lowBoundaryForDecimal("1.234", 2));
|
||||
assertEquals("1.57", Utilities.lowBoundaryForDecimal("1.567", 2));
|
||||
|
||||
Assertions.assertEquals("0.50000000", Utilities.highBoundaryForDecimal("0", 8));
|
||||
Assertions.assertEquals("1.500000", Utilities.highBoundaryForDecimal("1", 6));
|
||||
Assertions.assertEquals("1.0500000000", Utilities.highBoundaryForDecimal("1.0", 10));
|
||||
Assertions.assertEquals("-0.9500", Utilities.highBoundaryForDecimal("-1.0", 4));
|
||||
assertEquals("0.50000000", Utilities.highBoundaryForDecimal("0", 8));
|
||||
assertEquals("1.500000", Utilities.highBoundaryForDecimal("1", 6));
|
||||
assertEquals("1.0500000000", Utilities.highBoundaryForDecimal("1.0", 10));
|
||||
assertEquals("-0.9500", Utilities.highBoundaryForDecimal("-1.0", 4));
|
||||
|
||||
Assertions.assertEquals(0, Utilities.getDecimalPrecision("0"));
|
||||
Assertions.assertEquals(0, Utilities.getDecimalPrecision("1"));
|
||||
Assertions.assertEquals(1, Utilities.getDecimalPrecision("1.0"));
|
||||
Assertions.assertEquals(1, Utilities.getDecimalPrecision("-1.0"));
|
||||
Assertions.assertEquals(4, Utilities.getDecimalPrecision("-1.0200"));
|
||||
assertEquals(0, Utilities.getDecimalPrecision("0"));
|
||||
assertEquals(0, Utilities.getDecimalPrecision("1"));
|
||||
assertEquals(1, Utilities.getDecimalPrecision("1.0"));
|
||||
assertEquals(1, Utilities.getDecimalPrecision("-1.0"));
|
||||
assertEquals(4, Utilities.getDecimalPrecision("-1.0200"));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -212,23 +219,172 @@ class UtilitiesTest {
|
|||
// Assertions.assertEquals("2021-04-04T21:22:23.999Z", Utilities.highBoundaryForDate("2021-04-04T21:22:23Z"));
|
||||
// Assertions.assertEquals("2021-04-04T21:22:23.245+10:00", Utilities.highBoundaryForDate("2021-04-04T21:22:23.245+10:00"));
|
||||
|
||||
Assertions.assertEquals(8, Utilities.getDatePrecision("1900-01-01"));
|
||||
Assertions.assertEquals(4, Utilities.getDatePrecision("1900"));
|
||||
Assertions.assertEquals(6, Utilities.getDatePrecision("1900-06"));
|
||||
Assertions.assertEquals(14, Utilities.getDatePrecision("1900-06-06T14:00:00"));
|
||||
Assertions.assertEquals(17, Utilities.getDatePrecision("1900-06-06T14:00:00.000"));
|
||||
Assertions.assertEquals(8, Utilities.getDatePrecision("1900-01-01Z"));
|
||||
Assertions.assertEquals(4, Utilities.getDatePrecision("1900Z"));
|
||||
Assertions.assertEquals(6, Utilities.getDatePrecision("1900-06Z"));
|
||||
Assertions.assertEquals(14, Utilities.getDatePrecision("1900-06-06T14:00:00Z"));
|
||||
Assertions.assertEquals(17, Utilities.getDatePrecision("1900-06-06T14:00:00.000Z"));
|
||||
Assertions.assertEquals(8, Utilities.getDatePrecision("1900-01-01+10:00"));
|
||||
Assertions.assertEquals(4, Utilities.getDatePrecision("1900+10:00"));
|
||||
Assertions.assertEquals(6, Utilities.getDatePrecision("1900-06+10:00"));
|
||||
Assertions.assertEquals(14, Utilities.getDatePrecision("1900-06-06T14:00:00+10:00"));
|
||||
Assertions.assertEquals(17, Utilities.getDatePrecision("1900-06-06T14:00:00.000-10:00"));
|
||||
assertEquals(8, Utilities.getDatePrecision("1900-01-01"));
|
||||
assertEquals(4, Utilities.getDatePrecision("1900"));
|
||||
assertEquals(6, Utilities.getDatePrecision("1900-06"));
|
||||
assertEquals(14, Utilities.getDatePrecision("1900-06-06T14:00:00"));
|
||||
assertEquals(17, Utilities.getDatePrecision("1900-06-06T14:00:00.000"));
|
||||
assertEquals(8, Utilities.getDatePrecision("1900-01-01Z"));
|
||||
assertEquals(4, Utilities.getDatePrecision("1900Z"));
|
||||
assertEquals(6, Utilities.getDatePrecision("1900-06Z"));
|
||||
assertEquals(14, Utilities.getDatePrecision("1900-06-06T14:00:00Z"));
|
||||
assertEquals(17, Utilities.getDatePrecision("1900-06-06T14:00:00.000Z"));
|
||||
assertEquals(8, Utilities.getDatePrecision("1900-01-01+10:00"));
|
||||
assertEquals(4, Utilities.getDatePrecision("1900+10:00"));
|
||||
assertEquals(6, Utilities.getDatePrecision("1900-06+10:00"));
|
||||
assertEquals(14, Utilities.getDatePrecision("1900-06-06T14:00:00+10:00"));
|
||||
assertEquals(17, Utilities.getDatePrecision("1900-06-06T14:00:00.000-10:00"));
|
||||
}
|
||||
|
||||
public static Stream<Arguments> windowsRootPaths() {
|
||||
return Stream.of(
|
||||
Arguments.of((Object)new String[]{"C:"}),
|
||||
Arguments.of((Object)new String[]{"D:"}),
|
||||
Arguments.of((Object)new String[]{"C:", "anything"}),
|
||||
Arguments.of((Object)new String[]{"D:", "anything"}),
|
||||
Arguments.of((Object)new String[]{"C:/", "anything"}),
|
||||
Arguments.of((Object)new String[]{"C:/.", "anything"}),
|
||||
Arguments.of((Object)new String[]{"C:\\"}),
|
||||
Arguments.of((Object)new String[]{"D:\\"}),
|
||||
Arguments.of((Object)new String[]{"C:/child/.."}),
|
||||
Arguments.of((Object)new String[]{"C:/child/..", "anything"}),
|
||||
Arguments.of((Object)new String[]{"C:/child/../child/.."}),
|
||||
Arguments.of((Object)new String[]{"C:/child/../child/..", "anything"}),
|
||||
Arguments.of((Object)new String[]{"C:/child/second/../.."}),
|
||||
Arguments.of((Object)new String[]{"C:/child/second/../..", "anything"}),
|
||||
Arguments.of((Object)new String[]{"C:\\child\\.."}),
|
||||
Arguments.of((Object)new String[]{"C:\\child\\..", "anything"}),
|
||||
Arguments.of((Object)new String[]{"C:\\child\\..\\child/.."}),
|
||||
Arguments.of((Object)new String[]{"C:\\child\\..\\child\\..", "anything"}),
|
||||
Arguments.of((Object)new String[]{"C:\\child\\second\\..\\.."}),
|
||||
Arguments.of((Object)new String[]{"C:\\child\\second\\..\\..", "anything"})
|
||||
);
|
||||
}
|
||||
@ParameterizedTest
|
||||
@MethodSource("windowsRootPaths")
|
||||
@EnabledOnOs({OS.WINDOWS})
|
||||
public void testPathCantStartWithRootWindows(String[] pathStrings) {
|
||||
testCantStartWithRoot(pathStrings);
|
||||
}
|
||||
|
||||
public static Stream<Arguments> macAndLinuxRootPaths() {
|
||||
return Stream.of(
|
||||
Arguments.of((Object)new String[]{"/"}),
|
||||
Arguments.of((Object)new String[]{"/", "anything"}),
|
||||
Arguments.of((Object)new String[]{"//"}),
|
||||
Arguments.of((Object)new String[]{"//", "anything"}),
|
||||
Arguments.of((Object)new String[]{"//child/.."}),
|
||||
Arguments.of((Object)new String[]{"//child/..", "anything"}),
|
||||
Arguments.of((Object)new String[]{"//child/../child/.."}),
|
||||
Arguments.of((Object)new String[]{"//child/../child/..", "anything"}),
|
||||
Arguments.of((Object)new String[]{"//child/second/../.."}),
|
||||
Arguments.of((Object)new String[]{"//child/second/../..", "anything"})
|
||||
);
|
||||
}
|
||||
@ParameterizedTest
|
||||
@MethodSource("macAndLinuxRootPaths")
|
||||
@EnabledOnOs({OS.MAC, OS.LINUX})
|
||||
public void testPathCantStartWithRootMacAndLinux(String[] pathStrings) {
|
||||
testCantStartWithRoot(pathStrings);
|
||||
}
|
||||
|
||||
private static void testCantStartWithRoot(String[] pathStrings) {
|
||||
RuntimeException thrown = Assertions.assertThrows(RuntimeException.class, () -> {
|
||||
Utilities.path(pathStrings);
|
||||
});
|
||||
assertTrue(thrown.getMessage().endsWith(pathStrings[0]));
|
||||
}
|
||||
|
||||
public static Stream<Arguments> macAndLinuxNonFirstElementStartPaths() {
|
||||
return Stream.of(
|
||||
Arguments.of((Object)new String[]{"/root", ".."}),
|
||||
Arguments.of((Object)new String[]{"/root", "child/../.."}),
|
||||
Arguments.of((Object)new String[]{"/root", "child", "/../.."}),
|
||||
Arguments.of((Object)new String[]{"/root", "child", "../.."}),
|
||||
Arguments.of((Object)new String[]{"/root/a", "../.."}),
|
||||
Arguments.of((Object)new String[]{"/root/a", "child/../.."}),
|
||||
Arguments.of((Object)new String[]{"/root/a", "child", "/../../.."}),
|
||||
Arguments.of((Object)new String[]{"/root/a", "child", "../../.."})
|
||||
);
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("macAndLinuxNonFirstElementStartPaths")
|
||||
@EnabledOnOs({OS.MAC, OS.LINUX})
|
||||
public void testPathMustStartWithFirstElementMacAndLinux(String[] pathStrings) {
|
||||
testPathMustStartWithFirstElement(pathStrings);
|
||||
}
|
||||
|
||||
private static void testPathMustStartWithFirstElement(String[] pathStrings) {
|
||||
RuntimeException thrown = Assertions.assertThrows(RuntimeException.class, () -> {
|
||||
Utilities.path(pathStrings);
|
||||
});
|
||||
assertTrue(thrown.getMessage().startsWith("Computed path does not start with first element: " + pathStrings[0]));
|
||||
}
|
||||
|
||||
public static Stream<Arguments> macAndLinuxValidPaths() {
|
||||
return Stream.of(
|
||||
Arguments.of((Object) new String[]{"/root"}, "/root"),
|
||||
Arguments.of( (Object) new String[]{"/root", "child"}, "/root/child"),
|
||||
Arguments.of((Object) new String[]{"/root", "../root/child"}, "/root/child"),
|
||||
Arguments.of((Object) new String[]{"/root", "child", "anotherchild"}, "/root/child/anotherchild")
|
||||
);
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("macAndLinuxValidPaths")
|
||||
@EnabledOnOs({OS.MAC, OS.LINUX})
|
||||
public void testValidPathsMacAndLinux(String[] pathStrings, String expectedPath) throws IOException {
|
||||
testValidPath(pathStrings,expectedPath);
|
||||
}
|
||||
|
||||
public static Stream<Arguments> windowsValidPaths() {
|
||||
return Stream.of(
|
||||
Arguments.of((Object) new String[]{"C://root"}, "C:\\\\root"),
|
||||
Arguments.of( (Object) new String[]{"C://root", "child"}, "C:\\\\root\\child"),
|
||||
Arguments.of((Object) new String[]{"C://root", "../root/child"}, "C:\\\\root\\child"),
|
||||
Arguments.of((Object) new String[]{"C://root", "child", "anotherchild"}, "C:\\\\root\\child\\anotherchild"),
|
||||
Arguments.of((Object) new String[]{"C:\\\\root"}, "C:\\\\root"),
|
||||
Arguments.of( (Object) new String[]{"C:\\\\root", "child"}, "C:\\\\root\\child"),
|
||||
Arguments.of((Object) new String[]{"C:\\\\root", "..\\root\\child"}, "C:\\\\root\\child"),
|
||||
Arguments.of((Object) new String[]{"C:\\\\root", "child", "anotherchild"}, "C:\\\\root\\child\\anotherchild")
|
||||
);
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("windowsValidPaths")
|
||||
@EnabledOnOs({OS.WINDOWS})
|
||||
public void testValidPathsWindows(String[] pathStrings, String expectedPath) throws IOException {
|
||||
testValidPath(pathStrings,expectedPath);
|
||||
}
|
||||
|
||||
private static void testValidPath(String[] pathsStrings, String expectedPath) throws IOException {
|
||||
String actualPath = Utilities.path(pathsStrings);
|
||||
assertEquals(expectedPath, actualPath);
|
||||
}
|
||||
|
||||
public static Stream<Arguments> nullOrEmptyFirstEntryPaths() {
|
||||
return Stream.of(
|
||||
Arguments.of((Object)new String[]{null, "child"}),
|
||||
Arguments.of((Object)new String[]{null, "child/otherchild"}),
|
||||
Arguments.of((Object)new String[]{null, "child", "otherchild"}),
|
||||
Arguments.of((Object)new String[]{"", "child"}),
|
||||
Arguments.of((Object)new String[]{"", "child/otherchild"}),
|
||||
Arguments.of((Object)new String[]{"", "child", "otherchild"}),
|
||||
Arguments.of((Object)new String[]{" ", "child"}),
|
||||
Arguments.of((Object)new String[]{" ", "child/otherchild"}),
|
||||
Arguments.of((Object)new String[]{" ", "child", "otherchild"})
|
||||
);
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("nullOrEmptyFirstEntryPaths")
|
||||
public void testNullOrEmptyFirstPathEntryFails(String[] pathsStrings) {
|
||||
RuntimeException thrown = Assertions.assertThrows(RuntimeException.class, () -> {
|
||||
Utilities.path(pathsStrings);
|
||||
});
|
||||
assertEquals("First entry cannot be null or empty",thrown.getMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("trimWS tests")
|
||||
|
@ -266,5 +422,4 @@ class UtilitiesTest {
|
|||
Assertions.assertFalse("\u0009\n\u000B\u000C\r\u0020\u0085\u00A0\u1680\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200A\u2028\u2029\u202F\u205F\u3000".matches("^.+$"));
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -6,6 +6,7 @@ import java.io.File;
|
|||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
|
@ -374,16 +375,19 @@ public class IgLoader {
|
|||
protected Map<String, byte[]> readZip(InputStream stream) throws IOException {
|
||||
Map<String, byte[]> res = new HashMap<>();
|
||||
ZipInputStream zip = new ZipInputStream(stream);
|
||||
ZipEntry ze;
|
||||
while ((ze = zip.getNextEntry()) != null) {
|
||||
String name = ze.getName();
|
||||
ZipEntry zipEntry;
|
||||
while ((zipEntry = zip.getNextEntry()) != null) {
|
||||
String entryName = zipEntry.getName();
|
||||
if (entryName.contains("..") || Path.of(entryName).isAbsolute()) {
|
||||
throw new RuntimeException("Entry with an illegal path: " + entryName);
|
||||
}
|
||||
ByteArrayOutputStream b = new ByteArrayOutputStream();
|
||||
int n;
|
||||
byte[] buf = new byte[1024];
|
||||
while ((n = ((InputStream) zip).read(buf, 0, 1024)) > -1) {
|
||||
b.write(buf, 0, n);
|
||||
}
|
||||
res.put(name, b.toByteArray());
|
||||
res.put(entryName, b.toByteArray());
|
||||
zip.closeEntry();
|
||||
}
|
||||
zip.close();
|
||||
|
|
|
@ -35,11 +35,13 @@ import java.io.FileInputStream;
|
|||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.xml.parsers.DocumentBuilder;
|
||||
import javax.xml.parsers.DocumentBuilderFactory;
|
||||
|
||||
|
@ -478,7 +480,7 @@ public class UtilitiesXTests {
|
|||
|
||||
public static boolean findTestResource(String... paths) throws IOException {
|
||||
if (new File("../../fhir-test-cases").exists() && isTryToLoadFromFileSystem()) {
|
||||
String n = Utilities.path(System.getProperty("user.dir"), "..", "..", "fhir-test-cases", Utilities.path(paths));
|
||||
String n = Utilities.path(getUserDirFhirTestCases(), Utilities.path(paths));
|
||||
return new File(n).exists();
|
||||
} else {
|
||||
String classpath = ("/org/hl7/fhir/testcases/"+ Utilities.pathURL(paths));
|
||||
|
@ -498,7 +500,7 @@ public class UtilitiesXTests {
|
|||
|
||||
public static String loadTestResource(String... paths) throws IOException {
|
||||
if (new File("../../fhir-test-cases").exists() && isTryToLoadFromFileSystem()) {
|
||||
String n = Utilities.path(System.getProperty("user.dir"), "..", "..", "fhir-test-cases", Utilities.path(paths));
|
||||
String n = Utilities.path(getUserDirFhirTestCases(), Utilities.path(paths));
|
||||
// ok, we'll resolve this locally
|
||||
return TextFile.fileToString(new File(n));
|
||||
} else {
|
||||
|
@ -515,9 +517,14 @@ public class UtilitiesXTests {
|
|||
}
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
private static String getUserDirFhirTestCases() {
|
||||
return Path.of(System.getProperty("user.dir"), "..", "..", "fhir-test-cases").normalize().toString();
|
||||
}
|
||||
|
||||
public static InputStream loadTestResourceStream(String... paths) throws IOException {
|
||||
if (new File("../../fhir-test-cases").exists() && isTryToLoadFromFileSystem()) {
|
||||
String n = Utilities.path(System.getProperty("user.dir"), "..", "..", "fhir-test-cases", Utilities.path(paths));
|
||||
String n = Utilities.path(getUserDirFhirTestCases(), Utilities.path(paths));
|
||||
return new FileInputStream(n);
|
||||
} else {
|
||||
String classpath = ("/org/hl7/fhir/testcases/"+ Utilities.pathURL(paths));
|
||||
|
|
|
@ -4,6 +4,7 @@ import org.hl7.fhir.exceptions.FHIRException;
|
|||
import org.hl7.fhir.r5.context.SimpleWorkerContext;
|
||||
import org.hl7.fhir.r5.model.ImplementationGuide;
|
||||
import org.hl7.fhir.utilities.npm.FilesystemPackageCacheManager;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
|
@ -14,15 +15,13 @@ import org.mockito.Mockito;
|
|||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.*;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertLinesMatch;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
|
@ -97,4 +96,43 @@ public class IgLoaderTests {
|
|||
|
||||
assertLinesMatch(Arrays.asList(".*Unsupported FHIR Version.*"), Arrays.asList(exception.getMessage()));
|
||||
}
|
||||
|
||||
public static Stream<Arguments> zipSlipData() {
|
||||
|
||||
return Stream.of(
|
||||
Arguments.of("/zip-slip/zip-slip.zip", "Entry with an illegal path: ../evil.txt"),
|
||||
Arguments.of("/zip-slip/zip-slip-2.zip", "Entry with an illegal path: child/../../evil.txt"),
|
||||
Arguments.of("/zip-slip/zip-slip-peer.zip", "Entry with an illegal path: ../childpeer/evil.txt"),
|
||||
Arguments.of("/zip-slip/zip-slip-win.zip", "Entry with an illegal path: ../evil.txt")
|
||||
);
|
||||
}
|
||||
|
||||
@ParameterizedTest(name = "{index}: file {0}")
|
||||
@MethodSource("zipSlipData")
|
||||
public void testReadZipSlip(String classPath, String expectedMessage) {
|
||||
RuntimeException thrown = Assertions.assertThrows(RuntimeException.class, () -> {
|
||||
IgLoader igLoader = Mockito.spy(new IgLoader(
|
||||
filesystemPackageCacheManager,
|
||||
simpleWorkerContext,
|
||||
"4.0.1"
|
||||
));
|
||||
igLoader.readZip(IgLoaderTests.class.getResourceAsStream((classPath)));
|
||||
});
|
||||
assertNotNull(thrown);
|
||||
Assertions.assertEquals(expectedMessage, thrown.getMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadZip() throws IOException {
|
||||
IgLoader igLoader = Mockito.spy(new IgLoader(
|
||||
filesystemPackageCacheManager,
|
||||
simpleWorkerContext,
|
||||
"4.0.1"
|
||||
));
|
||||
Map<String, byte[]> map = igLoader.readZip(IgLoaderTests.class.getResourceAsStream("/zip-slip/zip-normal.zip"));
|
||||
final String testPath = "zip-normal/depth1/test.txt";
|
||||
assertTrue(map.containsKey(testPath));
|
||||
String testFileContent = new String(map.get(testPath), StandardCharsets.UTF_8);
|
||||
Assertions.assertEquals("dummy file content", testFileContent);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,10 +5,14 @@ import org.junit.jupiter.api.Assertions;
|
|||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.TestInstance;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.Arguments;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
|
@ -19,7 +23,7 @@ public class ScannerTest implements ResourceLoaderTests {
|
|||
public static final String ZIP_NORMAL_ZIP = "zip-normal.zip";
|
||||
public static final String ZIP_SLIP_ZIP = "zip-slip.zip";
|
||||
public static final String ZIP_SLIP_2_ZIP = "zip-slip-2.zip";
|
||||
|
||||
public static final String ZIP_SLIP_PEER_ZIP = "zip-slip-peer.zip";
|
||||
public static final String ZIP_SLIP_WIN_ZIP = "zip-slip-win.zip";
|
||||
|
||||
Path tempDir;
|
||||
|
@ -28,6 +32,8 @@ public class ScannerTest implements ResourceLoaderTests {
|
|||
|
||||
Path zipSlip2Path;
|
||||
|
||||
Path zipSlipPeerPath;
|
||||
|
||||
Path zipSlipWinPath;
|
||||
|
||||
@BeforeAll
|
||||
|
@ -37,12 +43,14 @@ public class ScannerTest implements ResourceLoaderTests {
|
|||
zipNormalPath = tempDir.resolve(ZIP_NORMAL_ZIP);
|
||||
zipSlipPath = tempDir.resolve(ZIP_SLIP_ZIP);
|
||||
zipSlip2Path = tempDir.resolve(ZIP_SLIP_2_ZIP);
|
||||
zipSlipPeerPath = tempDir.resolve(ZIP_SLIP_PEER_ZIP);
|
||||
zipSlipWinPath = tempDir.resolve(ZIP_SLIP_WIN_ZIP);
|
||||
|
||||
copyResourceToFile(zipNormalPath, "scanner", ZIP_NORMAL_ZIP);
|
||||
copyResourceToFile(zipSlipPath, "scanner", ZIP_SLIP_ZIP);
|
||||
copyResourceToFile(zipSlip2Path, "scanner", ZIP_SLIP_2_ZIP);
|
||||
copyResourceToFile(zipSlipWinPath, "scanner", ZIP_SLIP_WIN_ZIP);
|
||||
copyResourceToFile(zipNormalPath, "zip-slip", ZIP_NORMAL_ZIP);
|
||||
copyResourceToFile(zipSlipPath, "zip-slip", ZIP_SLIP_ZIP);
|
||||
copyResourceToFile(zipSlip2Path, "zip-slip", ZIP_SLIP_2_ZIP);
|
||||
copyResourceToFile(zipSlipPeerPath, "zip-slip", ZIP_SLIP_PEER_ZIP);
|
||||
copyResourceToFile(zipSlipWinPath, "zip-slip", ZIP_SLIP_WIN_ZIP);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -55,36 +63,25 @@ public class ScannerTest implements ResourceLoaderTests {
|
|||
assertEquals("dummy file content", actualContent);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSlipZip() throws IOException {
|
||||
RuntimeException thrown = Assertions.assertThrows(RuntimeException.class, () -> {
|
||||
Scanner scanner = new Scanner(null,null,null,null);
|
||||
scanner.unzip(zipSlipPath.toFile().getAbsolutePath(), tempDir.toFile().getAbsolutePath());
|
||||
//Code under test
|
||||
});
|
||||
assertNotNull(thrown);
|
||||
assertEquals("Entry with an illegal path: ../evil.txt", thrown.getMessage());
|
||||
public Stream<Arguments> zipSlipData() {
|
||||
|
||||
return Stream.of(
|
||||
Arguments.of(zipSlipPath, "Entry with an illegal path: ../evil.txt"),
|
||||
Arguments.of(zipSlip2Path, "Entry with an illegal path: child/../../evil.txt"),
|
||||
Arguments.of(zipSlipPeerPath, "Entry with an illegal path: ../childpeer/evil.txt"),
|
||||
Arguments.of(zipSlipWinPath, "Entry with an illegal path: ../evil.txt")
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSlipZip2() throws IOException {
|
||||
@ParameterizedTest(name = "{index}: file {0}")
|
||||
@MethodSource("zipSlipData")
|
||||
public void testUnzipZipSlip(Path path, String expectedMessage) {
|
||||
RuntimeException thrown = Assertions.assertThrows(RuntimeException.class, () -> {
|
||||
Scanner scanner = new Scanner(null,null,null,null);
|
||||
scanner.unzip(zipSlip2Path.toFile().getAbsolutePath(), tempDir.toFile().getAbsolutePath());
|
||||
//Code under test
|
||||
scanner.unzip(path.toFile().getAbsolutePath(), tempDir.toFile().getAbsolutePath());
|
||||
});
|
||||
assertNotNull(thrown);
|
||||
assertEquals("Entry with an illegal path: child/../../evil.txt", thrown.getMessage());
|
||||
assertEquals(expectedMessage, thrown.getMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSlipZipWin() throws IOException {
|
||||
RuntimeException thrown = Assertions.assertThrows(RuntimeException.class, () -> {
|
||||
Scanner scanner = new Scanner(null,null,null,null);
|
||||
scanner.unzip(zipSlipWinPath.toFile().getAbsolutePath(), tempDir.toFile().getAbsolutePath());
|
||||
//Code under test
|
||||
});
|
||||
assertNotNull(thrown);
|
||||
assertEquals("Entry with an illegal path: ../evil.txt", thrown.getMessage());
|
||||
}
|
||||
}
|
||||
|
|
Binary file not shown.
Loading…
Reference in New Issue