Skip to content

Commit

Permalink
security-scan: conditionally allow scanning pre-release versions (#543)
Browse files Browse the repository at this point in the history
  • Loading branch information
shashankram authored Nov 22, 2024
1 parent e020563 commit df75e5e
Show file tree
Hide file tree
Showing 7 changed files with 46 additions and 41 deletions.
4 changes: 4 additions & 0 deletions changelog/v0.27.2/enable-pre-release-scan.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
changelog:
- type: NON_USER_FACING
description: >
Enable security scans for pre-releases if --enable-pre-release=true.
4 changes: 2 additions & 2 deletions securityscanutils/commands/format_results.go
Original file line number Diff line number Diff line change
Expand Up @@ -239,7 +239,7 @@ func readImageVersionConstraintsFile(opts *formatResultsOptions) (map[string][]s
if len(values) < 2 {
return nil, MalformedVersionImageConstraintLine(line)
}
for i, _ := range values {
for i := range values {
trimVal := strings.TrimSpace(values[i])
values[i] = trimVal
if i > 0 {
Expand All @@ -249,7 +249,7 @@ func readImageVersionConstraintsFile(opts *formatResultsOptions) (map[string][]s
imagesPerVersion[values[0]] = values[1:]
}
var allImages []string
for image, _ := range imageSet {
for image := range imageSet {
allImages = append(allImages, image)
}
opts.allImages = allImages
Expand Down
5 changes: 5 additions & 0 deletions securityscanutils/commands/scan_repo.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ type scanRepoOptions struct {
releaseVersionConstraint string
imagesVersionConstraintFile string
additionalContextFile string

enablePreRelease bool
}

func (m *scanRepoOptions) addToFlags(flags *pflag.FlagSet) {
Expand All @@ -58,6 +60,8 @@ func (m *scanRepoOptions) addToFlags(flags *pflag.FlagSet) {
flags.StringVarP(&m.vulnerabilityAction, "vulnerability-action", "a", "none", "action to take when a vulnerability is discovered {none, github-issue-all, github-issue-latest, output-locally}")

flags.StringVarP(&m.releaseVersionConstraint, "release-constraint", "c", "", "version constraint for releases to scan")
flags.BoolVar(&m.enablePreRelease, "enable-pre-release", false, "enable pre-release versions to be scanned")

flags.StringVarP(&m.imagesVersionConstraintFile, "image-constraint-file", "i", "", "name of file with mapping of version to images")
flags.StringVarP(&m.additionalContextFile, "additional-context-file", "d", "", "name of file with any additional context to add to the top of the generated vulnerability report")

Expand Down Expand Up @@ -94,6 +98,7 @@ func doScanRepo(ctx context.Context, opts *scanRepoOptions) error {
CreateGithubIssuePerVersion: opts.vulnerabilityAction == "github-issue-all",
CreateGithubIssueForLatestPatchVersion: opts.vulnerabilityAction == "github-issue-latest",
AdditionalContext: additionalContext,
EnablePreRelease: opts.enablePreRelease,
},
},
},
Expand Down
10 changes: 4 additions & 6 deletions securityscanutils/commands/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,9 @@ import (
"github.com/rotisserie/eris"
)

var (
MalformedVersionImageConstraintLine = func(line string) error {
return eris.Errorf("Could not properly split version image constraint line: %s", line)
}
)
var MalformedVersionImageConstraintLine = func(line string) error {
return eris.Errorf("Could not properly split version image constraint line: %s", line)
}

// GetImagesPerVersionFromFile Reads in a file, and tries to turn it into a map from version constraints to lists of images
// As a byproduct, it also caches all unique images found into the option field 'allImages'
Expand All @@ -33,7 +31,7 @@ func GetImagesPerVersionFromFile(constraintsFile string) (map[string][]string, e
if len(values) < 2 {
return nil, MalformedVersionImageConstraintLine(line)
}
for i, _ := range values {
for i := range values {
trimVal := strings.TrimSpace(values[i])
values[i] = trimVal
if i > 0 {
Expand Down
13 changes: 9 additions & 4 deletions securityscanutils/predicate.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,24 +11,29 @@ import (
// The securityScanRepositoryReleasePredicate is responsible for defining which
// github.RepositoryRelease artifacts should be included in the bulk security scan
// At the moment, the two requirements are that:
// 1. The release is not a pre-release or draft
// 1. The release is not a pre-release or draft, unless explicitly enabled
// 2. The release matches the configured version constraint
type securityScanRepositoryReleasePredicate struct {
versionConstraint *semver.Constraints
enablePreRelease bool
}

func NewSecurityScanRepositoryReleasePredicate(constraint *semver.Constraints) *securityScanRepositoryReleasePredicate {
func NewSecurityScanRepositoryReleasePredicate(
constraint *semver.Constraints,
enablePreRelease bool,
) *securityScanRepositoryReleasePredicate {
return &securityScanRepositoryReleasePredicate{
versionConstraint: constraint,
enablePreRelease: enablePreRelease,
}
}

func (s *securityScanRepositoryReleasePredicate) Apply(release *github.RepositoryRelease) bool {
// Note: GetPrerelease() is referring to a pre-release in GitHub. The term pre-release is
// slightly overloaded between GitHub and semver. We _do_ want to scan semver pre-releases
// as those correspond to GitHub releases whose tag matches the pattern of a semver pre-release.
if release.GetPrerelease() || release.GetDraft() {
// Do not include pre-releases or drafts
if (release.GetPrerelease() && !s.enablePreRelease) || release.GetDraft() {
// Do not include drafts or pre-releases unless explicitly enabled
return false
}

Expand Down
45 changes: 17 additions & 28 deletions securityscanutils/predicate_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,55 +12,46 @@ import (
)

var _ = Describe("Predicate", func() {

Context("securityScanRepositoryReleasePredicate", func() {

var (
releasePredicate githubutils.RepositoryReleasePredicate
)

BeforeEach(func() {
twoPlusConstraint, err := semver.NewConstraint(fmt.Sprintf(">= %s", "v2.0.0-0"))
Expect(err).NotTo(HaveOccurred())

releasePredicate = securityscanutils.NewSecurityScanRepositoryReleasePredicate(twoPlusConstraint)
})

DescribeTable(
"Returns true/false based on release properties",
func(release *github.RepositoryRelease, expectedResult bool) {
func(release *github.RepositoryRelease, enablePreRelease bool, expectedResult bool) {
twoPlusConstraint, err := semver.NewConstraint(fmt.Sprintf(">= %s", "v2.0.0-0"))
Expect(err).NotTo(HaveOccurred())

releasePredicate := securityscanutils.NewSecurityScanRepositoryReleasePredicate(twoPlusConstraint, enablePreRelease)
Expect(releasePredicate.Apply(release)).To(Equal(expectedResult))
},
Entry("release is draft", &github.RepositoryRelease{
Draft: github.Bool(true),
}, false),
}, false, false),
Entry("release tag does not respect semver", &github.RepositoryRelease{
TagName: github.String("non-semver-tag-name"),
}, false),
}, false, false),
Entry("release tag does not pass version constraint", &github.RepositoryRelease{
TagName: github.String("v1.0.0"),
}, false),
}, false, false),
Entry("release tag does pass version constraint", &github.RepositoryRelease{
TagName: github.String("v2.0.1"),
}, true),
}, false, true),
Entry("release tag has beta", &github.RepositoryRelease{
TagName: github.String("v2.0.1-beta1"),
}, true),
}, false, true),
Entry("release tag has rc", &github.RepositoryRelease{
TagName: github.String("v2.0.1-rc2"),
}, true),
}, false, true),
Entry("release is a pre-release", &github.RepositoryRelease{
Prerelease: github.Bool(true),
}, false),
}, false, false),
Entry("pre-release scan is enabled", &github.RepositoryRelease{
TagName: github.String("v2.0.1-alpha.0"),
Prerelease: github.Bool(true),
}, true, true),
)

})

Context("latestPatchRepositoryReleasePredicate", func() {

var (
releasePredicate githubutils.RepositoryReleasePredicate
)
var releasePredicate githubutils.RepositoryReleasePredicate

BeforeEach(func() {
releaseSet := []*github.RepositoryRelease{
Expand Down Expand Up @@ -93,7 +84,5 @@ var _ = Describe("Predicate", func() {
TagName: github.String("v1.0.3"),
}, true),
)

})

})
6 changes: 5 additions & 1 deletion securityscanutils/securityscan.go
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,9 @@ type SecurityScanOpts struct {
// Additional context to add to the top of the generated vulnerability report.
// Example: This could be used to provide debug instructions to developers.
AdditionalContext string

// Enable scanning of pre-release versions
EnablePreRelease bool
}

// GenerateSecurityScans generates .md files and writes them to the configured OutputDir for each repo
Expand Down Expand Up @@ -156,7 +159,8 @@ func (s *SecurityScanner) initializeRepoConfiguration(ctx context.Context, repo
repoOptions := repo.Opts

// Set the Predicate used to filter releases we wish to scan
repo.scanReleasePredicate = NewSecurityScanRepositoryReleasePredicate(repoOptions.VersionConstraint)
repo.scanReleasePredicate = NewSecurityScanRepositoryReleasePredicate(
repoOptions.VersionConstraint, repoOptions.EnablePreRelease)

logger.Debugf("Scanning github repo for releases that match version constraint: %s", repoOptions.VersionConstraint)

Expand Down

0 comments on commit df75e5e

Please sign in to comment.