Skip to content

Commit

Permalink
#800. Update api sbom.go with a DeleteSBOM and cleanupSBOM method to …
Browse files Browse the repository at this point in the history
…remove zarf-sbom folder, ExtractSBOM now just looks for a path param instead of requiring the entire package. Update api start.go with get sbom/{path} and delete /sbom paths. Update ui api with the correct get and delete methods for sbom. Update ui BuildProvidence component with proper copy and made the link stand out per madeline. Update ui /packages layout to remove zarf-sbom folder on destroy.
  • Loading branch information
mike-winberry committed May 18, 2023
1 parent bfad7c5 commit 00262a5
Show file tree
Hide file tree
Showing 7 changed files with 70 additions and 42 deletions.
71 changes: 42 additions & 29 deletions src/internal/api/packages/sbom.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,19 @@
package packages

import (
"encoding/json"
"net/http"
"net/url"
"os"
"os/signal"
"path/filepath"
"syscall"

"github.com/defenseunicorns/zarf/src/config"
"github.com/defenseunicorns/zarf/src/internal/api/common"
"github.com/defenseunicorns/zarf/src/pkg/message"
"github.com/defenseunicorns/zarf/src/pkg/utils"
"github.com/defenseunicorns/zarf/src/types"
"github.com/go-chi/chi/v5"
"github.com/mholt/archiver/v3"
)

Expand All @@ -24,13 +26,9 @@ var filePaths = make(map[string]string)

// ExtractSBOM Extracts the SBOM from the package and returns the path to the SBOM
func ExtractSBOM(w http.ResponseWriter, r *http.Request) {
var body types.APIZarfPackage
err := json.NewDecoder(r.Body).Decode(&body)
if err != nil {
message.ErrorWebf(err, w, "Unable to decode the requested package")
return
}
sbom, err := extractSBOM(&body)
path := chi.URLParam(r, "path")

sbom, err := extractSBOM(path)

if err != nil {
message.ErrorWebf(err, w, err.Error())
Expand All @@ -40,56 +38,71 @@ func ExtractSBOM(w http.ResponseWriter, r *http.Request) {

}

func DeleteSBOM(w http.ResponseWriter, r *http.Request) {
err := cleanupSBOM()
if err != nil {
message.ErrorWebf(err, w, err.Error())
return
}
common.WriteJSONResponse(w, nil, http.StatusOK)
}

// cleanupSBOM removes the SBOM directory
func cleanupSBOM() error {
err := os.RemoveAll(config.ZarfSBOMDir)
if err != nil {
return err
}
filePaths = make(map[string]string)
return nil
}

// Extracts the SBOM from the package and returns the path to the SBOM
func extractSBOM(pkg *types.APIZarfPackage) (sbom types.APIPackageSBOM, err error) {
func extractSBOM(escapedPath string) (sbom types.APIPackageSBOM, err error) {
const sbomDir = "zarf-sbom"
const SBOM = "sboms.tar"

path := pkg.Path
name := pkg.ZarfPackage.Metadata.Name
path, err := url.QueryUnescape(escapedPath)
if err != nil {
return sbom, err
}

// Check if the SBOM has already been extracted
if filePaths[name] != "" {
sbom, err = getSbomViewFiles(filePaths[name])
if filePaths[path] != "" {
sbom, err = getSbomViewFiles(filePaths[path])
} else {
// Get the current working directory
cwd, err := os.UserHomeDir()
if err != nil {
return sbom, err
}
// ensure the package exists
if _, err := os.Stat(pkg.Path); os.IsNotExist(err) {
if _, err := os.Stat(path); os.IsNotExist(err) {
return sbom, err
}
tmpPath := filepath.Join(cwd, sbomDir, name)

// tmpSBOMs := filepath.Join(config.CommonOptions.TempDirectory, "sboms")
tmpDir, err := utils.MakeTempDir(tmpPath)
// Create the Zarf SBOM directory
tmpDir, err := utils.MakeTempDir(config.ZarfSBOMDir)
if err != nil {
return sbom, err
}
cleanup := func() {
os.RemoveAll(tmpDir)
}

// Extract the SBOM tar.gz from the package
err = archiver.Extract(path, SBOM, tmpDir)
if err != nil {
cleanup()
cleanupSBOM()
return sbom, err
}

// Unarchive the SBOM tar.gz
err = archiver.Unarchive(filepath.Join(tmpDir, SBOM), tmpDir)
if err != nil {
cleanup()
cleanupSBOM()
return sbom, err
}

// Get the SBOM viewer files
sbom, err = getSbomViewFiles(tmpDir)
if err != nil {
cleanup()
cleanupSBOM()
return sbom, err
}

Expand All @@ -99,22 +112,22 @@ func extractSBOM(pkg *types.APIZarfPackage) (sbom types.APIPackageSBOM, err erro
// Wait for a signal to be received
<-signalChan

// Call the cleanup function
os.RemoveAll(filepath.Join(cwd, sbomDir))
cleanupSBOM()

// Exit the program
os.Exit(0)
}()

filePaths[name] = tmpDir
filePaths[path] = tmpDir
}
return sbom, err
}

func getSbomViewFiles(sbomPath string) (sbom types.APIPackageSBOM, err error) {
cwd, _ := os.Getwd()
sbomViewFiles, err := filepath.Glob(filepath.Join(sbomPath, "sbom-viewer-*"))
if len(sbomViewFiles) > 0 {
sbom.Path = sbomViewFiles[0]
sbom.Path = filepath.Join(cwd, sbomViewFiles[0])
sbom.SBOMS = sbomViewFiles
}
return sbom, err
Expand Down
3 changes: 2 additions & 1 deletion src/internal/api/start.go
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,8 @@ func LaunchAPIServer() {
r.Delete("/{pkg}/disconnect/{name}", packages.DisconnectTunnel)
r.Get("/{pkg}/connections", packages.ListPackageConnections)
r.Get("/connections", packages.ListConnections)
r.Put("/sbom", packages.ExtractSBOM)
r.Get("/sbom/{path}", packages.ExtractSBOM)
r.Delete("/sbom", packages.DeleteSBOM)
})

r.Route("/components", func(r chi.Router) {
Expand Down
3 changes: 2 additions & 1 deletion src/ui/lib/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,8 @@ const Packages = {
http.del(
`/packages/${encodeURIComponent(pkgName)}/disconnect/${encodeURIComponent(connectionName)}`
),
sbom: (pkg: APIZarfPackage) => http.put<APIPackageSBOM>(`/packages/sbom`, pkg),
sbom: (path: string) => http.get<APIPackageSBOM>(`/packages/sbom/${encodeURIComponent(path)}`),
cleanSBOM: () => http.del('/packages/sbom'),
};

const DeployingComponents = {
Expand Down
12 changes: 9 additions & 3 deletions src/ui/lib/components/build-providence.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -58,11 +58,17 @@
</div>
</div>
<Typography variant="subtitle2">Software Bill of Materials (SBOM)</Typography>
{#await Packages.sbom($pkgStore) then sbom}
{#await Packages.sbom($pkgStore.path) then sbom}
<Typography element="p" variant="body2" color="text-secondary-on-dark">
This package has {sbom.sboms.length} images with software SBOMs included. You can view them now
in the zarf-sbom folder in your home directory or to go directly to one, open this in your browser:
<Typography variant="inherit" element="span" on:click={copyToClipboard}>
in the zarf-sbom folder in this directory or to go directly to one, open this in your browser:
<Typography
color="primary"
style="text-decoration: underline; cursor: pointer;"
variant="inherit"
element="span"
on:click={copyToClipboard}
>
{sbom.path}
</Typography>
<CopyToClipboard bind:copyToClipboard text={sbom.path} variant="h6" />
Expand Down
7 changes: 7 additions & 0 deletions src/ui/routes/packages/+layout.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@
-->
<script lang="ts">
import { page } from '$app/stores';
import { Packages } from '$lib/api';
import { Box, Stepper, type SSX } from '@ui';
import { onDestroy } from 'svelte';
const stepMap: Record<string, number> = {
packages: 1,
Expand All @@ -13,6 +15,11 @@
deploy: 4,
};
// Clean any sboms that may have been extracted.
onDestroy(() => {
Packages.cleanSBOM();
});
$: stepName = $page.route.id?.split('/').pop() || '';
$: stepNumber = (stepName && stepMap[stepName]) || 500;
$: getIconContent = (number: number): string | undefined =>
Expand Down
8 changes: 4 additions & 4 deletions src/ui/routes/packages/[name]/configure/+page.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,10 @@
<SectionHeader icon="secured_layer">
Supply Chain
<span slot="tooltip">
Supply chain information is used to help determine if a package can be trusted. It includes declarative data
regarding how this package was built. Build providence includes metadata about the build and
where the package was created. SBOM includes information on all of the code, images, and
resources contained in this package.
Supply chain information is used to help determine if a package can be trusted. It includes
declarative data regarding how this package was built. Build providence includes metadata about
the build and where the package was created. SBOM includes information on all of the code,
images, and resources contained in this package.
</span>
</SectionHeader>
<BuildProvidence build={$pkgStore.zarfPackage.build} />
Expand Down
8 changes: 4 additions & 4 deletions src/ui/routes/packages/[name]/review/+page.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,10 @@
<SectionHeader icon="secured_layer">
Supply Chain
<span slot="tooltip">
Supply chain information is used to help determine if a package can be trusted. It includes declarative data
regarding how this package was built. Build providence includes metadata about the build and
where the package was created. SBOM includes information on all of the code, images, and
resources contained in this package.
Supply chain information is used to help determine if a package can be trusted. It includes
declarative data regarding how this package was built. Build providence includes metadata about
the build and where the package was created. SBOM includes information on all of the code,
images, and resources contained in this package.
</span>
</SectionHeader>
<BuildProvidence build={$pkgStore.zarfPackage.build} />
Expand Down

0 comments on commit 00262a5

Please sign in to comment.