VYPR
Moderate severityNVD Advisory· Published Feb 26, 2014· Updated Apr 29, 2026

CVE-2013-4590

CVE-2013-4590

Description

Apache Tomcat before 6.0.39, 7.x before 7.0.50, and 8.x before 8.0.0-RC10 allows attackers to obtain "Tomcat internals" information by leveraging the presence of an untrusted web application with a context.xml, web.xml, *.jspx, *.tagx, or *.tld XML document containing an external entity declaration in conjunction with an entity reference, related to an XML External Entity (XXE) issue.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
org.apache.tomcat:tomcatMaven
< 6.0.396.0.39
org.apache.tomcat:tomcatMaven
>= 7.0.0, < 7.0.507.0.50
org.apache.tomcat:tomcatMaven
>= 8.0.0-RC1, < 8.0.0-RC108.0.0-RC10

Affected products

193
  • Apache/Tomcat191 versions
    cpe:2.3:a:apache:tomcat:*:*:*:*:*:*:*:*+ 190 more
    • cpe:2.3:a:apache:tomcat:*:*:*:*:*:*:*:*range: <=6.0.37
    • cpe:2.3:a:apache:tomcat:1.1.3:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:tomcat:3.0:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:tomcat:3.1:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:tomcat:3.1.1:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:tomcat:3.2:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:tomcat:3.2.1:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:tomcat:3.2.2:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:tomcat:3.2.2:beta2:*:*:*:*:*:*
    • cpe:2.3:a:apache:tomcat:3.2.3:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:tomcat:3.2.4:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:tomcat:3.3:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:tomcat:3.3.1:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:tomcat:3.3.1a:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:tomcat:3.3.2:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:tomcat:4:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:tomcat:4.0.0:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:tomcat:4.0.1:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:tomcat:4.0.2:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:tomcat:4.0.3:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:tomcat:4.0.4:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:tomcat:4.0.5:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:tomcat:4.0.6:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:tomcat:4.1.0:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:tomcat:4.1.1:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:tomcat:4.1.10:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:tomcat:4.1.12:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:tomcat:4.1.15:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:tomcat:4.1.2:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:tomcat:4.1.24:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:tomcat:4.1.28:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:tomcat:4.1.29:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:tomcat:4.1.3:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:tomcat:4.1.31:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:tomcat:4.1.36:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:tomcat:4.1.3:beta:*:*:*:*:*:*
    • cpe:2.3:a:apache:tomcat:4.1.9:beta:*:*:*:*:*:*
    • cpe:2.3:a:apache:tomcat:5:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:tomcat:5.0.0:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:tomcat:5.0.1:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:tomcat:5.0.10:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:tomcat:5.0.11:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:tomcat:5.0.12:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:tomcat:5.0.13:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:tomcat:5.0.14:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:tomcat:5.0.15:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:tomcat:5.0.16:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:tomcat:5.0.17:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:tomcat:5.0.18:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:tomcat:5.0.19:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:tomcat:5.0.2:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:tomcat:5.0.21:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:tomcat:5.0.22:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:tomcat:5.0.23:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:tomcat:5.0.24:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:tomcat:5.0.25:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:tomcat:5.0.26:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:tomcat:5.0.27:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:tomcat:5.0.28:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:tomcat:5.0.29:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:tomcat:5.0.3:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:tomcat:5.0.30:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:tomcat:5.0.4:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:tomcat:5.0.5:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:tomcat:5.0.6:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:tomcat:5.0.7:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:tomcat:5.0.8:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:tomcat:5.0.9:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:tomcat:5.5.0:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:tomcat:5.5.1:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:tomcat:5.5.10:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:tomcat:5.5.11:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:tomcat:5.5.12:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:tomcat:5.5.13:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:tomcat:5.5.14:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:tomcat:5.5.15:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:tomcat:5.5.16:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:tomcat:5.5.17:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:tomcat:5.5.18:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:tomcat:5.5.19:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:tomcat:5.5.2:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:tomcat:5.5.20:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:tomcat:5.5.21:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:tomcat:5.5.22:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:tomcat:5.5.23:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:tomcat:5.5.24:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:tomcat:5.5.25:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:tomcat:5.5.26:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:tomcat:5.5.27:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:tomcat:5.5.28:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:tomcat:5.5.29:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:tomcat:5.5.3:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:tomcat:5.5.30:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:tomcat:5.5.31:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:tomcat:5.5.32:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:tomcat:5.5.33:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:tomcat:5.5.34:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:tomcat:5.5.35:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:tomcat:5.5.4:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:tomcat:5.5.5:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:tomcat:5.5.6:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:tomcat:5.5.7:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:tomcat:5.5.8:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:tomcat:5.5.9:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:tomcat:6:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:tomcat:6.0:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:tomcat:6.0.0:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:tomcat:6.0.0:alpha:*:*:*:*:*:*
    • cpe:2.3:a:apache:tomcat:6.0.1:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:tomcat:6.0.10:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:tomcat:6.0.11:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:tomcat:6.0.12:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:tomcat:6.0.13:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:tomcat:6.0.14:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:tomcat:6.0.15:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:tomcat:6.0.16:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:tomcat:6.0.17:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:tomcat:6.0.18:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:tomcat:6.0.19:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:tomcat:6.0.1:alpha:*:*:*:*:*:*
    • cpe:2.3:a:apache:tomcat:6.0.2:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:tomcat:6.0.20:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:tomcat:6.0.24:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:tomcat:6.0.26:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:tomcat:6.0.27:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:tomcat:6.0.28:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:tomcat:6.0.29:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:tomcat:6.0.2:alpha:*:*:*:*:*:*
    • cpe:2.3:a:apache:tomcat:6.0.2:beta:*:*:*:*:*:*
    • cpe:2.3:a:apache:tomcat:6.0.3:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:tomcat:6.0.30:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:tomcat:6.0.31:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:tomcat:6.0.32:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:tomcat:6.0.33:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:tomcat:6.0.35:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:tomcat:6.0.36:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:tomcat:7.0.0:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:tomcat:7.0.0:beta:*:*:*:*:*:*
    • cpe:2.3:a:apache:tomcat:7.0.1:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:tomcat:7.0.10:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:tomcat:7.0.11:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:tomcat:7.0.12:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:tomcat:7.0.13:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:tomcat:7.0.14:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:tomcat:7.0.15:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:tomcat:7.0.16:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:tomcat:7.0.17:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:tomcat:7.0.18:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:tomcat:7.0.19:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:tomcat:7.0.2:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:tomcat:7.0.20:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:tomcat:7.0.21:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:tomcat:7.0.22:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:tomcat:7.0.23:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:tomcat:7.0.24:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:tomcat:7.0.25:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:tomcat:7.0.26:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:tomcat:7.0.27:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:tomcat:7.0.28:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:tomcat:7.0.29:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:tomcat:7.0.2:beta:*:*:*:*:*:*
    • cpe:2.3:a:apache:tomcat:7.0.3:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:tomcat:7.0.30:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:tomcat:7.0.31:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:tomcat:7.0.32:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:tomcat:7.0.33:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:tomcat:7.0.34:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:tomcat:7.0.35:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:tomcat:7.0.36:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:tomcat:7.0.37:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:tomcat:7.0.38:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:tomcat:7.0.39:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:tomcat:7.0.4:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:tomcat:7.0.40:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:tomcat:7.0.41:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:tomcat:7.0.42:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:tomcat:7.0.43:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:tomcat:7.0.44:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:tomcat:7.0.45:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:tomcat:7.0.46:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:tomcat:7.0.4:beta:*:*:*:*:*:*
    • cpe:2.3:a:apache:tomcat:7.0.50:*:*:*:*:*:*:*
    • cpe:2.3:a:apache:tomcat:8.0.0:rc1:*:*:*:*:*:*
    • cpe:2.3:a:apache:tomcat:8.0.0:rc2:*:*:*:*:*:*
    • cpe:2.3:a:apache:tomcat:8.0.0:rc3:*:*:*:*:*:*
    • cpe:2.3:a:apache:tomcat:8.0.0:rc4:*:*:*:*:*:*
    • cpe:2.3:a:apache:tomcat:8.0.0:rc5:*:*:*:*:*:*
    • cpe:2.3:a:apache:tomcat:8.0.0:rc6:*:*:*:*:*:*
    • cpe:2.3:a:apache:tomcat:8.0.0:rc7:*:*:*:*:*:*
    • cpe:2.3:a:apache:tomcat:8.0.0:rc8:*:*:*:*:*:*
    • cpe:2.3:a:apache:tomcat:8.0.0:rc9:*:*:*:*:*:*
  • cpe:2.3:o:debian:debian_linux:7.0:*:*:*:*:*:*:*
  • cpe:2.3:o:oracle:solaris:11.2:*:*:*:*:*:*:*

Patches

3
b9e06ead0198

Add an option to the Context to control the blocking of XML external entities when parsing XML configuration files and enable this blocking by default when a security manager is used. The block is implemented via a custom resolver to enable the logging of any blocked entities.

https://github.com/apache/tomcatMark Emlyn David ThomasDec 9, 2013via ghsa
26 files changed · +374 102
  • java/org/apache/catalina/ant/ValidatorTask.java+5 1 modified
    @@ -21,6 +21,7 @@
     import java.io.FileInputStream;
     import java.io.InputStream;
     
    +import org.apache.catalina.Globals;
     import org.apache.catalina.startup.Constants;
     import org.apache.tomcat.util.descriptor.DigesterFactory;
     import org.apache.tomcat.util.digester.Digester;
    @@ -87,7 +88,10 @@ public void execute() throws BuildException {
             Thread.currentThread().setContextClassLoader
                 (ValidatorTask.class.getClassLoader());
     
    -        Digester digester = DigesterFactory.newDigester(true, true, null);
    +        // Called through trusted manager interface. If running under a
    +        // SecurityManager assume that untrusted applications may be deployed.
    +        Digester digester = DigesterFactory.newDigester(
    +                true, true, null, Globals.IS_SECURITY_ENABLED);
             try {
                 file = file.getCanonicalFile();
                 InputStream stream = 
    
  • java/org/apache/catalina/Context.java+19 0 modified
    @@ -674,6 +674,25 @@ public void setSessionCookiePathUsesTrailingSlash(
         public void setTldNamespaceAware(boolean tldNamespaceAware);
     
     
    +    /**
    +     * Will the parsing of web.xml, web-fragment.xml, *.tld, *.jspx, *.tagx and
    +     * tagplugin.xml files for this Context block the use of external entities?
    +     *
    +     * @return true if access to external entities is blocked
    +     */
    +    public boolean getXmlBlockExternal();
    +
    +
    +    /**
    +     * Controls whether the parsing of web.xml, web-fragment.xml, *.tld, *.jspx,
    +     * *.tagx and tagplugin.xml files for this Context will block the use of
    +     * external entities.
    +     *
    +     * @param xmlBlockExternal true to block external entities
    +     */
    +    public void setXmlBlockExternal(boolean xmlBlockExternal);
    +
    +
         /**
          * Will the parsing of *.tld files for this Context be performed by a
          * validating parser?
    
  • java/org/apache/catalina/core/ApplicationContext.java+14 3 modified
    @@ -320,12 +320,20 @@ public String getContextPath() {
          */
         @Override
         public String getInitParameter(final String name) {
    -        // Special handling for XML validation as the context setting must
    +        // Special handling for XML settings as the context setting must
             // always override anything that might have been set by an application.
             if (Globals.JASPER_XML_VALIDATION_TLD_INIT_PARAM.equals(name) &&
                     context.getTldValidation()) {
                 return "true";
             }
    +        if (Globals.JASPER_XML_BLOCK_EXTERNAL_INIT_PARAM.equals(name)) {
    +            if (context.getXmlBlockExternal()) {
    +                return "true";
    +            } else if (Globals.IS_SECURITY_ENABLED) {
    +                // System admin has explicitly changed the default
    +                return "false";
    +            }
    +        }
             return parameters.get(name);
         }
     
    @@ -338,11 +346,14 @@ public String getInitParameter(final String name) {
         public Enumeration<String> getInitParameterNames() {
             Set<String> names = new HashSet<String>();
             names.addAll(parameters.keySet());
    -        // Special handling for XML validation as this attribute will always be
    -        // available if validation has been enabled on the context
    +        // Special handling for XML settings as these attributes will always be
    +        // available if they have been set on the context
             if (context.getTldValidation()) {
                 names.add(Globals.JASPER_XML_VALIDATION_TLD_INIT_PARAM);
             }
    +        if (context.getXmlBlockExternal() || Globals.IS_SECURITY_ENABLED) {
    +            names.add(Globals.JASPER_XML_BLOCK_EXTERNAL_INIT_PARAM);
    +        }
             return Collections.enumeration(names);
         }
     
    
  • java/org/apache/catalina/core/StandardContext.java+19 0 modified
    @@ -698,6 +698,13 @@ public StandardContext() {
         protected int cacheMaxSize = 10240; // 10 MB
     
     
    +
    +    /**
    +     * Attribute used to turn on/off the use of external entities.
    +     */
    +    private boolean xmlBlockExternal = Globals.IS_SECURITY_ENABLED;
    +
    +
         /**
          * Cache object max size in KB.
          */
    @@ -6624,6 +6631,18 @@ public void setTldNamespaceAware(boolean tldNamespaceAware){
         }    
     
     
    +    @Override
    +    public void setXmlBlockExternal(boolean xmlBlockExternal) {
    +        this.xmlBlockExternal = xmlBlockExternal;
    +    }
    +
    +
    +    @Override
    +    public boolean getXmlBlockExternal() {
    +        return xmlBlockExternal;
    +    }
    +
    +
         @Override
         public void setTldValidation(boolean tldValidation){
             this.tldValidation = tldValidation;
    
  • java/org/apache/catalina/Globals.java+11 0 modified
    @@ -316,4 +316,15 @@ public final class Globals {
          */
         public static final String JASPER_XML_VALIDATION_TLD_INIT_PARAM =
                 "org.apache.jasper.XML_VALIDATE_TLD";
    +
    +
    +    /**
    +     * Name of the ServletContext init-param that determines if the JSP engine
    +     * will block external entities from being used in *.tld, *.jspx, *.tagx and
    +     * tagplugin.xml files.
    +     * <p>
    +     * This must be kept in sync with org.apache.jasper.Constants
    +     */
    +    public static final String JASPER_XML_BLOCK_EXTERNAL_INIT_PARAM =
    +            "org.apache.jasper.XML_BLOCK_EXTERNAL";
     }
    
  • java/org/apache/catalina/startup/ContextConfig.java+4 2 modified
    @@ -516,14 +516,16 @@ protected void authenticatorConfig() {
         public void createWebXmlDigester(boolean namespaceAware,
                 boolean validation) {
     
    +        boolean blockExternal = context.getXmlBlockExternal();
    +        
             webRuleSet = new WebRuleSet(false);
             webDigester = DigesterFactory.newDigester(validation,
    -                namespaceAware, webRuleSet);
    +                namespaceAware, webRuleSet, blockExternal);
             webDigester.getParser();
     
             webFragmentRuleSet = new WebRuleSet(true);
             webFragmentDigester = DigesterFactory.newDigester(validation,
    -                namespaceAware, webFragmentRuleSet);
    +                namespaceAware, webFragmentRuleSet, blockExternal);
             webFragmentDigester.getParser();
         }
     
    
  • java/org/apache/catalina/startup/FailedContext.java+5 0 modified
    @@ -427,6 +427,11 @@ public void setXmlValidation(boolean xmlValidation) { /* NO-OP */ }
     
         @Override
         public void setTldValidation(boolean tldValidation) { /* NO-OP */ }
    +    @Override
    +    public boolean getXmlBlockExternal() { return true; }
    +    @Override
    +    public void setXmlBlockExternal(boolean xmlBlockExternal) { /* NO-OP */ }
    +
         @Override
         public boolean getTldValidation() { return false; }
     
    
  • java/org/apache/catalina/startup/TldConfig.java+6 4 modified
    @@ -85,20 +85,21 @@ public final class TldConfig  implements LifecycleListener {
          * Create (if necessary) and return a Digester configured to process the
          * tld.
          */
    -    private static Digester createTldDigester(boolean validation) {
    +    private static Digester createTldDigester(boolean validation,
    +            boolean blockExternal) {
             
             Digester digester = null;
             if (!validation) {
                 if (tldDigesters[0] == null) {
                     tldDigesters[0] = DigesterFactory.newDigester(validation,
    -                        true, new TldRuleSet());
    +                        true, new TldRuleSet(), blockExternal);
                     tldDigesters[0].getParser();
                 }
                 digester = tldDigesters[0];
             } else {
                 if (tldDigesters[1] == null) {
                     tldDigesters[1] = DigesterFactory.newDigester(validation,
    -                        true, new TldRuleSet());
    +                        true, new TldRuleSet(), blockExternal);
                     tldDigesters[1].getParser();
                 }
                 digester = tldDigesters[1];
    @@ -567,7 +568,8 @@ public void lifecycleEvent(LifecycleEvent event) {
         
         private void init() {
             if (tldDigester == null){
    -            tldDigester = createTldDigester(context.getTldValidation());
    +            tldDigester = createTldDigester(context.getTldValidation(),
    +                    context.getXmlBlockExternal());
             }
         }
     
    
  • java/org/apache/jasper/compiler/ImplicitTagLibraryInfo.java+14 3 modified
    @@ -23,6 +23,7 @@
     import java.util.Set;
     import java.util.Vector;
     
    +import javax.servlet.ServletContext;
     import javax.servlet.jsp.tagext.FunctionInfo;
     import javax.servlet.jsp.tagext.TagFileInfo;
     import javax.servlet.jsp.tagext.TagInfo;
    @@ -124,11 +125,21 @@ public ImplicitTagLibraryInfo(JspCompilationContext ctxt,
                                     pi.addDependant(path, ctxt.getLastModified(path));
                                 }
                                 
    +                            ServletContext servletContext = ctxt.getServletContext();
                                 boolean validate = Boolean.parseBoolean(
    -                                    ctxt.getServletContext().getInitParameter(
    +                                    servletContext.getInitParameter(
                                                 Constants.XML_VALIDATION_TLD_INIT_PARAM));
    -
    -                            ParserUtils pu = new ParserUtils(validate);
    +                            String blockExternalString =
    +                                    servletContext.getInitParameter(
    +                                            Constants.XML_BLOCK_EXTERNAL_INIT_PARAM);
    +                            boolean blockExternal;
    +                            if (blockExternalString == null) {
    +                                blockExternal = Constants.IS_SECURITY_ENABLED;
    +                            } else {
    +                                blockExternal = Boolean.parseBoolean(blockExternalString);
    +                            }
    +                            
    +                            ParserUtils pu = new ParserUtils(validate, blockExternal);
                                 TreeNode tld = pu.parseXMLDocument(uri, in);
     
                                 if (tld.findAttribute("version") != null) {
    
  • java/org/apache/jasper/compiler/JspConfig.java+9 1 modified
    @@ -80,10 +80,18 @@ private void processWebDotXml() throws JasperException {
                 
                 boolean validate = Boolean.parseBoolean(
                         ctxt.getInitParameter(Constants.XML_VALIDATION_TLD_INIT_PARAM));
    +            String blockExternalString =
    +                    ctxt.getInitParameter(Constants.XML_BLOCK_EXTERNAL_INIT_PARAM);
    +            boolean blockExternal;
    +            if (blockExternalString == null) {
    +                blockExternal = Constants.IS_SECURITY_ENABLED;
    +            } else {
    +                blockExternal = Boolean.parseBoolean(blockExternalString);
    +            }
     
                 TreeNode webApp = null;
                 if (webXml.getInputSource() != null) {
    -                ParserUtils pu = new ParserUtils(validate);
    +                ParserUtils pu = new ParserUtils(validate, blockExternal);
                     webApp = pu.parseXMLDocument(webXml.getSystemId(),
                             webXml.getInputSource());
                 }
    
  • java/org/apache/jasper/compiler/JspDocumentParser.java+37 1 modified
    @@ -30,15 +30,19 @@
     import javax.xml.parsers.SAXParser;
     import javax.xml.parsers.SAXParserFactory;
     
    +import org.apache.jasper.Constants;
     import org.apache.jasper.JasperException;
     import org.apache.jasper.JspCompilationContext;
    +import org.apache.tomcat.util.descriptor.DigesterFactory;
    +import org.apache.tomcat.util.descriptor.LocalResolver;
     import org.xml.sax.Attributes;
     import org.xml.sax.InputSource;
     import org.xml.sax.Locator;
     import org.xml.sax.SAXException;
     import org.xml.sax.SAXParseException;
     import org.xml.sax.XMLReader;
     import org.xml.sax.ext.DefaultHandler2;
    +import org.xml.sax.ext.EntityResolver2;
     import org.xml.sax.helpers.AttributesImpl;
     
     /**
    @@ -91,6 +95,7 @@ class JspDocumentParser
         private boolean inDTD;
     
         private boolean isValidating;
    +    private final EntityResolver2 entityResolver;
     
         private ErrorDispatcher err;
         private boolean isTagFile;
    @@ -119,6 +124,20 @@ public JspDocumentParser(
             this.isTagFile = isTagFile;
             this.directivesOnly = directivesOnly;
             this.isTop = true;
    +
    +        String blockExternalString = ctxt.getServletContext().getInitParameter(
    +                Constants.XML_BLOCK_EXTERNAL_INIT_PARAM);
    +        boolean blockExternal;
    +        if (blockExternalString == null) {
    +            blockExternal = Constants.IS_SECURITY_ENABLED;
    +        } else {
    +            blockExternal = Boolean.parseBoolean(blockExternalString);
    +        }
    +
    +        this.entityResolver = new LocalResolver(
    +                DigesterFactory.SERVLET_API_PUBLIC_IDS,
    +                DigesterFactory.SERVLET_API_SYSTEM_IDS,
    +                blockExternal);
         }
     
         /*
    @@ -239,12 +258,29 @@ private void addInclude(Node parent, List<String> files) throws SAXException {
             }
         }
     
    +    
    +    @Override
    +    public InputSource getExternalSubset(String name, String baseURI)
    +            throws SAXException, IOException {
    +        return entityResolver.getExternalSubset(name, baseURI);
    +    }
    +
    +    
    +    
    +    @Override
    +    public InputSource resolveEntity(String publicId, String systemId)
    +            throws SAXException, IOException {
    +        return entityResolver.resolveEntity(publicId, systemId);
    +    }
    +
    +    
         @Override
         public InputSource resolveEntity(String name, String publicId, String baseURI, String systemId)
                 throws SAXException, IOException {
    -        return null;
    +        return entityResolver.resolveEntity(name, publicId, baseURI, systemId);
         }
     
    +    
         /*
          * Receives notification of the start of an element.
          *
    
  • java/org/apache/jasper/compiler/TagLibraryInfoImpl.java+13 4 modified
    @@ -31,6 +31,7 @@
     import java.util.Map;
     import java.util.Vector;
     
    +import javax.servlet.ServletContext;
     import javax.servlet.jsp.tagext.FunctionInfo;
     import javax.servlet.jsp.tagext.PageData;
     import javax.servlet.jsp.tagext.TagAttributeInfo;
    @@ -213,12 +214,20 @@ private void parseTLD(String uri, InputStream in, JarResource jarResource)
             Vector<TagFileInfo> tagFileVector = new Vector<TagFileInfo>();
             Hashtable<String, FunctionInfo> functionTable = new Hashtable<String, FunctionInfo>();
     
    -        boolean validate = Boolean.parseBoolean(
    -                ctxt.getServletContext().getInitParameter(
    -                        Constants.XML_VALIDATION_TLD_INIT_PARAM));
    +        ServletContext servletContext = ctxt.getServletContext();
    +        boolean validate = Boolean.parseBoolean(servletContext.getInitParameter(
    +                Constants.XML_VALIDATION_TLD_INIT_PARAM));
    +        String blockExternalString = servletContext.getInitParameter(
    +                Constants.XML_BLOCK_EXTERNAL_INIT_PARAM);
    +        boolean blockExternal;
    +        if (blockExternalString == null) {
    +            blockExternal = Constants.IS_SECURITY_ENABLED;
    +        } else {
    +            blockExternal = Boolean.parseBoolean(blockExternalString);
    +        }
     
             // Create an iterator over the child elements of our <taglib> element
    -        ParserUtils pu = new ParserUtils(validate);
    +        ParserUtils pu = new ParserUtils(validate, blockExternal);
             TreeNode tld = pu.parseXMLDocument(uri, in);
     
             // Check to see if the <taglib> root element contains a 'version'
    
  • java/org/apache/jasper/compiler/TagPluginManager.java+13 2 modified
    @@ -25,6 +25,7 @@
     
     import javax.servlet.ServletContext;
     
    +import org.apache.jasper.Constants;
     import org.apache.jasper.JasperException;
     import org.apache.jasper.compiler.tagplugin.TagPlugin;
     import org.apache.jasper.compiler.tagplugin.TagPluginContext;
    @@ -119,8 +120,18 @@ private void init(ErrorDispatcher err) throws JasperException {
         private void loadTagPlugins(ErrorDispatcher err, InputStream is)
                 throws JasperException {
     
    -        TreeNode root =
    -                (new ParserUtils(false)).parseXMLDocument(TAG_PLUGINS_XML, is);
    +        String blockExternalString = ctxt.getInitParameter(
    +                Constants.XML_BLOCK_EXTERNAL_INIT_PARAM);
    +        boolean blockExternal;
    +        if (blockExternalString == null) {
    +            blockExternal = Constants.IS_SECURITY_ENABLED;
    +        } else {
    +            blockExternal = Boolean.parseBoolean(blockExternalString);
    +        }
    +        
    +        ParserUtils pu = new ParserUtils(false, blockExternal);
    +        
    +        TreeNode root = pu.parseXMLDocument(TAG_PLUGINS_XML, is);
             if (root == null) {
                 return;
             }
    
  • java/org/apache/jasper/compiler/TldLocationsCache.java+23 5 modified
    @@ -289,10 +289,20 @@ private void tldScanWebXml() throws Exception {
                 boolean validate = Boolean.parseBoolean(
                         ctxt.getInitParameter(
                                 Constants.XML_VALIDATION_TLD_INIT_PARAM));
    -
    +            String blockExternalString = ctxt.getInitParameter(
    +                    Constants.XML_BLOCK_EXTERNAL_INIT_PARAM);
    +            boolean blockExternal;
    +            if (blockExternalString == null) {
    +                blockExternal = Constants.IS_SECURITY_ENABLED;
    +            } else {
    +                blockExternal = Boolean.parseBoolean(blockExternalString);
    +            }
    +            
                 // Parse the web application deployment descriptor
    +            ParserUtils pu = new ParserUtils(validate, blockExternal);
    +            
                 TreeNode webtld = null;
    -            webtld = new ParserUtils(validate).parseXMLDocument(webXml.getSystemId(),
    +            webtld = pu.parseXMLDocument(webXml.getSystemId(),
                         webXml.getInputSource());
     
                 // Allow taglib to be an element of the root or jsp-config (JSP2.0)
    @@ -498,9 +508,17 @@ private void tldScanStream(String resourcePath, String entryName,
                 boolean validate = Boolean.parseBoolean(
                         ctxt.getInitParameter(
                                 Constants.XML_VALIDATION_TLD_INIT_PARAM));
    -
    -            TreeNode tld = new ParserUtils(validate).parseXMLDocument(
    -                    resourcePath, stream);
    +            String blockExternalString = ctxt.getInitParameter(
    +                    Constants.XML_BLOCK_EXTERNAL_INIT_PARAM);
    +            boolean blockExternal;
    +            if (blockExternalString == null) {
    +                blockExternal = Constants.IS_SECURITY_ENABLED;
    +            } else {
    +                blockExternal = Boolean.parseBoolean(blockExternalString);
    +            }
    +            
    +            ParserUtils pu = new ParserUtils(validate, blockExternal); 
    +            TreeNode tld = pu.parseXMLDocument(resourcePath, stream);
                 TreeNode uriNode = tld.findChild("uri");
                 if (uriNode != null) {
                     String body = uriNode.getBody();
    
  • java/org/apache/jasper/Constants.java+9 0 modified
    @@ -241,4 +241,13 @@ public class Constants {
          */
         public static final String XML_VALIDATION_TLD_INIT_PARAM =
                 "org.apache.jasper.XML_VALIDATE_TLD";
    +
    +    /**
    +     * Name of the ServletContext init-param that determines if the XML parsers
    +     * will block the resolution of external entities.
    +     * <p>
    +     * This must be kept in sync with org.apache.catalina.Globals
    +     */
    +    public static final String XML_BLOCK_EXTERNAL_INIT_PARAM =
    +            "org.apache.jasper.XML_BLOCK_EXTERNAL";
     }
    
  • java/org/apache/jasper/JspC.java+15 0 modified
    @@ -127,6 +127,7 @@ public class JspC extends Task implements Options {
         protected static final String SWITCH_SMAP = "-smap";
         protected static final String SWITCH_DUMP_SMAP = "-dumpsmap";
         protected static final String SWITCH_VALIDATE_TLD = "-validateTld";
    +    protected static final String SWITCH_BLOCK_EXTERNAL = "-blockExternal";
         protected static final String SHOW_SUCCESS ="-s";
         protected static final String LIST_ERRORS = "-l";
         protected static final int INC_WEBXML = 10;
    @@ -158,6 +159,7 @@ public class JspC extends Task implements Options {
         protected boolean trimSpaces = false;
         protected boolean genStringAsCharArray = false;
         protected boolean validateTld;
    +    protected boolean blockExternal;
         protected boolean xpoweredBy;
         protected boolean mappedFile = false;
         protected boolean poolingEnabled = true;
    @@ -367,6 +369,8 @@ public void setArgs(String[] arg) throws JasperException {
                     smapDumped = true;
                 } else if (tok.equals(SWITCH_VALIDATE_TLD)) {
                     setValidateTld(true);
    +            } else if (tok.equals(SWITCH_BLOCK_EXTERNAL)) {
    +                setBlockExternal(true);
                 } else {
                     if (tok.startsWith("-")) {
                         throw new JasperException("Unrecognized option: " + tok +
    @@ -854,6 +858,14 @@ public boolean isValidateTld() {
             return validateTld;
         }
     
    +    public void setBlockExternal( boolean b ) {
    +        this.blockExternal = b;
    +    }
    +
    +    public boolean isBlockExternal() {
    +        return blockExternal;
    +    }
    +
         public void setListErrors( boolean b ) {
             listErrors = b;
         }
    @@ -1435,6 +1447,9 @@ protected void initServletContext() {
             if (isValidateTld()) {
                 context.setInitParameter(Constants.XML_VALIDATION_TLD_INIT_PARAM, "true");
             }
    +        if (isBlockExternal()) {
    +            context.setInitParameter(Constants.XML_BLOCK_EXTERNAL_INIT_PARAM, "true");
    +        }
     
             rctxt = new JspRuntimeContext(context, this);
             jspConfig = new JspConfig(context);
    
  • java/org/apache/jasper/xmlparser/ParserUtils.java+17 4 modified
    @@ -14,7 +14,6 @@
      * See the License for the specific language governing permissions and
      * limitations under the License.
      */
    -
     package org.apache.jasper.xmlparser;
     
     import java.io.IOException;
    @@ -24,9 +23,11 @@
     import javax.xml.parsers.DocumentBuilderFactory;
     import javax.xml.parsers.ParserConfigurationException;
     
    +import org.apache.jasper.Constants;
     import org.apache.jasper.JasperException;
     import org.apache.jasper.compiler.Localizer;
     import org.apache.tomcat.util.descriptor.DigesterFactory;
    +import org.apache.tomcat.util.descriptor.LocalResolver;
     import org.apache.tomcat.util.descriptor.XmlErrorHandler;
     import org.w3c.dom.Comment;
     import org.w3c.dom.Document;
    @@ -49,7 +50,6 @@
      * @author Craig R. McClanahan
      * @version $Id$
      */
    -
     public class ParserUtils {
     
         /**
    @@ -60,12 +60,25 @@ public class ParserUtils {
         /**
          * An entity resolver for use when parsing XML documents.
          */
    -    static EntityResolver entityResolver = DigesterFactory.SERVLET_API_RESOLVER;
    +    static EntityResolver entityResolver;
    +    
    +    private final EntityResolver entityResolverInstance;
         
         private final boolean validating;
     
         public ParserUtils(boolean validating) {
    +        this(validating, Constants.IS_SECURITY_ENABLED);
    +    }
    +
    +    public ParserUtils(boolean validating, boolean blockExternal) {
             this.validating = validating;
    +        if (entityResolver == null) {
    +            this.entityResolverInstance = new LocalResolver(
    +                    DigesterFactory.SERVLET_API_PUBLIC_IDS,
    +                    DigesterFactory.SERVLET_API_SYSTEM_IDS, blockExternal);
    +        } else {
    +            this.entityResolverInstance = entityResolver;
    +        }
         }
     
         // --------------------------------------------------------- Public Methods
    @@ -92,7 +105,7 @@ public TreeNode parseXMLDocument(String location, InputSource is)
                 factory.setNamespaceAware(true);
                 factory.setValidating(validating);
                 DocumentBuilder builder = factory.newDocumentBuilder();
    -            builder.setEntityResolver(entityResolver);
    +            builder.setEntityResolver(entityResolverInstance);
                 builder.setErrorHandler(errorHandler);
                 document = builder.parse(is);
             } catch (ParserConfigurationException ex) {
    
  • java/org/apache/tomcat/util/descriptor/DigesterFactory.java+18 5 modified
    @@ -17,13 +17,15 @@
     package org.apache.tomcat.util.descriptor;
     
     import java.net.URL;
    +import java.util.Collections;
     import java.util.HashMap;
     import java.util.Map;
     
     import javax.servlet.ServletContext;
     
     import org.apache.tomcat.util.digester.Digester;
     import org.apache.tomcat.util.digester.RuleSet;
    +import org.xml.sax.ext.EntityResolver2;
     
     /**
      * Wrapper class around the Digester that hide Digester's initialization
    @@ -32,10 +34,16 @@
     public class DigesterFactory {
     
         /**
    -     * A resolver for the resources packaged in servlet-api.jar
    +     * Mapping of well-known public IDs used by the Servlet API to the matching
    +     * local resource.
          */
    -    public static final LocalResolver SERVLET_API_RESOLVER;
    +    public static final Map<String,String> SERVLET_API_PUBLIC_IDS;
     
    +    /**
    +     * Mapping of well-known system IDs used by the Servlet API to the matching
    +     * local resource.
    +     */
    +    public static final Map<String,String> SERVLET_API_SYSTEM_IDS;
     
         static {
             Map<String, String> publicIds = new HashMap<String, String>();
    @@ -81,7 +89,8 @@ public class DigesterFactory {
             addSelf(systemIds, "javaee_web_services_1_3.xsd");
             addSelf(systemIds, "javaee_web_services_client_1_3.xsd");
     
    -        SERVLET_API_RESOLVER = new LocalResolver(publicIds, systemIds);
    +        SERVLET_API_PUBLIC_IDS = Collections.unmodifiableMap(publicIds);
    +        SERVLET_API_SYSTEM_IDS = Collections.unmodifiableMap(systemIds);
         }
     
         private static void addSelf(Map<String, String> ids, String id) {
    @@ -103,15 +112,19 @@ private static String idFor(String url) {
          * @param xmlValidation turn on/off xml validation
          * @param xmlNamespaceAware turn on/off namespace validation
          * @param rule an instance of <code>RuleSet</code> used for parsing the xml.
    +     * @param blockExternal turn on/off the blocking of external resources
          */
         public static Digester newDigester(boolean xmlValidation,
                                            boolean xmlNamespaceAware,
    -                                       RuleSet rule) {
    +                                       RuleSet rule,
    +                                       boolean blockExternal) {
             Digester digester = new Digester();
             digester.setNamespaceAware(xmlNamespaceAware);
             digester.setValidating(xmlValidation);
             digester.setUseContextClassLoader(true);
    -        digester.setEntityResolver(SERVLET_API_RESOLVER);
    +        EntityResolver2 resolver = new LocalResolver(SERVLET_API_PUBLIC_IDS,
    +                SERVLET_API_SYSTEM_IDS, blockExternal);
    +        digester.setEntityResolver(resolver);
             if (rule != null) {
                 digester.addRuleSet(rule);
             }
    
  • java/org/apache/tomcat/util/descriptor/LocalResolver.java+71 47 modified
    @@ -16,13 +16,15 @@
      */
     package org.apache.tomcat.util.descriptor;
     
    +import java.io.FileNotFoundException;
     import java.io.IOException;
     import java.net.MalformedURLException;
     import java.net.URI;
     import java.net.URISyntaxException;
     import java.net.URL;
     import java.util.Map;
     
    +import org.apache.tomcat.util.res.StringManager;
     import org.xml.sax.InputSource;
     import org.xml.sax.SAXException;
     import org.xml.sax.ext.EntityResolver2;
    @@ -32,22 +34,30 @@
      */
     public class LocalResolver implements EntityResolver2 {
     
    +    private static final StringManager sm =
    +            StringManager.getManager(Constants.PACKAGE_NAME);
    +
         private final Map<String,String> publicIds;
         private final Map<String,String> systemIds;
    -
    +    private final boolean blockExternal;
     
         /**
          * Constructor providing mappings of public and system identifiers to local
          * resources. Each map contains a mapping from a well-known identifier to a
          * URL for a local resource path.
          *
    -     * @param publicIds mapping of public identifiers to local resources
    -     * @param systemIds mapping of system identifiers to local resources
    +     * @param publicIds mapping of well-known public identifiers to local
    +     *                  resources
    +     * @param systemIds mapping of well-known system identifiers to local
    +     *                  resources
    +     * @param blockExternal are external resources blocked that are not
    +     *                      well-known
          */
         public LocalResolver(Map<String,String> publicIds,
    -            Map<String,String> systemIds) {
    +            Map<String,String> systemIds, boolean blockExternal) {
             this.publicIds = publicIds;
             this.systemIds = systemIds;
    +        this.blockExternal = blockExternal;
         }
     
     
    @@ -60,63 +70,77 @@ public InputSource resolveEntity(String publicId, String systemId)
     
         @Override
         public InputSource resolveEntity(String name, String publicId,
    -            String baseURI, String systemId) throws SAXException, IOException {
    -
    -        String resolved = resolve(publicId, systemId, baseURI);
    -        if (resolved == null) {
    -            return null;
    -        }
    +            String base, String systemId) throws SAXException, IOException {
     
    -        InputSource is = new InputSource(resolved);
    -        is.setPublicId(publicId);
    -        return is;
    -    }
    -
    -
    -    @Override
    -    public InputSource getExternalSubset(String name, String baseURI)
    -            throws SAXException, IOException {
    -        return null;
    -    }
    -
    -
    -    private String resolve(String publicId, String systemId, String baseURI) {
    -        // try resolving using the publicId
    +        // First try resolving using the publicId
             String resolved = publicIds.get(publicId);
             if (resolved != null) {
    -            return resolved;
    +            InputSource is = new InputSource(resolved);
    +            is.setPublicId(publicId);
    +            return is;
             }
     
    -        // try resolving using the systemId
    +        // If there is no systemId, can't try anything else
             if (systemId == null) {
    -            return null;
    +            throw new FileNotFoundException(sm.getString("localResolver.unresolvedEntity",
    +                    name, publicId, systemId, base));
             }
     
    -        systemId = resolve(baseURI, systemId);
    +        // Try resolving with the supplied systemId
             resolved = systemIds.get(systemId);
             if (resolved != null) {
    -            return resolved;
    +            InputSource is = new InputSource(resolved);
    +            is.setPublicId(publicId);
    +            return is;
             }
     
    -        // fall back to the supplied systemId
    -        return systemId;
    -    }
    -
    -
    -    private static String resolve(String baseURI, String systemId) {
    +        // Resolve the supplied systemId against the base
    +        URI systemUri;
             try {
    -            if (baseURI == null) {
    -                return systemId;
    +            if (base == null) {
    +                systemUri = new URI(systemId);
    +            } else {
    +                // Can't use URI.resolve() because "jar:..." URLs are not valid
    +                // hierarchical URIs so resolve() does not work. new URL()
    +                // delegates to the jar: stream handler and it manages to figure
    +                // it out.
    +                URI baseUri = new URI(base);
    +                systemUri = new URL(baseUri.toURL(), systemId).toURI();
                 }
    -            URI systemUri = new URI(systemId);
    -            if (systemUri.isAbsolute()) {
    -                return systemId;
    -            }
    -            return new URL(new URL(baseURI), systemId).toString();
    +            systemUri = systemUri.normalize();
             } catch (URISyntaxException e) {
    -            return systemId;
    -        } catch (MalformedURLException e) {
    -            return systemId;
    +            // May be caused by a | being used instead of a : in an absolute
    +            // file URI on Windows.
    +            if (blockExternal) {
    +                // Absolute paths aren't allowed so block it
    +                throw new MalformedURLException(e.getMessage());
    +            } else {
    +                // See if the URLHandler can resolve it
    +                return new InputSource(systemId);
    +            }
    +        }
    +        if (systemUri.isAbsolute()) {
    +            // Try the resolved systemId
    +            resolved = systemIds.get(systemUri.toString());
    +            if (resolved != null) {
    +                InputSource is = new InputSource(resolved);
    +                is.setPublicId(publicId);
    +                return is;
    +            }
    +            if (!blockExternal) {
    +                InputSource is = new InputSource(systemUri.toString());
    +                is.setPublicId(publicId);
    +                return is;
    +            }
             }
    +        throw new FileNotFoundException(sm.getString("localResolver.unresolvedEntity",
    +                name, publicId, systemId, base));
    +    }
    +
    +
    +    @Override
    +    public InputSource getExternalSubset(String name, String baseURI)
    +            throws SAXException, IOException {
    +        return null;
         }
    -}
    \ No newline at end of file
    +}
    
  • java/org/apache/tomcat/util/descriptor/LocalStrings.properties+2 0 modified
    @@ -13,5 +13,7 @@
     # See the License for the specific language governing permissions and
     # limitations under the License.
     
    +localResolver.unresolvedEntity=Could not resolve XML resource [{0}] with public ID [{1}], system ID [{2}] and base URI [{3}] to a known, local entity.
    +
     xmlErrorHandler.error=Non-fatal error [{0}] reported processing [{1}].
     xmlErrorHandler.warning=Warning [{0}] reported processing [{1}].
    
  • test/javax/servlet/resources/TestSchemaValidation.java+10 10 modified
    @@ -31,8 +31,8 @@ public class TestSchemaValidation {
     
         @Test
         public void testWebapp_2_2() throws Exception {
    -        Digester digester =
    -                DigesterFactory.newDigester(true, true, new WebRuleSet(false));
    +        Digester digester = DigesterFactory.newDigester(
    +                true, true, new WebRuleSet(false), true);
             digester.push(new WebXml());
             WebXml desc = (WebXml) digester.parse(
                     new File("test/webapp-2.2/WEB-INF/web.xml"));
    @@ -42,8 +42,8 @@ public void testWebapp_2_2() throws Exception {
     
         @Test
         public void testWebapp_2_3() throws Exception {
    -        Digester digester =
    -                DigesterFactory.newDigester(true, true, new WebRuleSet(false));
    +        Digester digester = DigesterFactory.newDigester(
    +                true, true, new WebRuleSet(false), true);
             digester.push(new WebXml());
             WebXml desc = (WebXml) digester.parse(
                     new File("test/webapp-2.3/WEB-INF/web.xml"));
    @@ -53,8 +53,8 @@ public void testWebapp_2_3() throws Exception {
     
         @Test
         public void testWebapp_2_4() throws Exception {
    -        Digester digester =
    -                DigesterFactory.newDigester(true, true, new WebRuleSet(false));
    +        Digester digester = DigesterFactory.newDigester(
    +                true, true, new WebRuleSet(false), true);
             digester.push(new WebXml());
             WebXml desc = (WebXml) digester.parse(
                     new File("test/webapp-2.4/WEB-INF/web.xml"));
    @@ -63,8 +63,8 @@ public void testWebapp_2_4() throws Exception {
     
         @Test
         public void testWebapp_2_5() throws Exception {
    -        Digester digester =
    -                DigesterFactory.newDigester(true, true, new WebRuleSet(false));
    +        Digester digester = DigesterFactory.newDigester(
    +                true, true, new WebRuleSet(false), true);
             digester.push(new WebXml());
             WebXml desc = (WebXml) digester.parse(
                     new File("test/webapp-2.5/WEB-INF/web.xml"));
    @@ -73,8 +73,8 @@ public void testWebapp_2_5() throws Exception {
     
         @Test
         public void testWebapp_3_0() throws Exception {
    -        Digester digester =
    -                DigesterFactory.newDigester(true, true, new WebRuleSet(false));
    +        Digester digester = DigesterFactory.newDigester(
    +                true, true, new WebRuleSet(false), true);
             digester.push(new WebXml());
             WebXml desc = (WebXml) digester.parse(
                     new File("test/webapp-3.0/WEB-INF/web.xml"));
    
  • test/org/apache/catalina/core/TesterContext.java+10 0 modified
    @@ -635,6 +635,16 @@ public boolean getTldValidation() {
             return false;
         }
     
    +    @Override
    +    public boolean getXmlBlockExternal() {
    +        return false;
    +    }
    +
    +    @Override
    +    public void setXmlBlockExternal(boolean xmlBlockExternal) {
    +        // NO-OP
    +    }
    +
         @Override
         public boolean getTldNamespaceAware() {
             return true;
    
  • test/org/apache/tomcat/util/descriptor/TestLocalResolver.java+11 10 modified
    @@ -16,6 +16,7 @@
      */
     package org.apache.tomcat.util.descriptor;
     
    +import java.io.FileNotFoundException;
     import java.io.IOException;
     import java.util.HashMap;
     import java.util.Map;
    @@ -34,7 +35,7 @@ public class TestLocalResolver {
         private final Map<String, String> publicIds = new HashMap<String, String>();
         private final Map<String, String> systemIds = new HashMap<String, String>();
     
    -    private LocalResolver resolver = new LocalResolver(publicIds, systemIds);
    +    private LocalResolver resolver = new LocalResolver(publicIds, systemIds, true);
         private String WEB_22_LOCAL;
         private String WEB_30_LOCAL;
         private String WEBCOMMON_30_LOCAL;
    @@ -53,25 +54,25 @@ public String urlFor(String id) {
             return ServletContext.class.getResource(id).toExternalForm();
         }
     
    -    @Test
    -    public void unknownNullIdIsNull() throws IOException, SAXException {
    +    @Test(expected = FileNotFoundException.class)
    +    public void unknownNullId() throws IOException, SAXException {
             Assert.assertNull(resolver.resolveEntity(null, null));
         }
     
    -    @Test
    -    public void unknownPublicIdIsNull() throws IOException, SAXException {
    +    @Test(expected = FileNotFoundException.class)
    +    public void unknownPublicId() throws IOException, SAXException {
             Assert.assertNull(resolver.resolveEntity("unknown", null));
         }
     
    -    @Test
    -    public void unknownSystemIdIsReturned() throws IOException, SAXException {
    +    @Test(expected = FileNotFoundException.class)
    +    public void unknownSystemId() throws IOException, SAXException {
             InputSource source = resolver.resolveEntity(null, "unknown");
             Assert.assertEquals(null, source.getPublicId());
             Assert.assertEquals("unknown", source.getSystemId());
         }
     
    -    @Test
    -    public void unknownSystemIdIsResolvedAgainstBaseURI()
    +    @Test(expected = FileNotFoundException.class)
    +    public void unknownRelativeSystemId()
                 throws IOException, SAXException {
             InputSource source = resolver.resolveEntity(
                     null, null, "http://example.com/home.html", "unknown");
    @@ -121,4 +122,4 @@ public void absoluteSystemIdOverridesBaseURI()
             Assert.assertEquals(null, source.getPublicId());
             Assert.assertEquals(WEB_30_LOCAL, source.getSystemId());
         }
    -}
    \ No newline at end of file
    +}
    
  • webapps/docs/changelog.xml+6 0 modified
    @@ -216,6 +216,12 @@
             <bug>55851</bug>: Further fixes to enable SPNEGO authentication to work
             with IBM JDKs. Based on a patch by Arunav Sanyal. (markt)
           </fix>
    +      <add>
    +        Add an option to the Context to control the blocking of XML external
    +        entities when parsing XML configuration files and enable this blocking
    +        by default when a security manager is used. The block is implemented via
    +        a custom resolver to enable the logging of any blocked entities. (markt)
    +      </add>
         </changelog>
       </subsection>
       <subsection name="Coyote">
    
  • webapps/docs/config/context.xml+10 0 modified
    @@ -524,6 +524,16 @@
             Context.  If not specified, a standard default value will be used.</p>
           </attribute>
     
    +      <attribute name="xmlBlockExternal" required="false">
    +        <p>If the value of this flag is <code>true</code>, the parsing of
    +        <code>web.xml</code>, <code>web-fragment.xml</code>, <code>*.tld</code>,
    +        <code>*.jspx</code>, <code>*.tagx</code> and <code>tagPlugins.xml</code>
    +        files for this web application will not permit external entities to be
    +        loaded. If a <code>SecurityManager</code> is configured then the default
    +        value of this attribute will be <code>true</code>, else the default
    +        value will be <code>false</code>.</p>
    +      </attribute>
    +
           <attribute name="xmlNamespaceAware" required="false">
             <p>If the value of this flag is <code>true</code>, the parsing of
             <code>web.xml</code> and <code>web-fragment.xml</code> files for this
    
  • webapps/docs/security-howto.xml+3 0 modified
    @@ -179,6 +179,9 @@
         <ul>
           <li>The default value for the <strong>deployXML</strong> attribute of the
           <strong>Host</strong> element is changed to <code>false</code>.</li>
    +      <li>The default value for the <strong>xmlBlockExternal</strong> attribute
    +      of the <strong>Context</strong> element is changed to <code>true</code>.
    +      </li>
         </ul>
       </section>
     
    
05c84ff8304a

Add an option to the Context to control the blocking of XML external entities when parsing XML configuration files and enable this blocking by default. The block is implemented via a custom resolver to enable the logging of any blocked entities.

https://github.com/apache/tomcatMark Emlyn David ThomasDec 9, 2013via ghsa
29 files changed · +351 109
  • java/org/apache/catalina/ant/ValidatorTask.java+5 1 modified
    @@ -24,6 +24,7 @@
     import java.io.FileInputStream;
     import java.io.InputStream;
     
    +import org.apache.catalina.Globals;
     import org.apache.catalina.startup.Constants;
     import org.apache.tomcat.util.descriptor.DigesterFactory;
     import org.apache.tomcat.util.digester.Digester;
    @@ -90,7 +91,10 @@ public void execute() throws BuildException {
             Thread.currentThread().setContextClassLoader
                 (ValidatorTask.class.getClassLoader());
     
    -        Digester digester = DigesterFactory.newDigester(true, true, null);
    +        // Called through trusted manager interface. If running under a
    +        // SecurityManager assume that untrusted applications may be deployed.
    +        Digester digester = DigesterFactory.newDigester(
    +                true, true, null, Globals.IS_SECURITY_ENABLED);
             try {
                 file = file.getCanonicalFile();
                 InputStream stream =
    
  • java/org/apache/catalina/Context.java+19 0 modified
    @@ -616,6 +616,25 @@ public void setSessionCookiePathUsesTrailingSlash(
         public void setXmlValidation(boolean xmlValidation);
     
     
    +    /**
    +     * Will the parsing of web.xml, web-fragment.xml, *.tld, *.jspx, *.tagx and
    +     * tagplugin.xml files for this Context block the use of external entities?
    +     *
    +     * @return true if access to external entities is blocked
    +     */
    +    public boolean getXmlBlockExternal();
    +
    +
    +    /**
    +     * Controls whether the parsing of web.xml, web-fragment.xml, *.tld, *.jspx,
    +     * *.tagx and tagplugin.xml files for this Context will block the use of
    +     * external entities.
    +     *
    +     * @param xmlBlockExternal true to block external entities
    +     */
    +    public void setXmlBlockExternal(boolean xmlBlockExternal);
    +
    +
         /**
          * Will the parsing of *.tld files for this Context be performed by a
          * validating parser?
    
  • java/org/apache/catalina/core/ApplicationContext.java+14 3 modified
    @@ -304,12 +304,20 @@ public String getContextPath() {
          */
         @Override
         public String getInitParameter(final String name) {
    -        // Special handling for XML validation as the context setting must
    +        // Special handling for XML settings as the context setting must
             // always override anything that might have been set by an application.
             if (Globals.JASPER_XML_VALIDATION_TLD_INIT_PARAM.equals(name) &&
                     context.getTldValidation()) {
                 return "true";
             }
    +        if (Globals.JASPER_XML_BLOCK_EXTERNAL_INIT_PARAM.equals(name)) {
    +            if (context.getXmlBlockExternal()) {
    +                return "true";
    +            } else if (Globals.IS_SECURITY_ENABLED) {
    +                // System admin has explicitly changed the default
    +                return "false";
    +            }
    +        }
             return parameters.get(name);
         }
     
    @@ -322,11 +330,14 @@ public String getInitParameter(final String name) {
         public Enumeration<String> getInitParameterNames() {
             Set<String> names = new HashSet<>();
             names.addAll(parameters.keySet());
    -        // Special handling for XML validation as this attribute will always be
    -        // available if validation has been enabled on the context
    +        // Special handling for XML settings as these attributes will always be
    +        // available if they have been set on the context
             if (context.getTldValidation()) {
                 names.add(Globals.JASPER_XML_VALIDATION_TLD_INIT_PARAM);
             }
    +        if (context.getXmlBlockExternal() || Globals.IS_SECURITY_ENABLED) {
    +            names.add(Globals.JASPER_XML_BLOCK_EXTERNAL_INIT_PARAM);
    +        }
             return Collections.enumeration(names);
         }
     
    
  • java/org/apache/catalina/core/StandardContext.java+19 0 modified
    @@ -699,6 +699,13 @@ public StandardContext() {
          */
         private boolean webXmlNamespaceAware = Globals.STRICT_SERVLET_COMPLIANCE;
     
    +
    +    /**
    +     * Attribute used to turn on/off the use of external entities.
    +     */
    +    private boolean xmlBlockExternal = Globals.IS_SECURITY_ENABLED;
    +
    +
         /**
          * Attribute value used to turn on/off XML validation
          */
    @@ -6386,6 +6393,18 @@ public boolean getXmlValidation() {
         }
     
     
    +    @Override
    +    public void setXmlBlockExternal(boolean xmlBlockExternal) {
    +        this.xmlBlockExternal = xmlBlockExternal;
    +    }
    +
    +
    +    @Override
    +    public boolean getXmlBlockExternal() {
    +        return xmlBlockExternal;
    +    }
    +
    +
         @Override
         public void setTldValidation(boolean tldValidation) {
             this.tldValidation = tldValidation;
    
  • java/org/apache/catalina/Globals.java+11 0 modified
    @@ -279,4 +279,15 @@ public final class Globals {
          */
         public static final String JASPER_XML_VALIDATION_TLD_INIT_PARAM =
                 "org.apache.jasper.XML_VALIDATE_TLD";
    +
    +
    +    /**
    +     * Name of the ServletContext init-param that determines if the JSP engine
    +     * will block external entities from being used in *.tld, *.jspx, *.tagx and
    +     * tagplugin.xml files.
    +     * <p>
    +     * This must be kept in sync with org.apache.jasper.Constants
    +     */
    +    public static final String JASPER_XML_BLOCK_EXTERNAL_INIT_PARAM =
    +            "org.apache.jasper.XML_BLOCK_EXTERNAL";
     }
    
  • java/org/apache/catalina/startup/ContextConfig.java+1 1 modified
    @@ -730,7 +730,7 @@ protected void init() {
             contextConfig(contextDigester);
     
             webXmlParser = new WebXmlParser(context.getXmlNamespaceAware(),
    -                context.getXmlValidation());
    +                context.getXmlValidation(), context.getXmlBlockExternal());
         }
     
     
    
  • java/org/apache/catalina/startup/FailedContext.java+5 0 modified
    @@ -455,6 +455,11 @@ public void setXmlNamespaceAware(boolean xmlNamespaceAware) { /* NO-OP */ }
         @Override
         public void setXmlValidation(boolean xmlValidation) { /* NO-OP */ }
     
    +    @Override
    +    public boolean getXmlBlockExternal() { return true; }
    +    @Override
    +    public void setXmlBlockExternal(boolean xmlBlockExternal) { /* NO-OP */ }
    +
         @Override
         public boolean getTldValidation() { return false; }
         @Override
    
  • java/org/apache/jasper/compiler/ImplicitTagLibraryInfo.java+13 2 modified
    @@ -24,6 +24,7 @@
     import java.util.Set;
     import java.util.Vector;
     
    +import javax.servlet.ServletContext;
     import javax.servlet.jsp.tagext.FunctionInfo;
     import javax.servlet.jsp.tagext.TagFileInfo;
     import javax.servlet.jsp.tagext.TagInfo;
    @@ -119,10 +120,20 @@ public ImplicitTagLibraryInfo(JspCompilationContext ctxt,
                         try {
                             URL url = ctxt.getResource(path);
                             TldResourcePath resourcePath = new TldResourcePath(url, path);
    +                        ServletContext servletContext = ctxt.getServletContext();
                             boolean validate = Boolean.parseBoolean(
    -                                ctxt.getServletContext().getInitParameter(
    +                                servletContext.getInitParameter(
                                             Constants.XML_VALIDATION_TLD_INIT_PARAM));
    -                        TldParser parser = new TldParser(true, validate, new ImplicitTldRuleSet());
    +                        String blockExternalString = servletContext.getInitParameter(
    +                                Constants.XML_BLOCK_EXTERNAL_INIT_PARAM);
    +                        boolean blockExternal;
    +                        if (blockExternalString == null) {
    +                            blockExternal = Constants.IS_SECURITY_ENABLED;
    +                        } else {
    +                            blockExternal = Boolean.parseBoolean(blockExternalString);
    +                        }
    +                        TldParser parser = new TldParser(true, validate,
    +                                new ImplicitTldRuleSet(), blockExternal);
                             taglibXml = parser.parse(resourcePath);
                         } catch (IOException | SAXException e) {
                             err.jspError(e);
    
  • java/org/apache/jasper/compiler/JspDocumentParser.java+37 5 modified
    @@ -28,8 +28,11 @@
     import javax.xml.parsers.SAXParser;
     import javax.xml.parsers.SAXParserFactory;
     
    +import org.apache.jasper.Constants;
     import org.apache.jasper.JasperException;
     import org.apache.jasper.JspCompilationContext;
    +import org.apache.tomcat.util.descriptor.DigesterFactory;
    +import org.apache.tomcat.util.descriptor.LocalResolver;
     import org.apache.tomcat.util.descriptor.tld.TldResourcePath;
     import org.apache.tomcat.util.scan.Jar;
     import org.xml.sax.Attributes;
    @@ -39,6 +42,7 @@
     import org.xml.sax.SAXParseException;
     import org.xml.sax.XMLReader;
     import org.xml.sax.ext.DefaultHandler2;
    +import org.xml.sax.ext.EntityResolver2;
     import org.xml.sax.helpers.AttributesImpl;
     
     /**
    @@ -91,6 +95,7 @@ class JspDocumentParser
         private boolean inDTD;
     
         private boolean isValidating;
    +    private final EntityResolver2 entityResolver;
     
         private final ErrorDispatcher err;
         private final boolean isTagFile;
    @@ -119,6 +124,20 @@ public JspDocumentParser(
             this.isTagFile = isTagFile;
             this.directivesOnly = directivesOnly;
             this.isTop = true;
    +
    +        String blockExternalString = ctxt.getServletContext().getInitParameter(
    +                Constants.XML_BLOCK_EXTERNAL_INIT_PARAM);
    +        boolean blockExternal;
    +        if (blockExternalString == null) {
    +            blockExternal = Constants.IS_SECURITY_ENABLED;
    +        } else {
    +            blockExternal = Boolean.parseBoolean(blockExternalString);
    +        }
    +
    +        this.entityResolver = new LocalResolver(
    +                DigesterFactory.SERVLET_API_PUBLIC_IDS,
    +                DigesterFactory.SERVLET_API_SYSTEM_IDS,
    +                blockExternal);
         }
     
         /*
    @@ -232,13 +251,26 @@ private void addInclude(Node parent, Collection<String> files) throws SAXExcepti
             }
         }
     
    +
    +    @Override
    +    public InputSource getExternalSubset(String name, String baseURI)
    +            throws SAXException, IOException {
    +        return entityResolver.getExternalSubset(name, baseURI);
    +    }
    +
         @Override
    -    public InputSource resolveEntity(String name, String publicId, String baseURI, String systemId)
    +    public InputSource resolveEntity(String publicId, String systemId)
                 throws SAXException, IOException {
    -        // TODO URLs returned by the Jar abstraction may be of the form jar:jar: which
    -        // is not a URL that can be resolved by the JRE. This should use the JarFactory
    -        // to construct and return a valid InputSource.
    -        return null;
    +        return entityResolver.resolveEntity(publicId, systemId);
    +    }
    +
    +    @Override
    +    public InputSource resolveEntity(String name, String publicId,
    +            String baseURI, String systemId) throws SAXException, IOException {
    +        // TODO URLs returned by the Jar abstraction may be of the form jar:jar:
    +        //      which is not a URL that can be resolved by the JRE. This should
    +        //      use the JarFactory to construct and return a valid InputSource.
    +        return entityResolver.resolveEntity(name, publicId, baseURI, systemId);
         }
     
         /*
    
  • java/org/apache/jasper/compiler/TagPluginManager.java+11 1 modified
    @@ -24,6 +24,7 @@
     
     import javax.servlet.ServletContext;
     
    +import org.apache.jasper.Constants;
     import org.apache.jasper.JasperException;
     import org.apache.jasper.compiler.tagplugin.TagPlugin;
     import org.apache.jasper.compiler.tagplugin.TagPluginContext;
    @@ -61,7 +62,16 @@ private void init(ErrorDispatcher err) throws JasperException {
             if (initialized)
                 return;
     
    -        TagPluginParser parser = new TagPluginParser(ctxt);
    +        String blockExternalString = ctxt.getInitParameter(
    +                Constants.XML_BLOCK_EXTERNAL_INIT_PARAM);
    +        boolean blockExternal;
    +        if (blockExternalString == null) {
    +            blockExternal = Constants.IS_SECURITY_ENABLED;
    +        } else {
    +            blockExternal = Boolean.parseBoolean(blockExternalString);
    +        }
    +
    +        TagPluginParser parser = new TagPluginParser(ctxt, blockExternal);
     
             try {
                 Enumeration<URL> urls =
    
  • java/org/apache/jasper/compiler/TldCache.java+9 1 modified
    @@ -74,7 +74,15 @@ public TldCache(ServletContext servletContext,
             }
             boolean validate = Boolean.parseBoolean(
                     servletContext.getInitParameter(Constants.XML_VALIDATION_TLD_INIT_PARAM));
    -        tldParser = new TldParser(true, validate);
    +        String blockExternalString = servletContext.getInitParameter(
    +                Constants.XML_BLOCK_EXTERNAL_INIT_PARAM);
    +        boolean blockExternal;
    +        if (blockExternalString == null) {
    +            blockExternal = Constants.IS_SECURITY_ENABLED;
    +        } else {
    +            blockExternal = Boolean.parseBoolean(blockExternalString);
    +        }
    +        tldParser = new TldParser(true, validate, blockExternal);
         }
     
     
    
  • java/org/apache/jasper/Constants.java+9 0 modified
    @@ -160,4 +160,13 @@ public class Constants {
          */
         public static final String XML_VALIDATION_TLD_INIT_PARAM =
                 "org.apache.jasper.XML_VALIDATE_TLD";
    +
    +    /**
    +     * Name of the ServletContext init-param that determines if the XML parsers
    +     * will block the resolution of external entities.
    +     * <p>
    +     * This must be kept in sync with org.apache.catalina.Globals
    +     */
    +    public static final String XML_BLOCK_EXTERNAL_INIT_PARAM =
    +            "org.apache.jasper.XML_BLOCK_EXTERNAL";
     }
    
  • java/org/apache/jasper/JspC.java+17 1 modified
    @@ -134,6 +134,7 @@ public class JspC extends Task implements Options {
         protected static final String SWITCH_SMAP = "-smap";
         protected static final String SWITCH_DUMP_SMAP = "-dumpsmap";
         protected static final String SWITCH_VALIDATE_TLD = "-validateTld";
    +    protected static final String SWITCH_BLOCK_EXTERNAL = "-blockExternal";
         protected static final String SHOW_SUCCESS ="-s";
         protected static final String LIST_ERRORS = "-l";
         protected static final int INC_WEBXML = 10;
    @@ -165,6 +166,7 @@ public class JspC extends Task implements Options {
         protected boolean trimSpaces = false;
         protected boolean genStringAsCharArray = false;
         protected boolean validateTld;
    +    protected boolean blockExternal;
         protected boolean xpoweredBy;
         protected boolean mappedFile = false;
         protected boolean poolingEnabled = true;
    @@ -373,6 +375,8 @@ public void setArgs(String[] arg) throws JasperException {
                     smapDumped = true;
                 } else if (tok.equals(SWITCH_VALIDATE_TLD)) {
                     setValidateTld(true);
    +            } else if (tok.equals(SWITCH_BLOCK_EXTERNAL)) {
    +                setBlockExternal(true);
                 } else {
                     if (tok.startsWith("-")) {
                         throw new JasperException("Unrecognized option: " + tok +
    @@ -860,6 +864,14 @@ public boolean isValidateTld() {
             return validateTld;
         }
     
    +    public void setBlockExternal( boolean b ) {
    +        this.blockExternal = b;
    +    }
    +
    +    public boolean isBlockExternal() {
    +        return blockExternal;
    +    }
    +
         public void setListErrors( boolean b ) {
             listErrors = b;
         }
    @@ -1440,8 +1452,12 @@ protected void initServletContext(ClassLoader classLoader)
             if (isValidateTld()) {
                 context.setInitParameter(Constants.XML_VALIDATION_TLD_INIT_PARAM, "true");
             }
    +        if (isBlockExternal()) {
    +            context.setInitParameter(Constants.XML_BLOCK_EXTERNAL_INIT_PARAM, "true");
    +        }
     
    -        TldScanner scanner = new TldScanner(context, true, isValidateTld());
    +        TldScanner scanner = new TldScanner(
    +                context, true, isValidateTld(), isBlockExternal());
             scanner.setClassLoader(classLoader);
     
             try {
    
  • java/org/apache/jasper/servlet/JasperInitializer.java+9 1 modified
    @@ -80,9 +80,17 @@ public void onStartup(Set<Class<?>> types, ServletContext context) throws Servle
     
             boolean validate = Boolean.parseBoolean(
                     context.getInitParameter(Constants.XML_VALIDATION_TLD_INIT_PARAM));
    +        String blockExternalString = context.getInitParameter(
    +                Constants.XML_BLOCK_EXTERNAL_INIT_PARAM);
    +        boolean blockExternal;
    +        if (blockExternalString == null) {
    +            blockExternal = Constants.IS_SECURITY_ENABLED;
    +        } else {
    +            blockExternal = Boolean.parseBoolean(blockExternalString);
    +        }
     
             // scan the application for TLDs
    -        TldScanner scanner = new TldScanner(context, true, validate);
    +        TldScanner scanner = new TldScanner(context, true, validate, blockExternal);
             try {
                 scanner.scan();
             } catch (IOException | SAXException e) {
    
  • java/org/apache/jasper/servlet/JspCServletContext.java+12 4 modified
    @@ -44,11 +44,11 @@
     import javax.servlet.SessionTrackingMode;
     import javax.servlet.descriptor.JspConfigDescriptor;
     
    +import org.apache.jasper.Constants;
     import org.apache.jasper.JasperException;
     import org.apache.jasper.compiler.Localizer;
     import org.apache.jasper.util.ExceptionUtils;
     import org.apache.tomcat.JarScanType;
    -import org.apache.tomcat.util.descriptor.web.Constants;
     import org.apache.tomcat.util.descriptor.web.FragmentJarScannerCallback;
     import org.apache.tomcat.util.descriptor.web.WebXml;
     import org.apache.tomcat.util.descriptor.web.WebXmlParser;
    @@ -124,13 +124,21 @@ public JspCServletContext(PrintWriter aLogWriter, URL aResourceBaseURL, ClassLoa
     
         private WebXml buildMergedWebXml() throws JasperException {
             WebXml webXml = new WebXml();
    -
    -        WebXmlParser webXmlParser = new WebXmlParser(false, false);
    +        String blockExternalString = getInitParameter(
    +                Constants.XML_BLOCK_EXTERNAL_INIT_PARAM);
    +        boolean blockExternal;
    +        if (blockExternalString == null) {
    +            blockExternal = Constants.IS_SECURITY_ENABLED;
    +        } else {
    +            blockExternal = Boolean.parseBoolean(blockExternalString);
    +        }
    +        WebXmlParser webXmlParser = new WebXmlParser(false, false, blockExternal);
             // Use this class's classloader as Ant will have set the TCCL to its own
             webXmlParser.setClassLoader(getClass().getClassLoader());
     
             try {
    -            URL url = getResource(Constants.WEB_XML_LOCATION);
    +            URL url = getResource(
    +                    org.apache.tomcat.util.descriptor.web.Constants.WEB_XML_LOCATION);
                 if (!webXmlParser.parseWebXml(url, webXml, false)) {
                     throw new JasperException(Localizer.getMessage("jspc.error.invalidWebXml"));
                 }
    
  • java/org/apache/jasper/servlet/TldScanner.java+4 2 modified
    @@ -72,9 +72,11 @@ public class TldScanner {
          */
         public TldScanner(ServletContext context,
                           boolean namespaceAware,
    -                      boolean validation) {
    +                      boolean validation,
    +                      boolean blockExternal) {
             this.context = context;
    -        this.tldParser = new TldParser(namespaceAware, validation);
    +
    +        this.tldParser = new TldParser(namespaceAware, validation, blockExternal);
         }
     
         /**
    
  • java/org/apache/tomcat/util/descriptor/DigesterFactory.java+18 5 modified
    @@ -16,13 +16,15 @@
      */
     package org.apache.tomcat.util.descriptor;
     
    +import java.util.Collections;
     import java.util.HashMap;
     import java.util.Map;
     
     import javax.servlet.ServletContext;
     
     import org.apache.tomcat.util.digester.Digester;
     import org.apache.tomcat.util.digester.RuleSet;
    +import org.xml.sax.ext.EntityResolver2;
     
     /**
      * Wrapper class around the Digester that hide Digester's initialization
    @@ -31,10 +33,16 @@
     public class DigesterFactory {
     
         /**
    -     * A resolver for the resources packaged in servlet-api.jar
    +     * Mapping of well-known public IDs used by the Servlet API to the matching
    +     * local resource.
          */
    -    public static final LocalResolver SERVLET_API_RESOLVER;
    +    public static final Map<String,String> SERVLET_API_PUBLIC_IDS;
     
    +    /**
    +     * Mapping of well-known system IDs used by the Servlet API to the matching
    +     * local resource.
    +     */
    +    public static final Map<String,String> SERVLET_API_SYSTEM_IDS;
     
         static {
             Map<String, String> publicIds = new HashMap<>();
    @@ -89,7 +97,8 @@ public class DigesterFactory {
             addSelf(systemIds, "javaee_web_services_1_4.xsd");
             addSelf(systemIds, "javaee_web_services_client_1_4.xsd");
     
    -        SERVLET_API_RESOLVER = new LocalResolver(publicIds, systemIds);
    +        SERVLET_API_PUBLIC_IDS = Collections.unmodifiableMap(publicIds);
    +        SERVLET_API_SYSTEM_IDS = Collections.unmodifiableMap(systemIds);
         }
     
         private static void addSelf(Map<String, String> ids, String id) {
    @@ -107,15 +116,19 @@ private static String idFor(String url) {
          * @param xmlValidation turn on/off xml validation
          * @param xmlNamespaceAware turn on/off namespace validation
          * @param rule an instance of <code>RuleSet</code> used for parsing the xml.
    +     * @param blockExternal turn on/off the blocking of external resources
          */
         public static Digester newDigester(boolean xmlValidation,
                                            boolean xmlNamespaceAware,
    -                                       RuleSet rule) {
    +                                       RuleSet rule,
    +                                       boolean blockExternal) {
             Digester digester = new Digester();
             digester.setNamespaceAware(xmlNamespaceAware);
             digester.setValidating(xmlValidation);
             digester.setUseContextClassLoader(true);
    -        digester.setEntityResolver(SERVLET_API_RESOLVER);
    +        EntityResolver2 resolver = new LocalResolver(SERVLET_API_PUBLIC_IDS,
    +                SERVLET_API_SYSTEM_IDS, blockExternal);
    +        digester.setEntityResolver(resolver);
             if (rule != null) {
                 digester.addRuleSet(rule);
             }
    
  • java/org/apache/tomcat/util/descriptor/LocalResolver.java+71 47 modified
    @@ -16,13 +16,15 @@
      */
     package org.apache.tomcat.util.descriptor;
     
    +import java.io.FileNotFoundException;
     import java.io.IOException;
     import java.net.MalformedURLException;
     import java.net.URI;
     import java.net.URISyntaxException;
     import java.net.URL;
     import java.util.Map;
     
    +import org.apache.tomcat.util.res.StringManager;
     import org.xml.sax.InputSource;
     import org.xml.sax.SAXException;
     import org.xml.sax.ext.EntityResolver2;
    @@ -32,22 +34,30 @@
      */
     public class LocalResolver implements EntityResolver2 {
     
    +    private static final StringManager sm =
    +            StringManager.getManager(Constants.PACKAGE_NAME);
    +
         private final Map<String,String> publicIds;
         private final Map<String,String> systemIds;
    -
    +    private final boolean blockExternal;
     
         /**
          * Constructor providing mappings of public and system identifiers to local
          * resources. Each map contains a mapping from a well-known identifier to a
          * URL for a local resource path.
          *
    -     * @param publicIds mapping of public identifiers to local resources
    -     * @param systemIds mapping of system identifiers to local resources
    +     * @param publicIds mapping of well-known public identifiers to local
    +     *                  resources
    +     * @param systemIds mapping of well-known system identifiers to local
    +     *                  resources
    +     * @param blockExternal are external resources blocked that are not
    +     *                      well-known
          */
         public LocalResolver(Map<String,String> publicIds,
    -            Map<String,String> systemIds) {
    +            Map<String,String> systemIds, boolean blockExternal) {
             this.publicIds = publicIds;
             this.systemIds = systemIds;
    +        this.blockExternal = blockExternal;
         }
     
     
    @@ -60,63 +70,77 @@ public InputSource resolveEntity(String publicId, String systemId)
     
         @Override
         public InputSource resolveEntity(String name, String publicId,
    -            String baseURI, String systemId) throws SAXException, IOException {
    -
    -        String resolved = resolve(publicId, systemId, baseURI);
    -        if (resolved == null) {
    -            return null;
    -        }
    +            String base, String systemId) throws SAXException, IOException {
     
    -        InputSource is = new InputSource(resolved);
    -        is.setPublicId(publicId);
    -        return is;
    -    }
    -
    -
    -    @Override
    -    public InputSource getExternalSubset(String name, String baseURI)
    -            throws SAXException, IOException {
    -        return null;
    -    }
    -
    -
    -    private String resolve(String publicId, String systemId, String baseURI) {
    -        // try resolving using the publicId
    +        // First try resolving using the publicId
             String resolved = publicIds.get(publicId);
             if (resolved != null) {
    -            return resolved;
    +            InputSource is = new InputSource(resolved);
    +            is.setPublicId(publicId);
    +            return is;
             }
     
    -        // try resolving using the systemId
    +        // If there is no systemId, can't try anything else
             if (systemId == null) {
    -            return null;
    +            throw new FileNotFoundException(sm.getString("localResolver.unresolvedEntity",
    +                    name, publicId, systemId, base));
             }
     
    -        systemId = resolve(baseURI, systemId);
    +        // Try resolving with the supplied systemId
             resolved = systemIds.get(systemId);
             if (resolved != null) {
    -            return resolved;
    +            InputSource is = new InputSource(resolved);
    +            is.setPublicId(publicId);
    +            return is;
             }
     
    -        // fall back to the supplied systemId
    -        return systemId;
    -    }
    -
    -
    -    private static String resolve(String baseURI, String systemId) {
    +        // Resolve the supplied systemId against the base
    +        URI systemUri;
             try {
    -            if (baseURI == null) {
    -                return systemId;
    +            if (base == null) {
    +                systemUri = new URI(systemId);
    +            } else {
    +                // Can't use URI.resolve() because "jar:..." URLs are not valid
    +                // hierarchical URIs so resolve() does not work. new URL()
    +                // delegates to the jar: stream handler and it manages to figure
    +                // it out.
    +                URI baseUri = new URI(base);
    +                systemUri = new URL(baseUri.toURL(), systemId).toURI();
                 }
    -            URI systemUri = new URI(systemId);
    -            if (systemUri.isAbsolute()) {
    -                return systemId;
    -            }
    -            return new URL(new URL(baseURI), systemId).toString();
    +            systemUri = systemUri.normalize();
             } catch (URISyntaxException e) {
    -            return systemId;
    -        } catch (MalformedURLException e) {
    -            return systemId;
    +            // May be caused by a | being used instead of a : in an absolute
    +            // file URI on Windows.
    +            if (blockExternal) {
    +                // Absolute paths aren't allowed so block it
    +                throw new MalformedURLException(e.getMessage());
    +            } else {
    +                // See if the URLHandler can resolve it
    +                return new InputSource(systemId);
    +            }
    +        }
    +        if (systemUri.isAbsolute()) {
    +            // Try the resolved systemId
    +            resolved = systemIds.get(systemUri.toString());
    +            if (resolved != null) {
    +                InputSource is = new InputSource(resolved);
    +                is.setPublicId(publicId);
    +                return is;
    +            }
    +            if (!blockExternal) {
    +                InputSource is = new InputSource(systemUri.toString());
    +                is.setPublicId(publicId);
    +                return is;
    +            }
             }
    +        throw new FileNotFoundException(sm.getString("localResolver.unresolvedEntity",
    +                name, publicId, systemId, base));
    +    }
    +
    +
    +    @Override
    +    public InputSource getExternalSubset(String name, String baseURI)
    +            throws SAXException, IOException {
    +        return null;
         }
    -}
    \ No newline at end of file
    +}
    
  • java/org/apache/tomcat/util/descriptor/LocalStrings.properties+2 0 modified
    @@ -13,5 +13,7 @@
     # See the License for the specific language governing permissions and
     # limitations under the License.
     
    +localResolver.unresolvedEntity=Could not resolve XML resource [{0}] with public ID [{1}], system ID [{2}] and base URI [{3}] to a known, local entity.
    +
     xmlErrorHandler.error=Non-fatal error [{0}] reported processing [{1}].
     xmlErrorHandler.warning=Warning [{0}] reported processing [{1}].
    
  • java/org/apache/tomcat/util/descriptor/tagplugin/TagPluginParser.java+3 2 modified
    @@ -42,8 +42,9 @@ public class TagPluginParser {
         private final Digester digester;
         private final Map<String, String> plugins = new HashMap<>();
     
    -    public TagPluginParser(ServletContext context) {
    -        digester = DigesterFactory.newDigester(false, false, new TagPluginRuleSet());
    +    public TagPluginParser(ServletContext context, boolean blockExternal) {
    +        digester = DigesterFactory.newDigester(
    +                false, false, new TagPluginRuleSet(), blockExternal);
             digester.setClassLoader(context.getClassLoader());
         }
     
    
  • java/org/apache/tomcat/util/descriptor/tld/TldParser.java+7 4 modified
    @@ -35,12 +35,15 @@ public class TldParser {
         private static final Log log = LogFactory.getLog(TldParser.class);
         private final Digester digester;
     
    -    public TldParser(boolean namespaceAware, boolean validation) {
    -        this(namespaceAware, validation, new TldRuleSet());
    +    public TldParser(boolean namespaceAware, boolean validation,
    +            boolean blockExternal) {
    +        this(namespaceAware, validation, new TldRuleSet(), blockExternal);
         }
     
    -    public TldParser(boolean namespaceAware, boolean validation, RuleSet ruleSet) {
    -        digester = DigesterFactory.newDigester(validation, namespaceAware, ruleSet);
    +    public TldParser(boolean namespaceAware, boolean validation, RuleSet ruleSet,
    +            boolean blockExternal) {
    +        digester = DigesterFactory.newDigester(
    +                validation, namespaceAware, ruleSet, blockExternal);
         }
     
         public TaglibXml parse(TldResourcePath path) throws IOException, SAXException {
    
  • java/org/apache/tomcat/util/descriptor/web/WebXmlParser.java+4 3 modified
    @@ -55,15 +55,16 @@ public class WebXmlParser {
         private final WebRuleSet webFragmentRuleSet;
     
     
    -    public WebXmlParser(boolean namespaceAware, boolean validation) {
    +    public WebXmlParser(boolean namespaceAware, boolean validation,
    +            boolean blockExternal) {
             webRuleSet = new WebRuleSet(false);
             webDigester = DigesterFactory.newDigester(validation,
    -                namespaceAware, webRuleSet);
    +                namespaceAware, webRuleSet, blockExternal);
             webDigester.getParser();
     
             webFragmentRuleSet = new WebRuleSet(true);
             webFragmentDigester = DigesterFactory.newDigester(validation,
    -                namespaceAware, webFragmentRuleSet);
    +                namespaceAware, webFragmentRuleSet, blockExternal);
             webFragmentDigester.getParser();
         }
     
    
  • test/javax/servlet/resources/TestSchemaValidation.java+14 14 modified
    @@ -31,8 +31,8 @@ public class TestSchemaValidation {
     
         @Test
         public void testWebapp() throws Exception {
    -        Digester digester =
    -                DigesterFactory.newDigester(true, true, new WebRuleSet(false));
    +        Digester digester = DigesterFactory.newDigester(
    +                true, true, new WebRuleSet(false), true);
             digester.push(new WebXml());
             WebXml desc = (WebXml) digester.parse(
                     new File("test/webapp/WEB-INF/web.xml"));
    @@ -41,8 +41,8 @@ public void testWebapp() throws Exception {
     
         @Test
         public void testWebapp_2_2() throws Exception {
    -        Digester digester =
    -                DigesterFactory.newDigester(true, true, new WebRuleSet(false));
    +        Digester digester = DigesterFactory.newDigester(
    +                true, true, new WebRuleSet(false), true);
             digester.push(new WebXml());
             WebXml desc = (WebXml) digester.parse(
                     new File("test/webapp-2.2/WEB-INF/web.xml"));
    @@ -52,8 +52,8 @@ public void testWebapp_2_2() throws Exception {
     
         @Test
         public void testWebapp_2_3() throws Exception {
    -        Digester digester =
    -                DigesterFactory.newDigester(true, true, new WebRuleSet(false));
    +        Digester digester = DigesterFactory.newDigester(
    +                true, true, new WebRuleSet(false), true);
             digester.push(new WebXml());
             WebXml desc = (WebXml) digester.parse(
                     new File("test/webapp-2.3/WEB-INF/web.xml"));
    @@ -63,8 +63,8 @@ public void testWebapp_2_3() throws Exception {
     
         @Test
         public void testWebapp_2_4() throws Exception {
    -        Digester digester =
    -                DigesterFactory.newDigester(true, true, new WebRuleSet(false));
    +        Digester digester = DigesterFactory.newDigester(
    +                true, true, new WebRuleSet(false), true);
             digester.push(new WebXml());
             WebXml desc = (WebXml) digester.parse(
                     new File("test/webapp-2.4/WEB-INF/web.xml"));
    @@ -73,8 +73,8 @@ public void testWebapp_2_4() throws Exception {
     
         @Test
         public void testWebapp_2_5() throws Exception {
    -        Digester digester =
    -                DigesterFactory.newDigester(true, true, new WebRuleSet(false));
    +        Digester digester = DigesterFactory.newDigester(
    +                true, true, new WebRuleSet(false), true);
             digester.push(new WebXml());
             WebXml desc = (WebXml) digester.parse(
                     new File("test/webapp-2.5/WEB-INF/web.xml"));
    @@ -83,8 +83,8 @@ public void testWebapp_2_5() throws Exception {
     
         @Test
         public void testWebapp_3_0() throws Exception {
    -        Digester digester =
    -                DigesterFactory.newDigester(true, true, new WebRuleSet(false));
    +        Digester digester = DigesterFactory.newDigester(
    +                true, true, new WebRuleSet(false), true);
             digester.push(new WebXml());
             WebXml desc = (WebXml) digester.parse(
                     new File("test/webapp-3.0/WEB-INF/web.xml"));
    @@ -93,8 +93,8 @@ public void testWebapp_3_0() throws Exception {
     
         @Test
         public void testWebapp_3_1() throws Exception {
    -        Digester digester =
    -                DigesterFactory.newDigester(true, true, new WebRuleSet(false));
    +        Digester digester = DigesterFactory.newDigester(
    +                true, true, new WebRuleSet(false), true);
             digester.push(new WebXml());
             WebXml desc = (WebXml) digester.parse(
                     new File("test/webapp-3.1/WEB-INF/web.xml"));
    
  • test/org/apache/catalina/core/TesterContext.java+10 0 modified
    @@ -642,6 +642,16 @@ public void setXmlValidation(boolean xmlValidation) {
             // NO-OP
         }
     
    +    @Override
    +    public boolean getXmlBlockExternal() {
    +        return false;
    +    }
    +
    +    @Override
    +    public void setXmlBlockExternal(boolean xmlBlockExternal) {
    +        // NO-OP
    +    }
    +
         @Override
         public boolean getTldValidation(){
             return false;
    
  • test/org/apache/jasper/servlet/TestTldScanner.java+2 1 modified
    @@ -39,7 +39,8 @@ public void testWithWebapp() throws Exception {
             Context context = tomcat.addWebapp(null, "/test", appDir.getAbsolutePath());
             tomcat.start();
     
    -        TldScanner scanner = new TldScanner(context.getServletContext(), true, true);
    +        TldScanner scanner =
    +                new TldScanner(context.getServletContext(), true, true, true);
             scanner.scan();
             Assert.assertEquals(5, scanner.getUriTldResourcePathMap().size());
             Assert.assertEquals(1, scanner.getListeners().size());
    
  • test/org/apache/tomcat/util/descriptor/TestLocalResolver.java+11 10 modified
    @@ -16,6 +16,7 @@
      */
     package org.apache.tomcat.util.descriptor;
     
    +import java.io.FileNotFoundException;
     import java.io.IOException;
     import java.util.HashMap;
     import java.util.Map;
    @@ -34,7 +35,7 @@ public class TestLocalResolver {
         private final Map<String, String> publicIds = new HashMap<>();
         private final Map<String, String> systemIds = new HashMap<>();
     
    -    private LocalResolver resolver = new LocalResolver(publicIds, systemIds);
    +    private LocalResolver resolver = new LocalResolver(publicIds, systemIds, true);
         private String WEB_22_LOCAL;
         private String WEB_31_LOCAL;
         private String WEBCOMMON_31_LOCAL;
    @@ -53,25 +54,25 @@ public String urlFor(String id) {
             return ServletContext.class.getResource(id).toExternalForm();
         }
     
    -    @Test
    -    public void unknownNullIdIsNull() throws IOException, SAXException {
    +    @Test(expected = FileNotFoundException.class)
    +    public void unknownNullId() throws IOException, SAXException {
             Assert.assertNull(resolver.resolveEntity(null, null));
         }
     
    -    @Test
    -    public void unknownPublicIdIsNull() throws IOException, SAXException {
    +    @Test(expected = FileNotFoundException.class)
    +    public void unknownPublicId() throws IOException, SAXException {
             Assert.assertNull(resolver.resolveEntity("unknown", null));
         }
     
    -    @Test
    -    public void unknownSystemIdIsReturned() throws IOException, SAXException {
    +    @Test(expected = FileNotFoundException.class)
    +    public void unknownSystemId() throws IOException, SAXException {
             InputSource source = resolver.resolveEntity(null, "unknown");
             Assert.assertEquals(null, source.getPublicId());
             Assert.assertEquals("unknown", source.getSystemId());
         }
     
    -    @Test
    -    public void unknownSystemIdIsResolvedAgainstBaseURI()
    +    @Test(expected = FileNotFoundException.class)
    +    public void unknownRelativeSystemId()
                 throws IOException, SAXException {
             InputSource source = resolver.resolveEntity(
                     null, null, "http://example.com/home.html", "unknown");
    @@ -121,4 +122,4 @@ public void absoluteSystemIdOverridesBaseURI()
             Assert.assertEquals(null, source.getPublicId());
             Assert.assertEquals(WEB_31_LOCAL, source.getSystemId());
         }
    -}
    \ No newline at end of file
    +}
    
  • test/org/apache/tomcat/util/descriptor/tld/TestTldParser.java+1 1 modified
    @@ -36,7 +36,7 @@ public class TestTldParser {
     
         @Before
         public void init() {
    -        parser = new TldParser(true, true);
    +        parser = new TldParser(true, true, null, true);
         }
     
         @Test
    
  • webapps/docs/config/context.xml+10 0 modified
    @@ -524,6 +524,16 @@
             Context.  If not specified, a standard default value will be used.</p>
           </attribute>
     
    +      <attribute name="xmlBlockExternal" required="false">
    +        <p>If the value of this flag is <code>true</code>, the parsing of
    +        <code>web.xml</code>, <code>web-fragment.xml</code>, <code>*.tld</code>,
    +        <code>*.jspx</code>, <code>*.tagx</code> and <code>tagPlugins.xml</code>
    +        files for this web application will not permit external entities to be
    +        loaded. If a <code>SecurityManager</code> is configured then the default
    +        value of this attribute will be <code>true</code>, else the default
    +        value will be <code>false</code>.</p>
    +      </attribute>
    +
           <attribute name="xmlNamespaceAware" required="false">
             <p>If the value of this flag is <code>true</code>, the parsing of
             <code>web.xml</code> and <code>web-fragment.xml</code> files for this
    
  • webapps/docs/security-howto.xml+3 0 modified
    @@ -179,6 +179,9 @@
         <ul>
           <li>The default value for the <strong>deployXML</strong> attribute of the
           <strong>Host</strong> element is changed to <code>false</code>.</li>
    +      <li>The default value for the <strong>xmlBlockExternal</strong> attribute
    +      of the <strong>Context</strong> element is changed to <code>true</code>.
    +      </li>
         </ul>
       </section>
     
    
78dd7e6f3d84

Add an option to the Context to control the blocking of XML external entities when parsing XML configuration files and enable this blocking by default. The block is implemented via a custom resolver to enable the logging of any blocked entities.

https://github.com/apache/tomcatMark ThomasDec 9, 2013via ghsa
29 files changed · +351 109
  • java/org/apache/catalina/ant/ValidatorTask.java+5 1 modified
    @@ -24,6 +24,7 @@
     import java.io.FileInputStream;
     import java.io.InputStream;
     
    +import org.apache.catalina.Globals;
     import org.apache.catalina.startup.Constants;
     import org.apache.tomcat.util.descriptor.DigesterFactory;
     import org.apache.tomcat.util.digester.Digester;
    @@ -90,7 +91,10 @@ public void execute() throws BuildException {
             Thread.currentThread().setContextClassLoader
                 (ValidatorTask.class.getClassLoader());
     
    -        Digester digester = DigesterFactory.newDigester(true, true, null);
    +        // Called through trusted manager interface. If running under a
    +        // SecurityManager assume that untrusted applications may be deployed.
    +        Digester digester = DigesterFactory.newDigester(
    +                true, true, null, Globals.IS_SECURITY_ENABLED);
             try {
                 file = file.getCanonicalFile();
                 InputStream stream =
    
  • java/org/apache/catalina/Context.java+19 0 modified
    @@ -616,6 +616,25 @@ public void setSessionCookiePathUsesTrailingSlash(
         public void setXmlValidation(boolean xmlValidation);
     
     
    +    /**
    +     * Will the parsing of web.xml, web-fragment.xml, *.tld, *.jspx, *.tagx and
    +     * tagplugin.xml files for this Context block the use of external entities?
    +     *
    +     * @return true if access to external entities is blocked
    +     */
    +    public boolean getXmlBlockExternal();
    +
    +
    +    /**
    +     * Controls whether the parsing of web.xml, web-fragment.xml, *.tld, *.jspx,
    +     * *.tagx and tagplugin.xml files for this Context will block the use of
    +     * external entities.
    +     *
    +     * @param xmlBlockExternal true to block external entities
    +     */
    +    public void setXmlBlockExternal(boolean xmlBlockExternal);
    +
    +
         /**
          * Will the parsing of *.tld files for this Context be performed by a
          * validating parser?
    
  • java/org/apache/catalina/core/ApplicationContext.java+14 3 modified
    @@ -304,12 +304,20 @@ public String getContextPath() {
          */
         @Override
         public String getInitParameter(final String name) {
    -        // Special handling for XML validation as the context setting must
    +        // Special handling for XML settings as the context setting must
             // always override anything that might have been set by an application.
             if (Globals.JASPER_XML_VALIDATION_TLD_INIT_PARAM.equals(name) &&
                     context.getTldValidation()) {
                 return "true";
             }
    +        if (Globals.JASPER_XML_BLOCK_EXTERNAL_INIT_PARAM.equals(name)) {
    +            if (context.getXmlBlockExternal()) {
    +                return "true";
    +            } else if (Globals.IS_SECURITY_ENABLED) {
    +                // System admin has explicitly changed the default
    +                return "false";
    +            }
    +        }
             return parameters.get(name);
         }
     
    @@ -322,11 +330,14 @@ public String getInitParameter(final String name) {
         public Enumeration<String> getInitParameterNames() {
             Set<String> names = new HashSet<>();
             names.addAll(parameters.keySet());
    -        // Special handling for XML validation as this attribute will always be
    -        // available if validation has been enabled on the context
    +        // Special handling for XML settings as these attributes will always be
    +        // available if they have been set on the context
             if (context.getTldValidation()) {
                 names.add(Globals.JASPER_XML_VALIDATION_TLD_INIT_PARAM);
             }
    +        if (context.getXmlBlockExternal() || Globals.IS_SECURITY_ENABLED) {
    +            names.add(Globals.JASPER_XML_BLOCK_EXTERNAL_INIT_PARAM);
    +        }
             return Collections.enumeration(names);
         }
     
    
  • java/org/apache/catalina/core/StandardContext.java+19 0 modified
    @@ -699,6 +699,13 @@ public StandardContext() {
          */
         private boolean webXmlNamespaceAware = Globals.STRICT_SERVLET_COMPLIANCE;
     
    +
    +    /**
    +     * Attribute used to turn on/off the use of external entities.
    +     */
    +    private boolean xmlBlockExternal = Globals.IS_SECURITY_ENABLED;
    +
    +
         /**
          * Attribute value used to turn on/off XML validation
          */
    @@ -6386,6 +6393,18 @@ public boolean getXmlValidation() {
         }
     
     
    +    @Override
    +    public void setXmlBlockExternal(boolean xmlBlockExternal) {
    +        this.xmlBlockExternal = xmlBlockExternal;
    +    }
    +
    +
    +    @Override
    +    public boolean getXmlBlockExternal() {
    +        return xmlBlockExternal;
    +    }
    +
    +
         @Override
         public void setTldValidation(boolean tldValidation) {
             this.tldValidation = tldValidation;
    
  • java/org/apache/catalina/Globals.java+11 0 modified
    @@ -279,4 +279,15 @@ public final class Globals {
          */
         public static final String JASPER_XML_VALIDATION_TLD_INIT_PARAM =
                 "org.apache.jasper.XML_VALIDATE_TLD";
    +
    +
    +    /**
    +     * Name of the ServletContext init-param that determines if the JSP engine
    +     * will block external entities from being used in *.tld, *.jspx, *.tagx and
    +     * tagplugin.xml files.
    +     * <p>
    +     * This must be kept in sync with org.apache.jasper.Constants
    +     */
    +    public static final String JASPER_XML_BLOCK_EXTERNAL_INIT_PARAM =
    +            "org.apache.jasper.XML_BLOCK_EXTERNAL";
     }
    
  • java/org/apache/catalina/startup/ContextConfig.java+1 1 modified
    @@ -730,7 +730,7 @@ protected void init() {
             contextConfig(contextDigester);
     
             webXmlParser = new WebXmlParser(context.getXmlNamespaceAware(),
    -                context.getXmlValidation());
    +                context.getXmlValidation(), context.getXmlBlockExternal());
         }
     
     
    
  • java/org/apache/catalina/startup/FailedContext.java+5 0 modified
    @@ -455,6 +455,11 @@ public void setXmlNamespaceAware(boolean xmlNamespaceAware) { /* NO-OP */ }
         @Override
         public void setXmlValidation(boolean xmlValidation) { /* NO-OP */ }
     
    +    @Override
    +    public boolean getXmlBlockExternal() { return true; }
    +    @Override
    +    public void setXmlBlockExternal(boolean xmlBlockExternal) { /* NO-OP */ }
    +
         @Override
         public boolean getTldValidation() { return false; }
         @Override
    
  • java/org/apache/jasper/compiler/ImplicitTagLibraryInfo.java+13 2 modified
    @@ -24,6 +24,7 @@
     import java.util.Set;
     import java.util.Vector;
     
    +import javax.servlet.ServletContext;
     import javax.servlet.jsp.tagext.FunctionInfo;
     import javax.servlet.jsp.tagext.TagFileInfo;
     import javax.servlet.jsp.tagext.TagInfo;
    @@ -119,10 +120,20 @@ public ImplicitTagLibraryInfo(JspCompilationContext ctxt,
                         try {
                             URL url = ctxt.getResource(path);
                             TldResourcePath resourcePath = new TldResourcePath(url, path);
    +                        ServletContext servletContext = ctxt.getServletContext();
                             boolean validate = Boolean.parseBoolean(
    -                                ctxt.getServletContext().getInitParameter(
    +                                servletContext.getInitParameter(
                                             Constants.XML_VALIDATION_TLD_INIT_PARAM));
    -                        TldParser parser = new TldParser(true, validate, new ImplicitTldRuleSet());
    +                        String blockExternalString = servletContext.getInitParameter(
    +                                Constants.XML_BLOCK_EXTERNAL_INIT_PARAM);
    +                        boolean blockExternal;
    +                        if (blockExternalString == null) {
    +                            blockExternal = Constants.IS_SECURITY_ENABLED;
    +                        } else {
    +                            blockExternal = Boolean.parseBoolean(blockExternalString);
    +                        }
    +                        TldParser parser = new TldParser(true, validate,
    +                                new ImplicitTldRuleSet(), blockExternal);
                             taglibXml = parser.parse(resourcePath);
                         } catch (IOException | SAXException e) {
                             err.jspError(e);
    
  • java/org/apache/jasper/compiler/JspDocumentParser.java+37 5 modified
    @@ -28,8 +28,11 @@
     import javax.xml.parsers.SAXParser;
     import javax.xml.parsers.SAXParserFactory;
     
    +import org.apache.jasper.Constants;
     import org.apache.jasper.JasperException;
     import org.apache.jasper.JspCompilationContext;
    +import org.apache.tomcat.util.descriptor.DigesterFactory;
    +import org.apache.tomcat.util.descriptor.LocalResolver;
     import org.apache.tomcat.util.descriptor.tld.TldResourcePath;
     import org.apache.tomcat.util.scan.Jar;
     import org.xml.sax.Attributes;
    @@ -39,6 +42,7 @@
     import org.xml.sax.SAXParseException;
     import org.xml.sax.XMLReader;
     import org.xml.sax.ext.DefaultHandler2;
    +import org.xml.sax.ext.EntityResolver2;
     import org.xml.sax.helpers.AttributesImpl;
     
     /**
    @@ -91,6 +95,7 @@ class JspDocumentParser
         private boolean inDTD;
     
         private boolean isValidating;
    +    private final EntityResolver2 entityResolver;
     
         private final ErrorDispatcher err;
         private final boolean isTagFile;
    @@ -119,6 +124,20 @@ public JspDocumentParser(
             this.isTagFile = isTagFile;
             this.directivesOnly = directivesOnly;
             this.isTop = true;
    +
    +        String blockExternalString = ctxt.getServletContext().getInitParameter(
    +                Constants.XML_BLOCK_EXTERNAL_INIT_PARAM);
    +        boolean blockExternal;
    +        if (blockExternalString == null) {
    +            blockExternal = Constants.IS_SECURITY_ENABLED;
    +        } else {
    +            blockExternal = Boolean.parseBoolean(blockExternalString);
    +        }
    +
    +        this.entityResolver = new LocalResolver(
    +                DigesterFactory.SERVLET_API_PUBLIC_IDS,
    +                DigesterFactory.SERVLET_API_SYSTEM_IDS,
    +                blockExternal);
         }
     
         /*
    @@ -232,13 +251,26 @@ private void addInclude(Node parent, Collection<String> files) throws SAXExcepti
             }
         }
     
    +
    +    @Override
    +    public InputSource getExternalSubset(String name, String baseURI)
    +            throws SAXException, IOException {
    +        return entityResolver.getExternalSubset(name, baseURI);
    +    }
    +
         @Override
    -    public InputSource resolveEntity(String name, String publicId, String baseURI, String systemId)
    +    public InputSource resolveEntity(String publicId, String systemId)
                 throws SAXException, IOException {
    -        // TODO URLs returned by the Jar abstraction may be of the form jar:jar: which
    -        // is not a URL that can be resolved by the JRE. This should use the JarFactory
    -        // to construct and return a valid InputSource.
    -        return null;
    +        return entityResolver.resolveEntity(publicId, systemId);
    +    }
    +
    +    @Override
    +    public InputSource resolveEntity(String name, String publicId,
    +            String baseURI, String systemId) throws SAXException, IOException {
    +        // TODO URLs returned by the Jar abstraction may be of the form jar:jar:
    +        //      which is not a URL that can be resolved by the JRE. This should
    +        //      use the JarFactory to construct and return a valid InputSource.
    +        return entityResolver.resolveEntity(name, publicId, baseURI, systemId);
         }
     
         /*
    
  • java/org/apache/jasper/compiler/TagPluginManager.java+11 1 modified
    @@ -24,6 +24,7 @@
     
     import javax.servlet.ServletContext;
     
    +import org.apache.jasper.Constants;
     import org.apache.jasper.JasperException;
     import org.apache.jasper.compiler.tagplugin.TagPlugin;
     import org.apache.jasper.compiler.tagplugin.TagPluginContext;
    @@ -61,7 +62,16 @@ private void init(ErrorDispatcher err) throws JasperException {
             if (initialized)
                 return;
     
    -        TagPluginParser parser = new TagPluginParser(ctxt);
    +        String blockExternalString = ctxt.getInitParameter(
    +                Constants.XML_BLOCK_EXTERNAL_INIT_PARAM);
    +        boolean blockExternal;
    +        if (blockExternalString == null) {
    +            blockExternal = Constants.IS_SECURITY_ENABLED;
    +        } else {
    +            blockExternal = Boolean.parseBoolean(blockExternalString);
    +        }
    +
    +        TagPluginParser parser = new TagPluginParser(ctxt, blockExternal);
     
             try {
                 Enumeration<URL> urls =
    
  • java/org/apache/jasper/compiler/TldCache.java+9 1 modified
    @@ -74,7 +74,15 @@ public TldCache(ServletContext servletContext,
             }
             boolean validate = Boolean.parseBoolean(
                     servletContext.getInitParameter(Constants.XML_VALIDATION_TLD_INIT_PARAM));
    -        tldParser = new TldParser(true, validate);
    +        String blockExternalString = servletContext.getInitParameter(
    +                Constants.XML_BLOCK_EXTERNAL_INIT_PARAM);
    +        boolean blockExternal;
    +        if (blockExternalString == null) {
    +            blockExternal = Constants.IS_SECURITY_ENABLED;
    +        } else {
    +            blockExternal = Boolean.parseBoolean(blockExternalString);
    +        }
    +        tldParser = new TldParser(true, validate, blockExternal);
         }
     
     
    
  • java/org/apache/jasper/Constants.java+9 0 modified
    @@ -160,4 +160,13 @@ public class Constants {
          */
         public static final String XML_VALIDATION_TLD_INIT_PARAM =
                 "org.apache.jasper.XML_VALIDATE_TLD";
    +
    +    /**
    +     * Name of the ServletContext init-param that determines if the XML parsers
    +     * will block the resolution of external entities.
    +     * <p>
    +     * This must be kept in sync with org.apache.catalina.Globals
    +     */
    +    public static final String XML_BLOCK_EXTERNAL_INIT_PARAM =
    +            "org.apache.jasper.XML_BLOCK_EXTERNAL";
     }
    
  • java/org/apache/jasper/JspC.java+17 1 modified
    @@ -134,6 +134,7 @@ public class JspC extends Task implements Options {
         protected static final String SWITCH_SMAP = "-smap";
         protected static final String SWITCH_DUMP_SMAP = "-dumpsmap";
         protected static final String SWITCH_VALIDATE_TLD = "-validateTld";
    +    protected static final String SWITCH_BLOCK_EXTERNAL = "-blockExternal";
         protected static final String SHOW_SUCCESS ="-s";
         protected static final String LIST_ERRORS = "-l";
         protected static final int INC_WEBXML = 10;
    @@ -165,6 +166,7 @@ public class JspC extends Task implements Options {
         protected boolean trimSpaces = false;
         protected boolean genStringAsCharArray = false;
         protected boolean validateTld;
    +    protected boolean blockExternal;
         protected boolean xpoweredBy;
         protected boolean mappedFile = false;
         protected boolean poolingEnabled = true;
    @@ -373,6 +375,8 @@ public void setArgs(String[] arg) throws JasperException {
                     smapDumped = true;
                 } else if (tok.equals(SWITCH_VALIDATE_TLD)) {
                     setValidateTld(true);
    +            } else if (tok.equals(SWITCH_BLOCK_EXTERNAL)) {
    +                setBlockExternal(true);
                 } else {
                     if (tok.startsWith("-")) {
                         throw new JasperException("Unrecognized option: " + tok +
    @@ -860,6 +864,14 @@ public boolean isValidateTld() {
             return validateTld;
         }
     
    +    public void setBlockExternal( boolean b ) {
    +        this.blockExternal = b;
    +    }
    +
    +    public boolean isBlockExternal() {
    +        return blockExternal;
    +    }
    +
         public void setListErrors( boolean b ) {
             listErrors = b;
         }
    @@ -1440,8 +1452,12 @@ protected void initServletContext(ClassLoader classLoader)
             if (isValidateTld()) {
                 context.setInitParameter(Constants.XML_VALIDATION_TLD_INIT_PARAM, "true");
             }
    +        if (isBlockExternal()) {
    +            context.setInitParameter(Constants.XML_BLOCK_EXTERNAL_INIT_PARAM, "true");
    +        }
     
    -        TldScanner scanner = new TldScanner(context, true, isValidateTld());
    +        TldScanner scanner = new TldScanner(
    +                context, true, isValidateTld(), isBlockExternal());
             scanner.setClassLoader(classLoader);
     
             try {
    
  • java/org/apache/jasper/servlet/JasperInitializer.java+9 1 modified
    @@ -80,9 +80,17 @@ public void onStartup(Set<Class<?>> types, ServletContext context) throws Servle
     
             boolean validate = Boolean.parseBoolean(
                     context.getInitParameter(Constants.XML_VALIDATION_TLD_INIT_PARAM));
    +        String blockExternalString = context.getInitParameter(
    +                Constants.XML_BLOCK_EXTERNAL_INIT_PARAM);
    +        boolean blockExternal;
    +        if (blockExternalString == null) {
    +            blockExternal = Constants.IS_SECURITY_ENABLED;
    +        } else {
    +            blockExternal = Boolean.parseBoolean(blockExternalString);
    +        }
     
             // scan the application for TLDs
    -        TldScanner scanner = new TldScanner(context, true, validate);
    +        TldScanner scanner = new TldScanner(context, true, validate, blockExternal);
             try {
                 scanner.scan();
             } catch (IOException | SAXException e) {
    
  • java/org/apache/jasper/servlet/JspCServletContext.java+12 4 modified
    @@ -44,11 +44,11 @@
     import javax.servlet.SessionTrackingMode;
     import javax.servlet.descriptor.JspConfigDescriptor;
     
    +import org.apache.jasper.Constants;
     import org.apache.jasper.JasperException;
     import org.apache.jasper.compiler.Localizer;
     import org.apache.jasper.util.ExceptionUtils;
     import org.apache.tomcat.JarScanType;
    -import org.apache.tomcat.util.descriptor.web.Constants;
     import org.apache.tomcat.util.descriptor.web.FragmentJarScannerCallback;
     import org.apache.tomcat.util.descriptor.web.WebXml;
     import org.apache.tomcat.util.descriptor.web.WebXmlParser;
    @@ -124,13 +124,21 @@ public JspCServletContext(PrintWriter aLogWriter, URL aResourceBaseURL, ClassLoa
     
         private WebXml buildMergedWebXml() throws JasperException {
             WebXml webXml = new WebXml();
    -
    -        WebXmlParser webXmlParser = new WebXmlParser(false, false);
    +        String blockExternalString = getInitParameter(
    +                Constants.XML_BLOCK_EXTERNAL_INIT_PARAM);
    +        boolean blockExternal;
    +        if (blockExternalString == null) {
    +            blockExternal = Constants.IS_SECURITY_ENABLED;
    +        } else {
    +            blockExternal = Boolean.parseBoolean(blockExternalString);
    +        }
    +        WebXmlParser webXmlParser = new WebXmlParser(false, false, blockExternal);
             // Use this class's classloader as Ant will have set the TCCL to its own
             webXmlParser.setClassLoader(getClass().getClassLoader());
     
             try {
    -            URL url = getResource(Constants.WEB_XML_LOCATION);
    +            URL url = getResource(
    +                    org.apache.tomcat.util.descriptor.web.Constants.WEB_XML_LOCATION);
                 if (!webXmlParser.parseWebXml(url, webXml, false)) {
                     throw new JasperException(Localizer.getMessage("jspc.error.invalidWebXml"));
                 }
    
  • java/org/apache/jasper/servlet/TldScanner.java+4 2 modified
    @@ -72,9 +72,11 @@ public class TldScanner {
          */
         public TldScanner(ServletContext context,
                           boolean namespaceAware,
    -                      boolean validation) {
    +                      boolean validation,
    +                      boolean blockExternal) {
             this.context = context;
    -        this.tldParser = new TldParser(namespaceAware, validation);
    +
    +        this.tldParser = new TldParser(namespaceAware, validation, blockExternal);
         }
     
         /**
    
  • java/org/apache/tomcat/util/descriptor/DigesterFactory.java+18 5 modified
    @@ -16,13 +16,15 @@
      */
     package org.apache.tomcat.util.descriptor;
     
    +import java.util.Collections;
     import java.util.HashMap;
     import java.util.Map;
     
     import javax.servlet.ServletContext;
     
     import org.apache.tomcat.util.digester.Digester;
     import org.apache.tomcat.util.digester.RuleSet;
    +import org.xml.sax.ext.EntityResolver2;
     
     /**
      * Wrapper class around the Digester that hide Digester's initialization
    @@ -31,10 +33,16 @@
     public class DigesterFactory {
     
         /**
    -     * A resolver for the resources packaged in servlet-api.jar
    +     * Mapping of well-known public IDs used by the Servlet API to the matching
    +     * local resource.
          */
    -    public static final LocalResolver SERVLET_API_RESOLVER;
    +    public static final Map<String,String> SERVLET_API_PUBLIC_IDS;
     
    +    /**
    +     * Mapping of well-known system IDs used by the Servlet API to the matching
    +     * local resource.
    +     */
    +    public static final Map<String,String> SERVLET_API_SYSTEM_IDS;
     
         static {
             Map<String, String> publicIds = new HashMap<>();
    @@ -89,7 +97,8 @@ public class DigesterFactory {
             addSelf(systemIds, "javaee_web_services_1_4.xsd");
             addSelf(systemIds, "javaee_web_services_client_1_4.xsd");
     
    -        SERVLET_API_RESOLVER = new LocalResolver(publicIds, systemIds);
    +        SERVLET_API_PUBLIC_IDS = Collections.unmodifiableMap(publicIds);
    +        SERVLET_API_SYSTEM_IDS = Collections.unmodifiableMap(systemIds);
         }
     
         private static void addSelf(Map<String, String> ids, String id) {
    @@ -107,15 +116,19 @@ private static String idFor(String url) {
          * @param xmlValidation turn on/off xml validation
          * @param xmlNamespaceAware turn on/off namespace validation
          * @param rule an instance of <code>RuleSet</code> used for parsing the xml.
    +     * @param blockExternal turn on/off the blocking of external resources
          */
         public static Digester newDigester(boolean xmlValidation,
                                            boolean xmlNamespaceAware,
    -                                       RuleSet rule) {
    +                                       RuleSet rule,
    +                                       boolean blockExternal) {
             Digester digester = new Digester();
             digester.setNamespaceAware(xmlNamespaceAware);
             digester.setValidating(xmlValidation);
             digester.setUseContextClassLoader(true);
    -        digester.setEntityResolver(SERVLET_API_RESOLVER);
    +        EntityResolver2 resolver = new LocalResolver(SERVLET_API_PUBLIC_IDS,
    +                SERVLET_API_SYSTEM_IDS, blockExternal);
    +        digester.setEntityResolver(resolver);
             if (rule != null) {
                 digester.addRuleSet(rule);
             }
    
  • java/org/apache/tomcat/util/descriptor/LocalResolver.java+71 47 modified
    @@ -16,13 +16,15 @@
      */
     package org.apache.tomcat.util.descriptor;
     
    +import java.io.FileNotFoundException;
     import java.io.IOException;
     import java.net.MalformedURLException;
     import java.net.URI;
     import java.net.URISyntaxException;
     import java.net.URL;
     import java.util.Map;
     
    +import org.apache.tomcat.util.res.StringManager;
     import org.xml.sax.InputSource;
     import org.xml.sax.SAXException;
     import org.xml.sax.ext.EntityResolver2;
    @@ -32,22 +34,30 @@
      */
     public class LocalResolver implements EntityResolver2 {
     
    +    private static final StringManager sm =
    +            StringManager.getManager(Constants.PACKAGE_NAME);
    +
         private final Map<String,String> publicIds;
         private final Map<String,String> systemIds;
    -
    +    private final boolean blockExternal;
     
         /**
          * Constructor providing mappings of public and system identifiers to local
          * resources. Each map contains a mapping from a well-known identifier to a
          * URL for a local resource path.
          *
    -     * @param publicIds mapping of public identifiers to local resources
    -     * @param systemIds mapping of system identifiers to local resources
    +     * @param publicIds mapping of well-known public identifiers to local
    +     *                  resources
    +     * @param systemIds mapping of well-known system identifiers to local
    +     *                  resources
    +     * @param blockExternal are external resources blocked that are not
    +     *                      well-known
          */
         public LocalResolver(Map<String,String> publicIds,
    -            Map<String,String> systemIds) {
    +            Map<String,String> systemIds, boolean blockExternal) {
             this.publicIds = publicIds;
             this.systemIds = systemIds;
    +        this.blockExternal = blockExternal;
         }
     
     
    @@ -60,63 +70,77 @@ public InputSource resolveEntity(String publicId, String systemId)
     
         @Override
         public InputSource resolveEntity(String name, String publicId,
    -            String baseURI, String systemId) throws SAXException, IOException {
    -
    -        String resolved = resolve(publicId, systemId, baseURI);
    -        if (resolved == null) {
    -            return null;
    -        }
    +            String base, String systemId) throws SAXException, IOException {
     
    -        InputSource is = new InputSource(resolved);
    -        is.setPublicId(publicId);
    -        return is;
    -    }
    -
    -
    -    @Override
    -    public InputSource getExternalSubset(String name, String baseURI)
    -            throws SAXException, IOException {
    -        return null;
    -    }
    -
    -
    -    private String resolve(String publicId, String systemId, String baseURI) {
    -        // try resolving using the publicId
    +        // First try resolving using the publicId
             String resolved = publicIds.get(publicId);
             if (resolved != null) {
    -            return resolved;
    +            InputSource is = new InputSource(resolved);
    +            is.setPublicId(publicId);
    +            return is;
             }
     
    -        // try resolving using the systemId
    +        // If there is no systemId, can't try anything else
             if (systemId == null) {
    -            return null;
    +            throw new FileNotFoundException(sm.getString("localResolver.unresolvedEntity",
    +                    name, publicId, systemId, base));
             }
     
    -        systemId = resolve(baseURI, systemId);
    +        // Try resolving with the supplied systemId
             resolved = systemIds.get(systemId);
             if (resolved != null) {
    -            return resolved;
    +            InputSource is = new InputSource(resolved);
    +            is.setPublicId(publicId);
    +            return is;
             }
     
    -        // fall back to the supplied systemId
    -        return systemId;
    -    }
    -
    -
    -    private static String resolve(String baseURI, String systemId) {
    +        // Resolve the supplied systemId against the base
    +        URI systemUri;
             try {
    -            if (baseURI == null) {
    -                return systemId;
    +            if (base == null) {
    +                systemUri = new URI(systemId);
    +            } else {
    +                // Can't use URI.resolve() because "jar:..." URLs are not valid
    +                // hierarchical URIs so resolve() does not work. new URL()
    +                // delegates to the jar: stream handler and it manages to figure
    +                // it out.
    +                URI baseUri = new URI(base);
    +                systemUri = new URL(baseUri.toURL(), systemId).toURI();
                 }
    -            URI systemUri = new URI(systemId);
    -            if (systemUri.isAbsolute()) {
    -                return systemId;
    -            }
    -            return new URL(new URL(baseURI), systemId).toString();
    +            systemUri = systemUri.normalize();
             } catch (URISyntaxException e) {
    -            return systemId;
    -        } catch (MalformedURLException e) {
    -            return systemId;
    +            // May be caused by a | being used instead of a : in an absolute
    +            // file URI on Windows.
    +            if (blockExternal) {
    +                // Absolute paths aren't allowed so block it
    +                throw new MalformedURLException(e.getMessage());
    +            } else {
    +                // See if the URLHandler can resolve it
    +                return new InputSource(systemId);
    +            }
    +        }
    +        if (systemUri.isAbsolute()) {
    +            // Try the resolved systemId
    +            resolved = systemIds.get(systemUri.toString());
    +            if (resolved != null) {
    +                InputSource is = new InputSource(resolved);
    +                is.setPublicId(publicId);
    +                return is;
    +            }
    +            if (!blockExternal) {
    +                InputSource is = new InputSource(systemUri.toString());
    +                is.setPublicId(publicId);
    +                return is;
    +            }
             }
    +        throw new FileNotFoundException(sm.getString("localResolver.unresolvedEntity",
    +                name, publicId, systemId, base));
    +    }
    +
    +
    +    @Override
    +    public InputSource getExternalSubset(String name, String baseURI)
    +            throws SAXException, IOException {
    +        return null;
         }
    -}
    \ No newline at end of file
    +}
    
  • java/org/apache/tomcat/util/descriptor/LocalStrings.properties+2 0 modified
    @@ -13,5 +13,7 @@
     # See the License for the specific language governing permissions and
     # limitations under the License.
     
    +localResolver.unresolvedEntity=Could not resolve XML resource [{0}] with public ID [{1}], system ID [{2}] and base URI [{3}] to a known, local entity.
    +
     xmlErrorHandler.error=Non-fatal error [{0}] reported processing [{1}].
     xmlErrorHandler.warning=Warning [{0}] reported processing [{1}].
    
  • java/org/apache/tomcat/util/descriptor/tagplugin/TagPluginParser.java+3 2 modified
    @@ -42,8 +42,9 @@ public class TagPluginParser {
         private final Digester digester;
         private final Map<String, String> plugins = new HashMap<>();
     
    -    public TagPluginParser(ServletContext context) {
    -        digester = DigesterFactory.newDigester(false, false, new TagPluginRuleSet());
    +    public TagPluginParser(ServletContext context, boolean blockExternal) {
    +        digester = DigesterFactory.newDigester(
    +                false, false, new TagPluginRuleSet(), blockExternal);
             digester.setClassLoader(context.getClassLoader());
         }
     
    
  • java/org/apache/tomcat/util/descriptor/tld/TldParser.java+7 4 modified
    @@ -35,12 +35,15 @@ public class TldParser {
         private static final Log log = LogFactory.getLog(TldParser.class);
         private final Digester digester;
     
    -    public TldParser(boolean namespaceAware, boolean validation) {
    -        this(namespaceAware, validation, new TldRuleSet());
    +    public TldParser(boolean namespaceAware, boolean validation,
    +            boolean blockExternal) {
    +        this(namespaceAware, validation, new TldRuleSet(), blockExternal);
         }
     
    -    public TldParser(boolean namespaceAware, boolean validation, RuleSet ruleSet) {
    -        digester = DigesterFactory.newDigester(validation, namespaceAware, ruleSet);
    +    public TldParser(boolean namespaceAware, boolean validation, RuleSet ruleSet,
    +            boolean blockExternal) {
    +        digester = DigesterFactory.newDigester(
    +                validation, namespaceAware, ruleSet, blockExternal);
         }
     
         public TaglibXml parse(TldResourcePath path) throws IOException, SAXException {
    
  • java/org/apache/tomcat/util/descriptor/web/WebXmlParser.java+4 3 modified
    @@ -55,15 +55,16 @@ public class WebXmlParser {
         private final WebRuleSet webFragmentRuleSet;
     
     
    -    public WebXmlParser(boolean namespaceAware, boolean validation) {
    +    public WebXmlParser(boolean namespaceAware, boolean validation,
    +            boolean blockExternal) {
             webRuleSet = new WebRuleSet(false);
             webDigester = DigesterFactory.newDigester(validation,
    -                namespaceAware, webRuleSet);
    +                namespaceAware, webRuleSet, blockExternal);
             webDigester.getParser();
     
             webFragmentRuleSet = new WebRuleSet(true);
             webFragmentDigester = DigesterFactory.newDigester(validation,
    -                namespaceAware, webFragmentRuleSet);
    +                namespaceAware, webFragmentRuleSet, blockExternal);
             webFragmentDigester.getParser();
         }
     
    
  • test/javax/servlet/resources/TestSchemaValidation.java+14 14 modified
    @@ -31,8 +31,8 @@ public class TestSchemaValidation {
     
         @Test
         public void testWebapp() throws Exception {
    -        Digester digester =
    -                DigesterFactory.newDigester(true, true, new WebRuleSet(false));
    +        Digester digester = DigesterFactory.newDigester(
    +                true, true, new WebRuleSet(false), true);
             digester.push(new WebXml());
             WebXml desc = (WebXml) digester.parse(
                     new File("test/webapp/WEB-INF/web.xml"));
    @@ -41,8 +41,8 @@ public void testWebapp() throws Exception {
     
         @Test
         public void testWebapp_2_2() throws Exception {
    -        Digester digester =
    -                DigesterFactory.newDigester(true, true, new WebRuleSet(false));
    +        Digester digester = DigesterFactory.newDigester(
    +                true, true, new WebRuleSet(false), true);
             digester.push(new WebXml());
             WebXml desc = (WebXml) digester.parse(
                     new File("test/webapp-2.2/WEB-INF/web.xml"));
    @@ -52,8 +52,8 @@ public void testWebapp_2_2() throws Exception {
     
         @Test
         public void testWebapp_2_3() throws Exception {
    -        Digester digester =
    -                DigesterFactory.newDigester(true, true, new WebRuleSet(false));
    +        Digester digester = DigesterFactory.newDigester(
    +                true, true, new WebRuleSet(false), true);
             digester.push(new WebXml());
             WebXml desc = (WebXml) digester.parse(
                     new File("test/webapp-2.3/WEB-INF/web.xml"));
    @@ -63,8 +63,8 @@ public void testWebapp_2_3() throws Exception {
     
         @Test
         public void testWebapp_2_4() throws Exception {
    -        Digester digester =
    -                DigesterFactory.newDigester(true, true, new WebRuleSet(false));
    +        Digester digester = DigesterFactory.newDigester(
    +                true, true, new WebRuleSet(false), true);
             digester.push(new WebXml());
             WebXml desc = (WebXml) digester.parse(
                     new File("test/webapp-2.4/WEB-INF/web.xml"));
    @@ -73,8 +73,8 @@ public void testWebapp_2_4() throws Exception {
     
         @Test
         public void testWebapp_2_5() throws Exception {
    -        Digester digester =
    -                DigesterFactory.newDigester(true, true, new WebRuleSet(false));
    +        Digester digester = DigesterFactory.newDigester(
    +                true, true, new WebRuleSet(false), true);
             digester.push(new WebXml());
             WebXml desc = (WebXml) digester.parse(
                     new File("test/webapp-2.5/WEB-INF/web.xml"));
    @@ -83,8 +83,8 @@ public void testWebapp_2_5() throws Exception {
     
         @Test
         public void testWebapp_3_0() throws Exception {
    -        Digester digester =
    -                DigesterFactory.newDigester(true, true, new WebRuleSet(false));
    +        Digester digester = DigesterFactory.newDigester(
    +                true, true, new WebRuleSet(false), true);
             digester.push(new WebXml());
             WebXml desc = (WebXml) digester.parse(
                     new File("test/webapp-3.0/WEB-INF/web.xml"));
    @@ -93,8 +93,8 @@ public void testWebapp_3_0() throws Exception {
     
         @Test
         public void testWebapp_3_1() throws Exception {
    -        Digester digester =
    -                DigesterFactory.newDigester(true, true, new WebRuleSet(false));
    +        Digester digester = DigesterFactory.newDigester(
    +                true, true, new WebRuleSet(false), true);
             digester.push(new WebXml());
             WebXml desc = (WebXml) digester.parse(
                     new File("test/webapp-3.1/WEB-INF/web.xml"));
    
  • test/org/apache/catalina/core/TesterContext.java+10 0 modified
    @@ -642,6 +642,16 @@ public void setXmlValidation(boolean xmlValidation) {
             // NO-OP
         }
     
    +    @Override
    +    public boolean getXmlBlockExternal() {
    +        return false;
    +    }
    +
    +    @Override
    +    public void setXmlBlockExternal(boolean xmlBlockExternal) {
    +        // NO-OP
    +    }
    +
         @Override
         public boolean getTldValidation(){
             return false;
    
  • test/org/apache/jasper/servlet/TestTldScanner.java+2 1 modified
    @@ -39,7 +39,8 @@ public void testWithWebapp() throws Exception {
             Context context = tomcat.addWebapp(null, "/test", appDir.getAbsolutePath());
             tomcat.start();
     
    -        TldScanner scanner = new TldScanner(context.getServletContext(), true, true);
    +        TldScanner scanner =
    +                new TldScanner(context.getServletContext(), true, true, true);
             scanner.scan();
             Assert.assertEquals(5, scanner.getUriTldResourcePathMap().size());
             Assert.assertEquals(1, scanner.getListeners().size());
    
  • test/org/apache/tomcat/util/descriptor/TestLocalResolver.java+11 10 modified
    @@ -16,6 +16,7 @@
      */
     package org.apache.tomcat.util.descriptor;
     
    +import java.io.FileNotFoundException;
     import java.io.IOException;
     import java.util.HashMap;
     import java.util.Map;
    @@ -34,7 +35,7 @@ public class TestLocalResolver {
         private final Map<String, String> publicIds = new HashMap<>();
         private final Map<String, String> systemIds = new HashMap<>();
     
    -    private LocalResolver resolver = new LocalResolver(publicIds, systemIds);
    +    private LocalResolver resolver = new LocalResolver(publicIds, systemIds, true);
         private String WEB_22_LOCAL;
         private String WEB_31_LOCAL;
         private String WEBCOMMON_31_LOCAL;
    @@ -53,25 +54,25 @@ public String urlFor(String id) {
             return ServletContext.class.getResource(id).toExternalForm();
         }
     
    -    @Test
    -    public void unknownNullIdIsNull() throws IOException, SAXException {
    +    @Test(expected = FileNotFoundException.class)
    +    public void unknownNullId() throws IOException, SAXException {
             Assert.assertNull(resolver.resolveEntity(null, null));
         }
     
    -    @Test
    -    public void unknownPublicIdIsNull() throws IOException, SAXException {
    +    @Test(expected = FileNotFoundException.class)
    +    public void unknownPublicId() throws IOException, SAXException {
             Assert.assertNull(resolver.resolveEntity("unknown", null));
         }
     
    -    @Test
    -    public void unknownSystemIdIsReturned() throws IOException, SAXException {
    +    @Test(expected = FileNotFoundException.class)
    +    public void unknownSystemId() throws IOException, SAXException {
             InputSource source = resolver.resolveEntity(null, "unknown");
             Assert.assertEquals(null, source.getPublicId());
             Assert.assertEquals("unknown", source.getSystemId());
         }
     
    -    @Test
    -    public void unknownSystemIdIsResolvedAgainstBaseURI()
    +    @Test(expected = FileNotFoundException.class)
    +    public void unknownRelativeSystemId()
                 throws IOException, SAXException {
             InputSource source = resolver.resolveEntity(
                     null, null, "http://example.com/home.html", "unknown");
    @@ -121,4 +122,4 @@ public void absoluteSystemIdOverridesBaseURI()
             Assert.assertEquals(null, source.getPublicId());
             Assert.assertEquals(WEB_31_LOCAL, source.getSystemId());
         }
    -}
    \ No newline at end of file
    +}
    
  • test/org/apache/tomcat/util/descriptor/tld/TestTldParser.java+1 1 modified
    @@ -36,7 +36,7 @@ public class TestTldParser {
     
         @Before
         public void init() {
    -        parser = new TldParser(true, true);
    +        parser = new TldParser(true, true, null, true);
         }
     
         @Test
    
  • webapps/docs/config/context.xml+10 0 modified
    @@ -524,6 +524,16 @@
             Context.  If not specified, a standard default value will be used.</p>
           </attribute>
     
    +      <attribute name="xmlBlockExternal" required="false">
    +        <p>If the value of this flag is <code>true</code>, the parsing of
    +        <code>web.xml</code>, <code>web-fragment.xml</code>, <code>*.tld</code>,
    +        <code>*.jspx</code>, <code>*.tagx</code> and <code>tagPlugins.xml</code>
    +        files for this web application will not permit external entities to be
    +        loaded. If a <code>SecurityManager</code> is configured then the default
    +        value of this attribute will be <code>true</code>, else the default
    +        value will be <code>false</code>.</p>
    +      </attribute>
    +
           <attribute name="xmlNamespaceAware" required="false">
             <p>If the value of this flag is <code>true</code>, the parsing of
             <code>web.xml</code> and <code>web-fragment.xml</code> files for this
    
  • webapps/docs/security-howto.xml+3 0 modified
    @@ -179,6 +179,9 @@
         <ul>
           <li>The default value for the <strong>deployXML</strong> attribute of the
           <strong>Host</strong> element is changed to <code>false</code>.</li>
    +      <li>The default value for the <strong>xmlBlockExternal</strong> attribute
    +      of the <strong>Context</strong> element is changed to <code>true</code>.
    +      </li>
         </ul>
       </section>
     
    

Vulnerability mechanics

Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

42

News mentions

0

No linked articles in our index yet.