Add relationships for dpkg packages (#2212)

* add relationships for deb packages

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>

* update snapshots

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>

* bump json schema

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>

* small refactor to remove duplicate code

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>

---------

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>
This commit is contained in:
Alex Goodman 2023-10-11 08:56:26 -04:00 committed by GitHub
parent 0748945c83
commit ef759038f5
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
22 changed files with 2541 additions and 42 deletions

View file

@ -3,5 +3,5 @@ package internal
const (
// JSONSchemaVersion is the current schema version output by the JSON encoder
// This is roughly following the "SchemaVer" guidelines for versioning the JSON schema. Please see schema/json/README.md for details on how to increment.
JSONSchemaVersion = "11.0.0"
JSONSchemaVersion = "11.0.1"
)

File diff suppressed because it is too large Load diff

View file

@ -57,7 +57,7 @@ func redactor(values ...string) testutils.Redactor {
`sha256:[A-Za-z0-9]{64}`: `sha256:redacted`,
// BOM refs
`bom-ref="[a-zA-Z0-9\-:]+"`: `bom-ref:redacted`,
`bom-ref="[a-zA-Z0-9\-:]+"`: `bom-ref="redacted"`,
},
)
}

View file

@ -32,7 +32,7 @@
<property name="syft:location:0:path">/some/path/pkg1</property>
</properties>
</component>
<component bom-ref="pkg:deb/debian/package-2@2.0.1?package-id=db4abfe497c180d3" type="library">
<component bom-ref="pkg:deb/debian/package-2@2.0.1?package-id=ad5013466727018f" type="library">
<name>package-2</name>
<version>2.0.1</version>
<cpe>cpe:2.3:*:some:package:2:*:*:*:*:*:*:*</cpe>

View file

@ -34,7 +34,7 @@
<property name="syft:location:0:path">/somefile-1.txt</property>
</properties>
</component>
<component bom-ref="pkg:deb/debian/package-2@2.0.1?package-id=958443e2d9304af4" type="library">
<component bom-ref="pkg:deb/debian/package-2@2.0.1?package-id=f27313b22a5ba330" type="library">
<name>package-2</name>
<version>2.0.1</version>
<cpe>cpe:2.3:*:some:package:2:*:*:*:*:*:*:*</cpe>

View file

@ -39,7 +39,7 @@
},
{
"name": "package-2",
"SPDXID": "SPDXRef-Package-deb-package-2-db4abfe497c180d3",
"SPDXID": "SPDXRef-Package-deb-package-2-ad5013466727018f",
"versionInfo": "2.0.1",
"supplier": "NOASSERTION",
"downloadLocation": "NOASSERTION",
@ -78,7 +78,7 @@
},
{
"spdxElementId": "SPDXRef-DocumentRoot-Directory-some-path",
"relatedSpdxElement": "SPDXRef-Package-deb-package-2-db4abfe497c180d3",
"relatedSpdxElement": "SPDXRef-Package-deb-package-2-ad5013466727018f",
"relationshipType": "CONTAINS"
},
{

View file

@ -39,7 +39,7 @@
},
{
"name": "package-2",
"SPDXID": "SPDXRef-Package-deb-package-2-958443e2d9304af4",
"SPDXID": "SPDXRef-Package-deb-package-2-f27313b22a5ba330",
"versionInfo": "2.0.1",
"supplier": "NOASSERTION",
"downloadLocation": "NOASSERTION",
@ -92,7 +92,7 @@
},
{
"spdxElementId": "SPDXRef-DocumentRoot-Image-user-image-input",
"relatedSpdxElement": "SPDXRef-Package-deb-package-2-958443e2d9304af4",
"relatedSpdxElement": "SPDXRef-Package-deb-package-2-f27313b22a5ba330",
"relationshipType": "CONTAINS"
},
{

View file

@ -39,7 +39,7 @@
},
{
"name": "package-2",
"SPDXID": "SPDXRef-Package-deb-package-2-958443e2d9304af4",
"SPDXID": "SPDXRef-Package-deb-package-2-f27313b22a5ba330",
"versionInfo": "2.0.1",
"supplier": "NOASSERTION",
"downloadLocation": "NOASSERTION",
@ -214,7 +214,7 @@
},
{
"spdxElementId": "SPDXRef-DocumentRoot-Image-user-image-input",
"relatedSpdxElement": "SPDXRef-Package-deb-package-2-958443e2d9304af4",
"relatedSpdxElement": "SPDXRef-Package-deb-package-2-f27313b22a5ba330",
"relationshipType": "CONTAINS"
},
{

View file

@ -61,7 +61,7 @@ ExternalRef: PACKAGE-MANAGER purl pkg:oci/user-image-input@sha256:2731251dc34951
##### Package: package-2
PackageName: package-2
SPDXID: SPDXRef-Package-deb-package-2-958443e2d9304af4
SPDXID: SPDXRef-Package-deb-package-2-f27313b22a5ba330
PackageVersion: 2.0.1
PackageSupplier: NOASSERTION
PackageDownloadLocation: NOASSERTION
@ -97,6 +97,6 @@ Relationship: SPDXRef-Package-python-package-1-125840abc1c66dd7 CONTAINS SPDXRef
Relationship: SPDXRef-Package-python-package-1-125840abc1c66dd7 CONTAINS SPDXRef-File-d1-f3-c6f5b29dca12661f
Relationship: SPDXRef-Package-python-package-1-125840abc1c66dd7 CONTAINS SPDXRef-File-f2-f9e49132a4b96ccd
Relationship: SPDXRef-DocumentRoot-Image-user-image-input CONTAINS SPDXRef-Package-python-package-1-125840abc1c66dd7
Relationship: SPDXRef-DocumentRoot-Image-user-image-input CONTAINS SPDXRef-Package-deb-package-2-958443e2d9304af4
Relationship: SPDXRef-DocumentRoot-Image-user-image-input CONTAINS SPDXRef-Package-deb-package-2-f27313b22a5ba330
Relationship: SPDXRef-DOCUMENT DESCRIBES SPDXRef-DocumentRoot-Image-user-image-input

View file

@ -20,7 +20,7 @@ FilesAnalyzed: false
##### Package: package-2
PackageName: package-2
SPDXID: SPDXRef-Package-deb-package-2-db4abfe497c180d3
SPDXID: SPDXRef-Package-deb-package-2-ad5013466727018f
PackageVersion: 2.0.1
PackageSupplier: NOASSERTION
PackageDownloadLocation: NOASSERTION
@ -50,6 +50,6 @@ ExternalRef: PACKAGE-MANAGER purl a-purl-2
##### Relationships
Relationship: SPDXRef-DocumentRoot-Directory-some-path CONTAINS SPDXRef-Package-python-package-1-9265397e5e15168a
Relationship: SPDXRef-DocumentRoot-Directory-some-path CONTAINS SPDXRef-Package-deb-package-2-db4abfe497c180d3
Relationship: SPDXRef-DocumentRoot-Directory-some-path CONTAINS SPDXRef-Package-deb-package-2-ad5013466727018f
Relationship: SPDXRef-DOCUMENT DESCRIBES SPDXRef-DocumentRoot-Directory-some-path

View file

@ -23,7 +23,7 @@ ExternalRef: PACKAGE-MANAGER purl pkg:oci/user-image-input@sha256:2731251dc34951
##### Package: package-2
PackageName: package-2
SPDXID: SPDXRef-Package-deb-package-2-958443e2d9304af4
SPDXID: SPDXRef-Package-deb-package-2-f27313b22a5ba330
PackageVersion: 2.0.1
PackageSupplier: NOASSERTION
PackageDownloadLocation: NOASSERTION
@ -53,6 +53,6 @@ ExternalRef: PACKAGE-MANAGER purl a-purl-1
##### Relationships
Relationship: SPDXRef-DocumentRoot-Image-user-image-input CONTAINS SPDXRef-Package-python-package-1-125840abc1c66dd7
Relationship: SPDXRef-DocumentRoot-Image-user-image-input CONTAINS SPDXRef-Package-deb-package-2-958443e2d9304af4
Relationship: SPDXRef-DocumentRoot-Image-user-image-input CONTAINS SPDXRef-Package-deb-package-2-f27313b22a5ba330
Relationship: SPDXRef-DOCUMENT DESCRIBES SPDXRef-DocumentRoot-Image-user-image-input

View file

@ -41,7 +41,7 @@
}
},
{
"id": "db4abfe497c180d3",
"id": "ad5013466727018f",
"name": "package-2",
"version": "2.0.1",
"type": "deb",

View file

@ -36,7 +36,7 @@
}
},
{
"id": "9fd0b9f41034991d",
"id": "aa0ca2c331576dfd",
"name": "package-2",
"version": "2.0.1",
"type": "deb",

View file

@ -37,7 +37,7 @@
}
},
{
"id": "958443e2d9304af4",
"id": "f27313b22a5ba330",
"name": "package-2",
"version": "2.0.1",
"type": "deb",

View file

@ -44,6 +44,11 @@ func TestDpkgCataloger(t *testing.T) {
Contains configuration files and directories required for
authentication to work on Debian systems. This package is required
on almost all installations.`,
Depends: []string{
"debconf (>= 0.5) | debconf-2.0",
"debconf (>= 1.5.19) | cdebconf",
"libpam-modules (>= 1.0.1-6)",
},
Files: []pkg.DpkgFileRecord{
{
Path: "/etc/pam.conf",
@ -112,6 +117,7 @@ func TestDpkgCataloger(t *testing.T) {
SQLite is a C library that implements an SQL database engine.
Programs that link with the SQLite library can have SQL database
access without running a separate RDBMS process.`,
Depends: []string{"libc6 (>= 2.29)"},
Files: []pkg.DpkgFileRecord{
{Path: "/usr/lib/aarch64-linux-gnu/libsqlite3.so.0.8.6", Digest: &file.Digest{
Algorithm: "md5",

View file

@ -36,13 +36,15 @@ func newDpkgPackage(d pkg.DpkgMetadata, dbLocation file.Location, resolver file.
Metadata: d,
}
// the current entry only has what may have been listed in the status file, however, there are additional
// files that are listed in multiple other locations. We should retrieve them all and merge the file lists
// together.
mergeFileListing(resolver, dbLocation, &p)
if resolver != nil {
// the current entry only has what may have been listed in the status file, however, there are additional
// files that are listed in multiple other locations. We should retrieve them all and merge the file lists
// together.
mergeFileListing(resolver, dbLocation, &p)
// fetch additional data from the copyright file to derive the license information
addLicenses(resolver, dbLocation, &p)
// fetch additional data from the copyright file to derive the license information
addLicenses(resolver, dbLocation, &p)
}
p.SetID()

View file

@ -35,7 +35,7 @@ func parseDpkgDB(resolver file.Resolver, env *generic.Environment, reader file.L
pkgs = append(pkgs, newDpkgPackage(m, reader.Location, resolver, env.LinuxRelease))
}
return pkgs, nil, nil
return pkgs, associateRelationships(pkgs), nil
}
// parseDpkgStatus is a parser function for Debian DB status contents, returning all Debian packages listed.
@ -63,6 +63,22 @@ func parseDpkgStatus(reader io.Reader) ([]pkg.DpkgMetadata, error) {
return metadata, nil
}
// dpkgExtractedMetadata is an adapter struct to capture the fields from the dpkg status file, however, the final
// pkg.DpkgMetadata struct has different types for some fields (e.g. Provides, Depends, and PreDepends is []string, not a string).
type dpkgExtractedMetadata struct {
Package string `mapstructure:"Package"`
Source string `mapstructure:"Source"`
Version string `mapstructure:"Version"`
SourceVersion string `mapstructure:"SourceVersion"`
Architecture string `mapstructure:"Architecture"`
Maintainer string `mapstructure:"Maintainer"`
InstalledSize int `mapstructure:"InstalledSize"`
Description string `mapstructure:"Description"`
Provides string `mapstructure:"Provides"`
Depends string `mapstructure:"Depends"`
PreDepends string `mapstructure:"PreDepends"` // note: original doc is Pre-Depends
}
// parseDpkgStatusEntry returns an individual Dpkg entry, or returns errEndOfPackages if there are no more packages to parse from the reader.
func parseDpkgStatusEntry(reader *bufio.Reader) (*pkg.DpkgMetadata, error) {
var retErr error
@ -77,22 +93,36 @@ func parseDpkgStatusEntry(reader *bufio.Reader) (*pkg.DpkgMetadata, error) {
retErr = err
}
entry := pkg.DpkgMetadata{}
err = mapstructure.Decode(dpkgFields, &entry)
raw := dpkgExtractedMetadata{}
err = mapstructure.Decode(dpkgFields, &raw)
if err != nil {
return nil, err
}
sourceName, sourceVersion := extractSourceVersion(entry.Source)
sourceName, sourceVersion := extractSourceVersion(raw.Source)
if sourceVersion != "" {
entry.SourceVersion = sourceVersion
entry.Source = sourceName
raw.SourceVersion = sourceVersion
raw.Source = sourceName
}
if entry.Package == "" {
if raw.Package == "" {
return nil, retErr
}
entry := pkg.DpkgMetadata{
Package: raw.Package,
Source: raw.Source,
Version: raw.Version,
SourceVersion: raw.SourceVersion,
Architecture: raw.Architecture,
Maintainer: raw.Maintainer,
InstalledSize: raw.InstalledSize,
Description: raw.Description,
Provides: splitPkgList(raw.Provides),
Depends: splitPkgList(raw.Depends),
PreDepends: splitPkgList(raw.PreDepends),
}
// there may be an optional conffiles section that we should persist as files
if conffilesSection, exists := dpkgFields["Conffiles"]; exists && conffilesSection != nil {
if sectionStr, ok := conffilesSection.(string); ok {
@ -108,6 +138,17 @@ func parseDpkgStatusEntry(reader *bufio.Reader) (*pkg.DpkgMetadata, error) {
return &entry, retErr
}
func splitPkgList(pkgList string) (ret []string) {
fields := strings.Split(pkgList, ",")
for _, field := range fields {
field = strings.TrimSpace(field)
if field != "" {
ret = append(ret, field)
}
}
return ret
}
func extractAllFields(reader *bufio.Reader) (map[string]interface{}, error) {
dpkgFields := make(map[string]interface{})
var key string
@ -195,3 +236,79 @@ func handleNewKeyValue(line string) (key string, val interface{}, err error) {
return "", nil, fmt.Errorf("cannot parse field from line: '%s'", line)
}
// associateRelationships will create relationships between packages based on the "Depends", "Pre-Depends", and "Provides"
// fields for installed packages. if there is an installed package that has a dependency that is (somehow) not installed,
// then that relationship (between the installed and uninstalled package) will NOT be created.
func associateRelationships(pkgs []pkg.Package) (relationships []artifact.Relationship) {
// map["provides" + "package"] -> packages that provide that package
lookup := make(map[string][]pkg.Package)
// read provided and add as keys for lookup keys as well as package names
for _, p := range pkgs {
meta, ok := p.Metadata.(pkg.DpkgMetadata)
if !ok {
log.Warnf("cataloger failed to extract dpkg 'provides' metadata for package %+v", p.Name)
continue
}
lookup[p.Name] = append(lookup[p.Name], p)
for _, provides := range meta.Provides {
k := stripVersionSpecifier(provides)
lookup[k] = append(lookup[k], p)
}
}
// read "Depends" and "Pre-Depends" and match with keys
for _, p := range pkgs {
meta, ok := p.Metadata.(pkg.DpkgMetadata)
if !ok {
log.Warnf("cataloger failed to extract dpkg 'dependency' metadata for package %+v", p.Name)
continue
}
var allDeps []string
allDeps = append(allDeps, meta.Depends...)
allDeps = append(allDeps, meta.PreDepends...)
for _, depSpecifier := range allDeps {
deps := splitPackageChoice(depSpecifier)
for _, dep := range deps {
for _, depPkg := range lookup[dep] {
relationships = append(relationships, artifact.Relationship{
From: depPkg,
To: p,
Type: artifact.DependencyOfRelationship,
})
}
}
}
}
return relationships
}
func stripVersionSpecifier(s string) string {
// examples:
// libgmp10 (>= 2:6.2.1+dfsg1) --> libgmp10
// libgmp10 --> libgmp10
// foo [i386] --> foo
// default-mta | mail-transport-agent --> default-mta | mail-transport-agent
// kernel-headers-2.2.10 [!hurd-i386] --> kernel-headers-2.2.10
items := internal.SplitAny(s, "[(<>=")
if len(items) == 0 {
return s
}
return strings.TrimSpace(items[0])
}
func splitPackageChoice(s string) (ret []string) {
fields := strings.Split(s, "|")
for _, field := range fields {
field = strings.TrimSpace(field)
if field != "" {
ret = append(ret, stripVersionSpecifier(field))
}
}
return ret
}

View file

@ -11,9 +11,11 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/anchore/syft/syft/artifact"
"github.com/anchore/syft/syft/file"
"github.com/anchore/syft/syft/linux"
"github.com/anchore/syft/syft/pkg"
"github.com/anchore/syft/syft/pkg/cataloger/generic"
"github.com/anchore/syft/syft/pkg/cataloger/internal/pkgtest"
)
@ -48,6 +50,18 @@ func Test_parseDpkgStatus(t *testing.T) {
* apt-cdrom to use removable media as a source for packages
* apt-config as an interface to the configuration settings
* apt-key as an interface to manage authentication keys`,
Provides: []string{"apt-transport-https (= 1.8.2)"},
Depends: []string{
"adduser",
"gpgv | gpgv2 | gpgv1",
"debian-archive-keyring",
"libapt-pkg5.0 (>= 1.7.0~alpha3~)",
"libc6 (>= 2.15)",
"libgcc1 (>= 1:3.0)",
"libgnutls30 (>= 3.6.6)",
"libseccomp2 (>= 1.0.1)",
"libstdc++6 (>= 5.2)",
},
Files: []pkg.DpkgFileRecord{
{
Path: "/etc/apt/apt.conf.d/01autoremove",
@ -110,6 +124,18 @@ func Test_parseDpkgStatus(t *testing.T) {
* apt-cdrom to use removable media as a source for packages
* apt-config as an interface to the configuration settings
* apt-key as an interface to manage authentication keys`,
Provides: []string{"apt-transport-https (= 1.8.2)"},
Depends: []string{
"adduser",
"gpgv | gpgv2 | gpgv1",
"debian-archive-keyring",
"libapt-pkg5.0 (>= 1.7.0~alpha3~)",
"libc6 (>= 2.15)",
"libgcc1 (>= 1:3.0)",
"libgnutls30 (>= 3.6.6)",
"libseccomp2 (>= 1.0.1)",
"libstdc++6 (>= 5.2)",
},
Files: []pkg.DpkgFileRecord{},
},
},
@ -135,7 +161,9 @@ func Test_parseDpkgStatus(t *testing.T) {
globe. It is updated periodically to reflect changes made by
political bodies to time zone boundaries, UTC offsets, and
daylight-saving rules.`,
Files: []pkg.DpkgFileRecord{},
Provides: []string{"tzdata-buster"},
Depends: []string{"debconf (>= 0.5) | debconf-2.0"},
Files: []pkg.DpkgFileRecord{},
},
{
Package: "util-linux",
@ -149,6 +177,14 @@ func Test_parseDpkgStatus(t *testing.T) {
important utilities included in this package allow you to view kernel
messages, create new filesystems, view block device information,
interface with real time clock, etc.`,
Depends: []string{"fdisk", "login (>= 1:4.5-1.1~)"},
PreDepends: []string{
"libaudit1 (>= 1:2.2.1)", "libblkid1 (>= 2.31.1)", "libc6 (>= 2.25)",
"libcap-ng0 (>= 0.7.9)", "libmount1 (>= 2.25)", "libpam0g (>= 0.99.7.1)",
"libselinux1 (>= 2.6-3~)", "libsmartcols1 (>= 2.33)", "libsystemd0",
"libtinfo6 (>= 6)", "libudev1 (>= 183)", "libuuid1 (>= 2.16)",
"zlib1g (>= 1:1.1.4)",
},
Files: []pkg.DpkgFileRecord{
{
Path: "/etc/default/hwclock",
@ -397,3 +433,122 @@ func Test_handleNewKeyValue(t *testing.T) {
})
}
}
func Test_stripVersionSpecifier(t *testing.T) {
tests := []struct {
name string
input string
want string
}{
{
name: "package name only",
input: "test",
want: "test",
},
{
name: "with version",
input: "test (1.2.3)",
want: "test",
},
{
name: "multiple packages",
input: "test | other",
want: "test | other",
},
{
name: "with architecture specifiers",
input: "test [amd64 i386]",
want: "test",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
assert.Equal(t, tt.want, stripVersionSpecifier(tt.input))
})
}
}
func Test_associateRelationships(t *testing.T) {
tests := []struct {
name string
fixture string
wantRelationships map[string][]string
}{
{
name: "relationships for coreutils",
fixture: "test-fixtures/status/coreutils-relationships",
wantRelationships: map[string][]string{
"coreutils": {"libacl1", "libattr1", "libc6", "libgmp10", "libselinux1"},
"libacl1": {"libc6"},
"libattr1": {"libc6"},
"libc6": {"libgcc-s1"},
"libgcc-s1": {"gcc-12-base", "libc6"},
"libgmp10": {"libc6"},
"libpcre2-8-0": {"libc6"},
"libselinux1": {"libc6", "libpcre2-8-0"},
},
},
{
name: "relationships from dpkg example docs",
fixture: "test-fixtures/status/doc-examples",
wantRelationships: map[string][]string{
"made-up-package-1": {"kernel-headers-2.2.10", "hurd-dev", "gnumach-dev"},
"made-up-package-2": {"libluajit5.1-dev", "liblua5.1-dev"},
"made-up-package-3": {"foo", "bar"},
// note that the "made-up-package-4" depends on "made-up-package-5" but not via the direct
// package name, but through the "provides" virtual package name "virtual-package-5".
"made-up-package-4": {"made-up-package-5"},
// note that though there is a "default-mta | mail-transport-agent | not-installed"
// dependency choice we raise up the packages that are installed for every choice.
// In this case that means that "default-mta" and "mail-transport-agent".
"mutt": {"libc6", "default-mta", "mail-transport-agent"},
},
},
{
name: "relationships for libpam-runtime",
fixture: "test-fixtures/status/libpam-runtime",
wantRelationships: map[string][]string{
"libpam-runtime": {"debconf1", "debconf-2.0", "debconf2", "cdebconf", "libpam-modules"},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
f, err := os.Open(tt.fixture)
require.NoError(t, err)
reader := file.NewLocationReadCloser(file.NewLocation(tt.fixture), f)
pkgs, relationships, err := parseDpkgDB(nil, &generic.Environment{}, reader)
require.NotEmpty(t, pkgs)
require.NotEmpty(t, relationships)
require.NoError(t, err)
if d := cmp.Diff(tt.wantRelationships, abstractRelationships(t, relationships)); d != "" {
t.Errorf("unexpected relationships (-want +got):\n%s", d)
}
})
}
}
func abstractRelationships(t testing.TB, relationships []artifact.Relationship) map[string][]string {
t.Helper()
abstracted := make(map[string][]string)
for _, relationship := range relationships {
fromPkg, ok := relationship.From.(pkg.Package)
if !ok {
continue
}
toPkg, ok := relationship.To.(pkg.Package)
if !ok {
continue
}
// we build this backwards since we use DependencyOfRelationship instead of DependsOn
abstracted[toPkg.Name] = append(abstracted[toPkg.Name], fromPkg.Name)
}
return abstracted
}

View file

@ -0,0 +1,114 @@
Package: coreutils
Essential: yes
Status: install ok installed
Priority: required
Section: utils
Installed-Size: 20272
Maintainer: Michael Stone <mstone@debian.org>
Architecture: arm64
Multi-Arch: foreign
Version: 9.1-1
Pre-Depends: libacl1 (>= 2.2.23), libattr1 (>= 1:2.4.44), libc6 (>= 2.34), libgmp10 (>= 2:6.2.1+dfsg1), libselinux1 (>= 3.1~)
Package: libacl1
Status: install ok installed
Priority: optional
Section: libs
Installed-Size: 101
Maintainer: Guillem Jover <guillem@debian.org>
Architecture: arm64
Multi-Arch: same
Source: acl
Version: 2.3.1-3
Depends: libc6 (>= 2.33)
Package: libc6
Status: install ok installed
Priority: optional
Section: libs
Installed-Size: 23127
Maintainer: GNU Libc Maintainers <debian-glibc@lists.debian.org>
Architecture: arm64
Multi-Arch: same
Source: glibc
Version: 2.36-9+deb12u1
Depends: libgcc-s1
Recommends: libidn2-0 (>= 2.0.5~)
Suggests: glibc-doc, debconf | debconf-2.0, libc-l10n, locales, libnss-nis, libnss-nisplus
Breaks: aide (<< 0.17.3-4+b3), busybox (<< 1.30.1-6), chrony (<< 4.2-3~), fakechroot (<< 2.19-3.5), firefox (<< 91~), firefox-esr (<< 91~), gnumach-image-1.8-486 (<< 2:1.8+git20210923~), gnumach-image-1.8-486-dbg (<< 2:1.8+git20210923~), gnumach-image-1.8-xen-486 (<< 2:1.8+git20210923~), gnumach-image-1.8-xen-486-dbg (<< 2:1.8+git20210923~), hurd (<< 1:0.9.git20220301-2), ioquake3 (<< 1.36+u20200211.f2c61c1~dfsg-2~), iraf-fitsutil (<< 2018.07.06-4), libgegl-0.4-0 (<< 0.4.18), libtirpc1 (<< 0.2.3), locales (<< 2.36), locales-all (<< 2.36), macs (<< 2.2.7.1-3~), nocache (<< 1.1-1~), nscd (<< 2.36), openarena (<< 0.8.8+dfsg-4~), openssh-server (<< 1:8.1p1-5), python3-iptables (<< 1.0.0-2), r-cran-later (<< 0.7.5+dfsg-2), tinydns (<< 1:1.05-14), valgrind (<< 1:3.19.0-1~), wcc (<< 0.0.2+dfsg-3)
Package: libgcc-s1
Protected: yes
Status: install ok installed
Priority: optional
Section: libs
Installed-Size: 147
Maintainer: Debian GCC Maintainers <debian-gcc@lists.debian.org>
Architecture: arm64
Multi-Arch: same
Source: gcc-12
Version: 12.2.0-14
Replaces: libgcc1 (<< 1:10)
Provides: libgcc1 (= 1:12.2.0-14)
Depends: gcc-12-base (= 12.2.0-14), libc6 (>= 2.35)
Package: gcc-12-base
Status: install ok installed
Priority: optional
Section: libs
Installed-Size: 100
Maintainer: Debian GCC Maintainers <debian-gcc@lists.debian.org>
Architecture: arm64
Multi-Arch: same
Source: gcc-12
Version: 12.2.0-14
Breaks: gnat (<< 7)
Package: libattr1
Status: install ok installed
Priority: optional
Section: libs
Installed-Size: 99
Maintainer: Guillem Jover <guillem@debian.org>
Architecture: arm64
Multi-Arch: same
Source: attr
Version: 1:2.5.1-4
Depends: libc6 (>= 2.17)
Package: libgmp10
Status: install ok installed
Priority: optional
Section: libs
Installed-Size: 855
Maintainer: Debian Science Team <debian-science-maintainers@lists.alioth.debian.org>
Architecture: arm64
Multi-Arch: same
Source: gmp
Version: 2:6.2.1+dfsg1-1.1
Depends: libc6 (>= 2.17)
Breaks: libmath-gmp-perl (<< 2.20-1), libmath-prime-util-gmp-perl (<< 0.51-2), postgresql-pgmp (<< 1.0.3-1)
Package: libselinux1
Status: install ok installed
Priority: optional
Section: libs
Installed-Size: 223
Maintainer: Debian SELinux maintainers <selinux-devel@lists.alioth.debian.org>
Architecture: arm64
Multi-Arch: same
Source: libselinux (3.4-1)
Version: 3.4-1+b6
Depends: libc6 (>= 2.34), libpcre2-8-0 (>= 10.22)
Package: libpcre2-8-0
Status: install ok installed
Priority: optional
Section: libs
Installed-Size: 649
Maintainer: Matthew Vernon <matthew@debian.org>
Architecture: arm64
Multi-Arch: same
Source: pcre2
Version: 10.42-1
Depends: libc6 (>= 2.34)

View file

@ -0,0 +1,46 @@
Package: mutt
Version: 1.3.17-1
Depends: libc6 (>= 2.2.1), default-mta | mail-transport-agent | not-installed
Package: made-up-package-1
Version: 1.0.0
Source: glibc
Depends: kernel-headers-2.2.10 [!hurd-i386],
hurd-dev [hurd-i386], gnumach-dev [hurd-i386]
Package: made-up-package-2
Version: 2.0.0
Depends:
libluajit5.1-dev [i386 amd64 kfreebsd-i386 armel armhf powerpc mips],
liblua5.1-dev [hurd-i386 ia64 kfreebsd-amd64 s390x sparc],
Package: made-up-package-3
Version: 3.0.0
Depends: foo [i386], bar [amd64]
Package: made-up-package-4
Version: 3.0.0
Depends: virtual-package-5
Package: made-up-package-5
Provides: virtual-package-5 (=1.0)
Package: foo
Package: bar
Package: kernel-headers-2.2.10
Package: hurd-dev
Package: gnumach-dev
Package: default-mta
Package: mail-transport-agent
Package: libc6
Package: libluajit5.1-dev
Package: liblua5.1-dev

View file

@ -0,0 +1,23 @@
Package: libpam-runtime
Status: install ok installed
Priority: required
Section: admin
Installed-Size: 876
Maintainer: Sam Hartman <hartmans@debian.org>
Architecture: all
Multi-Arch: foreign
Source: pam
Version: 1.5.2-6+deb12u1
Replaces: libpam0g-dev, libpam0g-util
Depends: debconf1 (>= 0.5) | debconf-2.0, debconf2 (>= 1.5.19) | cdebconf, libpam-modules (>= 1.0.1-6)
Conflicts: libpam0g-util
Package: debconf1
Package: debconf2
Package: debconf-2.0
Package: cdebconf
Package: libpam-modules

View file

@ -14,16 +14,49 @@ var _ FileOwner = (*DpkgMetadata)(nil)
// DpkgMetadata represents all captured data for a Debian package DB entry; available fields are described
// at http://manpages.ubuntu.com/manpages/xenial/man1/dpkg-query.1.html in the --showformat section.
// Additional information about how these fields are used can be found at
// - https://www.debian.org/doc/debian-policy/ch-controlfields.html
// - https://www.debian.org/doc/debian-policy/ch-relationships.html
// - https://www.debian.org/doc/debian-policy/ch-binary.html#s-virtual-pkg
// - https://www.debian.org/doc/debian-policy/ch-relationships.html#s-virtual
type DpkgMetadata struct {
Package string `mapstructure:"Package" json:"package"`
Source string `mapstructure:"Source" json:"source" cyclonedx:"source"`
Version string `mapstructure:"Version" json:"version"`
SourceVersion string `mapstructure:"SourceVersion" json:"sourceVersion" cyclonedx:"sourceVersion"`
Architecture string `mapstructure:"Architecture" json:"architecture"`
Maintainer string `mapstructure:"Maintainer" json:"maintainer"`
InstalledSize int `mapstructure:"InstalledSize" json:"installedSize" cyclonedx:"installedSize"`
Description string `mapstructure:"Description" hash:"ignore" json:"-"`
Files []DpkgFileRecord `json:"files"`
Package string `json:"package"`
Source string `json:"source" cyclonedx:"source"`
Version string `json:"version"`
SourceVersion string `json:"sourceVersion" cyclonedx:"sourceVersion"`
// Architecture can include the following sets of values depending on context and the control file used:
// - a unique single word identifying a Debian machine architecture as described in Architecture specification string (https://www.debian.org/doc/debian-policy/ch-customized-programs.html#s-arch-spec) .
// - an architecture wildcard identifying a set of Debian machine architectures, see Architecture wildcards (https://www.debian.org/doc/debian-policy/ch-customized-programs.html#s-arch-wildcard-spec). any matches all Debian machine architectures and is the most frequently used.
// - "all", which indicates an architecture-independent package.
// - "source", which indicates a source package.
Architecture string `json:"architecture"`
// Maintainer is the package maintainers name and email address. The name must come first, then the email
// address inside angle brackets <> (in RFC822 format).
Maintainer string `json:"maintainer"`
InstalledSize int `json:"installedSize" cyclonedx:"installedSize"`
// Description contains a description of the binary package, consisting of two parts, the synopsis or the short
// description, and the long description (in a multiline format).
Description string `hash:"ignore" json:"-"`
// Provides is a virtual package that is provided by one or more packages. A virtual package is one which appears
// in the Provides control field of another package. The effect is as if the package(s) which provide a particular
// virtual package name had been listed by name everywhere the virtual package name appears. (See also Virtual packages)
Provides []string `json:"provides,omitempty"`
// Depends This declares an absolute dependency. A package will not be configured unless all of the packages listed in
// its Depends field have been correctly configured (unless there is a circular dependency).
Depends []string `json:"depends,omitempty"`
// PreDepends is like Depends, except that it also forces dpkg to complete installation of the packages named
// before even starting the installation of the package which declares the pre-dependency.
PreDepends []string `json:"preDepends,omitempty"`
Files []DpkgFileRecord `json:"files"`
}
// DpkgFileRecord represents a single file attributed to a debian package.