github.com/noqcks/syft@v0.0.0-20230920222752-a9e2c4e288e5/test/cli/cyclonedx_valid_test.go (about) 1 package cli 2 3 import ( 4 "os" 5 "strings" 6 "testing" 7 8 "github.com/anchore/stereoscope/pkg/imagetest" 9 ) 10 11 // We have schema validation mechanims in schema/cyclonedx/ 12 // This test allows us to double check that validation against the cyclonedx-cli tool 13 func TestValidCycloneDX(t *testing.T) { 14 imageFixture := func(t *testing.T) string { 15 fixtureImageName := "image-pkg-coverage" 16 imagetest.GetFixtureImage(t, "docker-archive", fixtureImageName) 17 tarPath := imagetest.GetFixtureImageTarPath(t, fixtureImageName) 18 return "docker-archive:" + tarPath 19 } 20 21 // TODO update image to exercise entire cyclonedx schema 22 tests := []struct { 23 name string 24 subcommand string 25 args []string 26 fixture func(*testing.T) string 27 assertions []traitAssertion 28 }{ 29 { 30 name: "validate cyclonedx output", 31 subcommand: "packages", 32 args: []string{"-o", "cyclonedx-json"}, 33 fixture: imageFixture, 34 assertions: []traitAssertion{ 35 assertSuccessfulReturnCode, 36 assertValidCycloneDX, 37 }, 38 }, 39 } 40 41 for _, test := range tests { 42 t.Run(test.name, func(t *testing.T) { 43 fixtureRef := test.fixture(t) 44 args := []string{ 45 test.subcommand, fixtureRef, "-q", 46 } 47 args = append(args, test.args...) 48 49 cmd, stdout, stderr := runSyft(t, nil, args...) 50 for _, traitFn := range test.assertions { 51 traitFn(t, stdout, stderr, cmd.ProcessState.ExitCode()) 52 } 53 logOutputOnFailure(t, cmd, stdout, stderr) 54 55 validateCycloneDXJSON(t, stdout) 56 }) 57 } 58 } 59 60 func assertValidCycloneDX(tb testing.TB, stdout, stderr string, rc int) { 61 tb.Helper() 62 f, err := os.CreateTemp("", "tmpfile-") 63 if err != nil { 64 tb.Fatal(err) 65 } 66 67 // close and remove the temporary file at the end of the program 68 defer f.Close() 69 defer os.Remove(f.Name()) 70 71 data := []byte(stdout) 72 73 if _, err := f.Write(data); err != nil { 74 tb.Fatal(err) 75 } 76 77 args := []string{ 78 "validate", 79 "--input-format", 80 "json", 81 "--input-version", 82 "v1_4", 83 "--input-file", 84 "/sbom", 85 } 86 87 cmd, stdout, stderr := runCycloneDXInDocker(tb, nil, "cyclonedx/cyclonedx-cli", f, args...) 88 if cmd.ProcessState.ExitCode() != 0 { 89 tb.Errorf("expected no validation failures for cyclonedx-cli but got rc=%d", rc) 90 } 91 92 logOutputOnFailure(tb, cmd, stdout, stderr) 93 } 94 95 // validate --input-format json --input-version v1_4 --input-file bom.json 96 func validateCycloneDXJSON(t *testing.T, stdout string) { 97 f, err := os.CreateTemp("", "tmpfile-") 98 if err != nil { 99 t.Fatal(err) 100 } 101 102 // close and remove the temporary file at the end of the program 103 defer f.Close() 104 defer os.Remove(f.Name()) 105 106 data := []byte(stdout) 107 108 if _, err := f.Write(data); err != nil { 109 t.Fatal(err) 110 } 111 112 args := []string{ 113 "validate", 114 "--input-format", 115 "json", 116 "--input-version", 117 "v1_4", 118 "--input-file", 119 "/sbom", 120 } 121 122 cmd, stdout, stderr := runCycloneDXInDocker(t, nil, "cyclonedx/cyclonedx-cli", f, args...) 123 if strings.Contains(stdout, "BOM is not valid") { 124 t.Errorf("expected no validation failures for cyclonedx-cli but found invalid BOM") 125 } 126 127 logOutputOnFailure(t, cmd, stdout, stderr) 128 }