This is an automated email from the ASF dual-hosted git repository. pkarwasz pushed a commit to branch feat/slsa in repository https://gitbox.apache.org/repos/asf/commons-build-plugin.git
commit 7ed5387683793ad89f0a2316262ce25a319c434b Author: Piotr P. Karwasz <[email protected]> AuthorDate: Fri Mar 27 13:08:16 2026 +0100 Add SLSA Provenance model classes Adds Jackson-annotated classes to generate SLSA Provenance annotations for Commons builds. --- pom.xml | 12 ++ .../build/models/slsa/v1_2/BuildDefinition.java | 168 ++++++++++++++++ .../build/models/slsa/v1_2/BuildMetadata.java | 136 +++++++++++++ .../commons/build/models/slsa/v1_2/Builder.java | 119 +++++++++++ .../commons/build/models/slsa/v1_2/Provenance.java | 116 +++++++++++ .../build/models/slsa/v1_2/ResourceDescriptor.java | 220 +++++++++++++++++++++ .../commons/build/models/slsa/v1_2/RunDetails.java | 132 +++++++++++++ .../commons/build/models/slsa/v1_2/Statement.java | 126 ++++++++++++ .../build/models/slsa/v1_2/package-info.java | 120 +++++++++++ 9 files changed, 1149 insertions(+) diff --git a/pom.xml b/pom.xml index 408d67a..408b7df 100644 --- a/pom.xml +++ b/pom.xml @@ -87,6 +87,8 @@ <commons.rc.version>RC1</commons.rc.version> <commons.release.isDistModule>true</commons.release.isDistModule> <commons.distSvnStagingUrl>scm:svn:https://dist.apache.org/repos/dist/dev/commons/${commons.componentid}</commons.distSvnStagingUrl> + <commons.jackson.version>2.21.1</commons.jackson.version> + <commons.jackson.annotations.version>2.21</commons.jackson.annotations.version> <commons.maven.version>3.9.12</commons.maven.version> <!-- Define the following in ~/.m2/settings.xml in an active profile: @@ -115,6 +117,16 @@ <artifactId>commons-codec</artifactId> <version>1.22.0-SNAPSHOT</version> </dependency> + <dependency> + <groupId>com.fasterxml.jackson.core</groupId> + <artifactId>jackson-databind</artifactId> + <version>${commons.jackson.version}</version> + </dependency> + <dependency> + <groupId>com.fasterxml.jackson.core</groupId> + <artifactId>jackson-annotations</artifactId> + <version>${commons.jackson.annotations.version}</version> + </dependency> <dependency> <!-- Try to deal with https://bz.apache.org/bugzilla/show_bug.cgi?id=66951 --> <groupId>org.apache.maven</groupId> diff --git a/src/main/java/org/apache/commons/build/models/slsa/v1_2/BuildDefinition.java b/src/main/java/org/apache/commons/build/models/slsa/v1_2/BuildDefinition.java new file mode 100644 index 0000000..12bbab7 --- /dev/null +++ b/src/main/java/org/apache/commons/build/models/slsa/v1_2/BuildDefinition.java @@ -0,0 +1,168 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.build.models.slsa.v1_2; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +/** + * Inputs that define the build: the build type, external and internal parameters, and resolved dependencies. + * + * <p>Specifies everything that influenced the build output. Together with {@link RunDetails}, it forms the complete + * {@link Provenance} record.</p> + * + * @see <a href="https://slsa.dev/spec/v1.2">SLSA v1.2 Specification</a> + */ +@JsonInclude(JsonInclude.Include.NON_NULL) +public class BuildDefinition { + + @JsonProperty("buildType") + private String buildType = "https://commons.apache.org/builds/0.1.0"; + + @JsonProperty("externalParameters") + private Map<String, Object> externalParameters; + + @JsonProperty("internalParameters") + private Map<String, Object> internalParameters; + + @JsonProperty("resolvedDependencies") + private List<ResourceDescriptor> resolvedDependencies; + + /** Creates a new BuildDefinition instance with the default build type. */ + public BuildDefinition() {} + + /** + * Creates a new BuildDefinition with the given build type and external parameters. + * + * @param buildType URI indicating what type of build was performed + * @param externalParameters inputs passed to the build + */ + public BuildDefinition(String buildType, Map<String, Object> externalParameters) { + this.buildType = buildType; + this.externalParameters = externalParameters; + } + + /** + * Returns the URI indicating what type of build was performed. + * + * <p>Determines the meaning of {@code externalParameters} and {@code internalParameters}.</p> + * + * @return the build type URI + */ + public String getBuildType() { + return buildType; + } + + /** + * Sets the URI indicating what type of build was performed. + * + * @param buildType the build type URI + */ + public void setBuildType(String buildType) { + this.buildType = buildType; + } + + /** + * Returns the inputs passed to the build, such as command-line arguments or environment variables. + * + * @return the external parameters map, or {@code null} if not set + */ + public Map<String, Object> getExternalParameters() { + return externalParameters; + } + + /** + * Sets the inputs passed to the build. + * + * @param externalParameters the external parameters map + */ + public void setExternalParameters(Map<String, Object> externalParameters) { + this.externalParameters = externalParameters; + } + + /** + * Returns the artifacts the build depends on, such as sources, dependencies, build tools, and base images, + * specified by URI and digest. + * + * @return the internal parameters map, or {@code null} if not set + */ + public Map<String, Object> getInternalParameters() { + return internalParameters; + } + + /** + * Sets the artifacts the build depends on. + * + * @param internalParameters the internal parameters map + */ + public void setInternalParameters(Map<String, Object> internalParameters) { + this.internalParameters = internalParameters; + } + + /** + * Returns the materials that influenced the build. + * + * <p>Considered incomplete unless resolved materials are present.</p> + * + * @return the list of resolved dependencies, or {@code null} if not set + */ + public List<ResourceDescriptor> getResolvedDependencies() { + return resolvedDependencies; + } + + /** + * Sets the materials that influenced the build. + * + * @param resolvedDependencies the list of resolved dependencies + */ + public void setResolvedDependencies(List<ResourceDescriptor> resolvedDependencies) { + this.resolvedDependencies = resolvedDependencies; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + BuildDefinition that = (BuildDefinition) o; + return Objects.equals(buildType, that.buildType) + && Objects.equals(externalParameters, that.externalParameters) + && Objects.equals(internalParameters, that.internalParameters) + && Objects.equals(resolvedDependencies, that.resolvedDependencies); + } + + @Override + public int hashCode() { + return Objects.hash(buildType, externalParameters, internalParameters, resolvedDependencies); + } + + @Override + public String toString() { + return "BuildDefinition{" + + "buildType='" + buildType + '\'' + + ", externalParameters=" + externalParameters + + ", internalParameters=" + internalParameters + + ", resolvedDependencies=" + resolvedDependencies + + '}'; + } +} diff --git a/src/main/java/org/apache/commons/build/models/slsa/v1_2/BuildMetadata.java b/src/main/java/org/apache/commons/build/models/slsa/v1_2/BuildMetadata.java new file mode 100644 index 0000000..5913395 --- /dev/null +++ b/src/main/java/org/apache/commons/build/models/slsa/v1_2/BuildMetadata.java @@ -0,0 +1,136 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.build.models.slsa.v1_2; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; + +import java.time.OffsetDateTime; +import java.util.Objects; + +/** + * Metadata about a build invocation: its identifier and start and finish timestamps. + * + * @see <a href="https://slsa.dev/spec/v1.2">SLSA v1.2 Specification</a> + */ +@JsonInclude(JsonInclude.Include.NON_NULL) +public class BuildMetadata { + + @JsonProperty("invocationId") + private String invocationId; + + @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss'Z'") + @JsonProperty("startedOn") + private OffsetDateTime startedOn; + + @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss'Z'") + @JsonProperty("finishedOn") + private OffsetDateTime finishedOn; + + /** Creates a new BuildMetadata instance. */ + public BuildMetadata() { + } + + /** + * Creates a new BuildMetadata instance with all fields set. + * + * @param invocationId identifier for this build invocation + * @param startedOn timestamp when the build started + * @param finishedOn timestamp when the build completed + */ + public BuildMetadata(String invocationId, OffsetDateTime startedOn, OffsetDateTime finishedOn) { + this.invocationId = invocationId; + this.startedOn = startedOn; + this.finishedOn = finishedOn; + } + + /** + * Returns the identifier for this build invocation. + * + * <p>Useful for finding associated logs or other ad-hoc analysis. The exact meaning and format is defined by the + * builder and is treated as opaque and case-sensitive. The value SHOULD be globally unique.</p> + * + * @return the invocation identifier, or {@code null} if not set + */ + public String getInvocationId() { + return invocationId; + } + + /** + * Sets the identifier for this build invocation. + * + * @param invocationId the invocation identifier + */ + public void setInvocationId(String invocationId) { + this.invocationId = invocationId; + } + + /** + * Returns the timestamp of when the build started, serialized as RFC 3339 in UTC ({@code "Z"} suffix). + * + * @return the start timestamp, or {@code null} if not set + */ + public OffsetDateTime getStartedOn() { + return startedOn; + } + + /** + * Sets the timestamp of when the build started. + * + * @param startedOn the start timestamp + */ + public void setStartedOn(OffsetDateTime startedOn) { + this.startedOn = startedOn; + } + + /** + * Returns the timestamp of when the build completed, serialized as RFC 3339 in UTC ({@code "Z"} suffix). + * + * @return the completion timestamp, or {@code null} if not set + */ + public OffsetDateTime getFinishedOn() { + return finishedOn; + } + + /** + * Sets the timestamp of when the build completed. + * + * @param finishedOn the completion timestamp + */ + public void setFinishedOn(OffsetDateTime finishedOn) { + this.finishedOn = finishedOn; + } + + @Override + public boolean equals(Object o) { + if (!(o instanceof BuildMetadata)) + return false; + BuildMetadata that = (BuildMetadata) o; + return Objects.equals(invocationId, that.invocationId) && Objects.equals(startedOn, that.startedOn) && Objects.equals(finishedOn, that.finishedOn); + } + + @Override + public int hashCode() { + return Objects.hash(invocationId, startedOn, finishedOn); + } + + @Override + public String toString() { + return "BuildMetadata{invocationId='" + invocationId + "', startedOn=" + startedOn + ", finishedOn=" + finishedOn + '}'; + } +} diff --git a/src/main/java/org/apache/commons/build/models/slsa/v1_2/Builder.java b/src/main/java/org/apache/commons/build/models/slsa/v1_2/Builder.java new file mode 100644 index 0000000..d783592 --- /dev/null +++ b/src/main/java/org/apache/commons/build/models/slsa/v1_2/Builder.java @@ -0,0 +1,119 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.build.models.slsa.v1_2; + +import com.fasterxml.jackson.annotation.JsonProperty; + +import java.util.List; +import java.util.Map; +import java.util.Objects; + +/** + * Entity that executed the build and is trusted to have correctly performed the operation and populated the provenance. + * + * @see <a href="https://slsa.dev/spec/v1.2">SLSA v1.2 Specification</a> + */ +public class Builder { + + @JsonProperty("id") + private String id; + + @JsonProperty("builderDependencies") + private List<ResourceDescriptor> builderDependencies; + + @JsonProperty("version") + private Map<String, String> version; + + /** Creates a new Builder instance. */ + public Builder() { + } + + /** + * Returns the identifier of the builder. + * + * @return the builder identifier URI + */ + public String getId() { + return id; + } + + /** + * Sets the identifier of the builder. + * + * @param id the builder identifier URI + */ + public void setId(String id) { + this.id = id; + } + + /** + * Returns orchestrator dependencies that do not run within the build workload and do not affect the build output, + * but may affect provenance generation or security guarantees. + * + * @return the list of builder dependencies, or {@code null} if not set + */ + public List<ResourceDescriptor> getBuilderDependencies() { + return builderDependencies; + } + + /** + * Sets the orchestrator dependencies that may affect provenance generation or security guarantees. + * + * @param builderDependencies the list of builder dependencies + */ + public void setBuilderDependencies(List<ResourceDescriptor> builderDependencies) { + this.builderDependencies = builderDependencies; + } + + /** + * Returns a map of build platform component names to their versions. + * + * @return the version map, or {@code null} if not set + */ + public Map<String, String> getVersion() { + return version; + } + + /** + * Sets the map of build platform component names to their versions. + * + * @param version the version map + */ + public void setVersion(Map<String, String> version) { + this.version = version; + } + + @Override + public boolean equals(Object o) { + if (!(o instanceof Builder)) + return false; + Builder that = (Builder) o; + return Objects.equals(id, that.id) + && Objects.equals(builderDependencies, that.builderDependencies) + && Objects.equals(version, that.version); + } + + @Override + public int hashCode() { + return Objects.hash(id, builderDependencies, version); + } + + @Override + public String toString() { + return "Builder{id='" + id + "', builderDependencies=" + builderDependencies + ", version=" + version + '}'; + } +} diff --git a/src/main/java/org/apache/commons/build/models/slsa/v1_2/Provenance.java b/src/main/java/org/apache/commons/build/models/slsa/v1_2/Provenance.java new file mode 100644 index 0000000..91d1d18 --- /dev/null +++ b/src/main/java/org/apache/commons/build/models/slsa/v1_2/Provenance.java @@ -0,0 +1,116 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.build.models.slsa.v1_2; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import java.util.Objects; + +/** + * Root predicate of an SLSA v1.2 provenance attestation, describing what was built and how. + * + * <p>Combines a {@link BuildDefinition} (the inputs) with {@link RunDetails} (the execution context). Intended to be + * used as the {@code predicate} field of an in-toto {@link Statement}.</p> + * + * @see <a href="https://slsa.dev/spec/v1.2">SLSA v1.2 Specification</a> + */ +@JsonInclude(JsonInclude.Include.NON_NULL) +public class Provenance { + + /** Predicate type URI used in the in-toto {@link Statement} wrapping this provenance. */ + public static final String PREDICATE_TYPE = "https://slsa.dev/provenance/v1"; + + @JsonProperty("buildDefinition") + private BuildDefinition buildDefinition; + + @JsonProperty("runDetails") + private RunDetails runDetails; + + /** Creates a new Provenance instance. */ + public Provenance() {} + + /** + * Creates a new Provenance with the given build definition and run details. + * + * @param buildDefinition inputs that defined the build + * @param runDetails details about the build invocation + */ + public Provenance(BuildDefinition buildDefinition, RunDetails runDetails) { + this.buildDefinition = buildDefinition; + this.runDetails = runDetails; + } + + /** + * Returns the build definition describing all inputs that produced the build output. + * + * <p>Includes source code, dependencies, build tools, base images, and other materials.</p> + * + * @return the build definition, or {@code null} if not set + */ + public BuildDefinition getBuildDefinition() { + return buildDefinition; + } + + /** + * Sets the build definition describing all inputs that produced the build output. + * + * @param buildDefinition the build definition + */ + public void setBuildDefinition(BuildDefinition buildDefinition) { + this.buildDefinition = buildDefinition; + } + + /** + * Returns the details about the invocation of the build tool and the environment in which it was run. + * + * @return the run details, or {@code null} if not set + */ + public RunDetails getRunDetails() { + return runDetails; + } + + /** + * Sets the details about the invocation of the build tool and the environment in which it was run. + * + * @param runDetails the run details + */ + public void setRunDetails(RunDetails runDetails) { + this.runDetails = runDetails; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + Provenance that = (Provenance) o; + return Objects.equals(buildDefinition, that.buildDefinition) && Objects.equals(runDetails, that.runDetails); + } + + @Override + public int hashCode() { + return Objects.hash(buildDefinition, runDetails); + } + + @Override + public String toString() { + return "Provenance{buildDefinition=" + buildDefinition + ", runDetails=" + runDetails + '}'; + } +} diff --git a/src/main/java/org/apache/commons/build/models/slsa/v1_2/ResourceDescriptor.java b/src/main/java/org/apache/commons/build/models/slsa/v1_2/ResourceDescriptor.java new file mode 100644 index 0000000..a34d4da --- /dev/null +++ b/src/main/java/org/apache/commons/build/models/slsa/v1_2/ResourceDescriptor.java @@ -0,0 +1,220 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.build.models.slsa.v1_2; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; + +import java.util.Map; +import java.util.Objects; + +/** + * Description of an artifact or resource referenced in the build, identified by URI and cryptographic digest. + * + * <p>Used to represent inputs to, outputs from, or byproducts of the build process.</p> + * + * @see <a href="https://slsa.dev/spec/v1.2">SLSA v1.2 Specification</a> + */ +@JsonInclude(JsonInclude.Include.NON_NULL) +public class ResourceDescriptor { + + @JsonProperty("name") + private String name; + + @JsonProperty("uri") + private String uri; + + @JsonProperty("digest") + private Map<String, String> digest; + + @JsonProperty("content") + private byte[] content; + + @JsonProperty("downloadLocation") + private String downloadLocation; + + @JsonProperty("mediaType") + private String mediaType; + + @JsonProperty("annotations") + private Map<String, Object> annotations; + + /** Creates a new ResourceDescriptor instance. */ + public ResourceDescriptor() { + } + + /** + * Creates a new ResourceDescriptor with the given URI and digest. + * + * @param uri URI identifying the resource + * @param digest map of digest algorithm names to their hex-encoded values + */ + public ResourceDescriptor(String uri, Map<String, String> digest) { + this.uri = uri; + this.digest = digest; + } + + /** + * Returns the name of the resource. + * + * @return the resource name, or {@code null} if not set + */ + public String getName() { + return name; + } + + /** + * Sets the name of the resource. + * + * @param name the resource name + */ + public void setName(String name) { + this.name = name; + } + + /** + * Returns the URI identifying the resource. + * + * @return the resource URI, or {@code null} if not set + */ + public String getUri() { + return uri; + } + + /** + * Sets the URI identifying the resource. + * + * @param uri the resource URI + */ + public void setUri(String uri) { + this.uri = uri; + } + + /** + * Returns the map of cryptographic digest algorithms to their corresponding hex-encoded values for this resource. + * + * <p>Common keys include {@code "sha256"} and {@code "sha512"}.</p> + * + * @return the digest map, or {@code null} if not set + */ + public Map<String, String> getDigest() { + return digest; + } + + /** + * Sets the map of cryptographic digest algorithms to their hex-encoded values. + * + * @param digest the digest map + */ + public void setDigest(Map<String, String> digest) { + this.digest = digest; + } + + /** + * Returns the raw contents of the resource, base64-encoded when serialized to JSON. + * + * @return the resource content, or {@code null} if not set + */ + public byte[] getContent() { + return content; + } + + /** + * Sets the raw contents of the resource. + * + * @param content the resource content + */ + public void setContent(byte[] content) { + this.content = content; + } + + /** + * Returns the download URI for the resource, if different from {@link #getUri()}. + * + * @return the download location URI, or {@code null} if not set + */ + public String getDownloadLocation() { + return downloadLocation; + } + + /** + * Sets the download URI for the resource. + * + * @param downloadLocation the download location URI + */ + public void setDownloadLocation(String downloadLocation) { + this.downloadLocation = downloadLocation; + } + + /** + * Returns the media type of the resource (e.g., {@code "application/octet-stream"}). + * + * @return the media type, or {@code null} if not set + */ + public String getMediaType() { + return mediaType; + } + + /** + * Sets the media type of the resource. + * + * @param mediaType the media type + */ + public void setMediaType(String mediaType) { + this.mediaType = mediaType; + } + + /** + * Returns additional key-value metadata about the resource, such as filename, size, or builder-specific attributes. + * + * @return the annotations map, or {@code null} if not set + */ + public Map<String, Object> getAnnotations() { + return annotations; + } + + /** + * Sets additional key-value metadata about the resource. + * + * @param annotations the annotations map + */ + public void setAnnotations(Map<String, Object> annotations) { + this.annotations = annotations; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + ResourceDescriptor that = (ResourceDescriptor) o; + return Objects.equals(uri, that.uri) && Objects.equals(digest, that.digest); + } + + @Override + public int hashCode() { + return Objects.hash(uri, digest); + } + + @Override + public String toString() { + return "ResourceDescriptor{uri='" + uri + '\'' + ", digest=" + digest + '}'; + } +} diff --git a/src/main/java/org/apache/commons/build/models/slsa/v1_2/RunDetails.java b/src/main/java/org/apache/commons/build/models/slsa/v1_2/RunDetails.java new file mode 100644 index 0000000..92872c9 --- /dev/null +++ b/src/main/java/org/apache/commons/build/models/slsa/v1_2/RunDetails.java @@ -0,0 +1,132 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.build.models.slsa.v1_2; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import java.util.List; +import java.util.Objects; + +/** + * Details about the build invocation: the builder identity, execution metadata, and any byproduct artifacts. + * + * @see <a href="https://slsa.dev/spec/v1.2">SLSA v1.2 Specification</a> + */ +@JsonInclude(JsonInclude.Include.NON_NULL) +public class RunDetails { + + @JsonProperty("builder") + private Builder builder; + + @JsonProperty("metadata") + private BuildMetadata metadata; + + @JsonProperty("byproducts") + private List<ResourceDescriptor> byproducts; + + /** Creates a new RunDetails instance. */ + public RunDetails() {} + + /** + * Creates a new RunDetails with the given builder and metadata. + * + * @param builder entity that executed the build + * @param metadata metadata about the build invocation + */ + public RunDetails(Builder builder, BuildMetadata metadata) { + this.builder = builder; + this.metadata = metadata; + } + + /** + * Returns the builder that executed the invocation. + * + * <p>Trusted to have correctly performed the operation and populated this provenance.</p> + * + * @return the builder, or {@code null} if not set + */ + public Builder getBuilder() { + return builder; + } + + /** + * Sets the builder that executed the invocation. + * + * @param builder the builder + */ + public void setBuilder(Builder builder) { + this.builder = builder; + } + + /** + * Returns the metadata about the build invocation, including its identifier and timing. + * + * @return the build metadata, or {@code null} if not set + */ + public BuildMetadata getMetadata() { + return metadata; + } + + /** + * Sets the metadata about the build invocation. + * + * @param metadata the build metadata + */ + public void setMetadata(BuildMetadata metadata) { + this.metadata = metadata; + } + + /** + * Returns artifacts produced as a side effect of the build that are not the primary output. + * + * @return the list of byproduct artifacts, or {@code null} if not set + */ + public List<ResourceDescriptor> getByproducts() { + return byproducts; + } + + /** + * Sets the artifacts produced as a side effect of the build that are not the primary output. + * + * @param byproducts the list of byproduct artifacts + */ + public void setByproducts(List<ResourceDescriptor> byproducts) { + this.byproducts = byproducts; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + RunDetails that = (RunDetails) o; + return Objects.equals(builder, that.builder) && Objects.equals(metadata, that.metadata) && Objects.equals(byproducts, that.byproducts); + } + + @Override + public int hashCode() { + return Objects.hash(builder, metadata, byproducts); + } + + @Override + public String toString() { + return "RunDetails{builder=" + builder + ", metadata=" + metadata + ", byproducts=" + byproducts + '}'; + } +} diff --git a/src/main/java/org/apache/commons/build/models/slsa/v1_2/Statement.java b/src/main/java/org/apache/commons/build/models/slsa/v1_2/Statement.java new file mode 100644 index 0000000..a6d690c --- /dev/null +++ b/src/main/java/org/apache/commons/build/models/slsa/v1_2/Statement.java @@ -0,0 +1,126 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.build.models.slsa.v1_2; + +import com.fasterxml.jackson.annotation.JsonProperty; + +import java.util.List; +import java.util.Objects; + +/** + * In-toto v1 attestation envelope that binds a set of subject artifacts to an SLSA provenance predicate. + * + * @see <a href="https://github.com/in-toto/attestation/blob/main/spec/v1/statement.md">in-toto Statement v1</a> + */ +public class Statement { + + @JsonProperty("_type") + private final String _type = "https://in-toto.io/Statement/v1"; + + @JsonProperty("subject") + private List<ResourceDescriptor> subject; + + @JsonProperty("predicateType") + private String predicateType; + + @JsonProperty("predicate") + private Provenance predicate; + + /** Creates a new Statement instance. */ + public Statement() { + } + + /** + * Returns the schema identifier for this statement. + * + * @return the fixed type URI {@code "https://in-toto.io/Statement/v1"} + */ + public String get_type() { + return _type; + } + + /** + * Returns the set of software artifacts that the attestation applies to. + * + * <p>Each element represents a single artifact. Artifacts are matched purely by digest, regardless of content + * type.</p> + * + * @return the list of subject artifacts, or {@code null} if not set + */ + public List<ResourceDescriptor> getSubject() { + return subject; + } + + /** + * Sets the set of software artifacts that the attestation applies to. + * + * @param subject the list of subject artifacts + */ + public void setSubject(List<ResourceDescriptor> subject) { + this.subject = subject; + } + + /** + * Returns the URI identifying the type of the predicate. + * + * @return the predicate type URI, or {@code null} if no predicate has been set + */ + public String getPredicateType() { + return predicateType; + } + + /** + * Returns the provenance predicate. + * + * <p>Unset is treated the same as set-but-empty. May be omitted if {@code predicateType} fully describes the + * predicate.</p> + * + * @return the provenance predicate, or {@code null} if not set + */ + public Provenance getPredicate() { + return predicate; + } + + /** + * Sets the provenance predicate and automatically assigns {@code predicateType} to the SLSA provenance v1 URI. + * + * @param predicate the provenance predicate + */ + public void setPredicate(Provenance predicate) { + this.predicate = predicate; + this.predicateType = Provenance.PREDICATE_TYPE; + } + + @Override + public boolean equals(Object o) { + if (!(o instanceof Statement)) + return false; + Statement statement = (Statement) o; + return Objects.equals(subject, statement.subject) && Objects.equals(predicateType, statement.predicateType) && Objects.equals(predicate, + statement.predicate); + } + + @Override + public int hashCode() { + return Objects.hash(subject, predicateType, predicate); + } + + @Override + public String toString() { + return "Statement{_type='" + _type + "', subject=" + subject + ", predicateType='" + predicateType + "', predicate=" + predicate + '}'; + } +} diff --git a/src/main/java/org/apache/commons/build/models/slsa/v1_2/package-info.java b/src/main/java/org/apache/commons/build/models/slsa/v1_2/package-info.java new file mode 100644 index 0000000..eb77ffe --- /dev/null +++ b/src/main/java/org/apache/commons/build/models/slsa/v1_2/package-info.java @@ -0,0 +1,120 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * SLSA 1.2 Build Attestation Models. + * + * <p>This package provides Jackson-annotated model classes that implement the <a + * href="https://slsa.dev/spec/v1.2">Supply-chain Levels for Software Artifacts (SLSA) v1.2 + * specification</a>. + * + * <h2>Overview</h2> + * + * <p>SLSA is a framework for evaluating and improving the security posture of build systems. SLSA + * v1.2 defines a standard for recording build provenance - information about how software + * artifacts were produced. + * + * <h2>Core Models</h2> + * + * <ul> + * <li><b>{@link org.apache.commons.build.models.slsa.v1_2.Provenance}</b> - Root object + * describing the build provenance. Contains BuildDefinition and RunDetails. + * <li><b>{@link org.apache.commons.build.models.slsa.v1_2.BuildDefinition}</b> - Specifies + * the inputs that define the build, including build type, configuration, external + * parameters, and resolved dependencies. + * <li><b>{@link org.apache.commons.build.models.slsa.v1_2.RunDetails}</b> - Specifies the + * details about the build invocation and environment, including the builder identity and + * build metadata. + * </ul> + * + * <h2>Supporting Models</h2> + * + * <ul> + * <li><b>{@link org.apache.commons.build.models.slsa.v1_2.Builder}</b> - Identifies the + * entity that executed the build. + * <li><b>{@link org.apache.commons.build.models.slsa.v1_2.BuildMetadata}</b> - Contains + * metadata about the build invocation, including timing information. + * <li><b>{@link org.apache.commons.build.models.slsa.v1_2.ResourceDescriptor}</b> - Describes + * an artifact or resource referenced in the build by URI and cryptographic digest. + * </ul> + * + * <h2>Usage Example</h2> + * + * <pre> + * // Create a builder + * Builder builder = new Builder(); + * builder.setId("https://github.com/actions"); + * builder.setVersion("1.0"); + * + * // Create build metadata + * BuildMetadata buildMetadata = new BuildMetadata(); + * buildMetadata.setInvocationId("build-12345"); + * buildMetadata.setStartedOn(OffsetDateTime.now(ZoneOffset.UTC)); + * buildMetadata.setFinishedOn(OffsetDateTime.now(ZoneOffset.UTC)); + * + * // Create run details + * RunDetails runDetails = new RunDetails(); + * runDetails.setBuilder(builder); + * runDetails.setMetadata(buildMetadata); + * + * // Create build definition + * BuildDefinition buildDefinition = new BuildDefinition(); + * buildDefinition.setBuildType("https://github.com/actions"); + * buildDefinition.setExternalParameters(new HashMap<>()); + * + * // Create provenance + * Provenance provenance = new Provenance(); + * provenance.setBuildDefinition(buildDefinition); + * provenance.setRunDetails(runDetails); + * + * // Serialize with Jackson + * ObjectMapper mapper = new ObjectMapper(); + * String json = mapper.writeValueAsString(provenance); + * </pre> + * + * <h2>Jackson Integration</h2> + * + * <p>All models use Jackson annotations for JSON serialization/deserialization: + * + * <ul> + * <li>{@code @JsonProperty} - Maps field names to JSON properties + * <li>{@code @JsonInclude} - Controls inclusion of null/empty values in serialization + * <li>{@code @JsonFormat} - Specifies date/time formatting + * </ul> + * + * <p>The models are compatible with Jackson's ObjectMapper and support both serialization to + * JSON and deserialization from JSON. + * + * <h2>Validation</h2> + * + * <p>Some models include Jakarta Validation annotations: + * + * <ul> + * <li>{@code @NotBlank} - Ensures required string fields are not empty + * </ul> + * + * <p>Users can enable validation using a Jakarta Validation provider to ensure provenance + * integrity. + * + * <h2>Reference</h2> + * + * @see <a href="https://slsa.dev/spec/v1.2">SLSA v1.2 Specification</a> + * @see <a href="https://github.com/in-toto/attestation">In-toto Attestation Framework</a> + * @see <a href="https://github.com/FasterXML/jackson">Jackson JSON processor</a> + */ +package org.apache.commons.build.models.slsa.v1_2; +
