Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Un-sign modified dependency JARs when filtering #40001

Merged
merged 1 commit into from
Apr 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions core/deployment/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,30 @@

<build>
<plugins>
<plugin>
<artifactId>maven-dependency-plugin</artifactId>
<executions>
<execution>
<id>download-signed-jar</id>
<phase>generate-test-resources</phase>
<goals>
<goal>copy</goal>
</goals>
<configuration>
<artifactItems>
<artifactItem>
<groupId>org.eclipse.jgit</groupId>
<artifactId>org.eclipse.jgit.ssh.apache</artifactId>
<version>6.9.0.202403050737-r</version>
<type>jar</type>
<destFileName>signed.jar</destFileName>
</artifactItem>
</artifactItems>
<outputDirectory>${project.build.testOutputDirectory}</outputDirectory>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
import java.io.BufferedInputStream;
import java.io.BufferedWriter;
import java.io.ByteArrayOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectOutputStream;
Expand Down Expand Up @@ -42,12 +41,12 @@
import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.jar.Attributes;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.jar.JarOutputStream;
import java.util.jar.Manifest;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipOutputStream;

import org.jboss.logging.Logger;

Expand Down Expand Up @@ -92,14 +91,14 @@

/**
* This build step builds both the thin jars and uber jars.
*
* <p>
* The way this is built is a bit convoluted. In general, we only want a single one built,
* as determined by the {@link PackageConfig} (unless the config explicitly asks for both of them)
*
* <p>
* However, we still need an extension to be able to ask for a specific one of these despite the config,
* e.g. if a serverless environment needs an uberjar to build its deployment package then we need
* to be able to provide this.
*
* <p>
* To enable this we have two build steps that strongly produce the respective artifact type build
* items, but not a {@link ArtifactResultBuildItem}. We then
* have another two build steps that only run if they are configured to consume these explicit
Expand Down Expand Up @@ -929,7 +928,7 @@ private void copyDependency(Set<ArtifactKey> parentFirstArtifacts, OutputTargetB
} else {
// we copy jars for which we remove entries to the same directory
// which seems a bit odd to me
filterZipFile(resolvedDep, targetPath, removedFromThisArchive);
filterJarFile(resolvedDep, targetPath, removedFromThisArchive);
}
}
}
Expand Down Expand Up @@ -1123,7 +1122,7 @@ private void copyLibraryJars(FileSystem runnerZipFs, OutputTargetBuildItem outpu
+ resolvedDep.getFileName();
final Path targetPath = libDir.resolve(fileName);
classPath.append(" lib/").append(fileName);
filterZipFile(resolvedDep, targetPath, transformedFromThisArchive);
filterJarFile(resolvedDep, targetPath, transformedFromThisArchive);
}
} else {
// This case can happen when we are building a jar from inside the Quarkus repository
Expand Down Expand Up @@ -1237,16 +1236,26 @@ private void handleParent(FileSystem runnerZipFs, String fileName, Map<String, S
}
}

private void filterZipFile(Path resolvedDep, Path targetPath, Set<String> transformedFromThisArchive) {

static void filterJarFile(Path resolvedDep, Path targetPath, Set<String> transformedFromThisArchive) {
try {
byte[] buffer = new byte[10000];
try (ZipFile in = new ZipFile(resolvedDep.toFile())) {
try (ZipOutputStream out = new ZipOutputStream(new FileOutputStream(targetPath.toFile()))) {
Enumeration<? extends ZipEntry> entries = in.entries();
try (JarFile in = new JarFile(resolvedDep.toFile(), false)) {
Manifest manifest = in.getManifest();
if (manifest != null) {
// Remove signature entries
manifest.getEntries().clear();
} else {
manifest = new Manifest();
}
gastaldi marked this conversation as resolved.
Show resolved Hide resolved
try (JarOutputStream out = new JarOutputStream(Files.newOutputStream(targetPath), manifest)) {
Enumeration<JarEntry> entries = in.entries();
while (entries.hasMoreElements()) {
ZipEntry entry = entries.nextElement();
if (!transformedFromThisArchive.contains(entry.getName())) {
JarEntry entry = entries.nextElement();
String entryName = entry.getName();
if (!transformedFromThisArchive.contains(entryName)
&& !entryName.equals(JarFile.MANIFEST_NAME)
&& !entryName.equals("META-INF/INDEX.LIST")
&& !isSignatureFile(entryName)) {
entry.setCompressedSize(-1);
out.putNextEntry(entry);
try (InputStream inStream = in.getInputStream(entry)) {
Expand All @@ -1255,17 +1264,30 @@ private void filterZipFile(Path resolvedDep, Path targetPath, Set<String> transf
out.write(buffer, 0, r);
}
}
} else {
log.debugf("Removed %s from %s", entryName, resolvedDep);
}
}
}
// let's make sure we keep the original timestamp
Files.setLastModifiedTime(targetPath, Files.getLastModifiedTime(resolvedDep));
}
} catch (IOException e) {
throw new RuntimeException(e);
throw new UncheckedIOException(e);
}
}

private static boolean isSignatureFile(String entry) {
entry = entry.toUpperCase();
if (entry.startsWith("META-INF/") && entry.indexOf('/', "META-INF/".length()) == -1) {
return entry.endsWith(".SF")
|| entry.endsWith(".DSA")
|| entry.endsWith(".RSA")
|| entry.endsWith(".EC");
}
return false;
}

/**
* Manifest generation is quite simple : we just have to push some attributes in manifest.
* However, it gets a little more complex if the manifest preexists.
Expand Down Expand Up @@ -1591,12 +1613,8 @@ public boolean downloadIfNecessary() {
"https://repo.maven.apache.org/maven2/org/vineflower/vineflower/%s/vineflower-%s.jar",
context.versionStr, context.versionStr);
try (BufferedInputStream in = new BufferedInputStream(new URL(downloadURL).openStream());
FileOutputStream fileOutputStream = new FileOutputStream(decompilerJar.toFile())) {
byte[] dataBuffer = new byte[1024];
int bytesRead;
while ((bytesRead = in.read(dataBuffer, 0, 1024)) != -1) {
fileOutputStream.write(dataBuffer, 0, bytesRead);
}
OutputStream fileOutputStream = Files.newOutputStream(decompilerJar)) {
in.transferTo(fileOutputStream);
return true;
} catch (IOException e) {
log.error("Unable to download Vineflower from " + downloadURL, e);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package io.quarkus.deployment.pkg.steps;

import static org.assertj.core.api.Assertions.assertThat;

import java.nio.file.Path;
import java.util.Set;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.jar.Manifest;

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;

/**
* Test for {@link JarResultBuildStep}
*/
class JarResultBuildStepTest {

@Test
void should_unsign_jar_when_filtered(@TempDir Path tempDir) throws Exception {
gastaldi marked this conversation as resolved.
Show resolved Hide resolved
Path signedJarFilePath = Path.of(getClass().getClassLoader().getResource("signed.jar").toURI());
Path jarFilePath = tempDir.resolve("unsigned.jar");
JarResultBuildStep.filterJarFile(signedJarFilePath, jarFilePath,
Set.of("org/eclipse/jgit/transport/sshd/SshdSessionFactory.class"));
try (JarFile jarFile = new JarFile(jarFilePath.toFile())) {
assertThat(jarFile.stream().map(JarEntry::getName)).doesNotContain("META-INF/ECLIPSE_.RSA", "META-INF/ECLIPSE_.SF");
// Check that the manifest is still present
Manifest manifest = jarFile.getManifest();
assertThat(manifest.getMainAttributes()).isNotEmpty();
assertThat(manifest.getEntries()).isEmpty();
}
}

}
Loading