Support image-vulnerabilities related controls

This commit is contained in:
David Wertenteil
2022-02-03 12:48:41 +02:00
committed by GitHub
60 changed files with 2672 additions and 177 deletions
+17
View File
@@ -0,0 +1,17 @@
name: create release digests
on:
release:
types: [ published]
branches: [ master ]
jobs:
once:
name: Creating digests
runs-on: ubuntu-latest
steps:
- name: Digest
uses: MCJack123/ghaction-generate-release-hashes@v1
with:
hash-type: sha1
file-name: kubescape-release-digests
+1 -1
View File
@@ -37,7 +37,7 @@ def main():
print("Building Kubescape")
# print environment variables
print(os.environ)
# print(os.environ)
# Set some variables
packageName = getPackageName()
+109 -58
View File
@@ -23,25 +23,31 @@ func ConfigFileFullPath() string { return getter.GetDefaultPath(configFileName +
// ======================================================================================
type ConfigObj struct {
CustomerGUID string `json:"customerGUID"`
Token string `json:"invitationParam"`
CustomerAdminEMail string `json:"adminMail"`
ClusterName string `json:"clusterName"`
}
func (co *ConfigObj) Json() []byte {
if b, err := json.Marshal(co); err == nil {
return b
}
return []byte{}
AccountID string `json:"accountID,omitempty"`
ClientID string `json:"clientID,omitempty"`
AccessKey string `json:"accessKey,omitempty"`
CustomerGUID string `json:"customerGUID,omitempty"` // Deprecated
Token string `json:"invitationParam,omitempty"`
CustomerAdminEMail string `json:"adminMail,omitempty"`
ClusterName string `json:"clusterName,omitempty"`
}
// Config - convert ConfigObj to config file
func (co *ConfigObj) Config() []byte {
// remove cluster name before saving to file
clusterName := co.ClusterName
co.ClusterName = "" // remove cluster name before saving to file
b, err := json.Marshal(co)
customerAdminEMail := co.CustomerAdminEMail
token := co.Token
co.ClusterName = ""
co.Token = ""
co.CustomerAdminEMail = ""
b, err := json.MarshalIndent(co, "", " ")
co.ClusterName = clusterName
co.CustomerAdminEMail = customerAdminEMail
co.Token = token
if err == nil {
return b
@@ -56,10 +62,12 @@ func (co *ConfigObj) Config() []byte {
type ITenantConfig interface {
// set
SetTenant() error
UpdateCachedConfig() error
DeleteCachedConfig() error
// getters
GetClusterName() string
GetCustomerGUID() string
GetAccountID() string
GetConfigObj() *ConfigObj
// GetBackendAPI() getter.IBackend
// GenerateURL()
@@ -93,12 +101,18 @@ func NewLocalConfig(backendAPI getter.IBackend, customerGUID, clusterName string
lc.configObj = configObj
}
if customerGUID != "" {
lc.configObj.CustomerGUID = customerGUID // override config customerGUID
lc.configObj.AccountID = customerGUID // override config customerGUID
}
if clusterName != "" {
lc.configObj.ClusterName = AdoptClusterName(clusterName) // override config clusterName
}
if lc.configObj.CustomerGUID != "" {
getAccountFromEnv(lc.configObj)
lc.backendAPI.SetAccountID(lc.configObj.AccountID)
lc.backendAPI.SetClientID(lc.configObj.ClientID)
lc.backendAPI.SetAccessKey(lc.configObj.AccessKey)
if lc.configObj.AccountID != "" {
if err := lc.SetTenant(); err != nil {
fmt.Println(err)
}
@@ -107,32 +121,38 @@ func NewLocalConfig(backendAPI getter.IBackend, customerGUID, clusterName string
return lc
}
func (lc *LocalConfig) GetConfigObj() *ConfigObj { return lc.configObj }
func (lc *LocalConfig) GetCustomerGUID() string { return lc.configObj.CustomerGUID }
func (lc *LocalConfig) SetCustomerGUID(customerGUID string) { lc.configObj.CustomerGUID = customerGUID }
func (lc *LocalConfig) GetClusterName() string { return lc.configObj.ClusterName }
func (lc *LocalConfig) IsConfigFound() bool { return existsConfigFile() }
func (lc *LocalConfig) GetConfigObj() *ConfigObj { return lc.configObj }
func (lc *LocalConfig) GetAccountID() string { return lc.configObj.AccountID }
func (lc *LocalConfig) GetClusterName() string { return lc.configObj.ClusterName }
func (lc *LocalConfig) IsConfigFound() bool { return existsConfigFile() }
func (lc *LocalConfig) SetTenant() error {
// ARMO tenant GUID
if err := getTenantConfigFromBE(lc.backendAPI, lc.configObj); err != nil {
return err
}
updateConfigFile(lc.configObj)
lc.UpdateCachedConfig()
return nil
}
func (lc *LocalConfig) UpdateCachedConfig() error {
return updateConfigFile(lc.configObj)
}
func (lc *LocalConfig) DeleteCachedConfig() error {
return DeleteConfigFile()
}
func getTenantConfigFromBE(backendAPI getter.IBackend, configObj *ConfigObj) error {
// get from armoBE
backendAPI.SetCustomerGUID(configObj.CustomerGUID)
tenantResponse, err := backendAPI.GetCustomerGUID()
tenantResponse, err := backendAPI.GetTenant()
if err == nil && tenantResponse != nil {
if tenantResponse.AdminMail != "" { // registered tenant
configObj.CustomerAdminEMail = tenantResponse.AdminMail
} else { // new tenant
configObj.Token = tenantResponse.Token
configObj.CustomerGUID = tenantResponse.TenantID
configObj.AccountID = tenantResponse.TenantID
}
} else {
if err != nil && !strings.Contains(err.Error(), "already exists") {
@@ -154,8 +174,11 @@ Supported environments variables:
KS_DEFAULT_CONFIGMAP_NAME // name of configmap, if not set default is 'kubescape'
KS_DEFAULT_CONFIGMAP_NAMESPACE // configmap namespace, if not set default is 'default'
KS_ACCOUNT_ID
KS_CLIENT_ID
KS_ACCESS_KEY
TODO - supprot:
KS_ACCOUNT // Account ID
KS_CACHE // path to cached files
*/
type ClusterConfig struct {
@@ -187,32 +210,36 @@ func NewClusterConfig(k8s *k8sinterface.KubernetesApi, backendAPI getter.IBacken
c.configObj = configObj
}
if customerGUID != "" {
c.configObj.CustomerGUID = customerGUID // override config customerGUID
c.configObj.AccountID = customerGUID // override config customerGUID
}
if clusterName != "" {
c.configObj.ClusterName = AdoptClusterName(clusterName) // override config clusterName
}
if c.configObj.CustomerGUID != "" {
if err := c.SetTenant(); err != nil {
fmt.Println(err)
}
}
getAccountFromEnv(c.configObj)
if c.configObj.ClusterName == "" {
c.configObj.ClusterName = AdoptClusterName(k8sinterface.GetClusterName())
} else { // override the cluster name if it has unwanted characters
c.configObj.ClusterName = AdoptClusterName(c.configObj.ClusterName)
}
c.backendAPI.SetAccountID(c.configObj.AccountID)
c.backendAPI.SetClientID(c.configObj.ClientID)
c.backendAPI.SetAccessKey(c.configObj.AccessKey)
if c.configObj.AccountID != "" {
if err := c.SetTenant(); err != nil {
fmt.Println(err) // TODO: print to log
}
}
return c
}
func (c *ClusterConfig) GetConfigObj() *ConfigObj { return c.configObj }
func (c *ClusterConfig) GetDefaultNS() string { return c.configMapNamespace }
func (c *ClusterConfig) GetCustomerGUID() string { return c.configObj.CustomerGUID }
func (c *ClusterConfig) SetCustomerGUID(customerGUID string) { c.configObj.CustomerGUID = customerGUID }
func (c *ClusterConfig) IsConfigFound() bool {
return existsConfigFile() || c.existsConfigMap()
}
func (c *ClusterConfig) GetConfigObj() *ConfigObj { return c.configObj }
func (c *ClusterConfig) GetDefaultNS() string { return c.configMapNamespace }
func (c *ClusterConfig) GetAccountID() string { return c.configObj.AccountID }
func (c *ClusterConfig) IsConfigFound() bool { return existsConfigFile() || c.existsConfigMap() }
func (c *ClusterConfig) SetTenant() error {
@@ -220,17 +247,34 @@ func (c *ClusterConfig) SetTenant() error {
if err := getTenantConfigFromBE(c.backendAPI, c.configObj); err != nil {
return err
}
// update/create config
if c.existsConfigMap() {
c.updateConfigMap()
} else {
c.createConfigMap()
}
updateConfigFile(c.configObj)
c.UpdateCachedConfig()
return nil
}
func (c *ClusterConfig) UpdateCachedConfig() error {
// update/create config
if c.existsConfigMap() {
if err := c.updateConfigMap(); err != nil {
return err
}
} else {
if err := c.createConfigMap(); err != nil {
return err
}
}
return updateConfigFile(c.configObj)
}
func (c *ClusterConfig) DeleteCachedConfig() error {
if err := c.deleteConfigMap(); err != nil {
return err
}
if err := DeleteConfigFile(); err != nil {
return err
}
return nil
}
func (c *ClusterConfig) GetClusterName() string {
return c.configObj.ClusterName
}
@@ -409,6 +453,10 @@ func readConfig(dat []byte) (*ConfigObj, error) {
if err := json.Unmarshal(dat, configObj); err != nil {
return nil, err
}
if configObj.AccountID == "" {
configObj.AccountID = configObj.CustomerGUID
}
configObj.CustomerGUID = ""
return configObj, nil
}
@@ -421,8 +469,7 @@ func (clusterConfig *ClusterConfig) IsSubmitted() bool {
func (clusterConfig *ClusterConfig) IsRegistered() bool {
// get from armoBE
clusterConfig.backendAPI.SetCustomerGUID(clusterConfig.GetCustomerGUID())
tenantResponse, err := clusterConfig.backendAPI.GetCustomerGUID()
tenantResponse, err := clusterConfig.backendAPI.GetTenant()
if err == nil && tenantResponse != nil {
if tenantResponse.AdminMail != "" { // this customer already belongs to some user
return true
@@ -431,16 +478,7 @@ func (clusterConfig *ClusterConfig) IsRegistered() bool {
return false
}
func (clusterConfig *ClusterConfig) DeleteConfig() error {
if err := clusterConfig.DeleteConfigMap(); err != nil {
return err
}
if err := DeleteConfigFile(); err != nil {
return err
}
return nil
}
func (clusterConfig *ClusterConfig) DeleteConfigMap() error {
func (clusterConfig *ClusterConfig) deleteConfigMap() error {
return clusterConfig.k8s.KubernetesClient.CoreV1().ConfigMaps(clusterConfig.configMapNamespace).Delete(context.Background(), clusterConfig.configMapName, metav1.DeleteOptions{})
}
@@ -465,3 +503,16 @@ func getConfigMapNamespace() string {
}
return "default"
}
func getAccountFromEnv(configObj *ConfigObj) {
// load from env
if accountID := os.Getenv("KS_ACCOUNT_ID"); accountID != "" {
configObj.AccountID = accountID
}
if clientID := os.Getenv("KS_CLIENT_ID"); clientID != "" {
configObj.ClientID = clientID
}
if accessKey := os.Getenv("KS_ACCESS_KEY"); accessKey != "" {
configObj.AccessKey = accessKey
}
}
+27 -25
View File
@@ -30,24 +30,26 @@ var (
// Armo API for downloading policies
type ArmoAPI struct {
httpClient *http.Client
apiURL string
erURL string
feURL string
customerGUID string
httpClient *http.Client
apiURL string
erURL string
feURL string
accountID string
clientID string
accessKey string
}
var globalArmoAPIConnecctor *ArmoAPI
var globalArmoAPIConnector *ArmoAPI
func SetARMOAPIConnector(armoAPI *ArmoAPI) {
globalArmoAPIConnecctor = armoAPI
globalArmoAPIConnector = armoAPI
}
func GetArmoAPIConnector() *ArmoAPI {
if globalArmoAPIConnecctor == nil {
if globalArmoAPIConnector == nil {
glog.Error("returning nil API connector")
}
return globalArmoAPIConnecctor
return globalArmoAPIConnector
}
func NewARMOAPIDev() *ArmoAPI {
@@ -85,17 +87,15 @@ func newArmoAPI() *ArmoAPI {
httpClient: &http.Client{Timeout: time.Duration(61) * time.Second},
}
}
func (armoAPI *ArmoAPI) SetCustomerGUID(customerGUID string) {
armoAPI.customerGUID = customerGUID
}
func (armoAPI *ArmoAPI) GetFrontendURL() string {
return armoAPI.feURL
}
func (armoAPI *ArmoAPI) GetReportReceiverURL() string {
return armoAPI.erURL
}
func (armoAPI *ArmoAPI) GetAccountID() string { return armoAPI.accountID }
func (armoAPI *ArmoAPI) GetClientID() string { return armoAPI.clientID }
func (armoAPI *ArmoAPI) GetAccessKey() string { return armoAPI.accessKey }
func (armoAPI *ArmoAPI) GetFrontendURL() string { return armoAPI.feURL }
func (armoAPI *ArmoAPI) GetReportReceiverURL() string { return armoAPI.erURL }
func (armoAPI *ArmoAPI) SetAccountID(accountID string) { armoAPI.accountID = accountID }
func (armoAPI *ArmoAPI) SetClientID(clientID string) { armoAPI.clientID = clientID }
func (armoAPI *ArmoAPI) SetAccessKey(accessKey string) { armoAPI.accessKey = accessKey }
func (armoAPI *ArmoAPI) GetFramework(name string) (*reporthandling.Framework, error) {
respStr, err := HttpGetter(armoAPI.httpClient, armoAPI.getFrameworkURL(name), nil)
@@ -146,10 +146,10 @@ func (armoAPI *ArmoAPI) GetExceptions(clusterName string) ([]armotypes.PostureEx
return exceptions, nil
}
func (armoAPI *ArmoAPI) GetCustomerGUID() (*TenantResponse, error) {
url := armoAPI.getCustomerURL()
if armoAPI.customerGUID != "" {
url = fmt.Sprintf("%s?customerGUID=%s", url, armoAPI.customerGUID)
func (armoAPI *ArmoAPI) GetTenant() (*TenantResponse, error) {
url := armoAPI.getAccountURL()
if armoAPI.accountID != "" {
url = fmt.Sprintf("%s?customerGUID=%s", url, armoAPI.accountID)
}
respStr, err := HttpGetter(armoAPI.httpClient, url, nil)
if err != nil {
@@ -159,14 +159,16 @@ func (armoAPI *ArmoAPI) GetCustomerGUID() (*TenantResponse, error) {
if err = JSONDecoder(respStr).Decode(tenant); err != nil {
return nil, err
}
if tenant.TenantID != "" {
armoAPI.accountID = tenant.TenantID
}
return tenant, nil
}
// ControlsInputs // map[<control name>][<input arguments>]
func (armoAPI *ArmoAPI) GetAccountConfig(clusterName string) (*armotypes.CustomerConfig, error) {
accountConfig := &armotypes.CustomerConfig{}
if armoAPI.customerGUID == "" {
if armoAPI.accountID == "" {
return accountConfig, nil
}
respStr, err := HttpGetter(armoAPI.httpClient, armoAPI.getAccountConfig(clusterName), nil)
+5 -5
View File
@@ -13,7 +13,7 @@ func (armoAPI *ArmoAPI) getFrameworkURL(frameworkName string) string {
u.Host = armoAPI.apiURL
u.Path = "api/v1/armoFrameworks"
q := u.Query()
q.Add("customerGUID", armoAPI.customerGUID)
q.Add("customerGUID", armoAPI.accountID)
if isNativeFramework(frameworkName) {
q.Add("frameworkName", strings.ToUpper(frameworkName))
} else {
@@ -31,7 +31,7 @@ func (armoAPI *ArmoAPI) getListFrameworkURL() string {
u.Host = armoAPI.apiURL
u.Path = "api/v1/armoFrameworks"
q := u.Query()
q.Add("customerGUID", armoAPI.customerGUID)
q.Add("customerGUID", armoAPI.accountID)
u.RawQuery = q.Encode()
return u.String()
@@ -43,7 +43,7 @@ func (armoAPI *ArmoAPI) getExceptionsURL(clusterName string) string {
u.Path = "api/v1/armoPostureExceptions"
q := u.Query()
q.Add("customerGUID", armoAPI.customerGUID)
q.Add("customerGUID", armoAPI.accountID)
// if clusterName != "" { // TODO - fix customer name support in Armo BE
// q.Add("clusterName", clusterName)
// }
@@ -59,7 +59,7 @@ func (armoAPI *ArmoAPI) getAccountConfig(clusterName string) string {
u.Path = "api/v1/armoCustomerConfiguration"
q := u.Query()
q.Add("customerGUID", armoAPI.customerGUID)
q.Add("customerGUID", armoAPI.accountID)
if clusterName != "" { // TODO - fix customer name support in Armo BE
q.Add("clusterName", clusterName)
}
@@ -68,7 +68,7 @@ func (armoAPI *ArmoAPI) getAccountConfig(clusterName string) string {
return u.String()
}
func (armoAPI *ArmoAPI) getCustomerURL() string {
func (armoAPI *ArmoAPI) getAccountURL() string {
u := url.URL{}
u.Scheme = "https"
u.Host = armoAPI.apiURL
+9 -2
View File
@@ -24,8 +24,15 @@ type IExceptionsGetter interface {
GetExceptions(clusterName string) ([]armotypes.PostureExceptionPolicy, error)
}
type IBackend interface {
GetCustomerGUID() (*TenantResponse, error)
SetCustomerGUID(customerGUID string)
GetAccountID() string
GetClientID() string
GetAccessKey() string
SetAccountID(accountID string)
SetClientID(clientID string)
SetAccessKey(accessKey string)
GetTenant() (*TenantResponse, error)
}
type IControlsInputsGetter interface {
+1 -1
View File
@@ -21,7 +21,7 @@ func GetDefaultPath(name string) string {
}
func SaveInFile(policy interface{}, pathStr string) error {
encodedData, err := json.Marshal(policy)
encodedData, err := json.MarshalIndent(policy, "", " ")
if err != nil {
return err
}
+7
View File
@@ -0,0 +1,7 @@
package clihandler
func CliDelete() error {
tenant := getTenantConfig("", "", getKubernetesApi()) // change k8sinterface
return tenant.DeleteCachedConfig()
}
+4 -4
View File
@@ -75,7 +75,7 @@ func downloadArtifacts(downloadInfo *cautils.DownloadInfo) error {
func downloadConfigInputs(downloadInfo *cautils.DownloadInfo) error {
tenant := getTenantConfig(downloadInfo.Account, "", getKubernetesApi())
controlsInputsGetter := getConfigInputsGetter(downloadInfo.Name, tenant.GetCustomerGUID(), nil)
controlsInputsGetter := getConfigInputsGetter(downloadInfo.Name, tenant.GetAccountID(), nil)
controlInputs, err := controlsInputsGetter.GetControlsInputs(tenant.GetClusterName())
if err != nil {
return err
@@ -97,7 +97,7 @@ func downloadExceptions(downloadInfo *cautils.DownloadInfo) error {
tenant := getTenantConfig(downloadInfo.Account, "", getKubernetesApi())
exceptionsGetter := getExceptionsGetter("")
exceptions := []armotypes.PostureExceptionPolicy{}
if tenant.GetCustomerGUID() != "" {
if tenant.GetAccountID() != "" {
exceptions, err = exceptionsGetter.GetExceptions(tenant.GetClusterName())
if err != nil {
return err
@@ -118,7 +118,7 @@ func downloadExceptions(downloadInfo *cautils.DownloadInfo) error {
func downloadFramework(downloadInfo *cautils.DownloadInfo) error {
tenant := getTenantConfig(downloadInfo.Account, "", getKubernetesApi())
g := getPolicyGetter(nil, tenant.GetCustomerGUID(), true, nil)
g := getPolicyGetter(nil, tenant.GetAccountID(), true, nil)
if downloadInfo.Name == "" {
// if framework name not specified - download all frameworks
@@ -154,7 +154,7 @@ func downloadFramework(downloadInfo *cautils.DownloadInfo) error {
func downloadControl(downloadInfo *cautils.DownloadInfo) error {
tenant := getTenantConfig(downloadInfo.Account, "", getKubernetesApi())
g := getPolicyGetter(nil, tenant.GetCustomerGUID(), false, nil)
g := getPolicyGetter(nil, tenant.GetAccountID(), false, nil)
if downloadInfo.Name == "" {
// TODO - support
+7 -7
View File
@@ -5,11 +5,11 @@ import (
"sort"
"strings"
"github.com/armosec/kubescape/cautils"
"github.com/armosec/kubescape/cautils/getter"
"github.com/armosec/kubescape/clihandler/cliobjects"
)
var listFunc = map[string]func(*cautils.ListPolicies) ([]string, error){
var listFunc = map[string]func(*cliobjects.ListPolicies) ([]string, error){
"controls": listControls,
"frameworks": listFrameworks,
}
@@ -21,7 +21,7 @@ func ListSupportCommands() []string {
}
return commands
}
func CliList(listPolicies *cautils.ListPolicies) error {
func CliList(listPolicies *cliobjects.ListPolicies) error {
if f, ok := listFunc[listPolicies.Target]; ok {
policies, err := f(listPolicies)
if err != nil {
@@ -40,16 +40,16 @@ func CliList(listPolicies *cautils.ListPolicies) error {
return fmt.Errorf("unknown command to download")
}
func listFrameworks(listPolicies *cautils.ListPolicies) ([]string, error) {
func listFrameworks(listPolicies *cliobjects.ListPolicies) ([]string, error) {
tenant := getTenantConfig(listPolicies.Account, "", getKubernetesApi()) // change k8sinterface
g := getPolicyGetter(nil, tenant.GetCustomerGUID(), true, nil)
g := getPolicyGetter(nil, tenant.GetAccountID(), true, nil)
return listFrameworksNames(g), nil
}
func listControls(listPolicies *cautils.ListPolicies) ([]string, error) {
func listControls(listPolicies *cliobjects.ListPolicies) ([]string, error) {
tenant := getTenantConfig(listPolicies.Account, "", getKubernetesApi()) // change k8sinterface
g := getPolicyGetter(nil, tenant.GetCustomerGUID(), false, nil)
g := getPolicyGetter(nil, tenant.GetAccountID(), false, nil)
l := getter.ListName
if listPolicies.ListIDs {
l = getter.ListID
@@ -1,4 +1,4 @@
package cautils
package cliobjects
type ListPolicies struct {
Target string
+7
View File
@@ -0,0 +1,7 @@
package cliobjects
type SetConfig struct {
Account string
ClientID string
AccessKey string
}
+5
View File
@@ -0,0 +1,5 @@
package cliobjects
type Submit struct {
Account string
}
+22
View File
@@ -0,0 +1,22 @@
package clihandler
import (
"github.com/armosec/kubescape/clihandler/cliobjects"
)
func CliSetConfig(setConfig *cliobjects.SetConfig) error {
tenant := getTenantConfig("", "", getKubernetesApi())
if setConfig.Account != "" {
tenant.GetConfigObj().AccountID = setConfig.Account
}
if setConfig.AccessKey != "" {
tenant.GetConfigObj().AccessKey = setConfig.AccessKey
}
if setConfig.ClientID != "" {
tenant.GetConfigObj().ClientID = setConfig.ClientID
}
return tenant.UpdateCachedConfig()
}
+9
View File
@@ -0,0 +1,9 @@
package clihandler
import "fmt"
func CliView() error {
tenant := getTenantConfig("", "", getKubernetesApi()) // change k8sinterface
fmt.Printf("%s\n", tenant.GetConfigObj().Config())
return nil
}
+4 -3
View File
@@ -6,9 +6,10 @@ import (
// clusterCmd represents the cluster command
var clusterCmd = &cobra.Command{
Use: "cluster",
Short: "Set configuration for cluster",
Long: ``,
Use: "cluster",
Short: "Set configuration for cluster",
Long: ``,
Deprecated: "use the 'set' command instead",
Run: func(cmd *cobra.Command, args []string) {
},
}
+4 -3
View File
@@ -11,9 +11,10 @@ import (
)
var getCmd = &cobra.Command{
Use: "get <key>",
Short: "Get configuration in cluster",
Long: ``,
Use: "get <key>",
Short: "Get configuration in cluster",
Long: ``,
Deprecated: "use the 'view' command instead",
Args: func(cmd *cobra.Command, args []string) error {
if len(args) < 1 || len(args) > 1 {
return fmt.Errorf("requires one argument")
+6 -5
View File
@@ -10,10 +10,11 @@ import (
"github.com/spf13/cobra"
)
var setCmd = &cobra.Command{
Use: "set <key>=<value>",
Short: "Set configuration in cluster",
Long: ``,
var setClusterCmd = &cobra.Command{
Use: "set <key>=<value>",
Short: "Set configuration in cluster",
Long: ``,
Deprecated: "use the 'set' command instead",
Args: func(cmd *cobra.Command, args []string) error {
if len(args) < 1 || len(args) > 1 {
return fmt.Errorf("requires one argument: <key>=<value>")
@@ -40,5 +41,5 @@ var setCmd = &cobra.Command{
}
func init() {
clusterCmd.AddCommand(setCmd)
clusterCmd.AddCommand(setClusterCmd)
}
+111 -2
View File
@@ -1,18 +1,127 @@
package cmd
import (
"fmt"
"os"
"strings"
"github.com/armosec/kubescape/clihandler"
"github.com/armosec/kubescape/clihandler/cliobjects"
"github.com/spf13/cobra"
)
var (
configExample = `
# View cached configurations
kubescape config view
# Delete cached configurations
kubescape config delete
# Set cached configurations
kubescape config set --help
`
setConfigExample = `
# Set account id
kubescape config set accountID <account id>
# Set client id
kubescape config set clientID <client id>
# Set access key
kubescape config set accessKey <access key>
`
)
// configCmd represents the config command
var configCmd = &cobra.Command{
Use: "config",
Short: "Set configuration",
Use: "config",
Short: "handle cached configurations",
Example: configExample,
}
var setConfig = cliobjects.SetConfig{}
// configCmd represents the config command
var configSetCmd = &cobra.Command{
Use: "set",
Short: fmt.Sprintf("Set configurations, supported: %s", strings.Join(stringKeysToSlice(supportConfigSet), "/")),
Example: setConfigExample,
ValidArgs: stringKeysToSlice(supportConfigSet),
RunE: func(cmd *cobra.Command, args []string) error {
if err := parseSetArgs(args); err != nil {
return err
}
if err := clihandler.CliSetConfig(&setConfig); err != nil {
fmt.Fprintf(os.Stderr, "error: %v\n", err)
os.Exit(1)
}
return nil
},
}
var supportConfigSet = map[string]func(*cliobjects.SetConfig, string){
"accountID": func(s *cliobjects.SetConfig, account string) { s.Account = account },
"clientID": func(s *cliobjects.SetConfig, clientID string) { s.ClientID = clientID },
"accessKey": func(s *cliobjects.SetConfig, accessKey string) { s.AccessKey = accessKey },
}
func stringKeysToSlice(m map[string]func(*cliobjects.SetConfig, string)) []string {
l := []string{}
for i := range m {
l = append(l, i)
}
return l
}
func parseSetArgs(args []string) error {
var key string
var value string
if len(args) == 1 {
if keyValue := strings.Split(args[0], "="); len(keyValue) == 2 {
key = keyValue[0]
value = keyValue[1]
}
} else if len(args) == 2 {
key = args[0]
value = args[1]
}
if setConfigFunc, ok := supportConfigSet[key]; ok {
setConfigFunc(&setConfig, value)
} else {
return fmt.Errorf("key '%s' unknown . supported: %s", key, strings.Join(stringKeysToSlice(supportConfigSet), "/"))
}
return nil
}
var configDeleteCmd = &cobra.Command{
Use: "delete",
Short: "Delete cached configurations",
Long: ``,
Run: func(cmd *cobra.Command, args []string) {
if err := clihandler.CliDelete(); err != nil {
fmt.Fprintf(os.Stderr, "error: %v\n", err)
os.Exit(1)
}
},
}
// configCmd represents the config command
var configViewCmd = &cobra.Command{
Use: "view",
Short: "View cached configurations",
Long: ``,
Run: func(cmd *cobra.Command, args []string) {
if err := clihandler.CliView(); err != nil {
fmt.Fprintf(os.Stderr, "error: %v\n", err)
os.Exit(1)
}
},
}
func init() {
rootCmd.AddCommand(configCmd)
configCmd.AddCommand(configSetCmd)
configCmd.AddCommand(configDeleteCmd)
configCmd.AddCommand(configViewCmd)
}
+1 -1
View File
@@ -69,7 +69,7 @@ func init() {
// cobra.OnInitialize(initConfig)
rootCmd.AddCommand(downloadCmd)
downloadCmd.Flags().StringVarP(&downloadInfo.Path, "output", "o", "", "Output file. If not specified, will save in `~/.kubescape/<policy name>.json`")
downloadCmd.PersistentFlags().StringVarP(&downloadInfo.Account, "account", "", "", "Armo portal account ID. Default will load account ID from configMap or config file")
downloadCmd.Flags().StringVarP(&downloadInfo.Path, "output", "o", "", "Output file. If not specified, will save in `~/.kubescape/<policy name>.json`")
}
+2 -1
View File
@@ -7,6 +7,7 @@ import (
"github.com/armosec/kubescape/cautils"
"github.com/armosec/kubescape/clihandler"
"github.com/armosec/kubescape/clihandler/cliobjects"
"github.com/spf13/cobra"
)
@@ -28,7 +29,7 @@ var (
https://hub.armo.cloud/docs/controls
`
)
var listPolicies = cautils.ListPolicies{}
var listPolicies = cliobjects.ListPolicies{}
var listCmd = &cobra.Command{
Use: "list <policy> [flags]",
+4 -3
View File
@@ -5,9 +5,10 @@ import (
)
var localCmd = &cobra.Command{
Use: "local",
Short: "Set configuration locally (for config.json)",
Long: ``,
Use: "local",
Short: "Set configuration locally (for config.json)",
Long: ``,
Deprecated: "use the 'set' command instead",
Run: func(cmd *cobra.Command, args []string) {
},
}
+4 -3
View File
@@ -9,9 +9,10 @@ import (
)
var localGetCmd = &cobra.Command{
Use: "get <key>",
Short: "Get configuration locally",
Long: ``,
Use: "get <key>",
Short: "Get configuration locally",
Long: ``,
Deprecated: "use the 'view' command instead",
Args: func(cmd *cobra.Command, args []string) error {
if len(args) < 1 || len(args) > 1 {
return fmt.Errorf("requires one argument")
+4 -3
View File
@@ -9,9 +9,10 @@ import (
)
var localSetCmd = &cobra.Command{
Use: "set <key>=<value>",
Short: "Set configuration locally",
Long: ``,
Use: "set <key>=<value>",
Short: "Set configuration locally",
Long: ``,
Deprecated: "use the 'set' command instead",
Args: func(cmd *cobra.Command, args []string) error {
if len(args) < 1 || len(args) > 1 {
return fmt.Errorf("requires one argument: <key>=<value>")
+1 -1
View File
@@ -29,7 +29,7 @@ var rabcCmd = &cobra.Command{
}
// list RBAC
rbacObjects := cautils.NewRBACObjects(rbacscanner.NewRbacScannerFromK8sAPI(k8s, clusterConfig.GetCustomerGUID(), clusterConfig.GetClusterName()))
rbacObjects := cautils.NewRBACObjects(rbacscanner.NewRbacScannerFromK8sAPI(k8s, clusterConfig.GetAccountID(), clusterConfig.GetClusterName()))
// submit resources
r := reporterv1.NewReportEventReceiver(clusterConfig.GetConfigObj())
+1 -1
View File
@@ -67,7 +67,7 @@ var resultsCmd = &cobra.Command{
return err
}
resultsObjects := NewResultsObject(clusterConfig.GetCustomerGUID(), clusterConfig.GetClusterName(), args[0])
resultsObjects := NewResultsObject(clusterConfig.GetAccountID(), clusterConfig.GetClusterName(), args[0])
// submit resources
r := reporterv1.NewReportEventReceiver(clusterConfig.GetConfigObj())
+1 -2
View File
@@ -10,7 +10,6 @@ import (
"github.com/spf13/cobra"
)
var cfgFile string
var armoBEURLs = ""
const envFlagUsage = "Send report results to specific URL. Format:<ReportReceiver>,<Backend>,<Frontend>.\n\t\tExample:report.armo.cloud,api.armo.cloud,portal.armo.cloud"
@@ -31,7 +30,7 @@ func Execute() {
}
func init() {
rootCmd.PersistentFlags().StringVarP(&scanInfo.Account, "account", "", "", "Armo portal account ID. Default will load account ID from configMap or config file")
flag.CommandLine.StringVar(&armoBEURLs, "environment", "", envFlagUsage)
rootCmd.PersistentFlags().StringVar(&armoBEURLs, "environment", "", envFlagUsage)
rootCmd.PersistentFlags().MarkHidden("environment")
+3 -1
View File
@@ -42,7 +42,9 @@ func init() {
cobra.OnInitialize(frameworkInitConfig)
rootCmd.AddCommand(scanCmd)
rootCmd.PersistentFlags().StringVarP(&scanInfo.KubeContext, "kube-context", "", "", "Kube context. Default will use the current-context")
scanCmd.PersistentFlags().StringVarP(&scanInfo.Account, "account", "", "", "Armo portal account ID. Default will load account ID from configMap or config file")
scanCmd.PersistentFlags().StringVarP(&scanInfo.KubeContext, "kube-context", "", "", "Kube context. Default will use the current-context")
scanCmd.PersistentFlags().StringVar(&scanInfo.ControlsInputs, "controls-config", "", "Path to an controls-config obj. If not set will download controls-config from ARMO management portal")
scanCmd.PersistentFlags().StringVar(&scanInfo.UseExceptions, "exceptions", "", "Path to an exceptions obj. If not set will download exceptions from ARMO management portal")
scanCmd.PersistentFlags().StringVar(&scanInfo.UseArtifactsFrom, "use-artifacts-from", "", "Load artifacts from local directory. If not used will download them")
+6 -2
View File
@@ -4,9 +4,12 @@ import (
"github.com/armosec/k8s-interface/k8sinterface"
"github.com/armosec/kubescape/cautils"
"github.com/armosec/kubescape/cautils/getter"
"github.com/armosec/kubescape/clihandler/cliobjects"
"github.com/spf13/cobra"
)
var submitInfo cliobjects.Submit
var submitCmd = &cobra.Command{
Use: "submit <command>",
Short: "Submit an object to the Kubescape SaaS version",
@@ -16,12 +19,13 @@ var submitCmd = &cobra.Command{
}
func init() {
submitCmd.PersistentFlags().StringVarP(&submitInfo.Account, "account", "", "", "Armo portal account ID. Default will load account ID from configMap or config file")
rootCmd.AddCommand(submitCmd)
}
func getSubmittedClusterConfig(k8s *k8sinterface.KubernetesApi) (*cautils.ClusterConfig, error) {
clusterConfig := cautils.NewClusterConfig(k8s, getter.GetArmoAPIConnector(), scanInfo.Account, scanInfo.KubeContext) // TODO - support none cluster env submit
if clusterConfig.GetCustomerGUID() != "" {
clusterConfig := cautils.NewClusterConfig(k8s, getter.GetArmoAPIConnector(), submitInfo.Account, scanInfo.KubeContext) // TODO - support none cluster env submit
if clusterConfig.GetAccountID() != "" {
if err := clusterConfig.SetTenant(); err != nil {
return clusterConfig, err
}
+14 -20
View File
@@ -11,7 +11,6 @@ import (
// printerv2 "github.com/armosec/kubescape/resultshandling/printer/v2"
"github.com/armosec/armoapi-go/armotypes"
"github.com/armosec/kubescape/cautils"
"github.com/armosec/kubescape/cautils/getter"
"github.com/armosec/kubescape/clihandler/cliinterfaces"
@@ -63,7 +62,12 @@ func getInterfaces(scanInfo *cautils.ScanInfo) componentInterfaces {
scanInfo.ExcludedNamespaces = fmt.Sprintf("%s,%s", scanInfo.ExcludedNamespaces, hostSensorHandler.GetNamespace())
}
resourceHandler := getResourceHandler(scanInfo, tenantConfig, k8s, hostSensorHandler)
registryAdaptors, err := resourcehandler.NewRegistryAdaptors()
if err != nil {
// display warning
}
resourceHandler := getResourceHandler(scanInfo, tenantConfig, k8s, hostSensorHandler, registryAdaptors)
// reporting behavior - setup reporter
reportHandler := getReporter(tenantConfig, scanInfo.Submit)
@@ -94,16 +98,16 @@ func ScanCliSetup(scanInfo *cautils.ScanInfo) error {
processNotification := make(chan *cautils.OPASessionObj)
reportResults := make(chan *cautils.OPASessionObj)
cautils.ClusterName = interfaces.tenantConfig.GetClusterName() // TODO - Deprecated
cautils.CustomerGUID = interfaces.tenantConfig.GetCustomerGUID() // TODO - Deprecated
cautils.ClusterName = interfaces.tenantConfig.GetClusterName() // TODO - Deprecated
cautils.CustomerGUID = interfaces.tenantConfig.GetAccountID() // TODO - Deprecated
interfaces.report.SetClusterName(interfaces.tenantConfig.GetClusterName())
interfaces.report.SetCustomerGUID(interfaces.tenantConfig.GetCustomerGUID())
interfaces.report.SetCustomerGUID(interfaces.tenantConfig.GetAccountID())
downloadReleasedPolicy := getter.NewDownloadReleasedPolicy() // download config inputs from github release
// set policy getter only after setting the customerGUID
scanInfo.Getters.PolicyGetter = getPolicyGetter(scanInfo.UseFrom, interfaces.tenantConfig.GetCustomerGUID(), scanInfo.FrameworkScan, downloadReleasedPolicy)
scanInfo.Getters.ControlsInputsGetter = getConfigInputsGetter(scanInfo.ControlsInputs, interfaces.tenantConfig.GetCustomerGUID(), downloadReleasedPolicy)
scanInfo.Getters.PolicyGetter = getPolicyGetter(scanInfo.UseFrom, interfaces.tenantConfig.GetAccountID(), scanInfo.FrameworkScan, downloadReleasedPolicy)
scanInfo.Getters.ControlsInputsGetter = getConfigInputsGetter(scanInfo.ControlsInputs, interfaces.tenantConfig.GetAccountID(), downloadReleasedPolicy)
scanInfo.Getters.ExceptionsGetter = getExceptionsGetter(scanInfo.UseExceptions)
// TODO - list supported frameworks/controls
@@ -153,19 +157,9 @@ func ScanCliSetup(scanInfo *cautils.ScanInfo) error {
}
func Scan(policyHandler *policyhandler.PolicyHandler, scanInfo *cautils.ScanInfo) error {
policyNotification := &reporthandling.PolicyNotification{
NotificationType: reporthandling.TypeExecPostureScan,
Rules: scanInfo.PolicyIdentifier,
Designators: armotypes.PortalDesignator{},
}
switch policyNotification.NotificationType {
case reporthandling.TypeExecPostureScan:
if err := policyHandler.HandleNotificationRequest(policyNotification, scanInfo); err != nil {
return err
}
default:
return fmt.Errorf("notification type '%s' Unknown", policyNotification.NotificationType)
policyNotification := &reporthandling.PolicyNotification{Rules: scanInfo.PolicyIdentifier}
if err := policyHandler.HandleNotificationRequest(policyNotification, scanInfo); err != nil {
return err
}
return nil
}
+5 -6
View File
@@ -42,7 +42,7 @@ func getExceptionsGetter(useExceptions string) getter.IExceptionsGetter {
func getRBACHandler(tenantConfig cautils.ITenantConfig, k8s *k8sinterface.KubernetesApi, submit bool) *cautils.RBACObjects {
if submit {
return cautils.NewRBACObjects(rbacscanner.NewRbacScannerFromK8sAPI(k8s, tenantConfig.GetCustomerGUID(), tenantConfig.GetClusterName()))
return cautils.NewRBACObjects(rbacscanner.NewRbacScannerFromK8sAPI(k8s, tenantConfig.GetAccountID(), tenantConfig.GetClusterName()))
}
return nil
}
@@ -55,12 +55,13 @@ func getReporter(tenantConfig cautils.ITenantConfig, submit bool) reporter.IRepo
return reporterv1.NewReportMock()
}
func getResourceHandler(scanInfo *cautils.ScanInfo, tenantConfig cautils.ITenantConfig, k8s *k8sinterface.KubernetesApi, hostSensorHandler hostsensorutils.IHostSensor) resourcehandler.IResourceHandler {
func getResourceHandler(scanInfo *cautils.ScanInfo, tenantConfig cautils.ITenantConfig, k8s *k8sinterface.KubernetesApi, hostSensorHandler hostsensorutils.IHostSensor, registryAdaptors *resourcehandler.RegistryAdaptors) resourcehandler.IResourceHandler {
if len(scanInfo.InputPatterns) > 0 || k8s == nil {
return resourcehandler.NewFileResourceHandler(scanInfo.InputPatterns)
return resourcehandler.NewFileResourceHandler(scanInfo.InputPatterns, registryAdaptors)
}
getter.GetArmoAPIConnector()
rbacObjects := getRBACHandler(tenantConfig, k8s, scanInfo.Submit)
return resourcehandler.NewK8sResourceHandler(k8s, getFieldSelector(scanInfo), hostSensorHandler, rbacObjects)
return resourcehandler.NewK8sResourceHandler(k8s, getFieldSelector(scanInfo), hostSensorHandler, rbacObjects, registryAdaptors)
}
func getHostSensorHandler(scanInfo *cautils.ScanInfo, k8s *k8sinterface.KubernetesApi) hostsensorutils.IHostSensor {
@@ -152,7 +153,6 @@ func getPolicyGetter(loadPoliciesFromFile []string, accountID string, frameworkS
}
if accountID != "" && frameworkScope {
g := getter.GetArmoAPIConnector() // download policy from ARMO backend
g.SetCustomerGUID(accountID)
return g
}
if downloadReleasedPolicy == nil {
@@ -183,7 +183,6 @@ func getConfigInputsGetter(ControlsInputs string, accountID string, downloadRele
}
if accountID != "" {
g := getter.GetArmoAPIConnector() // download config from ARMO backend
g.SetCustomerGUID(accountID)
return g
}
if downloadReleasedPolicy == nil {
+90
View File
@@ -0,0 +1,90 @@
package containerscan
import (
"bytes"
"math/rand"
"time"
"github.com/francoispqt/gojay"
)
// GenerateContainerScanReportMock - generate a scan result
func GenerateContainerScanReportMock() ScanResultReport {
ds := ScanResultReport{
WLID: "wlid://cluster-k8s-geriatrix-k8s-demo3/namespace-whisky-app/deployment-whisky4all-shipping",
CustomerGUID: "1231bcb1-49ce-4a67-bdd3-5da7a393ae08",
ImgTag: "dreg.armo.cloud:443/demoservice:v16",
ImgHash: "docker-pullable://dreg.armo.cloud:443/demoservice@sha256:754f3cfca915a07ed10655a301dd7a8dc5526a06f9bd06e7c932f4d4108a8296",
Timestamp: time.Now().UnixNano(),
}
ds.Layers = make(LayersList, 0)
layer := ScanResultLayer{}
GenerateContainerScanLayer(&layer)
ds.Layers = append(ds.Layers, layer)
return ds
}
// GenerateContainerScanReportMock - generate a scan result
func GenerateContainerScanReportNoVulMock() ScanResultReport {
ds := ScanResultReport{
WLID: "wlid://cluster-k8s-geriatrix-k8s-demo3/namespace-whisky-app/deployment-whisky4all-shipping",
CustomerGUID: "1231bcb1-49ce-4a67-bdd3-5da7a393ae08",
ImgTag: "dreg.armo.cloud:443/demoservice:v16",
ImgHash: "docker-pullable://dreg.armo.cloud:443/demoservice@sha256:754f3cfca915a07ed10655a301dd7a8dc5526a06f9bd06e7c932f4d4108a8296",
Timestamp: time.Now().UnixNano(),
ContainerName: "shipping",
}
ds.Layers = make(LayersList, 0)
layer := ScanResultLayer{LayerHash: "aaa"}
ds.Layers = append(ds.Layers, layer)
return ds
}
var hash = []rune("abcdef0123456789")
var nums = []rune("0123456789")
func randSeq(n int, bank []rune) string {
rand.Seed(time.Now().UnixNano())
b := make([]rune, n)
for i := range b {
b[i] = bank[rand.Intn(len(bank))]
}
return string(b)
}
// GenerateContainerScanLayer - generate a layer with random vuls
func GenerateContainerScanLayer(layer *ScanResultLayer) {
layer.LayerHash = randSeq(32, hash)
layer.Vulnerabilities = make(VulnerabilitiesList, 0)
layer.Packages = make(LinuxPkgs, 0)
vuls := rand.Intn(10) + 1
for i := 0; i < vuls; i++ {
v := Vulnerability{}
GenerateVulnerability(&v)
layer.Vulnerabilities = append(layer.Vulnerabilities, v)
}
pkg := LinuxPackage{PackageName: "coreutils"}
pkg.Files = make(PkgFiles, 0)
pf := PackageFile{Filename: "aa"}
pkg.Files = append(pkg.Files, pf)
layer.Packages = append(layer.Packages, pkg)
}
// GenerateVulnerability - generate a vul (just diff "cve"'s)
func GenerateVulnerability(v *Vulnerability) error {
baseVul := " { \"name\": \"CVE-2014-9471\", \"imageTag\": \"debian:8\", \"link\": \"https://security-tracker.debian.org/tracker/CVE-2014-9471\", \"description\": \"The parse_datetime function in GNU coreutils allows remote attackers to cause a denial of service (crash) or possibly execute arbitrary code via a crafted date string, as demonstrated by the sdf\", \"severity\": \"Low\", \"metadata\": { \"NVD\": { \"CVSSv2\": { \"Score\": 7.5, \"Vectors\": \"AV:N/AC:L/Au:N/C:P/I:P\" } } }, \"fixedIn\": [ { \"name\": \"coreutils\", \"imageTag\": \"debian:8\", \"version\": \"8.23-1\" } ] }"
b := []byte(baseVul)
r := bytes.NewReader(b)
er := gojay.NewDecoder(r).DecodeObject(v)
v.RelatedPackageName = "coreutils"
v.Severity = HighSeverity
v.Relevancy = Irelevant
v.Name = "CVE-" + randSeq(4, nums) + "-" + randSeq(4, nums)
return er
}
+90
View File
@@ -0,0 +1,90 @@
package containerscan
import (
"bytes"
"encoding/json"
"fmt"
"strings"
"testing"
"github.com/francoispqt/gojay"
)
func TestDecodeScanWIthDangearousArtifacts(t *testing.T) {
rhs := &ScanResultReport{}
er := gojay.NewDecoder(strings.NewReader(nginxScanJSON)).DecodeObject(rhs)
if er != nil {
t.Errorf("decode failed due to: %v", er.Error())
}
sumObj := rhs.Summarize()
if sumObj.Registry != "" {
t.Errorf("sumObj.Registry = %v", sumObj.Registry)
}
if sumObj.VersionImage != "nginx:1.18.0" {
t.Errorf("sumObj.VersionImage = %v", sumObj.Registry)
}
if sumObj.ImgTag != "nginx:1.18.0" {
t.Errorf("sumObj.ImgTag = %v", sumObj.ImgTag)
}
if sumObj.Status != "Success" {
t.Errorf("sumObj.Status = %v", sumObj.Status)
}
if len(sumObj.ListOfDangerousArtifcats) != 3 {
t.Errorf("sumObj.ListOfDangerousArtifcats = %v", sumObj.ListOfDangerousArtifcats)
}
}
func TestUnmarshalScanReport(t *testing.T) {
ds := GenerateContainerScanReportMock()
str1 := ds.AsFNVHash()
rhs := &ScanResultReport{}
bolB, _ := json.Marshal(ds)
r := bytes.NewReader(bolB)
er := gojay.NewDecoder(r).DecodeObject(rhs)
if er != nil {
t.Errorf("marshalling failed due to: %v", er.Error())
}
if rhs.AsFNVHash() != str1 {
t.Errorf("marshalling failed different values after marshal:\nOriginal:\n%v\nParsed:\n%v\n\n===\n", string(bolB), rhs)
}
}
func TestUnmarshalScanReport1(t *testing.T) {
ds := Vulnerability{}
if err := GenerateVulnerability(&ds); err != nil {
t.Errorf("%v\n%v\n", ds, err)
}
}
func TestGetByPkgNameSuccess(t *testing.T) {
ds := GenerateContainerScanReportMock()
a := ds.Layers[0].GetFilesByPackage("coreutils")
if a != nil {
fmt.Printf("%+v\n", *a)
}
}
func TestGetByPkgNameMissing(t *testing.T) {
ds := GenerateContainerScanReportMock()
a := ds.Layers[0].GetFilesByPackage("s")
if a != nil && len(*a) > 0 {
t.Errorf("expected - no such package should be in that layer %v\n\n; found - %v", ds, a)
}
}
func TestCalculateFixed(t *testing.T) {
res := CalculateFixed([]FixedIn{{
Name: "",
ImgTag: "",
Version: "",
}})
if 0 != res {
t.Errorf("wrong fix status: %v", res)
}
}
+37
View File
@@ -0,0 +1,37 @@
package containerscan
const (
//defines Relevancy as enum-like
Unknown = "Unknown"
Relevant = "Relevant"
Irelevant = "Irelevant"
NoSP = "No signature profile to compare"
//Clair Severities
UnknownSeverity = "Unknown"
NegligibleSeverity = "Negligible"
LowSeverity = "Low"
MediumSeverity = "Medium"
HighSeverity = "High"
CriticalSeverity = "Critical"
ContainerScanRedisPrefix = "_containerscan"
)
var KnownSeverities = map[string]bool{
UnknownSeverity: true,
NegligibleSeverity: true,
LowSeverity: true,
MediumSeverity: true,
HighSeverity: true,
CriticalSeverity: true,
}
func CalculateFixed(Fixes []FixedIn) int {
for _, fix := range Fixes {
if fix.Version != "None" && fix.Version != "" {
return 1
}
}
return 0
}
+51
View File
@@ -0,0 +1,51 @@
package containerscan
import (
"strings"
"github.com/armosec/armoapi-go/armotypes"
)
func (layer *ScanResultLayer) GetFilesByPackage(pkgname string) (files *PkgFiles) {
for _, pkg := range layer.Packages {
if pkg.PackageName == pkgname {
return &pkg.Files
}
}
return &PkgFiles{}
}
func (layer *ScanResultLayer) GetPackagesNames() []string {
pkgsNames := []string{}
for _, pkg := range layer.Packages {
pkgsNames = append(pkgsNames, pkg.PackageName)
}
return pkgsNames
}
func (scanresult *ScanResultReport) GetDesignatorsNContext() (*armotypes.PortalDesignator, []armotypes.ArmoContext) {
designatorsObj := armotypes.AttributesDesignatorsFromWLID(scanresult.WLID)
designatorsObj.Attributes["containerName"] = scanresult.ContainerName
designatorsObj.Attributes["customerGUID"] = scanresult.CustomerGUID
contextObj := armotypes.DesignatorToArmoContext(designatorsObj, "designators")
return designatorsObj, contextObj
}
func (scanresult *ScanResultReport) Validate() bool {
if scanresult.CustomerGUID == "" || (scanresult.ImgHash == "" && scanresult.ImgTag == "") || scanresult.Timestamp <= 0 {
return false
}
//TODO validate layers & vuls
return true
}
func (v *Vulnerability) IsRCE() bool {
desc := strings.ToLower(v.Description)
isRCE := strings.Contains(v.Description, "RCE")
return isRCE || strings.Contains(desc, "remote code execution") || strings.Contains(desc, "remote command execution") || strings.Contains(desc, "arbitrary code") || strings.Contains(desc, "code execution") || strings.Contains(desc, "code injection") || strings.Contains(desc, "command injection") || strings.Contains(desc, "inject arbitrary commands")
}
+141
View File
@@ -0,0 +1,141 @@
package containerscan
import (
"github.com/armosec/armoapi-go/armotypes"
cautils "github.com/armosec/utils-k8s-go/armometadata"
)
// ToFlatVulnerabilities - returnsgit p
func (scanresult *ScanResultReport) ToFlatVulnerabilities() []*ElasticContainerVulnerabilityResult {
vuls := make([]*ElasticContainerVulnerabilityResult, 0)
vul2indx := make(map[string]int)
scanID := scanresult.AsFNVHash()
designatorsObj, ctxList := scanresult.GetDesignatorsNContext()
for _, layer := range scanresult.Layers {
for _, vul := range layer.Vulnerabilities {
esLayer := ESLayer{LayerHash: layer.LayerHash, ParentLayerHash: layer.ParentLayerHash}
if indx, isOk := vul2indx[vul.Name]; isOk {
vuls[indx].Layers = append(vuls[indx].Layers, esLayer)
continue
}
result := &ElasticContainerVulnerabilityResult{WLID: scanresult.WLID,
Timestamp: scanresult.Timestamp,
Designators: *designatorsObj,
Context: ctxList}
result.Vulnerability = vul
result.Layers = make([]ESLayer, 0)
result.Layers = append(result.Layers, esLayer)
result.ContainerScanID = scanID
result.IsFixed = CalculateFixed(vul.Fixes)
result.RelevantLinks = append(result.RelevantLinks, "https://nvd.nist.gov/vuln/detail/"+vul.Name)
result.RelevantLinks = append(result.RelevantLinks, vul.Link)
result.Vulnerability.Link = "https://nvd.nist.gov/vuln/detail/" + vul.Name
result.Categories.IsRCE = result.IsRCE()
vuls = append(vuls, result)
vul2indx[vul.Name] = len(vuls) - 1
}
}
// find first introduced
for i, v := range vuls {
earlyLayer := ""
for _, layer := range v.Layers {
if layer.ParentLayerHash == earlyLayer {
earlyLayer = layer.LayerHash
}
}
vuls[i].IntroducedInLayer = earlyLayer
}
return vuls
}
func (scanresult *ScanResultReport) Summarize() *ElasticContainerScanSummaryResult {
designatorsObj, ctxList := scanresult.GetDesignatorsNContext()
summary := &ElasticContainerScanSummaryResult{
Designators: *designatorsObj,
Context: ctxList,
CustomerGUID: scanresult.CustomerGUID,
ImgTag: scanresult.ImgTag,
ImgHash: scanresult.ImgHash,
WLID: scanresult.WLID,
Timestamp: scanresult.Timestamp,
ContainerName: scanresult.ContainerName,
ContainerScanID: scanresult.AsFNVHash(),
ListOfDangerousArtifcats: scanresult.ListOfDangerousArtifcats,
}
summary.Cluster = designatorsObj.Attributes[armotypes.AttributeCluster]
summary.Namespace = designatorsObj.Attributes[armotypes.AttributeNamespace]
imageInfo, e2 := cautils.ImageTagToImageInfo(scanresult.ImgTag)
if e2 == nil {
summary.Registry = imageInfo.Registry
summary.VersionImage = imageInfo.VersionImage
}
summary.PackagesName = make([]string, 0)
severitiesStats := map[string]SeverityStats{}
uniqueVulsMap := make(map[string]bool)
for _, layer := range scanresult.Layers {
summary.PackagesName = append(summary.PackagesName, (layer.GetPackagesNames())...)
for _, vul := range layer.Vulnerabilities {
if _, isOk := uniqueVulsMap[vul.Name]; isOk {
continue
}
uniqueVulsMap[vul.Name] = true
// TODO: maybe add all severities just to have a placeholders
if !KnownSeverities[vul.Severity] {
vul.Severity = UnknownSeverity
}
vulnSeverityStats, ok := severitiesStats[vul.Severity]
if !ok {
vulnSeverityStats = SeverityStats{Severity: vul.Severity}
}
vulnSeverityStats.TotalCount++
summary.TotalCount++
isFixed := CalculateFixed(vul.Fixes) > 0
if isFixed {
vulnSeverityStats.FixAvailableOfTotalCount++
summary.FixAvailableOfTotalCount++
}
isRCE := vul.IsRCE()
if isRCE {
vulnSeverityStats.RCECount++
summary.RCECount++
}
if vul.Relevancy == Relevant {
vulnSeverityStats.RelevantCount++
summary.RelevantCount++
if isFixed {
vulnSeverityStats.FixAvailableForRelevantCount++
summary.FixAvailableForRelevantCount++
}
}
severitiesStats[vul.Severity] = vulnSeverityStats
}
}
summary.Status = "Success"
// if criticalStats, hasCritical := severitiesStats[CriticalSeverity]; hasCritical && criticalStats.TotalCount > 0 {
// summary.Status = "Fail"
// }
// if highStats, hasHigh := severitiesStats[HighSeverity]; hasHigh && highStats.RelevantCount > 0 {
// summary.Status = "Fail"
// }
for sever := range severitiesStats {
summary.SeveritiesStats = append(summary.SeveritiesStats, severitiesStats[sever])
}
return summary
}
+89
View File
@@ -0,0 +1,89 @@
package containerscan
import "github.com/armosec/armoapi-go/armotypes"
type ElasticContainerVulnerabilityResult struct {
Designators armotypes.PortalDesignator `json:"designators"`
Context []armotypes.ArmoContext `json:"context"`
WLID string `json:"wlid"`
ContainerScanID string `json:"containersScanID"`
Layers []ESLayer `json:"layers"`
Timestamp int64 `json:"timestamp"`
IsFixed int `json:"isFixed"`
IntroducedInLayer string `json:"layerHash"`
RelevantLinks []string `json:"links"` // shitty SE practice
Vulnerability `json:",inline"`
}
type ESLayer struct {
LayerHash string `json:"layerHash"`
ParentLayerHash string `json:"parentLayerHash"`
}
type SeverityStats struct {
Severity string `json:"severity,omitempty"`
TotalCount int64 `json:"total"`
FixAvailableOfTotalCount int64 `json:"fixedTotal"`
RelevantCount int64 `json:"totalRelevant"`
FixAvailableForRelevantCount int64 `json:"fixedRelevant"`
RCECount int64 `json:"rceTotal"`
UrgentCount int64 `json:"urgent"`
NeglectedCount int64 `json:"neglected"`
HealthStatus string `json:"healthStatus"`
}
type ElasticContainerScanSeveritySummary struct {
Designators armotypes.PortalDesignator `json:"designators"`
Context []armotypes.ArmoContext `json:"context"`
SeverityStats
CustomerGUID string `json:"customerGUID"`
ContainerScanID string `json:"containersScanID"`
Timestamp int64 `json:"timestamp"`
WLID string `json:"wlid"`
ImgTag string `json:"imageTag"`
ImgHash string `json:"imageHash"`
Cluster string `json:"cluster"`
Namespace string `json:"namespace"`
ContainerName string `json:"containerName"`
Status string `json:"status"`
Registry string `json:"registry"`
VersionImage string `json:"versionImage"`
Version string `json:"version"`
DayDate string `json:"dayDate"`
}
type ElasticContainerScanSummaryResult struct {
SeverityStats
Designators armotypes.PortalDesignator `json:"designators"`
Context []armotypes.ArmoContext `json:"context"`
CustomerGUID string `json:"customerGUID"`
ContainerScanID string `json:"containersScanID"`
Timestamp int64 `json:"timestamp"`
WLID string `json:"wlid"`
ImgTag string `json:"imageTag"`
ImgHash string `json:"imageHash"`
Cluster string `json:"cluster"`
Namespace string `json:"namespace"`
ContainerName string `json:"containerName"`
PackagesName []string `json:"packages"`
ListOfDangerousArtifcats []string `json:"listOfDangerousArtifcats"`
Status string `json:"status"`
Registry string `json:"registry"`
VersionImage string `json:"versionImage"`
SeveritiesStats []SeverityStats `json:"severitiesStats"`
Version string `json:"version"`
}
func (summary *ElasticContainerScanSummaryResult) Validate() bool {
return summary.CustomerGUID != "" && summary.ContainerScanID != "" && (summary.ImgTag != "" || summary.ImgHash != "") && summary.Timestamp > 0
}
+246
View File
@@ -0,0 +1,246 @@
package containerscan
import (
"github.com/francoispqt/gojay"
)
/*
responsible on fast unmarshaling of various COMMON containerscan structures and substructures
*/
// UnmarshalJSONObject - File inside a pkg
func (file *PackageFile) UnmarshalJSONObject(dec *gojay.Decoder, key string) (err error) {
switch key {
case "name":
err = dec.String(&(file.Filename))
}
return err
}
func (files *PkgFiles) UnmarshalJSONArray(dec *gojay.Decoder) error {
lae := PackageFile{}
if err := dec.Object(&lae); err != nil {
return err
}
*files = append(*files, lae)
return nil
}
func (file *PackageFile) NKeys() int {
return 0
}
// UnmarshalJSONObject--- Package
func (pkgnx *LinuxPackage) UnmarshalJSONObject(dec *gojay.Decoder, key string) (err error) {
switch key {
case "packageName":
err = dec.String(&(pkgnx.PackageName))
case "version":
err = dec.String(&(pkgnx.PackageVersion))
case "files":
err = dec.Array(&(pkgnx.Files))
}
return err
}
func (file *LinuxPackage) NKeys() int {
return 0
}
func (pkgs *LinuxPkgs) UnmarshalJSONArray(dec *gojay.Decoder) error {
lae := LinuxPackage{}
if err := dec.Object(&lae); err != nil {
return err
}
*pkgs = append(*pkgs, lae)
return nil
}
//--------Vul fixed in----------------------------------
func (fx *FixedIn) UnmarshalJSONObject(dec *gojay.Decoder, key string) (err error) {
switch key {
case "name":
err = dec.String(&(fx.Name))
case "imageTag":
err = dec.String(&(fx.ImgTag))
case "version":
err = dec.String(&(fx.Version))
}
return err
}
func (t *VulFixes) UnmarshalJSONArray(dec *gojay.Decoder) error {
lae := FixedIn{}
if err := dec.Object(&lae); err != nil {
return err
}
*t = append(*t, lae)
return nil
}
func (file *FixedIn) NKeys() int {
return 0
}
//------ VULNERABIlITy ---------------------
// Name string `json:"name"`
// ImgHash string `json:"imageHash"`
// ImgTag string `json:"imageTag",omitempty`
// RelatedPackageName string `json:"packageName"`
// PackageVersion string `json:"packageVersion"`
// Link string `json:"link"`
// Description string `json:"description"`
// Severity string `json:"severity"`
// Metadata interface{} `json:"metadata",omitempty`
// Fixes VulFixes `json:"fixedIn",omitempty`
// Relevancy string `json:"relevant"` // use the related enum
func (v *Vulnerability) UnmarshalJSONObject(dec *gojay.Decoder, key string) (err error) {
switch key {
case "name":
err = dec.String(&(v.Name))
case "imageTag":
err = dec.String(&(v.ImgTag))
case "imageHash":
err = dec.String(&(v.ImgHash))
case "packageName":
err = dec.String(&(v.RelatedPackageName))
case "packageVersion":
err = dec.String(&(v.PackageVersion))
case "link":
err = dec.String(&(v.Link))
case "description":
err = dec.String(&(v.Description))
case "severity":
err = dec.String(&(v.Severity))
case "relevant":
err = dec.String(&(v.Relevancy))
case "fixedIn":
err = dec.Array(&(v.Fixes))
case "metadata":
err = dec.Interface(&(v.Metadata))
}
return err
}
func (t *VulnerabilitiesList) UnmarshalJSONArray(dec *gojay.Decoder) error {
lae := Vulnerability{}
if err := dec.Object(&lae); err != nil {
return err
}
*t = append(*t, lae)
return nil
}
func (v *Vulnerability) NKeys() int {
return 0
}
//---------Layer Object----------------------------------
// type ScanResultLayer struct {
// LayerHash string `json:layerHash`
// Vulnerabilities []Vulnerability `json:vulnerabilities`
// Packages []LinuxPackage `json:packageToFile`
// }
func (scan *ScanResultLayer) UnmarshalJSONObject(dec *gojay.Decoder, key string) (err error) {
switch key {
// case "timestamp":
// err = dec.Time(&(reporter.Timestamp), time.RFC3339)
// reporter.Timestamp = reporter.Timestamp.Local()
case "layerHash":
err = dec.String(&(scan.LayerHash))
case "parentLayerHash":
err = dec.String(&(scan.ParentLayerHash))
case "vulnerabilities":
err = dec.Array(&(scan.Vulnerabilities))
case "packageToFile":
err = dec.Array(&(scan.Packages))
}
return err
}
func (t *LayersList) UnmarshalJSONArray(dec *gojay.Decoder) error {
lae := ScanResultLayer{}
if err := dec.Object(&lae); err != nil {
return err
}
*t = append(*t, lae)
return nil
}
func (scan *ScanResultLayer) NKeys() int {
return 0
}
//---------------------SCAN RESULT--------------------------------------------------------------------------
// type ScanResultReport struct {
// CustomerGUID string `json:customerGuid`
// ImgTag string `json:imageTag,omitempty`
// ImgHash string `json:imageHash`
// WLID string `json:wlid`
// Timestamp int `json:customerGuid`
// Layers []ScanResultLayer `json:layers`
// ContainerName
// }
func (scan *ScanResultReport) UnmarshalJSONObject(dec *gojay.Decoder, key string) (err error) {
switch key {
// case "timestamp":
// err = dec.Time(&(reporter.Timestamp), time.RFC3339)
// reporter.Timestamp = reporter.Timestamp.Local()
case "customerGUID":
err = dec.String(&(scan.CustomerGUID))
case "imageTag":
err = dec.String(&(scan.ImgTag))
case "imageHash":
err = dec.String(&(scan.ImgHash))
case "wlid":
err = dec.String(&(scan.WLID))
case "containerName":
err = dec.String(&(scan.ContainerName))
case "timestamp":
err = dec.Int64(&(scan.Timestamp))
case "layers":
err = dec.Array(&(scan.Layers))
case "listOfDangerousArtifcats":
err = dec.SliceString(&(scan.ListOfDangerousArtifcats))
}
return err
}
func (scan *ScanResultReport) NKeys() int {
return 0
}
+525
View File
@@ -0,0 +1,525 @@
package containerscan
var nginxScanJSON = `
{
"customerGUID": "1e3a88bf-92ce-44f8-914e-cbe71830d566",
"imageTag": "nginx:1.18.0",
"imageHash": "",
"wlid": "wlid://cluster-test/namespace-test/deployment-davidg",
"containerName": "nginx-1",
"timestamp": 1628091365,
"layers": [
{
"layerHash": "sha256:f7ec5a41d630a33a2d1db59b95d89d93de7ae5a619a3a8571b78457e48266eba",
"parentLayerHash": "",
"vulnerabilities": [
{
"name": "CVE-2009-0854",
"imageHash": "sha256:c2c45d506085d300b72a6d4b10e3dce104228080a2cf095fc38333afe237e2be",
"imageTag": "",
"packageName": "dash",
"packageVersion": "",
"link": "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2009-0854",
"description": "Untrusted search path vulnerability in dash 0.5.4, when used as a login shell, allows local users to execute arbitrary code via a Trojan horse .profile file in the current working directory.",
"severity": "Medium",
"metadata": null,
"fixedIn": [
{
"name": "",
"imageTag": "",
"version": "0:0"
}
],
"relevant": ""
},
{
"name": "CVE-2019-13627",
"imageHash": "sha256:c2c45d506085d300b72a6d4b10e3dce104228080a2cf095fc38333afe237e2be",
"imageTag": "",
"packageName": "libgcrypt20",
"packageVersion": "",
"link": "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-13627",
"description": "It was discovered that there was a ECDSA timing attack in the libgcrypt20 cryptographic library. Version affected: 1.8.4-5, 1.7.6-2+deb9u3, and 1.6.3-2+deb8u4. Versions fixed: 1.8.5-2 and 1.6.3-2+deb8u7.",
"severity": "Medium",
"metadata": null,
"fixedIn": [
{
"name": "",
"imageTag": "",
"version": "0:0"
}
],
"relevant": ""
},
{
"name": "CVE-2021-33560",
"imageHash": "sha256:c2c45d506085d300b72a6d4b10e3dce104228080a2cf095fc38333afe237e2be",
"imageTag": "",
"packageName": "libgcrypt20",
"packageVersion": "",
"link": "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-33560",
"description": "Libgcrypt before 1.8.8 and 1.9.x before 1.9.3 mishandles ElGamal encryption because it lacks exponent blinding to address a side-channel attack against mpi_powm, and the window size is not chosen appropriately. (There is also an interoperability problem because the selection of the k integer value does not properly consider the differences between basic ElGamal encryption and generalized ElGamal encryption.) This, for example, affects use of ElGamal in OpenPGP.",
"severity": "High",
"metadata": null,
"fixedIn": [
{
"name": "",
"imageTag": "",
"version": "0:1.8.4-5+deb10u1"
}
],
"relevant": ""
},
{
"name": "CVE-2021-3345",
"imageHash": "sha256:c2c45d506085d300b72a6d4b10e3dce104228080a2cf095fc38333afe237e2be",
"imageTag": "",
"packageName": "libgcrypt20",
"packageVersion": "",
"link": "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-3345",
"description": "_gcry_md_block_write in cipher/hash-common.c in Libgcrypt version 1.9.0 has a heap-based buffer overflow when the digest final function sets a large count value. It is recommended to upgrade to 1.9.1 or later.",
"severity": "High",
"metadata": null,
"fixedIn": [
{
"name": "",
"imageTag": "",
"version": "0:0"
}
],
"relevant": ""
},
{
"name": "CVE-2010-0834",
"imageHash": "sha256:c2c45d506085d300b72a6d4b10e3dce104228080a2cf095fc38333afe237e2be",
"imageTag": "",
"packageName": "base-files",
"packageVersion": "",
"link": "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2010-0834",
"description": "The base-files package before 5.0.0ubuntu7.1 on Ubuntu 9.10 and before 5.0.0ubuntu20.10.04.2 on Ubuntu 10.04 LTS, as shipped on Dell Latitude 2110 netbooks, does not require authentication for package installation, which allows remote archive servers and man-in-the-middle attackers to execute arbitrary code via a crafted package.",
"severity": "High",
"metadata": null,
"fixedIn": [
{
"name": "",
"imageTag": "",
"version": "0:0"
}
],
"relevant": ""
},
{
"name": "CVE-2018-6557",
"imageHash": "sha256:c2c45d506085d300b72a6d4b10e3dce104228080a2cf095fc38333afe237e2be",
"imageTag": "",
"packageName": "base-files",
"packageVersion": "",
"link": "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2018-6557",
"description": "The MOTD update script in the base-files package in Ubuntu 18.04 LTS before 10.1ubuntu2.2, and Ubuntu 18.10 before 10.1ubuntu6 incorrectly handled temporary files. A local attacker could use this issue to cause a denial of service, or possibly escalate privileges if kernel symlink restrictions were disabled.",
"severity": "High",
"metadata": null,
"fixedIn": [
{
"name": "",
"imageTag": "",
"version": "0:0"
}
],
"relevant": ""
},
{
"name": "CVE-2013-0223",
"imageHash": "sha256:c2c45d506085d300b72a6d4b10e3dce104228080a2cf095fc38333afe237e2be",
"imageTag": "",
"packageName": "coreutils",
"packageVersion": "",
"link": "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2013-0223",
"description": "The SUSE coreutils-i18n.patch for GNU coreutils allows context-dependent attackers to cause a denial of service (segmentation fault and crash) via a long string to the join command, when using the -i switch, which triggers a stack-based buffer overflow in the alloca function.",
"severity": "Low",
"metadata": null,
"fixedIn": [
{
"name": "",
"imageTag": "",
"version": "0:0"
}
],
"relevant": ""
},
{
"name": "CVE-2015-4041",
"imageHash": "sha256:c2c45d506085d300b72a6d4b10e3dce104228080a2cf095fc38333afe237e2be",
"imageTag": "",
"packageName": "coreutils",
"packageVersion": "",
"link": "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2015-4041",
"description": "The keycompare_mb function in sort.c in sort in GNU Coreutils through 8.23 on 64-bit platforms performs a size calculation without considering the number of bytes occupied by multibyte characters, which allows attackers to cause a denial of service (heap-based buffer overflow and application crash) or possibly have unspecified other impact via long UTF-8 strings.",
"severity": "High",
"metadata": null,
"fixedIn": [
{
"name": "",
"imageTag": "",
"version": "0:0"
}
],
"relevant": ""
},
{
"name": "CVE-2009-4135",
"imageHash": "sha256:c2c45d506085d300b72a6d4b10e3dce104228080a2cf095fc38333afe237e2be",
"imageTag": "",
"packageName": "coreutils",
"packageVersion": "",
"link": "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2009-4135",
"description": "The distcheck rule in dist-check.mk in GNU coreutils 5.2.1 through 8.1 allows local users to gain privileges via a symlink attack on a file in a directory tree under /tmp.",
"severity": "Medium",
"metadata": null,
"fixedIn": [
{
"name": "",
"imageTag": "",
"version": "0:0"
}
],
"relevant": ""
},
{
"name": "CVE-2015-4042",
"imageHash": "sha256:c2c45d506085d300b72a6d4b10e3dce104228080a2cf095fc38333afe237e2be",
"imageTag": "",
"packageName": "coreutils",
"packageVersion": "",
"link": "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2015-4042",
"description": "Integer overflow in the keycompare_mb function in sort.c in sort in GNU Coreutils through 8.23 might allow attackers to cause a denial of service (application crash) or possibly have unspecified other impact via long strings.",
"severity": "Critical",
"metadata": null,
"fixedIn": [
{
"name": "",
"imageTag": "",
"version": "0:0"
}
],
"relevant": ""
},
{
"name": "CVE-2013-0221",
"imageHash": "sha256:c2c45d506085d300b72a6d4b10e3dce104228080a2cf095fc38333afe237e2be",
"imageTag": "",
"packageName": "coreutils",
"packageVersion": "",
"link": "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2013-0221",
"description": "The SUSE coreutils-i18n.patch for GNU coreutils allows context-dependent attackers to cause a denial of service (segmentation fault and crash) via a long string to the sort command, when using the (1) -d or (2) -M switch, which triggers a stack-based buffer overflow in the alloca function.",
"severity": "Medium",
"metadata": null,
"fixedIn": [
{
"name": "",
"imageTag": "",
"version": "0:0"
}
],
"relevant": ""
},
{
"name": "CVE-2013-0222",
"imageHash": "sha256:c2c45d506085d300b72a6d4b10e3dce104228080a2cf095fc38333afe237e2be",
"imageTag": "",
"packageName": "coreutils",
"packageVersion": "",
"link": "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2013-0222",
"description": "The SUSE coreutils-i18n.patch for GNU coreutils allows context-dependent attackers to cause a denial of service (segmentation fault and crash) via a long string to the uniq command, which triggers a stack-based buffer overflow in the alloca function.",
"severity": "Low",
"metadata": null,
"fixedIn": [
{
"name": "",
"imageTag": "",
"version": "0:0"
}
],
"relevant": ""
},
{
"name": "CVE-2016-2781",
"imageHash": "sha256:c2c45d506085d300b72a6d4b10e3dce104228080a2cf095fc38333afe237e2be",
"imageTag": "",
"packageName": "coreutils",
"packageVersion": "",
"link": "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2016-2781",
"description": "chroot in GNU coreutils, when used with --userspec, allows local users to escape to the parent session via a crafted TIOCSTI ioctl call, which pushes characters to the terminal's input buffer.",
"severity": "Medium",
"metadata": null,
"fixedIn": [
{
"name": "",
"imageTag": "",
"version": "0:0"
}
],
"relevant": ""
},
{
"name": "CVE-2017-18018",
"imageHash": "sha256:c2c45d506085d300b72a6d4b10e3dce104228080a2cf095fc38333afe237e2be",
"imageTag": "",
"packageName": "coreutils",
"packageVersion": "",
"link": "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2017-18018",
"description": "In GNU Coreutils through 8.29, chown-core.c in chown and chgrp does not prevent replacement of a plain file with a symlink during use of the POSIX \"-R -L\" options, which allows local users to modify the ownership of arbitrary files by leveraging a race condition.",
"severity": "Medium",
"metadata": null,
"fixedIn": [
{
"name": "",
"imageTag": "",
"version": "0:0"
}
],
"relevant": ""
},
{
"name": "CVE-2021-20193",
"imageHash": "sha256:c2c45d506085d300b72a6d4b10e3dce104228080a2cf095fc38333afe237e2be",
"imageTag": "",
"packageName": "tar",
"packageVersion": "",
"link": "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-20193",
"description": "A flaw was found in the src/list.c of tar 1.33 and earlier. This flaw allows an attacker who can submit a crafted input file to tar to cause uncontrolled consumption of memory. The highest threat from this vulnerability is to system availability.",
"severity": "Medium",
"metadata": null,
"fixedIn": [
{
"name": "",
"imageTag": "",
"version": "0:0"
}
],
"relevant": ""
},
{
"name": "CVE-2005-2541",
"imageHash": "sha256:c2c45d506085d300b72a6d4b10e3dce104228080a2cf095fc38333afe237e2be",
"imageTag": "",
"packageName": "tar",
"packageVersion": "",
"link": "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2005-2541",
"description": "Tar 1.15.1 does not properly warn the user when extracting setuid or setgid files, which may allow local users or remote attackers to gain privileges.",
"severity": "High",
"metadata": null,
"fixedIn": [
{
"name": "",
"imageTag": "",
"version": "0:0"
}
],
"relevant": ""
},
{
"name": "CVE-2019-9923",
"imageHash": "sha256:c2c45d506085d300b72a6d4b10e3dce104228080a2cf095fc38333afe237e2be",
"imageTag": "",
"packageName": "tar",
"packageVersion": "",
"link": "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-9923",
"description": "pax_decode_header in sparse.c in GNU Tar before 1.32 had a NULL pointer dereference when parsing certain archives that have malformed extended headers.",
"severity": "High",
"metadata": null,
"fixedIn": [
{
"name": "",
"imageTag": "",
"version": "0:0"
}
],
"relevant": ""
},
{
"name": "CVE-2018-1000654",
"imageHash": "sha256:c2c45d506085d300b72a6d4b10e3dce104228080a2cf095fc38333afe237e2be",
"imageTag": "",
"packageName": "libtasn1-6",
"packageVersion": "",
"link": "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2018-1000654",
"description": "GNU Libtasn1-4.13 libtasn1-4.13 version libtasn1-4.13, libtasn1-4.12 contains a DoS, specifically CPU usage will reach 100% when running asn1Paser against the POC due to an issue in _asn1_expand_object_id(p_tree), after a long time, the program will be killed. This attack appears to be exploitable via parsing a crafted file.",
"severity": "High",
"metadata": null,
"fixedIn": [
{
"name": "",
"imageTag": "",
"version": "0:0"
}
],
"relevant": ""
},
{
"name": "CVE-2011-3374",
"imageHash": "sha256:c2c45d506085d300b72a6d4b10e3dce104228080a2cf095fc38333afe237e2be",
"imageTag": "",
"packageName": "apt",
"packageVersion": "",
"link": "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2011-3374",
"description": "It was found that apt-key in apt, all versions, do not correctly validate gpg keys with the master keyring, leading to a potential man-in-the-middle attack.",
"severity": "Medium",
"metadata": null,
"fixedIn": [
{
"name": "",
"imageTag": "",
"version": "0:0"
}
],
"relevant": ""
},
{
"name": "CVE-2021-37600",
"imageHash": "sha256:c2c45d506085d300b72a6d4b10e3dce104228080a2cf095fc38333afe237e2be",
"imageTag": "",
"packageName": "util-linux",
"packageVersion": "",
"link": "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-37600",
"description": "An integer overflow in util-linux through 2.37.1 can potentially cause a buffer overflow if an attacker were able to use system resources in a way that leads to a large number in the /proc/sysvipc/sem file.",
"severity": "Unknown",
"metadata": null,
"fixedIn": [
{
"name": "",
"imageTag": "",
"version": "0:0"
}
],
"relevant": ""
},
{
"name": "CVE-2007-0822",
"imageHash": "sha256:c2c45d506085d300b72a6d4b10e3dce104228080a2cf095fc38333afe237e2be",
"imageTag": "",
"packageName": "util-linux",
"packageVersion": "",
"link": "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2007-0822",
"description": "umount, when running with the Linux 2.6.15 kernel on Slackware Linux 10.2, allows local users to trigger a NULL dereference and application crash by invoking the program with a pathname for a USB pen drive that was mounted and then physically removed, which might allow the users to obtain sensitive information, including core file contents.",
"severity": "Low",
"metadata": null,
"fixedIn": [
{
"name": "",
"imageTag": "",
"version": "0:0"
}
],
"relevant": ""
},
{
"name": "CVE-2004-1349",
"imageHash": "sha256:c2c45d506085d300b72a6d4b10e3dce104228080a2cf095fc38333afe237e2be",
"imageTag": "",
"packageName": "gzip",
"packageVersion": "",
"link": "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2004-1349",
"description": "gzip before 1.3 in Solaris 8, when called with the -f or -force flags, will change the permissions of files that are hard linked to the target files, which allows local users to view or modify these files.",
"severity": "Low",
"metadata": null,
"fixedIn": [
{
"name": "",
"imageTag": "",
"version": "0:0"
}
],
"relevant": ""
},
{
"name": "CVE-2004-0603",
"imageHash": "sha256:c2c45d506085d300b72a6d4b10e3dce104228080a2cf095fc38333afe237e2be",
"imageTag": "",
"packageName": "gzip",
"packageVersion": "",
"link": "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2004-0603",
"description": "gzexe in gzip 1.3.3 and earlier will execute an argument when the creation of a temp file fails instead of exiting the program, which could allow remote attackers or local users to execute arbitrary commands, a different vulnerability than CVE-1999-1332.",
"severity": "High",
"metadata": null,
"fixedIn": [
{
"name": "",
"imageTag": "",
"version": "0:0"
}
],
"relevant": ""
},
{
"name": "CVE-2010-0002",
"imageHash": "sha256:c2c45d506085d300b72a6d4b10e3dce104228080a2cf095fc38333afe237e2be",
"imageTag": "",
"packageName": "bash",
"packageVersion": "",
"link": "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2010-0002",
"description": "The /etc/profile.d/60alias.sh script in the Mandriva bash package for Bash 2.05b, 3.0, 3.2, 3.2.48, and 4.0 enables the --show-control-chars option in LS_OPTIONS, which allows local users to send escape sequences to terminal emulators, or hide the existence of a file, via a crafted filename.",
"severity": "Low",
"metadata": null,
"fixedIn": [
{
"name": "",
"imageTag": "",
"version": "0:0"
}
],
"relevant": ""
},
{
"name": "CVE-2019-18276",
"imageHash": "sha256:c2c45d506085d300b72a6d4b10e3dce104228080a2cf095fc38333afe237e2be",
"imageTag": "",
"packageName": "bash",
"packageVersion": "",
"link": "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-18276",
"description": "An issue was discovered in disable_priv_mode in shell.c in GNU Bash through 5.0 patch 11. By default, if Bash is run with its effective UID not equal to its real UID, it will drop privileges by setting its effective UID to its real UID. However, it does so incorrectly. On Linux and other systems that support \"saved UID\" functionality, the saved UID is not dropped. An attacker with command execution in the shell can use \"enable -f\" for runtime loading of a new builtin, which can be a shared object that calls setuid() and therefore regains privileges. However, binaries running with an effective UID of 0 are unaffected.",
"severity": "High",
"metadata": null,
"fixedIn": [
{
"name": "",
"imageTag": "",
"version": "0:0"
}
],
"relevant": ""
}
],
"packageToFile": null
},
{
"layerHash": "sha256:0b20d28b5eb3007f70c43cdd8efcdb04016aa193192e5911cda5b7590ffaa635",
"parentLayerHash": "sha256:f7ec5a41d630a33a2d1db59b95d89d93de7ae5a619a3a8571b78457e48266eba",
"vulnerabilities": [],
"packageToFile": null
},
{
"layerHash": "sha256:1576642c97761adf346890bf67c43473217160a9a203ef47d0bc6020af652798",
"parentLayerHash": "sha256:0b20d28b5eb3007f70c43cdd8efcdb04016aa193192e5911cda5b7590ffaa635",
"vulnerabilities": [],
"packageToFile": null
},
{
"layerHash": "sha256:c12a848bad84d57e3f5faafab5880484434aee3bf8bdde4d519753b7c81254fd",
"parentLayerHash": "sha256:1576642c97761adf346890bf67c43473217160a9a203ef47d0bc6020af652798",
"vulnerabilities": [],
"packageToFile": null
},
{
"layerHash": "sha256:03f221d9cf00a7077231c6dcac3c95182727c7e7fd44fd2b2e882a01dcda2d70",
"parentLayerHash": "sha256:c12a848bad84d57e3f5faafab5880484434aee3bf8bdde4d519753b7c81254fd",
"vulnerabilities": [],
"packageToFile": null
}
],
"listOfDangerousArtifcats": [
"bin/dash",
"bin/bash",
"usr/bin/curl"
]
}
`
+93
View File
@@ -0,0 +1,93 @@
package containerscan
import (
"fmt"
"hash/fnv"
)
//!!!!!!!!!!!!EVERY CHANGE IN THESE STRUCTURES => CHANGE gojayunmarshaller ASWELL!!!!!!!!!!!!!!!!!!!!!!!!
// ScanResultReport - the report given from scanner to event receiver
type ScanResultReport struct {
CustomerGUID string `json:"customerGUID"`
ImgTag string `json:"imageTag"`
ImgHash string `json:"imageHash"`
WLID string `json:"wlid"`
ContainerName string `json:"containerName"`
Timestamp int64 `json:"timestamp"`
Layers LayersList `json:"layers"`
ListOfDangerousArtifcats []string `json:"listOfDangerousArtifcats"`
}
// ScanResultLayer - represents a single layer from container scan result
type ScanResultLayer struct {
LayerHash string `json:"layerHash"`
ParentLayerHash string `json:"parentLayerHash"`
Vulnerabilities VulnerabilitiesList `json:"vulnerabilities"`
Packages LinuxPkgs `json:"packageToFile"`
}
type VulnerabilityCategory struct {
IsRCE bool `json:"isRce"`
}
// Vulnerability - a vul object
type Vulnerability struct {
Name string `json:"name"`
ImgHash string `json:"imageHash"`
ImgTag string `json:"imageTag"`
RelatedPackageName string `json:"packageName"`
PackageVersion string `json:"packageVersion"`
Link string `json:"link"`
Description string `json:"description"`
Severity string `json:"severity"`
Metadata interface{} `json:"metadata"`
Fixes VulFixes `json:"fixedIn"`
Relevancy string `json:"relevant"` // use the related enum
UrgentCount int `json:"urgent"`
NeglectedCount int `json:"neglected"`
HealthStatus string `json:"healthStatus"`
Categories VulnerabilityCategory `json:"categories"`
}
// FixedIn when and which pkg was fixed (which version as well)
type FixedIn struct {
Name string `json:"name"`
ImgTag string `json:"imageTag"`
Version string `json:"version"`
}
// LinuxPackage- Linux package representation
type LinuxPackage struct {
PackageName string `json:"packageName"`
Files PkgFiles `json:"files"`
PackageVersion string `json:"version"`
}
// PackageFile - s.e
type PackageFile struct {
Filename string `json:"name"`
}
// types to provide unmarshalling:
//VulnerabilitiesList -s.e
type LayersList []ScanResultLayer
//VulnerabilitiesList -s.e
type VulnerabilitiesList []Vulnerability
//LinuxPkgs - slice of linux pkgs
type LinuxPkgs []LinuxPackage
//VulFixes - information bout when/how this vul was fixed
type VulFixes []FixedIn
//PkgFiles - slice of files belong to specific pkg
type PkgFiles []PackageFile
func (v *ScanResultReport) AsFNVHash() string {
hasher := fnv.New64a()
hasher.Write([]byte(fmt.Sprintf("%v", *v)))
return fmt.Sprintf("%v", hasher.Sum64())
}
@@ -89,7 +89,7 @@ type ContainerImageScanStatus struct {
LastScanDate time.Time
}
type ContainerImageVulnerability struct {
type ContainerImageVulnerabilityReport struct {
ImageID ContainerImageIdentifier
// TBD
}
@@ -110,7 +110,7 @@ type IContainerImageVulnerabilityAdaptor interface {
GetImagesScanStatus(imageIDs []ContainerImageIdentifier) ([]ContainerImageScanStatus, error)
GetImagesVulnerabilties(imageIDs []ContainerImageIdentifier) ([]ContainerImageVulnerability, error)
GetImagesVulnerabilties(imageIDs []ContainerImageIdentifier) ([]ContainerImageVulnerabilityReport, error)
GetImagesInformation(imageIDs []ContainerImageIdentifier) ([]ContainerImageInformation, error)
}
+4 -4
View File
@@ -5,12 +5,14 @@ go 1.17
require (
github.com/armosec/armoapi-go v0.0.41
github.com/armosec/k8s-interface v0.0.56
github.com/armosec/opa-utils v0.0.99
github.com/armosec/opa-utils v0.0.105
github.com/armosec/rbac-utils v0.0.12
github.com/armosec/utils-go v0.0.3
github.com/armosec/utils-k8s-go v0.0.1
github.com/briandowns/spinner v1.18.0
github.com/enescakir/emoji v1.0.0
github.com/fatih/color v1.13.0
github.com/francoispqt/gojay v1.2.13
github.com/gofrs/uuid v4.1.0+incompatible
github.com/golang/glog v1.0.0
github.com/mattn/go-isatty v0.0.14
@@ -19,7 +21,6 @@ require (
github.com/satori/go.uuid v1.2.0
github.com/spf13/cobra v1.2.1
github.com/stretchr/testify v1.7.0
go.uber.org/zap v1.19.1
gopkg.in/yaml.v2 v2.4.0
k8s.io/api v0.22.2
k8s.io/apimachinery v0.22.2
@@ -37,7 +38,6 @@ require (
github.com/Azure/go-autorest/tracing v0.6.0 // indirect
github.com/OneOfOne/xxhash v1.2.8 // indirect
github.com/armosec/armo-interfaces v0.0.3 // indirect
github.com/armosec/utils-k8s-go v0.0.1 // indirect
github.com/aws/aws-sdk-go v1.41.11 // indirect
github.com/coreos/go-oidc v2.2.1+incompatible // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
@@ -45,7 +45,6 @@ require (
github.com/docker/go-connections v0.4.0 // indirect
github.com/docker/go-units v0.4.0 // indirect
github.com/form3tech-oss/jwt-go v3.2.3+incompatible // indirect
github.com/francoispqt/gojay v1.2.13 // indirect
github.com/ghodss/yaml v1.0.0 // indirect
github.com/go-gota/gota v0.12.0 // indirect
github.com/go-logr/logr v0.4.0 // indirect
@@ -78,6 +77,7 @@ require (
go.opencensus.io v0.23.0 // indirect
go.uber.org/atomic v1.7.0 // indirect
go.uber.org/multierr v1.6.0 // indirect
go.uber.org/zap v1.19.1 // indirect
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97 // indirect
golang.org/x/net v0.0.0-20210825183410-e898025ed96a // indirect
golang.org/x/oauth2 v0.0.0-20211005180243-6b3c2da341f1 // indirect
+2 -2
View File
@@ -95,8 +95,8 @@ github.com/armosec/k8s-interface v0.0.50/go.mod h1:vHxGWqD/uh6+GQb9Sqv7OGMs+Rvc2
github.com/armosec/k8s-interface v0.0.56 h1:7dOgc3qZaI7ReLRZcJa2JZKk0rliyYi05l1vuHc6gcE=
github.com/armosec/k8s-interface v0.0.56/go.mod h1:vHxGWqD/uh6+GQb9Sqv7OGMs+Rvc2dsFVc0XtgRh1ZU=
github.com/armosec/opa-utils v0.0.64/go.mod h1:6tQP8UDq2EvEfSqh8vrUdr/9QVSCG4sJfju1SXQOn4c=
github.com/armosec/opa-utils v0.0.99 h1:ZuoIPg6vbgO4J09xJZDO/yIRD59odwmK2Bm55uTvkU8=
github.com/armosec/opa-utils v0.0.99/go.mod h1:BNTjeianyXlflJMz3bZM0GimBWqmzirUf1whWR6Os04=
github.com/armosec/opa-utils v0.0.105 h1:HQdP2LpFJtBoJpsfygh4IDXLLukIkQEuETOUePF/Zas=
github.com/armosec/opa-utils v0.0.105/go.mod h1:BNTjeianyXlflJMz3bZM0GimBWqmzirUf1whWR6Os04=
github.com/armosec/rbac-utils v0.0.1/go.mod h1:pQ8CBiij8kSKV7aeZm9FMvtZN28VgA7LZcYyTWimq40=
github.com/armosec/rbac-utils v0.0.12 h1:uJpMGDyLAX129PrKHp6NPNB6lVRhE0OZIwV6ywzSDrs=
github.com/armosec/rbac-utils v0.0.12/go.mod h1:Ex/IdGWhGv9HZq6Hs8N/ApzCKSIvpNe/ETqDfnuyah0=
+1
View File
@@ -6,6 +6,7 @@ echo
BASE_DIR=~/.kubescape
KUBESCAPE_EXEC=kubescape
KUBESCAPE_ZIP=kubescape.zip
osName=$(uname -s)
if [[ $osName == *"MINGW"* ]]; then
+1 -1
View File
@@ -50,7 +50,7 @@ func (policyHandler *PolicyHandler) HandleNotificationRequest(notification *repo
func (policyHandler *PolicyHandler) getResources(notification *reporthandling.PolicyNotification, opaSessionObj *cautils.OPASessionObj, scanInfo *cautils.ScanInfo) error {
opaSessionObj.PostureReport.ClusterAPIServerInfo = policyHandler.resourceHandler.GetClusterAPIServerInfo()
opaSessionObj.Report.ClusterAPIServerInfo = policyHandler.resourceHandler.GetClusterAPIServerInfo()
resourcesMap, allResources, err := policyHandler.resourceHandler.GetResources(opaSessionObj.Frameworks, &notification.Designators)
if err != nil {
return err
+1 -1
View File
@@ -16,7 +16,7 @@ func (policyHandler *PolicyHandler) getPolicies(notification *reporthandling.Pol
return err
}
if len(frameworks) == 0 {
return fmt.Errorf("failed to download policies: '%s'. Make sure the policy exist and you spelled it correctly. For more information, please feel free to contact ARMO team", strings.Join(policyIdentifierToSlice(notification.Rules), ","))
return fmt.Errorf("failed to download policies: '%s'. Make sure the policy exist and you spelled it correctly. For more information, please feel free to contact ARMO team", strings.Join(policyIdentifierToSlice(notification.Rules), ", "))
}
policiesAndResources.Frameworks = frameworks
+38
View File
@@ -0,0 +1,38 @@
# Integrate With Vulnerability Server
There are some controls that check the relation between the kubernetes manifest and vulnerabilities.
For these controls to work properly, it is necasery to
## Supported Servers
* Armosec
# Integrate With Armosec Server
1. Navigate to the [armosec.io](https://portal.armo.cloud/)
2. Click Profile(top right icon)->"User Management"->"API Tokens" and Generate a token
3. Copy the clientID and accessKey and run:
```
kubescape config set clientID <>
```
```
kubescape config set accessKey <>
```
4. Confirm the keys are set
```
kubescape config view
```
Expecting:
```
{
"accountID": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
"clientID": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
"accessKey": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
}
```
> If you are missing the `accountID` field, set it by running `kubescape config set accountID <>`
For CICD, set environments variables as following:
```
KS_ACCOUNT_ID // account id
KS_CLIENT_ID // client id
KS_ACCESS_KEY // access key
```
@@ -0,0 +1,157 @@
package v1
import (
"bytes"
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"github.com/armosec/kubescape/containerscan"
"github.com/armosec/kubescape/registryadaptors/registryvulnerabilities"
)
func NewArmoAdaptor(registry string, credentials map[string]string) (*ArmoCivAdaptor, error) {
var accountID string
var accessKey string
var clientID string
var ok bool
if accountID, ok = credentials["accountID"]; !ok {
return nil, fmt.Errorf("define accountID in credentials")
}
if clientID, ok = credentials["clientID"]; !ok {
return nil, fmt.Errorf("define clientID in credentials")
}
if accessKey, ok = credentials["accessKey"]; !ok {
return nil, fmt.Errorf("define accessKey in credentials")
}
armoCivAdaptor := ArmoCivAdaptor{registry: registry, clientID: clientID, accountID: accountID, accessKey: accessKey}
err := armoCivAdaptor.initializeUrls()
if err != nil {
return nil, err
}
return &armoCivAdaptor, nil
}
func (armoCivAdaptor *ArmoCivAdaptor) Login() error {
feLoginData := FeLoginData{ClientId: armoCivAdaptor.clientID, Secret: armoCivAdaptor.accessKey}
body, _ := json.Marshal(feLoginData)
authApiTokenEndpoint := fmt.Sprintf("%s/frontegg/identity/resources/auth/v1/api-token", armoCivAdaptor.armoUrls.AuthUrl)
resp, err := http.Post(authApiTokenEndpoint, "application/json", bytes.NewBuffer(body))
if err != nil {
return err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return fmt.Errorf("error authenticating: %d", resp.StatusCode)
}
responseBody, err := ioutil.ReadAll(resp.Body)
if err != nil {
return err
}
var feLoginResponse FeLoginResponse
if err = json.Unmarshal(responseBody, &feLoginResponse); err != nil {
return err
}
armoCivAdaptor.feToken = feLoginResponse
/* Now we have JWT */
armoCivAdaptor.authCookie, err = armoCivAdaptor.getAuthCookie()
if err != nil {
return err
}
return nil
}
func (armoCivAdaptor *ArmoCivAdaptor) GetImagesVulnerabilities(imageIDs []registryvulnerabilities.ContainerImageIdentifier) ([]registryvulnerabilities.ContainerImageVulnerabilityReport, error) {
resultList := make([]registryvulnerabilities.ContainerImageVulnerabilityReport, 0)
for _, imageID := range imageIDs {
result, err := armoCivAdaptor.GetImageVulnerability(&imageID)
if err == nil {
resultList = append(resultList, *result)
}
}
return resultList, nil
}
func (armoCivAdaptor *ArmoCivAdaptor) GetImageVulnerability(imageID *registryvulnerabilities.ContainerImageIdentifier) (*registryvulnerabilities.ContainerImageVulnerabilityReport, error) {
// First
containerScanId, err := armoCivAdaptor.getImageLastScanId(imageID)
if err != nil {
return nil, err
}
if containerScanId == "" {
return nil, fmt.Errorf("last scan ID is empty")
}
filter := []map[string]string{{"containersScanID": containerScanId}}
pageSize := 300
pageNumber := 1
request := V2ListRequest{PageSize: &pageSize, PageNum: &pageNumber, InnerFilters: filter, OrderBy: "timestamp:desc"}
requestBody, _ := json.Marshal(request)
requestUrl := fmt.Sprintf("%s/api/v1/vulnerability/scanResultsDetails?customerGUID=%s", armoCivAdaptor.armoUrls.BackendUrl, armoCivAdaptor.accountID)
client := &http.Client{}
httpRequest, err := http.NewRequest("POST", requestUrl, bytes.NewBuffer(requestBody))
if err != nil {
return nil, err
}
httpRequest.Header.Set("Content-Type", "application/json")
httpRequest.Header.Set("Authorization", fmt.Sprintf("Bearer %s", armoCivAdaptor.feToken.Token))
httpRequest.Header.Set("Cookie", fmt.Sprintf("auth=%s", armoCivAdaptor.authCookie))
resp, err := client.Do(httpRequest)
if err != nil {
return nil, err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("error requests %s with %d", requestUrl, resp.StatusCode)
}
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, err
}
scanDetailsResult := struct {
Total struct {
Value int `json:"value"`
Relation string `json:"relation"`
} `json:"total"`
Response containerscan.VulnerabilitiesList `json:"response"`
Cursor string `json:"cursor"`
}{}
err = json.Unmarshal(body, &scanDetailsResult)
if err != nil {
return nil, err
}
vulnerabilities := responseObjectToVulnerabilities(scanDetailsResult.Response)
resultImageVulnerabilityReport := registryvulnerabilities.ContainerImageVulnerabilityReport{
ImageID: *imageID,
Vulnerabilities: vulnerabilities,
}
return &resultImageVulnerabilityReport, nil
}
func (armoCivAdaptor *ArmoCivAdaptor) DescribeAdaptor() string {
// TODO
return ""
}
func (armoCivAdaptor *ArmoCivAdaptor) GetImagesInformation(imageIDs []registryvulnerabilities.ContainerImageIdentifier) ([]registryvulnerabilities.ContainerImageInformation, error) {
// TODO
return []registryvulnerabilities.ContainerImageInformation{}, nil
}
func (armoCivAdaptor *ArmoCivAdaptor) GetImagesScanStatus(imageIDs []registryvulnerabilities.ContainerImageIdentifier) ([]registryvulnerabilities.ContainerImageScanStatus, error) {
// TODO
return []registryvulnerabilities.ContainerImageScanStatus{}, nil
}
@@ -0,0 +1,23 @@
package v1
import (
"testing"
"github.com/armosec/kubescape/registryadaptors/registryvulnerabilities"
"github.com/stretchr/testify/assert"
)
func TestSum(t *testing.T) {
var err error
var adaptor registryvulnerabilities.IContainerImageVulnerabilityAdaptor
adaptor, err = NewArmoAdaptorMock()
assert.NoError(t, err)
assert.NoError(t, adaptor.Login())
imageVulnerabilityReport, err := adaptor.GetImageVulnerability(&registryvulnerabilities.ContainerImageIdentifier{Tag: "gke.gcr.io/gcp-compute-persistent-disk-csi-driver:v1.3.4-gke.0"})
assert.NoError(t, err)
assert.Equal(t, 25, len(imageVulnerabilityReport.Vulnerabilities))
}
File diff suppressed because one or more lines are too long
@@ -0,0 +1,149 @@
package v1
import (
"bytes"
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"strings"
"github.com/armosec/kubescape/containerscan"
"github.com/armosec/kubescape/registryadaptors/registryvulnerabilities"
)
func (armoCivAdaptor *ArmoCivAdaptor) initializeUrls() error {
configUrl := fmt.Sprintf("https://%s/assets/configs/config.json", armoCivAdaptor.registry)
resp, err := http.Get(configUrl)
if err != nil {
return err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return fmt.Errorf("cannot retrieve backend config file %s: status %d", configUrl, resp.StatusCode)
}
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return err
}
err = json.Unmarshal(body, &armoCivAdaptor.armoUrls)
if err != nil {
return err
}
return nil
}
func (armoCivAdaptor *ArmoCivAdaptor) getAuthCookie() (string, error) {
selectCustomer := ArmoSelectCustomer{SelectedCustomerGuid: armoCivAdaptor.accountID}
requestBody, _ := json.Marshal(selectCustomer)
requestUrl := fmt.Sprintf("%s/api/v1/openid_customers", armoCivAdaptor.armoUrls.BackendUrl)
client := &http.Client{}
httpRequest, err := http.NewRequest(http.MethodPost, requestUrl, bytes.NewBuffer(requestBody))
if err != nil {
return "", err
}
httpRequest.Header.Set("Content-Type", "application/json")
httpRequest.Header.Set("Authorization", fmt.Sprintf("Bearer %s", armoCivAdaptor.feToken.Token))
httpResponse, err := client.Do(httpRequest)
if err != nil {
return "", err
}
defer httpResponse.Body.Close()
if httpResponse.StatusCode != http.StatusOK {
return "", fmt.Errorf("error getting cookie at %s: status %d", requestUrl, httpResponse.StatusCode)
}
cookies := httpResponse.Header.Get("set-cookie")
if len(cookies) == 0 {
return "", fmt.Errorf("no cookie field in response from %s", requestUrl)
}
authCookie := ""
for _, cookie := range strings.Split(cookies, ";") {
kv := strings.Split(cookie, "=")
if kv[0] == "auth" {
authCookie = kv[1]
}
}
if len(authCookie) == 0 {
return "", fmt.Errorf("no auth cookie field in response from %s", requestUrl)
}
return authCookie, nil
}
func (armoCivAdaptor *ArmoCivAdaptor) getImageLastScanId(imageID *registryvulnerabilities.ContainerImageIdentifier) (string, error) {
filter := []map[string]string{{"imageTag": imageID.Tag, "status": "Success"}}
pageSize := 1
pageNumber := 1
request := V2ListRequest{PageSize: &pageSize, PageNum: &pageNumber, InnerFilters: filter, OrderBy: "timestamp:desc"}
requestBody, _ := json.Marshal(request)
requestUrl := fmt.Sprintf("%s/api/v1/vulnerability/scanResultsSumSummary?customerGUID=%s", armoCivAdaptor.armoUrls.BackendUrl, armoCivAdaptor.accountID)
client := &http.Client{}
httpRequest, err := http.NewRequest("POST", requestUrl, bytes.NewBuffer(requestBody))
if err != nil {
return "", err
}
httpRequest.Header.Set("Content-Type", "application/json")
httpRequest.Header.Set("Authorization", fmt.Sprintf("Bearer %s", armoCivAdaptor.feToken.Token))
httpRequest.Header.Set("Cookie", fmt.Sprintf("auth=%s", armoCivAdaptor.authCookie))
resp, err := client.Do(httpRequest)
if err != nil {
return "", err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return "", fmt.Errorf("error requests %s with %d", requestUrl, resp.StatusCode)
}
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return "", err
}
scanSummartResult := struct {
Total struct {
Value int `json:"value"`
Relation string `json:"relation"`
} `json:"total"`
Response []containerscan.ElasticContainerScanSummaryResult `json:"response"`
Cursor string `json:"cursor"`
}{}
err = json.Unmarshal(body, &scanSummartResult)
if err != nil {
return "", err
}
if len(scanSummartResult.Response) < pageSize {
return "", fmt.Errorf("did not get response for image %s", imageID.Tag)
}
return scanSummartResult.Response[0].ContainerScanID, nil
}
func responseObjectToVulnerabilities(vulnerabilitiesList containerscan.VulnerabilitiesList) []registryvulnerabilities.Vulnerability {
vulnerabilities := make([]registryvulnerabilities.Vulnerability, len(vulnerabilitiesList))
for i, vulnerabilityEntry := range vulnerabilitiesList {
vulnerabilities[i].Description = vulnerabilityEntry.Description
vulnerabilities[i].Fixes = make([]registryvulnerabilities.FixedIn, len(vulnerabilityEntry.Fixes))
for j, fix := range vulnerabilityEntry.Fixes {
vulnerabilities[i].Fixes[j].ImgTag = fix.ImgTag
vulnerabilities[i].Fixes[j].Name = fix.Name
vulnerabilities[i].Fixes[j].Version = fix.Version
}
vulnerabilities[i].HealthStatus = vulnerabilityEntry.HealthStatus
vulnerabilities[i].Link = vulnerabilityEntry.Link
vulnerabilities[i].Metadata = vulnerabilityEntry.Metadata
vulnerabilities[i].Name = vulnerabilityEntry.Name
vulnerabilities[i].PackageVersion = vulnerabilityEntry.PackageVersion
vulnerabilities[i].RelatedPackageName = vulnerabilityEntry.RelatedPackageName
vulnerabilities[i].Relevancy = vulnerabilityEntry.Relevancy
vulnerabilities[i].Severity = vulnerabilityEntry.Severity
vulnerabilities[i].UrgentCount = vulnerabilityEntry.UrgentCount
}
return vulnerabilities
}
@@ -0,0 +1,58 @@
package v1
import "time"
type V2ListRequest struct {
// properties of the requested next page
// Use ValidatePageProperties to set PageSize field
PageSize *int `json:"pageSize,omitempty"`
// One can leave it empty for 0, then call ValidatePageProperties
PageNum *int `json:"pageNum,omitempty"`
// The time window of the list to return. Default: since - begining og the time, until - now.
Since *time.Time `json:"since,omitempty"`
Until *time.Time `json:"until,omitempty"`
// Which elements of the list to return, each field can hold multiple values separated by comma
// Example: ": {"severity": "High,Medium", "type": "61539,30303"}
// An empty map means "return the complete list"
InnerFilters []map[string]string `json:"innerFilters,omitempty"`
// How to order (sort) the list, field name + sort order (asc/desc), like https://www.w3schools.com/sql/sql_orderby.asp
// Example: "timestamp:asc,severity:desc"
OrderBy string `json:"orderBy,omitempty"`
// Cursor to the next page of former requset. Not supported yet
// Cursor cannot be used with another parameters of this struct
Cursor string `json:"cursor,omitempty"`
// FieldsList allow us to return only subset of the source document fields
// Don't expose FieldsList outside without well designed decision
FieldsList []string `json:"includeFields,omitempty"`
FieldsReverseKeywordMap map[string]string `json:"-,omitempty"`
}
type FeLoginData struct {
Secret string `json:"secret"`
ClientId string `json:"clientId"`
}
type FeLoginResponse struct {
Token string `json:"accessToken"`
RefreshToken string `json:"refreshToken"`
ExpiresIn int32 `json:"expiresIn"`
Expires string `json:"expires"`
}
type ArmoBeConfiguration struct {
BackendUrl string `json:"backend"`
AuthUrl string `json:"authUrl"`
}
type ArmoSelectCustomer struct {
SelectedCustomerGuid string `json:"selectedCustomer"`
}
type ArmoCivAdaptor struct {
registry string
accountID string
clientID string
accessKey string
feToken FeLoginResponse
armoUrls ArmoBeConfiguration
authCookie string
}
+164
View File
@@ -0,0 +1,164 @@
# Container image vulnerability adaptor interface
## High level design of Kubescape
### Layers
* Controls and Rules: that actual control logic implementation, the "tests" themselves. Implemented in rego
* OPA engine: the [OPA](https://github.com/open-policy-agent/opa) rego interpreter
* Rules processor: Kubescape component, it enumerates and runs the controls while also preparing the all the input data that the controls need for running
* Data sources: set of different modules providing data to the Rules processor so it can run the controls with them. Examples: Kubernetes objects, cloud vendor API objects and adding in this proposal the vulnerability infomration
* Cloud Image Vulnerability adaption interface: the subject of this proposal, it gives a common interface for different registry/vulnerabilty vendors to adapt to.
* CIV adaptors: specific implementation of the CIV interface, example Harbor adaption
```
-----------------------
| Controls/Rules (rego) |
-----------------------
|
-----------------------
| OPA engine |
-----------------------
|
-----------------------
| Rules processor |
-----------------------
|
-----------------------
| Data sources |
-----------------------
|
=======================
| CIV adaption interface| <- Adding this layer in this proposal
=======================
|
-----------------------
| Specific CIV adaptors | <- Will be implemented based on this proposal
-----------------------
```
## Functionalities to cover
The interface needs to cover the following functionalities:
* Authentication against the information source (abstracted login)
* Triggering image scan (if applicable, the source might store vulnerabilities for images but cannot scan alone)
* Reading image scan status (with last scan date and etc.)
* Getting vulnerability information for a given image
* Getting image information
* Image manifests
* Image BOMs (bill of material)
## Go API proposal
```
/*type ContainerImageRegistryCredentials struct {
Password string
Tag string
Hash string
}*/
type ContainerImageIdentifier struct {
Registry string
Repository string
Tag string
Hash string
}
type ContainerImageScanStatus struct {
ImageID ContainerImageIdentifier
IsScanAvailable bool
IsBomAvailable bool
LastScanDate time.Time
}
type ContainerImageVulnerabilityReport struct {
ImageID ContainerImageIdentifier
// TBD
}
type ContainerImageInformation struct {
ImageID ContainerImageIdentifier
Bom []string
ImageManifest Manifest // will use here Docker package definition
}
type IContainerImageVulnerabilityAdaptor interface {
// Credentials are coming from user input (CLI or configuration file) and they are abstracted at string to string map level
// so and example use would be like registry: "simpledockerregistry:80" and credentials like {"username":"joedoe","password":"abcd1234"}
Login(registry string, credentials map[string]string) error
// For "help" purposes
DescribeAdaptor() string
GetImagesScanStatus(imageIDs []ContainerImageIdentifier) ([]ContainerImageScanStatus, error)
GetImagesVulnerabilties(imageIDs []ContainerImageIdentifier) ([]ContainerImageVulnerabilityReport, error)
GetImagesInformation(imageIDs []ContainerImageIdentifier) ([]ContainerImageInformation, error)
}
```
# Integration
# Input
The objects received from the interface will be converted to an IMetadata compatible objects as following
```
{
"apiVersion": "image.vulnscan.com/v1",
"kind": "ImageVulnerabilities",
"metadata": {
"name": "nginx:latest"
},
"data": {
// list of vulnerabilities
}
}
```
# Output
The rego results will be a combination of the k8s artifact and the list of relevant CVEs for the control
```
{
"apiVersion": "result.vulnscan.com/v1",
"kind": "Pod",
"metadata": {
"name": "nginx"
"namespace": "default"
},
"relatedObjects": [
{
"apiVersion": "v1",
"kind": "Pod",
"metadata": {
"name": "nginx"
"namespace": "default"
},
"spec": {
// podSpec
},
},
{
"apiVersion": "image.vulnscan.com/v1",
"kind": "ImageVulnerabilities",
"metadata": {
"name": "nginx:latest",
},
"data": {
// list of vulnerabilities
}
}
]
}
```
@@ -0,0 +1,50 @@
package registryvulnerabilities
import (
"time"
)
type ContainerImageIdentifier struct {
Registry string
Repository string
Tag string
Hash string
}
type ContainerImageScanStatus struct {
ImageID ContainerImageIdentifier
IsScanAvailable bool
IsBomAvailable bool
LastScanDate time.Time
}
type FixedIn struct {
Name string `json:"name"`
ImgTag string `json:"imageTag"`
Version string `json:"version"`
}
type Vulnerability struct {
Name string `json:"name"`
RelatedPackageName string `json:"packageName"`
PackageVersion string `json:"packageVersion"`
Link string `json:"link"`
Description string `json:"description"`
Severity string `json:"severity"`
Metadata interface{} `json:"metadata"`
Fixes []FixedIn `json:"fixedIn"`
Relevancy string `json:"relevant"` // use the related enum
UrgentCount int `json:"urgent"`
NeglectedCount int `json:"neglected"`
HealthStatus string `json:"healthStatus"`
}
type ContainerImageVulnerabilityReport struct {
ImageID ContainerImageIdentifier
Vulnerabilities []Vulnerability
}
type ContainerImageInformation struct {
ImageID ContainerImageIdentifier
Bom []string
//ImageManifest Manifest // will use here Docker package definition
}
@@ -0,0 +1,17 @@
package registryvulnerabilities
type IContainerImageVulnerabilityAdaptor interface {
// Credentials are coming from user input (CLI or configuration file) and they are abstracted at string to string map level
// so and example use would be like registry: "simpledockerregistry:80" and credentials like {"username":"joedoe","password":"abcd1234"}
Login() error
// For "help" purposes
DescribeAdaptor() string
GetImagesScanStatus(imageIDs []ContainerImageIdentifier) ([]ContainerImageScanStatus, error)
GetImagesVulnerabilities(imageIDs []ContainerImageIdentifier) ([]ContainerImageVulnerabilityReport, error)
GetImageVulnerability(imageID *ContainerImageIdentifier) (*ContainerImageVulnerabilityReport, error)
GetImagesInformation(imageIDs []ContainerImageIdentifier) ([]ContainerImageInformation, error)
}
+9 -3
View File
@@ -34,13 +34,15 @@ const (
// FileResourceHandler handle resources from files and URLs
type FileResourceHandler struct {
inputPatterns []string
inputPatterns []string
registryAdaptors *RegistryAdaptors
}
func NewFileResourceHandler(inputPatterns []string) *FileResourceHandler {
func NewFileResourceHandler(inputPatterns []string, registryAdaptors *RegistryAdaptors) *FileResourceHandler {
k8sinterface.InitializeMapResourcesMock() // initialize the resource map
return &FileResourceHandler{
inputPatterns: inputPatterns,
inputPatterns: inputPatterns,
registryAdaptors: registryAdaptors,
}
}
@@ -90,6 +92,10 @@ func (fileHandler *FileResourceHandler) GetResources(frameworks []reporthandling
}
}
if err := fileHandler.registryAdaptors.collectImagesVulnerabilities(k8sResources, allResources); err != nil {
cautils.WarningDisplay(os.Stderr, "Warning: failed to collect images vulnerabilities: %s\n", err.Error())
}
return k8sResources, allResources, nil
}
+8 -1
View File
@@ -30,14 +30,16 @@ type K8sResourceHandler struct {
hostSensorHandler hostsensorutils.IHostSensor
fieldSelector IFieldSelector
rbacObjectsAPI *cautils.RBACObjects
registryAdaptors *RegistryAdaptors
}
func NewK8sResourceHandler(k8s *k8sinterface.KubernetesApi, fieldSelector IFieldSelector, hostSensorHandler hostsensorutils.IHostSensor, rbacObjects *cautils.RBACObjects) *K8sResourceHandler {
func NewK8sResourceHandler(k8s *k8sinterface.KubernetesApi, fieldSelector IFieldSelector, hostSensorHandler hostsensorutils.IHostSensor, rbacObjects *cautils.RBACObjects, registryAdaptors *RegistryAdaptors) *K8sResourceHandler {
return &K8sResourceHandler{
k8s: k8s,
fieldSelector: fieldSelector,
hostSensorHandler: hostSensorHandler,
rbacObjectsAPI: rbacObjects,
registryAdaptors: registryAdaptors,
}
}
@@ -60,6 +62,11 @@ func (k8sHandler *K8sResourceHandler) GetResources(frameworks []reporthandling.F
if err := k8sHandler.pullResources(k8sResourcesMap, allResources, namespace, labels); err != nil {
return k8sResourcesMap, allResources, err
}
if err := k8sHandler.registryAdaptors.collectImagesVulnerabilities(k8sResourcesMap, allResources); err != nil {
cautils.WarningDisplay(os.Stderr, "Warning: failed to collect image vulnerabilities: %s\n", err.Error())
}
if err := k8sHandler.collectHostResources(allResources, k8sResourcesMap); err != nil {
cautils.WarningDisplay(os.Stderr, "Warning: failed to collect host sensor resources\n")
}
+155
View File
@@ -0,0 +1,155 @@
package resourcehandler
import (
"github.com/armosec/k8s-interface/k8sinterface"
"github.com/armosec/k8s-interface/workloadinterface"
"github.com/armosec/kubescape/cautils"
"github.com/armosec/kubescape/cautils/getter"
armosecadaptorv1 "github.com/armosec/kubescape/registryadaptors/armosec/v1"
"github.com/armosec/kubescape/registryadaptors/registryvulnerabilities"
"github.com/armosec/opa-utils/shared"
)
const (
ImagevulnerabilitiesObjectGroup = "image.vulnscan.com"
ImagevulnerabilitiesObjectVersion = "v1"
ImagevulnerabilitiesObjectKind = "ImageVulnerabilities"
)
type RegistryAdaptors struct {
adaptors []registryvulnerabilities.IContainerImageVulnerabilityAdaptor
}
func NewRegistryAdaptors() (*RegistryAdaptors, error) {
// list supported adaptors
registryAdaptors := &RegistryAdaptors{}
adaptors, err := listAdaptores()
if err != nil {
return registryAdaptors, err
}
registryAdaptors.adaptors = adaptors
return registryAdaptors, nil
}
func (registryAdaptors *RegistryAdaptors) collectImagesVulnerabilities(k8sResourcesMap *cautils.K8SResources, allResources map[string]workloadinterface.IMetadata) error {
// list cluster images
images := listImagesTags(k8sResourcesMap, allResources)
imagesIdentifiers := imageTagsToContainerImageIdentifier(images)
imagesVulnerability := map[string][]registryvulnerabilities.Vulnerability{}
for i := range registryAdaptors.adaptors { // login and and get vulnerabilities
if err := registryAdaptors.adaptors[i].Login(); err != nil {
return err
}
vulnerabilities, err := registryAdaptors.adaptors[i].GetImagesVulnerabilities(imagesIdentifiers)
if err != nil {
return err
}
for j := range vulnerabilities {
imagesVulnerability[vulnerabilities[j].ImageID.Tag] = vulnerabilities[j].Vulnerabilities
}
}
// convert result to IMetadata object
metaObjs := vulnerabilitiesToIMetadata(imagesVulnerability)
// save in resources map
for i := range metaObjs {
allResources[metaObjs[i].GetID()] = metaObjs[i]
}
(*k8sResourcesMap)[k8sinterface.JoinResourceTriplets(ImagevulnerabilitiesObjectGroup, ImagevulnerabilitiesObjectVersion, ImagevulnerabilitiesObjectKind)] = workloadinterface.ListMetaIDs(metaObjs)
return nil
}
func vulnerabilitiesToIMetadata(vulnerabilities map[string][]registryvulnerabilities.Vulnerability) []workloadinterface.IMetadata {
objs := []workloadinterface.IMetadata{}
for i := range vulnerabilities {
objs = append(objs, vulnerabilityToIMetadata(i, vulnerabilities[i]))
}
return objs
}
func vulnerabilityToIMetadata(imageTag string, vulnerabilities []registryvulnerabilities.Vulnerability) workloadinterface.IMetadata {
obj := map[string]interface{}{}
metadata := map[string]interface{}{}
metadata["name"] = imageTag // store image tag as object name
obj["kind"] = ImagevulnerabilitiesObjectKind
obj["apiVersion"] = k8sinterface.JoinGroupVersion(ImagevulnerabilitiesObjectGroup, ImagevulnerabilitiesObjectVersion)
obj["data"] = vulnerabilities
obj["metadata"] = metadata
return workloadinterface.NewWorkloadObj(obj)
}
// list all images tags
func listImagesTags(k8sResourcesMap *cautils.K8SResources, allResources map[string]workloadinterface.IMetadata) []string {
images := []string{}
for _, resources := range *k8sResourcesMap {
for j := range resources {
if resource, ok := allResources[resources[j]]; ok {
if resource.GetObjectType() == workloadinterface.TypeWorkloadObject {
workload := workloadinterface.NewWorkloadObj(resource.GetObject())
if contianers, err := workload.GetContainers(); err == nil {
for i := range contianers {
images = append(images, contianers[i].Image)
}
}
if contianers, err := workload.GetInitContainers(); err == nil {
for i := range contianers {
images = append(images, contianers[i].Image)
}
}
}
}
}
}
return shared.SliceStringToUnique(images)
}
func imageTagsToContainerImageIdentifier(images []string) []registryvulnerabilities.ContainerImageIdentifier {
imagesIdentifiers := make([]registryvulnerabilities.ContainerImageIdentifier, len(images))
for i := range images {
imageIdentifier := registryvulnerabilities.ContainerImageIdentifier{
Tag: images[i],
}
// splitted := strings.Split(images[i], "/")
// if len(splitted) == 1 {
// imageIdentifier.Tag = splitted[0]
// } else if len(splitted) == 2 {
// imageIdentifier.Registry = splitted[0]
// imageIdentifier.Tag = splitted[1]
// } else if len(splitted) >= 3 {
// imageIdentifier.Registry = splitted[0]
// imageIdentifier.Repository = strings.Join(splitted[1:len(splitted)-1], "/")
// imageIdentifier.Tag = splitted[len(splitted)-1]
// }
imagesIdentifiers[i] = imageIdentifier
}
return imagesIdentifiers
}
func listAdaptores() ([]registryvulnerabilities.IContainerImageVulnerabilityAdaptor, error) {
adaptors := []registryvulnerabilities.IContainerImageVulnerabilityAdaptor{}
armoAPI := getter.GetArmoAPIConnector()
if armoAPI == nil {
accountID := armoAPI.GetAccountID()
clientID := armoAPI.GetClientID()
accessKey := armoAPI.GetAccessKey()
if accountID != "" && clientID != "" && accessKey != "" {
armosecAdaptor, err := armosecadaptorv1.NewArmoAdaptor(armoAPI.GetFrontendURL(), map[string]string{"accountID": accountID, "clientID": clientID, "accessKey": accessKey})
if err != nil {
return nil, err
}
adaptors = append(adaptors, armosecAdaptor)
} else {
// TODO - print warning
}
}
return adaptors, nil
}
@@ -31,7 +31,7 @@ func NewReportEventReceiver(tenantConfig *cautils.ConfigObj) *ReportEventReceive
return &ReportEventReceiver{
httpClient: &http.Client{},
clusterName: tenantConfig.ClusterName,
customerGUID: tenantConfig.CustomerGUID,
customerGUID: tenantConfig.AccountID,
token: tenantConfig.Token,
customerAdminEMail: tenantConfig.CustomerAdminEMail,
}
@@ -32,7 +32,7 @@ func NewReportEventReceiver(tenantConfig *cautils.ConfigObj) *ReportEventReceive
return &ReportEventReceiver{
httpClient: &http.Client{},
clusterName: tenantConfig.ClusterName,
customerGUID: tenantConfig.CustomerGUID,
customerGUID: tenantConfig.AccountID,
token: tenantConfig.Token,
customerAdminEMail: tenantConfig.CustomerAdminEMail,
}