Skip to content

Commit

Permalink
Remove signatures from wrapped source bundles
Browse files Browse the repository at this point in the history
Currently when the source bundle is signed, wrapping it into a source
bundle will break the signature and make the jar invalid.

This now removes the signature from source bundles as they are
transformed.

(cherry picked from commit b447a1e)
  • Loading branch information
laeubi committed Feb 9, 2025
1 parent c4e3e0c commit 4329b3b
Show file tree
Hide file tree
Showing 4 changed files with 105 additions and 45 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
package org.eclipse.m2e.pde.target.shared;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
Expand All @@ -27,10 +29,16 @@
import java.util.Set;
import java.util.function.Function;
import java.util.jar.Attributes;
import java.util.jar.Attributes.Name;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.jar.JarInputStream;
import java.util.jar.JarOutputStream;
import java.util.jar.Manifest;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import java.util.zip.ZipEntry;

import org.apache.commons.codec.digest.DigestUtils;
import org.apache.maven.model.Model;
Expand Down Expand Up @@ -70,6 +78,9 @@
* </ul>
*/
public class MavenBundleWrapper {

public static final String ECLIPSE_SOURCE_BUNDLE_HEADER = "Eclipse-SourceBundle";

private MavenBundleWrapper() {
}

Expand Down Expand Up @@ -318,4 +329,49 @@ public static boolean isOutdated(Path cacheFile, Path sourceFile) throws IOExcep
}
return true;
}

private static boolean isExcludedFromWrapping(String name) {
return name.equals(JarFile.MANIFEST_NAME) || name.startsWith("META-INF/SIG-") || name.startsWith("META-INF/")
&& (name.endsWith(".SF") || name.endsWith(".RSA") || name.endsWith(".DSA"));
}

public static void addSourceBundleMetadata(Manifest manifest, String symbolicName, String version) {

Attributes attr = manifest.getMainAttributes();
if (attr.isEmpty()) {
attr.put(Name.MANIFEST_VERSION, "1.0");
}
attr.putValue(ECLIPSE_SOURCE_BUNDLE_HEADER, symbolicName + ";version=\"" + version + "\";roots:=\".\"");
attr.putValue(Constants.BUNDLE_MANIFESTVERSION, "2");
attr.putValue(Constants.BUNDLE_NAME, "Source Bundle for " + symbolicName + ":" + version);
attr.putValue(Constants.BUNDLE_SYMBOLICNAME, getSourceBundleName(symbolicName));
attr.putValue(Constants.BUNDLE_VERSION, version);
}

public static String getSourceBundleName(String symbolicName) {
return symbolicName + ".source";
}

public static void transferJarEntries(File source, Manifest manifest, File target) throws IOException {
Map<String, Attributes> manifestEntries = manifest.getEntries();
if (manifestEntries != null) {
// need to clear out signature infos
manifestEntries.clear();
}
try (var output = new JarOutputStream(new FileOutputStream(target), manifest);
var input = new JarInputStream(new FileInputStream(source));) {
for (JarEntry entry; (entry = input.getNextJarEntry()) != null;) {
if (MavenBundleWrapper.isExcludedFromWrapping(entry.getName())) {
// Exclude manifest and signatures
continue;
}
output.putNextEntry(new ZipEntry(entry.getName()));
input.transferTo(output);
}
}
}

public static boolean isValidSourceManifest(Manifest manifest) {
return manifest != null && manifest.getMainAttributes().getValue(ECLIPSE_SOURCE_BUNDLE_HEADER) != null;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,10 @@
*******************************************************************************/
package org.eclipse.tycho.core.resolver;

import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
Expand All @@ -29,14 +25,9 @@
import java.util.Properties;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.jar.Attributes;
import java.util.jar.Attributes.Name;
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.zip.ZipEntry;

import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.TransformerException;
Expand Down Expand Up @@ -94,13 +85,11 @@
import org.eclipse.tycho.targetplatform.TargetDefinitionContent;
import org.eclipse.tycho.targetplatform.TargetDefinitionResolutionException;
import org.osgi.framework.BundleException;
import org.osgi.framework.Constants;
import org.w3c.dom.Element;
import org.xml.sax.SAXException;

public class MavenTargetDefinitionContent implements TargetDefinitionContent {
private static final String POM_PACKAGING_TYPE = "pom";
public static final String ECLIPSE_SOURCE_BUNDLE_HEADER = "Eclipse-SourceBundle";
private final Map<IArtifactDescriptor, IInstallableUnit> repositoryContent = new HashMap<>();
private SupplierMetadataRepository metadataRepository;
private FileArtifactRepository artifactRepository;
Expand Down Expand Up @@ -293,7 +282,7 @@ public MavenTargetDefinitionContent(MavenGAVLocation location, MavenDependencies
manifest = Objects.requireNonNullElseGet(jar.getManifest(), Manifest::new);
}
IInstallableUnit unit;
if (isValidSourceManifest(manifest)) {
if (MavenBundleWrapper.isValidSourceManifest(manifest)) {
unit = publish(BundlesAction.createBundleDescription(sourceFile), sourceFile,
sourceArtifact);
} else {
Expand Down Expand Up @@ -365,32 +354,8 @@ private IInstallableUnit generateSourceBundle(String symbolicName, String bundle

File tempFile = File.createTempFile("tycho_wrapped_source", ".jar");
tempFile.deleteOnExit();
Attributes attr = manifest.getMainAttributes();
if (attr.isEmpty()) {
attr.put(Name.MANIFEST_VERSION, "1.0");
}
attr.putValue(ECLIPSE_SOURCE_BUNDLE_HEADER, symbolicName + ";version=\"" + bundleVersion + "\";roots:=\".\"");
attr.putValue(Constants.BUNDLE_MANIFESTVERSION, "2");
attr.putValue(Constants.BUNDLE_NAME, "Source Bundle for " + symbolicName + ":" + bundleVersion);
attr.putValue(Constants.BUNDLE_SYMBOLICNAME, symbolicName + ".source");
attr.putValue(Constants.BUNDLE_VERSION, bundleVersion);
try (JarOutputStream stream = new JarOutputStream(new BufferedOutputStream(new FileOutputStream(tempFile)),
manifest)) {
try (JarFile jar = new JarFile(sourceFile)) {
Enumeration<JarEntry> entries = jar.entries();
while (entries.hasMoreElements()) {
JarEntry jarEntry = entries.nextElement();
if (jarEntry.getName().equals(JarFile.MANIFEST_NAME)) {
continue;
}
try (InputStream is = jar.getInputStream(jarEntry)) {
stream.putNextEntry(new ZipEntry(jarEntry.getName()));
is.transferTo(stream);
stream.closeEntry();
}
}
}
}
MavenBundleWrapper.addSourceBundleMetadata(manifest, symbolicName, bundleVersion);
MavenBundleWrapper.transferJarEntries(sourceFile, manifest, tempFile);
return publish(BundlesAction.createBundleDescription(tempFile), tempFile, sourceArtifact);

}
Expand Down Expand Up @@ -441,11 +406,4 @@ private static String getKey(Artifact artifact) {
return key;
}

private static boolean isValidSourceManifest(Manifest manifest) {
if (manifest != null) {
return manifest.getMainAttributes().getValue(ECLIPSE_SOURCE_BUNDLE_HEADER) != null;
}
return false;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,9 @@
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.jar.Attributes;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.jar.JarInputStream;
import java.util.stream.Collectors;

import org.eclipse.core.runtime.IStatus;
Expand Down Expand Up @@ -176,6 +178,17 @@ static void assertTargetBundles(ITargetLocation target, List<ExpectedBundle> exp
tb -> tb.getBundleInfo().getSymbolicName() + ":" + tb.getBundleInfo().getVersion());
}

static void assertValidSignature(TargetBundle targetBundle) throws IOException {
try (JarInputStream stream = new JarInputStream(
targetBundle.getBundleInfo().getLocation().toURL().openStream())) {
for (JarEntry entry = stream.getNextJarEntry(); entry != null; entry = stream.getNextJarEntry()) {
for (byte[] drain = new byte[4096]; stream.read(drain, 0, drain.length) != -1;) {
// nothing we just want to trigger the signature verification
}
}
}
}

// --- assertion utilities for Features in a target ---

static ExpectedFeature originalFeature(String id, String version, String groupArtifact,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,39 @@ public void testBadSymbolicName() throws Exception {
assertStatusOk(getTargetStatus(target));
}

@Test
public void testSourceWithSignature() throws Exception {
ITargetLocation target = resolveMavenTarget(
"""
<location includeDependencyDepth="none" includeDependencyScopes="compile" label="LemMinX" includeSource="true" missingManifest="error" type="Maven">
<dependencies>
<dependency>
<groupId>org.eclipse.lemminx</groupId>
<artifactId>org.eclipse.lemminx</artifactId>
<version>0.29.0</version>
<type>jar</type>
</dependency>
</dependencies>
<repositories>
<repository>
<id>lemminx-releases</id>
<url>https://repo.eclipse.org/content/repositories/lemminx-releases/</url>
</repository>
</repositories>
</location>
""");
assertStatusOk(getTargetStatus(target));
TargetBundle[] allBundles = target.getBundles();
boolean sourcesFound = false;
for (TargetBundle targetBundle : allBundles) {
if (targetBundle.isSourceBundle()) {
sourcesFound = true;
assertValidSignature(targetBundle);
}
}
assertTrue("No source bundle generated!", sourcesFound);
}

@Test
public void testBadDependencyDirect() throws Exception {
ITargetLocation target = resolveMavenTarget("""
Expand Down

0 comments on commit 4329b3b

Please sign in to comment.