Skip to content

Commit

Permalink
Merge pull request #98 from aquasecurity/parameter-substitution
Browse files Browse the repository at this point in the history
Add parameter substitution option
  • Loading branch information
lizrice authored Jun 26, 2020
2 parents d325339 + 0062453 commit 61091b1
Show file tree
Hide file tree
Showing 7 changed files with 115 additions and 21 deletions.
27 changes: 15 additions & 12 deletions app.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,10 @@ import (
)

func app(cmd *cobra.Command, args []string) {
err := checkDefinitionFilePath(cfgFile)
glog.V(2).Info(fmt.Sprintf("Looking for config file: %s\n", cfgFile))
_, err := os.Stat(cfgFile)
if err != nil {
glog.V(2).Info(fmt.Sprintf("config file: %s not found.\n", cfgFile))
util.ExitWithError(err)
}

Expand All @@ -23,7 +25,7 @@ func app(cmd *cobra.Command, args []string) {

// Main entry point for benchmark functionality
func Main(filePath string, constraints []string) {
controls, err := getControls(filePath, constraints)
controls, err := getControls(filePath, constraints, substitutionFile)
if err != nil {
util.ExitWithError(err)
}
Expand Down Expand Up @@ -68,23 +70,24 @@ func runControls(controls *check.Controls, checkList string) check.Summary {
return summary
}

func getControls(path string, constraints []string) (*check.Controls, error) {
func getControls(path string, constraints []string, substitutionFile string) (*check.Controls, error) {
data, err := ioutil.ReadFile(path)
if err != nil {
return nil, err
}

controls, err := check.NewControls([]byte(data), constraints)
s := string(data)
if substitutionFile != "" {
substitutionData, err := ioutil.ReadFile(substitutionFile)
if err != nil {
return nil, err
}
substituMap := util.GetSubstitutionMap(substitutionData)
s = util.MakeSubstitutions(s, "", substituMap)
}
controls, err := check.NewControls([]byte(s), constraints)
if err != nil {
return nil, err
}

return controls, err
}

func checkDefinitionFilePath(filePath string) (err error) {
glog.V(2).Info(fmt.Sprintf("Looking for config file: %s\n", filePath))
_, err = os.Stat(filePath)

return err
}
6 changes: 3 additions & 3 deletions check/check.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,8 @@ func (audit Audit) Execute(customConfig ...interface{}) (result string, errMessa
res, err := exec.Command("sh", "-c", string(audit)).CombinedOutput()
// Errors mean the audit command failed, but that might be what we expect
// for example, if we grep for something that is not found, there is a non-zero exit code
// It is a problem if we can't find one of the audit commands to execute, but we deal
// with this case in (c *Check) Run()
// It is a problem if we can't find one of the audit commands to execute, but we deal
// with this case in (c *Check) Run()
if err != nil {
errMessage = err.Error()
}
Expand Down Expand Up @@ -139,7 +139,7 @@ func (c *Check) Run(definedConstraints map[string][]string) {
// Since this is an Scored check
// without tests return a 'WARN' to alert
// the user that this check needs attention
if len(strings.TrimSpace(c.Type)) == 0 && c.Tests == nil && c.SubChecks == nil{
if len(strings.TrimSpace(c.Type)) == 0 && c.Tests == nil && c.SubChecks == nil {
c.Reason = "There are no test items"
c.State = WARN
return
Expand Down
10 changes: 5 additions & 5 deletions check/check_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,23 +47,23 @@ func TestCheck_Run(t *testing.T) {
if err := yaml.Unmarshal([]byte(def1), ts); err != nil {
t.Fatalf("error unmarshaling tests yaml %v", err)
}

checkSubChecks := new(Check)
if err := yaml.Unmarshal([]byte(def2), checkSubChecks); err != nil {
t.Fatalf("error unmarshaling check yaml %v", err)
}

checkTypeManual := Check{Type: "manual", Tests: ts, Scored: true, auditer: Audit("ps -ef")}
checkTypeSkip := Check{Type: "skip", Tests: ts, Scored: true, auditer: Audit("ps -ef")}
checkNoTests := Check{Type: "", Scored: true, auditer: Audit("")}
checkScoredFail := Check{Scored: true, Tests: ts, auditer: Audit("echo anything")}
checkNotScoredFail := Check{Scored: false, Tests: ts, auditer: Audit("echo anything")}

testCases := []TestCase{
{check: checkTypeManual, Expected: WARN},
{check: checkTypeSkip, Expected: INFO},
{check: checkNoTests, Expected: WARN}, // If there are no tests in the check, warn
{check: checkScoredFail, Expected: FAIL}, // If scored test fails. FAIL
{check: checkNoTests, Expected: WARN}, // If there are no tests in the check, warn
{check: checkScoredFail, Expected: FAIL}, // If scored test fails. FAIL
{check: checkNotScoredFail, Expected: WARN}, // If not scored test fails, WARN
{check: *checkSubChecks, Expected: PASS},
}
Expand Down
3 changes: 2 additions & 1 deletion check/controls_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,10 @@ import (
"bytes"
"encoding/json"
"encoding/xml"
"github.com/onsi/ginkgo/reporters"
"strings"
"testing"

"github.com/onsi/ginkgo/reporters"
)

const def = `---
Expand Down
2 changes: 2 additions & 0 deletions root.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ var (
includeTestOutput bool
outputFile string
define []string
substitutionFile string
)

// rootCmd represents the base command when called without any subcommands
Expand Down Expand Up @@ -48,6 +49,7 @@ func init() {
// Cobra supports persistent flags, which, if defined here,
// will be global for your application.
rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.app.yaml)")
rootCmd.PersistentFlags().StringVar(&substitutionFile, "substitution", "", "parameters substitution file")
rootCmd.PersistentFlags().StringArrayVar(&define, "define", []string{""}, "")

// Cobra also supports local flags, which will only run
Expand Down
36 changes: 36 additions & 0 deletions util/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import (
"github.com/aquasecurity/bench-common/check"
"github.com/fatih/color"
"github.com/golang/glog"
"gopkg.in/yaml.v2"
)

var (
Expand All @@ -35,6 +36,11 @@ var (
}
)

// SubstitutionList is a Config file for substitution
type SubstitutionList struct {
Name string `yaml:"value"`
}

func printlnWarn(msg string) {
fmt.Fprintf(os.Stderr, "[%s] %s\n",
colors[check.WARN].Sprintf("%s", check.WARN),
Expand Down Expand Up @@ -177,3 +183,33 @@ func PrintOutput(output string, outputFile string) {
}
}
}

// GetSubstitutionMap is building the key:value map
func GetSubstitutionMap(substituData []byte) map[string]string {
//var yamlConfig Item
fileMap := make(map[string]SubstitutionList)
outputMap := make(map[string]string)
err := yaml.Unmarshal(substituData, &fileMap)
if err != nil {
fmt.Errorf("failed to unmarshal YAML: %s", err)
}
for k, v := range fileMap {
outputMap[k] = v.Name
}
return outputMap
}

// MakeSubstitutions will replace all $keys with values.
func MakeSubstitutions(s string, ext string, m map[string]string) string {
for k, v := range m {
subst := "$" + k + ext
if v == "" {
glog.V(2).Info(fmt.Sprintf("No substitution for '%s'\n", subst))
continue
}
glog.V(2).Info(fmt.Sprintf("Substituting %s with '%s'\n", subst, v))
s = multiWordReplace(s, subst, v)
}

return s
}
52 changes: 52 additions & 0 deletions util/util_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,23 @@
package util

import (
"reflect"
"strconv"
"testing"
)

var g string

const subs = `---
## Controls Files.
# These are YAML files that hold all the details for running checks.
# In here you can set all parameter substitution for docker-bench
docker-storage:
value: /var/lib/docker
example:
value: /example/change`

func fakeps(proc string) string {
return g
}
Expand Down Expand Up @@ -71,3 +82,44 @@ func TestMultiWordReplace(t *testing.T) {
})
}
}

func TestMakeSubsitutions(t *testing.T) {
cases := []struct {
input string
subst map[string]string
exp string
}{
{input: "Replace $thisbin", subst: map[string]string{"this": "that"}, exp: "Replace that"},
{input: "Replace $thisbin", subst: map[string]string{"this": "that", "here": "there"}, exp: "Replace that"},
{input: "Replace $thisbin and $herebin", subst: map[string]string{"this": "that", "here": "there"}, exp: "Replace that and there"},
}
for _, c := range cases {
t.Run(c.input, func(t *testing.T) {
s := MakeSubstitutions(c.input, "bin", c.subst)
if s != c.exp {
t.Fatalf("Got %s expected %s", s, c.exp)
}
})
}
}

func TestGetSubstitutionMap(t *testing.T) {
tests := []struct {
name string
substituData []byte
want map[string]string
}{
{
name: "Test for creating valid map",
substituData: []byte(subs),
want: map[string]string{"docker-storage": "/var/lib/docker", "example": "/example/change"},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := GetSubstitutionMap(tt.substituData); !reflect.DeepEqual(got, tt.want) {
t.Errorf("GetSubstitutionMap() = %v, want %v", got, tt.want)
}
})
}
}

0 comments on commit 61091b1

Please sign in to comment.