Skip to content

Commit

Permalink
CVE-2022-46751 don't parse doctypes and access external entities by d…
Browse files Browse the repository at this point in the history
…efault
  • Loading branch information
bodewig committed Aug 17, 2023
1 parent 6ac4f36 commit 2be17bc
Show file tree
Hide file tree
Showing 12 changed files with 605 additions and 67 deletions.
2 changes: 2 additions & 0 deletions asciidoc/settings.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ In order to work as you want, Ivy sometimes needs some settings. Actually, Ivy c
Settings are specified through an XML file, usually called `ivysettings.xml`. To configure Ivy from Ant, you just have to use the link:use/settings{outfilesuffix}[settings] datatype with the path of your settings file.
In addition certain link:systemproperties{outfilesuffix}[Java system properties] affect the XML parsing behavior of Ivy.
Here is an example of the settings file:
[source, xml]
Expand Down
67 changes: 67 additions & 0 deletions asciidoc/systemproperties.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
////
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

https://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.
////
= Java System Properties Affecting Ivy
== XML Parser Settings
Starting with Ivy 2.5.2 Ivy's XML parser can be controlled via the use
of two newly introduced system properties.
If you want to restore the default behavior of Ivy 2.5.1 and earlier
you need to set `ivy.xml.allow-doctype-processing` to `true` and
`ivy.xml.external-resources` to `ALL`.
=== `ivy.xml.allow-doctype-processing`
This system property accepts `true` or `false` as values. When set to
`false` Ivy will not allow any processing of doctype declarations at
all, while setting it to `true` enables it.
The default is to allow doctype processing if and only if Ivy is
parsing a Maven POM file.
=== `ivy.xml.external-resources`
This system property controls if external resources are read during
doctype processing - and if so, where they can be loadad from. The
value of this system property is only ever used if
`ivy.xml.allow-doctype-processing` is not `false`.
The accepted values are
* `PROHIBIT` makes Ivy fail if any doctype tries to load an external
resource.
* `IGNORE` makes Ivy ignore any external resource that the doctype
declaration wants to load.
* `LOCAL_ONLY` allows external resources to be loaded via `file:` or
`jar:file` URIs only.
* `ALL` allows external resources to be loaded from any URI.
The default behavior is to not allow doctype processing at all, but if
it is enabled the value `PROHIBIT` is assumed unless the property has
been set explicitly.
When reading Maven POMs a specific internal system id is recognized as
resource and will be loaded from a resource shipping with the Ivy
distribution in order to deal with invalid POM files accepted by
Apache Maven - and the default value for this property is
`IGNORE`in that case. See
link:https://issues.apache.org/jira/browse/IVY-921[IVY-921] for
details.
5 changes: 5 additions & 0 deletions asciidoc/toc.json
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,11 @@
}
]
},
{
"id": "systemproperties",
"title": "System Properties",
"children": []
},
{
"id":"settings",
"title":"Settings Files",
Expand Down
9 changes: 3 additions & 6 deletions src/java/org/apache/ivy/ant/IvyArtifactReport.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,6 @@

import javax.xml.transform.OutputKeys;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerFactoryConfigurationError;
import javax.xml.transform.sax.SAXTransformerFactory;
import javax.xml.transform.sax.TransformerHandler;
import javax.xml.transform.stream.StreamResult;

Expand All @@ -43,6 +41,7 @@
import org.apache.ivy.core.resolve.ResolveOptions;
import org.apache.ivy.core.resolve.ResolvedModuleRevision;
import org.apache.ivy.core.retrieve.RetrieveOptions;
import org.apache.ivy.util.XMLHelper;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.Project;
import org.xml.sax.SAXException;
Expand Down Expand Up @@ -170,10 +169,8 @@ private void generateXml(IvyNode[] dependencies,
}

private TransformerHandler createTransformerHandler(FileOutputStream fileOutputStream)
throws TransformerFactoryConfigurationError, TransformerConfigurationException {
SAXTransformerFactory transformerFact = (SAXTransformerFactory) SAXTransformerFactory
.newInstance();
TransformerHandler saxHandler = transformerFact.newTransformerHandler();
throws TransformerConfigurationException {
TransformerHandler saxHandler = XMLHelper.getTransformerHandler();
saxHandler.getTransformer().setOutputProperty(OutputKeys.ENCODING, "UTF-8");
saxHandler.getTransformer().setOutputProperty(OutputKeys.INDENT, "yes");
saxHandler.setResult(new StreamResult(fileOutputStream));
Expand Down
5 changes: 2 additions & 3 deletions src/java/org/apache/ivy/ant/IvyReport.java
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;

Expand All @@ -48,6 +47,7 @@
import org.apache.ivy.plugins.report.XmlReportParser;
import org.apache.ivy.util.FileUtil;
import org.apache.ivy.util.Message;
import org.apache.ivy.util.XMLHelper;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.taskdefs.XSLTProcess;
import org.apache.tools.ant.util.JAXPUtils;
Expand Down Expand Up @@ -313,8 +313,7 @@ private void genStyled(String[] confs, File style, String ext) throws IOExceptio
Source xsltSource = new StreamSource(xsltStream, JAXPUtils.getSystemId(style));

// create transformer
TransformerFactory tFactory = TransformerFactory.newInstance();
Transformer transformer = tFactory.newTransformer(xsltSource);
Transformer transformer = XMLHelper.getTransformer(xsltSource);

// add standard parameters
transformer.setParameter("confs", conf);
Expand Down
9 changes: 3 additions & 6 deletions src/java/org/apache/ivy/core/settings/XmlSettingsParser.java
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,6 @@
import java.util.List;
import java.util.Map;

import javax.xml.parsers.SAXParserFactory;

import org.apache.ivy.core.IvyPatternHelper;
import org.apache.ivy.core.cache.RepositoryCacheManager;
import org.apache.ivy.core.module.status.StatusManager;
Expand All @@ -46,6 +44,7 @@
import org.apache.ivy.util.Configurator;
import org.apache.ivy.util.FileResolver;
import org.apache.ivy.util.Message;
import org.apache.ivy.util.XMLHelper;
import org.apache.ivy.util.url.CredentialsStore;
import org.apache.ivy.util.url.TimeoutConstrainedURLHandler;
import org.apache.ivy.util.url.URLHandlerRegistry;
Expand Down Expand Up @@ -151,10 +150,8 @@ public File resolveFile(String path, String filename) {
@SuppressWarnings("deprecation")
private void doParse(URL settingsUrl) throws IOException, ParseException {
this.settings = settingsUrl;
try (InputStream stream = URLHandlerRegistry.getDefault().openStream(settingsUrl)) {
InputSource inSrc = new InputSource(stream);
inSrc.setSystemId(settingsUrl.toExternalForm());
SAXParserFactory.newInstance().newSAXParser().parse(settingsUrl.toExternalForm(), this);
try {
XMLHelper.parse(settingsUrl, null, this);
ivy.validate();
} catch (IOException e) {
throw e;
Expand Down
11 changes: 4 additions & 7 deletions src/java/org/apache/ivy/osgi/obr/xml/OBRXMLWriter.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,7 @@
import java.util.Set;

import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.sax.SAXTransformerFactory;
import javax.xml.transform.sax.TransformerHandler;
import javax.xml.transform.stream.StreamResult;

Expand All @@ -45,6 +43,7 @@
import org.apache.ivy.osgi.util.Version;
import org.apache.ivy.osgi.util.VersionRange;
import org.apache.ivy.util.Message;
import org.apache.ivy.util.XMLHelper;
import org.xml.sax.ContentHandler;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.AttributesImpl;
Expand All @@ -53,12 +52,10 @@ public class OBRXMLWriter {

public static ContentHandler newHandler(OutputStream out, String encoding, boolean indent)
throws TransformerConfigurationException {
SAXTransformerFactory tf = (SAXTransformerFactory) SAXTransformerFactory.newInstance();
TransformerHandler hd = tf.newTransformerHandler();
Transformer serializer = tf.newTransformer();
TransformerHandler hd = XMLHelper.getTransformerHandler();
hd.getTransformer().setOutputProperty(OutputKeys.ENCODING, encoding);
hd.getTransformer().setOutputProperty(OutputKeys.INDENT, indent ? "yes" : "no");
StreamResult stream = new StreamResult(out);
serializer.setOutputProperty(OutputKeys.ENCODING, encoding);
serializer.setOutputProperty(OutputKeys.INDENT, indent ? "yes" : "no");
hd.setResult(stream);
return hd;
}
Expand Down
3 changes: 2 additions & 1 deletion src/java/org/apache/ivy/plugins/parser/m2/PomReader.java
Original file line number Diff line number Diff line change
Expand Up @@ -130,12 +130,13 @@ public PomReader(final URL descriptorURL, final Resource res) throws IOException
public InputSource resolveEntity(String publicId, String systemId)
throws SAXException, IOException {
if (systemId != null && systemId.endsWith("m2-entities.ent")) {
// IVY-921: return an InputSource for our local packaged m2-entities.ent file
return new InputSource(
PomReader.class.getResourceAsStream("m2-entities.ent"));
}
return null;
}
});
}, true, XMLHelper.ExternalResources.IGNORE);
projectElement = pomDomDoc.getDocumentElement();
if (!PROJECT.equals(projectElement.getNodeName())
&& !MODEL.equals(projectElement.getNodeName())) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,13 @@
*/
package org.apache.ivy.plugins.parser.xml;

import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.StringReader;
import java.io.UnsupportedEncodingException;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
Expand Down Expand Up @@ -80,6 +84,7 @@
import org.apache.ivy.util.XMLHelper;
import org.apache.ivy.util.extendable.ExtendableItemHelper;
import org.xml.sax.Attributes;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;

import static org.apache.ivy.core.module.descriptor.Configuration.Visibility.getVisibility;
Expand Down Expand Up @@ -216,6 +221,8 @@ private State() {
protected static final List<String> ALLOWED_VERSIONS = Arrays.asList("1.0",
"1.1", "1.2", "1.3", "1.4", "2.0", "2.1", "2.2", "2.3", "2.4");

private static final String IVY_XSD_CONTENT;

/* how and what do we have to parse */
private ParserSettings settings;

Expand Down Expand Up @@ -248,6 +255,40 @@ private State() {

private Stack<ExtraInfoHolder> extraInfoStack = new Stack<>();

static {
String ivyXSDContent = null;
final InputStream is = Parser.class.getResourceAsStream("ivy.xsd");
if (is != null) {
final StringBuilder sb = new StringBuilder();
try {
try {
final BufferedReader reader = new BufferedReader(new InputStreamReader(is, "UTF-8"));
String line = null;
while ((line = reader.readLine()) != null) {
if (sb.length() != 0) {
sb.append("\n");
}
sb.append(line);
}
} catch (UnsupportedEncodingException e) {
// ignore
ivyXSDContent = null;
} catch (IOException e) {
// ignore
ivyXSDContent = null;
}
} finally {
try {
is.close();
} catch (Exception e) {
// ignore
}
}
ivyXSDContent = sb.length() == 0 ? null : sb.toString();
}
IVY_XSD_CONTENT = ivyXSDContent;
}

public Parser(ModuleDescriptorParser parser, ParserSettings ivySettings) {
super(parser);
settings = ivySettings;
Expand All @@ -268,10 +309,14 @@ public void setValidate(boolean validate) {
public void parse() throws ParseException {
try {
URL schemaURL = validate ? getSchemaURL() : null;
XMLHelper.ExternalResources e =
validate && System.getProperty(XMLHelper.EXTERNAL_RESOURCES) == null
? XMLHelper.ExternalResources.IGNORE
: XMLHelper.ExternalResources.fromSystemProperty();
if (descriptorURL != null) {
XMLHelper.parse(descriptorURL, schemaURL, this);
XMLHelper.parse(descriptorURL, schemaURL, this, null, e);
} else {
XMLHelper.parse(descriptorInput, schemaURL, this, null);
XMLHelper.parse(descriptorInput, schemaURL, this, null, e);
}
checkConfigurations();
replaceConfigurationWildcards();
Expand All @@ -296,6 +341,26 @@ public void parse() throws ParseException {
}
}

@Override
public InputSource resolveEntity(final String publicId, final String systemId)
throws IOException, SAXException {
if (isApacheOrgIvyXSDSystemId(systemId) && IVY_XSD_CONTENT != null) {
// redirect the schema location to local file based ivy.xsd whose content
// we have already read and is available in-memory.
final InputSource source = new InputSource(new StringReader(IVY_XSD_CONTENT));
return source;
}
return super.resolveEntity(publicId, systemId);
}

private static boolean isApacheOrgIvyXSDSystemId(final String systemId) {
if (systemId == null) {
return false;
}
return systemId.equals("http://ant.apache.org/ivy/schemas/ivy.xsd")
|| systemId.equals("https://ant.apache.org/ivy/schemas/ivy.xsd");
}

@Override
public void startElement(String uri, String localName, String qName, Attributes attributes)
throws SAXException {
Expand Down
7 changes: 2 additions & 5 deletions src/java/org/apache/ivy/plugins/report/XmlReportParser.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,6 @@
import java.util.SortedMap;
import java.util.TreeMap;

import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;

import org.apache.ivy.core.cache.ArtifactOrigin;
import org.apache.ivy.core.module.descriptor.Artifact;
import org.apache.ivy.core.module.descriptor.DefaultArtifact;
Expand All @@ -38,6 +35,7 @@
import org.apache.ivy.core.report.DownloadStatus;
import org.apache.ivy.core.report.MetadataArtifactDownloadReport;
import org.apache.ivy.util.DateUtil;
import org.apache.ivy.util.XMLHelper;
import org.apache.ivy.util.extendable.ExtendableItemHelper;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
Expand Down Expand Up @@ -242,8 +240,7 @@ private int getMaxPos() {
}

public void parse() throws Exception {
SAXParser saxParser = SAXParserFactory.newInstance().newSAXParser();
saxParser.parse(report, new XmlReportParserHandler());
XMLHelper.parse(report.toURI().toURL(), null, new XmlReportParserHandler());
}

private static boolean parseBoolean(String str) {
Expand Down
Loading

6 comments on commit 2be17bc

@apourche
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@bodewig
Thanks for these fixe
src/java/org/apache/ivy/util/XMLHelper.java l93 :
Unfortunately, this affectation is a problem when XMLreader don't use this property (ex : javax.xml.parsers.SAXParserFactory) : this throws the exception org.xml.sax.SAXNotRecognizedException.
You should write this code like trySetAttribute l402

@bodewig
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Many thanks @apourche I've created https://issues.apache.org/jira/browse/IVY-1647 for this. I almost missed this comment on a commit, though, so please use the mailing list or open an issue if you find additional problems.

@bodewig
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

see 5d69c9a

@apourche
Copy link

@apourche apourche commented on 2be17bc Sep 18, 2023 via email

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@apourche
Copy link

@apourche apourche commented on 2be17bc Jan 14, 2024 via email

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@nskvortsov
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IntelliJ IDEA also will benefit from the release 2.5.3

Please sign in to comment.