Skip to content

Conversation

@st3penta
Copy link
Contributor

@st3penta st3penta commented Dec 11, 2025

User description

Add support for SLSA v1.0 provenance attestations while maintaining backward compatibility with v0.2

Co-authored-by: Claude Code noreply@anthropic.com

Ref: https://issues.redhat.com/browse/EC-1581


PR Type

Enhancement, Tests


Description

  • Add SLSA v1.0 provenance attestation parsing and validation support

    • New SLSAProvenanceFromSignatureV1 parser for v1 attestations
    • JSON schema validation for SLSA v1 predicates
    • Backward compatible with existing v0.2 support
  • Extend acceptance test framework for SLSA v1 attestations

    • New CreateAndPushV1Attestation test helper function
    • New Gherkin step for v1 attestation creation
    • End-to-end test scenario validating v1 support
  • Update policy evaluation to handle both SLSA versions

    • Support v1 predicate type in attestation validation
    • Update example Rego policies for v1 compatibility
  • Add comprehensive AGENTS.md documentation for developers

    • Architecture overview and core components
    • Testing strategy and acceptance test framework
    • Policy evaluation and rule filtering system details

Diagram Walkthrough

flowchart LR
  A["SLSA v1 Attestation"] -->|Parse| B["SLSAProvenanceFromSignatureV1"]
  B -->|Validate| C["slsa_provenance_v1.json Schema"]
  C -->|Store| D["slsaProvenanceV1 Type"]
  E["SLSA v0.2 Attestation"] -->|Parse| F["SLSAProvenanceFromSignature"]
  F -->|Validate| G["slsa_provenance_v0.2.json Schema"]
  G -->|Store| H["slsaProvenanceV02 Type"]
  D -->|Evaluate| I["ApplicationSnapshotImage"]
  H -->|Evaluate| I
  I -->|Policy Check| J["Rego Rules"]
Loading

File Walkthrough

Relevant files
Enhancement
6 files
attestation.go
Add SLSA v1 statement creation and signing functions         
+77/-0   
image.go
Refactor attestation creation to support v0.2 and v1         
+39/-17 
slsa_provenance_v1.go
Implement SLSA v1 provenance attestation parser                   
+117/-0 
application_snapshot_image.go
Add SLSA v1 attestation validation in evaluation pipeline
+9/-0     
schema.go
Register SLSA v1 JSON schema for validation                           
+12/-0   
sigstore.rego
Update Rego policies to support SLSA v0.2 and v1                 
+15/-1   
Tests
2 files
slsa_provenance_v1_test.go
Add comprehensive unit tests for SLSA v1 parsing                 
+459/-0 
validate_image.feature
Add acceptance test scenario for SLSA v1 attestations       
+25/-0   
Documentation
3 files
slsa_provenance_v1.json
Add SLSA v1.0 JSON schema definition                                         
+187/-0 
AGENTS.md
Add comprehensive developer and agent instructions             
+243/-0 
CLAUDE.md
Add reference to AGENTS.md documentation                                 
+14/-0   

@qodo-code-review
Copy link
Contributor

qodo-code-review bot commented Dec 11, 2025

PR Compliance Guide 🔍

Below is a summary of compliance checks for this PR:

Security Compliance
🟢
No security concerns identified No security vulnerabilities detected by AI analysis. Human verification advised for critical code.
Ticket Compliance
🎫 No ticket provided
  • Create ticket/issue
Codebase Duplication Compliance
Codebase context is not defined

Follow the guide to enable codebase context checks.

Custom Compliance
🟢
Generic: Meaningful Naming and Self-Documenting Code

Objective: Ensure all identifiers clearly express their purpose and intent, making code
self-documenting

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Secure Error Handling

Objective: To prevent the leakage of sensitive system information through error messages while
providing sufficient detail for internal debugging.

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Secure Logging Practices

Objective: To ensure logs are useful for debugging and auditing without exposing sensitive
information like PII, PHI, or cardholder data.

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Comprehensive Audit Trails

Objective: To create a detailed and reliable record of critical system actions for security analysis
and compliance.

Status:
Missing Auditing: New critical actions creating and pushing attestations and signatures (including SLSA v1
path) do not emit audit logs with user, action, and outcome context.

Referred Code
var state *imageState
ctx, err := testenv.SetupState(ctx, &state)
if err != nil {
	return ctx, err
}

if state.Attestations[imageName] != "" {
	// we already created the attestation
	return ctx, nil
}

image, err := imageFrom(ctx, imageName)
if err != nil {
	return ctx, err
}

var signedAttestation []byte

if useV1 {
	// SLSA v1.0
	statementV1, err := attestation.CreateV1StatementFor(imageName, image)


 ... (clipped 89 lines)

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Robust Error Handling and Edge Case Management

Objective: Ensure comprehensive error handling that provides meaningful context and graceful
degradation

Status:
Generic Errors: Several returned errors are propagated without added context (e.g., json/protobuf
marshal/unmarshal and signer acquisition), which may hinder debugging and omit operation
context.

Referred Code
// Marshal the provenance to JSON then to protobuf Struct
provenanceJSON, err := json.Marshal(provenance)
if err != nil {
	return nil, err
}

var provenanceMap map[string]interface{}
if err := json.Unmarshal(provenanceJSON, &provenanceMap); err != nil {
	return nil, err
}

predicateStruct, err := structpb.NewStruct(provenanceMap)
if err != nil {
	return nil, err
}

// Create the statement with the predicate
statement := &attestationv1.Statement{
	Type: in_toto.StatementInTotoV01,
	Subject: []*attestationv1.ResourceDescriptor{
		{


 ... (clipped 48 lines)

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Security-First Input Validation and Data Handling

Objective: Ensure all data inputs are validated, sanitized, and handled securely to prevent
vulnerabilities

Status:
Predicate Validation: The v1 attestation parser trusts decoded payload after basic type checks without explicit
JSON schema validation of predicate structure, which may allow malformed inputs to pass
parsing.

Referred Code
payload, err := payloadFromSig(sig)
if err != nil {
	return nil, err
}

embedded, err := decodedPayload(payload)
if err != nil {
	return nil, err
}

var statement in_toto.ProvenanceStatementSLSA1
if err := json.Unmarshal(embedded, &statement); err != nil {
	return nil, fmt.Errorf("malformed attestation data: %w", err)
}

if statement.Type != in_toto.StatementInTotoV01 {
	return nil, fmt.Errorf("unsupported attestation type: %s", statement.Type)
}

if statement.PredicateType != v1.PredicateSLSAProvenance {
	return nil, fmt.Errorf("unsupported attestation predicate type: %s", statement.PredicateType)


 ... (clipped 8 lines)

Learn more about managing compliance generic rules or creating your own custom rules

  • Update
Compliance status legend 🟢 - Fully Compliant
🟡 - Partial Compliant
🔴 - Not Compliant
⚪ - Requires Further Human Verification
🏷️ - Compliance label

@qodo-code-review
Copy link
Contributor

qodo-code-review bot commented Dec 11, 2025

PR Code Suggestions ✨

Explore these optional code suggestions:

CategorySuggestion                                                                                                                                    Impact
Possible issue
Include full statement in JSON
Suggestion Impact:The commit removed the existing MarshalJSON that omitted the statement, aligning with the suggestion’s goal to avoid an incomplete JSON output. Although it didn’t add the statement to the JSON as proposed, deleting the method prevents the custom, incomplete serialization and allows default behavior (which can include the full statement), addressing the issue.

code diff:

-// Todo: It seems odd that this does not contain the statement.
-// (See also the equivalent method in attestation.go)
-func (a slsaProvenanceV1) MarshalJSON() ([]byte, error) {
-	val := struct {
-		Type               string                      `json:"type"`
-		PredicateType      string                      `json:"predicateType"`
-		PredicateBuildType string                      `json:"predicateBuildType"`
-		Signatures         []signature.EntitySignature `json:"signatures"`
-	}{
-		Type:               a.statement.Type,
-		PredicateType:      a.statement.PredicateType,
-		PredicateBuildType: a.statement.Predicate.BuildDefinition.BuildType,
-		Signatures:         a.signatures,
-	}
-
-	return json.Marshal(val)
-}

Update the MarshalJSON method for slsaProvenanceV1 to include the full
attestation statement in the JSON output, ensuring complete data is available
for policy evaluation.

internal/attestation/slsa_provenance_v1.go [103-117]

 func (a slsaProvenanceV1) MarshalJSON() ([]byte, error) {
+	// Note: a.Statement() returns the raw JSON of the statement, which is
+	// what we want to include in the output.
+	statementBytes := a.Statement()
+	var statementJSON json.RawMessage
+	if statementBytes != nil {
+		statementJSON = json.RawMessage(statementBytes)
+	}
+
 	val := struct {
 		Type               string                      `json:"type"`
 		PredicateType      string                      `json:"predicateType"`
 		PredicateBuildType string                      `json:"predicateBuildType"`
 		Signatures         []signature.EntitySignature `json:"signatures"`
+		Statement          json.RawMessage             `json:"statement,omitempty"`
 	}{
-		Type:               a.statement.Type,
-		PredicateType:      a.statement.PredicateType,
-		PredicateBuildType: a.statement.Predicate.BuildDefinition.BuildType,
-		Signatures:         a.signatures,
+		Type:               a.Type(),
+		PredicateType:      a.PredicateType(),
+		PredicateBuildType: a.PredicateBuildType(),
+		Signatures:         a.Signatures(),
+		Statement:          statementJSON,
 	}
 
 	return json.Marshal(val)
 }

[Suggestion processed]

Suggestion importance[1-10]: 8

__

Why: The suggestion correctly identifies that the MarshalJSON implementation for slsaProvenanceV1 is incomplete, which would prevent policy evaluation on the attestation statement. The proposed fix is accurate and necessary for the feature to function correctly.

Medium
Validate attestation against JSON schema
Suggestion Impact:The commit added schema validation of the embedded attestation data against the SLSA v1.0 schema, returning an error if non-conformant.

code diff:

+	"github.com/conforma/cli/pkg/schema"
 )
 
 const (
@@ -64,6 +65,14 @@
 		return nil, fmt.Errorf("cannot create signed entity: %w", err)
 	}
 
+	// Validate against SLSA v1 schema
+	var schemaValidation any
+	if err := json.Unmarshal(embedded, &schemaValidation); err == nil {
+		if err := schema.SLSA_Provenance_v1.Validate(schemaValidation); err != nil {
+			return nil, fmt.Errorf("attestation does not conform to SLSA v1.0 schema: %w", err)
+		}
+	}

Add a validation step in SLSAProvenanceFromSignatureV1 to check the parsed
attestation against the SLSA v1.0 JSON schema, ensuring data conformity before
further processing.

internal/attestation/slsa_provenance_v1.go [38-68]

 func SLSAProvenanceFromSignatureV1(sig oci.Signature) (Attestation, error) {
 	payload, err := payloadFromSig(sig)
 	if err != nil {
 		return nil, err
 	}
 
 	embedded, err := decodedPayload(payload)
 	if err != nil {
 		return nil, err
 	}
 
 	var statement in_toto.ProvenanceStatementSLSA1
 	if err := json.Unmarshal(embedded, &statement); err != nil {
 		return nil, fmt.Errorf("malformed attestation data: %w", err)
+	}
+
+	if err := schema.SLSA_Provenance_v1.Validate(bytes.NewReader(embedded)); err != nil {
+		return nil, fmt.Errorf("attestation does not conform to SLSA v1.0 schema: %w", err)
 	}
 
 	if statement.Type != in_toto.StatementInTotoV01 {
 		return nil, fmt.Errorf("unsupported attestation type: %s", statement.Type)
 	}
 
 	if statement.PredicateType != v1.PredicateSLSAProvenance {
 		return nil, fmt.Errorf("unsupported attestation predicate type: %s", statement.PredicateType)
 	}
 
 	signatures, err := createEntitySignatures(sig, payload)
 	if err != nil {
 		return nil, fmt.Errorf("cannot create signed entity: %w", err)
 	}
 
 	return slsaProvenanceV1{statement: statement, data: embedded, signatures: signatures}, nil
 }

[Suggestion processed]

Suggestion importance[1-10]: 7

__

Why: The suggestion correctly identifies that the new SLSA v1.0 attestation parsing logic lacks schema validation. Adding this validation improves the robustness of the implementation by ensuring that only compliant attestations are processed, which is a good practice.

Medium
High-level
Refactor duplicated attestation handling logic

Refactor duplicated code for handling SLSA v0.2 and v1.0 attestations in
acceptance tests. Abstract common logic for creating and signing attestations to
improve maintainability.

Examples:

acceptance/attestation/attestation.go [134-166]
func SignStatement(ctx context.Context, keyName string, statement in_toto.ProvenanceStatementSLSA02) ([]byte, error) {
	payload, err := json.Marshal(statement)
	if err != nil {
		return nil, err
	}

	signer, err := crypto.SignerWithKey(ctx, keyName)
	if err != nil {
		return nil, err
	}

 ... (clipped 23 lines)
acceptance/image/image.go [238-283]
func createAndPushAttestationInternal(ctx context.Context, imageName, keyName string, patches *godog.Table, useV1 bool) (context.Context, error) {
	var state *imageState
	ctx, err := testenv.SetupState(ctx, &state)
	if err != nil {
		return ctx, err
	}

	if state.Attestations[imageName] != "" {
		// we already created the attestation
		return ctx, nil

 ... (clipped 36 lines)

Solution Walkthrough:

Before:

// In acceptance/attestation/attestation.go
func SignStatement(ctx, keyName, statement v02) ([]byte, error) {
  // ... signing logic
}
func SignV1Statement(ctx, keyName, statement v1) ([]byte, error) {
  // ... identical signing logic
}

// In acceptance/image/image.go
func createAndPushAttestationInternal(..., useV1 bool) (..., error) {
  var signedAttestation []byte
  if useV1 {
    statementV1, _ := attestation.CreateV1StatementFor(...)
    signedAttestation, _ = attestation.SignV1Statement(..., statementV1)
  } else {
    statement, _ := attestation.CreateStatementFor(...)
    // ... apply patches
    signedAttestation, _ = attestation.SignStatement(..., *statement)
  }
  // ... common logic to push attestation
}

After:

// In acceptance/attestation/attestation.go
func SignStatement(ctx, keyName string, statement any) ([]byte, error) {
  // ... generic signing logic
}

// In acceptance/image/image.go
func createAndPushAttestationInternal(..., useV1 bool) (..., error) {
  var statement any
  if useV1 {
    statement, _ = attestation.CreateV1StatementFor(...)
  } else {
    v02Statement, _ := attestation.CreateStatementFor(...)
    statement, _ = applyPatches(v02Statement, ...)
  }
  signedAttestation, _ := attestation.SignStatement(..., statement)
  // ... common logic to push attestation
}
Suggestion importance[1-10]: 7

__

Why: The suggestion correctly identifies significant code duplication in acceptance/attestation/attestation.go and acceptance/image/image.go, and proposing a refactor to improve maintainability is a valid and impactful improvement.

Medium
General
Implement patching for SLSA v1

Implement patching for SLSA v1.0 attestations within the
createAndPushAttestationInternal function to ensure consistent behavior with
SLSA v0.2 attestations in the test suite.

acceptance/image/image.go [237-283]

 // createAndPushAttestationInternal is the internal implementation that supports both SLSA v0.2 and v1
 func createAndPushAttestationInternal(ctx context.Context, imageName, keyName string, patches *godog.Table, useV1 bool) (context.Context, error) {
 ...
 	var signedAttestation []byte
 
 	if useV1 {
 		// SLSA v1.0
 		statementV1, err := attestation.CreateV1StatementFor(imageName, image)
 		if err != nil {
 			return ctx, err
 		}
+
+		// TODO: Implement applyPatches for v1 statement type
+		// statementV1, err = applyPatchesV1(statementV1, patches)
+		// if err != nil {
+		// 	return ctx, err
+		// }
+
 		signedAttestation, err = attestation.SignV1Statement(ctx, keyName, statementV1)
 		if err != nil {
 			return ctx, err
 		}
 	} else {
 		// SLSA v0.2
 		statement, err := attestation.CreateStatementFor(imageName, image)
 ...
 		statement, err = applyPatches(statement, patches)
 		if err != nil {
 			return ctx, err
 		}
 
 		signedAttestation, err = attestation.SignStatement(ctx, keyName, *statement)
 ...
 	}
 ...
 }

[To ensure code accuracy, apply this suggestion manually]

Suggestion importance[1-10]: 5

__

Why: The suggestion correctly points out a missing feature (patching for SLSA v1 attestations) which leads to inconsistent behavior in test helpers. While not a bug in the core logic, implementing this would improve testability and maintainability.

Low
  • Update

@codecov
Copy link

codecov bot commented Dec 11, 2025

Codecov Report

❌ Patch coverage is 97.82609% with 1 line in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
pkg/schema/schema.go 66.66% 1 Missing ⚠️
Flag Coverage Δ
acceptance 55.98% <73.91%> (+0.18%) ⬆️
generative 18.97% <0.00%> (+<0.01%) ⬆️
integration 28.44% <0.00%> (-0.04%) ⬇️
unit 67.96% <97.82%> (+0.23%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
internal/attestation/attestation.go 90.16% <ø> (-1.39%) ⬇️
internal/attestation/slsa_provenance_02.go 100.00% <100.00%> (ø)
internal/attestation/slsa_provenance_v1.go 100.00% <100.00%> (ø)
...ation_snapshot_image/application_snapshot_image.go 83.06% <100.00%> (+4.26%) ⬆️
pkg/schema/schema.go 77.77% <66.66%> (-5.56%) ⬇️

... and 3 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@st3penta st3penta force-pushed the add-slsav1-support branch 6 times, most recently from 9eb54bf to 12ce1a1 Compare December 16, 2025 15:38
Implement parser and schema validation for SLSA v1.0 provenance
attestations to support the updated SLSA specification while
maintaining backward compatibility with v0.2.

Co-authored-by: Claude Code <noreply@anthropic.com>

Ref: https://issues.redhat.com/browse/EC-1581
Extends acceptance test framework to create and verify SLSA v1
attestations, enabling end-to-end testing of v1 provenance support.

Co-authored-by: Claude Code <noreply@anthropic.com>

Ref: https://issues.redhat.com/browse/EC-1581
Document the cli codebase for agent and developer reference.

Co-authored-by: Claude Code <noreply@anthropic.com>

Ref: https://issues.redhat.com/browse/EC-1581
Remove MarshalJSON methods from slsaProvenance, slsaProvenanceV1, and
provenance types as they are never invoked during JSON serialization.

These attestation objects are wrapped in AttestationResult struct, that
provides its own structure, so the custom MarshalJSON methods on the
inner attestation types are never called by json.Marshal().

The attestation objects themselves are only used internally for policy
evaluation and are never directly serialized to JSON in the current
codebase.

Co-authored-by: Claude Code <noreply@anthropic.com>

Ref: https://issues.redhat.com/browse/EC-1581
Add schema validation to SLSA v0.2 and v1.0 attestation parsers to
ensure incoming attestations conform to their respective schemas before
being accepted.

Co-authored-by: Claude Code <noreply@anthropic.com>

Ref: https://issues.redhat.com/browse/EC-1581
Copy link
Member

@simonbaird simonbaird left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lgtm.

@st3penta st3penta merged commit 092d551 into conforma:main Dec 19, 2025
12 checks passed
@st3penta st3penta deleted the add-slsav1-support branch December 19, 2025 11:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants