Skip to content

Commit

Permalink
rework bestpractice outputter to buildanalysis
Browse files Browse the repository at this point in the history
  • Loading branch information
TP Honey committed Jul 12, 2022
1 parent 52ef1df commit 3a63232
Show file tree
Hide file tree
Showing 11 changed files with 299 additions and 113 deletions.
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package bestpractice
package buildanalysis

import (
"context"
Expand All @@ -9,27 +9,28 @@ import (
)

const (
Name = outputter.BestPractice
Name = outputter.DroneBuildAnalysis
)

type (
OutputFields struct {
Command string `json:"command" yaml:"command"`
HelpURL string `json:"url" yaml:"url"`
}

outputterConfig struct {
name string
description string
stdOutput bool
outputToFile string
}

OutputFields struct {
RawYaml string `json:"raw_yaml" yaml:"raw_yaml"`
Command string `json:"command" yaml:"command"`
HelpURL string `json:"url" yaml:"url"`
}
)

func New(opts ...Option) (types.Outputter, error) {
oc := new(outputterConfig)
oc.name = Name
oc.description = "Suggests practical changes based on your project layout"
oc.description = "Suggests practical changes based on your project layout and build file"
// apply options
for _, opt := range opts {
opt(oc)
Expand All @@ -50,7 +51,7 @@ func (oc outputterConfig) Output(ctx context.Context, scanResults []types.Scanle
var bestPracticeResults []types.Scanlet
// iterate over enabled outputs
for _, output := range scanResults {
if output.OutputRenderer == Name {
if output.OutputRenderer == outputter.DroneBuildAnalysis {
bestPracticeResults = append(bestPracticeResults, output)
}
}
Expand All @@ -64,7 +65,9 @@ func (oc outputterConfig) Output(ctx context.Context, scanResults []types.Scanle
fmt.Printf(`- %s: %s
command to run: "%s"
url: %s
`, result.Name, result.Description, bp.Command, bp.HelpURL)
Drone YAML:
%s
`, result.Name, result.Description, bp.Command, bp.HelpURL, bp.RawYaml)
}
fmt.Println("")
return nil
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package bestpractice
package buildanalysis

type Option func(*outputterConfig)

Expand Down
Original file line number Diff line number Diff line change
@@ -1,22 +1,28 @@
package dronebuild
package dronebuildmaker

import (
"context"
"fmt"
"os"
"path/filepath"

"github.com/tphoney/best_practice/outputter"
"github.com/tphoney/best_practice/types"
)

const (
Name = outputter.DroneBuildMaker
FileName = ".drone.yml.new"
Name = outputter.DroneBuildMaker
)

var (
FileName = ".drone.yml"
)

type (
OutputFields struct {
RawYaml string `json:"raw_yaml" yaml:"raw_yaml"`
Command string `json:"command" yaml:"command"`
HelpURL string `json:"help_url" yaml:"help_url"`
}

outputterConfig struct {
Expand All @@ -31,7 +37,7 @@ type (
func New(opts ...Option) (types.Outputter, error) {
oc := new(outputterConfig)
oc.name = Name
oc.description = "Creates a Drone build file"
oc.description = "Creates a full Drone build file"
// apply options
for _, opt := range opts {
opt(oc)
Expand Down Expand Up @@ -88,6 +94,11 @@ steps:
fmt.Println(buildOutput)
}
if oc.outputToFile {
_, existsErr := os.Stat(FileName)
if existsErr == nil {
// file exists append .new to the file name
FileName += ".new"
}
fmt.Printf("Created a new Drone Build file '%s'\n", filepath.Join(oc.workingDirectory, FileName))
err := outputter.WriteToFile(filepath.Join(oc.workingDirectory, FileName), buildOutput)
if err != nil {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package dronebuild
package dronebuildmaker

type Option func(*outputterConfig)

Expand Down
8 changes: 4 additions & 4 deletions outputter/outputter.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@ import (
)

const (
DroneBuildMaker = "drone build"
HarnessProduct = "harness product"
BestPractice = "best practice"
DroneBuildMaker = "drone build maker"
HarnessProduct = "harness product"
DroneBuildAnalysis = "drone build analysis"
)

func RunOutput(ctx context.Context, outputters []types.Outputter, scanResults []types.Scanlet) (err error) {
Expand All @@ -27,5 +27,5 @@ func RunOutput(ctx context.Context, outputters []types.Outputter, scanResults []
}

func ListOutputterNames() []string {
return []string{DroneBuildMaker, HarnessProduct, BestPractice}
return []string{DroneBuildMaker, HarnessProduct, DroneBuildAnalysis}
}
10 changes: 5 additions & 5 deletions plugin/plugin.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ import (
"os"

"github.com/tphoney/best_practice/outputter"
"github.com/tphoney/best_practice/outputter/bestpractice"
"github.com/tphoney/best_practice/outputter/dronebuild"
"github.com/tphoney/best_practice/outputter/buildanalysis"
"github.com/tphoney/best_practice/outputter/dronebuildmaker"
"github.com/tphoney/best_practice/outputter/harnessproduct"
"github.com/tphoney/best_practice/scanner"
"github.com/tphoney/best_practice/scanner/docker"
Expand Down Expand Up @@ -102,10 +102,10 @@ func Exec(ctx context.Context, args *Args) error { // nolint:gocyclo
for _, outputName := range args.RequestedOutputs {
switch outputName {
case outputter.DroneBuildMaker:
db, _ := dronebuild.New(dronebuild.WithWorkingDirectory(args.WorkingDirectory), dronebuild.WithStdOutput(false), dronebuild.WithOutputToFile(true))
db, _ := dronebuildmaker.New(dronebuildmaker.WithWorkingDirectory(args.WorkingDirectory), dronebuildmaker.WithStdOutput(false), dronebuildmaker.WithOutputToFile(true))
outputters = append(outputters, db)
case outputter.BestPractice:
bp, _ := bestpractice.New(bestpractice.WithStdOutput(true))
case outputter.DroneBuildAnalysis:
bp, _ := buildanalysis.New(buildanalysis.WithStdOutput(true))
outputters = append(outputters, bp)
case outputter.HarnessProduct:
hp, _ := harnessproduct.New()
Expand Down
121 changes: 106 additions & 15 deletions scanner/docker/docker.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,13 @@ package docker
import (
"context"
"fmt"
"strings"

"github.com/tphoney/best_practice/outputter/dronebuild"
"github.com/tphoney/best_practice/outputter"
"github.com/tphoney/best_practice/outputter/buildanalysis"
"github.com/tphoney/best_practice/outputter/dronebuildmaker"
"github.com/tphoney/best_practice/scanner"
"github.com/tphoney/best_practice/scanner/dronescanner"
"github.com/tphoney/best_practice/types"
"golang.org/x/exp/slices"
)
Expand All @@ -19,9 +23,11 @@ type scannerConfig struct {
}

const (
dockerFilename = "Dockerfile"
Name = scanner.DockerScannerName
DockerFileCheck = "docker file"
dockerFilename = "Dockerfile"
Name = scanner.DockerScannerName
DockerBuildCheck = "docker build"
SecurityScanCheck = "security scan"
droneBuildCheck = "drone build"
)

func New(opts ...Option) (types.Scanner, error) {
Expand All @@ -46,7 +52,7 @@ func (sc *scannerConfig) Description() string {
}

func (sc *scannerConfig) AvailableChecks() []string {
return []string{DockerFileCheck}
return []string{DockerBuildCheck, SecurityScanCheck}
}

func (sc *scannerConfig) Scan(ctx context.Context, requestedOutputs []string) (returnVal []types.Scanlet, err error) {
Expand All @@ -57,9 +63,17 @@ func (sc *scannerConfig) Scan(ctx context.Context, requestedOutputs []string) (r
return returnVal, nil
}

if sc.runAll || slices.Contains(requestedOutputs, DockerFileCheck) {
if sc.runAll || slices.Contains(requestedOutputs, DockerBuildCheck) {
outputResults := sc.buildCheck(dockerFileMatches)
if len(outputResults) > 0 {
returnVal = append(returnVal, outputResults...)
}
if sc.runAll || slices.Contains(requestedOutputs, SecurityScanCheck) {
outputResults := sc.securityCheck(dockerFileMatches)
returnVal = append(returnVal, outputResults...)
}
if (sc.runAll || slices.Contains(requestedOutputs, droneBuildCheck)) && len(dockerFileMatches) > 0 {
outputResults, err := sc.droneBuildCheck()
if err == nil {
returnVal = append(returnVal, outputResults...)
}
}
Expand All @@ -71,11 +85,11 @@ func (sc *scannerConfig) buildCheck(dockerFiles []string) (outputResults []types
// lets check for the build system
for i := range dockerFiles {
testResult := types.Scanlet{
Name: DockerFileCheck,
Name: DockerBuildCheck,
ScannerFamily: Name,
Description: "add docker build step",
OutputRenderer: dronebuild.Name,
Spec: dronebuild.OutputFields{
OutputRenderer: dronebuildmaker.Name,
Spec: dronebuildmaker.OutputFields{
RawYaml: fmt.Sprintf(` - name: build %s
image: plugins/docker
settings:
Expand All @@ -87,17 +101,94 @@ func (sc *scannerConfig) buildCheck(dockerFiles []string) (outputResults []types
from_secret: docker_username
password:
from_secret: docker_password
- name: scan
image: drone-plugins/drone-snyk
privileged: true
settings:
`, dockerFiles[i], dockerFiles[i]),
Command: fmt.Sprintf("docker build --rm --no-cache -t organization/docker-image-name:latest -f %s .", dockerFiles[i]),
HelpURL: "https://docs.drone.io/reference/pipeline/docker/",
},
}
outputResults = append(outputResults, testResult)
}
return outputResults
}

func (sc *scannerConfig) securityCheck(dockerFiles []string) (outputResults []types.Scanlet) {
// lets check for the build system
for i := range dockerFiles {
testResult := types.Scanlet{
Name: DockerBuildCheck,
ScannerFamily: Name,
Description: "run snyk security scan",
OutputRenderer: dronebuildmaker.Name,
Spec: dronebuildmaker.OutputFields{
RawYaml: fmt.Sprintf(` - name: scan image %s
image: plugins//drone-snyk
privileged: true
settings:
dockerfile: %s
image: organization/docker-image-name
snyk:
from_secret: snyk_token`, dockerFiles[i], dockerFiles[i], dockerFiles[i]),
from_secret: snyk_token`, dockerFiles[i], dockerFiles[i]),
Command: fmt.Sprintf("docker scan drone-plugins/drone-snyk --file= %s", dockerFiles[i]),
HelpURL: "snyk.io/help/",
},
}
outputResults = append(outputResults, testResult)
}
return outputResults
}

func (sc *scannerConfig) droneBuildCheck() (outputResults []types.Scanlet, err error) {
pipelines, err := dronescanner.ReadDroneFile(sc.workingDirectory, dronescanner.DroneFileLocation)
if err != nil {
return outputResults, err
}
// iterate over the pipelines
foundDockerPlugin := false
foundSnykPlugin := false
foundDockerScanCommand := false
foundDockerBuildCommand := false
for i := range pipelines {
for j := range pipelines[i].Steps {
if strings.Contains(pipelines[i].Steps[j].Image, "plugins/docker") {
foundDockerPlugin = true
}
if strings.Contains(pipelines[i].Steps[j].Image, "plugins/drone-snyk") {
foundSnykPlugin = true
}
commands := pipelines[i].Steps[j].Commands
for k := range commands {
if strings.Contains(commands[k], "docker build") {
foundDockerBuildCommand = true
}
if strings.Contains(commands[k], "docker scan") {
foundDockerScanCommand = true
}
}
}
if !foundDockerPlugin || foundDockerBuildCommand {
bestPracticeResult := types.Scanlet{
Name: DockerBuildCheck,
ScannerFamily: Name,
Description: "pipeline '%s' should use the drone docker plugin",
OutputRenderer: outputter.DroneBuildAnalysis,
Spec: buildanalysis.OutputFields{
HelpURL: "https://docs.drone.io/yaml/docker/#the-depends_on-attribute",
},
}
outputResults = append(outputResults, bestPracticeResult)
}
if !foundSnykPlugin || foundDockerScanCommand {
bestPracticeResult := types.Scanlet{
Name: DockerBuildCheck,
ScannerFamily: Name,
Description: "pipeline '%s' should use the drone snyk plugin",
OutputRenderer: outputter.DroneBuildAnalysis,
Spec: buildanalysis.OutputFields{
HelpURL: "https://docs.drone.io/yaml/docker/#the-depends_on-attribute",
},
}
outputResults = append(outputResults, bestPracticeResult)
}
}
return outputResults, err
}
Loading

0 comments on commit 3a63232

Please sign in to comment.