Compare commits

...
3 Commits
Author SHA1 Message Date
Justin Chadwell b81c9805dc Allow scanning unpacked container filesystems (#1485)
* source: avoid second-step of symlink resolution in directory resolver

We can use the already existing file tree to peform symlink resolution
for FilesByPath, instead of traversing the symlinks again.

This moves all of the symlink logic into the indexing code, and then we
can rely on syft's resolution algorithm over the index in this part of
the codebase.

Signed-off-by: Justin Chadwell <me@jedevc.com>

* source: add base parameter to directory resolver

The new base parameter is an optional parameter for the directory
resolver that resolves all symlinks relative to this root. There are two
intended use cases:

- base = "/". The previous behavior, symlinks are resolved relative to
the root filesystem.
- base = path. Symlinks are resolved relative to the target filesystem,
allowing correct behavior when scanning unpacked container filesystems
on disk.

Signed-off-by: Justin Chadwell <me@jedevc.com>

* source: add tests for new base parameter

Signed-off-by: Justin Chadwell <me@jedevc.com>

---------

Signed-off-by: Justin Chadwell <me@jedevc.com>
2023-01-30 13:47:24 -05:00
Keith Zantow ba55963104 fix: allow template for syft convert (#1521) 2023-01-26 12:07:36 -05:00
Christopher Angelo Phillips 8c91605541 1465 attestation with private key (#1502)
1465 attestation with private key (#1502)

Signed-off-by: Christopher Phillips <christopher.phillips@anchore.com>
2023-01-26 11:19:13 -05:00
21 changed files with 275 additions and 92 deletions
+8 -3
View File
@@ -20,8 +20,7 @@ const (
attestHelp = attestExample + attestSchemeHelp
)
//nolint:dupl
func Attest(v *viper.Viper, app *config.Application, ro *options.RootOptions, po *options.PackagesOptions) *cobra.Command {
func Attest(v *viper.Viper, app *config.Application, ro *options.RootOptions, po *options.PackagesOptions, ao *options.AttestOptions) *cobra.Command {
cmd := &cobra.Command{
Use: "attest --output [FORMAT] <IMAGE>",
Short: "Generate an SBOM as an attestation for the given [SOURCE] container image",
@@ -50,11 +49,17 @@ func Attest(v *viper.Viper, app *config.Application, ro *options.RootOptions, po
},
}
// syft attest is an enhancment of the packages command, so it should have the same flags
// syft attest is an enhancement of the packages command, so it should have the same flags
err := po.AddFlags(cmd, v)
if err != nil {
log.Fatal(err)
}
// syft attest has its own options not included as part of the packages command
err = ao.AddFlags(cmd, v)
if err != nil {
log.Fatal(err)
}
return cmd
}
+11 -1
View File
@@ -97,6 +97,7 @@ func buildSBOM(app *config.Application, si source.Input, writer sbom.Writer, err
return sBytes, nil
}
//nolint:funlen
func execWorker(app *config.Application, si source.Input, writer sbom.Writer) <-chan error {
errs := make(chan error)
go func() {
@@ -131,9 +132,18 @@ func execWorker(app *config.Application, si source.Input, writer sbom.Writer) <-
}
args := []string{"attest", si.UserInput, "--type", "custom", "--predicate", f.Name()}
if app.Attest.Key != "" {
args = append(args, "--key", app.Attest.Key)
}
execCmd := exec.Command(cmd, args...)
execCmd.Env = os.Environ()
execCmd.Env = append(execCmd.Env, "COSIGN_EXPERIMENTAL=1")
if app.Attest.Key != "" {
execCmd.Env = append(execCmd.Env, fmt.Sprintf("COSIGN_PASSWORD=%s", app.Attest.Password))
} else {
// no key provided, use cosign's keyless mode
execCmd.Env = append(execCmd.Env, "COSIGN_EXPERIMENTAL=1")
}
// bus adapter for ui to hook into stdout via an os pipe
r, w, err := os.Pipe()
+2 -1
View File
@@ -45,12 +45,13 @@ func New() (*cobra.Command, error) {
// we also need the command to have information about the `root` options because of this alias
ro := &options.RootOptions{}
po := &options.PackagesOptions{}
ao := &options.AttestOptions{}
packagesCmd := Packages(v, app, ro, po)
// root options are also passed to the attestCmd so that a user provided config location can be discovered
poweruserCmd := PowerUser(v, app, ro)
convertCmd := Convert(v, app, ro, po)
attestCmd := Attest(v, app, ro, po)
attestCmd := Attest(v, app, ro, po, ao)
// rootCmd is currently an alias for the packages command
rootCmd := &cobra.Command{
+5 -3
View File
@@ -11,9 +11,9 @@ import (
"github.com/anchore/syft/syft"
)
func Run(ctx context.Context, app *config.Application, args []string) error {
func Run(_ context.Context, app *config.Application, args []string) error {
log.Warn("convert is an experimental feature, run `syft convert -h` for help")
writer, err := options.MakeWriter(app.Outputs, app.File, "")
writer, err := options.MakeWriter(app.Outputs, app.File, app.OutputTemplatePath)
if err != nil {
return err
}
@@ -30,7 +30,9 @@ func Run(ctx context.Context, app *config.Application, args []string) error {
if err != nil {
return fmt.Errorf("failed to open SBOM file: %w", err)
}
defer f.Close()
defer func() {
_ = f.Close()
}()
sbom, _, err := syft.Decode(f)
if err != nil {
+25
View File
@@ -0,0 +1,25 @@
package options
import (
"github.com/spf13/cobra"
"github.com/spf13/pflag"
"github.com/spf13/viper"
)
type AttestOptions struct {
Key string
}
var _ Interface = (*AttestOptions)(nil)
func (o AttestOptions) AddFlags(cmd *cobra.Command, v *viper.Viper) error {
cmd.Flags().StringVarP(&o.Key, "key", "k", "", "the key to use for the attestation")
return bindAttestConfigOptions(cmd.Flags(), v)
}
func bindAttestConfigOptions(flags *pflag.FlagSet, v *viper.Viper) error {
if err := v.BindPFlag("attest.key", flags.Lookup("key")); err != nil {
return err
}
return nil
}
+1
View File
@@ -47,6 +47,7 @@ type Application struct {
Log logging `yaml:"log" json:"log" mapstructure:"log"` // all logging-related options
Catalogers []string `yaml:"catalogers" json:"catalogers" mapstructure:"catalogers"`
Package pkg `yaml:"package" json:"package" mapstructure:"package"`
Attest attest `yaml:"attest" json:"attest" mapstructure:"attest"`
FileMetadata FileMetadata `yaml:"file-metadata" json:"file-metadata" mapstructure:"file-metadata"`
FileClassification fileClassification `yaml:"file-classification" json:"file-classification" mapstructure:"file-classification"`
FileContents fileContents `yaml:"file-contents" json:"file-contents" mapstructure:"file-contents"`
+13
View File
@@ -0,0 +1,13 @@
package config
import "github.com/spf13/viper"
type attest struct {
Key string `yaml:"key" json:"key" mapstructure:"key"`
Password string `yaml:"password" json:"password" mapstructure:"password"`
}
func (cfg attest) loadDefaultValues(v *viper.Viper) {
v.SetDefault("attest.key", "")
v.SetDefault("attest.password", "")
}
+46 -17
View File
@@ -37,6 +37,7 @@ type pathFilterFn func(string, os.FileInfo) bool
// directoryResolver implements path and content access for the directory data source.
type directoryResolver struct {
path string
base string
currentWdRelativeToRoot string
currentWd string
fileTree *filetree.FileTree
@@ -47,7 +48,7 @@ type directoryResolver struct {
errPaths map[string]error
}
func newDirectoryResolver(root string, pathFilters ...pathFilterFn) (*directoryResolver, error) {
func newDirectoryResolver(root string, base string, pathFilters ...pathFilterFn) (*directoryResolver, error) {
currentWD, err := os.Getwd()
if err != nil {
return nil, fmt.Errorf("could not get CWD: %w", err)
@@ -64,6 +65,18 @@ func newDirectoryResolver(root string, pathFilters ...pathFilterFn) (*directoryR
return nil, fmt.Errorf("could not evaluate root=%q symlinks: %w", root, err)
}
cleanBase := ""
if base != "" {
cleanBase, err = filepath.EvalSymlinks(base)
if err != nil {
return nil, fmt.Errorf("could not evaluate base=%q symlinks: %w", base, err)
}
cleanBase, err = filepath.Abs(cleanBase)
if err != nil {
return nil, err
}
}
var currentWdRelRoot string
if path.IsAbs(cleanRoot) {
currentWdRelRoot, err = filepath.Rel(cleanCWD, cleanRoot)
@@ -76,6 +89,7 @@ func newDirectoryResolver(root string, pathFilters ...pathFilterFn) (*directoryR
resolver := directoryResolver{
path: cleanRoot,
base: cleanBase,
currentWd: cleanCWD,
currentWdRelativeToRoot: currentWdRelRoot,
fileTree: filetree.NewFileTree(),
@@ -244,10 +258,25 @@ func (r directoryResolver) addSymlinkToIndex(p string, info os.FileInfo) (string
return "", fmt.Errorf("unable to readlink for path=%q: %w", p, err)
}
// note: if the link is not absolute (e.g, /dev/stderr -> fd/2 ) we need to resolve it relative to the directory
// in question (e.g. resolve to /dev/fd/2)
if !filepath.IsAbs(linkTarget) {
linkTarget = filepath.Join(filepath.Dir(p), linkTarget)
if filepath.IsAbs(linkTarget) {
// if the link is absolute (e.g, /bin/ls -> /bin/busybox) we need to
// resolve relative to the root of the base directory
linkTarget = filepath.Join(r.base, filepath.Clean(linkTarget))
} else {
// if the link is not absolute (e.g, /dev/stderr -> fd/2 ) we need to
// resolve it relative to the directory in question (e.g. resolve to
// /dev/fd/2)
if r.base == "" {
linkTarget = filepath.Join(filepath.Dir(p), linkTarget)
} else {
// if the base is set, then we first need to resolve the link,
// before finding it's location in the base
dir, err := filepath.Rel(r.base, filepath.Dir(p))
if err != nil {
return "", fmt.Errorf("unable to resolve relative path for path=%q: %w", p, err)
}
linkTarget = filepath.Join(r.base, filepath.Clean(filepath.Join("/", dir, linkTarget)))
}
}
ref, err := r.fileTree.AddSymLink(file.Path(p), file.Path(linkTarget))
@@ -336,14 +365,17 @@ func (r directoryResolver) FilesByPath(userPaths ...string) ([]Location, error)
}
// we should be resolving symlinks and preserving this information as a VirtualPath to the real file
evaluatedPath, err := filepath.EvalSymlinks(userStrPath)
exists, ref, err := r.fileTree.File(file.Path(userStrPath), filetree.FollowBasenameLinks)
if err != nil {
log.Tracef("unable to evaluate symlink for path=%q : %+v", userPath, err)
continue
}
if !exists {
continue
}
// TODO: why not use stored metadata?
fileMeta, err := os.Stat(evaluatedPath)
fileMeta, err := os.Stat(string(ref.RealPath))
if errors.Is(err, os.ErrNotExist) {
// note: there are other kinds of errors other than os.ErrNotExist that may be given that is platform
// specific, but essentially hints at the same overall problem (that the path does not exist). Such an
@@ -354,7 +386,7 @@ func (r directoryResolver) FilesByPath(userPaths ...string) ([]Location, error)
// invalid paths. This logging statement is meant to raise IO or permissions related problems.
var pathErr *os.PathError
if !errors.As(err, &pathErr) {
log.Warnf("path is not valid (%s): %+v", evaluatedPath, err)
log.Warnf("path is not valid (%s): %+v", ref.RealPath, err)
}
continue
}
@@ -368,15 +400,12 @@ func (r directoryResolver) FilesByPath(userPaths ...string) ([]Location, error)
userStrPath = windowsToPosix(userStrPath)
}
exists, ref, err := r.fileTree.File(file.Path(userStrPath), filetree.FollowBasenameLinks)
if err == nil && exists {
loc := NewVirtualLocationFromDirectory(
r.responsePath(string(ref.RealPath)), // the actual path relative to the resolver root
r.responsePath(userStrPath), // the path used to access this file, relative to the resolver root
*ref,
)
references = append(references, loc)
}
loc := NewVirtualLocationFromDirectory(
r.responsePath(string(ref.RealPath)), // the actual path relative to the resolver root
r.responsePath(userStrPath), // the path used to access this file, relative to the resolver root
*ref,
)
references = append(references, loc)
}
return references, nil
+92 -19
View File
@@ -57,7 +57,7 @@ func TestDirectoryResolver_FilesByPath_relativeRoot(t *testing.T) {
}
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
resolver, err := newDirectoryResolver(c.relativeRoot)
resolver, err := newDirectoryResolver(c.relativeRoot, "")
assert.NoError(t, err)
refs, err := resolver.FilesByPath(c.input)
@@ -111,7 +111,7 @@ func TestDirectoryResolver_FilesByPath_absoluteRoot(t *testing.T) {
absRoot, err := filepath.Abs(c.relativeRoot)
require.NoError(t, err)
resolver, err := newDirectoryResolver(absRoot)
resolver, err := newDirectoryResolver(absRoot, "")
assert.NoError(t, err)
refs, err := resolver.FilesByPath(c.input)
@@ -172,7 +172,7 @@ func TestDirectoryResolver_FilesByPath(t *testing.T) {
}
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
resolver, err := newDirectoryResolver(c.root)
resolver, err := newDirectoryResolver(c.root, "")
assert.NoError(t, err)
hasPath := resolver.HasPath(c.input)
@@ -220,7 +220,7 @@ func TestDirectoryResolver_MultipleFilesByPath(t *testing.T) {
}
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
resolver, err := newDirectoryResolver("./test-fixtures")
resolver, err := newDirectoryResolver("./test-fixtures", "")
assert.NoError(t, err)
refs, err := resolver.FilesByPath(c.input...)
assert.NoError(t, err)
@@ -233,7 +233,7 @@ func TestDirectoryResolver_MultipleFilesByPath(t *testing.T) {
}
func TestDirectoryResolver_FilesByGlobMultiple(t *testing.T) {
resolver, err := newDirectoryResolver("./test-fixtures")
resolver, err := newDirectoryResolver("./test-fixtures", "")
assert.NoError(t, err)
refs, err := resolver.FilesByGlob("**/image-symlinks/file*")
assert.NoError(t, err)
@@ -242,7 +242,7 @@ func TestDirectoryResolver_FilesByGlobMultiple(t *testing.T) {
}
func TestDirectoryResolver_FilesByGlobRecursive(t *testing.T) {
resolver, err := newDirectoryResolver("./test-fixtures/image-symlinks")
resolver, err := newDirectoryResolver("./test-fixtures/image-symlinks", "")
assert.NoError(t, err)
refs, err := resolver.FilesByGlob("**/*.txt")
assert.NoError(t, err)
@@ -250,7 +250,7 @@ func TestDirectoryResolver_FilesByGlobRecursive(t *testing.T) {
}
func TestDirectoryResolver_FilesByGlobSingle(t *testing.T) {
resolver, err := newDirectoryResolver("./test-fixtures")
resolver, err := newDirectoryResolver("./test-fixtures", "")
assert.NoError(t, err)
refs, err := resolver.FilesByGlob("**/image-symlinks/*1.txt")
assert.NoError(t, err)
@@ -277,7 +277,7 @@ func TestDirectoryResolver_FilesByPath_ResolvesSymlinks(t *testing.T) {
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
resolver, err := newDirectoryResolver("./test-fixtures/symlinks-simple")
resolver, err := newDirectoryResolver("./test-fixtures/symlinks-simple", "")
assert.NoError(t, err)
refs, err := resolver.FilesByPath(test.fixture)
@@ -300,7 +300,7 @@ func TestDirectoryResolver_FilesByPath_ResolvesSymlinks(t *testing.T) {
func TestDirectoryResolverDoesNotIgnoreRelativeSystemPaths(t *testing.T) {
// let's make certain that "dev/place" is not ignored, since it is not "/dev/place"
resolver, err := newDirectoryResolver("test-fixtures/system_paths/target")
resolver, err := newDirectoryResolver("test-fixtures/system_paths/target", "")
assert.NoError(t, err)
// ensure the correct filter function is wired up by default
expectedFn := reflect.ValueOf(isUnallowableFileType)
@@ -431,7 +431,7 @@ func Test_isUnallowableFileType(t *testing.T) {
func Test_directoryResolver_index(t *testing.T) {
// note: this test is testing the effects from newDirectoryResolver, indexTree, and addPathToIndex
r, err := newDirectoryResolver("test-fixtures/system_paths/target")
r, err := newDirectoryResolver("test-fixtures/system_paths/target", "")
if err != nil {
t.Fatalf("unable to get indexed dir resolver: %+v", err)
}
@@ -608,7 +608,7 @@ func Test_directoryResolver_FilesByMIMEType(t *testing.T) {
}
for _, test := range tests {
t.Run(test.fixturePath, func(t *testing.T) {
resolver, err := newDirectoryResolver(test.fixturePath)
resolver, err := newDirectoryResolver(test.fixturePath, "")
assert.NoError(t, err)
locations, err := resolver.FilesByMIMEType(test.mimeType)
assert.NoError(t, err)
@@ -621,7 +621,7 @@ func Test_directoryResolver_FilesByMIMEType(t *testing.T) {
}
func Test_IndexingNestedSymLinks(t *testing.T) {
resolver, err := newDirectoryResolver("./test-fixtures/symlinks-simple")
resolver, err := newDirectoryResolver("./test-fixtures/symlinks-simple", "")
require.NoError(t, err)
// check that we can get the real path
@@ -674,7 +674,7 @@ func Test_IndexingNestedSymLinks_ignoredIndexes(t *testing.T) {
return strings.HasSuffix(path, string(filepath.Separator)+"readme")
}
resolver, err := newDirectoryResolver("./test-fixtures/symlinks-simple", filterFn)
resolver, err := newDirectoryResolver("./test-fixtures/symlinks-simple", "", filterFn)
require.NoError(t, err)
// the path to the real file is PRUNED from the index, so we should NOT expect a location returned
@@ -694,7 +694,7 @@ func Test_IndexingNestedSymLinks_ignoredIndexes(t *testing.T) {
}
func Test_IndexingNestedSymLinksOutsideOfRoot(t *testing.T) {
resolver, err := newDirectoryResolver("./test-fixtures/symlinks-multiple-roots/root")
resolver, err := newDirectoryResolver("./test-fixtures/symlinks-multiple-roots/root", "")
require.NoError(t, err)
// check that we can get the real path
@@ -712,7 +712,7 @@ func Test_IndexingNestedSymLinksOutsideOfRoot(t *testing.T) {
}
func Test_RootViaSymlink(t *testing.T) {
resolver, err := newDirectoryResolver("./test-fixtures/symlinked-root/nested/link-root")
resolver, err := newDirectoryResolver("./test-fixtures/symlinked-root/nested/link-root", "")
require.NoError(t, err)
locations, err := resolver.FilesByPath("./file1.txt")
@@ -753,7 +753,7 @@ func Test_directoryResolver_FileContentsByLocation(t *testing.T) {
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
r, err := newDirectoryResolver(".")
r, err := newDirectoryResolver(".", "")
require.NoError(t, err)
actual, err := r.FileContentsByLocation(test.location)
@@ -819,7 +819,7 @@ func Test_isUnixSystemRuntimePath(t *testing.T) {
func Test_SymlinkLoopWithGlobsShouldResolve(t *testing.T) {
test := func(t *testing.T) {
resolver, err := newDirectoryResolver("./test-fixtures/symlinks-loop")
resolver, err := newDirectoryResolver("./test-fixtures/symlinks-loop", "")
require.NoError(t, err)
locations, err := resolver.FilesByGlob("**/file.target")
@@ -853,7 +853,7 @@ func Test_IncludeRootPathInIndex(t *testing.T) {
return path != "/"
}
resolver, err := newDirectoryResolver("/", filterFn)
resolver, err := newDirectoryResolver("/", "", filterFn)
require.NoError(t, err)
exists, ref, err := resolver.fileTree.File(file.Path("/"))
@@ -870,7 +870,7 @@ func TestDirectoryResolver_indexPath(t *testing.T) {
tempFile, err := os.CreateTemp("", "")
require.NoError(t, err)
resolver, err := newDirectoryResolver(tempFile.Name())
resolver, err := newDirectoryResolver(tempFile.Name(), "")
require.NoError(t, err)
t.Run("filtering path with nil os.FileInfo", func(t *testing.T) {
@@ -885,3 +885,76 @@ func TestDirectoryResolver_indexPath(t *testing.T) {
})
})
}
func TestDirectoryResolver_FilesByPath_baseRoot(t *testing.T) {
cases := []struct {
name string
root string
input string
expected []string
}{
{
name: "should find the base file",
root: "./test-fixtures/symlinks-base/",
input: "./base",
expected: []string{
"base",
},
},
{
name: "should follow a link with a pivoted root",
root: "./test-fixtures/symlinks-base/",
input: "./foo",
expected: []string{
"base",
},
},
{
name: "should follow a relative link with extra parents",
root: "./test-fixtures/symlinks-base/",
input: "./bar",
expected: []string{
"base",
},
},
{
name: "should follow an absolute link with extra parents",
root: "./test-fixtures/symlinks-base/",
input: "./baz",
expected: []string{
"base",
},
},
{
name: "should follow an absolute link with extra parents",
root: "./test-fixtures/symlinks-base/",
input: "./sub/link",
expected: []string{
"sub/item",
},
},
{
name: "should follow chained pivoted link",
root: "./test-fixtures/symlinks-base/",
input: "./chain",
expected: []string{
"base",
},
},
}
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
resolver, err := newDirectoryResolver(c.root, c.root)
assert.NoError(t, err)
refs, err := resolver.FilesByPath(c.input)
require.NoError(t, err)
assert.Len(t, refs, len(c.expected))
s := strset.New()
for _, actual := range refs {
s.Add(actual.RealPath)
}
assert.ElementsMatch(t, c.expected, s.List())
})
}
}
+1
View File
@@ -6,5 +6,6 @@ type Metadata struct {
Scheme Scheme // the source data scheme type (directory or image)
ImageMetadata ImageMetadata // all image info (image only)
Path string // the root path to be cataloged (directory only)
Base string // the base path to be cataloged (directory only)
Name string
}
+24 -1
View File
@@ -32,6 +32,7 @@ type Source struct {
Metadata Metadata
directoryResolver *directoryResolver `hash:"ignore"`
path string
base string
mutex *sync.Mutex
Exclusions []string `hash:"ignore"`
}
@@ -252,6 +253,11 @@ func NewFromDirectory(path string) (Source, error) {
return NewFromDirectoryWithName(path, "")
}
// NewFromDirectory creates a new source object tailored to catalog a given filesystem directory recursively.
func NewFromDirectoryRoot(path string) (Source, error) {
return NewFromDirectoryRootWithName(path, "")
}
// NewFromDirectoryWithName creates a new source object tailored to catalog a given filesystem directory recursively, with an explicitly provided name.
func NewFromDirectoryWithName(path string, name string) (Source, error) {
s := Source{
@@ -267,6 +273,23 @@ func NewFromDirectoryWithName(path string, name string) (Source, error) {
return s, nil
}
// NewFromDirectoryRootWithName creates a new source object tailored to catalog a given filesystem directory recursively, with an explicitly provided name.
func NewFromDirectoryRootWithName(path string, name string) (Source, error) {
s := Source{
mutex: &sync.Mutex{},
Metadata: Metadata{
Name: name,
Scheme: DirectoryScheme,
Path: path,
Base: path,
},
path: path,
base: path,
}
s.SetID()
return s, nil
}
// NewFromFile creates a new source object tailored to catalog a file.
func NewFromFile(path string) (Source, func()) {
return NewFromFileWithName(path, "")
@@ -428,7 +451,7 @@ func (s *Source) FileResolver(scope Scope) (FileResolver, error) {
if err != nil {
return nil, err
}
resolver, err := newDirectoryResolver(s.path, exclusionFunctions...)
resolver, err := newDirectoryResolver(s.path, s.base, exclusionFunctions...)
if err != nil {
return nil, fmt.Errorf("unable to create directory resolver: %w", err)
}
+1 -1
View File
@@ -121,7 +121,7 @@ func TestSetID(t *testing.T) {
Path: "test-fixtures/image-simple",
},
},
expected: artifact.ID("14b60020c4f9955"),
expected: artifact.ID("1b0dc351e6577b01"),
},
}
+1
View File
@@ -0,0 +1 @@
../../base
+1
View File
@@ -0,0 +1 @@
/../../base
+1
View File
@@ -0,0 +1 @@
/foo
+1
View File
@@ -0,0 +1 @@
/base
+1
View File
@@ -0,0 +1 @@
../sub/item
+38 -46
View File
@@ -1,6 +1,7 @@
package cli
import (
"fmt"
"os"
"path/filepath"
"strings"
@@ -9,13 +10,8 @@ import (
"github.com/stretchr/testify/require"
)
type conversion struct {
To string
From string
}
func TestConvertCmdFlags(t *testing.T) {
commonAssertions := []traitAssertion{
assertions := []traitAssertion{
func(tb testing.TB, stdout, _ string, _ int) {
tb.Helper()
if len(stdout) < 1000 {
@@ -26,52 +22,48 @@ func TestConvertCmdFlags(t *testing.T) {
}
tests := []struct {
name string
conversions []conversion
env map[string]string
assertions []traitAssertion
to string
from string
template string
env map[string]string
}{
{
name: "syft-format convertable to spdx-json",
conversions: []conversion{
{To: "syft-json", From: "spdx-json"},
{To: "syft-json", From: "cyclonedx-json"},
{To: "spdx-json", From: "syft-json"},
{To: "spdx-json", From: "cyclonedx-json"},
{To: "cyclonedx-json", From: "syft-json"},
{To: "cyclonedx-json", From: "spdx-json"},
},
assertions: commonAssertions,
},
{to: "syft-json", from: "spdx-json"},
{to: "syft-json", from: "cyclonedx-json"},
{to: "spdx-json", from: "syft-json"},
{to: "template", from: "syft-json", template: "test-fixtures/csv.template"},
{to: "spdx-json", from: "cyclonedx-json"},
{to: "cyclonedx-json", from: "syft-json"},
{to: "cyclonedx-json", from: "spdx-json"},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
for _, c := range test.conversions {
sbomArgs := []string{"dir:./test-fixtures/image-pkg-coverage", "-o", c.From}
cmd, stdout, stderr := runSyft(t, test.env, sbomArgs...)
if cmd.ProcessState.ExitCode() != 0 {
t.Fatalf("failure executing syft creating an sbom")
t.Log("STDOUT:\n", stdout)
t.Log("STDERR:\n", stderr)
t.Log("COMMAND:", strings.Join(cmd.Args, " "))
return
}
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, test.env, 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
}
tempDir := t.TempDir()
sbomFile := filepath.Join(tempDir, "sbom.json")
require.NoError(t, os.WriteFile(sbomFile, []byte(stdout), 0666))
tempDir := t.TempDir()
sbomFile := filepath.Join(tempDir, "sbom.json")
require.NoError(t, os.WriteFile(sbomFile, []byte(stdout), 0666))
convertArgs := []string{"convert", sbomFile, "-o", c.To}
cmd, stdout, stderr = runSyft(t, test.env, convertArgs...)
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, " "))
}
convertArgs := []string{"convert", sbomFile, "-o", test.to}
if test.template != "" {
convertArgs = append(convertArgs, "--template", test.template)
}
cmd, stdout, stderr = runSyft(t, test.env, convertArgs...)
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, " "))
}
})
}
+3
View File
@@ -618,6 +618,9 @@ func AttestationStartedHandler(ctx context.Context, fr *frame.Frame, event party
text := s.Text()
if strings.Contains(text, "tlog entry created with index") {
tlogEntry = text
} else {
// no tlog entry create so user used personal PKI
tlogEntry = "signed attestation using provided key"
}
_, err = line.Write([]byte(fmt.Sprintf(" %s %s", auxInfoFormat.Sprintf("░░"), text)))
if err != nil {