Skip to content

Commit

Permalink
test: add backref tests
Browse files Browse the repository at this point in the history
Signed-off-by: Mikhail Swift <mikhail@testifysec.com>
  • Loading branch information
mikhailswift authored and jkjell committed Sep 17, 2024
1 parent 113645e commit 71d7cbb
Showing 1 changed file with 152 additions and 0 deletions.
152 changes: 152 additions & 0 deletions verify_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import (
"github.com/in-toto/go-witness/dsse"
"github.com/in-toto/go-witness/policy"
"github.com/in-toto/go-witness/source"
"github.com/invopop/jsonschema"
"github.com/stretchr/testify/require"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
Expand Down Expand Up @@ -191,6 +192,71 @@ func TestVerify(t *testing.T) {
})
}

func TestBackRefs(t *testing.T) {
registerDummyAttestors()
testPolicy, functionarySigner := makePolicyWithPublicKeyFunctionary(t)
policyEnvelope, _, policyVerifier := signPolicyRSA(t, testPolicy)
workingDir := t.TempDir()

step1Result, err := Run(
"step01",
RunWithSigners(functionarySigner),
RunWithAttestors([]attestation.Attestor{
material.New(),
&dummySubjectAttestor{Data: "test"},
commandrun.New(
commandrun.WithCommand([]string{"bash", "-c", "echo 'test01' > test.txt"}),
),
product.New(),
}),
RunWithAttestationOpts(
attestation.WithWorkingDir(workingDir),
),
)
require.NoError(t, err)

step2Result, err := Run(
"step02",
RunWithSigners(functionarySigner),
RunWithAttestors([]attestation.Attestor{
material.New(),
&dummyBackrefAttestor{},
commandrun.New(
commandrun.WithCommand([]string{"bash", "-c", "echo 'test02' >> test.txt"}),
),
product.New(),
}),
RunWithAttestationOpts(
attestation.WithWorkingDir(workingDir),
),
)
require.NoError(t, err)

artifactSubject, err := cryptoutil.CalculateDigestSetFromFile(
filepath.Join(workingDir, "test.txt"),
[]cryptoutil.DigestValue{
{
GitOID: false,
Hash: crypto.SHA256,
},
},
)
require.NoError(t, err)
memorySource := source.NewMemorySource()
require.NoError(t, memorySource.LoadEnvelope("step01", step1Result.SignedEnvelope))
require.NoError(t, memorySource.LoadEnvelope("step02", step2Result.SignedEnvelope))

results, err := Verify(
context.Background(),
policyEnvelope,
[]cryptoutil.Verifier{policyVerifier},
VerifyWithCollectionSource(memorySource),
VerifyWithSubjectDigests([]cryptoutil.DigestSet{artifactSubject}),
)

require.NoError(t, err, fmt.Sprintf("failed with results: %+v", results))
}

func makePolicy(functionary policy.Functionary, publicKey policy.PublicKey, roots map[string]policy.Root) policy.Policy {
step01 := policy.Step{
Name: "step01",
Expand Down Expand Up @@ -276,3 +342,89 @@ func createTestRSAKey(t *testing.T) cryptoutil.Signer {
signer := cryptoutil.NewRSASigner(privKey, crypto.SHA256)
return signer
}

const (
dummySubjectAttestorName = "subject attestor"
dummySubjectAttestorType = "test/subjectattestor"
dummyBackrefAttestorName = "backref attestor"
dummyBackrefAttestorType = "test/backrefattestor"
matchSubjectName = "matchSubject"
)

// policy verification currently needs attestors to be registers to properly validate them
func registerDummyAttestors() {
attestation.RegisterAttestation(dummyBackrefAttestorName, dummyBackrefAttestorType, attestation.PreMaterialRunType, func() attestation.Attestor { return &dummyBackrefAttestor{} })
attestation.RegisterAttestation(dummySubjectAttestorName, dummySubjectAttestorType, attestation.PreMaterialRunType, func() attestation.Attestor { return &dummySubjectAttestor{} })
}

// dummySubjectAttestor is a test attestor used to create a subject on an attestation.
// this subject will be used to discover this attestor when searching by back ref subjects
// from a subsequent step in the policy.
type dummySubjectAttestor struct {
Data string
}

func (a *dummySubjectAttestor) Name() string {
return dummySubjectAttestorName
}

func (a *dummySubjectAttestor) Type() string {
return dummySubjectAttestorType
}

func (a *dummySubjectAttestor) RunType() attestation.RunType {
return attestation.PreMaterialRunType
}

func (a *dummySubjectAttestor) Attest(ctx *attestation.AttestationContext) error {
return nil
}

func (a *dummySubjectAttestor) Schema() *jsonschema.Schema {
return nil
}

func (a *dummySubjectAttestor) Subjects() map[string]cryptoutil.DigestSet {
return map[string]cryptoutil.DigestSet{
matchSubjectName: {
{Hash: crypto.SHA256}: "abcde",
},
}
}

// dummyBackrefAttestor is a test attestor used to expose a back ref subject, used to find
// attestations from preceding steps.
// for a practical example of this, consider policy that enforces two steps: a test step and a build step that produces a binary.
// when we begin policy evaluation, we only know two things: the hash of the binary, and the steps the policy expects.
// when we look up attestations that contain a product matching the binary's hash and satisfies the build step of the policy.
// that build attestation may contain a back ref subject that is the hash of the git commit, which also appears on the test attestation.
// we can then use this back ref subject to link the test attestation to the build attestation during policy evaluation.
type dummyBackrefAttestor struct{}

func (a *dummyBackrefAttestor) Name() string {
return dummyBackrefAttestorName
}

func (a *dummyBackrefAttestor) Type() string {
return dummyBackrefAttestorType
}

func (a *dummyBackrefAttestor) RunType() attestation.RunType {
return attestation.PreMaterialRunType
}

func (a *dummyBackrefAttestor) Attest(ctx *attestation.AttestationContext) error {
return nil
}

func (a *dummyBackrefAttestor) Schema() *jsonschema.Schema {
return nil
}

func (a *dummyBackrefAttestor) BackRefs() map[string]cryptoutil.DigestSet {
return map[string]cryptoutil.DigestSet{
matchSubjectName: {
{Hash: crypto.SHA256}: "abcde",
},
}
}

0 comments on commit 71d7cbb

Please sign in to comment.