mirror of
https://github.com/anchore/syft.git
synced 2025-11-17 08:23:15 +01:00
allow convert to take stdin (#1570)
Signed-off-by: Alex Goodman <alex.goodman@anchore.com>
This commit is contained in:
parent
57a13ae355
commit
9b9a7d6c98
@ -14,8 +14,9 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
convertExample = ` {{.appName}} {{.command}} img.syft.json -o spdx-json convert a syft SBOM to spdx-json, output goes to stdout in table format, by default
|
convertExample = ` {{.appName}} {{.command}} img.syft.json -o spdx-json convert a syft SBOM to spdx-json, output goes to stdout
|
||||||
{{.appName}} {{.command}} img.syft.json -o cyclonedx-json=img.cdx.json convert a syft SBOM to CycloneDX, output goes to a file named img.cdx.json
|
{{.appName}} {{.command}} img.syft.json -o cyclonedx-json=img.cdx.json convert a syft SBOM to CycloneDX, output is written to the file "img.cdx.json""
|
||||||
|
{{.appName}} {{.command}} - -o spdx-json convert an SBOM from STDIN to spdx-json
|
||||||
`
|
`
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@ -3,6 +3,7 @@ package convert
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/anchore/syft/cmd/syft/cli/options"
|
"github.com/anchore/syft/cmd/syft/cli/options"
|
||||||
@ -26,15 +27,23 @@ func Run(_ context.Context, app *config.Application, args []string) error {
|
|||||||
|
|
||||||
// this can only be a SBOM file
|
// this can only be a SBOM file
|
||||||
userInput := args[0]
|
userInput := args[0]
|
||||||
f, err := os.Open(userInput)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed to open SBOM file: %w", err)
|
|
||||||
}
|
|
||||||
defer func() {
|
|
||||||
_ = f.Close()
|
|
||||||
}()
|
|
||||||
|
|
||||||
sbom, _, err := formats.Decode(f)
|
var reader io.ReadCloser
|
||||||
|
|
||||||
|
if userInput == "-" {
|
||||||
|
reader = os.Stdin
|
||||||
|
} else {
|
||||||
|
f, err := os.Open(userInput)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to open SBOM file: %w", err)
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
_ = f.Close()
|
||||||
|
}()
|
||||||
|
reader = f
|
||||||
|
}
|
||||||
|
|
||||||
|
sbom, _, err := formats.Decode(reader)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to decode SBOM: %w", err)
|
return fmt.Errorf("failed to decode SBOM: %w", err)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -10,14 +10,9 @@ import (
|
|||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestConvertCmdFlags(t *testing.T) {
|
func TestAllFormatsConvertable(t *testing.T) {
|
||||||
assertions := []traitAssertion{
|
assertions := []traitAssertion{
|
||||||
func(tb testing.TB, stdout, _ string, _ int) {
|
assertStdoutLengthGreaterThan(1000),
|
||||||
tb.Helper()
|
|
||||||
if len(stdout) < 1000 {
|
|
||||||
tb.Errorf("there may not be any report output (len=%d)", len(stdout))
|
|
||||||
}
|
|
||||||
},
|
|
||||||
assertSuccessfulReturnCode,
|
assertSuccessfulReturnCode,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
50
test/cli/convert_cmd_test.go
Normal file
50
test/cli/convert_cmd_test.go
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
package cli
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestConvertCmd(t *testing.T) {
|
||||||
|
assertions := []traitAssertion{
|
||||||
|
assertInOutput("PackageName: musl-utils"),
|
||||||
|
assertSuccessfulReturnCode,
|
||||||
|
}
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
from string
|
||||||
|
to string
|
||||||
|
}{
|
||||||
|
{from: "syft-json", to: "spdx-tag-value"},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range tests {
|
||||||
|
t.Run(fmt.Sprintf("from %s to %s", test.from, test.to), func(t *testing.T) {
|
||||||
|
sbomArgs := []string{"dir:./test-fixtures/image-pkg-coverage", "-o", test.from}
|
||||||
|
cmd, stdout, stderr := runSyft(t, nil, sbomArgs...)
|
||||||
|
if cmd.ProcessState.ExitCode() != 0 {
|
||||||
|
t.Log("STDOUT:\n", stdout)
|
||||||
|
t.Log("STDERR:\n", stderr)
|
||||||
|
t.Log("COMMAND:", strings.Join(cmd.Args, " "))
|
||||||
|
t.Fatalf("failure executing syft creating an sbom")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
convertArgs := []string{"convert", "-", "-o", test.to}
|
||||||
|
cmd = getSyftCommand(t, convertArgs...)
|
||||||
|
|
||||||
|
cmd.Stdin = strings.NewReader(stdout)
|
||||||
|
stdout, stderr = runCommandObj(t, cmd, nil, false)
|
||||||
|
|
||||||
|
for _, traitFn := range 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, " "))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -177,7 +177,7 @@ func runSyftCommand(t testing.TB, env map[string]string, expectError bool, args
|
|||||||
t.Errorf("STDOUT: %s", stdout)
|
t.Errorf("STDOUT: %s", stdout)
|
||||||
t.Errorf("STDERR: %s", stderr)
|
t.Errorf("STDERR: %s", stderr)
|
||||||
|
|
||||||
// this probably indicates a timeout
|
// this probably indicates a timeout... lets run it again with more verbosity to help debug issues
|
||||||
args = append(args, "-vv")
|
args = append(args, "-vv")
|
||||||
cmd = getSyftCommand(t, args...)
|
cmd = getSyftCommand(t, args...)
|
||||||
|
|
||||||
@ -194,6 +194,48 @@ func runSyftCommand(t testing.TB, env map[string]string, expectError bool, args
|
|||||||
return cmd, stdout, stderr
|
return cmd, stdout, stderr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func runCommandObj(t testing.TB, cmd *exec.Cmd, env map[string]string, expectError bool) (string, string) {
|
||||||
|
cancel := make(chan bool, 1)
|
||||||
|
defer func() {
|
||||||
|
cancel <- true
|
||||||
|
}()
|
||||||
|
|
||||||
|
if env == nil {
|
||||||
|
env = make(map[string]string)
|
||||||
|
}
|
||||||
|
|
||||||
|
// we should not have tests reaching out for app update checks
|
||||||
|
env["SYFT_CHECK_FOR_APP_UPDATE"] = "false"
|
||||||
|
|
||||||
|
timeout := func() {
|
||||||
|
select {
|
||||||
|
case <-cancel:
|
||||||
|
return
|
||||||
|
case <-time.After(60 * time.Second):
|
||||||
|
}
|
||||||
|
|
||||||
|
if cmd != nil && cmd.Process != nil {
|
||||||
|
// get a stack trace printed
|
||||||
|
err := cmd.Process.Signal(syscall.SIGABRT)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("error aborting: %+v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
go timeout()
|
||||||
|
|
||||||
|
stdout, stderr, err := runCommand(cmd, env)
|
||||||
|
|
||||||
|
if !expectError && err != nil && stdout == "" {
|
||||||
|
t.Errorf("error running syft: %+v", err)
|
||||||
|
t.Errorf("STDOUT: %s", stdout)
|
||||||
|
t.Errorf("STDERR: %s", stderr)
|
||||||
|
}
|
||||||
|
|
||||||
|
return stdout, stderr
|
||||||
|
}
|
||||||
|
|
||||||
func runCosign(t testing.TB, env map[string]string, args ...string) (*exec.Cmd, string, string) {
|
func runCosign(t testing.TB, env map[string]string, args ...string) (*exec.Cmd, string, string) {
|
||||||
cmd := getCommand(t, ".tmp/cosign", args...)
|
cmd := getCommand(t, ".tmp/cosign", args...)
|
||||||
if env == nil {
|
if env == nil {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user