github.com/Racer159/jackal@v0.32.7-0.20240401174413-0bd2339e4f2e/src/pkg/layout/sbom.go (about)

     1  // SPDX-License-Identifier: Apache-2.0
     2  // SPDX-FileCopyrightText: 2021-Present The Jackal Authors
     3  
     4  // Package layout contains functions for interacting with Jackal's package layout on disk.
     5  package layout
     6  
     7  import (
     8  	"fmt"
     9  	"io/fs"
    10  	"os"
    11  	"path/filepath"
    12  
    13  	"github.com/defenseunicorns/pkg/helpers"
    14  	"github.com/mholt/archiver/v3"
    15  )
    16  
    17  // ComponentSBOM contains paths for a component's SBOM.
    18  type ComponentSBOM struct {
    19  	Files     []string
    20  	Component *ComponentPaths
    21  }
    22  
    23  // SBOMs contains paths for SBOMs.
    24  type SBOMs struct {
    25  	Path string
    26  }
    27  
    28  // Unarchive unarchives the package's SBOMs.
    29  func (s *SBOMs) Unarchive() (err error) {
    30  	if s.Path == "" || helpers.InvalidPath(s.Path) {
    31  		return &fs.PathError{
    32  			Op:   "stat",
    33  			Path: s.Path,
    34  			Err:  fs.ErrNotExist,
    35  		}
    36  	}
    37  	if helpers.IsDir(s.Path) {
    38  		return nil
    39  	}
    40  	tb := s.Path
    41  	dir := filepath.Join(filepath.Dir(tb), SBOMDir)
    42  	if err := archiver.Unarchive(tb, dir); err != nil {
    43  		return err
    44  	}
    45  	s.Path = dir
    46  	return os.Remove(tb)
    47  }
    48  
    49  // Archive archives the package's SBOMs.
    50  func (s *SBOMs) Archive() (err error) {
    51  	if s.Path == "" || helpers.InvalidPath(s.Path) {
    52  		return &fs.PathError{
    53  			Op:   "stat",
    54  			Path: s.Path,
    55  			Err:  fs.ErrNotExist,
    56  		}
    57  	}
    58  	if !helpers.IsDir(s.Path) {
    59  		return nil
    60  	}
    61  	dir := s.Path
    62  	tb := filepath.Join(filepath.Dir(dir), SBOMTar)
    63  
    64  	if err := helpers.CreateReproducibleTarballFromDir(dir, "", tb); err != nil {
    65  		return err
    66  	}
    67  	s.Path = tb
    68  	return os.RemoveAll(dir)
    69  }
    70  
    71  // StageSBOMViewFiles copies SBOM viewer HTML files to the Jackal SBOM directory.
    72  func (s *SBOMs) StageSBOMViewFiles() (sbomViewFiles, warnings []string, err error) {
    73  	if s.IsTarball() {
    74  		return nil, nil, fmt.Errorf("unable to process the SBOM files for this package: %s is a tarball", s.Path)
    75  	}
    76  
    77  	// If SBOMs were loaded, temporarily place them in the deploy directory
    78  	if !helpers.InvalidPath(s.Path) {
    79  		sbomViewFiles, err = filepath.Glob(filepath.Join(s.Path, "sbom-viewer-*"))
    80  		if err != nil {
    81  			return nil, nil, err
    82  		}
    83  
    84  		if _, err := s.OutputSBOMFiles(SBOMDir, ""); err != nil {
    85  			// Don't stop the deployment, let the user decide if they want to continue the deployment
    86  			warning := fmt.Sprintf("Unable to process the SBOM files for this package: %s", err.Error())
    87  			warnings = append(warnings, warning)
    88  		}
    89  	}
    90  
    91  	return sbomViewFiles, warnings, nil
    92  }
    93  
    94  // OutputSBOMFiles outputs SBOM files into outputDir.
    95  func (s *SBOMs) OutputSBOMFiles(outputDir, packageName string) (string, error) {
    96  	packagePath := filepath.Join(outputDir, packageName)
    97  
    98  	if err := os.RemoveAll(packagePath); err != nil {
    99  		return "", err
   100  	}
   101  
   102  	if err := helpers.CreateDirectory(packagePath, 0700); err != nil {
   103  		return "", err
   104  	}
   105  
   106  	return packagePath, helpers.CreatePathAndCopy(s.Path, packagePath)
   107  }
   108  
   109  // IsTarball returns true if the SBOMs are a tarball.
   110  func (s SBOMs) IsTarball() bool {
   111  	return !helpers.IsDir(s.Path) && filepath.Ext(s.Path) == ".tar"
   112  }