Skip to content

Commit

Permalink
add --exit-level option (#49)
Browse files Browse the repository at this point in the history
* add exit-level parameter

* sort Levels by risk

* rename file

* create config pkg and global Conf

* change abend algorithm

* update readme
  • Loading branch information
tomoyamachi authored Aug 25, 2019
1 parent 25d0a13 commit 135d2e5
Show file tree
Hide file tree
Showing 9 changed files with 156 additions and 88 deletions.
16 changes: 14 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ See [Installation](#installation) and [Common Examples](#common-examples)
- [Scan an image file](#scan-an-image-file)
- [Get or Save the results as JSON](#get-or-save-the-results-as-json)
- [Specify exit code](#specify-exit-code)
- [Specify exit level](#specify-exit-level)
- [Ignore the specified checkpoints](#ignore-the-specified-checkpoints)
- [Continuous Integration](#continuous-integration-ci)
- [Travis CI](#travis-ci)
Expand Down Expand Up @@ -430,10 +431,21 @@ $ dockle -f json -o results.json goodwithtech/test-image:v1
By default, `Dockle` exits with code `0` even if there are some problems.
Use the `--exit-code` option to exit with a non-zero exit code if any alert were found.
Use the `--exit-code, -c` option to exit with a non-zero exit code if `WARN` or `FATAL` alert were found.
```bash
$ dockle --exit-code 1 [IMAGE_NAME]
$ dockle --exit-code 1 [IMAGE_NAME]
```
### Specify exit level
By default, `--exit-code` run when there are `WARN` or `FATAL` level alerts.
Use the `--exit-level, -l` option to change alert level. You can set `info`, `warn` or `fatal`.
```bash
$ dockle --exit-code 1 --exit-level info [IMAGE_NAME]
$ dockle --exit-code 1 --exit-level fatal [IMAGE_NAME]
```
### Ignore the specified checkpoints
Expand Down
9 changes: 7 additions & 2 deletions cmd/dockle/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ OPTIONS:
app.Version = version
app.ArgsUsage = "image_name"

app.Usage = "A Simple Security Checker for Container Image, Suitable for CI"
app.Usage = "Container Image Linter for Security, Helping build the Best-Practice Docker Image, Easy to start"

app.Flags = []cli.Flag{
cli.StringFlag{
Expand All @@ -56,10 +56,15 @@ OPTIONS:
Usage: "output file name",
},
cli.IntFlag{
Name: "exit-code",
Name: "exit-code, c",
Usage: "Exit code when alert were found",
Value: 0,
},
cli.StringFlag{
Name: "exit-level, l",
Usage: "change ABEND level when use exit-code=1",
Value: "WARN",
},
cli.BoolFlag{
Name: "debug, d",
Usage: "debug mode",
Expand Down
77 changes: 77 additions & 0 deletions config/config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package config

import (
"bufio"
"os"
"strings"

"github.com/goodwithtech/dockle/pkg/types"

"github.com/goodwithtech/dockle/pkg/log"
"github.com/urfave/cli"
)

const (
dockleIgnore = ".dockleignore"
)

var exitLevelMap = map[string]int{
"info": types.InfoLevel,
"INFO": types.InfoLevel,
"warn": types.WarnLevel,
"WARN": types.WarnLevel,
"fatal": types.FatalLevel,
"FATAL": types.FatalLevel,
}

type Config struct {
IgnoreMap map[string]struct{}
ExitCode int
ExitLevel int
}

var Conf Config

func CreateFromCli(c *cli.Context) {
ignoreRules := c.StringSlice("ignore")
Conf = Config{
IgnoreMap: getIgnoreCheckpointMap(ignoreRules),
ExitCode: c.Int("exit-code"),
ExitLevel: getExitLevel(c.String("exit-level")),
}
}

func getExitLevel(param string) (exitLevel int) {
exitLevel, ok := exitLevelMap[param]
if !ok {
return types.WarnLevel
}
return exitLevel
}

func getIgnoreCheckpointMap(ignoreRules []string) map[string]struct{} {
ignoreCheckpointMap := map[string]struct{}{}
// from cli command
for _, rule := range ignoreRules {
ignoreCheckpointMap[rule] = struct{}{}
}

// from ignore file
f, err := os.Open(dockleIgnore)
if err != nil {
log.Logger.Debug("There is no .dockleignore file")
// dockle must work even if there isn't ignore file
return ignoreCheckpointMap
}
scanner := bufio.NewScanner(f)
for scanner.Scan() {
line := scanner.Text()
line = strings.TrimSpace(line)
if strings.HasPrefix(line, "#") || line == "" {
continue
}
log.Logger.Debugf("Add new ignore code: %s", line)
ignoreCheckpointMap[line] = struct{}{}
}
return ignoreCheckpointMap
}
14 changes: 7 additions & 7 deletions pkg/report/json.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,7 @@ import (
)

type JsonWriter struct {
Output io.Writer
IgnoreMap map[string]struct{}
Output io.Writer
}

type JsonOutputFormat struct {
Expand All @@ -32,13 +31,14 @@ type JsonDetail struct {
Alerts []string `json:"alerts"`
}

func (jw JsonWriter) Write(assessments []*types.Assessment) (bool, error) {
var abendAssessments []*types.Assessment
func (jw JsonWriter) Write(assessments AssessmentSlice) (bool, error) {
abend := AssessmentSlice{}
abendAssessments := &abend
jsonSummary := JsonSummary{}
jsonDetails := []*JsonDetail{}
targetType := types.MinTypeNumber
for targetType <= types.MaxTypeNumber {
filtered := filteredAssessments(jw.IgnoreMap, targetType, assessments)
filtered := assessments.FilteredByTargetCode(targetType)
level, detail := jsonDetail(targetType, filtered)
if detail != nil {
jsonDetails = append(jsonDetails, detail)
Expand All @@ -57,7 +57,7 @@ func (jw JsonWriter) Write(assessments []*types.Assessment) (bool, error) {
}

for _, assessment := range filtered {
abendAssessments = filterAbendAssessments(jw.IgnoreMap, abendAssessments, assessment)
abendAssessments.AddAbend(assessment)
}
targetType++
}
Expand All @@ -73,7 +73,7 @@ func (jw JsonWriter) Write(assessments []*types.Assessment) (bool, error) {
if _, err = fmt.Fprint(jw.Output, string(output)); err != nil {
return false, xerrors.Errorf("failed to write json: %w", err)
}
return len(abendAssessments) > 0, nil
return len(*abendAssessments) > 0, nil
}
func jsonDetail(assessmentType int, assessments []*types.Assessment) (level int, jsonInfo *JsonDetail) {
if len(assessments) == 0 {
Expand Down
29 changes: 14 additions & 15 deletions pkg/report/list.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,34 +16,33 @@ const (
NEWLINE = "\n"
)

var AlertLevelColors = []color.Color{
color.Magenta,
color.Yellow,
color.Red,
color.Green,
color.Blue,
color.Blue,
var AlertLevelColors = map[int]color.Color{
types.InfoLevel: color.Magenta,
types.WarnLevel: color.Yellow,
types.FatalLevel: color.Red,
types.PassLevel: color.Green,
types.SkipLevel: color.Blue,
types.IgnoreLevel: color.Blue,
}

type ListWriter struct {
Output io.Writer
IgnoreMap map[string]struct{}
Output io.Writer
}

func (lw ListWriter) Write(assessments []*types.Assessment) (bool, error) {
var abendAssessments []*types.Assessment

func (lw ListWriter) Write(assessments AssessmentSlice) (bool, error) {
abend := AssessmentSlice{}
abendAssessments := &abend
targetType := types.MinTypeNumber
for targetType <= types.MaxTypeNumber {
filtered := filteredAssessments(lw.IgnoreMap, targetType, assessments)
filtered := assessments.FilteredByTargetCode(targetType)
showTargetResult(targetType, filtered)

for _, assessment := range filtered {
abendAssessments = filterAbendAssessments(lw.IgnoreMap, abendAssessments, assessment)
abendAssessments.AddAbend(assessment)
}
targetType++
}
return len(abendAssessments) > 0, nil
return len(*abendAssessments) > 0, nil
}

func showTargetResult(assessmentType int, assessments []*types.Assessment) {
Expand Down
45 changes: 26 additions & 19 deletions pkg/report/writer.go
Original file line number Diff line number Diff line change
@@ -1,27 +1,30 @@
package report

import (
"github.com/goodwithtech/dockle/config"
"github.com/goodwithtech/dockle/pkg/types"
)

var AlertLabels = []string{
"INFO",
"WARN",
"FATAL",
"PASS",
"SKIP",
"IGNORE",
var AlertLabels = map[int]string{
types.InfoLevel: "INFO",
types.WarnLevel: "WARN",
types.FatalLevel: "FATAL",
types.PassLevel: "PASS",
types.SkipLevel: "SKIP",
types.IgnoreLevel: "IGNORE",
}

type AssessmentSlice []*types.Assessment
type Writer interface {
Write(assessments []*types.Assessment) (bool, error)
Write(assessments AssessmentSlice) (bool, error)
}

func filteredAssessments(ignoreCheckpointMap map[string]struct{}, target int, assessments []*types.Assessment) (filtered []*types.Assessment) {
// FilteredByTargetCode returns only target type assessments from all assessments slice
func (as *AssessmentSlice) FilteredByTargetCode(target int) (filtered AssessmentSlice) {
detail := types.AlertDetails[target]
for _, assessment := range assessments {
for _, assessment := range *as {
if assessment.Type == target {
if _, ok := ignoreCheckpointMap[detail.Code]; ok {
if _, ok := config.Conf.IgnoreMap[detail.Code]; ok {
assessment.Level = types.IgnoreLevel
}
filtered = append(filtered, assessment)
Expand All @@ -30,14 +33,18 @@ func filteredAssessments(ignoreCheckpointMap map[string]struct{}, target int, as
return filtered
}

func filterAbendAssessments(ignoreCheckpointMap map[string]struct{}, abendAssessments []*types.Assessment, assessment *types.Assessment) []*types.Assessment {
if assessment.Level == types.SkipLevel {
return abendAssessments
}

// AddAbend add assessment to AssessmentSlice pointer if abend level
func (as *AssessmentSlice) AddAbend(assessment *types.Assessment) {
level := assessment.Level
detail := types.AlertDetails[assessment.Type]
if _, ok := ignoreCheckpointMap[detail.Code]; ok {
return abendAssessments
if level == 0 {
level = detail.DefaultLevel
}
if level < config.Conf.ExitLevel {
return
}
if _, ok := config.Conf.IgnoreMap[detail.Code]; ok {
return
}
return append(abendAssessments, assessment)
*as = append(*as, assessment)
}
46 changes: 7 additions & 39 deletions pkg/run.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
package pkg

import (
"bufio"
l "log"
"os"
"strings"

"github.com/goodwithtech/dockle/config"
"github.com/goodwithtech/dockle/pkg/utils"

"github.com/goodwithtech/dockle/pkg/report"
Expand All @@ -24,15 +23,12 @@ var (
ignoreCheckpointMap map[string]struct{}
)

const (
dockleIgnore = ".dockleignore"
)

func Run(c *cli.Context) (err error) {
debug := c.Bool("debug")
if err = log.InitLogger(debug); err != nil {
l.Fatal(err)
}
config.CreateFromCli(c)

cliVersion := "v" + c.App.Version
latestVersion, err := utils.FetchLatestVersion()
Expand Down Expand Up @@ -86,11 +82,7 @@ func Run(c *cli.Context) (err error) {

log.Logger.Debug("End assessments...")

exitCode := c.Int("exit-code")

// Store ignore checkpoint code
ignoreRules := c.StringSlice("ignore")
getIgnoreCheckpointMap(ignoreRules)
o := c.String("output")
output := os.Stdout
if o != "" {
Expand All @@ -102,43 +94,19 @@ func Run(c *cli.Context) (err error) {
var writer report.Writer
switch format := c.String("format"); format {
case "json":
writer = &report.JsonWriter{Output: output, IgnoreMap: ignoreCheckpointMap}
writer = &report.JsonWriter{Output: output}
default:
writer = &report.ListWriter{Output: output, IgnoreMap: ignoreCheckpointMap}
writer = &report.ListWriter{Output: output}
}

abend, err := writer.Write(assessments)
if err != nil {
return xerrors.Errorf("failed to write results: %w", err)
}
if exitCode != 0 && abend {
os.Exit(exitCode)
}

return nil
}

func getIgnoreCheckpointMap(ignoreRules []string) {
ignoreCheckpointMap = map[string]struct{}{}
for _, rule := range ignoreRules {
ignoreCheckpointMap[rule] = struct{}{}
}

f, err := os.Open(dockleIgnore)
if err != nil {
log.Logger.Debug("There is no .dockleignore file")
// dockle must work even if there isn't ignore file
return
if config.Conf.ExitCode != 0 && abend {
os.Exit(config.Conf.ExitCode)
}

scanner := bufio.NewScanner(f)
for scanner.Scan() {
line := scanner.Text()
line = strings.TrimSpace(line)
if strings.HasPrefix(line, "#") || line == "" {
continue
}
log.Logger.Debugf("Add new ignore code: %s", line)
ignoreCheckpointMap[line] = struct{}{}
}
return nil
}
File renamed without changes.
Loading

0 comments on commit 135d2e5

Please sign in to comment.