Skip to content
Closed
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
}
---

[TestWriteInputFile/single_attestations - 1]
[TestBuildInput/single_attestations - 1]
{
"attestations": [
{
Expand Down Expand Up @@ -55,7 +55,7 @@
}
---

[TestWriteInputFile/multiple_attestations - 1]
[TestBuildInput/multiple_attestations - 1]
{
"attestations": [
{
Expand Down Expand Up @@ -115,7 +115,7 @@
}
---

[TestWriteInputFile/image_signatures - 1]
[TestBuildInput/image_signatures - 1]
{
"attestations": [
{
Expand Down Expand Up @@ -173,7 +173,7 @@
}
---

[TestWriteInputFile/image_config - 1]
[TestBuildInput/image_config - 1]
{
"attestations": null,
"image": {
Expand Down Expand Up @@ -205,7 +205,7 @@
}
---

[TestWriteInputFile/parent_image_config - 1]
[TestBuildInput/parent_image_config - 1]
{
"attestations": null,
"image": {
Expand Down Expand Up @@ -240,7 +240,7 @@
}
---

[TestWriteInputFile/attestation_with_signature - 1]
[TestBuildInput/attestation_with_signature - 1]
{
"attestations": [
{
Expand Down Expand Up @@ -300,51 +300,7 @@
}
---

[TestWriteInputFileMultipleAttestations - 1]
{
"attestations": [
{
"statement": {
"_type": "https://in-toto.io/Statement/v0.1",
"predicate": {
"buildType": "https://tekton.dev/attestations/chains/pipelinerun@v2",
"builder": {
"id": ""
},
"invocation": {
"configSource": {}
}
},
"predicateType": "https://slsa.dev/provenance/v0.2",
"subject": null
}
}
],
"image": {
"ref": "registry.io/repository/image:tag",
"source": {}
},
"policy_spec": {},
"snapshot": {
"application": "",
"artifacts": {},
"components": [
{
"containerImage": "registry.io/repository/image:tag",
"name": "",
"source": {}
},
{
"containerImage": "registry.io/other-repository/image2:tag",
"name": "",
"source": {}
}
]
}
}
---

[TestWriteInputFile/component_with_source - 1]
[TestBuildInput/component_with_source - 1]
{
"attestations": null,
"image": {
Expand Down Expand Up @@ -376,7 +332,7 @@
}
---

[TestWriteInputFile/component_name_in_input - 1]
[TestBuildInput/component_name_in_input - 1]
{
"attestations": null,
"component_name": "my-component",
Expand Down Expand Up @@ -404,7 +360,7 @@
}
---

[TestWriteInputFile/policy_spec_with_volatile_config - 1]
[TestBuildInput/policy_spec_with_volatile_config - 1]
{
"attestations": null,
"component_name": "test-component",
Expand Down Expand Up @@ -452,3 +408,47 @@
}
}
---

[TestBuildInputMultipleAttestations - 1]
{
"attestations": [
{
"statement": {
"_type": "https://in-toto.io/Statement/v0.1",
"predicate": {
"buildType": "https://tekton.dev/attestations/chains/pipelinerun@v2",
"builder": {
"id": ""
},
"invocation": {
"configSource": {}
}
},
"predicateType": "https://slsa.dev/provenance/v0.2",
"subject": null
}
}
],
"image": {
"ref": "registry.io/repository/image:tag",
"source": {}
},
"policy_spec": {},
"snapshot": {
"application": "",
"artifacts": {},
"components": [
{
"containerImage": "registry.io/repository/image:tag",
"name": "",
"source": {}
},
{
"containerImage": "registry.io/other-repository/image2:tag",
"name": "",
"source": {}
}
]
}
}
---
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,6 @@ import (
"encoding/json"
"errors"
"fmt"
"os"
"path"
"runtime/trace"

ecc "github.com/conforma/crds/api/v1alpha1"
Expand All @@ -34,15 +32,13 @@ import (
cosignOCI "github.com/sigstore/cosign/v3/pkg/oci"
ociremote "github.com/sigstore/cosign/v3/pkg/oci/remote"
log "github.com/sirupsen/logrus"
"github.com/spf13/afero"

"github.com/conforma/cli/internal/attestation"
"github.com/conforma/cli/internal/evaluator"
"github.com/conforma/cli/internal/fetchers/oci/config"
"github.com/conforma/cli/internal/fetchers/oci/files"
"github.com/conforma/cli/internal/policy"
"github.com/conforma/cli/internal/signature"
"github.com/conforma/cli/internal/utils"
"github.com/conforma/cli/internal/utils/oci"
"github.com/conforma/cli/pkg/schema"
)
Expand Down Expand Up @@ -419,15 +415,17 @@ type Input struct {
PolicySpec ecc.EnterpriseContractPolicySpec `json:"policy_spec,omitempty"`
}

// WriteInputFile writes the JSON from the attestations to input.json in a random temp dir
func (a *ApplicationSnapshotImage) WriteInputFile(ctx context.Context) (string, []byte, error) {
log.Debugf("Attempting to write %d attestations to input file", len(a.attestations))
// BuildInput constructs the OPA input as a Go map and JSON bytes without disk I/O.
// The JSON marshal/unmarshal round-trip ensures correct types for OPA (e.g. numbers
// as float64, consistent key ordering).
func (a *ApplicationSnapshotImage) BuildInput(_ context.Context) (map[string]any, []byte, error) {
log.Debugf("Building input for %d attestations", len(a.attestations))

var attestations []attestationData
for _, a := range a.attestations {
for _, att := range a.attestations {
attestations = append(attestations, attestationData{
Statement: a.Statement(),
Signatures: a.Signatures(),
Statement: att.Statement(),
Signatures: att.Signatures(),
})
}

Expand All @@ -452,31 +450,15 @@ func (a *ApplicationSnapshotImage) WriteInputFile(ctx context.Context) (string,
}
}

fs := utils.FS(ctx)
inputDir, err := afero.TempDir(fs, "", "ecp_input.")
if err != nil {
log.Debug("Problem making temp dir!")
return "", nil, err
}
log.Debugf("Created dir %s", inputDir)
inputJSONPath := path.Join(inputDir, "input.json")

f, err := fs.OpenFile(inputJSONPath, os.O_CREATE|os.O_WRONLY|os.O_EXCL, 0644)
if err != nil {
log.Debugf("Problem creating file in %s", inputDir)
return "", nil, err
}
defer f.Close()

inputJSON, err := json.Marshal(input)
if err != nil {
return "", nil, fmt.Errorf("input to JSON: %w", err)
return nil, nil, fmt.Errorf("input to JSON: %w", err)
}

if _, err := f.Write(inputJSON); err != nil {
return "", nil, fmt.Errorf("write input to file: %w", err)
var parsed map[string]any
if err := json.Unmarshal(inputJSON, &parsed); err != nil {
return nil, nil, fmt.Errorf("parse input JSON: %w", err)
}

log.Debugf("Done preparing input file:\n%s", inputJSONPath)
return inputJSONPath, inputJSON, nil
return parsed, inputJSON, nil
}
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,6 @@ import (
"github.com/sigstore/cosign/v3/pkg/oci/static"
cosignTypes "github.com/sigstore/cosign/v3/pkg/types"
"github.com/sigstore/sigstore/pkg/signature/payload"
"github.com/spf13/afero"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
"github.com/stretchr/testify/require"
Expand Down Expand Up @@ -122,7 +121,7 @@ func createSimpleAttestation(statement *in_toto.ProvenanceStatementSLSA02, o ...
return a
}

func TestWriteInputFile(t *testing.T) {
func TestBuildInput(t *testing.T) {
cases := []struct {
name string
snapshot ApplicationSnapshotImage
Expand Down Expand Up @@ -260,8 +259,7 @@ func TestWriteInputFile(t *testing.T) {

for _, tt := range cases {
t.Run(tt.name, func(t *testing.T) {
fs := afero.NewMemMapFs()
ctx := utils.WithFS(context.Background(), fs)
ctx := context.Background()
tt.snapshot.snapshot = app.SnapshotSpec{
Components: []app.SnapshotComponent{
{
Expand All @@ -272,25 +270,16 @@ func TestWriteInputFile(t *testing.T) {
},
},
}
inputPath, inputJSON, err := tt.snapshot.WriteInputFile(ctx)
inputMap, inputJSON, err := tt.snapshot.BuildInput(ctx)

assert.NoError(t, err)
assert.NotEmpty(t, inputPath)
assert.Regexp(t, `/ecp_input.\d+/input.json`, inputPath)
fileExists, err := afero.Exists(fs, inputPath)
assert.NoError(t, err)
assert.True(t, fileExists)

bytes, err := afero.ReadFile(fs, inputPath)
assert.NoError(t, err)
snaps.MatchJSON(t, bytes)

assert.JSONEq(t, string(inputJSON), string(bytes))
assert.NotNil(t, inputMap)
snaps.MatchJSON(t, inputJSON)
})
}
}

func TestWriteInputFileMultipleAttestations(t *testing.T) {
func TestBuildInputMultipleAttestations(t *testing.T) {
att := createSimpleAttestation(nil)
snapshot := app.SnapshotSpec{
Components: []app.SnapshotComponent{
Expand All @@ -308,22 +297,12 @@ func TestWriteInputFileMultipleAttestations(t *testing.T) {
snapshot: snapshot,
}

fs := afero.NewMemMapFs()
ctx := utils.WithFS(context.Background(), fs)
inputPath, inputJSON, err := a.WriteInputFile(ctx)

assert.NoError(t, err)
assert.NotEmpty(t, inputPath)
assert.Regexp(t, `/ecp_input.\d+/input.json`, inputPath)
fileExists, err := afero.Exists(fs, inputPath)
assert.NoError(t, err)
assert.True(t, fileExists)
ctx := context.Background()
inputMap, inputJSON, err := a.BuildInput(ctx)

bytes, err := afero.ReadFile(fs, inputPath)
assert.NoError(t, err)
snaps.MatchJSON(t, bytes)

assert.JSONEq(t, string(inputJSON), string(bytes))
assert.NotNil(t, inputMap)
snaps.MatchJSON(t, inputJSON)
}

func TestNewApplicationSnapshotImage(t *testing.T) {
Expand Down
Loading
Loading