Added DOMNodeSupport and JythonSupport boolean properties to DefaultObjectWrapper. This allows disabling the special wrapping of DOM nodes and Jython classes. This might be desirable for security reasons.
diff --git a/src/main/java/freemarker/template/DefaultObjectWrapper.java b/src/main/java/freemarker/template/DefaultObjectWrapper.java
index c0337db..eb44ce6 100644
--- a/src/main/java/freemarker/template/DefaultObjectWrapper.java
+++ b/src/main/java/freemarker/template/DefaultObjectWrapper.java
@@ -73,8 +73,10 @@
     private boolean useAdaptersForContainers;
     private boolean forceLegacyNonListCollections;
     private boolean iterableSupport;
+    private boolean domNodeSupport;
+    private boolean jythonSupport;
     private final boolean useAdapterForEnumerations;
-    
+
     /**
      * Creates a new instance with the incompatible-improvements-version specified in
      * {@link Configuration#DEFAULT_INCOMPATIBLE_IMPROVEMENTS}.
@@ -134,6 +136,8 @@
                 && getIncompatibleImprovements().intValue() >= _TemplateAPI.VERSION_INT_2_3_26;
         forceLegacyNonListCollections = dowDowCfg.getForceLegacyNonListCollections();
         iterableSupport = dowDowCfg.getIterableSupport();
+        domNodeSupport = dowDowCfg.getDOMNodeSupport();
+        jythonSupport = dowDowCfg.getJythonSupport();
         finalizeConstruction(writeProtected);
     }
 
@@ -255,9 +259,10 @@
      * Called for an object that isn't considered to be of a "basic" Java type, like for an application specific type,
      * or for a W3C DOM node. In its default implementation, W3C {@link Node}-s will be wrapped as {@link NodeModel}-s
      * (allows DOM tree traversal), Jython objects will be delegated to the {@code JythonWrapper}, others will be
-     * wrapped using {@link BeansWrapper#wrap(Object)}. Note that if {@link #getMemberAccessPolicy()} doesn't return
-     * a {@link DefaultMemberAccessPolicy} or {@link LegacyDefaultMemberAccessPolicy}, then Jython wrapper will be
-     * skipped for security reasons.
+     * wrapped using {@link BeansWrapper#wrap(Object)}. However, these can be turned off with the
+     * {@link #setDOMNodeSupport(boolean)} and {@link #setJythonSupport(boolean)}. Note that if
+     * {@link #getMemberAccessPolicy()} doesn't return a {@link DefaultMemberAccessPolicy} or
+     * {@link LegacyDefaultMemberAccessPolicy}, then Jython wrapper will be skipped for security reasons.
      * 
      * <p>
      * When you override this method, you should first decide if you want to wrap the object in a custom way (and if so
@@ -265,16 +270,20 @@
      * behavior is fine with you).
      */
     protected TemplateModel handleUnknownType(Object obj) throws TemplateModelException {
-        if (obj instanceof Node) {
+        if (domNodeSupport && obj instanceof Node) {
             return wrapDomNode(obj);
         }
-        MemberAccessPolicy memberAccessPolicy = getMemberAccessPolicy();
-        if (memberAccessPolicy instanceof DefaultMemberAccessPolicy
-                || memberAccessPolicy instanceof LegacyDefaultMemberAccessPolicy) {
-            if (JYTHON_WRAPPER != null && JYTHON_OBJ_CLASS.isInstance(obj)) {
-                return JYTHON_WRAPPER.wrap(obj);
+
+        if (jythonSupport) {
+            MemberAccessPolicy memberAccessPolicy = getMemberAccessPolicy();
+            if (memberAccessPolicy instanceof DefaultMemberAccessPolicy
+                    || memberAccessPolicy instanceof LegacyDefaultMemberAccessPolicy) {
+                if (JYTHON_WRAPPER != null && JYTHON_OBJ_CLASS.isInstance(obj)) {
+                    return JYTHON_WRAPPER.wrap(obj);
+                }
             }
         }
+
         return super.wrap(obj); 
     }
     
@@ -389,6 +398,51 @@
     }
 
     /**
+     * Getter pair of {@link #setDOMNodeSupport(boolean)}; see there.
+     *
+     * @since 2.3.31
+     */
+    public final boolean getDOMNodeSupport() {
+        return domNodeSupport;
+    }
+
+    /**
+     * Enables wrapping {@link Node}-s on a special way (as described in the "XML Processing Guide" in the Manual);
+     * defaults to {@code true}.. If this is {@code true}, {@link Node}+s will be wrapped like any other generic object.
+     *
+     * @see #handleUnknownType(Object)
+     *
+     * @since 2.3.31
+     */
+    public void setDOMNodeSupport(boolean domNodeSupport) {
+        checkModifiable();
+        this.domNodeSupport = domNodeSupport;
+    }
+
+    /**
+     * Getter pair of {@link #setJythonSupport(boolean)}; see there.
+     *
+     * @since 2.3.31
+     */
+    public final boolean getJythonSupport() {
+        return jythonSupport;
+    }
+
+    /**
+     * Enables wrapping Jython objects in a special way; defaults to {@code true}. If this is {@code false}, they will
+     * be wrapped like any other generic object. Note that Jython wrapping is legacy feature, and might by disabled by
+     * the selected {@link MemberAccessPolicy}, even if this is {@code true}; see {@link #handleUnknownType(Object)}.
+     *
+     * @see #handleUnknownType(Object)
+     *
+     * @since 2.3.31
+     */
+    public void setJythonSupport(boolean jythonSupport) {
+        checkModifiable();
+        this.jythonSupport = jythonSupport;
+    }
+
+    /**
      * Returns the lowest version number that is equivalent with the parameter version.
      * 
      * @since 2.3.22
@@ -416,8 +470,12 @@
             }
         }
         
-        return "useAdaptersForContainers=" + useAdaptersForContainers + ", forceLegacyNonListCollections="
-                + forceLegacyNonListCollections + ", iterableSupport=" + iterableSupport + bwProps;
+        return "useAdaptersForContainers=" + useAdaptersForContainers
+                + ", forceLegacyNonListCollections=" + forceLegacyNonListCollections
+                + ", iterableSupport=" + iterableSupport
+                + ", domNodeSupport=" + domNodeSupport
+                + ", jythonSupport=" + jythonSupport
+                + bwProps;
     }
     
 }
diff --git a/src/main/java/freemarker/template/DefaultObjectWrapperConfiguration.java b/src/main/java/freemarker/template/DefaultObjectWrapperConfiguration.java
index ff474fa..a9575bc 100644
--- a/src/main/java/freemarker/template/DefaultObjectWrapperConfiguration.java
+++ b/src/main/java/freemarker/template/DefaultObjectWrapperConfiguration.java
@@ -35,6 +35,8 @@
     private boolean useAdaptersForContainers;
     private boolean forceLegacyNonListCollections;
     private boolean iterableSupport;
+    private boolean domNodeSupport;
+    private boolean jythonSupport;
 
     protected DefaultObjectWrapperConfiguration(Version incompatibleImprovements) {
         super(DefaultObjectWrapper.normalizeIncompatibleImprovementsVersion(incompatibleImprovements), true);
@@ -43,6 +45,8 @@
                 "freemarker.configuration", "DefaultObjectWrapper");
         useAdaptersForContainers = getIncompatibleImprovements().intValue() >= _TemplateAPI.VERSION_INT_2_3_22;
         forceLegacyNonListCollections = true; // [2.4]: = IcI < _TemplateAPI.VERSION_INT_2_4_0;
+        domNodeSupport = true;
+        jythonSupport = true;
     }
 
     /** See {@link DefaultObjectWrapper#getUseAdaptersForContainers()}. */
@@ -65,6 +69,26 @@
         this.forceLegacyNonListCollections = legacyNonListCollectionWrapping;
     }
 
+    /** See {@link DefaultObjectWrapper#getDOMNodeSupport()}. */
+    public boolean getDOMNodeSupport() {
+        return domNodeSupport;
+    }
+
+    /** See {@link DefaultObjectWrapper#setDOMNodeSupport(boolean)}. */
+    public void setDOMNodeSupport(boolean domNodeSupport) {
+        this.domNodeSupport = domNodeSupport;
+    }
+
+    /** See {@link DefaultObjectWrapper#getJythonSupport()}. */
+    public boolean getJythonSupport() {
+        return jythonSupport;
+    }
+
+    /** See {@link DefaultObjectWrapper#setJythonSupport(boolean)}. */
+    public void setJythonSupport(boolean jythonSupport) {
+        this.jythonSupport = jythonSupport;
+    }
+
     /**
      * See {@link DefaultObjectWrapper#getIterableSupport()}.
      * 
@@ -90,6 +114,8 @@
         result = result * prime + (useAdaptersForContainers ? 1231 : 1237);
         result = result * prime + (forceLegacyNonListCollections ? 1231 : 1237);
         result = result * prime + (iterableSupport ? 1231 : 1237);
+        result = result * prime + (domNodeSupport ? 1231 : 1237);
+        result = result * prime + (jythonSupport ? 1231 : 1237);
         return result;
     }
 
@@ -99,7 +125,9 @@
         final DefaultObjectWrapperConfiguration thatDowCfg = (DefaultObjectWrapperConfiguration) that;
         return useAdaptersForContainers == thatDowCfg.getUseAdaptersForContainers()
                 && forceLegacyNonListCollections == thatDowCfg.forceLegacyNonListCollections
-                && iterableSupport == thatDowCfg.iterableSupport;
+                && iterableSupport == thatDowCfg.iterableSupport
+                && domNodeSupport == thatDowCfg.domNodeSupport
+                && jythonSupport == thatDowCfg.jythonSupport;
     }
 
 }
diff --git a/src/manual/en_US/book.xml b/src/manual/en_US/book.xml
index a83ec5d..9d9123e 100644
--- a/src/manual/en_US/book.xml
+++ b/src/manual/en_US/book.xml
@@ -29179,18 +29179,18 @@
                 <para>If you are using the default object wrapper class
                 (<literal>freemarker.template.DefaultObjectWrapper</literal>),
                 or a subclass of it, you should disable the XML (DOM) wrapping
-                feature of it, by overriding <literal>wrapDomNode(Object
-                obj)</literal> so that it does this: <literal>return
-                getModelFactory(obj.getClass()).create(obj, this);</literal>.
-                The problem with the XML wrapping feature, which wraps
-                <literal>org.w3c.dom.Node</literal> objects on special way to
-                make them easier to work with in templates, is that this
-                facility by design lets template authors evaluate arbitrary
-                XPath expressions, and XPath can do too much in certain
-                setups. If you really need the XML wrapping facility, review
-                carefully what XPath expressions are possible in your setup.
-                Also, be sure you don't use the long deprecated, and more
-                dangerous <literal>freemarker.ext.xml</literal> package, only
+                feature of it, by setting its
+                <literal>DOMNodeSupport</literal> property to
+                <literal>false</literal>. The problem with the XML wrapping
+                feature, which wraps <literal>org.w3c.dom.Node</literal>
+                objects on special way to make them easier to work with in
+                templates, is that this facility by design lets template
+                authors evaluate arbitrary XPath expressions, and XPath can do
+                too much in certain setups. If you really need the XML
+                wrapping facility, review carefully what XPath expressions are
+                possible in your setup. Also, be sure you don't use the long
+                deprecated, and more dangerous
+                <literal>freemarker.ext.xml</literal> package, only
                 <literal>freemarker.ext.dom</literal>. Also, note that when
                 using the XML wrapping feature, not allowing
                 <literal>org.w3c.dom.Node</literal> methods in the
@@ -29419,6 +29419,16 @@
 
           <itemizedlist>
             <listitem>
+              <para>Added <literal>DOMNodeSupport</literal> and
+              <literal>JythonSupport</literal> <literal>boolean</literal>
+              properties to <literal>DefaultObjectWrapper</literal>. This
+              allows disabling the special wrapping of DOM nodes and Jython
+              classes. This might be desirable <link
+              linkend="faq_template_uploading_security">for security
+              reasons</link>.</para>
+            </listitem>
+
+            <listitem>
               <para><link
               xlink:href="https://issues.apache.org/jira/browse/FREEMARKER-145">FREEMARKER-145</link>:
               Fixed bug where methods with "overloaded" return type may become
diff --git a/src/test/java/freemarker/template/DefaultObjectWrapperTest.java b/src/test/java/freemarker/template/DefaultObjectWrapperTest.java
index b973358..d9924f1 100644
--- a/src/test/java/freemarker/template/DefaultObjectWrapperTest.java
+++ b/src/test/java/freemarker/template/DefaultObjectWrapperTest.java
@@ -46,7 +46,9 @@
 import javax.xml.parsers.DocumentBuilderFactory;
 import javax.xml.parsers.ParserConfigurationException;
 
+import org.hamcrest.Matchers;
 import org.junit.Test;
+import org.python.core.PyString;
 import org.w3c.dom.Document;
 import org.xml.sax.InputSource;
 import org.xml.sax.SAXException;
@@ -58,6 +60,7 @@
 import freemarker.ext.beans.EnumerationModel;
 import freemarker.ext.beans.HashAdapter;
 import freemarker.ext.beans.WhitelistMemberAccessPolicy;
+import freemarker.ext.jython.JythonSequenceModel;
 import freemarker.ext.util.WrapperTemplateModel;
 
 public class DefaultObjectWrapperTest {
@@ -1033,11 +1036,49 @@
     @Test
     public void testCanWrapDOM() throws SAXException, IOException, ParserConfigurationException,
             TemplateModelException {
+        assertTrue(OW22.wrap(createDocument()) instanceof TemplateNodeModel);
+    }
+
+    @Test
+    public void testDisabledDOMNodeWrapping() throws SAXException, IOException, ParserConfigurationException,
+            TemplateModelException {
+        Document doc = createDocument();
+        DefaultObjectWrapperBuilder dowB = new DefaultObjectWrapperBuilder(Configuration.VERSION_2_3_31);
+        dowB.setDOMNodeSupport(false);
+        DefaultObjectWrapper ow = dowB.build();
+
+        testDisabledDomWrappingInternal(doc, ow);
+
+        ow = new DefaultObjectWrapper(Configuration.VERSION_2_3_31);
+        ow.setDOMNodeSupport(false);
+        testDisabledDomWrappingInternal(doc, ow);
+    }
+
+    private void testDisabledDomWrappingInternal(Document doc, DefaultObjectWrapper ow) throws TemplateModelException {
+        TemplateModel model = ow.wrap(doc);
+        assertFalse(model instanceof TemplateNodeModel);
+        assertTrue(model instanceof TemplateHashModel);
+        assertNotNull(((TemplateHashModel) model).get("getDoctype"));
+        assertNotNull(((TemplateHashModel) model).get("class"));
+    }
+
+    @Test
+    public void testDisabledJythonWrapping() throws SAXException, IOException, ParserConfigurationException,
+            TemplateModelException {
+        PyString pyString = new PyString("foo");
+
+        DefaultObjectWrapper ow = new DefaultObjectWrapper(Configuration.VERSION_2_3_31);
+        assertThat(ow.wrap(pyString), Matchers.instanceOf(JythonSequenceModel.class));
+
+        ow.setJythonSupport(false);
+        assertThat(ow.wrap(pyString), not(Matchers.instanceOf(JythonSequenceModel.class)));
+    }
+
+    private Document createDocument() throws ParserConfigurationException, SAXException, IOException {
         DocumentBuilder db = DocumentBuilderFactory.newInstance().newDocumentBuilder();
         InputSource is = new InputSource();
         is.setCharacterStream(new StringReader("<doc><sub a='1' /></doc>"));
-        Document doc = db.parse(is);        
-        assertTrue(OW22.wrap(doc) instanceof TemplateNodeModel);
+        return db.parse(is);
     }
 
     @Test