Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ go 1.26.0

require (
buf.build/gen/go/safedep/api/grpc/go v1.6.1-20260225163230-1a8a369418c0.1
buf.build/gen/go/safedep/api/protocolbuffers/go v1.36.11-20260225163230-1a8a369418c0.1
buf.build/gen/go/safedep/api/protocolbuffers/go v1.36.11-20260328070158-ea851c7714ec.1
entgo.io/ent v0.14.6-0.20260218064135-ab0540611e15
github.com/AlecAivazis/survey/v2 v2.3.7
github.com/BurntSushi/toml v1.6.0
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ buf.build/gen/go/safedep/api/grpc/go v1.6.1-20260225163230-1a8a369418c0.1 h1:0f3
buf.build/gen/go/safedep/api/grpc/go v1.6.1-20260225163230-1a8a369418c0.1/go.mod h1:AHcVPzz8YJJfW/ceSUcisDz3anAbqHr+KXeVf3J/Oao=
buf.build/gen/go/safedep/api/protocolbuffers/go v1.36.11-20260225163230-1a8a369418c0.1 h1:AsAzoqQ5hyDMspIDVGs/1Jfvms4tokCI1jHn1X6XIpg=
buf.build/gen/go/safedep/api/protocolbuffers/go v1.36.11-20260225163230-1a8a369418c0.1/go.mod h1:I8E+sZXJNqzWBtSlRGCoiEorLSRiix50h2R/66aBzME=
buf.build/gen/go/safedep/api/protocolbuffers/go v1.36.11-20260328070158-ea851c7714ec.1 h1:3CVlp+BQppafL2xI88yg5dIOd9MPyOl5KsXXNVAYldw=
buf.build/gen/go/safedep/api/protocolbuffers/go v1.36.11-20260328070158-ea851c7714ec.1/go.mod h1:I8E+sZXJNqzWBtSlRGCoiEorLSRiix50h2R/66aBzME=
buf.build/go/protovalidate v1.1.3 h1:m2GVEgQWd7rk+vIoAZ+f0ygGjvQTuqPQapBBdcpWVPE=
buf.build/go/protovalidate v1.1.3/go.mod h1:9XIuohWz+kj+9JVn3WQneHA5LZP50mjvneZMnbLkiIE=
cel.dev/expr v0.25.1 h1:1KrZg61W6TWSxuNZ37Xy49ps13NUovb66QLprthtwi4=
Expand Down
6 changes: 6 additions & 0 deletions pkg/models/models.go
Original file line number Diff line number Diff line change
Expand Up @@ -433,6 +433,12 @@ type Package struct {

// Manifest from where this package was found directly or indirectly
Manifest *PackageManifest `json:"-"`

// OsvSourceName is the source package name used for OSV vulnerability lookup.
// For Debian/Ubuntu packages, OSV indexes advisories by source package name
// (e.g. "shadow") rather than the binary name (e.g. "login", "passwd").
// This field is populated from osv-scalibr's dpkg metadata when available.
OsvSourceName string `json:"osv_source_name,omitempty"`
}

// Id returns a unique identifier for this package within a manifest
Expand Down
7 changes: 7 additions & 0 deletions pkg/readers/container_image_reader.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
scalibrlayerimage "github.com/google/osv-scalibr/artifact/image/layerscanning/image"
"github.com/google/osv-scalibr/binary/platform"
"github.com/google/osv-scalibr/converter"
dpkgmeta "github.com/google/osv-scalibr/extractor/filesystem/os/dpkg/metadata"
scalibrfs "github.com/google/osv-scalibr/fs"
"github.com/google/osv-scalibr/plugin"
pluginlist "github.com/google/osv-scalibr/plugin/list"
Expand Down Expand Up @@ -127,6 +128,12 @@ func (c containerImageReader) EnumManifests(handler func(*models.PackageManifest
Manifest: manifests[key],
}

// For Debian/Ubuntu packages, OSV indexes by source package name.
// Extract it from osv-scalibr's dpkg metadata when available.
if meta, ok := pkg.Metadata.(*dpkgmeta.Metadata); ok && meta.SourceName != "" {
pkgPackage.OsvSourceName = meta.SourceName
}

manifests[key].AddPackage(pkgPackage)
}

Expand Down
44 changes: 44 additions & 0 deletions pkg/readers/container_image_reader_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@
import (
"testing"

"github.com/safedep/vet/pkg/models"

Check failure on line 6 in pkg/readers/container_image_reader_test.go

View workflow job for this annotation

GitHub Actions / lint

File is not properly formatted (gci)
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

func TestContainerImageReader_ApplicationName(t *testing.T) {
Expand Down Expand Up @@ -50,6 +52,48 @@
assert.Equal(t, "Container Image Reader", reader.Name())
}

// TestContainerImageReader_DpkgSourceName verifies that scanning a Debian/Ubuntu
// image populates OsvSourceName from dpkg metadata for packages where the binary
// name differs from the source package name.
func TestContainerImageReader_DpkgSourceName(t *testing.T) {
// Binary → expected source package name mappings from issue #703.
// These are packages where OSV requires the source name for correct lookup.
wantSourceNames := map[string]string{
"login": "shadow",
"passwd": "shadow",
"libpam-modules": "pam",
"libsystemd0": "systemd",
"libudev1": "systemd",
"libncurses6": "ncurses",
"libtinfo6": "ncurses",
"gpgv": "gnupg2",
}

config := DefaultContainerImageReaderConfig()
Comment thread
KunalSin9h marked this conversation as resolved.
reader, err := NewContainerImageReader("ubuntu:22.04", config) // 28 MB image
Comment thread
KunalSin9h marked this conversation as resolved.
Outdated
require.NoError(t, err)

found := map[string]string{} // binary name → OsvSourceName

err = reader.EnumManifests(func(manifest *models.PackageManifest, pr PackageReader) error {
return pr.EnumPackages(func(pkg *models.Package) error {
if _, interesting := wantSourceNames[pkg.GetName()]; interesting {
found[pkg.GetName()] = pkg.OsvSourceName
}
return nil
})
})
require.NoError(t, err)

for binary, wantSrc := range wantSourceNames {
t.Run(binary, func(t *testing.T) {
got, ok := found[binary]
assert.True(t, ok, "package %q not found in scan results", binary)
assert.Equal(t, wantSrc, got, "OsvSourceName for binary package %q", binary)
})
}
}

func TestCheckPathExists(t *testing.T) {
cases := []struct {
name string
Expand Down
32 changes: 23 additions & 9 deletions pkg/scanner/enrich_insightsv2.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,16 +47,30 @@ func (e *insightsBasedPackageEnricherV2) Name() string {
func (e *insightsBasedPackageEnricherV2) Enrich(pkg *models.Package,
cb PackageDependencyCallbackFn,
) error {
res, err := e.client.GetPackageVersionInsight(context.Background(),
&insightsv2.GetPackageVersionInsightRequest{
PackageVersion: &packagev1.PackageVersion{
Package: &packagev1.Package{
Ecosystem: pkg.GetControlTowerSpecEcosystem(),
Name: pkg.GetName(),
},
Version: pkg.GetVersion(),
req := &insightsv2.GetPackageVersionInsightRequest{
PackageVersion: &packagev1.PackageVersion{
Package: &packagev1.Package{
Ecosystem: pkg.GetControlTowerSpecEcosystem(),
Name: pkg.GetName(),
},
})
Version: pkg.GetVersion(),
},
}

// For distro ecosystems (e.g. Alpine:v3.23, Ubuntu:22.04) that don't map to
// a known protobuf enum, pass the raw ecosystem string so control-tower can
// scope the OSV query to the correct distro advisory database.
if osvRawEcosystem := pkg.Manifest.Ecosystem; osvRawEcosystem != "" {
req.OsvEcosystem = &osvRawEcosystem
}
Comment thread
KunalSin9h marked this conversation as resolved.

// For Debian/Ubuntu packages, OSV indexes advisories by source package name.
// Pass it when available so control-tower uses it instead of the binary name.
if osvSourceName := pkg.OsvSourceName; osvSourceName != "" {
req.OsvSourceName = &osvSourceName
}

res, err := e.client.GetPackageVersionInsight(context.Background(), req)
if err != nil {
logger.Debugf("Failed to enrich package: %s/%s: %v",
pkg.GetName(), pkg.GetVersion(), err)
Expand Down
Loading