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;
+


Reply via email to