2017-08-11 16:59:57 +00:00
// Copyright © 2017 Aqua Security Software Ltd. <info@aquasec.com>
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package cmd
import (
2023-05-15 12:01:30 +00:00
"errors"
2017-08-30 17:01:53 +00:00
"os"
2018-06-29 11:19:34 +00:00
"path/filepath"
2017-08-30 17:01:53 +00:00
"reflect"
2021-05-11 08:52:24 +00:00
"sort"
2017-08-15 15:44:40 +00:00
"strconv"
2017-08-11 16:59:57 +00:00
"testing"
2017-08-30 17:01:53 +00:00
2021-05-11 08:52:24 +00:00
"github.com/magiconair/properties/assert"
2019-11-05 15:44:57 +00:00
"github.com/aquasecurity/kube-bench/check"
2017-08-30 17:01:53 +00:00
"github.com/spf13/viper"
2017-08-11 16:59:57 +00:00
)
2022-04-05 13:25:45 +00:00
var (
g string
e [ ] error
eIndex int
)
2017-08-15 15:44:40 +00:00
func fakeps ( proc string ) string {
return g
}
2017-08-30 17:01:53 +00:00
func fakestat ( file string ) ( os . FileInfo , error ) {
err := e [ eIndex ]
eIndex ++
return nil , err
}
2017-08-15 15:44:40 +00:00
func TestVerifyBin ( t * testing . T ) {
cases := [ ] struct {
proc string
psOut string
exp bool
} {
{ proc : "single" , psOut : "single" , exp : true } ,
{ proc : "single" , psOut : "" , exp : false } ,
{ proc : "two words" , psOut : "two words" , exp : true } ,
{ proc : "two words" , psOut : "" , exp : false } ,
{ proc : "cmd" , psOut : "cmd param1 param2" , exp : true } ,
{ proc : "cmd param" , psOut : "cmd param1 param2" , exp : true } ,
{ proc : "cmd param" , psOut : "cmd" , exp : false } ,
2017-08-31 15:01:31 +00:00
{ proc : "cmd" , psOut : "cmd x \ncmd y" , exp : true } ,
{ proc : "cmd y" , psOut : "cmd x \ncmd y" , exp : true } ,
{ proc : "cmd" , psOut : "/usr/bin/cmd" , exp : true } ,
{ proc : "cmd" , psOut : "kube-cmd" , exp : false } ,
{ proc : "cmd" , psOut : "/usr/bin/kube-cmd" , exp : false } ,
2017-08-15 15:44:40 +00:00
}
2017-08-30 16:51:28 +00:00
psFunc = fakeps
2017-08-15 15:44:40 +00:00
for id , c := range cases {
t . Run ( strconv . Itoa ( id ) , func ( t * testing . T ) {
g = c . psOut
2017-08-30 16:51:28 +00:00
v := verifyBin ( c . proc )
2017-08-15 15:44:40 +00:00
if v != c . exp {
t . Fatalf ( "Expected %v got %v" , c . exp , v )
}
} )
}
}
2017-08-15 16:00:35 +00:00
2017-08-30 11:07:46 +00:00
func TestFindExecutable ( t * testing . T ) {
cases := [ ] struct {
candidates [ ] string // list of executables we'd consider
psOut string // fake output from ps
exp string // the one we expect to find in the (fake) ps output
expErr bool
} {
{ candidates : [ ] string { "one" , "two" , "three" } , psOut : "two" , exp : "two" } ,
{ candidates : [ ] string { "one" , "two" , "three" } , psOut : "two three" , exp : "two" } ,
{ candidates : [ ] string { "one double" , "two double" , "three double" } , psOut : "two double is running" , exp : "two double" } ,
{ candidates : [ ] string { "one" , "two" , "three" } , psOut : "blah" , expErr : true } ,
{ candidates : [ ] string { "one double" , "two double" , "three double" } , psOut : "two" , expErr : true } ,
2017-08-30 16:51:28 +00:00
{ candidates : [ ] string { "apiserver" , "kube-apiserver" } , psOut : "kube-apiserver" , exp : "kube-apiserver" } ,
{ candidates : [ ] string { "apiserver" , "kube-apiserver" , "hyperkube-apiserver" } , psOut : "kube-apiserver" , exp : "kube-apiserver" } ,
2017-08-30 11:07:46 +00:00
}
2017-08-30 16:51:28 +00:00
psFunc = fakeps
2017-08-30 11:07:46 +00:00
for id , c := range cases {
t . Run ( strconv . Itoa ( id ) , func ( t * testing . T ) {
g = c . psOut
2017-08-30 16:51:28 +00:00
e , err := findExecutable ( c . candidates )
2017-08-30 11:07:46 +00:00
if e != c . exp {
t . Fatalf ( "Expected %v got %v" , c . exp , e )
}
if err == nil && c . expErr {
t . Fatalf ( "Expected error" )
}
if err != nil && ! c . expErr {
t . Fatalf ( "Didn't expect error: %v" , err )
}
} )
}
}
2017-08-30 17:01:53 +00:00
func TestGetBinaries ( t * testing . T ) {
cases := [ ] struct {
2019-03-07 17:02:43 +00:00
config map [ string ] interface { }
psOut string
exp map [ string ] string
expectErr bool
2017-08-30 17:01:53 +00:00
} {
{
2019-03-07 17:02:43 +00:00
config : map [ string ] interface { } { "components" : [ ] string { "apiserver" } , "apiserver" : map [ string ] interface { } { "bins" : [ ] string { "apiserver" , "kube-apiserver" } } } ,
psOut : "kube-apiserver" ,
exp : map [ string ] string { "apiserver" : "kube-apiserver" } ,
expectErr : false ,
2017-08-30 17:01:53 +00:00
} ,
{
2017-08-31 14:22:30 +00:00
// "thing" is not in the list of components
2019-03-07 17:02:43 +00:00
config : map [ string ] interface { } { "components" : [ ] string { "apiserver" } , "apiserver" : map [ string ] interface { } { "bins" : [ ] string { "apiserver" , "kube-apiserver" } } , "thing" : map [ string ] interface { } { "bins" : [ ] string { "something else" , "thing" } } } ,
psOut : "kube-apiserver thing" ,
exp : map [ string ] string { "apiserver" : "kube-apiserver" } ,
expectErr : false ,
2017-08-31 14:22:30 +00:00
} ,
{
2022-04-05 13:25:45 +00:00
// "anotherthing" in list of components but doesn't have a definition
2019-03-07 17:02:43 +00:00
config : map [ string ] interface { } { "components" : [ ] string { "apiserver" , "anotherthing" } , "apiserver" : map [ string ] interface { } { "bins" : [ ] string { "apiserver" , "kube-apiserver" } } , "thing" : map [ string ] interface { } { "bins" : [ ] string { "something else" , "thing" } } } ,
psOut : "kube-apiserver thing" ,
exp : map [ string ] string { "apiserver" : "kube-apiserver" } ,
expectErr : false ,
2017-08-31 14:22:30 +00:00
} ,
{
// more than one component
2019-03-07 17:02:43 +00:00
config : map [ string ] interface { } { "components" : [ ] string { "apiserver" , "thing" } , "apiserver" : map [ string ] interface { } { "bins" : [ ] string { "apiserver" , "kube-apiserver" } } , "thing" : map [ string ] interface { } { "bins" : [ ] string { "something else" , "thing" } } } ,
psOut : "kube-apiserver \nthing" ,
exp : map [ string ] string { "apiserver" : "kube-apiserver" , "thing" : "thing" } ,
expectErr : false ,
2017-08-30 17:01:53 +00:00
} ,
2017-08-31 14:22:30 +00:00
{
// default binary to component name
2019-03-07 17:02:43 +00:00
config : map [ string ] interface { } { "components" : [ ] string { "apiserver" , "thing" } , "apiserver" : map [ string ] interface { } { "bins" : [ ] string { "apiserver" , "kube-apiserver" } } , "thing" : map [ string ] interface { } { "bins" : [ ] string { "something else" , "thing" } , "optional" : true } } ,
psOut : "kube-apiserver \notherthing some params" ,
exp : map [ string ] string { "apiserver" : "kube-apiserver" , "thing" : "thing" } ,
expectErr : false ,
} ,
{
// missing mandatory component
config : map [ string ] interface { } { "components" : [ ] string { "apiserver" , "thing" } , "apiserver" : map [ string ] interface { } { "bins" : [ ] string { "apiserver" , "kube-apiserver" } } , "thing" : map [ string ] interface { } { "bins" : [ ] string { "something else" , "thing" } , "optional" : true } } ,
psOut : "otherthing some params" ,
exp : map [ string ] string { "apiserver" : "kube-apiserver" , "thing" : "thing" } ,
expectErr : true ,
2017-08-31 14:22:30 +00:00
} ,
2017-08-30 17:01:53 +00:00
}
v := viper . New ( )
psFunc = fakeps
for id , c := range cases {
t . Run ( strconv . Itoa ( id ) , func ( t * testing . T ) {
g = c . psOut
for k , val := range c . config {
v . Set ( k , val )
}
2019-11-05 15:44:57 +00:00
m , err := getBinaries ( v , check . MASTER )
2019-03-07 17:02:43 +00:00
if c . expectErr {
if err == nil {
t . Fatal ( "Got nil Expected error" )
}
} else if ! reflect . DeepEqual ( m , c . exp ) {
2017-08-30 17:01:53 +00:00
t . Fatalf ( "Got %v\nExpected %v" , m , c . exp )
}
} )
}
}
2017-08-15 16:00:35 +00:00
func TestMultiWordReplace ( t * testing . T ) {
cases := [ ] struct {
input string
sub string
subname string
output string
} {
{ input : "Here's a file with no substitutions" , sub : "blah" , subname : "blah" , output : "Here's a file with no substitutions" } ,
{ input : "Here's a file with a substitution" , sub : "blah" , subname : "substitution" , output : "Here's a file with a blah" } ,
{ input : "Here's a file with multi-word substitutions" , sub : "multi word" , subname : "multi-word" , output : "Here's a file with 'multi word' substitutions" } ,
{ input : "Here's a file with several several substitutions several" , sub : "blah" , subname : "several" , output : "Here's a file with blah blah substitutions blah" } ,
}
for id , c := range cases {
t . Run ( strconv . Itoa ( id ) , func ( t * testing . T ) {
s := multiWordReplace ( c . input , c . subname , c . sub )
if s != c . output {
t . Fatalf ( "Expected %s got %s" , c . output , s )
}
} )
}
}
2017-09-17 14:35:25 +00:00
2020-12-21 11:18:54 +00:00
func Test_getVersionFromKubectlOutput ( t * testing . T ) {
ver := getVersionFromKubectlOutput ( ` {
"serverVersion" : {
"major" : "1" ,
"minor" : "8" ,
"gitVersion" : "v1.8.0"
}
} ` )
if ver . BaseVersion ( ) != "1.8" {
t . Fatalf ( "Expected 1.8 got %s" , ver . BaseVersion ( ) )
2017-11-21 13:19:09 +00:00
}
2017-09-20 00:39:30 +00:00
2017-11-21 13:19:09 +00:00
ver = getVersionFromKubectlOutput ( "Something completely different" )
2020-12-21 11:18:54 +00:00
if ver . BaseVersion ( ) != defaultKubeVersion {
t . Fatalf ( "Expected %s got %s" , defaultKubeVersion , ver . BaseVersion ( ) )
2017-09-20 00:39:30 +00:00
}
}
2017-08-30 17:01:53 +00:00
func TestFindConfigFile ( t * testing . T ) {
cases := [ ] struct {
input [ ] string
statResults [ ] error
exp string
} {
{ input : [ ] string { "myfile" } , statResults : [ ] error { nil } , exp : "myfile" } ,
{ input : [ ] string { "thisfile" , "thatfile" } , statResults : [ ] error { os . ErrNotExist , nil } , exp : "thatfile" } ,
{ input : [ ] string { "thisfile" , "thatfile" } , statResults : [ ] error { os . ErrNotExist , os . ErrNotExist } , exp : "" } ,
2023-05-15 12:01:30 +00:00
{ input : [ ] string { "thisfile" , "/etc/dummy/thatfile" } , statResults : [ ] error { os . ErrNotExist , errors . New ( "stat /etc/dummy/thatfile: not a directory" ) } , exp : "" } ,
2017-08-30 17:01:53 +00:00
}
statFunc = fakestat
for id , c := range cases {
t . Run ( strconv . Itoa ( id ) , func ( t * testing . T ) {
e = c . statResults
eIndex = 0
conf := findConfigFile ( c . input )
if conf != c . exp {
t . Fatalf ( "Got %s expected %s" , conf , c . exp )
}
} )
}
}
func TestGetConfigFiles ( t * testing . T ) {
cases := [ ] struct {
config map [ string ] interface { }
exp map [ string ] string
statResults [ ] error
} {
{
2017-08-31 14:22:30 +00:00
config : map [ string ] interface { } { "components" : [ ] string { "apiserver" } , "apiserver" : map [ string ] interface { } { "confs" : [ ] string { "apiserver" , "kube-apiserver" } } } ,
statResults : [ ] error { os . ErrNotExist , nil } ,
exp : map [ string ] string { "apiserver" : "kube-apiserver" } ,
} ,
{
// Component "thing" isn't included in the list of components
config : map [ string ] interface { } {
"components" : [ ] string { "apiserver" } ,
"apiserver" : map [ string ] interface { } { "confs" : [ ] string { "apiserver" , "kube-apiserver" } } ,
2022-04-05 13:25:45 +00:00
"thing" : map [ string ] interface { } { "confs" : [ ] string { "/my/file/thing" } } ,
} ,
2017-08-30 17:01:53 +00:00
statResults : [ ] error { os . ErrNotExist , nil } ,
exp : map [ string ] string { "apiserver" : "kube-apiserver" } ,
} ,
{
2017-08-31 14:22:30 +00:00
// More than one component
config : map [ string ] interface { } {
"components" : [ ] string { "apiserver" , "thing" } ,
"apiserver" : map [ string ] interface { } { "confs" : [ ] string { "apiserver" , "kube-apiserver" } } ,
2022-04-05 13:25:45 +00:00
"thing" : map [ string ] interface { } { "confs" : [ ] string { "/my/file/thing" } } ,
} ,
2017-08-30 17:01:53 +00:00
statResults : [ ] error { os . ErrNotExist , nil , nil } ,
exp : map [ string ] string { "apiserver" : "kube-apiserver" , "thing" : "/my/file/thing" } ,
} ,
2017-08-31 14:22:30 +00:00
{
// Default thing to specified default config
config : map [ string ] interface { } {
"components" : [ ] string { "apiserver" , "thing" } ,
"apiserver" : map [ string ] interface { } { "confs" : [ ] string { "apiserver" , "kube-apiserver" } } ,
2022-04-05 13:25:45 +00:00
"thing" : map [ string ] interface { } { "confs" : [ ] string { "/my/file/thing" } , "defaultconf" : "another/thing" } ,
} ,
2017-08-31 14:22:30 +00:00
statResults : [ ] error { os . ErrNotExist , nil , os . ErrNotExist } ,
exp : map [ string ] string { "apiserver" : "kube-apiserver" , "thing" : "another/thing" } ,
} ,
{
// Default thing to component name
config : map [ string ] interface { } {
"components" : [ ] string { "apiserver" , "thing" } ,
"apiserver" : map [ string ] interface { } { "confs" : [ ] string { "apiserver" , "kube-apiserver" } } ,
2022-04-05 13:25:45 +00:00
"thing" : map [ string ] interface { } { "confs" : [ ] string { "/my/file/thing" } } ,
} ,
2017-08-31 14:22:30 +00:00
statResults : [ ] error { os . ErrNotExist , nil , os . ErrNotExist } ,
exp : map [ string ] string { "apiserver" : "kube-apiserver" , "thing" : "thing" } ,
} ,
2017-08-30 17:01:53 +00:00
}
v := viper . New ( )
statFunc = fakestat
for id , c := range cases {
t . Run ( strconv . Itoa ( id ) , func ( t * testing . T ) {
for k , val := range c . config {
v . Set ( k , val )
}
e = c . statResults
eIndex = 0
2019-07-13 06:48:24 +00:00
m := getFiles ( v , "config" )
2017-08-30 17:01:53 +00:00
if ! reflect . DeepEqual ( m , c . exp ) {
t . Fatalf ( "Got %v\nExpected %v" , m , c . exp )
}
} )
}
}
2017-08-30 17:37:01 +00:00
2018-10-23 02:26:38 +00:00
func TestGetServiceFiles ( t * testing . T ) {
cases := [ ] struct {
config map [ string ] interface { }
exp map [ string ] string
statResults [ ] error
} {
{
config : map [ string ] interface { } {
"components" : [ ] string { "kubelet" } ,
"kubelet" : map [ string ] interface { } { "svc" : [ ] string { "kubelet" , "10-kubeadm.conf" } } ,
} ,
statResults : [ ] error { os . ErrNotExist , nil } ,
exp : map [ string ] string { "kubelet" : "10-kubeadm.conf" } ,
} ,
{
// Component "thing" isn't included in the list of components
config : map [ string ] interface { } {
"components" : [ ] string { "kubelet" } ,
"kubelet" : map [ string ] interface { } { "svc" : [ ] string { "kubelet" , "10-kubeadm.conf" } } ,
"thing" : map [ string ] interface { } { "svc" : [ ] string { "/my/file/thing" } } ,
} ,
statResults : [ ] error { os . ErrNotExist , nil } ,
exp : map [ string ] string { "kubelet" : "10-kubeadm.conf" } ,
} ,
{
// More than one component
config : map [ string ] interface { } {
"components" : [ ] string { "kubelet" , "thing" } ,
"kubelet" : map [ string ] interface { } { "svc" : [ ] string { "kubelet" , "10-kubeadm.conf" } } ,
"thing" : map [ string ] interface { } { "svc" : [ ] string { "/my/file/thing" } } ,
} ,
statResults : [ ] error { os . ErrNotExist , nil , nil } ,
exp : map [ string ] string { "kubelet" : "10-kubeadm.conf" , "thing" : "/my/file/thing" } ,
} ,
{
// Default thing to specified default service
config : map [ string ] interface { } {
"components" : [ ] string { "kubelet" , "thing" } ,
"kubelet" : map [ string ] interface { } { "svc" : [ ] string { "kubelet" , "10-kubeadm.conf" } } ,
"thing" : map [ string ] interface { } { "svc" : [ ] string { "/my/file/thing" } , "defaultsvc" : "another/thing" } ,
} ,
statResults : [ ] error { os . ErrNotExist , nil , os . ErrNotExist } ,
exp : map [ string ] string { "kubelet" : "10-kubeadm.conf" , "thing" : "another/thing" } ,
} ,
{
// Default thing to component name
config : map [ string ] interface { } {
"components" : [ ] string { "kubelet" , "thing" } ,
"kubelet" : map [ string ] interface { } { "svc" : [ ] string { "kubelet" , "10-kubeadm.conf" } } ,
"thing" : map [ string ] interface { } { "svc" : [ ] string { "/my/file/thing" } } ,
} ,
statResults : [ ] error { os . ErrNotExist , nil , os . ErrNotExist } ,
exp : map [ string ] string { "kubelet" : "10-kubeadm.conf" , "thing" : "thing" } ,
} ,
}
v := viper . New ( )
statFunc = fakestat
for id , c := range cases {
t . Run ( strconv . Itoa ( id ) , func ( t * testing . T ) {
for k , val := range c . config {
v . Set ( k , val )
}
e = c . statResults
eIndex = 0
2019-07-13 06:48:24 +00:00
m := getFiles ( v , "service" )
2018-10-23 02:26:38 +00:00
if ! reflect . DeepEqual ( m , c . exp ) {
t . Fatalf ( "Got %v\nExpected %v" , m , c . exp )
}
} )
}
}
2022-11-25 13:32:49 +00:00
func TestGetDatadirFiles ( t * testing . T ) {
var err error
2023-12-05 08:52:24 +00:00
datadir , err := os . MkdirTemp ( "" , "kube-bench-test-etcd-data-dir" )
2022-11-25 13:32:49 +00:00
if err != nil {
t . Fatalf ( "Failed to create temp directory" )
}
defer os . RemoveAll ( datadir )
cases := [ ] struct {
config map [ string ] interface { }
exp map [ string ] string
statResults [ ] error
} {
{
config : map [ string ] interface { } {
"components" : [ ] string { "etcd" } ,
"etcd" : map [ string ] interface { } { "datadirs" : [ ] string { datadir } ,
"defaultdatadir" : "/var/lib/etcd/default.etcd" } ,
} ,
statResults : [ ] error { nil } ,
exp : map [ string ] string { "etcd" : datadir } ,
} ,
// fallback to defaultdatadir
{
config : map [ string ] interface { } {
"components" : [ ] string { "etcd" } ,
"etcd" : map [ string ] interface { } { "datadirs" : [ ] string { "/path/to/etcd/data.etcd" } ,
"defaultdatadir" : "/var/lib/etcd/default.etcd" } ,
} ,
statResults : [ ] error { os . ErrNotExist } ,
exp : map [ string ] string { "etcd" : "/var/lib/etcd/default.etcd" } ,
} ,
}
v := viper . New ( )
statFunc = fakestat
for id , c := range cases {
t . Run ( strconv . Itoa ( id ) , func ( t * testing . T ) {
for k , val := range c . config {
v . Set ( k , val )
}
e = c . statResults
eIndex = 0
m := getFiles ( v , "datadir" )
if ! reflect . DeepEqual ( m , c . exp ) {
t . Fatalf ( "Got %v\nExpected %v" , m , c . exp )
}
} )
}
}
2017-08-30 17:37:01 +00:00
func TestMakeSubsitutions ( t * testing . T ) {
cases := [ ] struct {
2021-05-11 08:52:24 +00:00
input string
subst map [ string ] string
exp string
2020-12-21 11:18:54 +00:00
expectedSubs [ ] string
2017-08-30 17:37:01 +00:00
} {
2020-12-21 11:18:54 +00:00
{ input : "Replace $thisbin" , subst : map [ string ] string { "this" : "that" } , exp : "Replace that" , expectedSubs : [ ] string { "that" } } ,
{ input : "Replace $thisbin" , subst : map [ string ] string { "this" : "that" , "here" : "there" } , exp : "Replace that" , expectedSubs : [ ] string { "that" } } ,
{ input : "Replace $thisbin and $herebin" , subst : map [ string ] string { "this" : "that" , "here" : "there" } , exp : "Replace that and there" , expectedSubs : [ ] string { "that" , "there" } } ,
2017-08-30 17:37:01 +00:00
}
for _ , c := range cases {
t . Run ( c . input , func ( t * testing . T ) {
2020-12-21 11:18:54 +00:00
s , subs := makeSubstitutions ( c . input , "bin" , c . subst )
2017-08-30 17:37:01 +00:00
if s != c . exp {
t . Fatalf ( "Got %s expected %s" , s , c . exp )
}
2021-05-11 08:52:24 +00:00
sort . Strings ( subs )
2020-12-21 11:18:54 +00:00
assert . Equal ( t , c . expectedSubs , subs )
2017-08-30 17:37:01 +00:00
} )
2017-09-17 14:35:25 +00:00
}
}
2018-06-29 11:19:34 +00:00
func TestGetConfigFilePath ( t * testing . T ) {
var err error
2023-12-05 08:52:24 +00:00
cfgDir , err = os . MkdirTemp ( "" , "kube-bench-test" )
2018-06-29 11:19:34 +00:00
if err != nil {
t . Fatalf ( "Failed to create temp directory" )
}
defer os . RemoveAll ( cfgDir )
2019-11-05 21:31:27 +00:00
d := filepath . Join ( cfgDir , "cis-1.4" )
2019-12-02 15:40:44 +00:00
err = os . Mkdir ( d , 0766 )
2018-06-29 11:19:34 +00:00
if err != nil {
2019-12-02 15:40:44 +00:00
t . Fatalf ( "Failed to create temp dir" )
}
2023-12-05 08:52:24 +00:00
err = os . WriteFile ( filepath . Join ( d , "master.yaml" ) , [ ] byte ( "hello world" ) , 0666 )
2019-12-02 15:40:44 +00:00
if err != nil {
t . Logf ( "Failed to create temp file" )
2018-06-29 11:19:34 +00:00
}
cases := [ ] struct {
2019-11-05 21:31:27 +00:00
benchmarkVersion string
2018-06-29 11:19:34 +00:00
succeed bool
exp string
} {
2019-11-05 21:31:27 +00:00
{ benchmarkVersion : "cis-1.4" , succeed : true , exp : d } ,
{ benchmarkVersion : "cis-1.5" , succeed : false , exp : "" } ,
{ benchmarkVersion : "1.1" , succeed : false , exp : "" } ,
2018-06-29 11:19:34 +00:00
}
for _ , c := range cases {
2019-11-05 21:31:27 +00:00
t . Run ( c . benchmarkVersion , func ( t * testing . T ) {
path , err := getConfigFilePath ( c . benchmarkVersion , "/master.yaml" )
if c . succeed {
if err != nil {
t . Fatalf ( "Error %v" , err )
}
if path != c . exp {
t . Fatalf ( "Got %s expected %s" , path , c . exp )
}
} else {
if err == nil {
t . Fatalf ( "Expected Error, but none" )
}
2018-06-29 11:19:34 +00:00
}
} )
}
}
2019-11-05 21:31:27 +00:00
func TestDecrementVersion ( t * testing . T ) {
cases := [ ] struct {
kubeVersion string
succeed bool
exp string
} {
{ kubeVersion : "1.13" , succeed : true , exp : "1.12" } ,
{ kubeVersion : "1.15" , succeed : true , exp : "1.14" } ,
{ kubeVersion : "1.11" , succeed : true , exp : "1.10" } ,
{ kubeVersion : "1.1" , succeed : true , exp : "" } ,
{ kubeVersion : "invalid" , succeed : false , exp : "" } ,
}
for _ , c := range cases {
rv := decrementVersion ( c . kubeVersion )
if c . succeed {
if c . exp != rv {
t . Fatalf ( "decrementVersion(%q) - Got %q expected %s" , c . kubeVersion , rv , c . exp )
}
} else {
if len ( rv ) > 0 {
t . Fatalf ( "decrementVersion(%q) - Expected empty string but Got %s" , c . kubeVersion , rv )
}
}
}
}
2019-12-02 15:40:44 +00:00
func TestGetYamlFilesFromDir ( t * testing . T ) {
2023-12-05 08:52:24 +00:00
cfgDir , err := os . MkdirTemp ( "" , "kube-bench-test" )
2019-12-02 15:40:44 +00:00
if err != nil {
t . Fatalf ( "Failed to create temp directory" )
}
defer os . RemoveAll ( cfgDir )
d := filepath . Join ( cfgDir , "cis-1.4" )
err = os . Mkdir ( d , 0766 )
if err != nil {
t . Fatalf ( "Failed to create temp dir" )
}
2023-12-05 08:52:24 +00:00
err = os . WriteFile ( filepath . Join ( d , "something.yaml" ) , [ ] byte ( "hello world" ) , 0666 )
2019-12-02 15:40:44 +00:00
if err != nil {
t . Fatalf ( "error writing file %v" , err )
}
2023-12-05 08:52:24 +00:00
err = os . WriteFile ( filepath . Join ( d , "config.yaml" ) , [ ] byte ( "hello world" ) , 0666 )
2019-12-02 15:40:44 +00:00
if err != nil {
t . Fatalf ( "error writing file %v" , err )
}
files , err := getYamlFilesFromDir ( d )
if err != nil {
t . Fatalf ( "Unexpected error: %v" , err )
}
if len ( files ) != 1 {
t . Fatalf ( "Expected to find one file, found %d" , len ( files ) )
}
if files [ 0 ] != filepath . Join ( d , "something.yaml" ) {
t . Fatalf ( "Expected to find something.yaml, found %s" , files [ 0 ] )
}
}
2020-12-21 11:18:54 +00:00
func Test_getPlatformNameFromKubectlOutput ( t * testing . T ) {
type args struct {
s string
}
tests := [ ] struct {
name string
args args
2022-01-10 13:25:15 +00:00
want Platform
2020-12-21 11:18:54 +00:00
} {
{
name : "eks" ,
args : args { s : "v1.17.9-eks-4c6976" } ,
2022-01-10 13:25:15 +00:00
want : Platform { Name : "eks" , Version : "1.17" } ,
2020-12-21 11:18:54 +00:00
} ,
{
name : "gke" ,
args : args { s : "v1.17.6-gke.1" } ,
2022-01-10 13:25:15 +00:00
want : Platform { Name : "gke" , Version : "1.17" } ,
2020-12-21 11:18:54 +00:00
} ,
2021-05-11 08:52:24 +00:00
{
name : "ack" ,
args : args { s : "v1.18.8-aliyun.1" } ,
2022-01-10 13:25:15 +00:00
want : Platform { Name : "aliyun" , Version : "1.18" } ,
2021-05-11 08:52:24 +00:00
} ,
2020-12-21 11:18:54 +00:00
{
name : "unknown" ,
args : args { s : "v1.17.6" } ,
2022-01-10 13:25:15 +00:00
want : Platform { } ,
2020-12-21 11:18:54 +00:00
} ,
{
name : "empty string" ,
args : args { s : "" } ,
2022-01-10 13:25:15 +00:00
want : Platform { } ,
2020-12-21 11:18:54 +00:00
} ,
Add CIS Benchmarks support to Rancher Distributions RKE/RKE2/K3s (#1523)
* add Support VMware Tanzu(TKGI) Benchmarks v1.2.53
with this change, we are adding
1. latest kubernetes cis benchmarks for VMware Tanzu1.2.53
2. logic to kube-bench so that kube-bench can auto detect vmware platform, will be able to execute the respective vmware tkgi compliance checks.
3. job-tkgi.yaml file to run the benchmark as a job in tkgi cluster
Reference Document for checks: https://network.pivotal.io/products/p-compliance-scanner/#/releases/1248397
* add Support VMware Tanzu(TKGI) Benchmarks v1.2.53
with this change, we are adding
1. latest kubernetes cis benchmarks for VMware Tanzu1.2.53
2. logic to kube-bench so that kube-bench can auto detect vmware platform, will be able to execute the respective vmware tkgi compliance checks.
3. job-tkgi.yaml file to run the benchmark as a job in tkgi cluster
Reference Document for checks: https://network.pivotal.io/products/p-compliance-scanner/#/releases/1248397
* release: prepare v0.6.15 (#1455)
Signed-off-by: chenk <hen.keinan@gmail.com>
* build(deps): bump golang from 1.19.4 to 1.20.4 (#1436)
Bumps golang from 1.19.4 to 1.20.4.
---
updated-dependencies:
- dependency-name: golang
dependency-type: direct:production
update-type: version-update:semver-minor
...
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
* build(deps): bump actions/setup-go from 3 to 4 (#1402)
Bumps [actions/setup-go](https://github.com/actions/setup-go) from 3 to 4.
- [Release notes](https://github.com/actions/setup-go/releases)
- [Commits](https://github.com/actions/setup-go/compare/v3...v4)
---
updated-dependencies:
- dependency-name: actions/setup-go
dependency-type: direct:production
update-type: version-update:semver-major
...
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: chenk <hen.keinan@gmail.com>
* Fix test_items in cis-1.7 - node - 4.2.12 (#1469)
Related issue: https://github.com/aquasecurity/kube-bench/issues/1468
* Fix node.yaml - 4.1.7 and 4.1.8 audit by adding uniq (#1472)
* chore: add fips compliant images (#1473)
For fips complaince we need to generate fips compliant images.
As part of this change, we will create new kube-bench image which will be fips compliant. Image name follows this tag pattern <version>-ubi-fips
* release: prepare v0.6.16-rc (#1476)
* release: prepare v0.6.16-rc
Signed-off-by: chenk <hen.keinan@gmail.com>
* release: prepare v0.6.16-rc
Signed-off-by: chenk <hen.keinan@gmail.com>
---------
Signed-off-by: chenk <hen.keinan@gmail.com>
* release: prepare v0.6.16 official (#1479)
Signed-off-by: chenk <hen.keinan@gmail.com>
* Update job.yaml (#1477)
* Update job.yaml
Fix on typo for image version
* chore: sync with upstream
Signed-off-by: chenk <hen.keinan@gmail.com>
---------
Signed-off-by: chenk <hen.keinan@gmail.com>
Co-authored-by: chenk <hen.keinan@gmail.com>
* release: prepare v0.6.17 (#1480)
Signed-off-by: chenk <hen.keinan@gmail.com>
* Bump docker base images (#1465)
During a recent CVE scan we found kube-bench to use `alpine:3.18` as the final image which has a known high CVE.
```
grype aquasec/kube-bench:v0.6.15
✔ Vulnerability DB [no update available]
✔ Loaded image
✔ Parsed image
✔ Cataloged packages [73 packages]
✔ Scanning image... [4 vulnerabilities]
├── 0 critical, 4 high, 0 medium, 0 low, 0 negligible
└── 4 fixed
NAME INSTALLED FIXED-IN TYPE VULNERABILITY SEVERITY
libcrypto3 3.1.0-r4 3.1.1-r0 apk CVE-2023-2650 High
libssl3 3.1.0-r4 3.1.1-r0 apk CVE-2023-2650 High
openssl 3.1.0-r4 3.1.1-r0 apk CVE-2023-2650 High
```
The CVE in question was addressed in the latest [alpine release](https://www.alpinelinux.org/posts/Alpine-3.15.9-3.16.6-3.17.4-3.18.2-released.html), hence updating the dockerfiles accordingly
* build(deps): bump golang from 1.20.4 to 1.20.6 (#1475)
Bumps golang from 1.20.4 to 1.20.6.
---
updated-dependencies:
- dependency-name: golang
dependency-type: direct:production
update-type: version-update:semver-patch
...
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
* Add CIS Benchmarks support to Rancher Distributions RKE/RKE2/K3s
Based on the information furnished in https://ranchermanager.docs.rancher.com/v2.7/pages-for-subheaders/rancher-hardening-guides
kube-bench executes CIS-1.23 (Kubernetes v1.23) , CIS-1.24(Kubernetes v1.24),CIS-1.7 (Kubernetes v1.25,v1.26,v1.27) CIS Benchmarks of respective distributions.
* RKE/RKE2 CIS Benchmarks
Updated the order of checks for RKE and RKE2 Platforms.
* fixed vulnerabilities|upgraded package golang.org/x/net to version v0.17.0
* Error handling for RKE Detection Pre-requisites
* Based on the information furnished in https://ranchermanager.docs.rancher.com/v2.7/pages-for-subheaders/rancher-hardening-guides#hardening-guides-and-benchmark-versions, kube-bench executes CIS-1.23 (Kubernetes v1.23) , CIS-1.24(Kubernetes v1.24),CIS-1.7 (Kubernetes v1.25,v1.26,v1.27) CIS Benchmarks of respective distributions.
updated documentation specific to added rancher platforms
* addressed review comments
1.Implemented IsRKE functionality in kube-bench
2. Removed containerd from global level config and accommodated in individual config file
3. Corrected the control id from 1.2.25 to 1.2.23 in master.yaml(k3s-cis-1.23 and k3s-cis-1.24)
* Removed unncessary dependency - kubernetes-provider-detector
---------
Signed-off-by: chenk <hen.keinan@gmail.com>
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: chenk <hen.keinan@gmail.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Andy Pitcher <andy.pitcher@suse.com>
Co-authored-by: Devendra Turkar <devendra.turkar@gmail.com>
Co-authored-by: Guille Vigil <contact@guillermotti.com>
Co-authored-by: Jonas-Taha El Sesiy <jonas-taha.elsesiy@snowflake.com>
2023-11-26 10:27:38 +00:00
{
name : "k3s" ,
args : args { s : "v1.27.6+k3s1" } ,
want : Platform { Name : "k3s" , Version : "1.27" } ,
} ,
{
name : "rancher1" ,
args : args { s : "v1.25.13-rancher1-1" } ,
want : Platform { Name : "rancher1" , Version : "1.25" } ,
} ,
{
name : "rke2" ,
args : args { s : "v1.27.6+rke2r1" } ,
want : Platform { Name : "rke2r" , Version : "1.27" } ,
} ,
2020-12-21 11:18:54 +00:00
}
for _ , tt := range tests {
t . Run ( tt . name , func ( t * testing . T ) {
2022-01-10 13:25:15 +00:00
got := getPlatformInfoFromVersion ( tt . args . s )
assert . Equal ( t , tt . want , got )
2020-12-21 11:18:54 +00:00
} )
}
}
func Test_getPlatformBenchmarkVersion ( t * testing . T ) {
type args struct {
2022-01-10 13:25:15 +00:00
platform Platform
2020-12-21 11:18:54 +00:00
}
tests := [ ] struct {
name string
args args
want string
} {
{
name : "eks" ,
args : args {
2022-01-10 13:25:15 +00:00
platform : Platform { Name : "eks" } ,
2020-12-21 11:18:54 +00:00
} ,
2023-05-21 14:53:58 +00:00
want : "eks-1.2.0" ,
2020-12-21 11:18:54 +00:00
} ,
{
2022-01-10 13:25:15 +00:00
name : "gke 1.19" ,
args : args {
platform : Platform { Name : "gke" , Version : "1.19" } ,
} ,
want : "gke-1.0" ,
} ,
{
name : "gke 1.20" ,
args : args {
platform : Platform { Name : "gke" , Version : "1.20" } ,
} ,
want : "gke-1.2.0" ,
} ,
{
name : "gke 1.22" ,
2020-12-21 11:18:54 +00:00
args : args {
2022-01-10 13:25:15 +00:00
platform : Platform { Name : "gke" , Version : "1.22" } ,
2020-12-21 11:18:54 +00:00
} ,
2021-12-09 10:04:38 +00:00
want : "gke-1.2.0" ,
2020-12-21 11:18:54 +00:00
} ,
2021-05-11 08:52:24 +00:00
{
name : "aliyun" ,
args : args {
2022-01-10 13:25:15 +00:00
platform : Platform { Name : "aliyun" } ,
2021-05-11 08:52:24 +00:00
} ,
want : "ack-1.0" ,
} ,
2020-12-21 11:18:54 +00:00
{
name : "unknown" ,
args : args {
2022-01-10 13:25:15 +00:00
platform : Platform { Name : "rh" } ,
2020-12-21 11:18:54 +00:00
} ,
want : "" ,
} ,
{
name : "empty" ,
args : args {
2022-01-10 13:25:15 +00:00
platform : Platform { } ,
2020-12-21 11:18:54 +00:00
} ,
want : "" ,
} ,
2021-03-24 16:06:54 +00:00
{
2021-04-29 14:08:41 +00:00
name : "openshift3" ,
2021-03-24 16:06:54 +00:00
args : args {
2022-01-10 13:25:15 +00:00
platform : Platform { Name : "ocp" , Version : "3.10" } ,
2021-03-24 16:06:54 +00:00
} ,
want : "rh-0.7" ,
} ,
2021-04-29 14:08:41 +00:00
{
name : "openshift4" ,
args : args {
2022-01-10 13:25:15 +00:00
platform : Platform { Name : "ocp" , Version : "4.1" } ,
2021-04-29 14:08:41 +00:00
} ,
want : "rh-1.0" ,
} ,
Add CIS Benchmarks support to Rancher Distributions RKE/RKE2/K3s (#1523)
* add Support VMware Tanzu(TKGI) Benchmarks v1.2.53
with this change, we are adding
1. latest kubernetes cis benchmarks for VMware Tanzu1.2.53
2. logic to kube-bench so that kube-bench can auto detect vmware platform, will be able to execute the respective vmware tkgi compliance checks.
3. job-tkgi.yaml file to run the benchmark as a job in tkgi cluster
Reference Document for checks: https://network.pivotal.io/products/p-compliance-scanner/#/releases/1248397
* add Support VMware Tanzu(TKGI) Benchmarks v1.2.53
with this change, we are adding
1. latest kubernetes cis benchmarks for VMware Tanzu1.2.53
2. logic to kube-bench so that kube-bench can auto detect vmware platform, will be able to execute the respective vmware tkgi compliance checks.
3. job-tkgi.yaml file to run the benchmark as a job in tkgi cluster
Reference Document for checks: https://network.pivotal.io/products/p-compliance-scanner/#/releases/1248397
* release: prepare v0.6.15 (#1455)
Signed-off-by: chenk <hen.keinan@gmail.com>
* build(deps): bump golang from 1.19.4 to 1.20.4 (#1436)
Bumps golang from 1.19.4 to 1.20.4.
---
updated-dependencies:
- dependency-name: golang
dependency-type: direct:production
update-type: version-update:semver-minor
...
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
* build(deps): bump actions/setup-go from 3 to 4 (#1402)
Bumps [actions/setup-go](https://github.com/actions/setup-go) from 3 to 4.
- [Release notes](https://github.com/actions/setup-go/releases)
- [Commits](https://github.com/actions/setup-go/compare/v3...v4)
---
updated-dependencies:
- dependency-name: actions/setup-go
dependency-type: direct:production
update-type: version-update:semver-major
...
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: chenk <hen.keinan@gmail.com>
* Fix test_items in cis-1.7 - node - 4.2.12 (#1469)
Related issue: https://github.com/aquasecurity/kube-bench/issues/1468
* Fix node.yaml - 4.1.7 and 4.1.8 audit by adding uniq (#1472)
* chore: add fips compliant images (#1473)
For fips complaince we need to generate fips compliant images.
As part of this change, we will create new kube-bench image which will be fips compliant. Image name follows this tag pattern <version>-ubi-fips
* release: prepare v0.6.16-rc (#1476)
* release: prepare v0.6.16-rc
Signed-off-by: chenk <hen.keinan@gmail.com>
* release: prepare v0.6.16-rc
Signed-off-by: chenk <hen.keinan@gmail.com>
---------
Signed-off-by: chenk <hen.keinan@gmail.com>
* release: prepare v0.6.16 official (#1479)
Signed-off-by: chenk <hen.keinan@gmail.com>
* Update job.yaml (#1477)
* Update job.yaml
Fix on typo for image version
* chore: sync with upstream
Signed-off-by: chenk <hen.keinan@gmail.com>
---------
Signed-off-by: chenk <hen.keinan@gmail.com>
Co-authored-by: chenk <hen.keinan@gmail.com>
* release: prepare v0.6.17 (#1480)
Signed-off-by: chenk <hen.keinan@gmail.com>
* Bump docker base images (#1465)
During a recent CVE scan we found kube-bench to use `alpine:3.18` as the final image which has a known high CVE.
```
grype aquasec/kube-bench:v0.6.15
✔ Vulnerability DB [no update available]
✔ Loaded image
✔ Parsed image
✔ Cataloged packages [73 packages]
✔ Scanning image... [4 vulnerabilities]
├── 0 critical, 4 high, 0 medium, 0 low, 0 negligible
└── 4 fixed
NAME INSTALLED FIXED-IN TYPE VULNERABILITY SEVERITY
libcrypto3 3.1.0-r4 3.1.1-r0 apk CVE-2023-2650 High
libssl3 3.1.0-r4 3.1.1-r0 apk CVE-2023-2650 High
openssl 3.1.0-r4 3.1.1-r0 apk CVE-2023-2650 High
```
The CVE in question was addressed in the latest [alpine release](https://www.alpinelinux.org/posts/Alpine-3.15.9-3.16.6-3.17.4-3.18.2-released.html), hence updating the dockerfiles accordingly
* build(deps): bump golang from 1.20.4 to 1.20.6 (#1475)
Bumps golang from 1.20.4 to 1.20.6.
---
updated-dependencies:
- dependency-name: golang
dependency-type: direct:production
update-type: version-update:semver-patch
...
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
* Add CIS Benchmarks support to Rancher Distributions RKE/RKE2/K3s
Based on the information furnished in https://ranchermanager.docs.rancher.com/v2.7/pages-for-subheaders/rancher-hardening-guides
kube-bench executes CIS-1.23 (Kubernetes v1.23) , CIS-1.24(Kubernetes v1.24),CIS-1.7 (Kubernetes v1.25,v1.26,v1.27) CIS Benchmarks of respective distributions.
* RKE/RKE2 CIS Benchmarks
Updated the order of checks for RKE and RKE2 Platforms.
* fixed vulnerabilities|upgraded package golang.org/x/net to version v0.17.0
* Error handling for RKE Detection Pre-requisites
* Based on the information furnished in https://ranchermanager.docs.rancher.com/v2.7/pages-for-subheaders/rancher-hardening-guides#hardening-guides-and-benchmark-versions, kube-bench executes CIS-1.23 (Kubernetes v1.23) , CIS-1.24(Kubernetes v1.24),CIS-1.7 (Kubernetes v1.25,v1.26,v1.27) CIS Benchmarks of respective distributions.
updated documentation specific to added rancher platforms
* addressed review comments
1.Implemented IsRKE functionality in kube-bench
2. Removed containerd from global level config and accommodated in individual config file
3. Corrected the control id from 1.2.25 to 1.2.23 in master.yaml(k3s-cis-1.23 and k3s-cis-1.24)
* Removed unncessary dependency - kubernetes-provider-detector
---------
Signed-off-by: chenk <hen.keinan@gmail.com>
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: chenk <hen.keinan@gmail.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Andy Pitcher <andy.pitcher@suse.com>
Co-authored-by: Devendra Turkar <devendra.turkar@gmail.com>
Co-authored-by: Guille Vigil <contact@guillermotti.com>
Co-authored-by: Jonas-Taha El Sesiy <jonas-taha.elsesiy@snowflake.com>
2023-11-26 10:27:38 +00:00
{
name : "k3s" ,
args : args {
platform : Platform { Name : "k3s" , Version : "1.27" } ,
} ,
want : "k3s-cis-1.7" ,
} ,
{
name : "rancher1" ,
args : args {
platform : Platform { Name : "rancher" , Version : "1.27" } ,
} ,
want : "rke-cis-1.7" ,
} ,
{
name : "rke2" ,
args : args {
platform : Platform { Name : "rke2r" , Version : "1.27" } ,
} ,
want : "rke2-cis-1.7" ,
} ,
2020-12-21 11:18:54 +00:00
}
for _ , tt := range tests {
t . Run ( tt . name , func ( t * testing . T ) {
if got := getPlatformBenchmarkVersion ( tt . args . platform ) ; got != tt . want {
t . Errorf ( "getPlatformBenchmarkVersion() = %v, want %v" , got , tt . want )
}
} )
}
}
2021-03-24 16:06:54 +00:00
func Test_getOcpValidVersion ( t * testing . T ) {
cases := [ ] struct {
openShiftVersion string
succeed bool
exp string
} {
{ openShiftVersion : "3.11" , succeed : true , exp : "3.10" } ,
{ openShiftVersion : "3.10" , succeed : true , exp : "3.10" } ,
{ openShiftVersion : "2.9" , succeed : false , exp : "" } ,
2021-04-29 14:08:41 +00:00
{ openShiftVersion : "4.1" , succeed : true , exp : "4.1" } ,
{ openShiftVersion : "4.5" , succeed : true , exp : "4.1" } ,
{ openShiftVersion : "4.6" , succeed : true , exp : "4.1" } ,
2021-03-24 16:06:54 +00:00
{ openShiftVersion : "invalid" , succeed : false , exp : "" } ,
}
for _ , c := range cases {
2021-08-09 08:40:01 +00:00
ocpVer , _ := getOcpValidVersion ( c . openShiftVersion )
2021-03-24 16:06:54 +00:00
if c . succeed {
if c . exp != ocpVer {
2021-04-29 14:08:41 +00:00
t . Errorf ( "getOcpValidVersion(%q) - Got %q expected %s" , c . openShiftVersion , ocpVer , c . exp )
2021-03-24 16:06:54 +00:00
}
} else {
if len ( ocpVer ) > 0 {
2021-04-29 14:08:41 +00:00
t . Errorf ( "getOcpValidVersion(%q) - Expected empty string but Got %s" , c . openShiftVersion , ocpVer )
2021-03-24 16:06:54 +00:00
}
}
}
}