mirror of
https://github.com/aquasecurity/trivy.git
synced 2025-12-05 20:40:16 -08:00
124 lines
3.6 KiB
Go
124 lines
3.6 KiB
Go
package sbom
|
|
|
|
import (
|
|
"context"
|
|
"crypto/sha256"
|
|
"encoding/json"
|
|
"os"
|
|
"path/filepath"
|
|
|
|
"github.com/opencontainers/go-digest"
|
|
"github.com/samber/lo"
|
|
"golang.org/x/xerrors"
|
|
|
|
"github.com/aquasecurity/trivy/pkg/cache"
|
|
"github.com/aquasecurity/trivy/pkg/fanal/analyzer"
|
|
"github.com/aquasecurity/trivy/pkg/fanal/artifact"
|
|
"github.com/aquasecurity/trivy/pkg/fanal/handler"
|
|
"github.com/aquasecurity/trivy/pkg/fanal/types"
|
|
"github.com/aquasecurity/trivy/pkg/log"
|
|
"github.com/aquasecurity/trivy/pkg/sbom"
|
|
)
|
|
|
|
const artifactVersion = 0
|
|
|
|
type Artifact struct {
|
|
filePath string
|
|
cache cache.ArtifactCache
|
|
analyzer analyzer.AnalyzerGroup
|
|
handlerManager handler.Manager
|
|
|
|
artifactOption artifact.Option
|
|
}
|
|
|
|
func NewArtifact(filePath string, c cache.ArtifactCache, opt artifact.Option) (artifact.Artifact, error) {
|
|
return Artifact{
|
|
filePath: filepath.Clean(filePath),
|
|
cache: c,
|
|
artifactOption: opt,
|
|
}, nil
|
|
}
|
|
|
|
func (a Artifact) Inspect(ctx context.Context) (artifact.Reference, error) {
|
|
f, err := os.Open(a.filePath)
|
|
if err != nil {
|
|
return artifact.Reference{}, xerrors.Errorf("failed to open sbom file error: %w", err)
|
|
}
|
|
defer f.Close()
|
|
|
|
// Format auto-detection
|
|
format, err := sbom.DetectFormat(f)
|
|
if err != nil {
|
|
return artifact.Reference{}, xerrors.Errorf("failed to detect SBOM format: %w", err)
|
|
}
|
|
log.Info("Detected SBOM format", log.String("format", string(format)))
|
|
|
|
ctx = log.WithContextAttrs(ctx, log.FilePath(a.filePath))
|
|
bom, err := sbom.Decode(ctx, f, format)
|
|
if err != nil {
|
|
return artifact.Reference{}, xerrors.Errorf("SBOM decode error: %w", err)
|
|
}
|
|
|
|
blobInfo := types.BlobInfo{
|
|
SchemaVersion: types.BlobJSONSchemaVersion,
|
|
OS: lo.FromPtr(bom.Metadata.OS),
|
|
PackageInfos: bom.Packages,
|
|
Applications: bom.Applications,
|
|
}
|
|
|
|
cacheKey, err := a.calcCacheKey(blobInfo)
|
|
if err != nil {
|
|
return artifact.Reference{}, xerrors.Errorf("failed to calculate a cache key: %w", err)
|
|
}
|
|
|
|
if err = a.cache.PutBlob(ctx, cacheKey, blobInfo); err != nil {
|
|
return artifact.Reference{}, xerrors.Errorf("failed to store blob (%s) in cache: %w", cacheKey, err)
|
|
}
|
|
|
|
var artifactType types.ArtifactType
|
|
switch format {
|
|
case sbom.FormatCycloneDXJSON, sbom.FormatCycloneDXXML, sbom.FormatAttestCycloneDXJSON,
|
|
sbom.FormatLegacyCosignAttestCycloneDXJSON, sbom.FormatSigstoreBundleCycloneDXJSON:
|
|
artifactType = types.TypeCycloneDX
|
|
case sbom.FormatSPDXTV, sbom.FormatSPDXJSON, sbom.FormatAttestSPDXJSON, sbom.FormatSigstoreBundleSPDXJSON:
|
|
artifactType = types.TypeSPDX
|
|
}
|
|
|
|
return artifact.Reference{
|
|
Name: a.filePath,
|
|
Type: artifactType,
|
|
ID: cacheKey, // use a cache key as pseudo artifact ID
|
|
BlobIDs: []string{cacheKey},
|
|
ImageMetadata: artifact.ImageMetadata{
|
|
ID: bom.Metadata.ImageID,
|
|
DiffIDs: bom.Metadata.DiffIDs,
|
|
RepoTags: bom.Metadata.RepoTags,
|
|
RepoDigests: bom.Metadata.RepoDigests,
|
|
Reference: bom.Metadata.Reference,
|
|
},
|
|
|
|
// Keep an original report
|
|
BOM: bom.BOM,
|
|
}, nil
|
|
}
|
|
|
|
func (a Artifact) Clean(reference artifact.Reference) error {
|
|
return a.cache.DeleteBlobs(context.TODO(), reference.BlobIDs)
|
|
}
|
|
|
|
func (a Artifact) calcCacheKey(blobInfo types.BlobInfo) (string, error) {
|
|
// calculate hash of JSON and use it as pseudo artifactID and blobID
|
|
h := sha256.New()
|
|
if err := json.NewEncoder(h).Encode(blobInfo); err != nil {
|
|
return "", xerrors.Errorf("json error: %w", err)
|
|
}
|
|
|
|
d := digest.NewDigest(digest.SHA256, h)
|
|
cacheKey, err := cache.CalcKey(d.String(), artifactVersion, a.analyzer.AnalyzerVersions(), a.handlerManager.Versions(), a.artifactOption)
|
|
if err != nil {
|
|
return "", xerrors.Errorf("cache key: %w", err)
|
|
}
|
|
|
|
return cacheKey, nil
|
|
}
|