Skip to content
This repository was archived by the owner on Jul 18, 2025. It is now read-only.

Commit 83836be

Browse files
committed
Add secret scanning
1 parent d19b93d commit 83836be

File tree

4 files changed

+147
-18
lines changed

4 files changed

+147
-18
lines changed

sbom/index.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,6 @@ func indexImage(cache *registry.ImageCache, cli command.Cli) (*types.Sbom, error
132132
}
133133

134134
sbom := types.Sbom{
135-
Artifacts: packages,
136135
Source: types.Source{
137136
Type: "image",
138137
Image: types.ImageSource{
@@ -151,6 +150,8 @@ func indexImage(cache *registry.ImageCache, cli command.Cli) (*types.Sbom, error
151150
Size: cache.Source.Image.Metadata.Size,
152151
},
153152
},
153+
Artifacts: packages,
154+
Secrets: trivyResult.Secrets,
154155
Descriptor: types.Descriptor{
155156
Name: "docker-index",
156157
Version: internal.FromBuild().Version,

sbom/syft.go

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
package sbom
1818

1919
import (
20+
"fmt"
2021
"strings"
2122

2223
"github.com/anchore/packageurl-go"
@@ -133,7 +134,7 @@ type sourcePackage struct {
133134

134135
func toPackage(p pkg2.Package, rels []artifact.Relationship, qualifiers map[string]string, lm *types.LayerMapping, pm packageMapping) []types.Package {
135136
pkg := types.Package{
136-
Purl: p.PURL,
137+
Purl: getPURL(p),
137138
Licenses: p.Licenses,
138139
Locations: make([]types.Location, 0),
139140
}
@@ -249,9 +250,10 @@ func toPackage(p pkg2.Package, rels []artifact.Relationship, qualifiers map[stri
249250
}
250251

251252
pkg.Files = append(pkg.Files, types.Location{
252-
Path: path,
253-
DiffId: corr.FileSystemID,
254-
Digest: lm.ByDiffId[corr.FileSystemID],
253+
Path: path,
254+
Ordinal: lm.OrdinalByDiffId[corr.FileSystemID],
255+
DiffId: corr.FileSystemID,
256+
Digest: lm.ByDiffId[corr.FileSystemID],
255257
})
256258
}
257259
}
@@ -265,9 +267,10 @@ func toPackage(p pkg2.Package, rels []artifact.Relationship, qualifiers map[stri
265267
}
266268

267269
pkg.Locations = append(pkg.Locations, types.Location{
268-
Path: path,
269-
DiffId: loc.FileSystemID,
270-
Digest: lm.ByDiffId[loc.FileSystemID],
270+
Path: path,
271+
Ordinal: lm.OrdinalByDiffId[loc.FileSystemID],
272+
DiffId: loc.FileSystemID,
273+
Digest: lm.ByDiffId[loc.FileSystemID],
271274
})
272275
}
273276

@@ -276,6 +279,7 @@ func toPackage(p pkg2.Package, rels []artifact.Relationship, qualifiers map[stri
276279
if loc.Path == "/lib/apk/db/installed" || loc.Path == "/var/lib/dpkg/status" || loc.Path == "/var/lib/rpm/Packages" {
277280
if layer, ok := pm[toKey(p)]; ok {
278281
// the stereoscope layers use diff_ids internally as their digest
282+
pkg.Locations[i].Ordinal = lm.OrdinalByDiffId[layer.Metadata.Digest]
279283
pkg.Locations[i].DiffId = layer.Metadata.Digest
280284
pkg.Locations[i].Digest = lm.ByDiffId[layer.Metadata.Digest]
281285
}
@@ -319,6 +323,18 @@ func toPackage(p pkg2.Package, rels []artifact.Relationship, qualifiers map[stri
319323
return []types.Package{pkg}
320324
}
321325

326+
func getPURL(pkp pkg2.Package) string {
327+
if pkp.PURL != "" {
328+
return pkp.PURL
329+
} else {
330+
switch pkp.Type {
331+
case "apk":
332+
return fmt.Sprintf("pkg:alpine/%s@%s", pkp.Name, pkp.Version)
333+
}
334+
}
335+
return ""
336+
}
337+
322338
func osQualifiers(release *linux.Release) (types.Distro, map[string]string) {
323339
qualifiers := make(map[string]string, 0)
324340
distro := types.Distro{}

sbom/trivy.go

Lines changed: 93 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ package sbom
1818

1919
import (
2020
"context"
21+
"encoding/json"
2122
"fmt"
2223
"strings"
2324

@@ -31,6 +32,8 @@ import (
3132
aimage "github.com/aquasecurity/trivy/pkg/fanal/artifact/image"
3233
"github.com/aquasecurity/trivy/pkg/fanal/cache"
3334
"github.com/aquasecurity/trivy/pkg/fanal/image"
35+
"github.com/aquasecurity/trivy/pkg/fanal/secret"
36+
stypes "github.com/aquasecurity/trivy/pkg/fanal/types"
3437
"github.com/aquasecurity/trivy/pkg/fanal/utils"
3538
"github.com/docker/index-cli-plugin/registry"
3639
"github.com/docker/index-cli-plugin/types"
@@ -42,6 +45,7 @@ func trivySbom(cache *registry.ImageCache, lm *types.LayerMapping, resultChan ch
4245
Name: "trivy",
4346
Status: types.Success,
4447
Packages: make([]types.Package, 0),
48+
Secrets: make([]types.Secret, 0),
4549
}
4650

4751
defer close(resultChan)
@@ -80,6 +84,53 @@ func trivySbom(cache *registry.ImageCache, lm *types.LayerMapping, resultChan ch
8084
}
8185

8286
a := applier.NewApplier(cacheClient)
87+
scanner, err := secret.NewScanner("")
88+
if err != nil {
89+
result.Status = types.Failed
90+
result.Error = errors.Wrap(err, "failed to create secret scanner")
91+
resultChan <- result
92+
return
93+
}
94+
config := &cache.Source.Image.Metadata.Config
95+
for o, h := range config.History {
96+
js, _ := json.MarshalIndent(h, "", " ")
97+
secrets := scanner.Scan(secret.ScanArgs{
98+
FilePath: "history",
99+
Content: js,
100+
})
101+
if len(secrets.Findings) > 0 {
102+
result.Secrets = append(result.Secrets, convertSecretFindings(secrets, types.SecretSource{
103+
Type: "history",
104+
Location: &types.Location{
105+
Ordinal: o,
106+
Digest: lm.DigestByOrdinal[o],
107+
DiffId: lm.DiffIdByOrdinal[o],
108+
},
109+
}))
110+
}
111+
}
112+
for k, v := range config.Config.Labels {
113+
secrets := scanner.Scan(secret.ScanArgs{
114+
FilePath: "label",
115+
Content: []byte(fmt.Sprintf("%s=%s", k, v)),
116+
})
117+
if len(secrets.Findings) > 0 {
118+
result.Secrets = append(result.Secrets, convertSecretFindings(secrets, types.SecretSource{
119+
Type: "label",
120+
}))
121+
}
122+
}
123+
for _, l := range config.Config.Env {
124+
secrets := scanner.Scan(secret.ScanArgs{
125+
FilePath: "env",
126+
Content: []byte(l),
127+
})
128+
if len(secrets.Findings) > 0 {
129+
result.Secrets = append(result.Secrets, convertSecretFindings(secrets, types.SecretSource{
130+
Type: "env",
131+
}))
132+
}
133+
}
83134
for v := range imageInfo.BlobIDs {
84135
mergedLayer, err := a.ApplyLayers(imageInfo.ID, []string{imageInfo.BlobIDs[v]})
85136
if err != nil {
@@ -92,6 +143,17 @@ func trivySbom(cache *registry.ImageCache, lm *types.LayerMapping, resultChan ch
92143
return
93144
}
94145
}
146+
for _, s := range mergedLayer.Secrets {
147+
result.Secrets = append(result.Secrets, convertSecretFindings(s, types.SecretSource{
148+
Type: "file",
149+
Location: &types.Location{
150+
Path: s.FilePath,
151+
Ordinal: lm.OrdinalByDiffId[s.Layer.DiffID],
152+
Digest: s.Layer.Digest,
153+
DiffId: s.Layer.DiffID,
154+
},
155+
}))
156+
}
95157
for _, app := range mergedLayer.Applications {
96158
switch app.Type {
97159
case "gobinary":
@@ -110,9 +172,10 @@ func trivySbom(cache *registry.ImageCache, lm *types.LayerMapping, resultChan ch
110172
pkg := types.Package{
111173
Purl: purl.String(),
112174
Locations: []types.Location{{
113-
Path: "/" + app.FilePath,
114-
Digest: lm.ByDiffId[lib.Layer.DiffID],
115-
DiffId: lib.Layer.DiffID,
175+
Path: "/" + app.FilePath,
176+
Ordinal: lm.OrdinalByDiffId[lib.Layer.DiffID],
177+
Digest: lm.ByDiffId[lib.Layer.DiffID],
178+
DiffId: lib.Layer.DiffID,
116179
}},
117180
}
118181
result.Packages = append(result.Packages, pkg)
@@ -137,9 +200,10 @@ func trivySbom(cache *registry.ImageCache, lm *types.LayerMapping, resultChan ch
137200
pkg := types.Package{
138201
Purl: purl.String(),
139202
Locations: []types.Location{{
140-
Path: "/" + lib.FilePath,
141-
Digest: lm.ByDiffId[lib.Layer.DiffID],
142-
DiffId: lib.Layer.DiffID,
203+
Path: "/" + lib.FilePath,
204+
Ordinal: lm.OrdinalByDiffId[lib.Layer.DiffID],
205+
Digest: lm.ByDiffId[lib.Layer.DiffID],
206+
DiffId: lib.Layer.DiffID,
143207
}},
144208
}
145209
result.Packages = append(result.Packages, pkg)
@@ -148,6 +212,7 @@ func trivySbom(cache *registry.ImageCache, lm *types.LayerMapping, resultChan ch
148212
}
149213
}
150214
}
215+
151216
resultChan <- result
152217
}
153218

@@ -157,3 +222,25 @@ func initializeCache() (cache.Cache, error) {
157222
cacheClient, err = cache.NewFSCache(utils.CacheDir())
158223
return cacheClient, err
159224
}
225+
226+
func convertSecretFindings(s stypes.Secret, source types.SecretSource) types.Secret {
227+
secret := types.Secret{
228+
Source: source,
229+
Findings: make([]types.SecretFinding, 0),
230+
}
231+
for _, f := range s.Findings {
232+
finding := types.SecretFinding{
233+
RuleID: f.RuleID,
234+
Category: string(f.Category),
235+
Title: f.Title,
236+
Severity: f.Severity,
237+
Match: f.Match,
238+
}
239+
if source.Type == "file" {
240+
finding.StartLine = f.StartLine
241+
finding.EndLine = f.EndLine
242+
}
243+
secret.Findings = append(secret.Findings, finding)
244+
}
245+
return secret
246+
}

types/types.go

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,9 @@
1616

1717
package types
1818

19-
import v1 "github.com/google/go-containerregistry/pkg/v1"
19+
import (
20+
v1 "github.com/google/go-containerregistry/pkg/v1"
21+
)
2022

2123
type Score struct {
2224
Type string `edn:"vulnerability.reference.score/type" json:"type"`
@@ -69,6 +71,7 @@ type LayerMapping struct {
6971
type IndexResult struct {
7072
Name string
7173
Packages []Package
74+
Secrets []Secret
7275
Status string
7376
Error error
7477
Distro Distro
@@ -98,9 +101,10 @@ type Platform struct {
98101
}
99102

100103
type Location struct {
101-
Path string `json:"path"`
102-
Digest string `json:"digest"`
103-
DiffId string `json:"diff_id"`
104+
Path string `json:"path,omitempty"`
105+
Ordinal int `json:"ordinal,omitempty"`
106+
Digest string `json:"digest,omitempty"`
107+
DiffId string `json:"diff_id,omitempty"`
104108
}
105109

106110
type ImageSource struct {
@@ -129,10 +133,31 @@ type Source struct {
129133
BaseImages []BaseImageMatch `json:"base_images,omitempty"`
130134
}
131135

136+
type Secret struct {
137+
Source SecretSource `json:"source"`
138+
Findings []SecretFinding `json:"findings"`
139+
}
140+
141+
type SecretSource struct {
142+
Type string `json:"type"`
143+
Location *Location `json:"location,omitempty"`
144+
}
145+
146+
type SecretFinding struct {
147+
RuleID string `json:"rule_id"`
148+
Category string `json:"category"`
149+
Title string `json:"title"`
150+
Severity string `json:"severity"`
151+
StartLine int `json:"start_line,omitempty"`
152+
EndLine int `json:"end_line,omitempty"`
153+
Match string `json:"match"`
154+
}
155+
132156
type Sbom struct {
133157
Source Source `json:"source"`
134158
Artifacts []Package `json:"artifacts"`
135159
Vulnerabilities []VulnerabilitiesByPurl `json:"vulnerabilities,omitempty"`
160+
Secrets []Secret `json:"secrets,omitempty"`
136161
Descriptor Descriptor `json:"descriptor"`
137162
}
138163

0 commit comments

Comments
 (0)