Improve URL validation + fix NPE in Measure validation

This commit is contained in:
Grahame Grieve 2020-04-17 10:58:06 +10:00
parent 549abaa799
commit 4d30314a3d
5 changed files with 34 additions and 96 deletions

View File

@ -37,6 +37,7 @@ import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Map.Entry;
import java.util.UUID; import java.util.UUID;
import java.util.zip.ZipEntry; import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream; import java.util.zip.ZipInputStream;
@ -114,90 +115,17 @@ public class ProfileComparer implements ProfileKnowledgeProvider {
this.context = context; this.context = context;
this.keygen = keygen; this.keygen = keygen;
this.folder = folder; this.folder = folder;
if (!new File(Utilities.path(folder, "conparison-zip-marker.bin")).exists()) { for (Entry<String, byte[]> e : context.getBinaries().entrySet()) {
String f = Utilities.path(folder, "comparison.zip"); TextFile.bytesToFile(e.getValue(), Utilities.path(folder, e.getKey()));
download("http://www.fhir.org/archive/comparison.zip", f);
unzip(f, folder);
} }
} }
private void download(String address, String filename) throws IOException {
// System.out.print("Download "+address+" to "+filename);
URL url = new URL(address);
URLConnection c = url.openConnection();
InputStream s = c.getInputStream();
FileOutputStream f = new FileOutputStream(filename);
transfer(s, f, 1024);
f.close();
// System.out.println(" ... "+new File(filename).length()+" bytes");
}
public static void transfer(InputStream in, OutputStream out, int buffer) throws IOException {
byte[] read = new byte[buffer]; // Your buffer size.
while (0 < (buffer = in.read(read)))
out.write(read, 0, buffer);
}
/**
* Size of the buffer to read/write data
*/
private static final int BUFFER_SIZE = 4096;
/**
* Extracts a zip file specified by the zipFilePath to a directory specified by
* destDirectory (will be created if does not exists)
* @param zipFilePath
* @param destDirectory
* @throws IOException
*/
public void unzip(String zipFilePath, String destDirectory) throws IOException {
File destDir = new File(destDirectory);
if (!destDir.exists()) {
destDir.mkdir();
}
ZipInputStream zipIn = new ZipInputStream(new FileInputStream(zipFilePath));
ZipEntry entry = zipIn.getNextEntry();
// iterates over entries in the zip file
while (entry != null) {
String filePath = destDirectory + File.separator + entry.getName();
if (!entry.isDirectory()) {
// if the entry is a file, extracts it
extractFile(zipIn, filePath);
} else {
// if the entry is a directory, make the directory
File dir = new File(filePath);
dir.mkdir();
}
zipIn.closeEntry();
entry = zipIn.getNextEntry();
}
zipIn.close();
}
/**
* Extracts a zip entry (file entry)
* @param zipIn
* @param filePath
* @throws IOException
*/
private void extractFile(ZipInputStream zipIn, String filePath) throws IOException {
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(filePath));
byte[] bytesIn = new byte[BUFFER_SIZE];
int read = 0;
while ((read = zipIn.read(bytesIn)) != -1) {
bos.write(bytesIn, 0, read);
}
bos.close();
}
public ProfileComparer(IWorkerContext context, String folder) throws IOException { public ProfileComparer(IWorkerContext context, String folder) throws IOException {
super(); super();
this.context = context; this.context = context;
this.folder = folder; this.folder = folder;
if (!new File(Utilities.path(folder, "conparison-zip-marker.bin")).exists()) { for (Entry<String, byte[]> e : context.getBinaries().entrySet()) {
String f = Utilities.path(folder, "comparison.zip"); TextFile.bytesToFile(e.getValue(), Utilities.path(folder, e.getKey()));
download("https://www.fhir.org/archive/comparison.zip", f);
unzip(f, folder);
} }
} }

View File

@ -502,4 +502,6 @@ public class I18nConstants {
//public static final String //public static final String
//public static final String //public static final String
//public static final String //public static final String
public static final String XHTML_URL_EMPTY = "XHTML_URL_EMPTY";
public static final String XHTML_URL_INVALID_CHARS = "XHTML_URL_INVALID_CHARS";
} }

View File

@ -20,10 +20,10 @@ Bundle_MSG_Event_Count = Expected {0} but found {1} event elements
Bundle_Document_Date_Missing = A document must have a date {0} Bundle_Document_Date_Missing = A document must have a date {0}
Bundle_Document_Date_Missing_html = [(type = 'document') implies (meta.lastUpdated.hasValue())] Bundle_Document_Date_Missing_html = [(type = 'document') implies (meta.lastUpdated.hasValue())]
CapabalityStatement_CS_SP_WrongType = Type mismatch - SearchParameter "{0}" type is {1}, but type here is {2} CapabalityStatement_CS_SP_WrongType = Type mismatch - SearchParameter "{0}" type is {1}, but type here is {2}
CodeSystem_CS_VS_IncludeDetails = CodeSystem {0} has a ''all system'' value set of {1}, but the include has extra details CodeSystem_CS_VS_IncludeDetails = CodeSystem {0} has an ''all system'' value set of {1}, but the include has extra details
CodeSystem_CS_VS_Invalid = CodeSystem {0} has a ''all system'' value set of {1}, but doesn''t have a single include CodeSystem_CS_VS_Invalid = CodeSystem {0} has an ''all system'' value set of {1}, but doesn''t have a single include
CodeSystem_CS_VS_MisMatch = CodeSystem {0} has a ''all system'' value set of {1}, but it is an expansion CodeSystem_CS_VS_MisMatch = CodeSystem {0} has an ''all system'' value set of {1}, but it is an expansion
CodeSystem_CS_VS_WrongSystem = CodeSystem {0} has a ''all system'' value set of {1}, but doesn''t have a matching system ({2}) CodeSystem_CS_VS_WrongSystem = CodeSystem {0} has an ''all system'' value set of {1}, but doesn''t have a matching system ({2})
Extension_EXT_Context_Wrong = The extension {0} is not allowed to be used at this point (allowed = {1}; this element is [{2}) Extension_EXT_Context_Wrong = The extension {0} is not allowed to be used at this point (allowed = {1}; this element is [{2})
Extension_EXT_Count_Mismatch = Extensions count mismatch: expected {0} but found {1} Extension_EXT_Count_Mismatch = Extensions count mismatch: expected {0} but found {1}
Extension_EXT_Count_NotFound = Extension count mismatch: unable to find extension: {0} Extension_EXT_Count_NotFound = Extension count mismatch: unable to find extension: {0}
@ -436,7 +436,7 @@ documentmsg = (document)
xml_attr_value_invalid = The XML Attribute {0} has an illegal character xml_attr_value_invalid = The XML Attribute {0} has an illegal character
xml_encoding_invalid = The XML encoding is invalid (must be UTF-8) xml_encoding_invalid = The XML encoding is invalid (must be UTF-8)
xml_stated_encoding_invalid = The XML encoding stated in the header is invalid (must be "UTF-8" if stated) xml_stated_encoding_invalid = The XML encoding stated in the header is invalid (must be "UTF-8" if stated)
XHTML_URL_INVALID = The URL {0} is not valid XHTML_URL_INVALID = The URL {0} is not valid ({1})
MEASURE_MR_GRP_NO_CODE = Group should have a code that matches the group definition in the measure MEASURE_MR_GRP_NO_CODE = Group should have a code that matches the group definition in the measure
MEASURE_MR_GRP_UNK_CODE = The code for this group has no match in the measure definition MEASURE_MR_GRP_UNK_CODE = The code for this group has no match in the measure definition
MEASURE_MR_GRP_DUPL_CODE = The code for this group is duplicated with another group MEASURE_MR_GRP_DUPL_CODE = The code for this group is duplicated with another group
@ -480,4 +480,5 @@ MEASURE_M_CRITERIA_CQL_ERROR = Error in {0}: ''{1}''
MEASURE_M_CRITERIA_CQL_NO_ELM = Error in {0}: No compiled version of CQL found MEASURE_M_CRITERIA_CQL_NO_ELM = Error in {0}: No compiled version of CQL found
MEASURE_M_CRITERIA_CQL_ELM_NOT_VALID = = Error in {0}: Compiled version of CQL is not valid MEASURE_M_CRITERIA_CQL_ELM_NOT_VALID = = Error in {0}: Compiled version of CQL is not valid
MEASURE_M_CRITERIA_CQL_NOT_FOUND = The function {1} does not exist in the library {0} MEASURE_M_CRITERIA_CQL_NOT_FOUND = The function {1} does not exist in the library {0}
XHTML_URL_EMPTY = URL is empty
XHTML_URL_INVALID_CHARS = URL contains Invalid Characters ({0})

View File

@ -2012,28 +2012,35 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
for (XhtmlNode node : list) { for (XhtmlNode node : list) {
if (node.getNodeType() == NodeType.Element) { if (node.getNodeType() == NodeType.Element) {
if ("a".equals(node.getName())) { if ("a".equals(node.getName())) {
rule(errors, IssueType.INVALID, e.line(), e.col(), path, isValidUrl(node.getAttribute("href")), I18nConstants.XHTML_URL_INVALID, node.getAttribute("href")); String msg = checkValidUrl(node.getAttribute("href"));
rule(errors, IssueType.INVALID, e.line(), e.col(), path, msg == null, I18nConstants.XHTML_URL_INVALID, node.getAttribute("href"), msg);
} else if ("img".equals(node.getName())) { } else if ("img".equals(node.getName())) {
rule(errors, IssueType.INVALID, e.line(), e.col(), path, isValidUrl(node.getAttribute("src")), I18nConstants.XHTML_URL_INVALID, node.getAttribute("src")); String msg = checkValidUrl(node.getAttribute("src"));
rule(errors, IssueType.INVALID, e.line(), e.col(), path, msg == null, I18nConstants.XHTML_URL_INVALID, node.getAttribute("src"), msg);
} }
checkUrls(errors, e, path, node.getChildNodes()); checkUrls(errors, e, path, node.getChildNodes());
} }
} }
} }
private boolean isValidUrl(String value) { private String checkValidUrl(String value) {
if (value == null) { if (value == null) {
return true; return null;
} }
try { if (Utilities.noString(value)) {
return context.formatMessage(I18nConstants.XHTML_URL_EMPTY);
}
Set<Character> invalidChars = new HashSet<>();
for (char ch : value.toCharArray()) { for (char ch : value.toCharArray()) {
if (!(Character.isDigit(ch) || Character.isAlphabetic(ch) || Utilities.existsInList(ch, ';', '?', ':', '@', '&', '=', '+', '$', '.', ',', '/', '%', '-', '_', '~', '#', '[', ']', '!', '\'', '(', ')', '*' ))) { if (!(Character.isDigit(ch) || Character.isAlphabetic(ch) || Utilities.existsInList(ch, ';', '?', ':', '@', '&', '=', '+', '$', '.', ',', '/', '%', '-', '_', '~', '#', '[', ']', '!', '\'', '(', ')', '*' ))) {
return false; invalidChars.add(ch);
} }
} }
return true; if (invalidChars.isEmpty()) {
} catch (Exception e) { return null;
return false; } else {
return context.formatMessage(I18nConstants.XHTML_URL_INVALID_CHARS, invalidChars.toString());
} }
} }

View File

@ -116,7 +116,7 @@ public class MeasureValidator extends BaseValidator {
String name = cqlRef.substring(0, cqlRef.indexOf(".")); String name = cqlRef.substring(0, cqlRef.indexOf("."));
cqlRef = cqlRef.substring(cqlRef.indexOf(".")+1); cqlRef = cqlRef.substring(cqlRef.indexOf(".")+1);
for (Library l : mctxt.libraries()) { for (Library l : mctxt.libraries()) {
if (l.getName().equals(name)) { if (name.equals(l.getName())) {
if (rule(errors, IssueType.INVALID, crit.line(), crit.col(), nsc.getLiteralPath(), lib == null, I18nConstants.MEASURE_M_CRITERIA_CQL_LIB_DUPL)) { if (rule(errors, IssueType.INVALID, crit.line(), crit.col(), nsc.getLiteralPath(), lib == null, I18nConstants.MEASURE_M_CRITERIA_CQL_LIB_DUPL)) {
lib = l; lib = l;
} }