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

Edit for spdx and cdx sboms #92

Merged
merged 4 commits into from
Aug 21, 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
148 changes: 145 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
[![OpenSSF Scorecard](https://api.securityscorecards.dev/projects/github.com/interlynk-io/sbomasm/badge)](https://securityscorecards.dev/viewer/?uri=github.com/interlynk-io/sbomasm)
![GitHub all releases](https://img.shields.io/github/downloads/interlynk-io/sbomasm/total)

`sbomasm` is your primary tool to assemble SBOMs, for easy management and distribution.
`sbomasm` is your primary tool to assemble and edit SBOMs, for easy management and distribution.

```sh
go install github.com/interlynk-io/sbomasm@latest
Expand All @@ -35,11 +35,13 @@ other installation [options](#installation).
)](https://app.interlynk.io/customer/products?id=c706ae8e-56dc-4386-9c8e-11c2401c0e94&signed_url_params=eyJfcmFpbHMiOnsibWVzc2FnZSI6IklqbGtaVFZqTVdKaUxUSTJPV0V0TkdNeE55MWhaVEZpTFRBek1ETmlOREF3TlRjNFpDST0iLCJleHAiOm51bGwsInB1ciI6InNoYXJlX2x5bmsvc2hhcmVfbHluayJ9fQ==--84180d9ed3c786dce7119abc7fc35eb7adb0fbc8a9093c4f6e7e5d0ad778089e)

# Usage

### Assemle SBOMs
```sh
`SPDX` assemble multiple SBOMs
```sh
sbomasm assemble -n "mega spdx app" -v "1.0.0" -t "application" -o final-product.spdx.json sdk.spdx.json demo-app.spdx.json report.spdx.json
```

`CDX` assemble multiple SBOMs
```sh
sbomasm assemble -n "mega cdx app" -v "1.0.0" -t "application" -o final-product.cdx.json sbom1.json sbom2.json sbom3.json
Expand All @@ -57,10 +59,32 @@ docker run -v .:/app/sboms/ ghcr.io/interlynk-io/sbomasm:v0.1.3 assemble -n "ass
sbomasm assemble -n "mega cdx app" -v "1.0.0" -t "application" -e 1.4 -o final-product.cdx.json sbom1.json sbom2.json sbom3.json
```

### Edit SBOMs
Change the name and version of the primary component.
```sh
sbomasm edit --subject primary-component --name "cool-app" --version "v1.0.0" --type "application" --output cool-app-mod.spdx.json cool-app.spdx.json
```

Add supplier information & timestamp to the document, if missing.
```sh
sbomasm edit --missing --subject document --timestamp --supplier "interlynk ([email protected])" in-sbom-cdx.json
```

Append a new author to the primary component.
```sh
sbomasm edit --append --subject primary-component --author "abc ([email protected])" in-sbom-2.json
```

Find a component by name & version and add update its purl
```sh
sbomasm edit --subject component-name-version --search "abc (v1.0.0)" --purl "pkg:deb/debian/[email protected]" in-sbom-3.json
```

# Features
- SBOM format agnostic
- Supports Hierarchial and Flat merging
- Supports Hierarchial/Flat and Assemble merging
- Configurable primary component/package
- Edit metadata for SBOMs
- Blazing fast :rocket:

# Why should we assemble SBOMs?
Expand Down Expand Up @@ -213,6 +237,124 @@ To get more details in case of issues or just information, run the above command

The assembled SBOM can now be monitored using any SBOM monitoring tool of your choice. If you don't have one, contact us, we are building an SBOM monitor product to help with this.


# Edit
The edit command allows you to modify an existing Software Bill of Materials (SBOM) by filling in gaps or adding information that may have been missed during the generation process. This command operates by first locating the entity to edit and then adding the required information. The goal of edit is not to provide a full editing experience but to help fill in filling in missing information useful for compliance and security purposes

## How it works
The edit command works based on locating entities and then modifying their metadata.

We support locating the following entities.
- *Document*: This is the SBOM itself.
- *Primary Component*: The primary component described by the SBOM.
- *Any Component via search*: Any component or package described by the SBOM, which can be located by name & version.

We support the following modifications operations
- *Overwrite (default)*: This operation replaces the existing value.
- *Append*: This operation appends the new value to the existing value.
- *Missing*: This operation is only applied if the field or value is missing.

## Fields supported

`Document`
| Input Param | Input Format | CDX Spec Field | SPDX Spec field |
|----------|----------|----------| -----------------------------|
| author | "name (email)" | Metadata->authors | CreationInfo->Creator->Person|
| supplier | "name (url)" | Metadata->Supplier | CreationInfo->Creator->Comment |
| tool | "name (version)" | Metadata->Tools | CreationInfo->Creator->Tool |
| lifecycle | "build" | Metadata->lifecycles->phase | - |
| type | "application" | - | - |
| name | "name" | - | - |
| version | "1.0.0" | - | - |
| description | "description" | - | DocumentComment |
| copyright| "abc @2023" | - | - |
| repository | "github.com/interlynk/sbomasm"| bom->externalreferences | - |
| cpe | "cpe:2.3:a:apache:tomcat:9.0.0:*:*:*:*:*:*:*" | - | - |
| purl| "pkg:github/apache/[email protected]" | - | - |
| hash | "MD5 (1234567890)" | - | - |
| license | "MIT (mit.edu/~amini/LICENSE.md)" | Metadata->Licenses | DataLicense |
| timestamp | "2023-05-03T04:49:33.378-0700" | Metadata->timestamp | CreationInfo->Created |


`Primary Component & Component Name Version`
| Input Param | Input Format | CDX Spec Field | SPDX Spec field |
|----------|----------|----------| -----------------------------|
| author | "name (email)" | Comp->authors or author | - |
| supplier | "name (url)" | Comp->Supplier | Pkg->Supplier |
| tool | "name (version)" | - | - |
| lifecycle | "build" | - | - |
| type | "application" | Comp->Type | Pkg->PrimaryPackagePurpose |
| name | "name" | Comp->name | Pkg->PackageName |
| version | "1.0.0" | Comp->version | Pkg->PackageVersion |
| description | "description" | Comp->Description | Pkg->PackageDescription |
| copyright| "abc @2023" | Comp->copyright | pkg->copyright |
| repository | "github.com/interlynk/sbomasm"| Comp->externalreferences | Pkg->PackageDownloadLocation |
| cpe | "cpe:2.3:a:apache:tomcat:9.0.0:*:*:*:*:*:*:*" | Comp->cpe | Pkg->ExternalReferences->Security |
| purl| "pkg:github/apache/[email protected]" | Comp->purl | Pkg->ExternalReferences->PackageManager |
| hash | "MD5 (1234567890)" | Comp->hashes | Pkg->Checksums |
| license | "MIT (mit.edu/~amini/LICENSE.md)" | Comp->Licenses | Pkg->ConcludedLicense |
| timestamp | "2023-05-03T04:49:33.378-0700" | - | - |


## Searching for a component

Edit allows you to search for a component to edit. Currently you can only search for a component by its name & version.

```sh
sbomasm edit --subject component-name-version --search "apache tomcat (9.0.0)" --name "apache tomcat" --version "9.0.0" --author "apache" --license "Apache-2.0" --supplier "apache.org" --repository "github.com/apache/tomcat" --cpe "cpe:2.3:a:apache:tomcat:9.0.0:*:*:*:*:*:*:*" --purl "pkg:github/apache/[email protected]" --hash "MD5 (1234567890)" in-cdx.json
```

In the above command, the subject indicate the type of search to use, and the search parameter is the format of the search string. The format is
`name (version)`. The name and version are required fields.

## Things to know
- Edit never modifies the original SBOM, it creates a new SBOM with the modifications.
- Every edit operation changes the serial number in CDX spec.
- Edit attempts to write out the SBOM in the same format it was read in. Only SPDX rdf & xml cannot be serialized out.

## Example
The primary use-case this was build for is to augment recently merged sboms or fix sboms which have know bad metadata. In your CICD pipeline
once you merge two sboms using sbomasm, you would like to provide more metadata to its primary component to meet compliance
standards. e.g you would like to add supplier, author, license data.

`Step 1`: Merge the sboms
```sh
sbomasm assemble -n "mega cdx app" -v "1.0.0" -t "application" -o final-product.cdx.json sbom1.json sbom2.json sbom3.json
```

`Step 2`: Edit the document metadata add in 2 authors, a supplier, a tool, a license, a repository, and update the timestamp and write out the final sbom to a new sbom.

```sh
sbomasm edit --subject document --author "fred ([email protected])" --author "jane ([email protected])" --supplier "interlynk.io (https://interlynk.io)" --tool "sbomasm edit (v1.0.0)" --license "Apache-2.0 (https://www.apache.org/licenses/LICENSE-2.0.txt)" --repository "github.com/interlynk/cool-app" --timestamp" -o final-mod-product.json final-product.cdx.json
```

`Step 3`: Edit the primary component, set its version to be the one provided by ENV, and also update its PURL as the sbom-generate wrote out a malformed one.

```sh
sbomasm edit --subject primary-component --purl "pkg:golang/interlynk/[email protected]" --version "$PRODUCT_VERSION" -o final-mod-primary-product.json final-mod-product.json
```

`Step 4`: Edit some components which are missing license data, which we know it should be Apache-2.0

```bash
edit_components() {
for component in "$@"; do
name=$(echo "$component" | cut -d',' -f1)
version=$(echo "$component" | cut -d',' -f2 | sed 's/\s//g')
sbomasm edit --subject component-name-version --search "$name ($version)" --license "Apache-2.0 (https://www.apache.org/licenses/LICENSE-2.0.txt)" -o final-mod-primary-product.json final-mod-primary-product.json
done
}

components=("demo-lib, v1.0.0" "third-party-lib, v2.1.3" "local-lib, v0.9.2")
edit_components "${components[@]}"
```

`Step 5`: Upload the final-mod-primary-product.json to your artifact for vuln scanning and compliance checks to Interlynk Platform.

```bash
python3 ${{ env.PYLYNK_TEMP_DIR }}/pylynk.py --verbose upload --prod ${{env.TOOL_NAME}} --env ${{ env.SBOM_ENV }} --sbom final-mod-pimary-product.json --token ${{ secrets.INTERLYNK_SECURITY_TOKEN }}
```

# Installation

## Using Prebuilt binaries
Expand Down
1 change: 0 additions & 1 deletion cmd/assemble.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,6 @@ func init() {
assembleCmd.Flags().BoolP("json", "j", true, "output in json format")
assembleCmd.MarkFlagsMutuallyExclusive("xml", "json")

assembleCmd.PersistentFlags().BoolP("debug", "d", false, "debug output")
}

func validatePath(path string) error {
Expand Down
164 changes: 164 additions & 0 deletions cmd/edit.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
/*
Copyright © 2024 NAME HERE <EMAIL ADDRESS>
*/
package cmd

import (
"context"

"github.com/interlynk-io/sbomasm/pkg/edit"
"github.com/interlynk-io/sbomasm/pkg/logger"
"github.com/spf13/cobra"
)

// editCmd represents the edit command
var editCmd = &cobra.Command{
Use: "edit",
Short: "helps editing an sbom",
Long: `The edit command allows you to modify an existing Software Bill of Materials (SBOM) by filling in gaps or adding information that may have been missed during the generation process. This command operates by first locating the entity to edit and then adding the required information. The goal of edit is not to provide a full editing experience but to help fill in filling in missing information useful for compliance and security purposes.

Usage
sbomasm edit [flags] <input-sbom-file>

Basic Example:
# Edit's an sbom to add app-name and version to the primary component
$ sbomasm edit --subject primary-component --name "my-cool-app" --version "1.0.0" in-sbom-2.json

# Edit's an sbom to add created-at timestamp and supplier information only for missing fields
$ sbomasm edit --missing --subject document --timestamp --supplier "interlynk ([email protected])" in-sbom-1.json

# Edit's an sbom add a new author to the primary component preserving the existing authors in the doc
# if append is not provided the default behavior is to replace.
$ sbomasm edit --append --subject primary-component --author "abc ([email protected])" in-sbom-2.json

Advanced Example:
# Edit's an sbom to add purl to a component by search it by name and version
$ sbomasm edit --subject component-name-version --search "abc (v1.0.0)" --purl "pkg:deb/debian/[email protected]" in-sbom-3.json

# Edit's an sbom to add multiple authors to the document
$ sbomasm edit --subject document --author "abc ([email protected])" --author "def ([email protected])" in-sbom-4.json

# Edit's an sbom to add multiple hashes to the primary component
$ sbomasm edit --subject primary-component --hash "MD5 (hash1)" --hash "SHA256 (hash2)" in-sbom-5.json
`,
SilenceUsage: true,
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
debug, _ := cmd.Flags().GetBool("debug")
if debug {
logger.InitDebugLogger()
} else {
logger.InitProdLogger()
}

ctx := logger.WithLogger(context.Background())

editParams, err := extractEditArgs(cmd, args)

if err != nil {
return err
}

editParams.Ctx = &ctx
return edit.Edit(editParams)
},
}

func init() {
rootCmd.AddCommand(editCmd)
// Output controls
editCmd.Flags().StringP("output", "o", "", "path to edited sbom, defaults to stdout")

// Edit locations
editCmd.Flags().String("subject", "document", "subject to edit (document, primary-component, component-name-version)")
editCmd.MarkFlagRequired("subject")
editCmd.Flags().String("search", "", "search string to find the entity")

// Edit controls
editCmd.Flags().BoolP("missing", "m", false, "edit only missing fields")
editCmd.Flags().BoolP("append", "a", false, "append to field instead of replacing")

// Edit fields
editCmd.Flags().String("name", "", "name of the entity")
editCmd.Flags().String("version", "", "version of the entity")
editCmd.Flags().String("supplier", "", "supplier to add e.g 'name (email)'")
editCmd.Flags().StringSlice("author", []string{}, "author to add e.g 'name (email)'")
editCmd.Flags().String("purl", "", "purl to add e.g 'pkg:deb/debian/[email protected]'")
editCmd.Flags().String("cpe", "", "cpe to add e.g 'cpe:2.3:a:microsoft:internet_explorer:8.*:sp?:*:*:*:*:*:*'")
editCmd.Flags().StringSlice("license", []string{}, "license to add e.g 'MIT'")
editCmd.Flags().StringSlice("hash", []string{}, "checksum to add e.g 'MD5 (hash'")
editCmd.Flags().StringSlice("tool", []string{}, "tool to add e.g 'sbomasm (v1.0.0)'")
editCmd.Flags().String("copyright", "", "copyright to add e.g 'Copyright © 2024'")
editCmd.Flags().StringSlice("lifecycle", []string{}, "lifecycle to add e.g 'build'")
editCmd.Flags().String("description", "", "description to add e.g 'this is a cool app'")
editCmd.Flags().String("repository", "", "repository to add e.g 'github.com/interlynk-io/sbomasm'")
editCmd.Flags().String("type", "", "type to add e.g 'application'")

editCmd.Flags().Bool("timestamp", false, "add created-at timestamp")
}

func extractEditArgs(cmd *cobra.Command, args []string) (*edit.EditParams, error) {
editParams := edit.NewEditParams()

editParams.Input = args[0]
editParams.Output, _ = cmd.Flags().GetString("output")

subject, _ := cmd.Flags().GetString("subject")
editParams.Subject = subject

search, _ := cmd.Flags().GetString("search")
editParams.Search = search

missing, _ := cmd.Flags().GetBool("missing")
editParams.Missing = missing

append, _ := cmd.Flags().GetBool("append")
editParams.Append = append

name, _ := cmd.Flags().GetString("name")
editParams.Name = name

version, _ := cmd.Flags().GetString("version")
editParams.Version = version

supplier, _ := cmd.Flags().GetString("supplier")
editParams.Supplier = supplier

authors, _ := cmd.Flags().GetStringSlice("author")
editParams.Authors = authors

purl, _ := cmd.Flags().GetString("purl")
editParams.Purl = purl

cpe, _ := cmd.Flags().GetString("cpe")
editParams.Cpe = cpe

licenses, _ := cmd.Flags().GetStringSlice("license")
editParams.Licenses = licenses

hashes, _ := cmd.Flags().GetStringSlice("hash")
editParams.Hashes = hashes

tools, _ := cmd.Flags().GetStringSlice("tool")
editParams.Tools = tools

copyright, _ := cmd.Flags().GetString("copyright")
editParams.CopyRight = copyright

lifecycles, _ := cmd.Flags().GetStringSlice("lifecycle")
editParams.Lifecycles = lifecycles

description, _ := cmd.Flags().GetString("description")
editParams.Description = description

repository, _ := cmd.Flags().GetString("repository")
editParams.Repository = repository

typ, _ := cmd.Flags().GetString("type")
editParams.Type = typ

timestamp, _ := cmd.Flags().GetBool("timestamp")
editParams.Timestamp = timestamp

return editParams, nil
}
1 change: 1 addition & 0 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ func init() {
// Cobra also supports local flags, which will only run
// when this action is called directly.
rootCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
rootCmd.PersistentFlags().BoolP("debug", "d", false, "debug output")
}

func checkIfLatestRelease() {
Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ toolchain go1.21.5
require (
github.com/CycloneDX/cyclonedx-go v0.9.0
github.com/Masterminds/semver/v3 v3.2.1
github.com/github/go-spdx/v2 v2.3.1
github.com/google/go-github/v52 v52.0.0
github.com/google/uuid v1.6.0
github.com/mitchellh/copystructure v1.2.0
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46t
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/github/go-spdx/v2 v2.3.1 h1:ffGuHTbHuHzWPt53n8f9o8clGutuLPObo3zB4JAjxU8=
github.com/github/go-spdx/v2 v2.3.1/go.mod h1:2ZxKsOhvBp+OYBDlsGnUMcchLeo2mrpEBn2L1C+U3IQ=
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
Expand Down
Loading
Loading