Skip to content

Commit

Permalink
Merge branch 'release/1.0.19'
Browse files Browse the repository at this point in the history
  • Loading branch information
yeyisan committed Feb 22, 2022
2 parents e6a7012 + ec43fcf commit b4cfa8b
Show file tree
Hide file tree
Showing 3 changed files with 241 additions and 9 deletions.
120 changes: 120 additions & 0 deletions client/sbom.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
package client

import (
"bytes"
"errors"
"fmt"
"io"
"mime/multipart"
"net/http"
"net/url"
"os"
"path/filepath"

"github.com/kondukto-io/kdt/klog"
"github.com/spf13/viper"
)

func (c *Client) ImportSBOM(file string, repo string, form ImportForm) error {
klog.Debugf("importing sbom content using file:%s", file)

projectId := form["project"]

if projectId != "" {
projects, err := c.ListProjects(projectId, repo)
if err != nil {
return err
}

if len(projects) == 0 {
return errors.New("no projects found for given parameters")
}

if len(projects) == 1 {
projectId = projects[0].ID
form["project"] = projects[0].Name
}

if len(projects) > 1 {
return errors.New("multiple projects found for given parameters")
}
} else {
projects, err := c.ListProjects(projectId, repo)
if err != nil {
return err
}

if len(projects) == 0 {
return errors.New("no projects found for given parameters")
}

if len(projects) == 1 {
projectId = projects[0].ID
form["project"] = projects[0].Name
}

if len(projects) > 1 {
return errors.New("multiple projects found for given parameters")
}
}

path := fmt.Sprintf("/api/v2/%s/sbom/upload", projectId)
rel := &url.URL{Path: path}
u := c.BaseURL.ResolveReference(rel)

body := &bytes.Buffer{}
writer := multipart.NewWriter(body)

if _, err := os.Stat(file); os.IsNotExist(err) {
return err
}
f, err := os.Open(file)
if err != nil {
return err
}
defer func() { _ = f.Close() }()
part, err := writer.CreateFormFile("file", filepath.Base(f.Name()))
if err != nil {
return err
}
_, err = io.Copy(part, f)
if err != nil {
return err
}

for k := range form {
if err = writer.WriteField(k, form[k]); err != nil {
return fmt.Errorf("failed to write form field [%s]: %w", k, err)
}
}

_ = writer.Close()

req, err := http.NewRequest(http.MethodPost, u.String(), body)
if err != nil {
return err
}

req.Header.Add("Content-Type", writer.FormDataContentType())

req.Header.Set("Accept", "application/json")
req.Header.Set("User-Agent", userAgent)
req.Header.Set("X-Cookie", viper.GetString("token"))

type importSBOMResponse struct {
Message string `json:"message"`
Error string `json:"error"`
}

var importResponse importSBOMResponse
resp, err := c.do(req, &importResponse)
if err != nil {
return err
}

if resp.StatusCode != http.StatusOK {
return fmt.Errorf("failed to import sbom: %s, status code: %s", importResponse.Error, resp.StatusCode)
}

return nil
}
115 changes: 115 additions & 0 deletions cmd/sbom.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
package cmd

import (
"errors"
"fmt"

"github.com/kondukto-io/kdt/client"
"github.com/kondukto-io/kdt/klog"
"github.com/spf13/cobra"
)

// sbomCmd represents the sbom root command
var sbomCmd = &cobra.Command{
Use: "sbom",
Short: "base command for SBOM(Software bill of materials) imports",
Run: func(cmd *cobra.Command, args []string) {
if len(args) == 0 {
_ = cmd.Help()
qwm(ExitCodeSuccess, "")
}
},
}

func init() {
rootCmd.AddCommand(sbomCmd)

sbomCmd.AddCommand(importSbomCmd)

importSbomCmd.Flags().StringP("file", "f", "", "SBOM file to be imported. Currently only .json format is supported")
importSbomCmd.Flags().StringP("project", "p", "", "Kondukto project id or name")
importSbomCmd.Flags().StringP("repo-id", "r", "", "URL or ID of ALM repository")
importSbomCmd.Flags().StringP("sbom-type", "s", "", "Custom type(optional). Passing a different value than existing type(i.e application, container etc.) is advised")
importSbomCmd.Flags().StringP("branch", "b", "", "Branch name for the project receiving the sbom")
}

// importSbomCmd represents the sbom import command
var importSbomCmd = &cobra.Command{
Use: "import",
Short: "imports sbom file to Kondukto",
Run: importSbomRootCommand,
}

func importSbomRootCommand(cmd *cobra.Command, _ []string) {
// Initialize Kondukto client
c, err := client.New()
if err != nil {
qwe(ExitCodeError, err, "could not initialize Kondukto client")
}
sbomImport := SBOMImport{
cmd: cmd,
client: c,
}
if err = sbomImport.sbomImport(); err != nil {
qwe(ExitCodeError, err, "failed to import sbom file")
}
}

type SBOMImport struct {
cmd *cobra.Command
client *client.Client
}

func (s *SBOMImport) sbomImport() error {
// Parse command line flags needed for file uploads
file, err := s.cmd.Flags().GetString("file")
if err != nil {
return fmt.Errorf("failed to parse file flag: %w", err)
}

if !s.cmd.Flags().Changed("repo-id") && !s.cmd.Flags().Changed("project") {
return errors.New("missing a required flag(repo or project) to get project detail")
}

projectName, err := s.cmd.Flags().GetString("project")
if err != nil {
return fmt.Errorf("failed to get project flag: %w", err)
}

branch, err := s.cmd.Flags().GetString("branch")
if err != nil {
return fmt.Errorf("failed to parse branch flag: %w", err)
}

repo, err := s.cmd.Flags().GetString("repo-id")
if err != nil {
return fmt.Errorf("failed to parse repo-id flag: %w", err)
}

sbomType, err := s.cmd.Flags().GetString("sbom-type")
if err != nil {
return fmt.Errorf("failed to parse sbom-type flag: %w", err)
}

var form = client.ImportForm{
"project": projectName,
"branch": branch,
"sbom_type": sbomType,
}

err = s.client.ImportSBOM(file, repo, form)
if err != nil {
return fmt.Errorf("failed to import scan results: %w", err)
}

importInfo := ""
if projectName == "" {
importInfo = fmt.Sprintf("%s(ALM)", repo)
} else {
importInfo = fmt.Sprintf("%s(kondukto project)", projectName)
}

klog.Printf("sbom file imported successfully for: [%s]", importInfo)

return nil
}
15 changes: 6 additions & 9 deletions cmd/scan.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ func init() {
scanCmd.Flags().StringP("file", "f", "", "scan result file")
scanCmd.Flags().StringP("branch", "b", "", "branch")
scanCmd.Flags().StringP("merge-target", "M", "", "source branch name for pull-request")
scanCmd.Flags().StringP("github-pr-number", "", "", "github pull-request number")
scanCmd.Flags().StringP("pr-number", "", "", "pull-request number. supported alms[github, gitlab, azure, bitbucket]")
scanCmd.Flags().Bool("no-decoration", false, "no decoration for pr number")
scanCmd.Flags().StringP("image", "I", "", "image to scan with container security products")
scanCmd.Flags().StringP("agent", "a", "", "agent name for agent type scanners")
Expand Down Expand Up @@ -629,12 +629,9 @@ func (s *Scan) startScanByProjectToolAndPRNumber() (string, error) {
if err != nil {
return "", fmt.Errorf("failed to parse no-decoration flag: %w", err)
}
prNumber, err := s.cmd.Flags().GetString("github-pr-number")
prNumber, err := s.cmd.Flags().GetString("pr-number")
if err != nil {
return "", fmt.Errorf("failed to parse pr-number flag: %w", err)
}
if prNumber == "" {
return "", errors.New("missing pr-number fields")
return "", fmt.Errorf("failed to get request number: %w", err)
}
override, err := s.cmd.Flags().GetBool("override")
if err != nil {
Expand Down Expand Up @@ -970,7 +967,7 @@ func getScanMode(cmd *cobra.Command) uint {
byBranch := cmd.Flag("merge-target").Changed
byForkScan := cmd.Flag("fork-scan").Changed
byMerge := cmd.Flag("branch").Changed
byGithubPRNumber := cmd.Flag("github-pr-number").Changed
byPRNumber := cmd.Flag("pr-number").Changed
byImage := cmd.Flag("image").Changed
byRepo := cmd.Flag("repo-id").Changed
byProjectORRepo := byProject || byRepo
Expand All @@ -979,8 +976,8 @@ func getScanMode(cmd *cobra.Command) uint {
byProjectAndTool := byProjectORRepo && byTool && !byPR //
byProjectAndToolAndFile := byProjectAndTool && byImportFile
byProjectAndToolAndForkScan := byProjectORRepo && byTool && byForkScan && !byPR
byProjectAndToolAndPullRequest := byProjectORRepo && byTool && byPR && !byImportFile //
byProjectAndToolAndPullRequestNumber := byProjectORRepo && byTool && byGithubPRNumber && !byImportFile //
byProjectAndToolAndPullRequestNumber := byProjectORRepo && byTool && byPRNumber && !byImportFile
byProjectAndToolAndPullRequest := byProjectORRepo && byTool && byPR && !byImportFile //

mode := func() uint {
// sorted by priority
Expand Down

0 comments on commit b4cfa8b

Please sign in to comment.