Skip to content

Commit

Permalink
(feat) check drone build for outdated tags (#33)
Browse files Browse the repository at this point in the history
  • Loading branch information
TP Honey authored Jul 30, 2022
1 parent 83e8121 commit b954140
Showing 1 changed file with 90 additions and 2 deletions.
92 changes: 90 additions & 2 deletions scanner/docker/docker.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,12 @@ package docker

import (
"context"
"encoding/json"
"fmt"
"net/http"
"strings"

"github.com/Masterminds/semver"
"github.com/tphoney/best_practice/outputter"
"github.com/tphoney/best_practice/outputter/buildmaker"
"github.com/tphoney/best_practice/outputter/dronebuildanalysis"
Expand All @@ -23,7 +26,7 @@ type scannerConfig struct {
}

const (
dockerFilename = "Dockerfile"
dockerFilename = "Dockerfile*"
Name = scanner.DockerScannerName
BuildCheck = "Docker build"
SecurityScanCheck = "Docker security scan"
Expand Down Expand Up @@ -146,20 +149,30 @@ func (sc *scannerConfig) droneBuildCheck() (outputResults []types.Scanlet, err e
if err != nil {
return outputResults, err
}
// iterate over the pipelines
foundDockerPlugin := false
foundSnykPlugin := false
foundDockerScanCommand := false
foundDockerBuildCommand := false
var imagesWithTag []*image
// iterate over the pipelines
for i := range pipelines {
for j := range pipelines[i].Steps {
// check for plugins
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
// check for images with tagged versions
if strings.Contains(pipelines[i].Steps[j].Image, ":") {
imagesWithTag = append(imagesWithTag,
&image{
image: pipelines[i].Steps[j].Image,
stepName: pipelines[i].Steps[j].Name,
})
}
for k := range commands {
if strings.Contains(commands[k], "docker build") {
foundDockerBuildCommand = true
Expand Down Expand Up @@ -216,6 +229,81 @@ func (sc *scannerConfig) droneBuildCheck() (outputResults []types.Scanlet, err e
}
outputResults = append(outputResults, bestPracticeResult)
}
// check for images with tagged versions
containerErr := getContainerUpdates(imagesWithTag)
if containerErr != nil {
fmt.Printf("error getting container updates: %s", containerErr)
}
for k := range imagesWithTag {
if imagesWithTag[k].updatedImage != "" {
bestPracticeResult := types.Scanlet{
Name: BuildCheck,
ScannerFamily: Name,
Description: fmt.Sprintf("pipeline '%s' step `%s` update image from %s to %s",
pipelines[i].Name, imagesWithTag[k].stepName, imagesWithTag[k].image, imagesWithTag[k].updatedImage),
OutputRenderer: outputter.DroneBuildAnalysis,
Spec: dronebuildanalysis.OutputFields{
HelpURL: "https://docs.docker.com/engine/reference/commandline/pull/",
Command: fmt.Sprintf("docker pull %s", imagesWithTag[k].updatedImage),
RawYaml: fmt.Sprintf(`image: %s`, imagesWithTag[k].updatedImage),
},
}
outputResults = append(outputResults, bestPracticeResult)
}
}
}
return outputResults, err
}

type image struct {
image string
updatedImage string
stepName string
}

type dockerTags []struct {
Layer string `json:"layer"`
Name string `json:"name"`
}

func getContainerUpdates(images []*image) (err error) {
for i := range images {
// split the name and tag
split := strings.Split(images[i].image, ":")
if len(split) != 2 { //nolint:gomnd
return fmt.Errorf("image name is not in the format 'image:tag'")
}
name, currentTag := split[0], split[1]
currentSemver, currentErr := scanner.ReturnVersionObject(currentTag)
if currentErr != nil {
return currentErr
}
// get the docker tags for the image
resp, err := http.Get(fmt.Sprintf("https://registry.hub.docker.com/v1/repositories/%s/tags", name))
if err != nil {
return err
}
var tags dockerTags
err = json.NewDecoder(resp.Body).Decode(&tags)
resp.Body.Close()
if err != nil {
return err
}
// find the tag in the list of tags
var semVers []*semver.Version
for j := range tags {
// convert to a semver
version, err := scanner.ReturnVersionObject(tags[j].Name)
if err == nil {
semVers = append(semVers, version)
}
}
// iterate over the semvers and find any that are greater than the tag
for j := range semVers {
if semVers[j].GreaterThan(currentSemver) && strings.Contains(currentTag, semVers[j].Prerelease()) {
images[i].updatedImage = fmt.Sprintf("%s:%s", name, semVers[j].String())
}
}
}
return nil
}

0 comments on commit b954140

Please sign in to comment.