From b9186a8dd52f67d6fe0c00ca382a390d26ee98c2 Mon Sep 17 00:00:00 2001 From: Zac Spitzer Date: Wed, 19 Jul 2023 09:24:03 +0200 Subject: [PATCH] LDEV-3451 add back support for pass thru of additional xml feature settings https://luceeserver.atlassian.net/browse/LDEV-3451 --- .../system/GetApplicationSettings.java | 9 +++++ .../java/lucee/runtime/text/xml/XMLUtil.java | 34 ++++++++++--------- test/tickets/LDEV4348.cfc | 13 +++++++ test/tickets/LDEV4348/Application.cfc | 8 +++++ 4 files changed, 48 insertions(+), 16 deletions(-) diff --git a/core/src/main/java/lucee/runtime/functions/system/GetApplicationSettings.java b/core/src/main/java/lucee/runtime/functions/system/GetApplicationSettings.java index 41a4de0df6..f8d0f32dce 100644 --- a/core/src/main/java/lucee/runtime/functions/system/GetApplicationSettings.java +++ b/core/src/main/java/lucee/runtime/functions/system/GetApplicationSettings.java @@ -116,6 +116,15 @@ public static Struct call(PageContext pc, boolean suppressFunctions, boolean onl sxml.setEL("secure", xmlFeatures.get("secure", true)); sxml.setEL("disallowDoctypeDecl", xmlFeatures.get("disallowDoctypeDecl", true)); sxml.setEL("externalGeneralEntities", xmlFeatures.get("externalGeneralEntities", false)); + if (!xmlFeatures.isEmpty()){ // pass thru other values + Iterator it = xmlFeatures.keySet().iterator(); + Key name; + while (it.hasNext()) { + name = KeyImpl.toKey(it.next()); + if (!sxml.containsKey( name ) ) + sxml.setEL(name,xmlFeatures.get(name)); + } + } sct.setEL("xmlFeatures", sxml); sct.setEL("customTagPaths", toArray(ac.getCustomTagMappings())); diff --git a/core/src/main/java/lucee/runtime/text/xml/XMLUtil.java b/core/src/main/java/lucee/runtime/text/xml/XMLUtil.java index 3764f06237..7064342483 100755 --- a/core/src/main/java/lucee/runtime/text/xml/XMLUtil.java +++ b/core/src/main/java/lucee/runtime/text/xml/XMLUtil.java @@ -332,24 +332,28 @@ private static DocumentBuilderFactory newDocumentBuilderFactory(InputSource vali boolean featureSecure = true; boolean disallowDocType = true; boolean externalGeneralEntities = false; + Struct features = null; // can be overriden per application PageContext pc = ThreadLocalPageContext.get(); if (pc != null) { ApplicationContextSupport ac = ((ApplicationContextSupport) pc.getApplicationContext()); - Struct features = ac == null ? null : ac.getXmlFeatures(); + features = ac == null ? null : ac.getXmlFeatures(); if (features != null) { try { // handle feature aliases, e.g. secure Object obj; obj = features.get(KEY_FEATURE_SECURE, null); if (obj != null) featureSecure = Caster.toBoolean(obj); + features.remove(KEY_FEATURE_SECURE, null); obj = features.get(KEY_FEATURE_DISALLOW_DOCTYPE_DECL, null); if (obj != null) disallowDocType = Caster.toBoolean(obj); + features.remove(KEY_FEATURE_DISALLOW_DOCTYPE_DECL, null); obj = features.get(KEY_FEATURE_EXTERNAL_GENERAL_ENTITIES, null); if (obj != null) externalGeneralEntities = Caster.toBoolean(obj); + features.remove(KEY_FEATURE_EXTERNAL_GENERAL_ENTITIES, null); } catch (PageException ex) { throw new RuntimeException(ex); @@ -357,7 +361,7 @@ private static DocumentBuilderFactory newDocumentBuilderFactory(InputSource vali } } - try { // handle feature aliases, e.g. secure + try { // set built in feature aliases if (featureSecure) { // set features per // https://cheatsheetseries.owasp.org/cheatsheets/XML_External_Entity_Prevention_Cheat_Sheet.html @@ -370,27 +374,25 @@ private static DocumentBuilderFactory newDocumentBuilderFactory(InputSource vali factory.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, ""); factory.setAttribute(XMLConstants.ACCESS_EXTERNAL_SCHEMA, ""); } - //features.remove(KEY_FEATURE_SECURE); factory.setFeature(XMLConstants.FEATURE_DISALLOW_DOCTYPE_DECL, disallowDocType); - //features.remove(KEY_FEATURE_DISALLOW_DOCTYPE_DECL); - factory.setFeature(XMLConstants.FEATURE_EXTERNAL_GENERAL_ENTITIES, externalGeneralEntities); - //features.remove(KEY_FEATURE_EXTERNAL_GENERAL_ENTITIES); } catch (ParserConfigurationException ex) { throw new RuntimeException(ex); } - /* - features.forEach((k, v) -> { - try { - factory.setFeature(k.toString().toLowerCase(), Caster.toBoolean(v)); - } - catch (PageException | ParserConfigurationException ex) { - throw new RuntimeException(ex); - } - }); - */ + // pass thru any additional feature directives + // https://xerces.apache.org/xerces2-j/features.html#disallow-doctype-decl + if (features != null){ + features.forEach((k, v) -> { + try { + factory.setFeature(k.toString().toLowerCase(), Caster.toBoolean(v)); + } + catch (PageException | ParserConfigurationException ex) { + throw new RuntimeException(ex); + } + }); + } return factory; } diff --git a/test/tickets/LDEV4348.cfc b/test/tickets/LDEV4348.cfc index ac04b5876b..540163dca8 100644 --- a/test/tickets/LDEV4348.cfc +++ b/test/tickets/LDEV4348.cfc @@ -43,6 +43,19 @@ component extends = "org.lucee.cfml.test.LuceeTestCase" labels="xml" { expect( result.externalGeneralEntities ).toBeTrue(); }); + it( title="Check xmlFeatures, check pass thru",body = function ( currentSpec ) { + local.result = _InternalRequest( + template : "#uri#/LDEV4348.cfm", + forms : { + scene: "testPassthru" + } + ).filecontent.deserializeJson(); + expect( result.secure ).toBeFalse(); + expect( result.disallowDoctypeDecl ).toBeFalse(); + expect( result.externalGeneralEntities ).toBeTrue(); + expect( result["http://apache.org/xml/features/validation/id-idref-checking"] ).toBeTrue(); + }); + }); } diff --git a/test/tickets/LDEV4348/Application.cfc b/test/tickets/LDEV4348/Application.cfc index efeafcde28..dd0546672c 100644 --- a/test/tickets/LDEV4348/Application.cfc +++ b/test/tickets/LDEV4348/Application.cfc @@ -17,6 +17,14 @@ component { "disallowDoctypeDecl": false }; break; + case "testPassthru": + this.xmlFeatures = { + "externalGeneralEntities": true, + "secure": false, + "disallowDoctypeDecl": false, + "http://apache.org/xml/features/validation/id-idref-checking": true + }; + break; case "default": break; default: