diff --git a/carton/buildpack_dependency.go b/carton/buildpack_dependency.go index 6e901a4..618155c 100644 --- a/carton/buildpack_dependency.go +++ b/carton/buildpack_dependency.go @@ -40,6 +40,10 @@ type BuildpackDependency struct { URI string Version string VersionPattern string + CPE string + CPEPattern string + PURL string + PURLPattern string } func (b BuildpackDependency) Update(options ...Option) { @@ -54,12 +58,26 @@ func (b BuildpackDependency) Update(options ...Option) { logger := bard.NewLogger(os.Stdout) _, _ = fmt.Fprintf(logger.TitleWriter(), "\n%s\n", bard.FormatIdentity(b.ID, b.VersionPattern)) logger.Headerf("Version: %s", b.Version) + logger.Headerf("PURL: %s", b.PURL) + logger.Headerf("CPEs: %s", b.CPE) logger.Headerf("URI: %s", b.URI) logger.Headerf("SHA256: %s", b.SHA256) versionExp, err := regexp.Compile(b.VersionPattern) if err != nil { - config.exitHandler.Error(fmt.Errorf("unable to compile regex %s\n%w", b.VersionPattern, err)) + config.exitHandler.Error(fmt.Errorf("unable to compile version regex %s\n%w", b.VersionPattern, err)) + return + } + + cpeExp, err := regexp.Compile(b.CPEPattern) + if err != nil { + config.exitHandler.Error(fmt.Errorf("unable to compile cpe regex %s\n%w", b.CPEPattern, err)) + return + } + + purlExp, err := regexp.Compile(b.PURLPattern) + if err != nil { + config.exitHandler.Error(fmt.Errorf("unable to compile cpe regex %s\n%w", b.PURLPattern, err)) return } @@ -135,6 +153,36 @@ func (b BuildpackDependency) Update(options ...Option) { dep["uri"] = b.URI dep["sha256"] = b.SHA256 } + + purlUnwrapped, found := dep["purl"] + if !found { + continue + } + + purl, ok := purlUnwrapped.(string) + if !ok { + continue + } + dep["purl"] = purlExp.ReplaceAllString(purl, b.PURL) + + cpesUnwrapped, found := dep["cpes"] + if !found { + continue + } + + cpes, ok := cpesUnwrapped.([]interface{}) + if !ok { + continue + } + + for i := 0; i < len(cpes); i++ { + cpe, ok := cpes[i].(string) + if !ok { + continue + } + + cpes[i] = cpeExp.ReplaceAllString(cpe, b.CPE) + } } } diff --git a/carton/buildpack_dependency_test.go b/carton/buildpack_dependency_test.go index 90ba4e8..641c432 100644 --- a/carton/buildpack_dependency_test.go +++ b/carton/buildpack_dependency_test.go @@ -96,6 +96,56 @@ stacks = [ "test-stack" ] `)) }) + it("updates dependency with purl & cpes", func() { + Expect(ioutil.WriteFile(path, []byte(`api = "0.7" +[buildpack] +id = "some-buildpack" +name = "Some Buildpack" +version = "1.2.3" + +[[metadata.dependencies]] +id = "test-id" +name = "Test Name" +version = "test-version-1" +uri = "test-uri-1" +sha256 = "test-sha256-1" +stacks = [ "test-stack" ] +purl = "pkg:generic/test-jre@different-version-1?arch=amd64" +cpes = ["cpe:2.3:a:test-vendor:test-product:test-version-1:patch1:*:*:*:*:*:*:*"] +`), 0644)).To(Succeed()) + + d := carton.BuildpackDependency{ + BuildpackPath: path, + ID: "test-id", + SHA256: "test-sha256-2", + URI: "test-uri-2", + Version: "test-version-2", + VersionPattern: `test-version-[\d]`, + PURL: "different-version-2", + PURLPattern: `different-version-[\d]`, + CPE: "test-version-2:patch2", + CPEPattern: `test-version-[\d]:patch[\d]`, + } + + d.Update(carton.WithExitHandler(exitHandler)) + + Expect(ioutil.ReadFile(path)).To(internal.MatchTOML(`api = "0.7" +[buildpack] +id = "some-buildpack" +name = "Some Buildpack" +version = "1.2.3" + +[[metadata.dependencies]]id = "test-id" +name = "Test Name" +version = "test-version-2" +uri = "test-uri-2" +sha256 = "test-sha256-2" +stacks = [ "test-stack" ] +purl = "pkg:generic/test-jre@different-version-2?arch=amd64" +cpes = ["cpe:2.3:a:test-vendor:test-product:test-version-2:patch2:*:*:*:*:*:*:*"] +`)) + }) + it("updates indented dependency", func() { Expect(ioutil.WriteFile(path, []byte(`# it should preserve # these comments diff --git a/cmd/update-buildpack-dependency/main.go b/cmd/update-buildpack-dependency/main.go index 2ef89d4..f8ec7d2 100644 --- a/cmd/update-buildpack-dependency/main.go +++ b/cmd/update-buildpack-dependency/main.go @@ -36,6 +36,10 @@ func main() { flagSet.StringVar(&b.URI, "uri", "", "the new uri of the dependency") flagSet.StringVar(&b.Version, "version", "", "the new version of the dependency") flagSet.StringVar(&b.VersionPattern, "version-pattern", "", "the version pattern of the dependency") + flagSet.StringVar(&b.PURL, "purl", "", "the new purl version of the dependency, if not set defaults to version") + flagSet.StringVar(&b.PURLPattern, "purl-pattern", "", "the purl version pattern of the dependency, if not set defaults to version-pattern") + flagSet.StringVar(&b.CPE, "cpe", "", "the new version use in all CPEs, if not set defaults to version") + flagSet.StringVar(&b.CPEPattern, "cpe-pattern", "", "the cpe version pattern of the dependency, if not set defaults to version-pattern") if err := flagSet.Parse(os.Args[1:]); err != nil { log.Fatal(fmt.Errorf("unable to parse flags\n%w", err)) @@ -65,5 +69,21 @@ func main() { log.Fatal("version-pattern must be set") } + if b.PURL == "" { + b.PURL = b.Version + } + + if b.PURLPattern == "" { + b.PURLPattern = b.VersionPattern + } + + if b.CPE == "" { + b.CPE = b.Version + } + + if b.CPEPattern == "" { + b.CPEPattern = b.VersionPattern + } + b.Update() }