Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

security-scan: conditionally allow scanning pre-release versions #543

Merged
merged 1 commit into from
Nov 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading