Skip to content

Commit

Permalink
Adding optional RPM summary to SBOMs
Browse files Browse the repository at this point in the history
This change adds an option to include RPM summary in a SBOM.

Signed-off-by: Alexey Ovchinnikov <[email protected]>
  • Loading branch information
a-ovchinnikov committed Jan 2, 2025
1 parent 4739b4b commit bdc850d
Show file tree
Hide file tree
Showing 6 changed files with 36 additions and 5 deletions.
1 change: 1 addition & 0 deletions cachi2/core/models/input.py
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,7 @@ class RpmPackageInput(_PackageInputBase):
"""Accepted input for a rpm package."""

type: Literal["rpm"]
add_rpm_summary: bool = False
options: Optional[ExtraOptions] = None


Expand Down
8 changes: 8 additions & 0 deletions cachi2/core/models/property_semantics.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ class PropertySet:
npm_development: bool = False
pip_package_binary: bool = False
bundler_package_binary: bool = False
rpm_summary: str = ""

@classmethod
def from_properties(cls, props: Iterable[Property]) -> "Self":
Expand All @@ -44,6 +45,7 @@ def from_properties(cls, props: Iterable[Property]) -> "Self":
npm_development = False
pip_package_binary = False
bundler_package_binary = False
rpm_summary = ""

for prop in props:
if prop.name == "cachi2:found_by":
Expand All @@ -58,6 +60,8 @@ def from_properties(cls, props: Iterable[Property]) -> "Self":
pip_package_binary = True
elif prop.name == "cachi2:bundler:package:binary":
bundler_package_binary = True
elif prop.name == "cachi2:rpm_summary":
rpm_summary = ""
else:
assert_never(prop.name)

Expand All @@ -68,6 +72,7 @@ def from_properties(cls, props: Iterable[Property]) -> "Self":
npm_development,
pip_package_binary,
bundler_package_binary,
rpm_summary,
)

def to_properties(self) -> list[Property]:
Expand All @@ -87,6 +92,8 @@ def to_properties(self) -> list[Property]:
props.append(Property(name="cachi2:pip:package:binary", value="true"))
if self.bundler_package_binary:
props.append(Property(name="cachi2:bundler:package:binary", value="true"))
if self.rpm_summary:
props.append(Property(name="cachi2:rpm_summary", value=self.rpm_summary))

return sorted(props, key=lambda p: (p.name, p.value))

Expand All @@ -100,4 +107,5 @@ def merge(self, other: "Self") -> "Self":
npm_development=self.npm_development and other.npm_development,
pip_package_binary=self.pip_package_binary or other.pip_package_binary,
bundler_package_binary=self.bundler_package_binary or other.bundler_package_binary,
rpm_summary=self.rpm_summary or other.rpm_summary,
)
1 change: 1 addition & 0 deletions cachi2/core/models/sbom.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
PropertyName = Literal[
"cachi2:bundler:package:binary",
"cachi2:found_by",
"cachi2:rpm_summary",
"cachi2:missing_hash:in_file",
"cachi2:pip:package:binary",
"cdx:npm:package:bundled",
Expand Down
24 changes: 20 additions & 4 deletions cachi2/core/package_managers/rpm/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ class Package:
vendor: Optional[str] = None
checksum: Optional[str] = None
repository_id: Optional[str] = None
summary: Optional[str] = None

@classmethod
def from_filepath(cls, rpm_filepath: Path, rpm_download_metadata: dict[str, Any]) -> "Package":
Expand Down Expand Up @@ -85,6 +86,7 @@ def _query_rpm_fields(file_path: Path) -> dict[str, str]:
"version=%{VERSION}\n"
"release=%{RELEASE}\n"
"arch=%{ARCH}\n"
"summary=%|SUMMARY?{%{SUMMARY}}:{}|\n"
# vendor and epoch are optional RPM tags; return "" if not set instead of "(None)"
"vendor=%|VENDOR?{%{VENDOR}}:{}|\n"
"epoch=%|EPOCH?{%{EPOCH}}:{}|\n"
Expand Down Expand Up @@ -205,7 +207,14 @@ def fetch_rpm_source(request: Request) -> RequestOutput:

for package in request.rpm_packages:
path = request.source_dir.join_within_root(package.path)
components.extend(_resolve_rpm_project(path, request.output_dir, options=package.options))
components.extend(
_resolve_rpm_project(
path,
request.output_dir,
options=package.options,
add_rpm_summary=package.add_rpm_summary,
)
)

# FIXME: this is only ever good enough for a PoC, but needs to be handled properly in the
# future.
Expand Down Expand Up @@ -240,6 +249,7 @@ def _resolve_rpm_project(
source_dir: RootedPath,
output_dir: RootedPath,
options: Optional[ExtraOptions] = None,
add_rpm_summary: bool = False,
) -> list[Component]:
"""
Process a request for a single RPM source directory.
Expand Down Expand Up @@ -288,7 +298,7 @@ def _resolve_rpm_project(
_verify_downloaded(metadata)

lockfile_relative_path = source_dir.subpath_from_root / DEFAULT_LOCKFILE_NAME
return _generate_sbom_components(metadata, lockfile_relative_path)
return _generate_sbom_components(metadata, lockfile_relative_path, add_rpm_summary)


def _download(
Expand Down Expand Up @@ -383,14 +393,20 @@ def _is_rpm_file(file_path: Path) -> bool:


def _generate_sbom_components(
files_metadata: dict[Path, Any], lockfile_path: Path
files_metadata: dict[Path, Any],
lockfile_path: Path,
add_rpm_summary: bool = False,
) -> list[Component]:
components = []
for file_path, file_metadata in files_metadata.items():
if not _is_rpm_file(file_path):
continue
package = Package.from_filepath(file_path, file_metadata)
components.append(package.to_component(lockfile_path))
component = package.to_component(lockfile_path)
if add_rpm_summary:
summary = Property(name="cachi2:rpm_summary", value=str(package.summary))
component.properties.append(summary)
components.append(component)
return components


Expand Down
5 changes: 5 additions & 0 deletions tests/unit/models/test_input.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ class TestPackageInput:
"type": "rpm",
"path": Path("."),
"options": None,
"add_rpm_summary": False,
},
),
(
Expand All @@ -80,6 +81,7 @@ class TestPackageInput:
"foorepo": {"arch": "x86_64", "enabled": True},
}
},
"add_rpm_summary": False,
},
{
"type": "rpm",
Expand All @@ -91,6 +93,7 @@ class TestPackageInput:
},
"ssl": None,
},
"add_rpm_summary": False,
},
),
(
Expand All @@ -110,6 +113,7 @@ class TestPackageInput:
"ssl_verify": False,
},
},
"add_rpm_summary": False,
},
),
(
Expand Down Expand Up @@ -138,6 +142,7 @@ class TestPackageInput:
"ssl_verify": False,
},
},
"add_rpm_summary": False,
},
),
],
Expand Down
2 changes: 1 addition & 1 deletion tests/unit/package_managers/test_rpm.py
Original file line number Diff line number Diff line change
Expand Up @@ -361,7 +361,7 @@ def test_resolve_rpm_project(
mock_model_validate.return_value, mock_package_dir_path, None
)
mock_verify_downloaded.assert_called_once_with({})
mock_generate_sbom_components.assert_called_once_with({}, Path("rpms.lock.yaml"))
mock_generate_sbom_components.assert_called_once_with({}, Path("rpms.lock.yaml"), False)


@mock.patch("cachi2.core.package_managers.rpm.main.run_cmd")
Expand Down

0 comments on commit bdc850d

Please sign in to comment.