Skip to content

Commit

Permalink
Preventing serialization of incorrect XML characters (MID-1944)
Browse files Browse the repository at this point in the history
  • Loading branch information
mederly committed Jul 17, 2014
1 parent 17ecc6a commit 8752c72
Show file tree
Hide file tree
Showing 5 changed files with 140 additions and 11 deletions.
Expand Up @@ -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;
Expand All @@ -34,8 +35,6 @@
import java.util.List;
import java.util.Map;

import javax.xml.bind.JAXBException;

/**
* @author lazyman
*/
Expand Down Expand Up @@ -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 = "<?xml version='1.0'?><message>" + StringEscapeUtils.escapeXml(m) + "</message>";
}
}

Expand Down
Expand Up @@ -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 {
Expand All @@ -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;
Expand Down Expand Up @@ -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);
}
}

Expand Down
@@ -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<String> valOkNode = new PrimitiveXNode<>("abcdef");
PrimitiveXNode<String> 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", "<ok>abcdef</ok>", 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");
}
}
}
1 change: 1 addition & 0 deletions infra/prism/testng.xml
Expand Up @@ -33,6 +33,7 @@
<class name="com.evolveum.midpoint.prism.TestCompareXml"/>
<class name="com.evolveum.midpoint.prism.TestCompareJson"/>
<class name="com.evolveum.midpoint.prism.TestCompareYaml"/>
<class name="com.evolveum.midpoint.prism.TestXmlSerialization"/>
<class name="com.evolveum.midpoint.prism.TestPrismObjectConstruction"/>
<class name="com.evolveum.midpoint.prism.TestPrismSchemaConstruction"/>
<class name="com.evolveum.midpoint.prism.TestExtraSchema"/>
Expand Down
49 changes: 46 additions & 3 deletions infra/util/src/main/java/com/evolveum/midpoint/util/DOMUtil.java
Expand Up @@ -25,15 +25,13 @@
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;
import java.util.Map;
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;
Expand All @@ -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;
Expand Down Expand Up @@ -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);
}
Expand All @@ -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,
Expand Down Expand Up @@ -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);
}
Expand Down Expand Up @@ -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();
}


}

0 comments on commit 8752c72

Please sign in to comment.