]> source.dussan.org Git - xmlgraphics-fop.git/commitdiff
SAX filter providing a MD5 checksum for ensuring XML input integrity.
authorJoerg Pietschmann <pietsch@apache.org>
Fri, 19 Sep 2003 16:50:56 +0000 (16:50 +0000)
committerJoerg Pietschmann <pietsch@apache.org>
Fri, 19 Sep 2003 16:50:56 +0000 (16:50 +0000)
git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@196925 13f79535-47bb-0310-9956-ffa450edef68

test/java/org/apache/fop/DigestFilterTestCase.java [new file with mode: 0644]
test/java/org/apache/fop/util/DigestFilter.java [new file with mode: 0644]

diff --git a/test/java/org/apache/fop/DigestFilterTestCase.java b/test/java/org/apache/fop/DigestFilterTestCase.java
new file mode 100644 (file)
index 0000000..a976b09
--- /dev/null
@@ -0,0 +1,187 @@
+/*
+ * $Id$
+ * ============================================================================
+ *                    The Apache Software License, Version 1.1
+ * ============================================================================
+ * 
+ * Copyright (C) 1999-2003 The Apache Software Foundation. All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, with or without modifica-
+ * tion, are permitted provided that the following conditions are met:
+ * 
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 
+ * 3. The end-user documentation included with the redistribution, if any, must
+ *    include the following acknowledgment: "This product includes software
+ *    developed by the Apache Software Foundation (http://www.apache.org/)."
+ *    Alternately, this acknowledgment may appear in the software itself, if
+ *    and wherever such third-party acknowledgments normally appear.
+ * 
+ * 4. The names "FOP" and "Apache Software Foundation" must not be used to
+ *    endorse or promote products derived from this software without prior
+ *    written permission. For written permission, please contact
+ *    apache@apache.org.
+ * 
+ * 5. Products derived from this software may not be called "Apache", nor may
+ *    "Apache" appear in their name, without prior written permission of the
+ *    Apache Software Foundation.
+ * 
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * APACHE SOFTWARE FOUNDATION OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLU-
+ * DING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ============================================================================
+ * 
+ * This software consists of voluntary contributions made by many individuals
+ * on behalf of the Apache Software Foundation and was originally created by
+ * James Tauber <jtauber@jtauber.com>. For more information on the Apache
+ * Software Foundation, please see <http://www.apache.org/>.
+ */
+
+package org.apache.fop;
+
+import java.io.IOException;
+import java.io.StringReader;
+import java.security.NoSuchAlgorithmException;
+
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.parsers.SAXParserFactory;
+
+import junit.framework.TestCase;
+
+import org.apache.fop.util.DigestFilter;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+import org.xml.sax.XMLReader;
+
+/**
+ * Test case for digesting SAX filter.
+ *
+ */
+public class DigestFilterTestCase extends TestCase {
+
+    private SAXParserFactory parserFactory;
+
+    /* (non-Javadoc)
+     * @see junit.framework.TestCase#setUp()
+     */
+    protected void setUp() {
+        parserFactory = SAXParserFactory.newInstance();
+        parserFactory.setNamespaceAware(true);
+    }
+
+    private boolean compareDigest(byte a[], byte b[]) {
+        if (a.length != b.length) {
+            return false;
+        }
+        for (int i = 0; i < a.length; i++) {
+            if (a[i] != b[i]) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    private String digestToString(byte digest[]) {
+        StringBuffer buffer = new StringBuffer(2 * digest.length);
+        for (int i = 0; i < digest.length; i++) {
+            int val = digest[i];
+            int hi = (val >> 4) & 0xF;
+            int lo = val & 0xF;
+            if (hi < 10) {
+                buffer.append((char) (hi + 0x30));
+            } else {
+                buffer.append((char) (hi + 0x61 - 10));
+            }
+            if (lo < 10) {
+                buffer.append((char) (lo + 0x30));
+            } else {
+                buffer.append((char) (lo + 0x61 - 10));
+            }
+        }
+        return buffer.toString();
+    }
+
+    private byte[] runTest(String input)
+        throws
+            NoSuchAlgorithmException,
+            ParserConfigurationException,
+            SAXException,
+            IOException {
+        XMLReader parser = parserFactory.newSAXParser().getXMLReader();
+        DigestFilter digestFilter = new DigestFilter("MD5");
+        digestFilter.setParent(parser);
+        digestFilter.setFeature("http://xml.org/sax/features/namespaces",true);
+        parser.setContentHandler(digestFilter);
+        InputSource inputSource = new InputSource(new StringReader(input));
+        parser.parse(inputSource);
+        return digestFilter.getDigestValue();
+    }
+
+    public final void testLineFeed()
+        throws
+            NoSuchAlgorithmException,
+            ParserConfigurationException,
+            SAXException,
+            IOException {
+        byte lfDigest[] = runTest("<a>\n</a>");
+        byte crlfDigest[] = runTest("<a>\r\n</a>");
+        assertTrue(
+            "LF: "
+                + digestToString(lfDigest)
+                + " CRLF: "
+                + digestToString(crlfDigest),
+            compareDigest(lfDigest, crlfDigest));
+    }
+
+    public final void testAttributeOrder()
+        throws
+            NoSuchAlgorithmException,
+            ParserConfigurationException,
+            SAXException,
+            IOException {
+        byte sortDigest[] = runTest("<a a1='1' a2='2' a3='3'/>");
+        byte permutationDigest[] = runTest("<a a2='2' a3='3' a1='1'/>");
+        assertTrue(
+            "Sort: "
+                + digestToString(sortDigest)
+                + " permuted: "
+                + digestToString(permutationDigest),
+            compareDigest(sortDigest, permutationDigest));
+        byte reverseDigest[] = runTest("<a a3='3' a2='2' a1='1'/>");
+        assertTrue(
+            "Sort: "
+                + digestToString(sortDigest)
+                + " permuted: "
+                + digestToString(reverseDigest),
+            compareDigest(sortDigest, reverseDigest));
+    }
+
+    public final void testNamespacePrefix()
+        throws
+            NoSuchAlgorithmException,
+            ParserConfigurationException,
+            SAXException,
+            IOException {
+        byte prefix1Digest[] = runTest("<a:a xmlns:a='foo'/>");
+        byte prefix2Digest[] = runTest("<b:a xmlns:b='foo'/>");
+        assertTrue(
+            "prefix1: "
+                + digestToString(prefix1Digest)
+                + " prefix2: "
+                + digestToString(prefix2Digest),
+            compareDigest(prefix1Digest, prefix2Digest));
+    }
+
+}
diff --git a/test/java/org/apache/fop/util/DigestFilter.java b/test/java/org/apache/fop/util/DigestFilter.java
new file mode 100644 (file)
index 0000000..d6ac21d
--- /dev/null
@@ -0,0 +1,173 @@
+/*
+ * $Id$
+ * ============================================================================
+ *                    The Apache Software License, Version 1.1
+ * ============================================================================
+ * 
+ * Copyright (C) 1999-2003 The Apache Software Foundation. All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, with or without modifica-
+ * tion, are permitted provided that the following conditions are met:
+ * 
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 
+ * 3. The end-user documentation included with the redistribution, if any, must
+ *    include the following acknowledgment: "This product includes software
+ *    developed by the Apache Software Foundation (http://www.apache.org/)."
+ *    Alternately, this acknowledgment may appear in the software itself, if
+ *    and wherever such third-party acknowledgments normally appear.
+ * 
+ * 4. The names "FOP" and "Apache Software Foundation" must not be used to
+ *    endorse or promote products derived from this software without prior
+ *    written permission. For written permission, please contact
+ *    apache@apache.org.
+ * 
+ * 5. Products derived from this software may not be called "Apache", nor may
+ *    "Apache" appear in their name, without prior written permission of the
+ *    Apache Software Foundation.
+ * 
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * APACHE SOFTWARE FOUNDATION OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLU-
+ * DING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ============================================================================
+ * 
+ * This software consists of voluntary contributions made by many individuals
+ * on behalf of the Apache Software Foundation and was originally created by
+ * James Tauber <jtauber@jtauber.com>. For more information on the Apache
+ * Software Foundation, please see <http://www.apache.org/>.
+ */
+package org.apache.fop.util;
+
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.TreeMap;
+
+import org.xml.sax.Attributes;
+import org.xml.sax.SAXException;
+import org.xml.sax.SAXNotRecognizedException;
+import org.xml.sax.SAXNotSupportedException;
+import org.xml.sax.helpers.XMLFilterImpl;
+
+/**
+ * SAX filter which produces a digest over the XML elements.
+ * Insignificant whitespace as determined by the, comments and
+ * processing instructions are not part of the digest.
+ * If the filter is namespace aware, the URI and local name for
+ * each element is digested, otherwise the QName. Attributes are
+ * sorted before the name-value pairs are digested.
+ * 
+ */
+public class DigestFilter extends XMLFilterImpl {
+
+    private MessageDigest digest;
+    private byte value[];
+    private boolean isNamespaceAware;
+
+    public DigestFilter(String algorithm) throws NoSuchAlgorithmException {
+        digest = MessageDigest.getInstance(algorithm);
+    }
+
+    public byte[] getDigestValue() {
+        return value;
+    }
+
+    public String getDigestString() {
+        if (value != null) {
+            StringBuffer buffer = new StringBuffer(2 * value.length);
+            for (int i = 0; i < value.length; i++) {
+                int val = value[i];
+                int hi = (val >> 4) & 0xF;
+                int lo = val & 0xF;
+                if (hi < 10) {
+                    buffer.append((char) (hi + 0x30));
+                } else {
+                    buffer.append((char) (hi + 0x61 - 10));
+                }
+                if (lo < 10) {
+                    buffer.append((char) (lo + 0x30));
+                } else {
+                    buffer.append((char) (lo + 0x61 - 10));
+                }
+            }
+            return buffer.toString();
+        } else {
+            return null;
+        }
+    }
+
+    /* (non-Javadoc)
+     * @see org.xml.sax.ContentHandler#characters(char[], int, int)
+     */
+    public void characters(char[] chars, int start, int length)
+        throws SAXException {
+        digest.update(new String(chars, start, length).getBytes());
+        super.characters(chars, start, length);
+    }
+
+    /* (non-Javadoc)
+     * @see org.xml.sax.ContentHandler#endDocument()
+     */
+    public void endDocument() throws SAXException {
+        value = digest.digest();
+        super.endDocument();
+    }
+
+    /* (non-Javadoc)
+     * @see org.xml.sax.ContentHandler#startElement(java.lang.String, java.lang.String, java.lang.String, org.xml.sax.Attributes)
+     */
+    public void startElement(
+        String url,
+        String localName,
+        String qName,
+        Attributes attr)
+        throws SAXException {
+        Map map = new TreeMap();
+        if (isNamespaceAware) {
+            digest.update(url.getBytes());
+            digest.update(localName.getBytes());
+            for (int i = 0; i < attr.getLength(); i++) {
+                map.put(
+                    attr.getLocalName(i) + attr.getURI(i),
+                    attr.getValue(i));
+            }
+        } else {
+            digest.update(qName.getBytes());
+            for (int i = 0; i < attr.getLength(); i++) {
+                map.put(attr.getQName(i), attr.getValue(i));
+            }
+        }
+        for (Iterator i = map.entrySet().iterator(); i.hasNext();) {
+            Map.Entry entry = (Map.Entry)i.next();
+            digest.update(((String)entry.getKey()).getBytes());
+            digest.update(((String)entry.getValue()).getBytes());
+        }
+        super.startElement(url, localName, qName, attr);
+    }
+
+
+    /* (non-Javadoc)
+     * @see org.xml.sax.XMLReader#setFeature(java.lang.String, boolean)
+     */
+    public void setFeature(String feature, boolean value)
+        throws SAXNotRecognizedException, SAXNotSupportedException {
+        if(feature.equals("http://xml.org/sax/features/namespaces")) {
+            isNamespaceAware = value;
+        }
+        super.setFeature(feature, value);
+    }
+
+}