From fb466bce5278ce2ee8571382b79e22585cb74ce8 Mon Sep 17 00:00:00 2001 From: "Dmitriy.Panov" Date: Thu, 1 Sep 2022 14:18:20 +0200 Subject: [PATCH] maven artifacts resolution: reading archive entries number as a validation check GitOrigin-RevId: d8cbc1e5cb8501534524ea19c17e68b35a83bfaf --- .../aether/ArtifactRepositoryManager.java | 2 +- .../aether/StrictLocalRepositoryManager.java | 99 +++++++++++++++++++ .../aether/ArtifactRepositoryManagerTest.java | 52 ++++++++++ 3 files changed, 152 insertions(+), 1 deletion(-) create mode 100644 aether-dependency-resolver/src/org/jetbrains/idea/maven/aether/StrictLocalRepositoryManager.java diff --git a/aether-dependency-resolver/src/org/jetbrains/idea/maven/aether/ArtifactRepositoryManager.java b/aether-dependency-resolver/src/org/jetbrains/idea/maven/aether/ArtifactRepositoryManager.java index 1706dc90cae2..420118f31d96 100644 --- a/aether-dependency-resolver/src/org/jetbrains/idea/maven/aether/ArtifactRepositoryManager.java +++ b/aether-dependency-resolver/src/org/jetbrains/idea/maven/aether/ArtifactRepositoryManager.java @@ -161,7 +161,7 @@ public final class ArtifactRepositoryManager { } // setup session here - session.setLocalRepositoryManager(ourSystem.newLocalRepositoryManager(session, new LocalRepository(localRepositoryPath))); + session.setLocalRepositoryManager(new StrictLocalRepositoryManager(ourSystem.newLocalRepositoryManager(session, new LocalRepository(localRepositoryPath)))); session.setProxySelector(ourProxySelector); session.setOffline(offline); session.setReadOnly(); diff --git a/aether-dependency-resolver/src/org/jetbrains/idea/maven/aether/StrictLocalRepositoryManager.java b/aether-dependency-resolver/src/org/jetbrains/idea/maven/aether/StrictLocalRepositoryManager.java new file mode 100644 index 000000000000..1cfc60b9c828 --- /dev/null +++ b/aether-dependency-resolver/src/org/jetbrains/idea/maven/aether/StrictLocalRepositoryManager.java @@ -0,0 +1,99 @@ +// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +package org.jetbrains.idea.maven.aether; + +import org.eclipse.aether.RepositorySystemSession; +import org.eclipse.aether.artifact.Artifact; +import org.eclipse.aether.metadata.Metadata; +import org.eclipse.aether.repository.*; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.nio.file.Files; +import java.util.zip.ZipFile; + +class StrictLocalRepositoryManager implements LocalRepositoryManager { + private static final Logger LOG = LoggerFactory.getLogger(StrictLocalRepositoryManager.class); + private static final boolean STRICT_VALIDATION = "true".equals(System.getProperty("org.jetbrains.idea.maven.aether.strictValidation")); + private final LocalRepositoryManager delegate; + + StrictLocalRepositoryManager(LocalRepositoryManager delegate) { + this.delegate = delegate; + } + + @Override + public LocalArtifactResult find(RepositorySystemSession session, LocalArtifactRequest request) { + var result = delegate.find(session, request); + // TODO: to be revised after IDEA-269182 is implemented + if (STRICT_VALIDATION && + result.isAvailable() && + (result.getFile().getName().endsWith(".jar") || + result.getFile().getName().endsWith(".zip"))) { + validateArchive(result); + } + return result; + } + + private static void validateArchive(LocalArtifactResult artifact) { + long entriesCount; + var file = artifact.getFile(); + try (var zip = new ZipFile(file)) { + entriesCount = zip.size(); + } + catch (IOException e) { + LOG.warn("Unable to read a number of entries in " + file, e); + entriesCount = 0; + } + if (entriesCount <= 0) { + LOG.warn(file + " is probably corrupted, deleting"); + try { + Files.deleteIfExists(file.toPath()); + } + catch (IOException e) { + throw new RuntimeException("Unable to delete " + file, e); + } + artifact.setFile(null); + artifact.setAvailable(false); + } + } + + @Override + public LocalRepository getRepository() { + return delegate.getRepository(); + } + + @Override + public String getPathForLocalArtifact(Artifact artifact) { + return delegate.getPathForLocalArtifact(artifact); + } + + @Override + public String getPathForRemoteArtifact(Artifact artifact, RemoteRepository repository, String context) { + return delegate.getPathForRemoteArtifact(artifact, repository, context); + } + + @Override + public String getPathForLocalMetadata(Metadata metadata) { + return delegate.getPathForLocalMetadata(metadata); + } + + @Override + public String getPathForRemoteMetadata(Metadata metadata, RemoteRepository repository, String context) { + return delegate.getPathForRemoteMetadata(metadata, repository, context); + } + + @Override + public void add(RepositorySystemSession session, LocalArtifactRegistration request) { + delegate.add(session, request); + } + + @Override + public LocalMetadataResult find(RepositorySystemSession session, LocalMetadataRequest request) { + return delegate.find(session, request); + } + + @Override + public void add(RepositorySystemSession session, LocalMetadataRegistration request) { + delegate.add(session, request); + } +} diff --git a/aether-dependency-resolver/testSrc/org/jetbrains/idea/maven/aether/ArtifactRepositoryManagerTest.java b/aether-dependency-resolver/testSrc/org/jetbrains/idea/maven/aether/ArtifactRepositoryManagerTest.java index 278c490707f1..681cbf21b47b 100644 --- a/aether-dependency-resolver/testSrc/org/jetbrains/idea/maven/aether/ArtifactRepositoryManagerTest.java +++ b/aether-dependency-resolver/testSrc/org/jetbrains/idea/maven/aether/ArtifactRepositoryManagerTest.java @@ -31,11 +31,13 @@ import java.util.Collections; import java.util.List; import java.util.stream.Collectors; import java.util.stream.Stream; +import java.util.zip.ZipFile; public class ArtifactRepositoryManagerTest extends UsefulTestCase { private static final RemoteRepository mavenLocal; static { + System.setProperty("org.jetbrains.idea.maven.aether.strictValidation", "true"); File mavenLocalDir = new File(SystemProperties.getUserHome(), ".m2/repository"); mavenLocal = new RemoteRepository .Builder("mavenLocal", "default", "file://" + mavenLocalDir.getAbsolutePath()) @@ -114,6 +116,56 @@ public class ArtifactRepositoryManagerTest extends UsefulTestCase { } } + private Path resolveSomeSingleJar() throws Exception { + myRepositoryManager.resolveDependency( + "org.jetbrains", "annotations", "20.1.0", + false, Collections.emptyList() + ); + try (var stream = Files.walk(localRepository.toPath())) { + var resolvedJars = stream.filter(file -> file.getFileName().toString().endsWith(".jar")).collect(Collectors.toList()); + assertSize(1, resolvedJars); + var resolvedJar = resolvedJars.get(0); + try (var zip = new ZipFile(resolvedJar.toFile())) { + assert zip.size() > 0; + } + return resolvedJar; + } + } + + public void testResolutionForEmptyJar() throws Exception { + var resolvedJar = resolveSomeSingleJar(); + Files.delete(resolvedJar); + Files.createFile(resolvedJar); + assert Files.size(resolvedJar) == 0; + resolveSomeSingleJar(); + } + + public void testResolutionForSymbolicLinkToEmptyJar() throws Exception { + var resolvedJar = resolveSomeSingleJar(); + Files.delete(resolvedJar); + var brokenTarget = Files.createTempFile("empty", ".jar"); + assert Files.size(brokenTarget) == 0; + Files.createSymbolicLink(resolvedJar, brokenTarget); + resolveSomeSingleJar(); + } + + public void testResolutionForMalformedJar() throws Exception { + var resolvedJar = resolveSomeSingleJar(); + Files.delete(resolvedJar); + Files.createFile(resolvedJar); + Files.writeString(resolvedJar, "malformed jar content"); + resolveSomeSingleJar(); + } + + public void testResolutionForSymbolicLinkToMalformedJar() throws Exception { + var resolvedJar = resolveSomeSingleJar(); + Files.delete(resolvedJar); + var brokenTarget = Files.createTempFile("malformed", ".jar"); + Files.writeString(brokenTarget, "malformed jar content"); + Files.createSymbolicLink(resolvedJar, brokenTarget); + resolveSomeSingleJar(); + } + private static void assertCoordinates(Artifact artifact, String groupId, String artifactId, String version) { assertEquals(groupId, artifact.getGroupId()); assertEquals(artifactId, artifact.getArtifactId());