Skip to content

Commit d309420

Browse files
committed
Add build-attestation target
This PR was moved from apache/commons-build-plugin#417 It adds a goal to generate a [SLSA](https://slsa.dev/) build attestation and attaches it to the build as a file with the `.intoto.json` extension. The attestation records the following information about the build environment: - The Java version used (vendor, version string) - The Maven version used - The `gitTree` hash of the unpacked Java distribution - The `gitTree` hash of the unpacked Maven distribution
1 parent fb16018 commit d309420

20 files changed

Lines changed: 2049 additions & 1 deletion

checkstyle.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -185,7 +185,7 @@
185185
<module name="UpperEll" />
186186
<module name="ImportOrder">
187187
<property name="option" value="top"/>
188-
<property name="groups" value="java,javax,org"/>
188+
<property name="groups" value="java,javax"/>
189189
<property name="ordered" value="true"/>
190190
<property name="separated" value="true"/>
191191
</module>

fb-excludes.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,11 @@
1818
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
1919
xsi:schemaLocation="https://github.com/spotbugs/filter/3.0.0 https://raw.githubusercontent.com/spotbugs/spotbugs/3.1.0/spotbugs/etc/findbugsfilter.xsd">
2020

21+
<!-- Mutable objects are not passed to untrusted methods, so we exclude these checks -->
22+
<Match>
23+
<Bug pattern="EI_EXPOSE_REP,EI_EXPOSE_REP2" />
24+
</Match>
25+
2126
<!-- Omit junit tests -->
2227
<Match>
2328
<Class name="~.*\.*Test.*"/>

pom.xml

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,9 @@
113113
<!-- Until Maven plugins used here don't fail the Moditect plugin -->
114114
<moditect.skip>true</moditect.skip>
115115
<japicmp.skip>true</japicmp.skip>
116+
<!-- Dependency versions -->
117+
<commons.jackson.version>2.21.1</commons.jackson.version>
118+
<commons.jackson.annotations.version>2.21</commons.jackson.annotations.version>
116119
</properties>
117120
<dependencies>
118121
<dependency>
@@ -151,6 +154,18 @@
151154
<artifactId>maven-scm-api</artifactId>
152155
<version>${maven-scm.version}</version>
153156
</dependency>
157+
<dependency>
158+
<groupId>org.apache.maven.scm</groupId>
159+
<artifactId>maven-scm-manager-plexus</artifactId>
160+
<version>${maven-scm.version}</version>
161+
<scope>compile</scope>
162+
</dependency>
163+
<dependency>
164+
<groupId>org.apache.maven.scm</groupId>
165+
<artifactId>maven-scm-provider-gitexe</artifactId>
166+
<version>${maven-scm.version}</version>
167+
<scope>runtime</scope>
168+
</dependency>
154169
<dependency>
155170
<groupId>org.apache.maven.scm</groupId>
156171
<artifactId>maven-scm-provider-svnexe</artifactId>
@@ -171,6 +186,22 @@
171186
<artifactId>commons-compress</artifactId>
172187
<version>1.28.0</version>
173188
</dependency>
189+
<dependency>
190+
<groupId>com.fasterxml.jackson.core</groupId>
191+
<artifactId>jackson-databind</artifactId>
192+
<version>${commons.jackson.version}</version>
193+
</dependency>
194+
<dependency>
195+
<groupId>com.fasterxml.jackson.core</groupId>
196+
<artifactId>jackson-annotations</artifactId>
197+
<version>${commons.jackson.annotations.version}</version>
198+
</dependency>
199+
<dependency>
200+
<groupId>com.fasterxml.jackson.datatype</groupId>
201+
<artifactId>jackson-datatype-jsr310</artifactId>
202+
<version>${commons.jackson.version}</version>
203+
<scope>runtime</scope>
204+
</dependency>
174205
<dependency>
175206
<groupId>org.apache.maven.plugin-testing</groupId>
176207
<artifactId>maven-plugin-testing-harness</artifactId>
@@ -188,11 +219,28 @@
188219
<artifactId>junit-jupiter</artifactId>
189220
<scope>test</scope>
190221
</dependency>
222+
<dependency>
223+
<groupId>net.javacrumbs.json-unit</groupId>
224+
<artifactId>json-unit-assertj</artifactId>
225+
<version>2.40.1</version>
226+
<scope>test</scope>
227+
</dependency>
228+
<dependency>
229+
<groupId>org.junit.jupiter</groupId>
230+
<artifactId>junit-jupiter-api</artifactId>
231+
<scope>test</scope>
232+
</dependency>
191233
<dependency>
192234
<groupId>org.junit.vintage</groupId>
193235
<artifactId>junit-vintage-engine</artifactId>
194236
<scope>test</scope>
195237
</dependency>
238+
<dependency>
239+
<groupId>org.mockito</groupId>
240+
<artifactId>mockito-core</artifactId>
241+
<version>4.11.0</version>
242+
<scope>test</scope>
243+
</dependency>
196244
<!-- A bit of jar-hell requires this to come last. -->
197245
<dependency>
198246
<groupId>org.apache.maven</groupId>
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* https://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
package org.apache.commons.release.plugin.internal;
18+
19+
import java.io.IOException;
20+
import java.util.HashMap;
21+
import java.util.Map;
22+
23+
import org.apache.commons.codec.digest.DigestUtils;
24+
import org.apache.commons.release.plugin.slsa.v1_2.ResourceDescriptor;
25+
import org.apache.maven.artifact.Artifact;
26+
import org.apache.maven.plugin.MojoExecutionException;
27+
28+
/**
29+
* Utilities to convert {@link Artifact} from and to other types.
30+
*/
31+
public final class ArtifactUtils {
32+
33+
private ArtifactUtils() {
34+
// prevent instantiation
35+
}
36+
37+
/**
38+
* Returns the conventional filename for the given artifact.
39+
*
40+
* @param artifact A Maven artifact.
41+
* @return A filename.
42+
*/
43+
public static String getFileName(Artifact artifact) {
44+
return getFileName(artifact, artifact.getArtifactHandler().getExtension());
45+
}
46+
47+
/**
48+
* Returns the filename for the given artifact with a changed extension.
49+
*
50+
* @param artifact A Maven artifact.
51+
* @param extension The file name extension.
52+
* @return A filename.
53+
*/
54+
public static String getFileName(Artifact artifact, String extension) {
55+
StringBuilder fileName = new StringBuilder();
56+
fileName.append(artifact.getArtifactId()).append("-").append(artifact.getVersion());
57+
if (artifact.getClassifier() != null) {
58+
fileName.append("-").append(artifact.getClassifier());
59+
}
60+
fileName.append(".").append(extension);
61+
return fileName.toString();
62+
}
63+
64+
/**
65+
* Returns the Package URL corresponding to this artifact.
66+
*
67+
* @param artifact A maven artifact.
68+
* @return A PURL for the given artifact.
69+
*/
70+
public static String getPackageUrl(Artifact artifact) {
71+
StringBuilder sb = new StringBuilder();
72+
sb.append("pkg:maven/").append(artifact.getGroupId()).append("/").append(artifact.getArtifactId()).append("@").append(artifact.getVersion())
73+
.append("?");
74+
String classifier = artifact.getClassifier();
75+
if (classifier != null) {
76+
sb.append("classifier=").append(classifier).append("&");
77+
}
78+
sb.append("type=").append(artifact.getType());
79+
return sb.toString();
80+
}
81+
82+
private static Map<String, String> getChecksums(Artifact artifact) throws IOException {
83+
Map<String, String> checksums = new HashMap<>();
84+
DigestUtils digest = new DigestUtils(DigestUtils.getSha256Digest());
85+
String sha256sum = digest.digestAsHex(artifact.getFile());
86+
checksums.put("sha256", sha256sum);
87+
return checksums;
88+
}
89+
90+
/**
91+
* Converts a Maven artifact to a SLSA {@link ResourceDescriptor}.
92+
*
93+
* @param artifact A Maven artifact.
94+
* @return A SLSA resource descriptor.
95+
* @throws MojoExecutionException If an I/O error occurs retrieving the artifact.
96+
*/
97+
public static ResourceDescriptor toResourceDescriptor(Artifact artifact) throws MojoExecutionException {
98+
ResourceDescriptor descriptor = new ResourceDescriptor();
99+
descriptor.setName(getFileName(artifact));
100+
descriptor.setUri(getPackageUrl(artifact));
101+
if (artifact.getFile() != null) {
102+
try {
103+
descriptor.setDigest(getChecksums(artifact));
104+
} catch (IOException e) {
105+
throw new MojoExecutionException("Unable to compute hash for artifact file: " + artifact.getFile(), e);
106+
}
107+
}
108+
return descriptor;
109+
}
110+
}
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* https://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
package org.apache.commons.release.plugin.internal;
18+
19+
import java.io.IOException;
20+
import java.io.InputStream;
21+
import java.nio.file.Path;
22+
import java.util.HashMap;
23+
import java.util.Map;
24+
import java.util.Properties;
25+
26+
import org.apache.commons.release.plugin.slsa.v1_2.ResourceDescriptor;
27+
28+
/**
29+
* Factory methods for {@link ResourceDescriptor} instances representing build-tool dependencies.
30+
*/
31+
public final class BuildToolDescriptors {
32+
33+
private BuildToolDescriptors() {
34+
// no instantiation
35+
}
36+
37+
/**
38+
* Creates a {@link ResourceDescriptor} for the JDK used during the build.
39+
*
40+
* @param javaHome path to the JDK home directory (value of the {@code java.home} system property)
41+
* @return a descriptor with digest and annotations populated from system properties
42+
* @throws IOException if hashing the JDK directory fails
43+
*/
44+
public static ResourceDescriptor jvm(Path javaHome) throws IOException {
45+
ResourceDescriptor descriptor = new ResourceDescriptor();
46+
descriptor.setName("JDK");
47+
Map<String, String> digest = new HashMap<>();
48+
digest.put("gitTree", GitUtils.gitTree(javaHome));
49+
descriptor.setDigest(digest);
50+
String[] propertyNames = {"java.version", "java.vendor", "java.vendor.version", "java.vm.name", "java.vm.version", "java.vm.vendor",
51+
"java.runtime.name", "java.runtime.version", "java.specification.version"};
52+
Map<String, Object> annotations = new HashMap<>();
53+
for (String prop : propertyNames) {
54+
annotations.put(prop.substring("java.".length()), System.getProperty(prop));
55+
}
56+
descriptor.setAnnotations(annotations);
57+
return descriptor;
58+
}
59+
60+
/**
61+
* Creates a {@link ResourceDescriptor} for the Maven installation used during the build.
62+
*
63+
* @param version Maven version string
64+
* @param mavenHome path to the Maven home directory
65+
* @return a descriptor for the Maven installation
66+
* @throws IOException if hashing the Maven home directory fails
67+
*/
68+
public static ResourceDescriptor maven(String version, Path mavenHome) throws IOException {
69+
ResourceDescriptor descriptor = new ResourceDescriptor();
70+
descriptor.setName("Maven");
71+
descriptor.setUri("pkg:maven/org.apache.maven/apache-maven@" + version);
72+
Map<String, String> digest = new HashMap<>();
73+
digest.put("gitTree", GitUtils.gitTree(mavenHome));
74+
descriptor.setDigest(digest);
75+
Properties buildProps = new Properties();
76+
try (InputStream in = BuildToolDescriptors.class.getResourceAsStream("/org/apache/maven/messages/build.properties")) {
77+
if (in != null) {
78+
buildProps.load(in);
79+
}
80+
}
81+
if (!buildProps.isEmpty()) {
82+
Map<String, Object> annotations = new HashMap<>();
83+
buildProps.forEach((key, value) -> annotations.put((String) key, value));
84+
descriptor.setAnnotations(annotations);
85+
}
86+
return descriptor;
87+
}
88+
}

0 commit comments

Comments
 (0)