[ https://issues.apache.org/jira/browse/WW-5233?focusedWorklogId=868411&page=com.atlassian.jira.plugin.system.issuetabpanels:worklog-tabpanel#worklog-868411 ]
ASF GitHub Bot logged work on WW-5233: -------------------------------------- Author: ASF GitHub Bot Created on: 29/Jun/23 10:52 Start Date: 29/Jun/23 10:52 Worklog Time Spent: 10m Work Description: github-code-scanning[bot] commented on code in PR #608: URL: https://github.com/apache/struts/pull/608#discussion_r1246462268 ########## plugins/tiles/src/main/java/org/apache/tiles/core/definition/digester/DigesterDefinitionsReader.java: ########## @@ -0,0 +1,464 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.tiles.core.definition.digester; + +import org.apache.commons.digester.Digester; +import org.apache.commons.digester.Rule; +import org.apache.tiles.api.Attribute; +import org.apache.tiles.api.Definition; +import org.apache.tiles.api.Expression; +import org.apache.tiles.api.ListAttribute; +import org.apache.tiles.core.definition.DefinitionsFactoryException; +import org.apache.tiles.core.definition.DefinitionsReader; +import org.xml.sax.Attributes; +import org.xml.sax.ErrorHandler; +import org.xml.sax.SAXException; +import org.xml.sax.SAXParseException; + +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.util.LinkedHashMap; +import java.util.Map; + +/** + * Reads {@link Definition} objects from + * an XML InputStream using Digester. <p/> + * <p> + * This <code>DefinitionsReader</code> implementation expects the source to be + * passed as an <code>InputStream</code>. It parses XML data from the source + * and builds a Map of Definition objects. + * </p> + * <p/> + * <p> + * The Digester object can be configured by passing in initialization + * parameters. Currently the only parameter that is supported is the + * <code>validating</code> parameter. This value is set to <code>false</code> + * by default. To enable DTD validation for XML Definition files, give the init + * method a parameter with a key of + * <code>org.apache.tiles.definition.digester.DigesterDefinitionsReader.PARSER_VALIDATE</code> + * and a value of <code>"true"</code>. <p/> + * <p> + * The Definition objects are stored internally in a Map. The Map is stored as + * an instance variable rather than a local variable in the <code>read</code> + * method. This means that instances of this class are <strong>not</strong> + * thread-safe and access by multiple threads must be synchronized. + * </p> + */ +public class DigesterDefinitionsReader implements DefinitionsReader { + + /** + * Digester validation parameter name. + */ + public static final String PARSER_VALIDATE_PARAMETER_NAME = "org.apache.tiles.definition.digester.DigesterDefinitionsReader.PARSER_VALIDATE"; + + // Digester rules constants for tag interception. + + /** + * Intercepts a <definition> tag. + */ + private static final String DEFINITION_TAG = "tiles-definitions/definition"; + + /** + * Intercepts a <put-attribute> tag. + */ + private static final String PUT_TAG = "*/definition/put-attribute"; + + /** + * Intercepts a <definition> inside a <put-attribute> tag. + */ + private static final String PUT_DEFINITION_TAG = "*/put-attribute/definition"; + + /** + * Intercepts a <definition> inside an <add-attribute> tag. + */ + private static final String ADD_DEFINITION_TAG = "*/add-attribute/definition"; + + /** + * Intercepts a <put-list-attribute> tag inside a %lt;definition> + * tag. + */ + private static final String DEF_LIST_TAG = "*/definition/put-list-attribute"; + + /** + * Intercepts a <add-attribute> tag. + */ + private static final String ADD_LIST_ELE_TAG = "*/add-attribute"; + + /** + * Intercepts a <add-list-attribute> tag. + */ + private static final String NESTED_LIST = "*/add-list-attribute"; + + // Handler class names. + + /** + * The handler to create definitions. + * + * @since 2.1.0 + */ + protected static final String DEFINITION_HANDLER_CLASS = + Definition.class.getName(); + + /** + * The handler to create attributes. + * + * @since 2.1.0 + */ + protected static final String PUT_ATTRIBUTE_HANDLER_CLASS = + Attribute.class.getName(); + + /** + * The handler to create list attributes. + * + * @since 2.1.0 + */ + protected static final String LIST_HANDLER_CLASS = + ListAttribute.class.getName(); + + /** + * Digester rule to manage definition filling. + * + * @since 2.1.2 + */ + public static class FillDefinitionRule extends Rule { + + /** {@inheritDoc} */ + @Override + public void begin(String namespace, String name, Attributes attributes) { + Definition definition = (Definition) digester.peek(); + definition.setName(attributes.getValue("name")); + definition.setPreparer(attributes.getValue("preparer")); + String extendsAttribute = attributes.getValue("extends"); + definition.setExtends(extendsAttribute); + + String template = attributes.getValue("template"); + Attribute attribute = Attribute.createTemplateAttribute(template); + attribute.setExpressionObject(Expression + .createExpressionFromDescribedExpression(attributes + .getValue("templateExpression"))); + attribute.setRole(attributes.getValue("role")); + String templateType = attributes.getValue("templateType"); + if (templateType != null) { + attribute.setRenderer(templateType); + } else if (extendsAttribute != null) { + attribute.setRenderer(null); + } + definition.setTemplateAttribute(attribute); + } + } + + /** + * Digester rule to manage attribute filling. + * + * @since 2.1.0 + */ + public static class FillAttributeRule extends Rule { + + /** {@inheritDoc} */ + @Override + public void begin(String namespace, String name, Attributes attributes) { + Attribute attribute = (Attribute) digester.peek(); + attribute.setValue(attributes.getValue("value")); + String expression = attributes.getValue("expression"); + attribute.setExpressionObject(Expression + .createExpressionFromDescribedExpression(expression)); + attribute.setRole(attributes.getValue("role")); + attribute.setRenderer(attributes.getValue("type")); + } + } + + /** + * Digester rule to manage assignment of the attribute to the parent + * element. + * + * @since 2.1.0 + */ + public static class PutAttributeRule extends Rule { + + /** {@inheritDoc} */ + @Override + public void begin(String namespace, String name, Attributes attributes) { + Attribute attribute = (Attribute) digester.peek(0); + Definition definition = (Definition) digester.peek(1); + definition.putAttribute(attributes.getValue("name"), attribute, + "true".equals(attributes.getValue("cascade"))); + } + } + + /** + * Digester rule to manage assignment of a nested definition in an attribute + * value. + * + * @since 2.1.0 + */ + public class AddNestedDefinitionRule extends Rule { + + /** {@inheritDoc} */ + @Override + public void begin(String namespace, String name, Attributes attributes) { + Definition definition = (Definition) digester.peek(0); + if (definition.getName() == null) { + definition.setName(getNextUniqueDefinitionName(definitions)); + } + Attribute attribute = (Attribute) digester.peek(1); + attribute.setValue(definition.getName()); + attribute.setRenderer("definition"); + } + } + + /** + * <code>Digester</code> object used to read Definition data + * from the source. + */ + protected Digester digester; + + /** + * The set of public identifiers, and corresponding resource names for + * the versions of the configuration file DTDs we know about. There + * <strong>MUST</strong> be an even number of Strings in this list! + */ + protected String[] registrations; + + /** + * Stores Definition objects. + */ + private Map<String, Definition> definitions; + + /** + * Index to be used to create unique definition names for anonymous + * (nested) definitions. + */ + private int anonymousDefinitionIndex = 1; + + /** + * Creates a new instance of DigesterDefinitionsReader. + */ + public DigesterDefinitionsReader() { + digester = new Digester(); + digester.setNamespaceAware(true); + digester.setUseContextClassLoader(true); + digester.setErrorHandler(new ThrowingErrorHandler()); + + // Register our local copy of the DTDs that we can find + String[] registrations = getRegistrations(); + for (int i = 0; i < registrations.length; i += 2) { + URL url = this.getClass().getResource( + registrations[i + 1]); + if (url != null) { + digester.register(registrations[i], url.toString()); + } + } + + initSyntax(digester); + } + + /** + * Sets the validation of XML files. + * + * @param validating <code>true</code> means that XML validation is turned + * on. <code>false</code> otherwise. + * @since 3.3.0 + */ + public void setValidating(boolean validating) { + digester.setValidating(validating); + } + + /** + * Reads <code>{@link Definition}</code> objects from a source. + * <p/> + * Implementations should publish what type of source object is expected. + * + * @param source The <code>InputStream</code> source from which definitions + * will be read. + * @return a Map of <code>Definition</code> objects read from + * the source. + * @throws DefinitionsFactoryException If the source is invalid or + * an error occurs when reading definitions. + */ + public Map<String, Definition> read(Object source) { + // This is an instance variable instead of a local variable because + // we want to be able to call the addDefinition method to populate it. + // But we reset the Map here, which, of course, has threading implications. + definitions = new LinkedHashMap<>(); + + if (source == null) { + // Perhaps we should throw an exception here. + return null; + } + + InputStream input; + try { + input = (InputStream) source; + } catch (ClassCastException e) { + throw new DefinitionsFactoryException( + "Invalid source type. Requires java.io.InputStream.", e); + } + + try { + // set first object in stack + //digester.clear(); + digester.push(this); + // parse + digester.parse(input); Review Comment: ## Resolving XML external entity in user-controlled data XML parsing depends on a [user-provided value](1) without guarding against external entity expansion. XML parsing depends on a [user-provided value](2) without guarding against external entity expansion. [Show more details](https://github.com/apache/struts/security/code-scanning/235) Issue Time Tracking ------------------- Worklog Id: (was: 868411) Time Spent: 3h 20m (was: 3h 10m) > Include Apache Tiles code base in the Tiles plugin > -------------------------------------------------- > > Key: WW-5233 > URL: https://issues.apache.org/jira/browse/WW-5233 > Project: Struts 2 > Issue Type: Improvement > Components: Plugin - Tiles > Reporter: Lukasz Lenart > Priority: Major > Fix For: 6.3.0 > > Time Spent: 3h 20m > Remaining Estimate: 0h > > Apache Tiles has retired and it isn't maintained anymore. There are some > outstanding security issues that can be addressed right now. It will be > easier to maintain the code base as a part of the Tiles plugin instead of > taking the project back from attick. -- This message was sent by Atlassian Jira (v8.20.10#820010)