Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package syft for openSUSE:Factory checked in at 2023-02-01 16:39:05 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/syft (Old) and /work/SRC/openSUSE:Factory/.syft.new.32243 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "syft" Wed Feb 1 16:39:05 2023 rev:24 rq:1062288 version:0.69.0 Changes: -------- --- /work/SRC/openSUSE:Factory/syft/syft.changes 2023-01-26 14:10:58.968433840 +0100 +++ /work/SRC/openSUSE:Factory/.syft.new.32243/syft.changes 2023-02-01 16:39:08.773633704 +0100 @@ -1,0 +2,8 @@ +Tue Jan 31 15:04:23 UTC 2023 - ka...@b1-systems.de + +- Update to version 0.69.0: + * Allow scanning unpacked container filesystems (#1485) + * fix: allow template for syft convert (#1521) + * 1465 attestation with private key (#1502) + +------------------------------------------------------------------- Old: ---- syft-0.68.1.tar.gz New: ---- syft-0.69.0.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ syft.spec ++++++ --- /var/tmp/diff_new_pack.JZ1pKS/_old 2023-02-01 16:39:10.485644870 +0100 +++ /var/tmp/diff_new_pack.JZ1pKS/_new 2023-02-01 16:39:10.489644896 +0100 @@ -19,7 +19,7 @@ %define __arch_install_post export NO_BRP_STRIP_DEBUG=true Name: syft -Version: 0.68.1 +Version: 0.69.0 Release: 0 Summary: CLI tool and library for generating a Software Bill of Materials License: Apache-2.0 ++++++ _service ++++++ --- /var/tmp/diff_new_pack.JZ1pKS/_old 2023-02-01 16:39:10.553645313 +0100 +++ /var/tmp/diff_new_pack.JZ1pKS/_new 2023-02-01 16:39:10.557645339 +0100 @@ -3,7 +3,7 @@ <param name="url">https://github.com/anchore/syft</param> <param name="scm">git</param> <param name="exclude">.git</param> - <param name="revision">v0.68.1</param> + <param name="revision">v0.69.0</param> <param name="versionformat">@PARENT_TAG@</param> <param name="changesgenerate">enable</param> <param name="versionrewrite-pattern">v(.*)</param> @@ -16,7 +16,7 @@ <param name="compression">gz</param> </service> <service name="go_modules" mode="disabled"> - <param name="archive">syft-0.68.1.tar.gz</param> + <param name="archive">syft-0.69.0.tar.gz</param> </service> </services> ++++++ _servicedata ++++++ --- /var/tmp/diff_new_pack.JZ1pKS/_old 2023-02-01 16:39:10.577645469 +0100 +++ /var/tmp/diff_new_pack.JZ1pKS/_new 2023-02-01 16:39:10.585645522 +0100 @@ -1,6 +1,6 @@ <servicedata> <service name="tar_scm"> <param name="url">https://github.com/anchore/syft</param> - <param name="changesrevision">4c0aef09b8d7fb78200b04416f474b90b79370de</param></service></servicedata> + <param name="changesrevision">b81c9805dcc9bf25dad7659fd9c2bbf7dd3f3d90</param></service></servicedata> (No newline at EOF) ++++++ syft-0.68.1.tar.gz -> syft-0.69.0.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/syft-0.68.1/cmd/syft/cli/attest/attest.go new/syft-0.69.0/cmd/syft/cli/attest/attest.go --- old/syft-0.68.1/cmd/syft/cli/attest/attest.go 2023-01-25 18:18:24.000000000 +0100 +++ new/syft-0.69.0/cmd/syft/cli/attest/attest.go 2023-01-30 19:47:24.000000000 +0100 @@ -97,6 +97,7 @@ 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 @@ } 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() diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/syft-0.68.1/cmd/syft/cli/attest.go new/syft-0.69.0/cmd/syft/cli/attest.go --- old/syft-0.68.1/cmd/syft/cli/attest.go 2023-01-25 18:18:24.000000000 +0100 +++ new/syft-0.69.0/cmd/syft/cli/attest.go 2023-01-30 19:47:24.000000000 +0100 @@ -20,8 +20,7 @@ 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 @@ }, } - // 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 } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/syft-0.68.1/cmd/syft/cli/commands.go new/syft-0.69.0/cmd/syft/cli/commands.go --- old/syft-0.68.1/cmd/syft/cli/commands.go 2023-01-25 18:18:24.000000000 +0100 +++ new/syft-0.69.0/cmd/syft/cli/commands.go 2023-01-30 19:47:24.000000000 +0100 @@ -45,12 +45,13 @@ // 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{ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/syft-0.68.1/cmd/syft/cli/convert/convert.go new/syft-0.69.0/cmd/syft/cli/convert/convert.go --- old/syft-0.68.1/cmd/syft/cli/convert/convert.go 2023-01-25 18:18:24.000000000 +0100 +++ new/syft-0.69.0/cmd/syft/cli/convert/convert.go 2023-01-30 19:47:24.000000000 +0100 @@ -11,9 +11,9 @@ "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 @@ 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 { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/syft-0.68.1/cmd/syft/cli/options/attest.go new/syft-0.69.0/cmd/syft/cli/options/attest.go --- old/syft-0.68.1/cmd/syft/cli/options/attest.go 1970-01-01 01:00:00.000000000 +0100 +++ new/syft-0.69.0/cmd/syft/cli/options/attest.go 2023-01-30 19:47:24.000000000 +0100 @@ -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 +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/syft-0.68.1/internal/config/application.go new/syft-0.69.0/internal/config/application.go --- old/syft-0.68.1/internal/config/application.go 2023-01-25 18:18:24.000000000 +0100 +++ new/syft-0.69.0/internal/config/application.go 2023-01-30 19:47:24.000000000 +0100 @@ -47,6 +47,7 @@ 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"` diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/syft-0.68.1/internal/config/attest.go new/syft-0.69.0/internal/config/attest.go --- old/syft-0.68.1/internal/config/attest.go 1970-01-01 01:00:00.000000000 +0100 +++ new/syft-0.69.0/internal/config/attest.go 2023-01-30 19:47:24.000000000 +0100 @@ -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", "") +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/syft-0.68.1/syft/source/directory_resolver.go new/syft-0.69.0/syft/source/directory_resolver.go --- old/syft-0.68.1/syft/source/directory_resolver.go 2023-01-25 18:18:24.000000000 +0100 +++ new/syft-0.69.0/syft/source/directory_resolver.go 2023-01-30 19:47:24.000000000 +0100 @@ -37,6 +37,7 @@ // 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 @@ 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 @@ 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 @@ resolver := directoryResolver{ path: cleanRoot, + base: cleanBase, currentWd: cleanCWD, currentWdRelativeToRoot: currentWdRelRoot, fileTree: filetree.NewFileTree(), @@ -244,10 +258,25 @@ 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 @@ } // 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 @@ // 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 @@ 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 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/syft-0.68.1/syft/source/directory_resolver_test.go new/syft-0.69.0/syft/source/directory_resolver_test.go --- old/syft-0.68.1/syft/source/directory_resolver_test.go 2023-01-25 18:18:24.000000000 +0100 +++ new/syft-0.69.0/syft/source/directory_resolver_test.go 2023-01-30 19:47:24.000000000 +0100 @@ -57,7 +57,7 @@ } 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 @@ 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 @@ } 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 @@ } 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_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_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_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 @@ 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 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_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 @@ } 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_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 @@ 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_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_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 @@ } 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_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 @@ 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 @@ 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_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()) + }) + } +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/syft-0.68.1/syft/source/metadata.go new/syft-0.69.0/syft/source/metadata.go --- old/syft-0.68.1/syft/source/metadata.go 2023-01-25 18:18:24.000000000 +0100 +++ new/syft-0.69.0/syft/source/metadata.go 2023-01-30 19:47:24.000000000 +0100 @@ -6,5 +6,6 @@ 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 } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/syft-0.68.1/syft/source/source.go new/syft-0.69.0/syft/source/source.go --- old/syft-0.68.1/syft/source/source.go 2023-01-25 18:18:24.000000000 +0100 +++ new/syft-0.69.0/syft/source/source.go 2023-01-30 19:47:24.000000000 +0100 @@ -32,6 +32,7 @@ Metadata Metadata directoryResolver *directoryResolver `hash:"ignore"` path string + base string mutex *sync.Mutex Exclusions []string `hash:"ignore"` } @@ -252,6 +253,11 @@ 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 @@ 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 @@ 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) } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/syft-0.68.1/syft/source/source_test.go new/syft-0.69.0/syft/source/source_test.go --- old/syft-0.68.1/syft/source/source_test.go 2023-01-25 18:18:24.000000000 +0100 +++ new/syft-0.69.0/syft/source/source_test.go 2023-01-30 19:47:24.000000000 +0100 @@ -121,7 +121,7 @@ Path: "test-fixtures/image-simple", }, }, - expected: artifact.ID("14b60020c4f9955"), + expected: artifact.ID("1b0dc351e6577b01"), }, } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/syft-0.68.1/syft/source/test-fixtures/symlinks-base/bar new/syft-0.69.0/syft/source/test-fixtures/symlinks-base/bar --- old/syft-0.68.1/syft/source/test-fixtures/symlinks-base/bar 1970-01-01 01:00:00.000000000 +0100 +++ new/syft-0.69.0/syft/source/test-fixtures/symlinks-base/bar 2023-02-01 16:39:11.193649487 +0100 @@ -0,0 +1 @@ +symbolic link to ../../base diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/syft-0.68.1/syft/source/test-fixtures/symlinks-base/baz new/syft-0.69.0/syft/source/test-fixtures/symlinks-base/baz --- old/syft-0.68.1/syft/source/test-fixtures/symlinks-base/baz 1970-01-01 01:00:00.000000000 +0100 +++ new/syft-0.69.0/syft/source/test-fixtures/symlinks-base/baz 2023-02-01 16:39:11.169649331 +0100 @@ -0,0 +1 @@ +symbolic link to /../../base diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/syft-0.68.1/syft/source/test-fixtures/symlinks-base/chain new/syft-0.69.0/syft/source/test-fixtures/symlinks-base/chain --- old/syft-0.68.1/syft/source/test-fixtures/symlinks-base/chain 1970-01-01 01:00:00.000000000 +0100 +++ new/syft-0.69.0/syft/source/test-fixtures/symlinks-base/chain 2023-02-01 16:39:11.161649278 +0100 @@ -0,0 +1 @@ +symbolic link to /foo diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/syft-0.68.1/syft/source/test-fixtures/symlinks-base/foo new/syft-0.69.0/syft/source/test-fixtures/symlinks-base/foo --- old/syft-0.68.1/syft/source/test-fixtures/symlinks-base/foo 1970-01-01 01:00:00.000000000 +0100 +++ new/syft-0.69.0/syft/source/test-fixtures/symlinks-base/foo 2023-02-01 16:39:11.181649409 +0100 @@ -0,0 +1 @@ +symbolic link to /base diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/syft-0.68.1/syft/source/test-fixtures/symlinks-base/sub/link new/syft-0.69.0/syft/source/test-fixtures/symlinks-base/sub/link --- old/syft-0.68.1/syft/source/test-fixtures/symlinks-base/sub/link 1970-01-01 01:00:00.000000000 +0100 +++ new/syft-0.69.0/syft/source/test-fixtures/symlinks-base/sub/link 2023-02-01 16:39:11.177649383 +0100 @@ -0,0 +1 @@ +symbolic link to ../sub/item diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/syft-0.68.1/test/cli/all_formats_convertible_test.go new/syft-0.69.0/test/cli/all_formats_convertible_test.go --- old/syft-0.68.1/test/cli/all_formats_convertible_test.go 2023-01-25 18:18:24.000000000 +0100 +++ new/syft-0.69.0/test/cli/all_formats_convertible_test.go 2023-01-30 19:47:24.000000000 +0100 @@ -1,6 +1,7 @@ package cli import ( + "fmt" "os" "path/filepath" "strings" @@ -9,13 +10,8 @@ "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 @@ } 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 - } - - 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, " ")) - } + 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)) + + 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, " ")) } }) } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/syft-0.68.1/ui/event_handlers.go new/syft-0.69.0/ui/event_handlers.go --- old/syft-0.68.1/ui/event_handlers.go 2023-01-25 18:18:24.000000000 +0100 +++ new/syft-0.69.0/ui/event_handlers.go 2023-01-30 19:47:24.000000000 +0100 @@ -618,6 +618,9 @@ 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 { ++++++ vendor.tar.gz ++++++ /work/SRC/openSUSE:Factory/syft/vendor.tar.gz /work/SRC/openSUSE:Factory/.syft.new.32243/vendor.tar.gz differ: char 5, line 1