0
Fork 0
mirror of https://github.com/project-zot/zot.git synced 2025-01-06 22:40:28 -05:00
zot/pkg/cli/client/cves_sub_cmd.go
LaurentiuNiculae 5039128723
feat(cve): cli cve diff (#2242)
* feat(gql): add new query for diff of cves for 2 images

Signed-off-by: Laurentiu Niculae <niculae.laurentiu1@gmail.com>

* feat(cli): add cli for cve diff

Signed-off-by: Laurentiu Niculae <niculae.laurentiu1@gmail.com>

---------

Signed-off-by: Laurentiu Niculae <niculae.laurentiu1@gmail.com>
2024-03-06 10:40:29 +02:00

278 lines
7 KiB
Go

//go:build search
// +build search
package client
import (
"fmt"
"strings"
"github.com/spf13/cobra"
zerr "zotregistry.dev/zot/errors"
zcommon "zotregistry.dev/zot/pkg/common"
)
const (
maxRetries = 20
)
func NewCveForImageCommand(searchService SearchService) *cobra.Command {
var (
searchedCVEID string
cveListSortFlag = CVEListSortFlag(SortBySeverity)
)
cveForImageCmd := &cobra.Command{
Use: "list [repo:tag]|[repo@digest]",
Short: "List CVEs by REPO:TAG or REPO@DIGEST",
Long: `List CVEs by REPO:TAG or REPO@DIGEST`,
Args: OneImageWithRefArg,
RunE: func(cmd *cobra.Command, args []string) error {
searchConfig, err := GetSearchConfigFromFlags(cmd, searchService)
if err != nil {
return err
}
err = CheckExtEndPointQuery(searchConfig, CVEListForImageQuery())
if err != nil {
return fmt.Errorf("%w: '%s'", err, CVEListForImageQuery().Name)
}
image := args[0]
return SearchCVEForImageGQL(searchConfig, image, searchedCVEID)
},
}
cveForImageCmd.Flags().StringVar(&searchedCVEID, SearchedCVEID, "", "Search for a specific CVE by name/id")
cveForImageCmd.Flags().Var(&cveListSortFlag, SortByFlag,
fmt.Sprintf("Options for sorting the output: [%s]", CVEListSortOptionsStr()))
return cveForImageCmd
}
func NewImagesByCVEIDCommand(searchService SearchService) *cobra.Command {
var (
repo string
imageListSortFlag = ImageListSortFlag(SortByAlphabeticAsc)
)
imagesByCVEIDCmd := &cobra.Command{
Use: "affected [cveId]",
Short: "List images affected by a CVE",
Long: `List images affected by a CVE`,
Args: func(cmd *cobra.Command, args []string) error {
if err := cobra.ExactArgs(1)(cmd, args); err != nil {
return err
}
if !strings.HasPrefix(args[0], "CVE") {
return fmt.Errorf("%w: expected a cveid 'CVE-...' got '%s'", zerr.ErrInvalidCLIParameter, args[0])
}
return nil
},
RunE: func(cmd *cobra.Command, args []string) error {
searchConfig, err := GetSearchConfigFromFlags(cmd, searchService)
if err != nil {
return err
}
err = CheckExtEndPointQuery(searchConfig, ImageListForCVEQuery())
if err != nil {
return fmt.Errorf("%w: '%s'", err, ImageListForCVEQuery().Name)
}
searchedCVEID := args[0]
return SearchImagesByCVEIDGQL(searchConfig, repo, searchedCVEID)
},
}
imagesByCVEIDCmd.Flags().StringVar(&repo, "repo", "", "Search for a specific CVE by name/id")
imagesByCVEIDCmd.Flags().Var(&imageListSortFlag, SortByFlag,
fmt.Sprintf("Options for sorting the output: [%s]", ImageListSortOptionsStr()))
return imagesByCVEIDCmd
}
func NewFixedTagsCommand(searchService SearchService) *cobra.Command {
imageListSortFlag := ImageListSortFlag(SortByAlphabeticAsc)
fixedTagsCmd := &cobra.Command{
Use: "fixed [repo] [cveId]",
Short: "List tags where a CVE is fixed",
Long: `List tags where a CVE is fixed`,
Args: func(cmd *cobra.Command, args []string) error {
const argCount = 2
if err := cobra.ExactArgs(argCount)(cmd, args); err != nil {
return err
}
if !zcommon.CheckIsCorrectRepoNameFormat(args[0]) {
return fmt.Errorf("%w: expected a valid repo name for first argument '%s'", zerr.ErrInvalidCLIParameter, args[0])
}
return nil
},
RunE: func(cmd *cobra.Command, args []string) error {
searchConfig, err := GetSearchConfigFromFlags(cmd, searchService)
if err != nil {
return err
}
err = CheckExtEndPointQuery(searchConfig, ImageListWithCVEFixedQuery())
if err != nil {
return fmt.Errorf("%w: '%s'", err, ImageListWithCVEFixedQuery().Name)
}
repo := args[0]
searchedCVEID := args[1]
return SearchFixedTagsGQL(searchConfig, repo, searchedCVEID)
},
}
fixedTagsCmd.Flags().Var(&imageListSortFlag, SortByFlag,
fmt.Sprintf("Options for sorting the output: [%s]", ImageListSortOptionsStr()))
return fixedTagsCmd
}
func NewCVEDiffCommand(searchService SearchService) *cobra.Command {
var (
minuendStr, minuendArch string
subtrahendStr, subtrahendArch string
)
imagesByCVEIDCmd := &cobra.Command{
Use: "diff [minuend] ([minuend-platform]) [subtrahend] ([subtrahend-platform])",
Short: "List the CVE's present in minuend that are not present in subtrahend",
Long: `List the CVE's present in minuend that are not present in subtrahend`,
Args: func(cmd *cobra.Command, args []string) error {
const (
twoArgs = 2
threeArgs = 3
fourArgs = 4
)
if err := cobra.RangeArgs(twoArgs, fourArgs)(cmd, args); err != nil {
return err
}
if !isRepoTag(args[0]) {
return fmt.Errorf("%w: first parameter should be a repo:tag", zerr.ErrInvalidArgs)
}
minuendStr = args[0]
if isRepoTag(args[1]) {
subtrahendStr = args[1]
} else {
minuendArch = args[1]
if len(args) == twoArgs {
return fmt.Errorf("%w: not enough arguments, specified only 1 image with arch", zerr.ErrInvalidArgs)
}
}
if len(args) == twoArgs {
return nil
}
if isRepoTag(args[2]) {
if subtrahendStr == "" {
subtrahendStr = args[2]
} else {
return fmt.Errorf("%w: too many repo:tag inputs", zerr.ErrInvalidArgs)
}
} else {
if subtrahendStr == "" {
return fmt.Errorf("%w: 3rd argument should be a repo:tag", zerr.ErrInvalidArgs)
} else {
subtrahendArch = args[2]
}
}
if len(args) == threeArgs {
return nil
}
if isRepoTag(args[3]) {
return fmt.Errorf("%w: 4th argument should not be a repo:tag but an arch", zerr.ErrInvalidArgs)
} else {
subtrahendArch = args[3]
}
return nil
},
RunE: func(cmd *cobra.Command, args []string) error {
searchConfig, err := GetSearchConfigFromFlags(cmd, searchService)
if err != nil {
return err
}
err = CheckExtEndPointQuery(searchConfig, CVEDiffListForImagesQuery())
if err != nil {
return fmt.Errorf("%w: '%s'", err, CVEDiffListForImagesQuery().Name)
}
// parse the args and determine the input
minuend := getImageIdentifier(minuendStr, minuendArch)
subtrahend := getImageIdentifier(subtrahendStr, subtrahendArch)
return SearchCVEDiffList(searchConfig, minuend, subtrahend)
},
}
return imagesByCVEIDCmd
}
func isRepoTag(arg string) bool {
_, _, _, err := zcommon.GetRepoReference(arg) //nolint:dogsled
return err == nil
}
type osArch struct {
Os string
Arch string
}
type ImageIdentifier struct {
Repo string `json:"repo"`
Tag string `json:"tag"`
Digest string `json:"digest"`
Platform *osArch `json:"platform"`
}
func getImageIdentifier(repoTagStr, platformStr string) ImageIdentifier {
var tag, digest string
repo, ref, isTag, err := zcommon.GetRepoReference(repoTagStr)
if err != nil {
return ImageIdentifier{}
}
if isTag {
tag = ref
} else {
digest = ref
}
// check if the following input is a repo:tag or repo@digest, if not then it's a platform
var platform *osArch
if platformStr != "" {
os, arch, _ := strings.Cut(platformStr, "/")
platform = &osArch{Os: os, Arch: arch}
}
return ImageIdentifier{
Repo: repo,
Tag: tag,
Digest: digest,
Platform: platform,
}
}