diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/LenientErrorHandler.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/LenientErrorHandler.java index fc60dc8410f..d159f1e999b 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/LenientErrorHandler.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/LenientErrorHandler.java @@ -26,7 +26,7 @@ import ca.uhn.fhir.parser.json.BaseJsonLikeValue.ValueType; import static org.apache.commons.lang3.StringUtils.isBlank; /** - * The default error handler, which logs issues but does not abort parsing, with only one exception: + * The default error handler, which logs issues but does not abort parsing, with only two exceptions: *
* The {@link #invalidValue(ca.uhn.fhir.parser.IParserErrorHandler.IParseLocation, String, String)} * method will throw a {@link DataFormatException} by default since ignoring this type of error @@ -36,12 +36,20 @@ import static org.apache.commons.lang3.StringUtils.isBlank; * * @see IParser#setParserErrorHandler(IParserErrorHandler) * @see FhirContext#setParserErrorHandler(IParserErrorHandler) + * + *
+ * The {@link #extensionContainsValueAndNestedExtensions(ca.uhn.fhir.parser.IParserErrorHandler.IParseLocation)} + * method will throw a {@link DataFormatException} by default since ignoring this type of error will allow malformed + * resouces to be created and result in errors when attempts to read, update or delete the resource in the future. + * See {@link #setErrorOnInvalidExtension(boolean)} for information on this. + *
*/ public class LenientErrorHandler extends ParseErrorHandler implements IParserErrorHandler { private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(LenientErrorHandler.class); private static final StrictErrorHandler STRICT_ERROR_HANDLER = new StrictErrorHandler(); private boolean myErrorOnInvalidValue = true; + private boolean myErrorOnInvalidExtension = true; private boolean myLogErrors; /** @@ -92,7 +100,7 @@ public class LenientErrorHandler extends ParseErrorHandler implements IParserErr /** * If set tofalse
(default is true
) invalid values will be logged. By
- * default invalid attribute values cause this error handler to throw a {@link DataFormatException} (unlike
+ * default, invalid attribute values cause this error handler to throw a {@link DataFormatException} (unlike
* other methods in this class which default to simply logging errors).
*
* Note that empty values (e.g. ""
) will not lead to an error when this is set to
@@ -105,6 +113,17 @@ public class LenientErrorHandler extends ParseErrorHandler implements IParserErr
return myErrorOnInvalidValue;
}
+ /**
+ * If set to false
(default is true
) invalid extensions will be logged. By
+ * default, invalid resource extensions cause this error handler to throw a {@link DataFormatException} (unlike
+ * other methods in this class which default to simply logging errors).
+ *
+ * @see #setErrorOnInvalidExtension(boolean)
+ */
+ public boolean isErrorOnInvalidExtension() {
+ return myErrorOnInvalidExtension;
+ }
+
@Override
public void missingRequiredElement(IParseLocation theLocation, String theElementName) {
if (myLogErrors) {
@@ -114,7 +133,7 @@ public class LenientErrorHandler extends ParseErrorHandler implements IParserErr
/**
* If set to false
(default is true
) invalid values will be logged. By
- * default invalid attribute values cause this error handler to throw a {@link DataFormatException} (unlike
+ * default, invalid attribute values cause this error handler to throw a {@link DataFormatException} (unlike
* other methods in this class which default to simply logging errors).
*
* Note that empty values (e.g. ""
) will not lead to an error when this is set to
@@ -129,6 +148,28 @@ public class LenientErrorHandler extends ParseErrorHandler implements IParserErr
return this;
}
+ /**
+ * If set to false
(default is true
) invalid extensions will be logged. By
+ * default, invalid resource extensions cause this error handler to throw a {@link DataFormatException} (unlike
+ * other methods in this class which default to simply logging errors).
+ *
+ * @return Returns a reference to this
for easy method chaining
+ * @see #isErrorOnInvalidExtension()
+ */
+ public LenientErrorHandler setErrorOnInvalidExtension(boolean theErrorOnInvalidExtension) {
+ myErrorOnInvalidExtension = theErrorOnInvalidExtension;
+ return this;
+ }
+
+ /**
+ * If this method is called, both invalid resource extensions and invalid attribute values will set to simply logging errors.
+ */
+ public LenientErrorHandler disableAllErrors() {
+ myErrorOnInvalidValue = false;
+ myErrorOnInvalidExtension = false;
+ return this;
+ }
+
@Override
public void unexpectedRepeatingElement(IParseLocation theLocation, String theElementName) {
if (myLogErrors) {
@@ -158,8 +199,10 @@ public class LenientErrorHandler extends ParseErrorHandler implements IParserErr
}
@Override
- public void extensionContainsValueAndNestedExtensions(IParseLocation theLocation) {
- if (myLogErrors) {
+ public void extensionContainsValueAndNestedExtensions(IParseLocation theLocation){
+ if (myErrorOnInvalidExtension) {
+ STRICT_ERROR_HANDLER.extensionContainsValueAndNestedExtensions(theLocation);
+ } else if (myLogErrors) {
ourLog.warn("{}Extension contains both a value and nested extensions", describeLocation(theLocation));
}
}
diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_6_0/4746-hl7v2-inbound-endpoint-should-reject-malformed-resources.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_6_0/4746-hl7v2-inbound-endpoint-should-reject-malformed-resources.yaml
new file mode 100644
index 00000000000..328ccd606b3
--- /dev/null
+++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_6_0/4746-hl7v2-inbound-endpoint-should-reject-malformed-resources.yaml
@@ -0,0 +1,7 @@
+---
+type: fix
+issue: 4746
+jira: SMILE-4710
+title: "Previously, the Lenient error handler will accept invalid extension containing value and nested extensions by default.
+This will lead to errors when client further attempt to read, update or delete the resources. Now, this has been fixed and
+a DataFormatException will be thrown instead."
diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/JpaStorageResourceParser.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/JpaStorageResourceParser.java
index ca51ff69f8b..3d3165f736a 100644
--- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/JpaStorageResourceParser.java
+++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/JpaStorageResourceParser.java
@@ -76,7 +76,7 @@ import static org.apache.commons.lang3.StringUtils.defaultString;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
public class JpaStorageResourceParser implements IJpaStorageResourceParser {
- public static final LenientErrorHandler LENIENT_ERROR_HANDLER = new LenientErrorHandler(false).setErrorOnInvalidValue(false);
+ public static final LenientErrorHandler LENIENT_ERROR_HANDLER = new LenientErrorHandler(false).disableAllErrors();
private static final Logger ourLog = LoggerFactory.getLogger(JpaStorageResourceParser.class);
@Autowired
private FhirContext myFhirContext;
diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/TolerantJsonParser.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/TolerantJsonParser.java
index 09c475b4fe5..ec95f9d9861 100644
--- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/TolerantJsonParser.java
+++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/TolerantJsonParser.java
@@ -110,7 +110,7 @@ public class TolerantJsonParser extends JsonParser {
}
public static TolerantJsonParser createWithLenientErrorHandling(FhirContext theContext, @Nullable Long theResourcePid) {
- LenientErrorHandler errorHandler = new LenientErrorHandler(false).setErrorOnInvalidValue(false);
+ LenientErrorHandler errorHandler = new LenientErrorHandler(false).disableAllErrors();
return new TolerantJsonParser(theContext, errorHandler, theResourcePid);
}
}
diff --git a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/parser/ErrorHandlerTest.java b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/parser/ErrorHandlerTest.java
index 5980237ec95..54c94f7add0 100644
--- a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/parser/ErrorHandlerTest.java
+++ b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/parser/ErrorHandlerTest.java
@@ -50,6 +50,7 @@ public class ErrorHandlerTest {
new LenientErrorHandler().unknownReference(null, null);
new LenientErrorHandler().incorrectJsonType(null, null, ValueType.ARRAY, null, ValueType.SCALAR, null);
new LenientErrorHandler().setErrorOnInvalidValue(false).invalidValue(null, "FOO", "");
+ new LenientErrorHandler().setErrorOnInvalidExtension(false).extensionContainsValueAndNestedExtensions(null);
new LenientErrorHandler().invalidValue(null, null, "");
try {
new LenientErrorHandler().invalidValue(null, "FOO", "");
@@ -57,6 +58,13 @@ public class ErrorHandlerTest {
} catch (DataFormatException e) {
// good, this one method defaults to causing an error
}
+
+ try {
+ new LenientErrorHandler().extensionContainsValueAndNestedExtensions(null);
+ fail();
+ } catch (DataFormatException e) {
+ // good, this one method defaults to causing an error
+ }
}
@Test
diff --git a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/parser/XmlParserDstu3Test.java b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/parser/XmlParserDstu3Test.java
index b35ffafdb11..f05232ba97a 100644
--- a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/parser/XmlParserDstu3Test.java
+++ b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/parser/XmlParserDstu3Test.java
@@ -1619,7 +1619,7 @@ public class XmlParserDstu3Test {
child.setValue(new StringType("CHILD_VALUE"));
// Lenient error handler
- IParser parser = ourCtx.newXmlParser();
+ IParser parser = ourCtx.newXmlParser().setParserErrorHandler(new LenientErrorHandler(false).disableAllErrors());
String output = parser.encodeResourceToString(p);
ourLog.info("Output: {}", output);
assertThat(output, containsString("http://root"));
diff --git a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/parser/JsonParserR4Test.java b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/parser/JsonParserR4Test.java
index 26ad94eb604..9b716d0b7b7 100644
--- a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/parser/JsonParserR4Test.java
+++ b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/parser/JsonParserR4Test.java
@@ -473,7 +473,6 @@ public class JsonParserR4Test extends BaseTest {
}
-
@Test
public void testEncodeWithInvalidExtensionContainingValueAndNestedExtensions() {
@@ -485,14 +484,15 @@ public class JsonParserR4Test extends BaseTest {
child.setUrl("http://child");
child.setValue(new StringType("CHILD_VALUE"));
+ // According to issue4129, all error handlers should reject malformed resources
// Lenient error handler
IParser parser = ourCtx.newJsonParser();
- String output = parser.encodeResourceToString(p);
- ourLog.info("Output: {}", output);
- assertThat(output, containsString("http://root"));
- assertThat(output, containsString("ROOT_VALUE"));
- assertThat(output, containsString("http://child"));
- assertThat(output, containsString("CHILD_VALUE"));
+ try {
+ parser.encodeResourceToString(p);
+ fail();
+ } catch (DataFormatException e) {
+ assertEquals(Msg.code(1827) + "[element=\"Patient(res).extension\"] Extension contains both a value and nested extensions", e.getMessage());
+ }
// Strict error handler
try {
@@ -505,6 +505,30 @@ public class JsonParserR4Test extends BaseTest {
}
+ @Test
+ public void testEncodeWithInvalidExtensionContainingValueAndNestedExtensions_withDisableAllErrorsShouldSucceed() {
+
+ Patient p = new Patient();
+ Extension root = p.addExtension();
+ root.setUrl("http://root");
+ root.setValue(new StringType("ROOT_VALUE"));
+ Extension child = root.addExtension();
+ child.setUrl("http://child");
+ child.setValue(new StringType("CHILD_VALUE"));
+
+ // Lenient error handler - should parse successfully with no error
+ LenientErrorHandler errorHandler = new LenientErrorHandler(true).disableAllErrors();
+ IParser parser = ourCtx.newJsonParser().setParserErrorHandler(errorHandler);
+ String output = parser.encodeResourceToString(p);
+ ourLog.info("Output: {}", output);
+ assertThat(output, containsString("http://root"));
+ assertThat(output, containsString("ROOT_VALUE"));
+ assertThat(output, containsString("http://child"));
+ assertThat(output, containsString("CHILD_VALUE"));
+ assertEquals(false, errorHandler.isErrorOnInvalidExtension());
+ assertEquals(false, errorHandler.isErrorOnInvalidValue());
+ }
+
@Test
public void testEncodeResourceWithMixedManualAndAutomaticContainedResourcesLocalFirst() {