diff --git a/maven-compat/src/main/java/org/apache/maven/artifact/repository/metadata/DefaultRepositoryMetadataManager.java b/maven-compat/src/main/java/org/apache/maven/artifact/repository/metadata/DefaultRepositoryMetadataManager.java index 54ca354e2b4b..14f73e12907d 100644 --- a/maven-compat/src/main/java/org/apache/maven/artifact/repository/metadata/DefaultRepositoryMetadataManager.java +++ b/maven-compat/src/main/java/org/apache/maven/artifact/repository/metadata/DefaultRepositoryMetadataManager.java @@ -33,8 +33,8 @@ import org.apache.maven.artifact.repository.ArtifactRepositoryPolicy; import org.apache.maven.artifact.repository.DefaultRepositoryRequest; import org.apache.maven.artifact.repository.RepositoryRequest; -import org.apache.maven.artifact.repository.metadata.io.xpp3.MetadataXpp3Reader; import org.apache.maven.artifact.repository.metadata.io.xpp3.MetadataXpp3Writer; +import org.apache.maven.repository.internal.metadata.ValidatingMetadataXpp3Reader; import org.apache.maven.repository.legacy.UpdateCheckManager; import org.apache.maven.repository.legacy.WagonManager; import org.apache.maven.wagon.ResourceDoesNotExistException; @@ -270,7 +270,7 @@ protected Metadata readMetadata(File mappingFile) throws RepositoryMetadataReadE Metadata result; try (Reader reader = ReaderFactory.newXmlReader(mappingFile)) { - MetadataXpp3Reader mappingReader = new MetadataXpp3Reader(); + ValidatingMetadataXpp3Reader mappingReader = new ValidatingMetadataXpp3Reader(); result = mappingReader.read(reader, false); } catch (FileNotFoundException e) { diff --git a/maven-core/src/main/java/org/apache/maven/artifact/repository/metadata/AbstractRepositoryMetadata.java b/maven-core/src/main/java/org/apache/maven/artifact/repository/metadata/AbstractRepositoryMetadata.java index 82bd9fea5a75..2b97665f5d19 100644 --- a/maven-core/src/main/java/org/apache/maven/artifact/repository/metadata/AbstractRepositoryMetadata.java +++ b/maven-core/src/main/java/org/apache/maven/artifact/repository/metadata/AbstractRepositoryMetadata.java @@ -27,8 +27,8 @@ import org.apache.maven.artifact.metadata.ArtifactMetadata; import org.apache.maven.artifact.repository.ArtifactRepository; import org.apache.maven.artifact.repository.ArtifactRepositoryPolicy; -import org.apache.maven.artifact.repository.metadata.io.xpp3.MetadataXpp3Reader; import org.apache.maven.artifact.repository.metadata.io.xpp3.MetadataXpp3Writer; +import org.apache.maven.repository.internal.metadata.ValidatingMetadataXpp3Reader; import org.codehaus.plexus.util.ReaderFactory; import org.codehaus.plexus.util.WriterFactory; import org.codehaus.plexus.util.xml.pull.XmlPullParserException; @@ -64,7 +64,7 @@ public void storeInLocalRepository(ArtifactRepository localRepository, ArtifactR protected void updateRepositoryMetadata(ArtifactRepository localRepository, ArtifactRepository remoteRepository) throws IOException, XmlPullParserException { - MetadataXpp3Reader mappingReader = new MetadataXpp3Reader(); + ValidatingMetadataXpp3Reader mappingReader = new ValidatingMetadataXpp3Reader(); Metadata metadata = null; diff --git a/maven-core/src/main/java/org/apache/maven/artifact/repository/metadata/io/DefaultMetadataReader.java b/maven-core/src/main/java/org/apache/maven/artifact/repository/metadata/io/DefaultMetadataReader.java index 9ecc7c6dac74..f1ad14f5227a 100644 --- a/maven-core/src/main/java/org/apache/maven/artifact/repository/metadata/io/DefaultMetadataReader.java +++ b/maven-core/src/main/java/org/apache/maven/artifact/repository/metadata/io/DefaultMetadataReader.java @@ -29,7 +29,7 @@ import java.util.Objects; import org.apache.maven.artifact.repository.metadata.Metadata; -import org.apache.maven.artifact.repository.metadata.io.xpp3.MetadataXpp3Reader; +import org.apache.maven.repository.internal.metadata.ValidatingMetadataXpp3Reader; import org.codehaus.plexus.util.ReaderFactory; import org.codehaus.plexus.util.xml.pull.XmlPullParserException; @@ -54,7 +54,7 @@ public Metadata read(Reader input, Map options) throws IOException { Objects.requireNonNull(input, "input cannot be null"); try (Reader in = input) { - return new MetadataXpp3Reader().read(in, isStrict(options)); + return new ValidatingMetadataXpp3Reader().read(in, isStrict(options)); } catch (XmlPullParserException e) { throw new MetadataParseException(e.getMessage(), e.getLineNumber(), e.getColumnNumber(), e); } @@ -64,7 +64,7 @@ public Metadata read(InputStream input, Map options) throws IOExcepti Objects.requireNonNull(input, "input cannot be null"); try (InputStream in = input) { - return new MetadataXpp3Reader().read(in, isStrict(options)); + return new ValidatingMetadataXpp3Reader().read(in, isStrict(options)); } catch (XmlPullParserException e) { throw new MetadataParseException(e.getMessage(), e.getLineNumber(), e.getColumnNumber(), e); } diff --git a/maven-core/src/main/java/org/apache/maven/internal/aether/MavenValidator.java b/maven-core/src/main/java/org/apache/maven/internal/aether/MavenValidator.java new file mode 100644 index 000000000000..4369d18a55d8 --- /dev/null +++ b/maven-core/src/main/java/org/apache/maven/internal/aether/MavenValidator.java @@ -0,0 +1,91 @@ +/* + * 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.maven.internal.aether; + +import org.eclipse.aether.artifact.Artifact; +import org.eclipse.aether.graph.Dependency; +import org.eclipse.aether.metadata.Metadata; +import org.eclipse.aether.repository.LocalRepository; +import org.eclipse.aether.spi.validator.Validator; +import org.eclipse.aether.util.PathUtils; + +/** + * Simplest Maven specific validator that is meant to prevent un-interpolated + * elements enter resolver; if it does, is most likely some bug. + *

+ * Note: {@link org.eclipse.aether.repository.RemoteRepository} is not validated here, + * see GH-7398 for details, + * and to keep very same behavior as Maven 3 did so far. If you want more, upgrade to Maven 4 ;) + */ +public class MavenValidator implements Validator { + protected boolean containsPlaceholder(String value) { + return value != null && value.contains("${"); + } + + @Override + public void validateArtifact(Artifact artifact) throws IllegalArgumentException { + if (containsPlaceholder(artifact.getGroupId()) + || containsPlaceholder(artifact.getArtifactId()) + || containsPlaceholder(artifact.getVersion()) + || containsPlaceholder(artifact.getClassifier()) + || containsPlaceholder(artifact.getExtension())) { + throw new IllegalArgumentException("Not fully interpolated artifact " + artifact); + } + PathUtils.validateArtifactComponents(artifact); + } + + @Override + public void validateMetadata(Metadata metadata) throws IllegalArgumentException { + if (containsPlaceholder(metadata.getGroupId()) + || containsPlaceholder(metadata.getArtifactId()) + || containsPlaceholder(metadata.getVersion()) + || containsPlaceholder(metadata.getType())) { + throw new IllegalArgumentException("Not fully interpolated metadata " + metadata); + } + PathUtils.validateMetadataComponents(metadata); + } + + @Override + public void validateDependency(Dependency dependency) throws IllegalArgumentException { + Artifact artifact = dependency.getArtifact(); + if (containsPlaceholder(artifact.getGroupId()) + || containsPlaceholder(artifact.getArtifactId()) + || containsPlaceholder(artifact.getVersion()) + || containsPlaceholder(artifact.getClassifier()) + || containsPlaceholder(artifact.getExtension()) + || containsPlaceholder(dependency.getScope()) + || dependency.getExclusions().stream() + .anyMatch(e -> containsPlaceholder(e.getGroupId()) + || containsPlaceholder(e.getArtifactId()) + || containsPlaceholder(e.getClassifier()) + || containsPlaceholder(e.getExtension()))) { + throw new IllegalArgumentException("Not fully interpolated dependency " + dependency); + } + PathUtils.validateArtifactComponents(artifact); + } + + @Override + public void validateLocalRepository(LocalRepository localRepository) throws IllegalArgumentException { + if (containsPlaceholder(localRepository.getBasePath().toString()) + || containsPlaceholder(localRepository.getContentType()) + || containsPlaceholder(localRepository.getId())) { + throw new IllegalArgumentException("Not fully interpolated local repository " + localRepository); + } + } +} diff --git a/maven-core/src/main/java/org/apache/maven/internal/aether/MavenValidatorFactory.java b/maven-core/src/main/java/org/apache/maven/internal/aether/MavenValidatorFactory.java new file mode 100644 index 000000000000..83a48104c32c --- /dev/null +++ b/maven-core/src/main/java/org/apache/maven/internal/aether/MavenValidatorFactory.java @@ -0,0 +1,37 @@ +/* + * 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.maven.internal.aether; + +import javax.inject.Named; +import javax.inject.Singleton; + +import org.eclipse.aether.RepositorySystemSession; +import org.eclipse.aether.spi.validator.Validator; +import org.eclipse.aether.spi.validator.ValidatorFactory; + +@Named +@Singleton +public class MavenValidatorFactory implements ValidatorFactory { + private final MavenValidator instance = new MavenValidator(); + + @Override + public Validator newInstance(RepositorySystemSession repositorySystemSession) { + return instance; + } +} diff --git a/maven-resolver-provider/src/main/java/org/apache/maven/repository/internal/DefaultVersionRangeResolver.java b/maven-resolver-provider/src/main/java/org/apache/maven/repository/internal/DefaultVersionRangeResolver.java index 23b7bc84b502..9542bece8977 100644 --- a/maven-resolver-provider/src/main/java/org/apache/maven/repository/internal/DefaultVersionRangeResolver.java +++ b/maven-resolver-provider/src/main/java/org/apache/maven/repository/internal/DefaultVersionRangeResolver.java @@ -34,7 +34,7 @@ import org.apache.maven.artifact.ArtifactUtils; import org.apache.maven.artifact.repository.metadata.Versioning; -import org.apache.maven.artifact.repository.metadata.io.xpp3.MetadataXpp3Reader; +import org.apache.maven.repository.internal.metadata.ValidatingMetadataXpp3Reader; import org.eclipse.aether.RepositoryEvent; import org.eclipse.aether.RepositoryEvent.EventType; import org.eclipse.aether.RepositorySystemSession; @@ -274,8 +274,9 @@ private Versioning readVersions( if (metadata.getFile() != null && metadata.getFile().exists()) { try (InputStream in = new FileInputStream(metadata.getFile())) { - versioning = - new MetadataXpp3Reader().read(in, false).getVersioning(); + versioning = new ValidatingMetadataXpp3Reader() + .read(in, false) + .getVersioning(); } } } diff --git a/maven-resolver-provider/src/main/java/org/apache/maven/repository/internal/DefaultVersionResolver.java b/maven-resolver-provider/src/main/java/org/apache/maven/repository/internal/DefaultVersionResolver.java index cccbd9f5ff92..b3b5fe70cac6 100644 --- a/maven-resolver-provider/src/main/java/org/apache/maven/repository/internal/DefaultVersionResolver.java +++ b/maven-resolver-provider/src/main/java/org/apache/maven/repository/internal/DefaultVersionResolver.java @@ -36,7 +36,7 @@ import org.apache.maven.artifact.repository.metadata.Snapshot; import org.apache.maven.artifact.repository.metadata.SnapshotVersion; import org.apache.maven.artifact.repository.metadata.Versioning; -import org.apache.maven.artifact.repository.metadata.io.xpp3.MetadataXpp3Reader; +import org.apache.maven.repository.internal.metadata.ValidatingMetadataXpp3Reader; import org.codehaus.plexus.util.StringUtils; import org.eclipse.aether.RepositoryCache; import org.eclipse.aether.RepositoryEvent; @@ -278,8 +278,9 @@ private Versioning readVersions( if (metadata.getFile() != null && metadata.getFile().exists()) { try (InputStream in = new FileInputStream(metadata.getFile())) { - versioning = - new MetadataXpp3Reader().read(in, false).getVersioning(); + versioning = new ValidatingMetadataXpp3Reader() + .read(in, false) + .getVersioning(); /* NOTE: Users occasionally misuse the id "local" for remote repos which screws up the metadata diff --git a/maven-resolver-provider/src/main/java/org/apache/maven/repository/internal/MavenMetadata.java b/maven-resolver-provider/src/main/java/org/apache/maven/repository/internal/MavenMetadata.java index 6588b32fa526..12cc79bb086a 100644 --- a/maven-resolver-provider/src/main/java/org/apache/maven/repository/internal/MavenMetadata.java +++ b/maven-resolver-provider/src/main/java/org/apache/maven/repository/internal/MavenMetadata.java @@ -28,8 +28,8 @@ import java.util.Map; import org.apache.maven.artifact.repository.metadata.Metadata; -import org.apache.maven.artifact.repository.metadata.io.xpp3.MetadataXpp3Reader; import org.apache.maven.artifact.repository.metadata.io.xpp3.MetadataXpp3Writer; +import org.apache.maven.repository.internal.metadata.ValidatingMetadataXpp3Reader; import org.codehaus.plexus.util.ReaderFactory; import org.codehaus.plexus.util.WriterFactory; import org.codehaus.plexus.util.xml.pull.XmlPullParserException; @@ -99,7 +99,7 @@ static Metadata read(File metadataFile) throws RepositoryException { } try (Reader reader = ReaderFactory.newXmlReader(metadataFile)) { - return new MetadataXpp3Reader().read(reader, false); + return new ValidatingMetadataXpp3Reader().read(reader, false); } catch (IOException e) { throw new RepositoryException("Could not read metadata " + metadataFile + ": " + e.getMessage(), e); } catch (XmlPullParserException e) { diff --git a/maven-resolver-provider/src/main/java/org/apache/maven/repository/internal/metadata/ValidatingMetadataXpp3Reader.java b/maven-resolver-provider/src/main/java/org/apache/maven/repository/internal/metadata/ValidatingMetadataXpp3Reader.java new file mode 100644 index 000000000000..338befe1be1b --- /dev/null +++ b/maven-resolver-provider/src/main/java/org/apache/maven/repository/internal/metadata/ValidatingMetadataXpp3Reader.java @@ -0,0 +1,75 @@ +/* + * 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.maven.repository.internal.metadata; + +import java.io.IOException; +import java.io.InputStream; +import java.io.Reader; + +import org.apache.maven.artifact.repository.metadata.Metadata; +import org.apache.maven.artifact.repository.metadata.Versioning; +import org.apache.maven.artifact.repository.metadata.io.xpp3.MetadataXpp3Reader; +import org.codehaus.plexus.util.xml.pull.XmlPullParserException; +import org.eclipse.aether.util.PathUtils; + +/** + * Validating metadata reader. + * + * @since 3.10.0 + */ +public final class ValidatingMetadataXpp3Reader { + private final MetadataXpp3Reader mr = new MetadataXpp3Reader(); + + /** + * Delegates to {@link MetadataXpp3Reader#read(Reader, boolean)} + */ + public Metadata read(Reader reader, boolean strict) throws IOException, XmlPullParserException { + return validate(mr.read(reader, strict)); + } + + /** + * Delegates to {@link MetadataXpp3Reader#read(InputStream, boolean)} + */ + public Metadata read(InputStream in, boolean strict) throws IOException, XmlPullParserException { + return validate(mr.read(in, strict)); + } + + /** + * Validates {@link Metadata}. + */ + public static Metadata validate(Metadata metadata) { + if (metadata != null) { + PathUtils.validatePathComponent(metadata.getVersion(), "version"); + Versioning versioning = metadata.getVersioning(); + if (versioning != null) { + PathUtils.validatePathComponent(versioning.getLatest(), "versioning/latest"); + PathUtils.validatePathComponent(versioning.getRelease(), "versioning/release"); + for (int i = 0; i < versioning.getVersions().size(); i++) { + PathUtils.validatePathComponent(versioning.getVersions().get(i), "versioning/versions[" + i + "]"); + } + for (int i = 0; i < versioning.getSnapshotVersions().size(); i++) { + PathUtils.validatePathComponent( + versioning.getSnapshotVersions().get(i).getVersion(), + "versioning/snapshotVersions[" + i + "]/version"); + } + } + } + return metadata; + } +} diff --git a/pom.xml b/pom.xml index 07fd63400b37..88367df3db9b 100644 --- a/pom.xml +++ b/pom.xml @@ -144,7 +144,7 @@ under the License. 2.0 2.0 1.4.0 - 2.0.20 + 2.0.21-SNAPSHOT 2.0.18 2.12.0 2.0.9