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

Commit 1304a66

Browse files
committed
Add detection of additional packages
Signed-off-by: Christian Dupuis <[email protected]>
1 parent e1dd637 commit 1304a66

File tree

6 files changed

+173
-26
lines changed

6 files changed

+173
-26
lines changed

sbom/addition.go

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
/*
2+
* Copyright © 2022 Docker, Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package sbom
18+
19+
import (
20+
"fmt"
21+
"strings"
22+
23+
"github.com/anchore/syft/syft/source"
24+
"github.com/docker/index-cli-plugin/types"
25+
)
26+
27+
type PackageDetector = func(packages []types.Package, image source.Source, lm types.LayerMapping) []types.Package
28+
29+
var detectors []PackageDetector
30+
31+
func init() {
32+
detectors = []PackageDetector{nodePackageDetector}
33+
}
34+
35+
func detectAdditionalPackages(packages []types.Package, image source.Source, lm types.LayerMapping) []types.Package {
36+
additionalPackages := make([]types.Package, 0)
37+
for _, d := range detectors {
38+
additionalPackages = append(additionalPackages, d(packages, image, lm)...)
39+
}
40+
return additionalPackages
41+
}
42+
43+
func nodePackageDetector(_ []types.Package, image source.Source, lm types.LayerMapping) []types.Package {
44+
var path []string
45+
var nodeVersion string
46+
47+
env := image.Image.Metadata.Config.Config.Env
48+
for _, e := range env {
49+
k := strings.Split(e, "=")[0]
50+
v := strings.Split(e, "=")[1]
51+
switch k {
52+
case "NODE_VERSION":
53+
nodeVersion = v
54+
case "PATH":
55+
path = strings.Split(v, ":")
56+
}
57+
}
58+
59+
if nodeVersion != "" && len(path) > 0 {
60+
res, _ := image.FileResolver(source.SquashedScope)
61+
for _, p := range path {
62+
fp := fmt.Sprintf("%s/node", p)
63+
if locations, err := res.FilesByPath(fp); err == nil && len(locations) > 0 {
64+
loc := locations[0]
65+
return []types.Package{{
66+
Type: "github",
67+
Namespace: "nodejs",
68+
Name: "node",
69+
Version: nodeVersion,
70+
Purl: fmt.Sprintf("pkg:github/nodejs/node@%s", nodeVersion),
71+
Author: "Node.js Project",
72+
Description: "Node.js JavaScript runtime",
73+
Licenses: []string{"MIT"},
74+
Url: "https://nodejs.org",
75+
Locations: []types.Location{{
76+
Path: fp,
77+
DiffId: loc.FileSystemID,
78+
Digest: lm.ByDiffId[loc.FileSystemID],
79+
}},
80+
}}
81+
}
82+
}
83+
}
84+
85+
return []types.Package{}
86+
}

sbom/addition_test.go

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
/*
2+
* Copyright © 2022 Docker, Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package sbom
18+
19+
import (
20+
"testing"
21+
22+
stereoscopeimage "github.com/anchore/stereoscope/pkg/image"
23+
"github.com/anchore/syft/syft/source"
24+
"github.com/docker/cli/cli/command"
25+
"github.com/docker/index-cli-plugin/registry"
26+
"github.com/docker/index-cli-plugin/types"
27+
)
28+
29+
func TestNodeDetector(t *testing.T) {
30+
cmd, _ := command.NewDockerCli()
31+
img, ociPath, _ := registry.SaveImage("node@sha256:2b00d259f3b07d8aa694b298a7dcf4655571aea2ab91375b5adb8e5a905d3ee2", cmd.Client())
32+
lm := createLayerMapping(img)
33+
i := source.Input{
34+
Scheme: source.ImageScheme,
35+
ImageSource: stereoscopeimage.OciDirectorySource,
36+
Location: ociPath,
37+
}
38+
src, _, _ := source.New(i, nil, nil)
39+
packages := nodePackageDetector([]types.Package{}, *src, lm)
40+
if len(packages) != 1 {
41+
t.Errorf("Expected package missing")
42+
}
43+
node := packages[0]
44+
if node.Purl != "pkg:github/nodejs/[email protected]" {
45+
t.Errorf("Wrong nodejs version detected")
46+
}
47+
}

sbom/diff.go

Lines changed: 32 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ package sbom
1919
import (
2020
"fmt"
2121
"strings"
22+
"sync"
2223

2324
"github.com/anchore/packageurl-go"
2425
"github.com/docker/docker/client"
@@ -58,13 +59,23 @@ func init() {
5859
}
5960

6061
func DiffImages(image1 string, image2 string, client client.APIClient, workspace string, apikey string) error {
61-
resultChan1 := make(chan ImageIndexResult)
62-
resultChan2 := make(chan ImageIndexResult)
63-
go indexImageAsync(image1, client, resultChan1)
64-
go indexImageAsync(image2, client, resultChan2)
65-
66-
result1 := <-resultChan1
67-
result2 := <-resultChan2
62+
resultChan := make(chan ImageIndexResult, 2)
63+
var wg sync.WaitGroup
64+
wg.Add(2)
65+
go indexImageAsync(&wg, image1, client, resultChan)
66+
go indexImageAsync(&wg, image2, client, resultChan)
67+
wg.Wait()
68+
close(resultChan)
69+
70+
var result1, result2 ImageIndexResult
71+
for result := range resultChan {
72+
switch result.Input {
73+
case image1:
74+
result1 = result
75+
case image2:
76+
result2 = result
77+
}
78+
}
6879

6980
diffPackages(result1, result2)
7081
diffCves(result1, result2)
@@ -154,7 +165,7 @@ func diffPackages(result1, result2 ImageIndexResult) {
154165

155166
t.SetColumnConfigs([]table.ColumnConfig{
156167
{Name: "Package", AutoMerge: true},
157-
{Name: "Version", AutoMerge: true},
168+
{Name: "Version", AutoMerge: true, Align: text.AlignRight},
158169
{Number: 3, Align: text.AlignCenter, AlignFooter: text.AlignCenter, AlignHeader: text.AlignCenter},
159170
{Number: 4, Align: text.AlignCenter, AlignFooter: text.AlignCenter, AlignHeader: text.AlignCenter},
160171
})
@@ -239,33 +250,30 @@ func colorizeSeverity(severity string) string {
239250
}
240251

241252
func toSeverity(cve types.Cve) string {
242-
if cve.Cve != nil {
243-
for _, r := range cve.Cve.References {
244-
if r.Source == "atomist" {
245-
for _, s := range r.Scores {
246-
if s.Type == "atm_severity" {
247-
v := s.Value
248-
if v != "SEVERITY_UNSPECIFIED" {
249-
return v
250-
}
251-
}
252-
}
253-
}
253+
findSeverity := func(adv *types.Advisory) (string, bool) {
254+
if adv == nil {
255+
return "", false
254256
}
255-
}
256-
if cve.Advisory != nil {
257-
for _, r := range cve.Advisory.References {
257+
for _, r := range (*adv).References {
258258
if r.Source == "atomist" {
259259
for _, s := range r.Scores {
260260
if s.Type == "atm_severity" {
261261
v := s.Value
262262
if v != "SEVERITY_UNSPECIFIED" {
263-
return v
263+
return v, true
264264
}
265265
}
266266
}
267267
}
268268
}
269+
return "", false
270+
}
271+
272+
if severity, ok := findSeverity(cve.Cve); ok {
273+
return severity
274+
}
275+
if severity, ok := findSeverity(cve.Advisory); ok {
276+
return severity
269277
}
270278

271279
return "IN TRIAGE"

sbom/index.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import (
2222
"os"
2323
"path/filepath"
2424
"strings"
25+
"sync"
2526

2627
"github.com/atomist-skills/go-skill"
2728
"github.com/docker/docker/client"
@@ -35,18 +36,21 @@ import (
3536
)
3637

3738
type ImageIndexResult struct {
39+
Input string
3840
Image *v1.Image
3941
Sbom *types.Sbom
4042
Error error
4143
}
4244

43-
func indexImageAsync(image string, client client.APIClient, resultChan chan<- ImageIndexResult) {
45+
func indexImageAsync(wg *sync.WaitGroup, image string, client client.APIClient, resultChan chan<- ImageIndexResult) {
46+
defer wg.Done()
4447
sbom, img, err := IndexImage(image, client)
4548
cves, err := query.QueryCves(sbom, "", "", "")
4649
if err == nil {
4750
sbom.Vulnerabilities = *cves
4851
}
4952
resultChan <- ImageIndexResult{
53+
Input: image,
5054
Image: img,
5155
Sbom: sbom,
5256
Error: err,

sbom/index_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ func TestMergePackages(t *testing.T) {
4343
DiffId: "sha256:5678",
4444
}},
4545
}
46-
packages := mergePackages(types.IndexResult{
46+
packages := types.MergePackages(types.IndexResult{
4747
Status: types.Success,
4848
Packages: []types.Package{pkga},
4949
}, types.IndexResult{

sbom/syft.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,8 @@ func syftSbom(ociPath string, lm types.LayerMapping, resultChan chan<- types.Ind
107107
pkg := toPackage(p, packageRelationships, qualifiers, lm, pm)
108108
result.Packages = append(result.Packages, pkg...)
109109
}
110+
111+
result.Packages = append(result.Packages, detectAdditionalPackages(result.Packages, *src, lm)...)
110112
resultChan <- result
111113
}
112114

0 commit comments

Comments
 (0)