Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
119 changes: 119 additions & 0 deletions cmd/sign_off.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
// Copyright 2022 Red Hat, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// SPDX-License-Identifier: Apache-2.0

package cmd

import (
"context"
"encoding/json"
"errors"
"fmt"
"log"

appstudioshared "github.com/redhat-appstudio/managed-gitops/appstudio-shared/apis/appstudio.redhat.com/v1alpha1"
"github.com/spf13/cobra"

"github.com/hacbs-contract/ec-cli/internal/applicationsnapshot"
"github.com/hacbs-contract/ec-cli/internal/image"
)

func signOffCmd() *cobra.Command {
var data = struct {
imageRef string
publicKey string
filePath string
input string
spec *appstudioshared.ApplicationSnapshotSpec
}{
imageRef: "",
publicKey: "",
}
cmd := &cobra.Command{
Use: "sign-off",
Short: "Capture signed off signatures from a source (github repo, Jira)",
Long: `Supported sign off sources are commits captured from a git repo and jira issues.
The git sources return a signed off value and the git commit. The jira issue is
a TODO, but will return the Jira issue with any sign off values.`,

PreRunE: func(cmd *cobra.Command, args []string) error {
spec, err := applicationsnapshot.DetermineInputSpec(data.filePath, data.input, data.imageRef)
if err != nil {
return err
}

data.spec = spec

return nil
},
RunE: func(cmd *cobra.Command, args []string) error {
for _, comp := range data.spec.Components {
err := validate(cmd.Context(), comp.ContainerImage, data.publicKey)
if err != nil {
log.Println(err)
continue
}
}
return nil
},
}

cmd.Flags().StringVar(&data.publicKey, "public-key", "", "Public key")
cmd.Flags().StringVar(&data.imageRef, "image-ref", data.imageRef, "The OCI repo to fetch the attestation from.")
cmd.Flags().StringVarP(&data.filePath, "file-path", "f", data.filePath, "Path to ApplicationSnapshot JSON file")
cmd.Flags().StringVarP(&data.input, "json-input", "j", data.input, "ApplicationSnapshot JSON string")

return cmd
}

func validate(ctx context.Context, imageRef, publicKey string) error {
imageValidator, err := image.NewImageValidator(ctx, imageRef, publicKey, "")
if err != nil {
return err
}

validatedImage, err := imageValidator.ValidateImage(ctx)
if err != nil {
return err
}

for _, att := range validatedImage.Attestations {
signoffSource, err := att.NewSignOffSource()
if err != nil {
return err
}
if signoffSource == nil {
return errors.New("there is no signoff source in attestation")
}

signOff, err := signoffSource.GetSignOff()
if err != nil {
return err
}

if signOff != nil {
payload, err := json.Marshal(signOff)
if err != nil {
return err
}
fmt.Println(string(payload))
}
}
return nil
}

func init() {
rootCmd.AddCommand(signOffCmd())
}
56 changes: 1 addition & 55 deletions cmd/validate_image.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,21 +18,17 @@ package cmd

import (
"context"
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"sync"

"github.com/hashicorp/go-multierror"
appstudioshared "github.com/redhat-appstudio/managed-gitops/appstudio-shared/apis/appstudio.redhat.com/v1alpha1"
log "github.com/sirupsen/logrus"
"github.com/spf13/afero"
"github.com/spf13/cobra"

"github.com/hacbs-contract/ec-cli/internal/applicationsnapshot"
"github.com/hacbs-contract/ec-cli/internal/output"
"github.com/hacbs-contract/ec-cli/internal/utils"
)

type imageValidationFunc func(ctx context.Context, imageRef, policyConfiguration, publicKey, rekorURL string) (*output.Output, error)
Expand Down Expand Up @@ -72,7 +68,7 @@ instance in strict mode:

ec validate image --file-path my-app.yaml --public-key my-key.pem --rekor-url https://rekor.example.org --strict`,
PreRunE: func(cmd *cobra.Command, args []string) error {
s, err := determineInputSpec(data.filePath, data.input, data.imageRef)
s, err := applicationsnapshot.DetermineInputSpec(data.filePath, data.input, data.imageRef)
if err != nil {
return err
}
Expand Down Expand Up @@ -173,53 +169,3 @@ ec validate image --file-path my-app.yaml --public-key my-key.pem --rekor-url ht
}
return cmd
}

func determineInputSpec(filePath string, input string, imageRef string) (*appstudioshared.ApplicationSnapshotSpec, error) {
var appSnapshot appstudioshared.ApplicationSnapshotSpec

// read ApplicationSnapshot provided as a file
if len(filePath) > 0 {
content, err := afero.ReadFile(utils.AppFS, filePath)
if err != nil {
log.Debugf("Problem reading application snapshot from file %s", filePath)
return nil, err
}

err = json.Unmarshal(content, &appSnapshot)
if err != nil {
log.Debugf("Problem parsing application snapshot from file %s", filePath)
return nil, err
}

log.Debugf("Read application snapshot from file %s", filePath)
return &appSnapshot, nil
}

// read ApplicationSnapshot provided as a string
if len(input) > 0 {
// Unmarshall json into struct, exit on failure
if err := json.Unmarshal([]byte(input), &appSnapshot); err != nil {
log.Debugf("Problem parsing application snapshot from input param %s", input)
return nil, err
}

log.Debug("Read application snapshot from input param")
return &appSnapshot, nil
}

// create ApplicationSnapshot with a single image
if len(imageRef) > 0 {
log.Debugf("Generating application snapshot from imageRef %s", imageRef)
return &appstudioshared.ApplicationSnapshotSpec{
Components: []appstudioshared.ApplicationSnapshotComponent{
{
Name: "Unnamed",
ContainerImage: imageRef,
},
},
}, nil
}

log.Debug("No application snapshot available")
return nil, errors.New("neither ApplicationSnapshot nor image reference provided to validate")
}
3 changes: 2 additions & 1 deletion cmd/validate_image_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import (
appstudioshared "github.com/redhat-appstudio/managed-gitops/appstudio-shared/apis/appstudio.redhat.com/v1alpha1"
"github.com/stretchr/testify/assert"

"github.com/hacbs-contract/ec-cli/internal/applicationsnapshot"
"github.com/hacbs-contract/ec-cli/internal/output"
)

Expand Down Expand Up @@ -136,7 +137,7 @@ func Test_determineInputSpec(t *testing.T) {

for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
s, err := determineInputSpec(c.arguments.filePath, c.arguments.input, c.arguments.imageRef)
s, err := applicationsnapshot.DetermineInputSpec(c.arguments.filePath, c.arguments.input, c.arguments.imageRef)
if c.err != "" {
assert.EqualError(t, err, c.err)
}
Expand Down
61 changes: 61 additions & 0 deletions internal/applicationsnapshot/input.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package applicationsnapshot

import (
"encoding/json"
"errors"

"github.com/hacbs-contract/ec-cli/internal/utils"
appstudioshared "github.com/redhat-appstudio/managed-gitops/appstudio-shared/apis/appstudio.redhat.com/v1alpha1"
log "github.com/sirupsen/logrus"
"github.com/spf13/afero"
)

func DetermineInputSpec(filePath string, input string, imageRef string) (*appstudioshared.ApplicationSnapshotSpec, error) {
var appSnapshot appstudioshared.ApplicationSnapshotSpec

// read ApplicationSnapshot provided as a file
if len(filePath) > 0 {
content, err := afero.ReadFile(utils.AppFS, filePath)
if err != nil {
log.Debugf("Problem reading application snapshot from file %s", filePath)
return nil, err
}

err = json.Unmarshal(content, &appSnapshot)
if err != nil {
log.Debugf("Problem parsing application snapshot from file %s", filePath)
return nil, err
}

log.Debugf("Read application snapshot from file %s", filePath)
return &appSnapshot, nil
}

// read ApplicationSnapshot provided as a string
if len(input) > 0 {
// Unmarshall json into struct, exit on failure
if err := json.Unmarshal([]byte(input), &appSnapshot); err != nil {
log.Debugf("Problem parsing application snapshot from input param %s", input)
return nil, err
}

log.Debug("Read application snapshot from input param")
return &appSnapshot, nil
}

// create ApplicationSnapshot with a single image
if len(imageRef) > 0 {
log.Debugf("Generating application snapshot from imageRef %s", imageRef)
return &appstudioshared.ApplicationSnapshotSpec{
Components: []appstudioshared.ApplicationSnapshotComponent{
{
Name: "Unnamed",
ContainerImage: imageRef,
},
},
}, nil
}

log.Debug("No application snapshot available")
return nil, errors.New("neither ApplicationSnapshot nor image reference provided to validate")
}
Loading