Skip to content

Commit

Permalink
Merge pull request #284 from hibell/include-features-and-ifixes-in-sbom
Browse files Browse the repository at this point in the history
Include features and ifixes in SBoM
  • Loading branch information
dmikusa authored Dec 9, 2022
2 parents 87fe051 + a507793 commit d3ac57a
Show file tree
Hide file tree
Showing 6 changed files with 213 additions and 23 deletions.
16 changes: 8 additions & 8 deletions buildpack.toml
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ api = "0.7"
name = "BP_LIBERTY_FEATURES"

[[metadata.dependencies]]
cpes = ["cpe:2.3:a:ibm:websphere_application_server:22.0.0.12:*:*:*:liberty:*:*:*"]
cpes = ["cpe:2.3:a:ibm:open_liberty:22.0.0.12:*:*:*:*:*:*:*"]
id = "open-liberty-runtime-full"
name = "Open Liberty (All Features)"
purl = "pkg:maven/io.openliberty/[email protected]"
Expand All @@ -101,7 +101,7 @@ api = "0.7"
uri = "https://raw.githubusercontent.com/OpenLiberty/open-liberty/integration/LICENSE"

[[metadata.dependencies]]
cpes = ["cpe:2.3:a:ibm:websphere_application_server:22.0.0.12:*:*:*:liberty:*:*:*"]
cpes = ["cpe:2.3:a:ibm:open_liberty:22.0.0.12:*:*:*:*:*:*:*"]
id = "open-liberty-runtime-jakartaee9"
name = "Open Liberty (Jakarta EE9)"
purl = "pkg:maven/io.openliberty/[email protected]"
Expand All @@ -115,7 +115,7 @@ api = "0.7"
uri = "https://raw.githubusercontent.com/OpenLiberty/open-liberty/integration/LICENSE"

[[metadata.dependencies]]
cpes = ["cpe:2.3:a:ibm:websphere_application_server:22.0.0.12:*:*:*:liberty:*:*:*"]
cpes = ["cpe:2.3:a:ibm:open_liberty:22.0.0.12:*:*:*:*:*:*:*"]
id = "open-liberty-runtime-javaee8"
name = "Open Liberty (Java EE8)"
purl = "pkg:maven/io.openliberty/[email protected]"
Expand All @@ -129,7 +129,7 @@ api = "0.7"
uri = "https://raw.githubusercontent.com/OpenLiberty/open-liberty/integration/LICENSE"

[[metadata.dependencies]]
cpes = ["cpe:2.3:a:ibm:websphere_application_server:22.0.0.12:*:*:*:liberty:*:*:*"]
cpes = ["cpe:2.3:a:ibm:open_liberty:22.0.0.12:*:*:*:*:*:*:*"]
id = "open-liberty-runtime-webProfile9"
name = "Open Liberty (Web Profile 9)"
purl = "pkg:maven/io.openliberty/[email protected]"
Expand All @@ -143,7 +143,7 @@ api = "0.7"
uri = "https://raw.githubusercontent.com/OpenLiberty/open-liberty/integration/LICENSE"

[[metadata.dependencies]]
cpes = ["cpe:2.3:a:ibm:websphere_application_server:22.0.0.12:*:*:*:liberty:*:*:*"]
cpes = ["cpe:2.3:a:ibm:open_liberty:22.0.0.12:*:*:*:*:*:*:*"]
id = "open-liberty-runtime-webProfile8"
name = "Open Liberty (Web Profile 8)"
purl = "pkg:maven/io.openliberty/[email protected]"
Expand All @@ -157,7 +157,7 @@ api = "0.7"
uri = "https://raw.githubusercontent.com/OpenLiberty/open-liberty/integration/LICENSE"

[[metadata.dependencies]]
cpes = ["cpe:2.3:a:ibm:websphere_application_server:22.0.0.12:*:*:*:liberty:*:*:*"]
cpes = ["cpe:2.3:a:ibm:open_liberty:22.0.0.12:*:*:*:*:*:*:*"]
id = "open-liberty-runtime-microProfile5"
name = "Open Liberty (Micro Profile 5)"
purl = "pkg:maven/io.openliberty/[email protected]"
Expand All @@ -171,7 +171,7 @@ api = "0.7"
uri = "https://raw.githubusercontent.com/OpenLiberty/open-liberty/integration/LICENSE"

[[metadata.dependencies]]
cpes = ["cpe:2.3:a:ibm:websphere_application_server:22.0.0.12:*:*:*:liberty:*:*:*"]
cpes = ["cpe:2.3:a:ibm:open_liberty:22.0.0.12:*:*:*:*:*:*:*"]
id = "open-liberty-runtime-microProfile4"
name = "Open Liberty (Micro Profile 4)"
purl = "pkg:maven/io.openliberty/[email protected]"
Expand All @@ -185,7 +185,7 @@ api = "0.7"
uri = "https://raw.githubusercontent.com/OpenLiberty/open-liberty/integration/LICENSE"

[[metadata.dependencies]]
cpes = ["cpe:2.3:a:ibm:websphere_application_server:22.0.0.12:*:*:*:liberty:*:*:*"]
cpes = ["cpe:2.3:a:ibm:open_liberty:22.0.0.12:*:*:*:*:*:*:*"]
id = "open-liberty-runtime-kernel"
name = "Open Liberty (Kernel)"
purl = "pkg:maven/io.openliberty/[email protected]"
Expand Down
63 changes: 63 additions & 0 deletions internal/server/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
package server

import (
"bufio"
"bytes"
"encoding/xml"
"fmt"
"github.com/paketo-buildpacks/liberty/internal/util"
Expand All @@ -26,6 +28,7 @@ import (
"io/ioutil"
"os"
"path/filepath"
"regexp"
"sort"
"strings"
)
Expand Down Expand Up @@ -242,6 +245,66 @@ func InstallFeatures(runtimePath string, serverName string, executor effect.Exec
return nil
}

type InstalledIFix struct {
APAR string
IFix string
}

func GetInstalledIFixes(runtimePath string, executor effect.Executor) ([]InstalledIFix, error) {
buf := &bytes.Buffer{}

if err := executor.Execute(effect.Execution{
Command: filepath.Join(runtimePath, "bin", "productInfo"),
Args: []string{"version", "--ifixes"},
Stdout: buf,
}); err != nil {
return []InstalledIFix{}, fmt.Errorf("unable to get installed iFixes\n%w", err)
}

re, err := regexp.Compile(`^(.*) in the iFix\(es\): \[(.*)\]$`)
if err != nil {
return []InstalledIFix{}, fmt.Errorf("unable to create iFix regex\n%w", err)
}

installed := make([]InstalledIFix, 0)
scanner := bufio.NewScanner(buf)
for scanner.Scan() {
line := scanner.Text()
if match := re.FindStringSubmatch(line); len(match) >= 2 {
installed = append(installed, InstalledIFix{
APAR: strings.TrimSpace(match[1]),
IFix: strings.TrimSpace(match[2]),
})
}
}

return installed, nil
}

func GetInstalledFeatures(runtimePath string, executor effect.Executor) ([]string, error) {
buf := &bytes.Buffer{}

if err := executor.Execute(effect.Execution{
Command: filepath.Join(runtimePath, "bin", "productInfo"),
Args: []string{"featureInfo"},
Stdout: buf,
}); err != nil {
return []string{}, fmt.Errorf("unable to get list of installed features\n%w", err)
}

installedFeatures := make([]string, 0)
scanner := bufio.NewScanner(buf)
for scanner.Scan() {
feature := strings.TrimSpace(scanner.Text())
if len(feature) <= 0 {
continue
}
installedFeatures = append(installedFeatures, feature)
}

return installedFeatures, nil
}

type Config struct {
XMLName xml.Name `xml:"server"`
FeatureManager struct {
Expand Down
48 changes: 48 additions & 0 deletions internal/server/server_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,34 @@ func testServer(t *testing.T, when spec.G, it spec.S) {
Expect(execution.Command).To(Equal("java"))
Expect(execution.Args).To(Equal([]string{"-jar", ifixes[1], "--installLocation", wlpPath}))
})

it("lists installed iFixes", func() {
executor := &mocks.Executor{}
executor.On("Execute", mock.Anything).Run(func(args mock.Arguments) {
arg := args.Get(0).(effect.Execution)
_, err := arg.Stdout.Write([]byte(`
Product name: Open Liberty
Product version: 22.0.0.11
Product edition: Open
PH49719 in the iFix(es): [220011-wlp-archive-IFPH49719]`),
)
Expect(err).ToNot(HaveOccurred())
}).Return(nil)
installedFixes, err := server.GetInstalledIFixes(wlpPath, executor)
Expect(err).ToNot(HaveOccurred())

execution := executor.Calls[0].Arguments[0].(effect.Execution)
Expect(execution.Command).To(Equal(filepath.Join(wlpPath, "bin", "productInfo")))
Expect(execution.Args).To(Equal([]string{"version", "--ifixes"}))

Expect(installedFixes).To(Equal([]server.InstalledIFix{
{
APAR: "PH49719",
IFix: "220011-wlp-archive-IFPH49719",
},
}))
})
})

when("installing features", func() {
Expand All @@ -244,5 +272,25 @@ func testServer(t *testing.T, when spec.G, it spec.S) {
Expect(execution.Command).To(Equal(filepath.Join(wlpPath, "bin", "featureUtility")))
Expect(execution.Args).To(Equal([]string{"installServerFeatures", "--acceptLicense", "--noCache", "testServer"}))
})

it("lists installed features", func() {
executor := &mocks.Executor{}
executor.On("Execute", mock.Anything).Run(func(args mock.Arguments) {
arg := args.Get(0).(effect.Execution)
_, err := arg.Stdout.Write([]byte(`
microProfile-5.0
webProfile-9.1`),
)
Expect(err).ToNot(HaveOccurred())
}).Return(nil)
installedFeatures, err := server.GetInstalledFeatures(wlpPath, executor)
Expect(err).ToNot(HaveOccurred())

execution := executor.Calls[0].Arguments[0].(effect.Execution)
Expect(execution.Command).To(Equal(filepath.Join(wlpPath, "bin", "productInfo")))
Expect(execution.Args).To(Equal([]string{"featureInfo"}))

Expect(installedFeatures).To(Equal([]string{"microProfile-5.0", "webProfile-9.1"}))
})
})
}
16 changes: 13 additions & 3 deletions liberty/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import (
"fmt"
"github.com/paketo-buildpacks/liberty/internal/util"
"github.com/paketo-buildpacks/libpak/bindings"
sherpa "github.com/paketo-buildpacks/libpak/sherpa"
"github.com/paketo-buildpacks/libpak/sherpa"
"strings"

"github.com/buildpacks/libcnb"
Expand Down Expand Up @@ -206,7 +206,17 @@ func (b Build) Build(context libcnb.BuildContext) (libcnb.BuildResult, error) {
result.Layers = append(result.Layers, base)

if installType == openLibertyInstall || installType == websphereLibertyInstall {
if err := b.buildDistributionRuntime(profile, version, installType, serverName, context.Application.Path, featureList, detectedBuildSrc, dr, dc, &result); err != nil {
if err := b.buildDistributionRuntime(
profile,
version,
installType,
serverName,
context.Application.Path,
featureList,
detectedBuildSrc,
dr,
dc,
&result); err != nil {
return libcnb.BuildResult{}, err
}
} else if installType == noneInstall {
Expand Down Expand Up @@ -251,7 +261,7 @@ func (b Build) buildDistributionRuntime(
return fmt.Errorf("unable to load iFixes\n%w", err)
}

distro := NewDistribution(dep, cache, serverName, appPath, features, iFixes, b.Executor)
distro := NewDistribution(dep, cache, installType, serverName, appPath, features, iFixes, b.Executor)
distro.Logger = b.Logger

result.Layers = append(result.Layers, distro)
Expand Down
73 changes: 71 additions & 2 deletions liberty/distribution.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,12 @@ package liberty

import (
"fmt"
"github.com/paketo-buildpacks/liberty/internal/server"
"github.com/paketo-buildpacks/libpak/sbom"
"os"
"path/filepath"
"strconv"

"github.com/paketo-buildpacks/liberty/internal/server"
"strings"

"github.com/buildpacks/libcnb"
"github.com/paketo-buildpacks/libjvm/count"
Expand All @@ -33,7 +34,9 @@ import (
)

type Distribution struct {
Dependency libpak.BuildpackDependency
ApplicationPath string
InstallType string
ServerName string
Executor effect.Executor
Features []string
Expand All @@ -45,6 +48,7 @@ type Distribution struct {
func NewDistribution(
dependency libpak.BuildpackDependency,
cache libpak.DependencyCache,
installType string,
serverName string,
applicationPath string,
features []string,
Expand All @@ -64,6 +68,8 @@ func NewDistribution(
}

return Distribution{
Dependency: dependency,
InstallType: installType,
ApplicationPath: applicationPath,
ServerName: serverName,
Executor: executor,
Expand Down Expand Up @@ -118,10 +124,73 @@ func (d Distribution) Contribute(layer libcnb.Layer) (libcnb.Layer, error) {
layer.LaunchEnvironment.Default("WLP_LOGGING_APPS_WRITE_JSON", "true")
layer.LaunchEnvironment.Default("WLP_LOGGING_JSON_ACCESS_LOG_FIELDS", "default")

if err := d.ContributeSBOM(layer); err != nil {
return libcnb.Layer{}, fmt.Errorf("unable to contribute SBOM\n%w", err)
}

return layer, nil
})
}

func (d Distribution) ContributeSBOM(layer libcnb.Layer) error {
sbomArtifact, err := d.Dependency.AsSyftArtifact()
if err != nil {
return fmt.Errorf("unable to get SBOM artifact %s\n%w", d.Dependency.ID, err)
}
artifacts := []sbom.SyftArtifact{sbomArtifact}

installedIFixes, err := server.GetInstalledIFixes(layer.Path, d.Executor)
if err != nil {
return fmt.Errorf("unable to get installed iFixes\n%w", err)
}
for _, ifix := range installedIFixes {
d.Logger.Debugf("Found installed iFix for APAR %s: %s\n", ifix.APAR, ifix.IFix)
artifacts = append(artifacts, sbom.SyftArtifact{
ID: ifix.APAR,
Name: ifix.APAR,
Version: sbomArtifact.Version,
Type: "jar",
Locations: []sbom.SyftLocation{{Path: ifix.IFix + ".jar"}},
})
}
installedFeatures, err := server.GetInstalledFeatures(layer.Path, d.Executor)
if err != nil {
return fmt.Errorf("unable to get installed features\n%w", err)
}
var groupId string
if d.InstallType == openLibertyInstall {
groupId = "io.openliberty.features"
} else {
groupId = "com.ibm.websphere.appserver.features"
}

var version string
if parts := strings.Split(sbomArtifact.PURL, "@"); len(parts) == 2 {
version = parts[1]
}
for _, feature := range installedFeatures {
d.Logger.Debugf("Found installed feature: %s", feature)
artifacts = append(artifacts, sbom.SyftArtifact{
ID: feature,
Name: feature,
Version: sbomArtifact.Version,
Type: "esa",
PURL: fmt.Sprintf("pkg:maven/%s/%s@%s", groupId, feature, version),
})

}

sbomPath := layer.SBOMPath(libcnb.SyftJSON)
dep := sbom.NewSyftDependency(layer.Path, artifacts)

d.Logger.Debugf("Writing Syft SBOM at %s: %+v", sbomPath, dep)
if err := dep.WriteTo(sbomPath); err != nil {
return fmt.Errorf("unable to write SBOM\n%w", err)
}

return nil
}

func (d Distribution) Name() string {
return d.LayerContributor.LayerName()
}
Expand Down
Loading

0 comments on commit d3ac57a

Please sign in to comment.