Skip to content

Commit

Permalink
Oran/image scan (#69)
Browse files Browse the repository at this point in the history
* Add: support for image scanning

This will assume repositry name and id is full registry url for example: 1111111.dkr.ecr.us-east-1.amazonaws.com/alpine
And also inject tag as branch name
Currently support trivy standalone with no server caching


Co-authored-by: oranmoshai <oran.moshai@aquasec.com>
  • Loading branch information
oranmoshai and oranmoshai authored Apr 10, 2022
1 parent 0edc09d commit 1e899b5
Show file tree
Hide file tree
Showing 12 changed files with 301 additions and 41 deletions.
7 changes: 5 additions & 2 deletions cmd/aqua/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,15 +82,18 @@ func main() {
},
)

imageCmd := commands.NewImageCommand()
imageCmd.Action = runScan

app.Action = runScan
app.Flags = fsCmd.Flags

app.Commands = []*cli.Command{
fsCmd,
configCmd,
imageCmd,
commands.NewPluginCommand(),
commands.NewClientCommand(),
commands.NewImageCommand(),
commands.NewRepositoryCommand(),
commands.NewRootfsCommand(),
commands.NewServerCommand(),
Expand Down Expand Up @@ -125,7 +128,7 @@ func runScan(c *cli.Context) error {
}
log.Logger.Debugf("Using scanPath %s", scanPath)

client, err := buildClient.Get(scanPath)
client, err := buildClient.Get(scanPath, c)
if err != nil {
return err
}
Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ require (
github.com/twitchtv/twirp v8.1.1+incompatible
github.com/urfave/cli/v2 v2.3.0
go.uber.org/zap v1.20.0
golang.org/x/text v0.3.7
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1
google.golang.org/protobuf v1.27.1
)
6 changes: 5 additions & 1 deletion pkg/buildClient/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import (
"net/http"
"os"

"github.com/urfave/cli/v2"

"github.com/aquasecurity/trivy-plugin-aqua/pkg/log"
"github.com/aquasecurity/trivy-plugin-aqua/pkg/proto/buildsecurity"
"github.com/pkg/errors"
Expand All @@ -20,6 +22,7 @@ type Client interface {

type TwirpClient struct {
client buildsecurity.BuildSecurity
c *cli.Context
scanPath string
jwtToken string
aquaUrl string
Expand All @@ -28,7 +31,7 @@ type TwirpClient struct {

var buildClient Client

func Get(scanPath string) (Client, error) {
func Get(scanPath string, c *cli.Context) (Client, error) {
if buildClient != nil {
log.Logger.Debugf("Valid client found, re-using...")
return buildClient, nil
Expand Down Expand Up @@ -67,6 +70,7 @@ func Get(scanPath string) (Client, error) {
scanPath: scanPath,
jwtToken: jwtToken,
aquaUrl: aquaURL,
c: c,
}

return buildClient, nil
Expand Down
2 changes: 1 addition & 1 deletion pkg/buildClient/policies.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ func (bc *TwirpClient) GetPoliciesForRepository() ([]*buildsecurity.Policy, erro
return nil, err
}

_, branch, err := metadata.GetRepositoryDetails(bc.scanPath)
_, branch, err := metadata.GetRepositoryDetails(bc.scanPath, bc.c.Command.Name)
if err != nil {
return nil, err
}
Expand Down
31 changes: 22 additions & 9 deletions pkg/buildClient/repository.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package buildClient

import (
"fmt"

"github.com/aquasecurity/trivy-plugin-aqua/pkg/log"
"github.com/aquasecurity/trivy-plugin-aqua/pkg/metadata"
"github.com/aquasecurity/trivy-plugin-aqua/pkg/proto/buildsecurity"
Expand Down Expand Up @@ -57,20 +59,31 @@ func (bc *TwirpClient) GetOrCreateRepository() (string, error) {
return repoId, nil
}

func (bc *TwirpClient) getScmID() (string, error) {
scmID, err := metadata.GetScmID(bc.scanPath)
if err != nil {
return "", err
func (bc *TwirpClient) getScmID() (scmID string, err error) {
switch bc.c.Command.Name {
case "image":
prefix, repo, _ := metadata.GetImageDetails(bc.scanPath)
scmID = metadata.GetRepositoryUrl(prefix, repo)
default:
scmID, err = metadata.GetScmID(bc.scanPath)
if err != nil {
return "", fmt.Errorf("failed get scm id: %w", err)
}
}

return scmID, nil
}

func (bc *TwirpClient) getRepoName() (string, error) {
repoName, _, err := metadata.GetRepositoryDetails(bc.scanPath)
if err != nil {
return "", err
func (bc *TwirpClient) getRepoName() (repoName string, err error) {
switch bc.c.Command.Name {
case "image":
prefix, repo, _ := metadata.GetImageDetails(bc.scanPath)
repoName = metadata.GetRepositoryUrl(prefix, repo)
default:
repoName, _, err = metadata.GetRepositoryDetails(bc.scanPath, "")
if err != nil {
return "", err
}
}

return repoName, nil
}
2 changes: 1 addition & 1 deletion pkg/buildClient/upload.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ func (bc *TwirpClient) Upload(results []*buildsecurity.Result, tags map[string]s
}

gitUser := metadata.GetGitUser(bc.scanPath)
_, branch, err := metadata.GetRepositoryDetails(bc.scanPath)
_, branch, err := metadata.GetRepositoryDetails(bc.scanPath, bc.c.Command.Name)
if err != nil {
return err
}
Expand Down
130 changes: 108 additions & 22 deletions pkg/metadata/collector.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package metadata

import (
"fmt"
"net"

"io/ioutil"
"os"
Expand All @@ -12,6 +13,74 @@ import (
"github.com/aquasecurity/trivy-plugin-aqua/pkg/log"
)

var (
// SHARegexp is used to split an image digest value to a registry prefix,
// repository name, and the SHA256 hash.
SHARegexp = regexp.MustCompile(`^(?:([^/]+)/)([^@]+)(@sha256:[0-9a-f]+)$`)

// SplitImageNameRegexp is used to split a fully qualified image name to a
// registry prefix, repository name and image tag.
SplitImageNameRegexp = regexp.MustCompile(`^(?:([^/]+)/)?([^:]+)(?::(.*))?$`)
// PortRegexp is used to check whether a string ends with a port suffix
// (e.g. :8080)
PortRegexp = regexp.MustCompile(`:\d+$`)
)

func GetRepositoryUrl(prefix, repo string) string {
if prefix != "" {
return fmt.Sprintf("%s/%s", prefix, repo)
}
return repo
}

// GetImageDetails gets the full name of an image (e.g. "repo/test:master",
// or even "docker.io/repo/test:master") and splits it into the registry
// prefix, repository name and the image tag (e.g. "docker.io", "repo/test"
// and "master" in the previous example).
func GetImageDetails(imageName string) (prefix, repo, tag string) {
if imageName == "" {
return prefix, repo, tag
}

shaMatches := SHARegexp.FindStringSubmatch(imageName)
if len(shaMatches) == 4 {
prefix = shaMatches[1]
repo = shaMatches[2]
tag = shaMatches[3]
} else {
matches := SplitImageNameRegexp.FindStringSubmatch(imageName)
if len(matches) < 3 {
return prefix, repo, tag
}

prefix = matches[1]
repo = matches[2]
tag = matches[3]
}

// we may have extracted a prefix, but it may actually be part of the
// repository name, because repository names can contain multiple slashes.
// to verify, we will check that the prefix we extract is a valid IP address
// or DNS name. We will also assume everything with a port suffix (e.g.
// :8080) is a registry prefix
if prefix != "" && !PortRegexp.MatchString(prefix) {
if net.ParseIP(prefix) == nil {
dns, _ := net.LookupIP(prefix)
if len(dns) == 0 {
// prefix is probably a part of the repository name
if repo == "" {
repo = prefix
} else {
repo = fmt.Sprintf("%s/%s", prefix, repo)
}
prefix = ""
}
}
}

return prefix, repo, tag
}

// GetScmID extracts the git path from the config file
func GetScmID(scanPath string) (string, error) {
gitConfigFile := filepath.Join(scanPath, ".git", "config")
Expand Down Expand Up @@ -53,28 +122,8 @@ func GetBuildSystem() string {
return "other"
}

// GetRepositoryDetails gets the repository name and branch
// multiple env vars will be checked first before falling back to the folder name
func GetRepositoryDetails(scanPath string) (repoName, branch string, err error) {

for _, repoEnv := range possibleRepoEnvVars {
if v, ok := os.LookupEnv(repoEnv); ok {
repoName = v
break
}
}

for _, branchEnv := range possibleBranchEnvVars {
if v, ok := os.LookupEnv(branchEnv); ok {
branch = v
break
}
}

if repoName != "" && branch != "" {
return repoName, branch, nil
}

// Get repository details based on FS scan
func getFsRepositoryDetails(scanPath string) (repoName, branch string, err error) {
workingDir := scanPath
abs, err := filepath.Abs(workingDir)
if err != nil {
Expand Down Expand Up @@ -107,6 +156,43 @@ func GetRepositoryDetails(scanPath string) (repoName, branch string, err error)
return inferredRepoName, "", nil
}

// GetRepositoryDetails gets the repository name and branch
// multiple env vars will be checked first before falling back to the folder name
func GetRepositoryDetails(scanPath string, cmd string) (repoName, branch string, err error) {

for _, repoEnv := range possibleRepoEnvVars {
if v, ok := os.LookupEnv(repoEnv); ok {
repoName = v
break
}
}

for _, branchEnv := range possibleBranchEnvVars {
if v, ok := os.LookupEnv(branchEnv); ok {
branch = v
break
}
}

if repoName != "" && branch != "" {
return repoName, branch, nil
}

switch cmd {
case "image":
prefix, repo, tag := GetImageDetails(scanPath)
branch = tag
repoName = GetRepositoryUrl(prefix, repo)
default:
repoName, branch, err = getFsRepositoryDetails(scanPath)
if err != nil {
return repoName, branch, fmt.Errorf("failed get FS repository details: %w", err)
}
}

return repoName, branch, nil
}

// GetCommitID gets the current CommitID of the repository
func GetCommitID(scanPath string) (commitId string) {

Expand Down
Loading

0 comments on commit 1e899b5

Please sign in to comment.