mirror of
https://github.com/anchore/syft.git
synced 2026-10-30 14:02:26 +01:00
510 - SBOM attestation stdout (#785)
add syft attest command to produce an attestation as application/vnd.in-toto+json to standard out using on disk PKI Signed-off-by: Christopher Phillips <christopher.phillips@anchore.com>
This commit is contained in:
@@ -0,0 +1,62 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestAttestCmd(t *testing.T) {
|
||||
coverageImage := "docker-archive:" + getFixtureImage(t, "image-pkg-coverage")
|
||||
tests := []struct {
|
||||
name string
|
||||
args []string
|
||||
env map[string]string
|
||||
assertions []traitAssertion
|
||||
pw string
|
||||
}{
|
||||
{
|
||||
name: "no-args-shows-help",
|
||||
args: []string{"attest"},
|
||||
assertions: []traitAssertion{
|
||||
assertInOutput("an image/directory argument is required"), // specific error that should be shown
|
||||
assertInOutput("from a container image as the predicate of an in-toto attestation"), // excerpt from help description
|
||||
assertFailingReturnCode,
|
||||
},
|
||||
pw: "",
|
||||
},
|
||||
{
|
||||
name: "can encode syft.json as the predicate given a password",
|
||||
args: []string{"attest", "-o", "json", coverageImage},
|
||||
assertions: []traitAssertion{
|
||||
assertSuccessfulReturnCode,
|
||||
// assertVerifyAttestation(coverageImage), Follow up on this assertion with verify blog or ephemperal registry
|
||||
},
|
||||
pw: "test",
|
||||
},
|
||||
{
|
||||
name: "can encode syft.json as the predicate given a blank password",
|
||||
args: []string{"attest", "-o", "json", coverageImage},
|
||||
assertions: []traitAssertion{
|
||||
assertSuccessfulReturnCode,
|
||||
// assertVerifyAttestation(coverageImage), Follow up on this assertion with verify blog or ephemperal registry
|
||||
},
|
||||
pw: "",
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
cleanup := setupPKI(t, test.pw)
|
||||
defer cleanup()
|
||||
cmd, stdout, stderr := runSyft(t, test.env, test.args...)
|
||||
for _, traitFn := range test.assertions {
|
||||
traitFn(t, stdout, stderr, cmd.ProcessState.ExitCode())
|
||||
}
|
||||
if t.Failed() {
|
||||
t.Log("STDOUT:\n", stdout)
|
||||
t.Log("STDERR:\n", stderr)
|
||||
t.Log("COMMAND:", strings.Join(cmd.Args, " "))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,8 @@ package cli
|
||||
import (
|
||||
"encoding/json"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strings"
|
||||
"testing"
|
||||
@@ -116,6 +118,46 @@ func assertSuccessfulReturnCode(tb testing.TB, _, _ string, rc int) {
|
||||
}
|
||||
}
|
||||
|
||||
func assertVerifyAttestation(coverageImage string) traitAssertion {
|
||||
return func(tb testing.TB, stdout, _ string, _ int) {
|
||||
tb.Helper()
|
||||
cosignPath := filepath.Join(repoRoot(tb), ".tmp/cosign")
|
||||
err := os.WriteFile("attestation.json", []byte(stdout), 0664)
|
||||
if err != nil {
|
||||
tb.Errorf("could not write attestation to disk")
|
||||
}
|
||||
defer os.Remove("attestation.json")
|
||||
attachCmd := exec.Command(
|
||||
cosignPath,
|
||||
"attach",
|
||||
"attestation",
|
||||
"--attestation",
|
||||
"attestation.json",
|
||||
coverageImage, // TODO which remote image to use?
|
||||
)
|
||||
|
||||
stdout, stderr := runCommand(attachCmd, nil)
|
||||
if attachCmd.ProcessState.ExitCode() != 0 {
|
||||
tb.Log("STDOUT", stdout)
|
||||
tb.Log("STDERR", stderr)
|
||||
tb.Fatalf("could not attach image")
|
||||
}
|
||||
|
||||
verifyCmd := exec.Command(
|
||||
cosignPath,
|
||||
"verify-attestation",
|
||||
coverageImage, // TODO which remote image to use?
|
||||
)
|
||||
|
||||
stdout, stderr = runCommand(verifyCmd, nil)
|
||||
if attachCmd.ProcessState.ExitCode() != 0 {
|
||||
tb.Log("STDOUT", stdout)
|
||||
tb.Log("STDERR", stderr)
|
||||
tb.Fatalf("could not verify attestation")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func assertFileExists(file string) traitAssertion {
|
||||
return func(tb testing.TB, _, _ string, _ int) {
|
||||
tb.Helper()
|
||||
|
||||
@@ -14,6 +14,39 @@ import (
|
||||
"github.com/anchore/stereoscope/pkg/imagetest"
|
||||
)
|
||||
|
||||
func setupPKI(t *testing.T, pw string) func() {
|
||||
err := os.Setenv("COSIGN_PASSWORD", pw)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
cosignPath := filepath.Join(repoRoot(t), ".tmp/cosign")
|
||||
cmd := exec.Command(cosignPath, "generate-key-pair")
|
||||
stdout, stderr := runCommand(cmd, nil)
|
||||
if cmd.ProcessState.ExitCode() != 0 {
|
||||
t.Log("STDOUT", stdout)
|
||||
t.Log("STDERR", stderr)
|
||||
t.Fatalf("could not generate keypair")
|
||||
}
|
||||
|
||||
return func() {
|
||||
err := os.Unsetenv("COSIGN_PASSWORD")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
err = os.Remove("cosign.key")
|
||||
if err != nil {
|
||||
t.Fatalf("could not cleanup cosign.key")
|
||||
}
|
||||
|
||||
err = os.Remove("cosign.pub")
|
||||
if err != nil {
|
||||
t.Fatalf("could not cleanup cosign.key")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func getFixtureImage(t testing.TB, fixtureImageName string) string {
|
||||
t.Logf("obtaining fixture image for %s", fixtureImageName)
|
||||
imagetest.GetFixtureImage(t, "docker-archive", fixtureImageName)
|
||||
|
||||
Reference in New Issue
Block a user