Skip to content

Commit

Permalink
Unsign modified dependency JARs when filtering
Browse files Browse the repository at this point in the history
Add test for JarResultBuildStep#filterJarFile

(cherry picked from commit 7e229d4)
  • Loading branch information
gastaldi authored and gsmet committed Apr 17, 2024
1 parent 40abe48 commit 1485e97
Show file tree
Hide file tree
Showing 3 changed files with 99 additions and 23 deletions.
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 @@ -931,7 +930,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 @@ -1125,7 +1124,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 @@ -1240,16 +1239,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();
}
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 @@ -1258,17 +1267,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 @@ -1612,12 +1634,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 {
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();
}
}

}

0 comments on commit 1485e97

Please sign in to comment.