From 3f690717c4c1991529a8c3654e9a9475e82d52d6 Mon Sep 17 00:00:00 2001 From: Keith Zantow Date: Mon, 10 Oct 2022 13:39:56 -0400 Subject: [PATCH 1/3] refactor: Remove experimental Anchore Enterprise upload functionality Signed-off-by: Keith Zantow --- README.md | 22 - cmd/syft/cli/options/packages.go | 64 +-- cmd/syft/cli/packages/packages.go | 59 --- internal/anchore/client.go | 115 ------ internal/anchore/client_test.go | 210 ---------- internal/anchore/import.go | 145 ------- internal/anchore/import_config.go | 50 --- internal/anchore/import_config_test.go | 129 ------ internal/anchore/import_dockerfile.go | 42 -- internal/anchore/import_dockerfile_test.go | 123 ------ internal/anchore/import_manifest.go | 50 --- internal/anchore/import_manifest_test.go | 129 ------ internal/anchore/import_package_sbom.go | 119 ------ internal/anchore/import_package_sbom_test.go | 398 ------------------- internal/config/anchore.go | 21 - internal/config/application.go | 9 - 16 files changed, 7 insertions(+), 1678 deletions(-) delete mode 100644 internal/anchore/client.go delete mode 100644 internal/anchore/client_test.go delete mode 100644 internal/anchore/import.go delete mode 100644 internal/anchore/import_config.go delete mode 100644 internal/anchore/import_config_test.go delete mode 100644 internal/anchore/import_dockerfile.go delete mode 100644 internal/anchore/import_dockerfile_test.go delete mode 100644 internal/anchore/import_manifest.go delete mode 100644 internal/anchore/import_manifest_test.go delete mode 100644 internal/anchore/import_package_sbom.go delete mode 100644 internal/anchore/import_package_sbom_test.go delete mode 100644 internal/config/anchore.go diff --git a/README.md b/README.md index 235285ef8a4..cd09cf40733 100644 --- a/README.md +++ b/README.md @@ -610,28 +610,6 @@ log: # location to write the log file (default is not to have a log file) # same as SYFT_LOG_FILE env var file: "" - -# uploading package SBOM is exposed through the packages subcommand -anchore: - # (feature-preview) the Anchore Enterprise Host or URL to upload results to (supported on Enterprise 3.0+) - # same as -H ; SYFT_ANCHORE_HOST env var - host: "" - - # (feature-preview) the path after the host to the Anchore External API (supported on Enterprise 3.0+) - # same as SYFT_ANCHORE_PATH env var - path: "" - - # (feature-preview) the username to authenticate against Anchore Enterprise (supported on Enterprise 3.0+) - # same as -u ; SYFT_ANCHORE_USERNAME env var - username: "" - - # (feature-preview) the password to authenticate against Anchore Enterprise (supported on Enterprise 3.0+) - # same as -p ; SYFT_ANCHORE_PASSWORD env var - password: "" - - # (feature-preview) path to dockerfile to be uploaded with the syft results to Anchore Enterprise (supported on Enterprise 3.0+) - # same as -d ; SYFT_ANCHORE_DOCKERFILE env var - dockerfile: "" ``` ### Adding an SBOM to an image as an attestation using Syft diff --git a/cmd/syft/cli/options/packages.go b/cmd/syft/cli/options/packages.go index d6783bf6f27..08ff362b21a 100644 --- a/cmd/syft/cli/options/packages.go +++ b/cmd/syft/cli/options/packages.go @@ -14,19 +14,13 @@ import ( ) type PackagesOptions struct { - Scope string - Output []string - OutputTemplatePath string - File string - Platform string - Host string - Username string - Password string - Dockerfile string - Exclude []string - OverwriteExistingImage bool - ImportTimeout uint - Catalogers []string + Scope string + Output []string + OutputTemplatePath string + File string + Platform string + Exclude []string + Catalogers []string } var _ Interface = (*PackagesOptions)(nil) @@ -47,30 +41,12 @@ func (o *PackagesOptions) AddFlags(cmd *cobra.Command, v *viper.Viper) error { cmd.Flags().StringVarP(&o.Platform, "platform", "", "", "an optional platform specifier for container image sources (e.g. 'linux/arm64', 'linux/arm64/v8', 'arm64', 'linux')") - cmd.Flags().StringVarP(&o.Host, "host", "H", "", - "the hostname or URL of the Anchore Enterprise instance to upload to") - - cmd.Flags().StringVarP(&o.Username, "username", "u", "", - "the username to authenticate against Anchore Enterprise") - - cmd.Flags().StringVarP(&o.Password, "password", "p", "", - "the password to authenticate against Anchore Enterprise") - - cmd.Flags().StringVarP(&o.Dockerfile, "dockerfile", "d", "", - "include dockerfile for upload to Anchore Enterprise") - cmd.Flags().StringArrayVarP(&o.Exclude, "exclude", "", nil, "exclude paths from being scanned using a glob expression") cmd.Flags().StringArrayVarP(&o.Catalogers, "catalogers", "", nil, "enable one or more package catalogers") - cmd.Flags().BoolVarP(&o.OverwriteExistingImage, "overwrite-existing-image", "", false, - "overwrite an existing image during the upload to Anchore Enterprise") - - cmd.Flags().UintVarP(&o.ImportTimeout, "import-timeout", "", 30, - "set a timeout duration (in seconds) for the upload to Anchore Enterprise") - return bindPackageConfigOptions(cmd.Flags(), v) } @@ -105,31 +81,5 @@ func bindPackageConfigOptions(flags *pflag.FlagSet, v *viper.Viper) error { return err } - // Upload options ////////////////////////////////////////////////////////// - - if err := v.BindPFlag("anchore.host", flags.Lookup("host")); err != nil { - return err - } - - if err := v.BindPFlag("anchore.username", flags.Lookup("username")); err != nil { - return err - } - - if err := v.BindPFlag("anchore.password", flags.Lookup("password")); err != nil { - return err - } - - if err := v.BindPFlag("anchore.dockerfile", flags.Lookup("dockerfile")); err != nil { - return err - } - - if err := v.BindPFlag("anchore.overwrite-existing-image", flags.Lookup("overwrite-existing-image")); err != nil { - return err - } - - if err := v.BindPFlag("anchore.import-timeout", flags.Lookup("import-timeout")); err != nil { - return err - } - return nil } diff --git a/cmd/syft/cli/packages/packages.go b/cmd/syft/cli/packages/packages.go index 40681b944db..72bea0c78d2 100644 --- a/cmd/syft/cli/packages/packages.go +++ b/cmd/syft/cli/packages/packages.go @@ -3,8 +3,6 @@ package packages import ( "context" "fmt" - "io" - "os" "github.com/wagoodman/go-partybus" @@ -12,7 +10,6 @@ import ( "github.com/anchore/syft/cmd/syft/cli/eventloop" "github.com/anchore/syft/cmd/syft/cli/options" "github.com/anchore/syft/internal" - "github.com/anchore/syft/internal/anchore" "github.com/anchore/syft/internal/bus" "github.com/anchore/syft/internal/config" "github.com/anchore/syft/internal/log" @@ -88,13 +85,6 @@ func execWorker(app *config.Application, si source.Input, writer sbom.Writer) <- errs <- fmt.Errorf("no SBOM produced for %q", si.UserInput) } - if app.Anchore.Host != "" { - if err := runPackageSbomUpload(src, *s, app); err != nil { - errs <- err - return - } - } - bus.Publish(partybus.Event{ Type: event.Exit, Value: func() error { return writer.Write(*s) }, @@ -144,55 +134,6 @@ func MergeRelationships(cs ...<-chan artifact.Relationship) (relationships []art return relationships } -func runPackageSbomUpload(src *source.Source, s sbom.SBOM, app *config.Application) error { - log.Infof("uploading results to %s", app.Anchore.Host) - - if src.Metadata.Scheme != source.ImageScheme { - return fmt.Errorf("unable to upload results: only images are supported") - } - - var dockerfileContents []byte - if app.Anchore.Dockerfile != "" { - if _, err := os.Stat(app.Anchore.Dockerfile); os.IsNotExist(err) { - return fmt.Errorf("unable dockerfile=%q does not exist: %w", app.Anchore.Dockerfile, err) - } - - fh, err := os.Open(app.Anchore.Dockerfile) - if err != nil { - return fmt.Errorf("unable to open dockerfile=%q: %w", app.Anchore.Dockerfile, err) - } - - dockerfileContents, err = io.ReadAll(fh) - if err != nil { - return fmt.Errorf("unable to read dockerfile=%q: %w", app.Anchore.Dockerfile, err) - } - } - - c, err := anchore.NewClient(anchore.Configuration{ - BaseURL: app.Anchore.Host, - Username: app.Anchore.Username, - Password: app.Anchore.Password, - }) - - if err != nil { - return fmt.Errorf("failed to create anchore client: %w", err) - } - - importCfg := anchore.ImportConfig{ - ImageMetadata: src.Image.Metadata, - SBOM: s, - Dockerfile: dockerfileContents, - OverwriteExistingUpload: app.Anchore.OverwriteExistingImage, - Timeout: app.Anchore.ImportTimeout, - } - - if err := c.Import(context.Background(), importCfg); err != nil { - return fmt.Errorf("failed to upload results to host=%s: %+v", app.Anchore.Host, err) - } - - return nil -} - func validateOutputOptions(app *config.Application) error { var usesTemplateOutput bool for _, o := range app.Outputs { diff --git a/internal/anchore/client.go b/internal/anchore/client.go deleted file mode 100644 index b048fcdcaa3..00000000000 --- a/internal/anchore/client.go +++ /dev/null @@ -1,115 +0,0 @@ -package anchore - -import ( - "context" - "errors" - "fmt" - "path" - "strings" - "unicode" - - "github.com/anchore/client-go/pkg/external" - "github.com/anchore/syft/internal" - "github.com/anchore/syft/internal/version" -) - -type Configuration struct { - BaseURL string - Username string - Password string - UserAgent string -} - -type Client struct { - config Configuration - client *external.APIClient -} - -func NewClient(cfg Configuration) (*Client, error) { - if cfg.UserAgent == "" { - versionInfo := version.FromBuild() - // format: product / product-version comment - cfg.UserAgent = fmt.Sprintf("%s / %s %s", internal.ApplicationName, versionInfo.Version, versionInfo.Platform) - } - - baseURL, err := prepareBaseURLForClient(cfg.BaseURL) - if err != nil { - return nil, fmt.Errorf("unable to create client: %w", err) - } - - return &Client{ - config: cfg, - client: external.NewAPIClient(&external.Configuration{ - BasePath: baseURL, - UserAgent: cfg.UserAgent, - }), - }, nil -} - -func (c *Client) newRequestContext(parentContext context.Context) context.Context { - if parentContext == nil { - parentContext = context.Background() - } - return context.WithValue( - parentContext, - external.ContextBasicAuth, - external.BasicAuth{ - UserName: c.config.Username, - Password: c.config.Password, - }, - ) -} - -var ErrInvalidBaseURLInput = errors.New("invalid base URL input") - -func prepareBaseURLForClient(baseURL string) (string, error) { - if err := checkBaseURLInput(baseURL); err != nil { - return "", err - } - - scheme, urlWithoutScheme := splitSchemeFromURL(baseURL) - - if scheme == "" { - scheme = "http" - } - - urlWithoutScheme = path.Clean(urlWithoutScheme) - - const requiredSuffix = "v1" - if path.Base(urlWithoutScheme) != requiredSuffix { - urlWithoutScheme = path.Join(urlWithoutScheme, requiredSuffix) - } - - preparedBaseURL := scheme + "://" + urlWithoutScheme - return preparedBaseURL, nil -} - -func checkBaseURLInput(url string) error { - if url == "" { - return ErrInvalidBaseURLInput - } - - firstCharacter := rune(url[0]) - if !(unicode.IsLetter(firstCharacter)) { - return ErrInvalidBaseURLInput - } - - return nil -} - -func splitSchemeFromURL(url string) (scheme, urlWithoutScheme string) { - if hasScheme(url) { - urlParts := strings.SplitN(url, "://", 2) - scheme = urlParts[0] - urlWithoutScheme = urlParts[1] - return - } - - return "", url -} - -func hasScheme(url string) bool { - parts := strings.Split(url, "://") - - return len(parts) > 1 -} diff --git a/internal/anchore/client_test.go b/internal/anchore/client_test.go deleted file mode 100644 index fc07ce70827..00000000000 --- a/internal/anchore/client_test.go +++ /dev/null @@ -1,210 +0,0 @@ -package anchore - -import "testing" - -func TestHasScheme(t *testing.T) { - cases := []struct { - url string - expected bool - }{ - { - url: "http://localhost", - expected: true, - }, - { - url: "https://anchore.com:8443", - expected: true, - }, - { - url: "google.com", - expected: false, - }, - { - url: "", - expected: false, - }, - } - - for _, testCase := range cases { - t.Run(testCase.url, func(t *testing.T) { - result := hasScheme(testCase.url) - - if testCase.expected != result { - t.Errorf("expected %t but got %t", testCase.expected, result) - } - }) - } -} - -func TestPrepareBaseURLForClient(t *testing.T) { - cases := []struct { - inputURL string - expectedURL string - expectedErr error - }{ - { - inputURL: "", - expectedURL: "", - expectedErr: ErrInvalidBaseURLInput, - }, - { - inputURL: "localhost", - expectedURL: "http://localhost/v1", - expectedErr: nil, - }, - { - inputURL: "https://localhost", - expectedURL: "https://localhost/v1", - expectedErr: nil, - }, - { - inputURL: "https://localhost/", - expectedURL: "https://localhost/v1", - expectedErr: nil, - }, - { - inputURL: "https://localhost/v1/", - expectedURL: "https://localhost/v1", - expectedErr: nil, - }, - { - inputURL: "https://localhost/v1//", - expectedURL: "https://localhost/v1", - expectedErr: nil, - }, - { - inputURL: "http://something.com/platform/v1/services/anchore", - expectedURL: "http://something.com/platform/v1/services/anchore/v1", - expectedErr: nil, - }, - { - inputURL: "my-host:8228", - expectedURL: "http://my-host:8228/v1", - expectedErr: nil, - }, - { - inputURL: "v1/v1", - expectedURL: "http://v1/v1", - expectedErr: nil, - }, - { - inputURL: "/v1", - expectedURL: "", - expectedErr: ErrInvalidBaseURLInput, - }, - { - inputURL: "/imports/images", - expectedURL: "", - expectedErr: ErrInvalidBaseURLInput, - }, - } - - for _, testCase := range cases { - t.Run(testCase.inputURL, func(t *testing.T) { - resultURL, err := prepareBaseURLForClient(testCase.inputURL) - if err != testCase.expectedErr { - t.Errorf("expected err to be '%v' but got '%v'", testCase.expectedErr, err) - } - - if resultURL != testCase.expectedURL { - t.Errorf("expected URL to be '%v' but got '%v'", testCase.expectedURL, resultURL) - } - }) - } -} - -func TestCheckBaseURLInput(t *testing.T) { - cases := []struct { - input string - expected error - }{ - { - input: "", - expected: ErrInvalidBaseURLInput, - }, - { - input: "x", - expected: nil, - }, - { - input: "localhost:8000", - expected: nil, - }, - { - input: ":80", - expected: ErrInvalidBaseURLInput, - }, - { - input: "/v1", - expected: ErrInvalidBaseURLInput, - }, - } - - for _, testCase := range cases { - t.Run(testCase.input, func(t *testing.T) { - resultErr := checkBaseURLInput(testCase.input) - - if testCase.expected != resultErr { - t.Errorf("expected err to be '%v' but got '%v'", testCase.expected, resultErr) - } - }) - } -} - -func TestSplitSchemeFromURL(t *testing.T) { - cases := []struct { - input string - expectedScheme string - expectedURLWithoutScheme string - }{ - { - input: "", - expectedScheme: "", - expectedURLWithoutScheme: "", - }, - { - input: "localhost", - expectedScheme: "", - expectedURLWithoutScheme: "localhost", - }, - { - input: "https://anchore.com/path", - expectedScheme: "https", - expectedURLWithoutScheme: "anchore.com/path", - }, - { - input: "tcp://host:1234", - expectedScheme: "tcp", - expectedURLWithoutScheme: "host:1234", - }, - { - input: "/hello", - expectedScheme: "", - expectedURLWithoutScheme: "/hello", - }, - { - input: "://host", - expectedScheme: "", - expectedURLWithoutScheme: "host", - }, - { - input: "http//localhost", - expectedScheme: "", - expectedURLWithoutScheme: "http//localhost", - }, - } - - for _, testCase := range cases { - t.Run(testCase.input, func(t *testing.T) { - resultScheme, resultURLWithoutScheme := splitSchemeFromURL(testCase.input) - - if testCase.expectedScheme != resultScheme { - t.Errorf("expected scheme to be '%s' but got '%s'", testCase.expectedScheme, resultScheme) - } - - if testCase.expectedURLWithoutScheme != resultURLWithoutScheme { - t.Errorf("expected urlWithoutScheme to be '%s' but got '%s'", testCase.expectedURLWithoutScheme, resultURLWithoutScheme) - } - }) - } -} diff --git a/internal/anchore/import.go b/internal/anchore/import.go deleted file mode 100644 index e3459a26f08..00000000000 --- a/internal/anchore/import.go +++ /dev/null @@ -1,145 +0,0 @@ -package anchore - -import ( - "context" - "errors" - "fmt" - "time" - - "github.com/antihax/optional" - "github.com/wagoodman/go-partybus" - "github.com/wagoodman/go-progress" - - "github.com/anchore/client-go/pkg/external" - "github.com/anchore/stereoscope/pkg/image" - "github.com/anchore/syft/internal/bus" - "github.com/anchore/syft/syft/event" - "github.com/anchore/syft/syft/sbom" -) - -type ImportConfig struct { - ImageMetadata image.Metadata - SBOM sbom.SBOM - Dockerfile []byte - OverwriteExistingUpload bool - Timeout uint -} - -func importProgress(source string) (*progress.Stage, *progress.Manual) { - stage := &progress.Stage{} - prog := &progress.Manual{ - // this is the number of stages to expect; start + individual endpoints + stop - Total: 6, - } - bus.Publish(partybus.Event{ - Type: event.ImportStarted, - Source: source, - Value: progress.StagedProgressable(&struct { - progress.Stager - progress.Progressable - }{ - Stager: progress.Stager(stage), - Progressable: prog, - }), - }) - - return stage, prog -} - -//nolint:funlen -func (c *Client) Import(ctx context.Context, cfg ImportConfig) error { - stage, prog := importProgress(c.config.BaseURL) - - timeout := time.Duration(cfg.Timeout) * time.Second - ctxWithTimeout, cancel := context.WithTimeout(ctx, timeout) - defer cancel() - - authedCtx := c.newRequestContext(ctxWithTimeout) - - stage.Current = "starting session" - startOperation, createResponse, err := c.client.ImportsApi.CreateOperation(authedCtx) - if err != nil { - var detail = "no details given" - var openAPIErr external.GenericOpenAPIError - if errors.As(err, &openAPIErr) { - detail = string(openAPIErr.Body()) - } - return fmt.Errorf("unable to start import session: %w: %s", err, detail) - } - defer createResponse.Body.Close() - - prog.N++ - sessionID := startOperation.Uuid - - packageDigest, err := importPackageSBOM(authedCtx, c.client.ImportsApi, sessionID, cfg.SBOM, stage) - if err != nil { - return fmt.Errorf("failed to import Package SBOM: %w", err) - } - prog.N++ - - manifestDigest, err := importManifest(authedCtx, c.client.ImportsApi, sessionID, cfg.SBOM.Source.ImageMetadata.RawManifest, stage) - if err != nil { - return fmt.Errorf("failed to import Manifest: %w", err) - } - prog.N++ - - configDigest, err := importConfig(authedCtx, c.client.ImportsApi, sessionID, cfg.SBOM.Source.ImageMetadata.RawConfig, stage) - if err != nil { - return fmt.Errorf("failed to import Config: %w", err) - } - prog.N++ - - dockerfileDigest, err := importDockerfile(authedCtx, c.client.ImportsApi, sessionID, cfg.Dockerfile, stage) - if err != nil { - return fmt.Errorf("failed to import Dockerfile: %w", err) - } - prog.N++ - - stage.Current = "finalizing" - imageModel := addImageModel(cfg.ImageMetadata, packageDigest, manifestDigest, dockerfileDigest, configDigest, sessionID) - opts := external.AddImageOpts{ - Force: optional.NewBool(cfg.OverwriteExistingUpload), - } - - _, addResponse, err := c.client.ImagesApi.AddImage(authedCtx, imageModel, &opts) - if err != nil { - var detail = "no details given" - var openAPIErr external.GenericOpenAPIError - if errors.As(err, &openAPIErr) { - detail = string(openAPIErr.Body()) - } - return fmt.Errorf("unable to complete import session=%q: %w: %s", sessionID, err, detail) - } - defer addResponse.Body.Close() - - prog.N++ - - stage.Current = "" - prog.SetCompleted() - - return nil -} - -func addImageModel(imageMetadata image.Metadata, packageDigest, manifestDigest, dockerfileDigest, configDigest, sessionID string) external.ImageAnalysisRequest { - var tags = make([]string, len(imageMetadata.Tags)) - for i, t := range imageMetadata.Tags { - tags[i] = t.String() - } - - return external.ImageAnalysisRequest{ - Source: external.ImageSource{ - Import: &external.ImageImportManifest{ - Contents: external.ImportContentDigests{ - Packages: packageDigest, - Manifest: manifestDigest, - Dockerfile: dockerfileDigest, - ImageConfig: configDigest, - }, - Tags: tags, - Digest: imageMetadata.ManifestDigest, - LocalImageId: imageMetadata.ID, - OperationUuid: sessionID, - }, - }, - } -} diff --git a/internal/anchore/import_config.go b/internal/anchore/import_config.go deleted file mode 100644 index feedc0aa3c4..00000000000 --- a/internal/anchore/import_config.go +++ /dev/null @@ -1,50 +0,0 @@ -//nolint:dupl -package anchore - -import ( - "context" - "encoding/json" - "errors" - "fmt" - "net/http" - - "github.com/wagoodman/go-progress" - - "github.com/anchore/client-go/pkg/external" - "github.com/anchore/syft/internal/log" -) - -type configImportAPI interface { - ImportImageConfig(ctx context.Context, sessionID string, contents interface{}) (external.ImageImportContentResponse, *http.Response, error) -} - -func importConfig(ctx context.Context, api configImportAPI, sessionID string, config []byte, stage *progress.Stage) (string, error) { - if len(config) > 0 { - log.Debug("importing image config") - stage.Current = "image config" - - // API requires an object, but we do not verify the shape of this object locally - var sender map[string]interface{} - if err := json.Unmarshal(config, &sender); err != nil { - return "", err - } - - response, httpResponse, err := api.ImportImageConfig(ctx, sessionID, sender) - if err != nil { - var openAPIErr external.GenericOpenAPIError - if errors.As(err, &openAPIErr) { - log.Errorf("api response: %+v", string(openAPIErr.Body())) - } - return "", fmt.Errorf("unable to import Config: %w", err) - } - - defer httpResponse.Body.Close() - - if httpResponse.StatusCode != 200 { - return "", fmt.Errorf("unable to import Config: %s", httpResponse.Status) - } - - return response.Digest, nil - } - return "", nil -} diff --git a/internal/anchore/import_config_test.go b/internal/anchore/import_config_test.go deleted file mode 100644 index ecd964be302..00000000000 --- a/internal/anchore/import_config_test.go +++ /dev/null @@ -1,129 +0,0 @@ -package anchore - -import ( - "context" - "encoding/json" - "fmt" - "net/http" - "strings" - "testing" - - "github.com/docker/docker/pkg/ioutils" - "github.com/go-test/deep" - "github.com/wagoodman/go-progress" - - "github.com/anchore/client-go/pkg/external" -) - -type mockConfigImportAPI struct { - sessionID string - model interface{} - httpResponse *http.Response - err error - ctx context.Context - responseDigest string - wasCalled bool -} - -func (m *mockConfigImportAPI) ImportImageConfig(ctx context.Context, sessionID string, contents interface{}) (external.ImageImportContentResponse, *http.Response, error) { - m.wasCalled = true - m.model = contents - m.sessionID = sessionID - m.ctx = ctx - if m.httpResponse == nil { - m.httpResponse = &http.Response{} - } - m.httpResponse.Body = ioutils.NewReadCloserWrapper(strings.NewReader(""), func() error { return nil }) - return external.ImageImportContentResponse{Digest: m.responseDigest}, m.httpResponse, m.err -} - -func TestConfigImport(t *testing.T) { - - sessionID := "my-session" - - tests := []struct { - name string - manifestJSONStr string - api *mockConfigImportAPI - expectsError bool - expectsCall bool - }{ - - { - name: "Go case: import works", - manifestJSONStr: `{ "key": "the-manifest-contents!" }`, - api: &mockConfigImportAPI{ - httpResponse: &http.Response{StatusCode: 200}, - responseDigest: "digest!", - }, - expectsCall: true, - }, - { - name: "No manifest provided", - manifestJSONStr: "", - api: &mockConfigImportAPI{}, - expectsCall: false, - }, - { - name: "API returns an error", - manifestJSONStr: `{ "key": "the-manifest-contents!" }`, - api: &mockConfigImportAPI{ - err: fmt.Errorf("api error, something went wrong"), - }, - expectsError: true, - expectsCall: true, - }, - { - name: "API HTTP-level error", - manifestJSONStr: `{ "key": "the-manifest-contents!" }`, - api: &mockConfigImportAPI{ - httpResponse: &http.Response{StatusCode: 404}, - }, - expectsError: true, - expectsCall: true, - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - - digest, err := importConfig(context.TODO(), test.api, sessionID, []byte(test.manifestJSONStr), &progress.Stage{}) - - // validate error handling - if err != nil && !test.expectsError { - t.Fatalf("did not expect an error, but got: %+v", err) - } else if err == nil && test.expectsError { - t.Fatalf("did expect an error, but got none") - } - - if !test.api.wasCalled && test.expectsCall { - t.Fatalf("was not called!") - } else if test.api.wasCalled && !test.expectsCall { - t.Fatalf("should not have been called") - } - - if !test.expectsCall { - return - } - - if digest != test.api.responseDigest { - t.Errorf("unexpected content digest: %q != %q", digest, test.api.responseDigest) - } - - // validating that the mock got the right parameters - if test.api.sessionID != sessionID { - t.Errorf("different session ID: %s != %s", test.api.sessionID, sessionID) - } - - var expected map[string]interface{} - if err := json.Unmarshal([]byte(test.manifestJSONStr), &expected); err != nil { - t.Fatalf("could not unmarshal expected results") - } - - for _, d := range deep.Equal(test.api.model, expected) { - t.Errorf("model difference: %s", d) - } - - }) - } -} diff --git a/internal/anchore/import_dockerfile.go b/internal/anchore/import_dockerfile.go deleted file mode 100644 index 83fd31633bd..00000000000 --- a/internal/anchore/import_dockerfile.go +++ /dev/null @@ -1,42 +0,0 @@ -package anchore - -import ( - "context" - "errors" - "fmt" - "net/http" - - "github.com/wagoodman/go-progress" - - "github.com/anchore/client-go/pkg/external" - "github.com/anchore/syft/internal/log" -) - -type dockerfileImportAPI interface { - ImportImageDockerfile(ctx context.Context, sessionID string, contents string) (external.ImageImportContentResponse, *http.Response, error) -} - -func importDockerfile(ctx context.Context, api dockerfileImportAPI, sessionID string, dockerfile []byte, stage *progress.Stage) (string, error) { - if len(dockerfile) > 0 { - log.Debug("importing dockerfile") - stage.Current = "dockerfile" - - response, httpResponse, err := api.ImportImageDockerfile(ctx, sessionID, string(dockerfile)) - if err != nil { - var openAPIErr external.GenericOpenAPIError - if errors.As(err, &openAPIErr) { - log.Errorf("api response: %+v", string(openAPIErr.Body())) - } - return "", fmt.Errorf("unable to import Dockerfile: %w", err) - } - - defer httpResponse.Body.Close() - - if httpResponse.StatusCode != 200 { - return "", fmt.Errorf("unable to import Dockerfile: %s", httpResponse.Status) - } - - return response.Digest, nil - } - return "", nil -} diff --git a/internal/anchore/import_dockerfile_test.go b/internal/anchore/import_dockerfile_test.go deleted file mode 100644 index 336fea1c491..00000000000 --- a/internal/anchore/import_dockerfile_test.go +++ /dev/null @@ -1,123 +0,0 @@ -package anchore - -import ( - "context" - "fmt" - "net/http" - "strings" - "testing" - - "github.com/docker/docker/pkg/ioutils" - "github.com/go-test/deep" - "github.com/wagoodman/go-progress" - - "github.com/anchore/client-go/pkg/external" -) - -type mockDockerfileImportAPI struct { - sessionID string - model string - httpResponse *http.Response - err error - ctx context.Context - responseDigest string - wasCalled bool -} - -func (m *mockDockerfileImportAPI) ImportImageDockerfile(ctx context.Context, sessionID string, contents string) (external.ImageImportContentResponse, *http.Response, error) { - m.wasCalled = true - m.model = contents - m.sessionID = sessionID - m.ctx = ctx - if m.httpResponse == nil { - m.httpResponse = &http.Response{} - } - m.httpResponse.Body = ioutils.NewReadCloserWrapper(strings.NewReader(""), func() error { return nil }) - return external.ImageImportContentResponse{Digest: m.responseDigest}, m.httpResponse, m.err -} - -func TestDockerfileImport(t *testing.T) { - - sessionID := "my-session" - - tests := []struct { - name string - dockerfile string - api *mockDockerfileImportAPI - expectsError bool - expectsCall bool - }{ - - { - name: "Go case: import works", - dockerfile: "the-manifest-contents!", - api: &mockDockerfileImportAPI{ - httpResponse: &http.Response{StatusCode: 200}, - responseDigest: "digest!", - }, - expectsCall: true, - }, - { - name: "No manifest provided", - dockerfile: "", - api: &mockDockerfileImportAPI{}, - expectsCall: false, - }, - { - name: "API returns an error", - dockerfile: "the-manifest-contents!", - api: &mockDockerfileImportAPI{ - err: fmt.Errorf("api error, something went wrong"), - }, - expectsError: true, - expectsCall: true, - }, - { - name: "API HTTP-level error", - dockerfile: "the-manifest-contents!", - api: &mockDockerfileImportAPI{ - httpResponse: &http.Response{StatusCode: 404}, - }, - expectsError: true, - expectsCall: true, - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - - digest, err := importDockerfile(context.TODO(), test.api, sessionID, []byte(test.dockerfile), &progress.Stage{}) - - // validate error handling - if err != nil && !test.expectsError { - t.Fatalf("did not expect an error, but got: %+v", err) - } else if err == nil && test.expectsError { - t.Fatalf("did expect an error, but got none") - } - - if !test.api.wasCalled && test.expectsCall { - t.Fatalf("was not called!") - } else if test.api.wasCalled && !test.expectsCall { - t.Fatalf("should not have been called") - } - - if !test.expectsCall { - return - } - - if digest != test.api.responseDigest { - t.Errorf("unexpected content digest: %q != %q", digest, test.api.responseDigest) - } - - // validating that the mock got the right parameters - if test.api.sessionID != sessionID { - t.Errorf("different session ID: %s != %s", test.api.sessionID, sessionID) - } - - for _, d := range deep.Equal(test.api.model, test.dockerfile) { - t.Errorf("model difference: %s", d) - } - - }) - } -} diff --git a/internal/anchore/import_manifest.go b/internal/anchore/import_manifest.go deleted file mode 100644 index f1fb7d905cf..00000000000 --- a/internal/anchore/import_manifest.go +++ /dev/null @@ -1,50 +0,0 @@ -//nolint:dupl -package anchore - -import ( - "context" - "encoding/json" - "errors" - "fmt" - "net/http" - - "github.com/wagoodman/go-progress" - - "github.com/anchore/client-go/pkg/external" - "github.com/anchore/syft/internal/log" -) - -type manifestImportAPI interface { - ImportImageManifest(ctx context.Context, sessionID string, contents interface{}) (external.ImageImportContentResponse, *http.Response, error) -} - -func importManifest(ctx context.Context, api manifestImportAPI, sessionID string, manifest []byte, stage *progress.Stage) (string, error) { - if len(manifest) > 0 { - log.Debug("importing image manifest") - stage.Current = "image manifest" - - // API requires an object, but we do not verify the shape of this object locally - var sender map[string]interface{} - if err := json.Unmarshal(manifest, &sender); err != nil { - return "", err - } - - response, httpResponse, err := api.ImportImageManifest(ctx, sessionID, sender) - if err != nil { - var openAPIErr external.GenericOpenAPIError - if errors.As(err, &openAPIErr) { - log.Errorf("api response: %+v", string(openAPIErr.Body())) - } - return "", fmt.Errorf("unable to import Manifest: %w", err) - } - - defer httpResponse.Body.Close() - - if httpResponse.StatusCode != 200 { - return "", fmt.Errorf("unable to import Manifest: %s", httpResponse.Status) - } - - return response.Digest, nil - } - return "", nil -} diff --git a/internal/anchore/import_manifest_test.go b/internal/anchore/import_manifest_test.go deleted file mode 100644 index e4edea3a0d0..00000000000 --- a/internal/anchore/import_manifest_test.go +++ /dev/null @@ -1,129 +0,0 @@ -package anchore - -import ( - "context" - "encoding/json" - "fmt" - "net/http" - "strings" - "testing" - - "github.com/docker/docker/pkg/ioutils" - "github.com/go-test/deep" - "github.com/wagoodman/go-progress" - - "github.com/anchore/client-go/pkg/external" -) - -type mockManifestImportAPI struct { - sessionID string - model interface{} - httpResponse *http.Response - err error - ctx context.Context - responseDigest string - wasCalled bool -} - -func (m *mockManifestImportAPI) ImportImageManifest(ctx context.Context, sessionID string, contents interface{}) (external.ImageImportContentResponse, *http.Response, error) { - m.wasCalled = true - m.model = contents - m.sessionID = sessionID - m.ctx = ctx - if m.httpResponse == nil { - m.httpResponse = &http.Response{} - } - m.httpResponse.Body = ioutils.NewReadCloserWrapper(strings.NewReader(""), func() error { return nil }) - return external.ImageImportContentResponse{Digest: m.responseDigest}, m.httpResponse, m.err -} - -func TestManifestImport(t *testing.T) { - - sessionID := "my-session" - - tests := []struct { - name string - manifest string - api *mockManifestImportAPI - expectsError bool - expectsCall bool - }{ - - { - name: "Go case: import works", - manifest: `{ "key": "the-config-contents!" }`, - api: &mockManifestImportAPI{ - httpResponse: &http.Response{StatusCode: 200}, - responseDigest: "digest!", - }, - expectsCall: true, - }, - { - name: "No manifest provided", - manifest: "", - api: &mockManifestImportAPI{}, - expectsCall: false, - }, - { - name: "API returns an error", - manifest: `{ "key": "the-config-contents!" }`, - api: &mockManifestImportAPI{ - err: fmt.Errorf("api error, something went wrong"), - }, - expectsError: true, - expectsCall: true, - }, - { - name: "API HTTP-level error", - manifest: `{ "key": "the-config-contents!" }`, - api: &mockManifestImportAPI{ - httpResponse: &http.Response{StatusCode: 404}, - }, - expectsError: true, - expectsCall: true, - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - - digest, err := importManifest(context.TODO(), test.api, sessionID, []byte(test.manifest), &progress.Stage{}) - - // validate error handling - if err != nil && !test.expectsError { - t.Fatalf("did not expect an error, but got: %+v", err) - } else if err == nil && test.expectsError { - t.Fatalf("did expect an error, but got none") - } - - if !test.api.wasCalled && test.expectsCall { - t.Fatalf("was not called!") - } else if test.api.wasCalled && !test.expectsCall { - t.Fatalf("should not have been called") - } - - if !test.expectsCall { - return - } - - if digest != test.api.responseDigest { - t.Errorf("unexpected content digest: %q != %q", digest, test.api.responseDigest) - } - - // validating that the mock got the right parameters - if test.api.sessionID != sessionID { - t.Errorf("different session ID: %s != %s", test.api.sessionID, sessionID) - } - - var expected map[string]interface{} - if err := json.Unmarshal([]byte(test.manifest), &expected); err != nil { - t.Fatalf("could not unmarshal expected results") - } - - for _, d := range deep.Equal(test.api.model, expected) { - t.Errorf("model difference: %s", d) - } - - }) - } -} diff --git a/internal/anchore/import_package_sbom.go b/internal/anchore/import_package_sbom.go deleted file mode 100644 index cf655197208..00000000000 --- a/internal/anchore/import_package_sbom.go +++ /dev/null @@ -1,119 +0,0 @@ -package anchore - -import ( - "bytes" - "context" - "encoding/json" - "errors" - "fmt" - "net/http" - - "github.com/wagoodman/go-progress" - - "github.com/anchore/client-go/pkg/external" - "github.com/anchore/syft/internal/log" - "github.com/anchore/syft/syft/formats/syftjson" - syftjsonModel "github.com/anchore/syft/syft/formats/syftjson/model" - "github.com/anchore/syft/syft/sbom" -) - -type packageSBOMImportAPI interface { - ImportImagePackages(context.Context, string, external.ImagePackageManifest) (external.ImageImportContentResponse, *http.Response, error) -} - -// importSBOM mirrors all elements found on the syftjson model format object relative to the anchore engine import schema. -type importSBOM struct { - Artifacts []syftjsonModel.Package `json:"artifacts"` // Artifacts is the list of packages discovered and placed into the catalog - ArtifactRelationships []syftjsonModel.Relationship `json:"artifactRelationships"` - Files []syftjsonModel.File `json:"files,omitempty"` // note: must have omitempty - Secrets []syftjsonModel.Secrets `json:"secrets,omitempty"` // note: must have omitempty - Source syftjsonModel.Source `json:"source"` // Source represents the original object that was cataloged - Distro external.ImportDistribution `json:"distro"` // Distro represents the Linux distribution that was detected from the source - Descriptor syftjsonModel.Descriptor `json:"descriptor"` // Descriptor is a block containing self-describing information about syft - Schema syftjsonModel.Schema `json:"schema"` // Schema is a block reserved for defining the version for the shape of this JSON document and where to find the schema document to validate the shape -} - -// toImportSBOMModel transforms the current sbom shape into what is needed for the current anchore import api shape. -func toImportSBOMModel(s sbom.SBOM) importSBOM { - m := syftjson.ToFormatModel(s) - - var idLike string - if len(m.Distro.IDLike) > 0 { - idLike = m.Distro.IDLike[0] - } - - var version = m.Distro.VersionID // note: version is intentionally not used as the default - if version == "" { - version = m.Distro.Version - } - - var name = m.Distro.ID // note: name is intentionally not used as the default - if name == "" { - name = m.Distro.Name - } - - return importSBOM{ - Artifacts: m.Artifacts, - ArtifactRelationships: m.ArtifactRelationships, - Files: m.Files, - Secrets: m.Secrets, - Source: m.Source, - Distro: external.ImportDistribution{ - Name: name, - Version: version, - IdLike: idLike, - }, - Descriptor: m.Descriptor, - Schema: m.Schema, - } -} - -func packageSbomModel(s sbom.SBOM) (*external.ImagePackageManifest, error) { - var buf bytes.Buffer - - doc := toImportSBOMModel(s) - - enc := json.NewEncoder(&buf) - // prevent > and < from being escaped in the payload - enc.SetEscapeHTML(false) - enc.SetIndent("", " ") - - if err := enc.Encode(&doc); err != nil { - return nil, fmt.Errorf("unable to encode import JSON model: %w", err) - } - - // the model is 1:1 the JSON output of today. As the schema changes, this will need to be converted into individual mappings. - var model external.ImagePackageManifest - if err := json.Unmarshal(buf.Bytes(), &model); err != nil { - return nil, fmt.Errorf("unable to convert JSON output to import model: %w", err) - } - - return &model, nil -} - -func importPackageSBOM(ctx context.Context, api packageSBOMImportAPI, sessionID string, s sbom.SBOM, stage *progress.Stage) (string, error) { - log.Debug("importing package SBOM") - stage.Current = "package SBOM" - - model, err := packageSbomModel(s) - if err != nil { - return "", fmt.Errorf("unable to create PackageSBOM model: %w", err) - } - - response, httpResponse, err := api.ImportImagePackages(ctx, sessionID, *model) - if err != nil { - var openAPIErr external.GenericOpenAPIError - if errors.As(err, &openAPIErr) { - log.Errorf("api response: %+v", string(openAPIErr.Body())) - } - return "", fmt.Errorf("unable to import PackageSBOM: %w", err) - } - - defer httpResponse.Body.Close() - - if httpResponse.StatusCode != 200 { - return "", fmt.Errorf("unable to import PackageSBOM: %s", httpResponse.Status) - } - - return response.Digest, nil -} diff --git a/internal/anchore/import_package_sbom_test.go b/internal/anchore/import_package_sbom_test.go deleted file mode 100644 index c1603efd71c..00000000000 --- a/internal/anchore/import_package_sbom_test.go +++ /dev/null @@ -1,398 +0,0 @@ -package anchore - -import ( - "context" - "encoding/json" - "fmt" - "net/http" - "strings" - "testing" - - "github.com/docker/docker/pkg/ioutils" - "github.com/go-test/deep" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "github.com/wagoodman/go-progress" - - "github.com/anchore/client-go/pkg/external" - "github.com/anchore/syft/syft/artifact" - "github.com/anchore/syft/syft/formats/syftjson" - "github.com/anchore/syft/syft/linux" - "github.com/anchore/syft/syft/pkg" - "github.com/anchore/syft/syft/sbom" - "github.com/anchore/syft/syft/source" -) - -func must(c pkg.CPE, e error) pkg.CPE { - if e != nil { - panic(e) - } - return c -} - -type mockPackageSBOMImportAPI struct { - sessionID string - model external.ImagePackageManifest - httpResponse *http.Response - err error - ctx context.Context - responseDigest string -} - -func (m *mockPackageSBOMImportAPI) ImportImagePackages(ctx context.Context, sessionID string, model external.ImagePackageManifest) (external.ImageImportContentResponse, *http.Response, error) { - m.model = model - m.sessionID = sessionID - m.ctx = ctx - if m.httpResponse == nil { - m.httpResponse = &http.Response{} - } - m.httpResponse.Body = ioutils.NewReadCloserWrapper(strings.NewReader(""), func() error { return nil }) - return external.ImageImportContentResponse{Digest: m.responseDigest}, m.httpResponse, m.err -} - -func sbomFixture() sbom.SBOM { - return sbom.SBOM{ - Artifacts: sbom.Artifacts{ - PackageCatalog: pkg.NewCatalog(pkg.Package{ - Name: "name", - Version: "version", - FoundBy: "foundBy", - Locations: source.NewLocationSet( - source.Location{ - Coordinates: source.Coordinates{ - RealPath: "path", - FileSystemID: "layerID", - }, - }, - ), - Licenses: []string{"license"}, - Language: pkg.Python, - Type: pkg.PythonPkg, - CPEs: []pkg.CPE{ - must(pkg.NewCPE("cpe:2.3:*:some:package:1:*:*:*:*:*:*:*")), - }, - PURL: "purl", - MetadataType: pkg.PythonPackageMetadataType, - Metadata: pkg.PythonPackageMetadata{ - Name: "p-name", - Version: "p-version", - License: "p-license", - Author: "p-author", - AuthorEmail: "p-email", - Platform: "p-platform", - Files: []pkg.PythonFileRecord{ - { - Path: "p-path", - Digest: &pkg.PythonFileDigest{ - Algorithm: "p-alg", - Value: "p-digest", - }, - Size: "p-size", - }, - }, - SitePackagesRootPath: "p-site-packages-root", - TopLevelPackages: []string{"top-level"}, - }, - }), - LinuxDistribution: &linux.Release{ - ID: "centos", - Version: "8.0", - VersionID: "8.0", - IDLike: []string{"rhel"}, - }, - }, - Relationships: []artifact.Relationship{ - { - From: source.NewLocation("/place1"), - To: source.NewLocation("/place2"), - Type: artifact.ContainsRelationship, - }, - }, - Source: source.Metadata{ - Scheme: source.ImageScheme, - ImageMetadata: source.ImageMetadata{ - UserInput: "user-in", - Layers: nil, - Size: 10, - ManifestDigest: "sha256:digest!", - MediaType: "mediatype!", - Tags: nil, - }, - }, - } - -} - -func TestPackageSbomImport(t *testing.T) { - sbomResult := sbomFixture() - theModel, err := packageSbomModel(sbomResult) - if err != nil { - t.Fatalf("could not get sbom model: %+v", err) - } - - sessionID := "my-session" - - tests := []struct { - name string - api *mockPackageSBOMImportAPI - expectsError bool - }{ - - { - name: "Go case: import works", - api: &mockPackageSBOMImportAPI{ - httpResponse: &http.Response{StatusCode: 200}, - responseDigest: "digest!", - }, - }, - { - name: "API returns an error", - api: &mockPackageSBOMImportAPI{ - err: fmt.Errorf("API error, something went wrong."), - }, - expectsError: true, - }, - { - name: "API HTTP-level error", - api: &mockPackageSBOMImportAPI{ - httpResponse: &http.Response{StatusCode: 404}, - }, - expectsError: true, - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - - digest, err := importPackageSBOM(context.TODO(), test.api, sessionID, sbomResult, &progress.Stage{}) - - // validate error handling - if err != nil && !test.expectsError { - t.Fatalf("did not expect an error, but got: %+v", err) - } else if err == nil && test.expectsError { - t.Fatalf("did expect an error, but got none") - } - - if digest != test.api.responseDigest { - t.Errorf("unexpected content digest: %q != %q", digest, test.api.responseDigest) - } - - // validating that the mock got the right parameters (api.ImportImagePackages) - if test.api.sessionID != sessionID { - t.Errorf("different session ID: %s != %s", test.api.sessionID, sessionID) - } - - for _, d := range deep.Equal(&test.api.model, theModel) { - t.Errorf("model difference: %s", d) - } - - }) - } -} - -type modelAssertion func(t *testing.T, model *external.ImagePackageManifest) - -func Test_packageSbomModel(t *testing.T) { - fix := sbomFixture() - - tests := []struct { - name string - sbom sbom.SBOM - traits []modelAssertion - }{ - { - name: "distro: has single distro id-like", - sbom: sbom.SBOM{ - Artifacts: sbom.Artifacts{ - LinuxDistribution: &linux.Release{ - Name: "centos-name", - ID: "centos-id", - IDLike: []string{ - "centos-id-like-1", - }, - Version: "version", - VersionID: "version-id", - }, - }, - }, - traits: []modelAssertion{ - hasDistroInfo("centos-id", "version-id", "centos-id-like-1"), - }, - }, - { - name: "distro: has multiple distro id-like", - sbom: sbom.SBOM{ - Artifacts: sbom.Artifacts{ - LinuxDistribution: &linux.Release{ - Name: "centos-name", - ID: "centos-id", - IDLike: []string{ - "centos-id-like-1", - "centos-id-like-2", - }, - Version: "version", - VersionID: "version-id", - }, - }, - }, - traits: []modelAssertion{ - hasDistroInfo("centos-id", "version-id", "centos-id-like-1"), - }, - }, - { - name: "distro: has no distro id-like", - sbom: sbom.SBOM{ - Artifacts: sbom.Artifacts{ - LinuxDistribution: &linux.Release{ - Name: "centos-name", - ID: "centos-id", - IDLike: []string{}, - Version: "version", - VersionID: "version-id", - }, - }, - }, - traits: []modelAssertion{ - hasDistroInfo("centos-id", "version-id", ""), - }, - }, - { - name: "distro: has no version-id", - sbom: sbom.SBOM{ - Artifacts: sbom.Artifacts{ - LinuxDistribution: &linux.Release{ - Name: "centos-name", - ID: "centos-id", - IDLike: []string{}, - Version: "version", - VersionID: "", - }, - }, - }, - traits: []modelAssertion{ - hasDistroInfo("centos-id", "version", ""), - }, - }, - { - name: "distro: has no id", - sbom: sbom.SBOM{ - Artifacts: sbom.Artifacts{ - LinuxDistribution: &linux.Release{ - Name: "centos-name", - ID: "", - IDLike: []string{}, - Version: "version", - VersionID: "version-id", - }, - }, - }, - traits: []modelAssertion{ - hasDistroInfo("centos-name", "version-id", ""), - }, - }, - { - name: "should have expected packages", - sbom: fix, - traits: []modelAssertion{ - func(t *testing.T, model *external.ImagePackageManifest) { - require.Len(t, model.Artifacts, 1) - - modelPkg := model.Artifacts - modelBytes, err := json.Marshal(&modelPkg) - require.NoError(t, err) - - fixPkg := syftjson.ToFormatModel(fix).Artifacts - fixBytes, err := json.Marshal(&fixPkg) - require.NoError(t, err) - - assert.JSONEq(t, string(fixBytes), string(modelBytes)) - }, - }, - }, - { - name: "should have expected relationships", - sbom: fix, - traits: []modelAssertion{ - func(t *testing.T, model *external.ImagePackageManifest) { - modelPkg := model.ArtifactRelationships - modelBytes, err := json.Marshal(&modelPkg) - require.NoError(t, err) - - fixPkg := syftjson.ToFormatModel(fix).ArtifactRelationships - fixBytes, err := json.Marshal(&fixPkg) - require.NoError(t, err) - - assert.JSONEq(t, string(fixBytes), string(modelBytes)) - }, - }, - }, - { - name: "should have expected schema", - sbom: fix, - traits: []modelAssertion{ - func(t *testing.T, model *external.ImagePackageManifest) { - modelPkg := model.Schema - modelBytes, err := json.Marshal(&modelPkg) - require.NoError(t, err) - - fixPkg := syftjson.ToFormatModel(fix).Schema - fixBytes, err := json.Marshal(&fixPkg) - require.NoError(t, err) - - assert.JSONEq(t, string(fixBytes), string(modelBytes)) - }, - }, - }, - { - name: "should have expected descriptor", - sbom: fix, - traits: []modelAssertion{ - func(t *testing.T, model *external.ImagePackageManifest) { - modelPkg := model.Descriptor - modelBytes, err := json.Marshal(&modelPkg) - require.NoError(t, err) - - fixPkg := syftjson.ToFormatModel(fix).Descriptor - fixBytes, err := json.Marshal(&fixPkg) - require.NoError(t, err) - - assert.JSONEq(t, string(fixBytes), string(modelBytes)) - }, - }, - }, - { - name: "should have expected source", - sbom: fix, - traits: []modelAssertion{ - func(t *testing.T, model *external.ImagePackageManifest) { - modelPkg := model.Source - modelBytes, err := json.Marshal(&modelPkg) - require.NoError(t, err) - - fixPkg := syftjson.ToFormatModel(fix).Source - fixBytes, err := json.Marshal(&fixPkg) - require.NoError(t, err) - - assert.JSONEq(t, string(fixBytes), string(modelBytes)) - }, - }, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got, err := packageSbomModel(tt.sbom) - require.NoError(t, err) - for _, fn := range tt.traits { - fn(t, got) - } - }) - } -} - -func hasDistroInfo(name, version, idLike string) modelAssertion { - return func(t *testing.T, model *external.ImagePackageManifest) { - assert.Equal(t, name, model.Distro.Name) - assert.Equal(t, version, model.Distro.Version) - assert.Equal(t, idLike, model.Distro.IdLike) - } -} diff --git a/internal/config/anchore.go b/internal/config/anchore.go deleted file mode 100644 index 7e3bc281c9d..00000000000 --- a/internal/config/anchore.go +++ /dev/null @@ -1,21 +0,0 @@ -package config - -import "github.com/spf13/viper" - -type anchore struct { - // upload options - Host string `yaml:"host" json:"host" mapstructure:"host"` // -H , hostname of the engine/enterprise instance to upload to (setting this value enables upload) - Path string `yaml:"path" json:"path" mapstructure:"path"` // override the engine/enterprise API upload path - // IMPORTANT: do not show the username in any YAML/JSON output (sensitive information) - Username string `yaml:"-" json:"-" mapstructure:"username"` // -u , username to authenticate upload - // IMPORTANT: do not show the password in any YAML/JSON output (sensitive information) - Password string `yaml:"-" json:"-" mapstructure:"password"` // -p , password to authenticate upload - Dockerfile string `yaml:"dockerfile" json:"dockerfile" mapstructure:"dockerfile"` // -d , dockerfile to attach for upload - OverwriteExistingImage bool `yaml:"overwrite-existing-image" json:"overwrite-existing-image" mapstructure:"overwrite-existing-image"` // --overwrite-existing-image , if any of the SBOM components have already been uploaded this flag will ensure they are overwritten with the current upload - ImportTimeout uint `yaml:"import-timeout" json:"import-timeout" mapstructure:"import-timeout"` // --import-timeout - // , customize the number of seconds within which the SBOM import must be completed or canceled -} - -func (cfg anchore) loadDefaultValues(v *viper.Viper) { - v.SetDefault("anchore.path", "") -} diff --git a/internal/config/application.go b/internal/config/application.go index 037412c5870..13ec69979f4 100644 --- a/internal/config/application.go +++ b/internal/config/application.go @@ -43,7 +43,6 @@ type Application struct { OutputTemplatePath string `yaml:"output-template-path" json:"output-template-path" mapstructure:"output-template-path"` // -t template file to use for output File string `yaml:"file" json:"file" mapstructure:"file"` // --file, the file to write report output to CheckForAppUpdate bool `yaml:"check-for-app-update" json:"check-for-app-update" mapstructure:"check-for-app-update"` // whether to check for an application update on start up or not - Anchore anchore `yaml:"anchore" json:"anchore" mapstructure:"anchore"` // options for interacting with Anchore Engine/Enterprise Dev development `yaml:"dev" json:"dev" mapstructure:"dev"` Log logging `yaml:"log" json:"log" mapstructure:"log"` // all logging-related options Catalogers []string `yaml:"catalogers" json:"catalogers" mapstructure:"catalogers"` @@ -112,7 +111,6 @@ func (cfg *Application) parseConfigValues() error { // parse application config options for _, optionFn := range []func() error{ - cfg.parseUploadOptions, cfg.parseLogLevelOption, cfg.parseFile, } { @@ -136,13 +134,6 @@ func (cfg *Application) parseConfigValues() error { return nil } -func (cfg *Application) parseUploadOptions() error { - if cfg.Anchore.Host == "" && cfg.Anchore.Dockerfile != "" { - return fmt.Errorf("cannot provide dockerfile option without enabling upload") - } - return nil -} - func (cfg *Application) parseLogLevelOption() error { switch { case cfg.Quiet: From 359eafd534d5f13c893309f26cff6f1d68b7c132 Mon Sep 17 00:00:00 2001 From: Keith Zantow Date: Mon, 10 Oct 2022 13:48:02 -0400 Subject: [PATCH 2/3] lint: mod tidy Signed-off-by: Keith Zantow --- go.mod | 2 -- go.sum | 3 --- 2 files changed, 5 deletions(-) diff --git a/go.mod b/go.mod index 32c702771e2..7fd5b91ab85 100644 --- a/go.mod +++ b/go.mod @@ -8,12 +8,10 @@ require ( github.com/acobaugh/osrelease v0.1.0 github.com/adrg/xdg v0.3.3 github.com/alecthomas/jsonschema v0.0.0-20210301060011-54c507b6f074 - github.com/anchore/client-go v0.0.0-20210222170800-9c70f9b80bcf github.com/anchore/go-macholibre v0.0.0-20220308212642-53e6d0aaf6fb github.com/anchore/go-testutils v0.0.0-20200925183923-d5f45b0d3c04 github.com/anchore/go-version v1.2.2-0.20200701162849-18adb9c92b9b github.com/anchore/packageurl-go v0.1.1-0.20220428202044-a072fa3cb6d7 - github.com/antihax/optional v1.0.0 github.com/bmatcuk/doublestar/v4 v4.0.2 github.com/dustin/go-humanize v1.0.0 github.com/facebookincubator/nvdtools v0.1.4 diff --git a/go.sum b/go.sum index bdb30270218..42e92c903a1 100644 --- a/go.sum +++ b/go.sum @@ -270,8 +270,6 @@ github.com/alibabacloud-go/tea-xml v1.1.2/go.mod h1:Rq08vgCcCAjHyRi/M7xlHKUykZCE github.com/aliyun/credentials-go v1.1.2/go.mod h1:ozcZaMR5kLM7pwtCMEpVmQ242suV6qTJya2bDq4X1Tw= github.com/aliyun/credentials-go v1.2.3 h1:Vmodnr52Rz1mcbwn0kzMhLRKb6soizewuKXdfZiNemU= github.com/aliyun/credentials-go v1.2.3/go.mod h1:/KowD1cfGSLrLsH28Jr8W+xwoId0ywIy5lNzDz6O1vw= -github.com/anchore/client-go v0.0.0-20210222170800-9c70f9b80bcf h1:DYssiUV1pBmKqzKsm4mqXx8artqC0Q8HgZsVI3lMsAg= -github.com/anchore/client-go v0.0.0-20210222170800-9c70f9b80bcf/go.mod h1:FaODhIA06mxO1E6R32JE0TL1JWZZkmjRIAd4ULvHUKk= github.com/anchore/go-macholibre v0.0.0-20220308212642-53e6d0aaf6fb h1:iDMnx6LIjtjZ46C0akqveX83WFzhpTD3eqOthawb5vU= github.com/anchore/go-macholibre v0.0.0-20220308212642-53e6d0aaf6fb/go.mod h1:DmTY2Mfcv38hsHbG78xMiTDdxFtkHpgYNVDPsF2TgHk= github.com/anchore/go-testutils v0.0.0-20200925183923-d5f45b0d3c04 h1:VzprUTpc0vW0nnNKJfJieyH/TZ9UYAnTZs5/gHTdAe8= @@ -290,7 +288,6 @@ github.com/andybalholm/brotli v1.0.4 h1:V7DdXeJtZscaqfNuAdSRuRFzuiKlHSC/Zh3zl9qY github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= github.com/antihax/optional v0.0.0-20180407024304-ca021399b1a6/go.mod h1:V8iCPQYkqmusNa815XgQio277wI47sdRh1dUOLdyC6Q= -github.com/antihax/optional v1.0.0 h1:xK2lYat7ZLaVVcIuj82J8kIro4V6kDe0AUDFboUCwcg= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/aokoli/goutils v1.0.1/go.mod h1:SijmP0QR8LtwsmDs8Yii5Z/S4trXFGFC2oO5g9DP+DQ= github.com/apache/beam v2.28.0+incompatible/go.mod h1:/8NX3Qi8vGstDLLaeaU7+lzVEu/ACaQhYjeefzQ0y1o= From 54b764d904bcefd32cdcd54466c5bc88ad7075f3 Mon Sep 17 00:00:00 2001 From: Keith Zantow Date: Mon, 10 Oct 2022 14:21:24 -0400 Subject: [PATCH 3/3] chore: remove erroneous tests Signed-off-by: Keith Zantow --- test/cli/packages_cmd_test.go | 42 ----------------------------------- 1 file changed, 42 deletions(-) diff --git a/test/cli/packages_cmd_test.go b/test/cli/packages_cmd_test.go index abd3d8e2e7c..2170c6bc506 100644 --- a/test/cli/packages_cmd_test.go +++ b/test/cli/packages_cmd_test.go @@ -132,48 +132,6 @@ func TestPackagesCmdFlags(t *testing.T) { assertSuccessfulReturnCode, }, }, - { - name: "attempt-upload-on-cli-switches", - args: []string{"packages", "-vv", "-H", "localhost:8080", "-u", "the-username", "-d", "test-fixtures/image-pkg-coverage/Dockerfile", "--overwrite-existing-image", coverageImage}, - env: map[string]string{ - "SYFT_ANCHORE_PATH": "path/to/api", - "SYFT_ANCHORE_PASSWORD": "the-password", - }, - assertions: []traitAssertion{ - // we cannot easily assert a successful upload behavior, so instead we are doing the next best thing - // and asserting that the parsed configuration has the expected values and we see log entries - // indicating an upload attempt. - assertNotInOutput("the-username"), - assertNotInOutput("the-password"), - assertInOutput("uploading results to localhost:8080"), - assertInOutput(`dockerfile: test-fixtures/image-pkg-coverage/Dockerfile`), - assertInOutput(`overwrite-existing-image: true`), - assertInOutput(`path: path/to/api`), - assertInOutput(`host: localhost:8080`), - assertFailingReturnCode, // upload can't go anywhere, so if this passes that would be surprising - }, - }, - { - name: "dockerfile-without-upload-is-invalid", - args: []string{"packages", "-vv", "-d", "test-fixtures/image-pkg-coverage/Dockerfile", coverageImage}, - assertions: []traitAssertion{ - - assertNotInOutput("uploading results to localhost:8080"), - assertInOutput("invalid application config: cannot provide dockerfile option without enabling upload"), - assertFailingReturnCode, - }, - }, - { - name: "attempt-upload-with-env-host-set", - args: []string{"packages", "-vv", coverageImage}, - env: map[string]string{ - "SYFT_ANCHORE_HOST": "localhost:8080", - }, - assertions: []traitAssertion{ - assertInOutput("uploading results to localhost:8080"), - assertFailingReturnCode, // upload can't go anywhere, so if this passes that would be surprising - }, - }, { // we want to make certain that syft can catalog a single go binary and get a SBOM report that is not empty name: "catalog-single-go-binary",