Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
5 changes: 5 additions & 0 deletions src/debsbom/dpkg/package.py
Original file line number Diff line number Diff line change
Expand Up @@ -569,6 +569,7 @@ class BinaryPackage(Package):
provides: list[VirtualPackage]
built_using: list[Dependency]
description: str | None
essential: bool
manually_installed: bool
status: DpkgStatus
_locator: str | None = None
Expand All @@ -585,6 +586,7 @@ def __init__(
provides: list[VirtualPackage] = [],
built_using: list[Dependency] = [],
description: str | None = None,
essential: bool = False,
homepage: str | None = None,
checksums: dict[ChecksumAlgo, str] | None = None,
manually_installed: bool = True,
Expand All @@ -600,6 +602,7 @@ def __init__(
self.provides = provides
self.built_using = built_using
self.description = description
self.essential = essential
self.homepage = homepage
self.checksums = checksums or {}
self.manually_installed = manually_installed
Expand Down Expand Up @@ -654,6 +657,7 @@ def merge_with(self, other: "BinaryPackage"):
self.source = other.source
if not self.description:
self.description = other.description
self.essential |= other.essential
self.manually_installed |= other.manually_installed
# we cannot merge the status, but if the other package is
# marked as installed, consider all as installed.
Expand Down Expand Up @@ -759,6 +763,7 @@ def from_deb822(cls, package) -> "BinaryPackage":
provides=provides,
built_using=sdepends,
description=cls._cleanup_description(package.get("Description")),
essential=package.get("Essential") == "yes",
homepage=package.get("Homepage"),
checksums=checksums_from_package(package),
status=status,
Expand Down
7 changes: 6 additions & 1 deletion src/debsbom/export/cdx.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ def add_key(name, _type, _for):
add_key("purl", "string", "node")
add_key("type", "string", "node")
add_key("section", "string", "node")
add_key("essential", "string", "node")
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If this property only exists for CDX SBOMs we want to document that. IIRC we recommend to use SPDX SBOMs for graph exporting since they contain better relationship information, so we might want to add this info there too.


def add_packages(self, graph: ET.Element):
for p in self.document.components:
Expand All @@ -58,11 +59,15 @@ def add_packages(self, graph: ET.Element):
ET.SubElement(node, "data", {"key": "d_purl"}).text = str(p.purl)
ET.SubElement(node, "data", {"key": "d_type"}).text = p.type
section = "unknown"
essential = "unknown"
for prop in p.properties:
if prop.name == "section":
section = prop.value
break
elif prop.name == "essential":
essential = prop.value

ET.SubElement(node, "data", {"key": "d_section"}).text = section
ET.SubElement(node, "data", {"key": "d_essential"}).text = essential

def add_dependencies(self, graph: ET.Element):
for r in self.document.dependencies:
Expand Down
3 changes: 3 additions & 0 deletions src/debsbom/generate/cdx.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,9 @@ def cdx_package_repr(
if package.is_binary():
entry.description = package.description
entry.properties.add(cdx_model.Property(name="section", value=package.section))
entry.properties.add(
cdx_model.Property(name="essential", value="yes" if package.essential else "no")
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we not use a simple True or False here? It should translate directly to the JSON boolean type

)
logger.debug(f"Created binary component: {entry}")
elif package.is_source():
if package.vcs:
Expand Down
14 changes: 14 additions & 0 deletions tests/data/dpkg-status-minimal
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,17 @@ Description: GNU assembler, linker and binary utilities
and various libraries to build programs.
Homepage: https://www.gnu.org/software/binutils/

Package: coreutils
Essential: yes
Status: install ok installed
Priority: required
Section: utils
Installed-Size: 17994
Maintainer: Michael Stone <mstone@debian.org>
Architecture: amd64
Multi-Arch: foreign
Version: 9.10-1
Pre-Depends: libacl1 (>= 2.2.23), libattr1 (>= 1:2.4.48), libc6 (>= 2.42), libgmp10 (>= 2:6.3.0+dfsg), libselinux1 (>= 3.1~), libssl3t64 (>= 3.0.0), libsystemd0 (>= 254)
Description: GNU core utilities
This package contains the basic file, shell and text manipulation
utilities which are expected to exist on every operating system.
6 changes: 3 additions & 3 deletions tests/test_download.py
Original file line number Diff line number Diff line change
Expand Up @@ -143,12 +143,12 @@ def test_package_resolver_resolve_spdx(spdx_bomfile, tmpdir, sdl):
rs_cache = PersistentResolverCache(cachedir)
urs = UpstreamResolver(sdl, rs_cache)

files = list(urs.resolve(next(prs)))
files = list(urs.resolve(next(filter(lambda p: p.name == "binutils", prs))))
assert "binutils" in files[0].filename

# resolve with cache
prs = PackageResolver.create(spdx_bomfile)
files = list(urs.resolve(next(prs)))
files = list(urs.resolve(next(filter(lambda p: p.name == "binutils", prs))))
assert "binutils" in files[0].filename


Expand Down Expand Up @@ -210,7 +210,7 @@ def test_repack(tmpdir, spdx_bomfile, cdx_bomfile, http_session, sdl):

# download a single package
dl = PackageDownloader(dl_dir, session=http_session)
for p in filter_sources(pkgs):
for p in filter(lambda p: p.name == "binutils", filter_sources(pkgs)):
dl.register(urs.resolve(p), p)
files = list(dl.download())
assert len(files) == 3
Expand Down
5 changes: 5 additions & 0 deletions tests/test_dpkg.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ def test_parse_minimal_status_file(mode):

assert bpkg.name == "binutils"
assert bpkg.section == "devel"
assert bpkg.essential is False
assert bpkg.maintainer == "Matthias Klose <doko@debian.org>"
assert bpkg.source == Dependency(bpkg.name, None, ("=", bpkg.version), arch="source")
assert bpkg.version == "2.40-2"
Expand All @@ -61,6 +62,10 @@ def test_parse_minimal_status_file(mode):
assert bpkg.homepage == "https://www.gnu.org/software/binutils/"
assert bpkg.status == DpkgStatus.INSTALLED

coreutils = [p for p in packages if isinstance(p, BinaryPackage)][1]
assert coreutils.name == "coreutils"
assert coreutils.essential is True

spkg = packages[1]
assert spkg.name == "binutils"
assert spkg.version == bpkg.version
Expand Down
9 changes: 9 additions & 0 deletions tests/test_export.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,12 @@ def get_name(node):
return data.text
return None

def get_essential(node):
for data in node.findall(f"{{{NAMESPACE}}}data"):
if data.get("key") == "d_essential":
return data.text
return None

dbom = sbom_generator("tests/root/tree", sbom_types=[sbom_type])
outdir = Path(tmpdir)
dbom.generate(str(outdir / "sbom"), validate=False)
Expand All @@ -55,6 +61,9 @@ def get_name(node):
assert root.tag == f"{{{NAMESPACE}}}graphml"
node_tag = f"{{{NAMESPACE}}}node"
assert any(map(lambda n: get_name(n) == "binutils", root.iter(node_tag)))
if sbom_type == SBOMType.CycloneDX:
jansson = next(filter(lambda n: get_name(n) == "libjansson4", root.iter(node_tag)))
assert get_essential(jansson) == "no"

edge_tag = f"{{{NAMESPACE}}}edge"
assert any(map(lambda n: "binutils" in n.get("id"), root.iter(edge_tag)))