diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/component/message/OpResult.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/component/message/OpResult.java
index cfbff16189f..5ded9f3a763 100644
--- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/component/message/OpResult.java
+++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/component/message/OpResult.java
@@ -24,6 +24,7 @@
import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectFactory;
import com.evolveum.midpoint.xml.ns._public.common.common_3.OperationResultType;
+import org.apache.commons.lang.StringEscapeUtils;
import org.apache.commons.lang.Validate;
import java.io.PrintWriter;
@@ -34,8 +35,6 @@
import java.util.List;
import java.util.Map;
-import javax.xml.bind.JAXBException;
-
/**
* @author lazyman
*/
@@ -104,8 +103,10 @@ public OpResult(OperationResult result) {
OperationResultType resultType = result.createOperationResultType();
ObjectFactory of = new ObjectFactory();
xml = getPrismContext().serializeAtomicValue(of.createOperationResult(resultType), PrismContext.LANG_XML);
- } catch (SchemaException ex) {
- error("Can't create xml: " + ex);
+ } catch (SchemaException|RuntimeException ex) {
+ String m = "Can't create xml: " + ex;
+ error(m);
+ xml = "" + StringEscapeUtils.escapeXml(m) + "";
}
}
diff --git a/infra/prism/src/main/java/com/evolveum/midpoint/prism/parser/DomSerializer.java b/infra/prism/src/main/java/com/evolveum/midpoint/prism/parser/DomSerializer.java
index 2847890e132..09058d9cd3b 100644
--- a/infra/prism/src/main/java/com/evolveum/midpoint/prism/parser/DomSerializer.java
+++ b/infra/prism/src/main/java/com/evolveum/midpoint/prism/parser/DomSerializer.java
@@ -194,7 +194,7 @@ private void serializePrimitiveElementOrAttribute(PrimitiveXNode> xprim, Eleme
String stringValue = xprim.getStringValue();
if (stringValue != null) {
if (asAttribute) {
- parentElement.setAttribute(elementOrAttributeName.getLocalPart(), stringValue);
+ DOMUtil.setAttributeValue(parentElement, elementOrAttributeName.getLocalPart(), stringValue);
} else {
Element element;
try {
@@ -203,7 +203,7 @@ private void serializePrimitiveElementOrAttribute(PrimitiveXNode> xprim, Eleme
throw new DOMException(e.code, e.getMessage() + "; creating element "+elementOrAttributeName+" in element "+DOMUtil.getQName(parentElement));
}
parentElement.appendChild(element);
- element.setTextContent(stringValue);
+ DOMUtil.setElementTextContent(element, stringValue);
}
}
return;
@@ -259,9 +259,9 @@ private void serializePrimitiveElementOrAttribute(PrimitiveXNode> xprim, Eleme
String value = xprim.getGuessedFormattedValue();
if (asAttribute) {
- parentElement.setAttribute(elementOrAttributeName.getLocalPart(), value);
+ DOMUtil.setAttributeValue(parentElement, elementOrAttributeName.getLocalPart(), value);
} else {
- element.setTextContent(value);
+ DOMUtil.setElementTextContent(element, value);
}
}
diff --git a/infra/prism/src/test/java/com/evolveum/midpoint/prism/TestXmlSerialization.java b/infra/prism/src/test/java/com/evolveum/midpoint/prism/TestXmlSerialization.java
new file mode 100644
index 00000000000..84a2f4f4b09
--- /dev/null
+++ b/infra/prism/src/test/java/com/evolveum/midpoint/prism/TestXmlSerialization.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) 2010-2014 Evolveum
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.evolveum.midpoint.prism;
+
+import com.evolveum.midpoint.prism.PrismContext;
+import com.evolveum.midpoint.prism.PrismInternalTestUtil;
+import com.evolveum.midpoint.prism.crypto.Protector;
+import com.evolveum.midpoint.prism.crypto.TestProtector;
+import com.evolveum.midpoint.prism.parser.util.XNodeProcessorUtil;
+import com.evolveum.midpoint.prism.util.PrismTestUtil;
+import com.evolveum.midpoint.prism.xnode.MapXNode;
+import com.evolveum.midpoint.prism.xnode.PrimitiveXNode;
+import com.evolveum.midpoint.util.PrettyPrinter;
+import com.evolveum.midpoint.util.exception.SchemaException;
+import com.evolveum.prism.xml.ns._public.types_3.ProtectedStringType;
+import org.apache.xml.security.encryption.XMLCipher;
+import org.testng.annotations.BeforeSuite;
+import org.testng.annotations.Test;
+import org.xml.sax.SAXException;
+
+import javax.xml.namespace.QName;
+import java.io.IOException;
+
+import static com.evolveum.midpoint.prism.PrismInternalTestUtil.DEFAULT_NAMESPACE_PREFIX;
+import static com.evolveum.midpoint.prism.PrismInternalTestUtil.displayTestTitle;
+import static org.testng.Assert.assertTrue;
+import static org.testng.AssertJUnit.assertEquals;
+
+/**
+ * @author mederly
+ *
+ */
+public class TestXmlSerialization {
+
+ @BeforeSuite
+ public void setupDebug() throws SchemaException, SAXException, IOException {
+ PrettyPrinter.setDefaultNamespacePrefix(DEFAULT_NAMESPACE_PREFIX);
+ PrismTestUtil.resetPrismContext(new PrismInternalTestUtil());
+ }
+
+ @Test
+ public void testHandlingInvalidChars() throws Exception {
+ final String TEST_NAME = "testHandlingInvalidChars";
+ displayTestTitle(TEST_NAME);
+
+ // GIVEN
+
+ PrismContext prismContext = PrismTestUtil.getPrismContext();
+
+ // WHEN
+
+ PrimitiveXNode valOkNode = new PrimitiveXNode<>("abcdef");
+ PrimitiveXNode valWrongNode = new PrimitiveXNode<>("abc\1def");
+
+ // THEN
+
+ String ok = prismContext.getParserDom().serializeToString(valOkNode, new QName("ok"));
+ System.out.println("correct value serialized to: " + ok);
+ assertEquals("Wrong serialization", "abcdef", ok.trim()); // todo make this less brittle with regards to serialization style
+
+ try {
+ String wrong = prismContext.getParserDom().serializeToString(valWrongNode, new QName("wrong"));
+ System.out.println("wrong value serialized to: " + wrong);
+ assert false : "Wrong value serialization had to fail but it didn't!";
+ } catch (RuntimeException e) {
+ System.out.println("wrong value was not serialized (as expected): " + e);
+ assertTrue(e.getMessage().contains("Invalid character"), "Didn't get expected error message");
+ }
+ }
+}
diff --git a/infra/prism/testng.xml b/infra/prism/testng.xml
index eb6814ab207..b03ac0698ad 100644
--- a/infra/prism/testng.xml
+++ b/infra/prism/testng.xml
@@ -33,6 +33,7 @@
+
diff --git a/infra/util/src/main/java/com/evolveum/midpoint/util/DOMUtil.java b/infra/util/src/main/java/com/evolveum/midpoint/util/DOMUtil.java
index c8d2491c171..503339318d5 100644
--- a/infra/util/src/main/java/com/evolveum/midpoint/util/DOMUtil.java
+++ b/infra/util/src/main/java/com/evolveum/midpoint/util/DOMUtil.java
@@ -25,7 +25,6 @@
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Collection;
-import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
@@ -33,7 +32,6 @@
import java.util.Map.Entry;
import java.util.Random;
-import javax.xml.XMLConstants;
import javax.xml.namespace.QName;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
@@ -46,6 +44,7 @@
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
+import com.sun.org.apache.xml.internal.utils.XMLChar;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.Validate;
@@ -529,6 +528,7 @@ private static void setQNameAttribute(Element element, Attr attr, QName attribut
attrValue = valuePrefix + ":" + attributeValue.getLocalPart();
}
NamedNodeMap attributes = element.getAttributes();
+ checkValidXmlChars(attrValue);
attr.setValue(attrValue);
attributes.setNamedItem(attr);
}
@@ -543,7 +543,7 @@ public static void setQNameValue(Element element, QName elementValue) {
} else {
stringValue = valuePrefix + ":" + elementValue.getLocalPart();
}
- element.setTextContent(stringValue);
+ setElementTextContent(element, stringValue);
}
public static String lookupOrCreateNamespaceDeclaration(Element element, String namespaceUri,
@@ -659,6 +659,7 @@ public static void setNamespaceDeclaration(Element element, String prefix, Strin
attr = doc
.createAttributeNS(W3C_XML_SCHEMA_XMLNS_URI, W3C_XML_SCHEMA_XMLNS_PREFIX + ":" + prefix);
}
+ checkValidXmlChars(namespaceUri);
attr.setValue(namespaceUri);
attributes.setNamedItem(attr);
}
@@ -1218,4 +1219,46 @@ public static boolean isEmpty(Attr attr) {
return StringUtils.isEmpty(attr.getValue());
}
+ public static void setAttributeValue(Element element, String name, String value) {
+ checkValidXmlChars(value);
+ element.setAttribute(name, value);
+ }
+
+ public static void setElementTextContent(Element element, String value) {
+ checkValidXmlChars(value);
+ element.setTextContent(value);
+ }
+
+ public static void checkValidXmlChars(String stringValue) {
+ if (stringValue == null) {
+ return;
+ }
+ for (int i = 0; i < stringValue.length(); i++) {
+ if (!XMLChar.isValid(stringValue.charAt(i))) {
+ throw new IllegalStateException("Invalid character with regards to XML (code " + ((int) stringValue.charAt(i)) + ") in '" + makeSafelyPrintable(stringValue, 200) + "'");
+ }
+ }
+ }
+
+ // todo move to some Util class
+ private static String makeSafelyPrintable(String text, int maxSize) {
+ StringBuilder sb = new StringBuilder();
+ for (int i = 0; i < text.length(); i++) {
+ char c = text.charAt(i);
+ if (!XMLChar.isValid(c)) {
+ sb.append('.');
+ } else if (Character.isWhitespace(c)) {
+ sb.append(' ');
+ } else {
+ sb.append(c);
+ }
+ if (i == maxSize) {
+ sb.append("...");
+ break;
+ }
+ }
+ return sb.toString();
+ }
+
+
}