Skip to content

Commit c3fa5c2

Browse files
committed
Extend sbom
1 parent 904acb9 commit c3fa5c2

2 files changed

Lines changed: 107 additions & 9 deletions

File tree

dfetch/reporting/sbom_reporter.py

Lines changed: 103 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,23 @@
1515
An fetched project generates an sbom
1616
"""
1717

18+
from decimal import Decimal
1819
from typing import List
1920

2021
from cyclonedx.builder.this import this_component as cdx_lib_component
2122
from cyclonedx.model import ExternalReference, ExternalReferenceType, XsUri
2223
from cyclonedx.model.bom import Bom
2324
from cyclonedx.model.component import Component, ComponentType
24-
from cyclonedx.model.license import LicenseExpression
25-
from cyclonedx.model.tool import Tool
25+
from cyclonedx.model.component_evidence import (
26+
AnalysisTechnique,
27+
ComponentEvidence,
28+
Identity,
29+
IdentityField,
30+
Method,
31+
Occurrence,
32+
)
33+
from cyclonedx.model.contact import OrganizationalEntity
34+
from cyclonedx.model.license import DisjunctiveLicense as CycloneDxLicense
2635
from cyclonedx.output import make_outputter
2736
from cyclonedx.schema import OutputFormat, SchemaVersion
2837

@@ -40,14 +49,57 @@
4049
class SbomReporter(Reporter):
4150
"""Reporter for generating SBoM's."""
4251

43-
dfetch_tool = Tool(vendor="dfetch-org", name="dfetch", version=dfetch.__version__)
52+
dfetch_tool = Component(
53+
type=ComponentType.APPLICATION,
54+
supplier=OrganizationalEntity(name="dfetch-org"),
55+
name="dfetch",
56+
version=dfetch.__version__,
57+
bom_ref=f"dfetch-{dfetch.__version__}",
58+
licenses=[CycloneDxLicense(name="MIT License", id="MIT")],
59+
external_references=[
60+
ExternalReference(
61+
type=ExternalReferenceType.VCS,
62+
url=XsUri("https://github.com/dfetch-org/dfetch"),
63+
),
64+
ExternalReference(
65+
type=ExternalReferenceType.BUILD_SYSTEM,
66+
url=XsUri("https://github.com/dfetch-org/dfetch/actions"),
67+
),
68+
ExternalReference(
69+
type=ExternalReferenceType.ISSUE_TRACKER,
70+
url=XsUri("https://github.com/dfetch-org/dfetch/issues"),
71+
),
72+
ExternalReference(
73+
type=ExternalReferenceType.DISTRIBUTION,
74+
url=XsUri("https://pypi.org/project/dfetch/"),
75+
),
76+
ExternalReference(
77+
type=ExternalReferenceType.DOCUMENTATION,
78+
url=XsUri("https://dfetch.readthedocs.io/"),
79+
),
80+
ExternalReference(
81+
type=ExternalReferenceType.LICENSE,
82+
url=XsUri("https://github.com/dfetch-org/dfetch/blob/main/LICENSE"),
83+
),
84+
ExternalReference(
85+
type=ExternalReferenceType.RELEASE_NOTES,
86+
url=XsUri(
87+
"https://github.com/dfetch-org/dfetch/blob/main/CHANGELOG.rst"
88+
),
89+
),
90+
ExternalReference(
91+
type=ExternalReferenceType.WEBSITE,
92+
url=XsUri("https://dfetch-org.github.io/"),
93+
),
94+
],
95+
)
4496

4597
name = "SBoM"
4698

4799
def __init__(self) -> None:
48100
"""Start the report."""
49101
self._bom = Bom()
50-
self._bom.metadata.tools.tools.add(self.dfetch_tool)
102+
self._bom.metadata.tools.components.add(self.dfetch_tool)
51103
self._bom.metadata.tools.components.add(cdx_lib_component())
52104

53105
def add_project(
@@ -66,6 +118,47 @@ def add_project(
66118
version=version,
67119
type=ComponentType.LIBRARY,
68120
purl=purl,
121+
evidence=ComponentEvidence(
122+
occurrences=[Occurrence(location="dfetch.yaml")],
123+
identity=[
124+
Identity(
125+
field=IdentityField.NAME,
126+
tools=[self.dfetch_tool.bom_ref],
127+
methods=[
128+
Method(
129+
technique=AnalysisTechnique.MANIFEST_ANALYSIS,
130+
confidence=Decimal.from_float(0.4),
131+
value="Name as used for project in dfetch.yaml",
132+
)
133+
],
134+
concluded_value=project.name,
135+
),
136+
Identity(
137+
field=IdentityField.VERSION,
138+
tools=[self.dfetch_tool.bom_ref],
139+
methods=[
140+
Method(
141+
technique=AnalysisTechnique.MANIFEST_ANALYSIS,
142+
confidence=Decimal.from_float(0.4),
143+
value="Version as used for project in dfetch.yaml",
144+
)
145+
],
146+
concluded_value=version,
147+
),
148+
Identity(
149+
field=IdentityField.PURL,
150+
tools=[self.dfetch_tool.bom_ref],
151+
methods=[
152+
Method(
153+
technique=AnalysisTechnique.MANIFEST_ANALYSIS,
154+
confidence=Decimal.from_float(0.4),
155+
value="Determined from the VCS url as used for project in dfetch.yaml",
156+
)
157+
],
158+
concluded_value=purl.to_string(),
159+
),
160+
],
161+
),
69162
)
70163

71164
if purl.type == "github":
@@ -96,7 +189,12 @@ def add_project(
96189
)
97190

98191
for lic in licenses:
99-
component.licenses.add(LicenseExpression(lic.name))
192+
cdx_license = CycloneDxLicense(name=lic.name, id=lic.spdx_id)
193+
194+
component.licenses.add(cdx_license)
195+
if component.evidence:
196+
component.evidence.licenses.add(cdx_license.bom_ref)
197+
100198
self._bom.components.add(component)
101199

102200
def dump_to_file(self, outfile: str) -> bool:

dfetch/util/license.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
"""*Dfetch* uses *Infer-License* to guess licenses from files."""
22

3-
import os
43
from dataclasses import dataclass
4+
from os import PathLike
55
from typing import Optional, Union
66

77
import infer_license
@@ -13,7 +13,7 @@ class License:
1313
"""Class to hold license information."""
1414

1515
name: str # SPDX Full name
16-
shortname: str # SPDX Identifier
16+
spdx_id: str # SPDX Identifier
1717
trove_classifier: Optional[str]
1818
probability: float
1919

@@ -24,14 +24,14 @@ def from_inferred(
2424
"""Create License from an InferredLicense."""
2525
return License(
2626
name=inferred_license.name,
27-
shortname=inferred_license.shortname,
27+
spdx_id=inferred_license.shortname,
2828
trove_classifier=inferred_license.trove_classifier,
2929
probability=probability,
3030
)
3131

3232

3333
def guess_license_in_file(
34-
filename: Union[str, "os.PathLike[str]"],
34+
filename: Union[str, PathLike[str]],
3535
) -> Optional[License]:
3636
"""Guess license from file."""
3737
try:

0 commit comments

Comments
 (0)