2022-10-27 08:44:35 +03:00
|
|
|
/*
|
|
|
|
* Copyright © 2022 Docker, Inc.
|
|
|
|
*
|
|
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
* you may not use this file except in compliance with the License.
|
|
|
|
* You may obtain a copy of the License at
|
|
|
|
*
|
|
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
*
|
|
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
* See the License for the specific language governing permissions and
|
|
|
|
* limitations under the License.
|
|
|
|
*/
|
|
|
|
|
|
|
|
package sbom
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"fmt"
|
|
|
|
"strings"
|
|
|
|
|
|
|
|
"github.com/aquasecurity/trivy/pkg/fanal/analyzer"
|
|
|
|
_ "github.com/aquasecurity/trivy/pkg/fanal/analyzer/language/golang/binary"
|
|
|
|
_ "github.com/aquasecurity/trivy/pkg/fanal/analyzer/language/golang/mod"
|
|
|
|
_ "github.com/aquasecurity/trivy/pkg/fanal/analyzer/language/java/jar"
|
|
|
|
_ "github.com/aquasecurity/trivy/pkg/fanal/analyzer/language/java/pom"
|
|
|
|
"github.com/aquasecurity/trivy/pkg/fanal/applier"
|
|
|
|
"github.com/aquasecurity/trivy/pkg/fanal/artifact"
|
|
|
|
aimage "github.com/aquasecurity/trivy/pkg/fanal/artifact/image"
|
|
|
|
"github.com/aquasecurity/trivy/pkg/fanal/cache"
|
|
|
|
"github.com/aquasecurity/trivy/pkg/fanal/image"
|
2023-01-04 13:53:03 +03:00
|
|
|
"github.com/aquasecurity/trivy/pkg/fanal/secret"
|
|
|
|
stypes "github.com/aquasecurity/trivy/pkg/fanal/types"
|
2022-10-27 08:44:35 +03:00
|
|
|
"github.com/aquasecurity/trivy/pkg/fanal/utils"
|
2023-01-09 13:49:37 +03:00
|
|
|
"github.com/pkg/errors"
|
|
|
|
|
2022-12-21 20:44:09 +03:00
|
|
|
"github.com/docker/index-cli-plugin/registry"
|
2022-10-28 17:25:28 +03:00
|
|
|
"github.com/docker/index-cli-plugin/types"
|
2022-10-27 08:44:35 +03:00
|
|
|
)
|
|
|
|
|
2022-12-21 20:44:09 +03:00
|
|
|
func trivySbom(cache *registry.ImageCache, lm *types.LayerMapping, resultChan chan<- types.IndexResult) {
|
2022-10-28 17:25:28 +03:00
|
|
|
result := types.IndexResult{
|
2022-10-27 08:44:35 +03:00
|
|
|
Name: "trivy",
|
2022-10-28 17:25:28 +03:00
|
|
|
Status: types.Success,
|
|
|
|
Packages: make([]types.Package, 0),
|
2023-01-04 13:53:03 +03:00
|
|
|
Secrets: make([]types.Secret, 0),
|
2022-10-27 08:44:35 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
defer close(resultChan)
|
|
|
|
|
|
|
|
cacheClient, err := initializeCache()
|
|
|
|
if err != nil {
|
2022-10-28 17:25:28 +03:00
|
|
|
result.Status = types.Failed
|
2022-10-27 08:44:35 +03:00
|
|
|
result.Error = errors.Wrap(err, "failed to initialize cache")
|
2022-11-24 15:30:49 +03:00
|
|
|
resultChan <- result
|
|
|
|
return
|
2022-10-27 08:44:35 +03:00
|
|
|
}
|
2023-01-02 19:36:29 +03:00
|
|
|
defer cacheClient.Close() //nolint:errcheck
|
2022-10-27 08:44:35 +03:00
|
|
|
|
2022-12-21 20:44:09 +03:00
|
|
|
img, err := image.NewArchiveImage(cache.ImagePath)
|
2022-10-27 08:44:35 +03:00
|
|
|
if err != nil {
|
2022-10-28 17:25:28 +03:00
|
|
|
result.Status = types.Failed
|
2022-10-27 08:44:35 +03:00
|
|
|
result.Error = errors.Wrap(err, "failed to open archived image")
|
2022-11-24 15:30:49 +03:00
|
|
|
resultChan <- result
|
|
|
|
return
|
2022-10-27 08:44:35 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
art, err := aimage.NewArtifact(img, cacheClient, artifact.Option{})
|
|
|
|
if err != nil {
|
2022-10-28 17:25:28 +03:00
|
|
|
result.Status = types.Failed
|
2022-10-27 08:44:35 +03:00
|
|
|
result.Error = errors.Wrap(err, "failed to create new artifact")
|
2022-11-24 15:30:49 +03:00
|
|
|
resultChan <- result
|
|
|
|
return
|
2022-10-27 08:44:35 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
imageInfo, err := art.Inspect(context.Background())
|
|
|
|
if err != nil {
|
2022-10-28 17:25:28 +03:00
|
|
|
result.Status = types.Failed
|
2022-10-27 08:44:35 +03:00
|
|
|
result.Error = errors.Wrap(err, "failed to inspect image")
|
2022-11-24 15:30:49 +03:00
|
|
|
resultChan <- result
|
|
|
|
return
|
2022-10-27 08:44:35 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
a := applier.NewApplier(cacheClient)
|
2023-01-04 13:53:03 +03:00
|
|
|
scanner, err := secret.NewScanner("")
|
|
|
|
if err != nil {
|
|
|
|
result.Status = types.Failed
|
|
|
|
result.Error = errors.Wrap(err, "failed to create secret scanner")
|
|
|
|
resultChan <- result
|
|
|
|
return
|
|
|
|
}
|
|
|
|
config := &cache.Source.Image.Metadata.Config
|
|
|
|
for o, h := range config.History {
|
|
|
|
secrets := scanner.Scan(secret.ScanArgs{
|
|
|
|
FilePath: "history",
|
2023-01-05 13:20:48 +03:00
|
|
|
Content: []byte(fmt.Sprintf("%s\n%s\n%s", h.CreatedBy, h.Author, h.Comment)),
|
2023-01-04 13:53:03 +03:00
|
|
|
})
|
|
|
|
if len(secrets.Findings) > 0 {
|
|
|
|
result.Secrets = append(result.Secrets, convertSecretFindings(secrets, types.SecretSource{
|
|
|
|
Type: "history",
|
|
|
|
Location: &types.Location{
|
|
|
|
Ordinal: o,
|
|
|
|
Digest: lm.DigestByOrdinal[o],
|
|
|
|
DiffId: lm.DiffIdByOrdinal[o],
|
|
|
|
},
|
|
|
|
}))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for k, v := range config.Config.Labels {
|
|
|
|
secrets := scanner.Scan(secret.ScanArgs{
|
|
|
|
FilePath: "label",
|
|
|
|
Content: []byte(fmt.Sprintf("%s=%s", k, v)),
|
|
|
|
})
|
|
|
|
if len(secrets.Findings) > 0 {
|
|
|
|
result.Secrets = append(result.Secrets, convertSecretFindings(secrets, types.SecretSource{
|
|
|
|
Type: "label",
|
|
|
|
}))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for _, l := range config.Config.Env {
|
|
|
|
secrets := scanner.Scan(secret.ScanArgs{
|
|
|
|
FilePath: "env",
|
|
|
|
Content: []byte(l),
|
|
|
|
})
|
|
|
|
if len(secrets.Findings) > 0 {
|
|
|
|
result.Secrets = append(result.Secrets, convertSecretFindings(secrets, types.SecretSource{
|
|
|
|
Type: "env",
|
|
|
|
}))
|
|
|
|
}
|
|
|
|
}
|
2022-10-27 08:44:35 +03:00
|
|
|
for v := range imageInfo.BlobIDs {
|
|
|
|
mergedLayer, err := a.ApplyLayers(imageInfo.ID, []string{imageInfo.BlobIDs[v]})
|
|
|
|
if err != nil {
|
|
|
|
switch err {
|
|
|
|
case analyzer.ErrUnknownOS, analyzer.ErrNoPkgsDetected:
|
|
|
|
default:
|
2022-10-28 17:25:28 +03:00
|
|
|
result.Status = types.Failed
|
2022-10-27 08:44:35 +03:00
|
|
|
result.Error = errors.Wrap(err, "failed to inspect layer")
|
2022-11-24 15:30:49 +03:00
|
|
|
resultChan <- result
|
|
|
|
return
|
2022-10-27 08:44:35 +03:00
|
|
|
}
|
|
|
|
}
|
2023-01-04 13:53:03 +03:00
|
|
|
for _, s := range mergedLayer.Secrets {
|
|
|
|
result.Secrets = append(result.Secrets, convertSecretFindings(s, types.SecretSource{
|
|
|
|
Type: "file",
|
|
|
|
Location: &types.Location{
|
|
|
|
Path: s.FilePath,
|
|
|
|
Ordinal: lm.OrdinalByDiffId[s.Layer.DiffID],
|
2023-01-04 15:07:32 +03:00
|
|
|
Digest: lm.ByDiffId[s.Layer.DiffID],
|
2023-01-04 13:53:03 +03:00
|
|
|
DiffId: s.Layer.DiffID,
|
|
|
|
},
|
|
|
|
}))
|
|
|
|
}
|
2022-10-27 08:44:35 +03:00
|
|
|
for _, app := range mergedLayer.Applications {
|
|
|
|
switch app.Type {
|
|
|
|
case "gobinary":
|
|
|
|
for _, lib := range app.Libraries {
|
|
|
|
if lib.Version == "" || lib.Name == "" {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
url := fmt.Sprintf(`pkg:golang/%s@%s`, lib.Name, lib.Version)
|
2022-10-28 17:25:28 +03:00
|
|
|
purl, err := types.ToPackageUrl(url)
|
2022-10-27 08:44:35 +03:00
|
|
|
if err != nil {
|
2022-10-28 17:25:28 +03:00
|
|
|
result.Status = types.Failed
|
2022-10-27 08:44:35 +03:00
|
|
|
result.Error = errors.Wrapf(err, "failed to create purl from %s", url)
|
|
|
|
break
|
|
|
|
}
|
2022-10-28 17:25:28 +03:00
|
|
|
pkg := types.Package{
|
2022-10-27 08:44:35 +03:00
|
|
|
Purl: purl.String(),
|
2022-10-28 17:25:28 +03:00
|
|
|
Locations: []types.Location{{
|
2023-01-04 13:53:03 +03:00
|
|
|
Path: "/" + app.FilePath,
|
|
|
|
Ordinal: lm.OrdinalByDiffId[lib.Layer.DiffID],
|
|
|
|
Digest: lm.ByDiffId[lib.Layer.DiffID],
|
|
|
|
DiffId: lib.Layer.DiffID,
|
2022-10-27 08:44:35 +03:00
|
|
|
}},
|
|
|
|
}
|
|
|
|
result.Packages = append(result.Packages, pkg)
|
|
|
|
}
|
|
|
|
case "jar":
|
|
|
|
for _, lib := range app.Libraries {
|
|
|
|
if lib.Version == "" || !strings.Contains(lib.Name, ":") {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
namespace := strings.Split(lib.Name, ":")[0]
|
|
|
|
name := strings.Split(lib.Name, ":")[1]
|
|
|
|
|
|
|
|
url := fmt.Sprintf(`pkg:maven/%s/%s@%s`, namespace, name, lib.Version)
|
2022-10-28 17:25:28 +03:00
|
|
|
purl, err := types.ToPackageUrl(url)
|
2022-10-27 08:44:35 +03:00
|
|
|
if err != nil {
|
2022-10-28 17:25:28 +03:00
|
|
|
result.Status = types.Failed
|
2022-10-27 08:44:35 +03:00
|
|
|
result.Error = errors.Wrapf(err, "failed to create purl from %s", url)
|
2022-11-24 15:30:49 +03:00
|
|
|
resultChan <- result
|
|
|
|
return
|
2022-10-27 08:44:35 +03:00
|
|
|
}
|
2022-10-28 17:25:28 +03:00
|
|
|
pkg := types.Package{
|
2022-10-27 08:44:35 +03:00
|
|
|
Purl: purl.String(),
|
2022-10-28 17:25:28 +03:00
|
|
|
Locations: []types.Location{{
|
2023-01-04 13:53:03 +03:00
|
|
|
Path: "/" + lib.FilePath,
|
|
|
|
Ordinal: lm.OrdinalByDiffId[lib.Layer.DiffID],
|
|
|
|
Digest: lm.ByDiffId[lib.Layer.DiffID],
|
|
|
|
DiffId: lib.Layer.DiffID,
|
2022-10-27 08:44:35 +03:00
|
|
|
}},
|
|
|
|
}
|
|
|
|
result.Packages = append(result.Packages, pkg)
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2023-01-04 13:53:03 +03:00
|
|
|
|
2022-10-27 08:44:35 +03:00
|
|
|
resultChan <- result
|
|
|
|
}
|
|
|
|
|
|
|
|
func initializeCache() (cache.Cache, error) {
|
|
|
|
var cacheClient cache.Cache
|
|
|
|
var err error
|
|
|
|
cacheClient, err = cache.NewFSCache(utils.CacheDir())
|
|
|
|
return cacheClient, err
|
|
|
|
}
|
2023-01-04 13:53:03 +03:00
|
|
|
|
|
|
|
func convertSecretFindings(s stypes.Secret, source types.SecretSource) types.Secret {
|
|
|
|
secret := types.Secret{
|
|
|
|
Source: source,
|
|
|
|
Findings: make([]types.SecretFinding, 0),
|
|
|
|
}
|
|
|
|
for _, f := range s.Findings {
|
|
|
|
finding := types.SecretFinding{
|
|
|
|
RuleID: f.RuleID,
|
|
|
|
Category: string(f.Category),
|
|
|
|
Title: f.Title,
|
|
|
|
Severity: f.Severity,
|
|
|
|
Match: f.Match,
|
|
|
|
}
|
|
|
|
if source.Type == "file" {
|
|
|
|
finding.StartLine = f.StartLine
|
|
|
|
finding.EndLine = f.EndLine
|
|
|
|
}
|
|
|
|
secret.Findings = append(secret.Findings, finding)
|
|
|
|
}
|
|
|
|
return secret
|
|
|
|
}
|