0
Fork 0
mirror of https://github.com/project-zot/zot.git synced 2024-12-16 21:56:37 -05:00

cli: add dummy cli for CVE search commands (WIP)

It supports commands for searching CVEs with various params.
run "zot search -h" and "zot search cve -h" for commands
Does not make any API calls yet.
This commit is contained in:
Tanmay Naik 2020-05-28 17:02:34 -04:00
parent 36efa17915
commit 90a943790c
6 changed files with 227 additions and 1 deletions

3
.gitignore vendored
View file

@ -19,4 +19,5 @@ test/data/
.idea/ .idea/
coverage.html coverage.html
tags tags
vendor/ vendor/
.vscode/

View file

@ -24,4 +24,5 @@ var (
ErrCacheRootBucket = errors.New("cache: unable to create/update root bucket") ErrCacheRootBucket = errors.New("cache: unable to create/update root bucket")
ErrCacheNoBucket = errors.New("cache: unable to find bucket") ErrCacheNoBucket = errors.New("cache: unable to find bucket")
ErrCacheMiss = errors.New("cache: miss") ErrCacheMiss = errors.New("cache: miss")
ErrInvalidArgs = errors.New("cli: Invalid Arguments")
) )

79
pkg/cli/cveCmd.go Normal file
View file

@ -0,0 +1,79 @@
package cli
import (
"errors"
"fmt"
zotErrors "github.com/anuvu/zot/errors"
"github.com/spf13/cobra"
)
var searchParams = make(map[string]*string)
func init() {
searchCmd.AddCommand(cveCmd)
searchCmd.AddCommand(imageCmd)
setupFlags()
cveCmd.SetUsageTemplate(cveCmd.UsageTemplate() + allowedCombinations)
}
var cveCmd = &cobra.Command{
Use: "cve",
Short: "Find CVEs",
Long: `Find CVEs (Common Vulnerabilities and Exposures)`,
Run: func(cmd *cobra.Command, args []string) {
if cmd.Flags().NFlag() == 0 {
cmd.Usage()
panic(zotErrors.ErrInvalidArgs) //TODO to panic or not to panic
}
err := searchCve(searchParams)
if err != nil {
cmd.PrintErrln(err.Error())
cmd.PrintErrln()
cmd.Usage()
}
},
}
func setupFlags() {
searchParams["imageName"] = cveCmd.Flags().StringP("image-name", "I", "", "Specify image name")
searchParams["tag"] = cveCmd.Flags().StringP("tag", "t", "", "Specify tag")
searchParams["packageName"] = cveCmd.Flags().StringP("package-name", "p", "", "Specify package name")
searchParams["packageVersion"] = cveCmd.Flags().StringP("package-version", "V", "", "Specify package version")
searchParams["packageVendor"] = cveCmd.Flags().StringP("package-vendor", "d", "", "Specify package vendor")
searchParams["cveID"] = cveCmd.Flags().StringP("cve-id", "i", "", "Find by CVE-ID")
searchParams["cveIDForImage"] = imageCmd.Flags().StringP("cve-id", "c", "", "Find by CVE-ID")
}
func searchCve(params map[string]*string) error {
foundResults := false
for _, searcher := range getSearchers() {
results, err := searcher.search(params)
if err == nil {
foundResults = true
fmt.Println(results)
break
}
}
if !foundResults {
return errors.New("Invalid combination of arguments") // TODO error handling and printing updated help/usage
}
return nil
}
var imageCmd = &cobra.Command{
Use: "image",
Short: "Find images",
Long: `Find images`,
Run: func(cmd *cobra.Command, args []string) {
if cmd.Flags().NFlag() == 0 {
cmd.Usage()
panic(zotErrors.ErrInvalidArgs)
}
searchCve(searchParams)
},
}

129
pkg/cli/cveSearchers.go Normal file
View file

@ -0,0 +1,129 @@
package cli
import (
"errors"
"fmt"
)
func getSearchers() []searcher {
searchers := []searcher{
new(searchCveByID),
new(searchByImageName),
new(searchByImageNameAndTag),
new(searchByPackageNameAndVersion),
new(searchByPackageName),
new(searchByPackageVendor),
}
return searchers
}
var allowedCombinations = `
Only these combinations of flags(or their shorthands) are allowed:
--cve-id
--image-name
--image-name --tag
--package-vendor
--package-name
--package-name --package-version
--package-version
`
type searcher interface {
search(params map[string]*string) (string, error)
}
func canSearch(params map[string]*string, requiredParams *set) bool {
for key, value := range params {
if requiredParams.contains(key) && *value == "" {
return false
} else if !requiredParams.contains(key) && *value != "" {
return false
}
}
return true
}
type searchCveByID struct{}
func (search searchCveByID) search(params map[string]*string) (string, error) {
if !canSearch(params, newSet("cveID")) {
return "", errors.New("searchByCveId: cannot search with given params. Only CVE ID is required")
}
return fmt.Sprintf("Searching with CVE ID: %s", *params["cveID"]), nil
}
type searchImageByCveID struct{}
func (search searchImageByCveID) search(params map[string]*string) (string, error) {
if !canSearch(params, newSet("cveIDForImage")) {
return "", errors.New("searchImageByCveID: cannot search image with given params. Only CVE ID is required")
}
return fmt.Sprintf("Searching image with CVE ID: %s", *params["cveID"]), nil
}
type searchByImageNameAndTag struct{}
func (search searchByImageNameAndTag) search(params map[string]*string) (string, error) {
if !canSearch(params, newSet("imageName", "tag")) {
return "", errors.New("searchByImageNameAndTag: cannot search with given params. Only image name and tag are required")
}
return fmt.Sprintf("Searching with image name and tag: %s and %s", *params["imageName"], *params["tag"]), nil
}
type searchByImageName struct{}
func (search searchByImageName) search(params map[string]*string) (string, error) {
if !canSearch(params, newSet("imageName")) {
return "", errors.New("searchByImageName: cannot search with given params. Only image name is required")
}
return fmt.Sprintf("Searching with image name: %s", *params["imageName"]), nil
}
type searchByPackageNameAndVersion struct{}
func (search searchByPackageNameAndVersion) search(params map[string]*string) (string, error) {
if !canSearch(params, newSet("packageName", "packageVersion")) {
return "", errors.New("searchByPackageNameAndVersion: cannot search with given params. Only package name and version are required")
}
return fmt.Sprintf("Searching with package name: %s and version %s", *params["packageName"], *params["packageVersion"]), nil
}
type searchByPackageName struct{}
func (search searchByPackageName) search(params map[string]*string) (string, error) {
if !canSearch(params, newSet("packageName")) {
return "", errors.New("searchByPackageName: cannot search with given params. Only package name is required")
}
return fmt.Sprintf("Searching with package name: %s", *params["packageName"]), nil
}
type searchByPackageVendor struct{}
func (search searchByPackageVendor) search(params map[string]*string) (string, error) {
if !canSearch(params, newSet("packageVendor")) {
return "", errors.New("searchByPackageVendor: cannot search with given params. Only package vendor is required")
}
return fmt.Sprintf("Searching with package vendor: %s", *params["packageVendor"]), nil
}
var exists = struct{}{}
type set struct {
m map[string]struct{}
}
func newSet(initialValues ...string) *set {
s := &set{}
s.m = make(map[string]struct{})
for _, val := range initialValues {
s.m[val] = exists
}
return s
}
func (s *set) contains(value string) bool {
_, c := s.m[value]
return c
}

View file

@ -96,6 +96,7 @@ func NewRootCmd() *cobra.Command {
rootCmd.AddCommand(serveCmd) rootCmd.AddCommand(serveCmd)
rootCmd.AddCommand(gcCmd) rootCmd.AddCommand(gcCmd)
initSearchCommand(rootCmd)
rootCmd.Flags().BoolVarP(&showVersion, "version", "v", false, "show the version and exit") rootCmd.Flags().BoolVarP(&showVersion, "version", "v", false, "show the version and exit")
return rootCmd return rootCmd

15
pkg/cli/searchCmd.go Normal file
View file

@ -0,0 +1,15 @@
package cli
import (
"github.com/spf13/cobra"
)
func initSearchCommand(rootCmd *cobra.Command) {
rootCmd.AddCommand(searchCmd)
}
var searchCmd = &cobra.Command{
Use: "search",
Short: "Search in zot",
Long: `Search in zot`,
}