Commits

Oswaldo Hernandez committed e195b5c

Do not resolve external references to unknown xml entities when loading a workflow definition.

Comments (0)

Files changed (2)

src/java/com/opensymphony/workflow/loader/SecureDTDEntityResolver.java

+/*
+ * Copyright (c) 2002-2003 by OpenSymphony
+ * All rights reserved.
+ */
+package com.opensymphony.workflow.loader;
+
+import org.xml.sax.EntityResolver;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import java.net.MalformedURLException;
+import java.net.URL;
+
+
+/**
+ * Resolves external references to the os-workflow DTD to a well known document in the local file system, and disallows
+ * any other external entity references.
+ *
+ * @author Oswaldo Hernandez (ohernandez@atlassian.com)
+ */
+public class SecureDTDEntityResolver implements EntityResolver {
+    //~ Methods ////////////////////////////////////////////////////////////////
+
+    public InputSource resolveEntity(String publicId, String systemId) throws SAXException, IOException {
+        if (systemId == null) {
+            return failure();
+        }
+
+        try {
+            final URL url = new URL(systemId);
+            String file = url.getFile();
+
+            if ((file != null) && (file.indexOf('/') > -1)) {
+                file = file.substring(file.lastIndexOf('/') + 1);
+            }
+
+            if (isOpenSymphonyUrl(url) && isDtdReference(systemId)) {
+                return localDtd(file);
+            }
+        }
+        //modified by mbussetti - 15 nov 2004
+        //if the systemId isn't an URL, it is searched in the usual classpath
+         catch (MalformedURLException e) {
+            return localDtd(systemId);
+        }
+
+        return failure();
+    }
+
+    private boolean isDtdReference(final String systemId) {
+        return systemId.endsWith(".dtd");
+    }
+
+    private boolean isOpenSymphonyUrl(final URL url) {
+        return "www.opensymphony.com".equals(url.getHost());
+    }
+
+    /**
+     * <p>Disallow unknown entities by returning an empty InputSource.<p/>
+     *
+     * <p>Consequently, when parsing malicious input, the empty InputSource returned by the custom resolver causes
+     * a java.net. MalformedURLException to be thrown.</p>
+     *
+     * @return An empty InputSource.
+     */
+    private InputSource failure() {
+        return new InputSource();
+    }
+
+    /**
+     * Attempts to load the DTD from the classpath, and fail if this is not possible.
+     *
+     * @param fileName The name of the file containing the dtd to retrieve.
+     * @return An input source for a dtd retrieved from the class path, or an empty input source if it can not be found.
+     */
+    private InputSource localDtd(final String fileName) {
+        InputStream is = getClass().getResourceAsStream("/META-INF/" + fileName);
+
+        if (is == null) {
+            is = getClass().getResourceAsStream('/' + fileName);
+        }
+
+        if (is != null) {
+            return new InputSource(is);
+        }
+
+        return failure();
+    }
+}

src/java/com/opensymphony/workflow/loader/WorkflowLoader.java

 import org.w3c.dom.Document;
 import org.w3c.dom.Element;
 
-import org.xml.sax.*;
+import org.xml.sax.ErrorHandler;
+import org.xml.sax.SAXException;
+import org.xml.sax.SAXParseException;
 
-import java.io.*;
+import java.io.IOException;
+import java.io.InputStream;
 
 import java.net.URL;
 
 import java.util.ArrayList;
 import java.util.List;
 
-import javax.xml.parsers.*;
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
 
 
 /**
 
         try {
             db = dbf.newDocumentBuilder();
-            db.setEntityResolver(new DTDEntityResolver());
+            db.setEntityResolver(new SecureDTDEntityResolver());
         } catch (ParserConfigurationException e) {
             throw new SAXException("Error creating document builder", e);
         }
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.