Skip to content

Commit f82fb0d

Browse files
committed
Fix sbom feature test
1 parent 4494a19 commit f82fb0d

4 files changed

Lines changed: 192 additions & 83 deletions

File tree

dfetch/manifest/manifest.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,9 @@ def __init__(
123123
self.__version: str = str(manifest.get("version", self.CURRENT_VERSION))
124124
self.__text: str = text if text else ""
125125
self.__path: str = str(path) if path else ""
126+
self.__relative_path: str = (
127+
os.path.relpath(self.__path, os.getcwd()) if self.__path else ""
128+
)
126129

127130
self._remotes, default_remotes = self._determine_remotes(
128131
manifest.get("remotes", [])
@@ -248,6 +251,11 @@ def path(self) -> str:
248251
"""Path to the manifest file."""
249252
return self.__path
250253

254+
@property
255+
def relative_path(self) -> str:
256+
"""Path to the manifest file relative to the current working directory."""
257+
return self.__relative_path
258+
251259
@property
252260
def version(self) -> str:
253261
"""Version of the manifest file."""

dfetch/reporting/sbom_reporter.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,7 @@ def add_project(
131131
evidence=ComponentEvidence(
132132
occurrences=[
133133
Occurrence(
134-
location=self._manifest.path,
134+
location=self._manifest.relative_path,
135135
line=location.line_number,
136136
offset=location.start,
137137
)

features/report-sbom.feature

Lines changed: 180 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
@wip
12
Feature: Create an CycloneDX sbom
23

34
*Dfetch* can generate a software Bill-of-Materials (SBOM).
@@ -19,78 +20,75 @@ Feature: Create an CycloneDX sbom
1920
url: https://github.com/cpputest/cpputest
2021
tag: v3.4
2122
src: 'include/CppUTest'
22-
2323
"""
2424
And all projects are updated
2525
When I run "dfetch report -t sbom"
2626
Then the 'report.json' file contains
2727
"""
28-
{
29-
"$schema": "http://cyclonedx.org/schema/bom-1.6.schema.json",
30-
"bomFormat": "CycloneDX",
31-
"specVersion": "1.6",
32-
"serialNumber": "urn:uuid:3ce78767-c202-4beb-935e-67f539cf3a58",
33-
"version": 1,
34-
"dependencies": [
28+
{
29+
"components": [
3530
{
36-
"ref": "BomRef.7805091949677974.3172811758515278"
37-
}
38-
],
39-
"metadata": {
40-
"timestamp": "2025-10-03T20:56:03.645362+00:00",
41-
"tools": [
42-
{
43-
"vendor": "dfetch-org",
44-
"name": "dfetch",
45-
"version": "0.10.0"
46-
},
47-
{
48-
"vendor": "CycloneDX",
49-
"name": "cyclonedx-python-lib",
50-
"version": "11.1.0",
51-
"externalReferences": [
52-
{
53-
"url": "https://pypi.org/project/cyclonedx-python-lib/",
54-
"type": "distribution"
55-
},
56-
{
57-
"url": "https://github.com/CycloneDX/cyclonedx-python-lib/#readme",
58-
"type": "website"
59-
},
31+
"bom-ref": "cpputest-v3.4",
32+
"evidence": {
33+
"identity": [
6034
{
61-
"url": "https://github.com/CycloneDX/cyclonedx-python-lib/actions",
62-
"type": "build-system"
35+
"concludedValue": "cpputest",
36+
"field": "name",
37+
"methods": [
38+
{
39+
"confidence": 0.4,
40+
"technique": "manifest-analysis",
41+
"value": "Name as used for project in dfetch.yaml"
42+
}
43+
],
44+
"tools": [
45+
"dfetch-0.10.0"
46+
]
6347
},
6448
{
65-
"url": "https://github.com/CycloneDX/cyclonedx-python-lib/blob/main/LICENSE",
66-
"type": "license"
49+
"concludedValue": "pkg:github/cpputest/cpputest@v3.4#include/CppUTest",
50+
"field": "purl",
51+
"methods": [
52+
{
53+
"confidence": 0.4,
54+
"technique": "manifest-analysis",
55+
"value": "Determined from https://github.com/cpputest/cpputest as used for the project cpputest in dfetch.yaml"
56+
}
57+
],
58+
"tools": [
59+
"dfetch-0.10.0"
60+
]
6761
},
6862
{
69-
"url": "https://github.com/CycloneDX/cyclonedx-python-lib/blob/main/CHANGELOG.md",
70-
"type": "release-notes"
71-
},
72-
{
73-
"url": "https://cyclonedx-python-library.readthedocs.io/",
74-
"type": "documentation"
75-
},
63+
"concludedValue": "v3.4",
64+
"field": "version",
65+
"methods": [
66+
{
67+
"confidence": 0.4,
68+
"technique": "manifest-analysis",
69+
"value": "Version as used for project in dfetch.yaml"
70+
}
71+
],
72+
"tools": [
73+
"dfetch-0.10.0"
74+
]
75+
}
76+
],
77+
"licenses": [
7678
{
77-
"url": "https://github.com/CycloneDX/cyclonedx-python-lib/issues",
78-
"type": "issue-tracker"
79-
},
79+
"license": {
80+
"id": "BSD-3-Clause"
81+
}
82+
}
83+
],
84+
"occurrences": [
8085
{
81-
"url": "https://github.com/CycloneDX/cyclonedx-python-lib",
82-
"type": "vcs"
86+
"line": 5,
87+
"location": "dfetch.yaml",
88+
"offset": 13
8389
}
8490
]
85-
}
86-
]
87-
},
88-
"components": [
89-
{
90-
"type": "library",
91-
"bom-ref": "BomRef.7805091949677974.3172811758515278",
92-
"name": "cpputest",
93-
"version": "v3.4",
91+
},
9492
"externalReferences": [
9593
{
9694
"type": "vcs",
@@ -99,11 +97,133 @@ Feature: Create an CycloneDX sbom
9997
],
10098
"licenses": [
10199
{
102-
"expression": "BSD 3-Clause \"New\" or \"Revised\" License"
100+
"license": {
101+
"id": "BSD-3-Clause"
102+
}
103103
}
104104
],
105-
"purl": "pkg:github/cpputest/cpputest@v3.4#include/CppUTest"
105+
"name": "cpputest",
106+
"purl": "pkg:github/cpputest/cpputest@v3.4#include/CppUTest",
107+
"type": "library",
108+
"version": "v3.4"
109+
}
110+
],
111+
"dependencies": [
112+
{
113+
"ref": "cpputest-v3.4"
106114
}
107-
]
115+
],
116+
"metadata": {
117+
"timestamp": "2025-10-10T18:28:32.074803+00:00",
118+
"tools": {
119+
"components": [
120+
{
121+
"bom-ref": "dfetch-0.10.0",
122+
"externalReferences": [
123+
{
124+
"type": "build-system",
125+
"url": "https://github.com/dfetch-org/dfetch/actions"
126+
},
127+
{
128+
"type": "distribution",
129+
"url": "https://pypi.org/project/dfetch/"
130+
},
131+
{
132+
"type": "documentation",
133+
"url": "https://dfetch.readthedocs.io/"
134+
},
135+
{
136+
"type": "issue-tracker",
137+
"url": "https://github.com/dfetch-org/dfetch/issues"
138+
},
139+
{
140+
"type": "license",
141+
"url": "https://github.com/dfetch-org/dfetch/blob/main/LICENSE"
142+
},
143+
{
144+
"type": "release-notes",
145+
"url": "https://github.com/dfetch-org/dfetch/blob/main/CHANGELOG.rst"
146+
},
147+
{
148+
"type": "vcs",
149+
"url": "https://github.com/dfetch-org/dfetch"
150+
},
151+
{
152+
"type": "website",
153+
"url": "https://dfetch-org.github.io/"
154+
}
155+
],
156+
"licenses": [
157+
{
158+
"license": {
159+
"acknowledgement": "declared",
160+
"id": "MIT"
161+
}
162+
}
163+
],
164+
"name": "dfetch",
165+
"supplier": {
166+
"name": "dfetch-org"
167+
},
168+
"type": "application",
169+
"version": "0.10.0"
170+
},
171+
{
172+
"description": "Python library for CycloneDX",
173+
"externalReferences": [
174+
{
175+
"type": "build-system",
176+
"url": "https://github.com/CycloneDX/cyclonedx-python-lib/actions"
177+
},
178+
{
179+
"type": "distribution",
180+
"url": "https://pypi.org/project/cyclonedx-python-lib/"
181+
},
182+
{
183+
"type": "documentation",
184+
"url": "https://cyclonedx-python-library.readthedocs.io/"
185+
},
186+
{
187+
"type": "issue-tracker",
188+
"url": "https://github.com/CycloneDX/cyclonedx-python-lib/issues"
189+
},
190+
{
191+
"type": "license",
192+
"url": "https://github.com/CycloneDX/cyclonedx-python-lib/blob/main/LICENSE"
193+
},
194+
{
195+
"type": "release-notes",
196+
"url": "https://github.com/CycloneDX/cyclonedx-python-lib/blob/main/CHANGELOG.md"
197+
},
198+
{
199+
"type": "vcs",
200+
"url": "https://github.com/CycloneDX/cyclonedx-python-lib"
201+
},
202+
{
203+
"type": "website",
204+
"url": "https://github.com/CycloneDX/cyclonedx-python-lib/#readme"
205+
}
206+
],
207+
"group": "CycloneDX",
208+
"licenses": [
209+
{
210+
"license": {
211+
"acknowledgement": "declared",
212+
"id": "Apache-2.0"
213+
}
214+
}
215+
],
216+
"name": "cyclonedx-python-lib",
217+
"type": "library",
218+
"version": "11.1.0"
219+
}
220+
]
221+
}
222+
},
223+
"serialNumber": "urn:uuid:7621038e-3047-4862-99e7-d637ee9458a9",
224+
"version": 1,
225+
"$schema": "http://cyclonedx.org/schema/bom-1.6.schema.json",
226+
"bomFormat": "CycloneDX",
227+
"specVersion": "1.6"
108228
}
109229
"""

features/steps/generic_steps.py

Lines changed: 3 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
import pathlib
1010
import re
1111
from itertools import zip_longest
12-
from typing import Iterable, List, Optional, Pattern, Tuple
12+
from typing import Iterable, List, Optional, Pattern, Tuple, Union
1313

1414
from behave import given, then, when # pylint: disable=no-name-in-module
1515
from behave.runner import Context
@@ -57,38 +57,19 @@ def check_file_exists(path):
5757
assert os.path.isfile(path), f"Expected {path} to exist, but it didn't!"
5858

5959

60-
def check_json(path, content):
61-
"""Check a file."""
60+
def check_json(path: Union[str, os.PathLike], content: str) -> None:
61+
"""Check a JSON file."""
6262

6363
with open(path, "r", encoding="UTF-8") as file_to_check:
6464
actual_json = json.load(file_to_check)
6565
expected_json = json.loads(content)
6666

67-
if "bomFormat" in expected_json:
68-
sort_sbom(expected_json)
69-
if "bomFormat" in actual_json:
70-
sort_sbom(actual_json)
71-
7267
check_content(
7368
json.dumps(expected_json, indent=4, sort_keys=True).splitlines(),
7469
json.dumps(actual_json, indent=4, sort_keys=True).splitlines(),
7570
)
7671

7772

78-
def sort_sbom(sbom):
79-
"""Sort some fields in an sbom."""
80-
81-
for tool in sbom["metadata"]["tools"]:
82-
if "externalReferences" in tool:
83-
tool["externalReferences"] = sorted(
84-
tool["externalReferences"], key=lambda x: x["type"]
85-
)
86-
87-
sbom["metadata"]["tools"] = sorted(
88-
sbom["metadata"]["tools"], key=lambda x: x["name"]
89-
)
90-
91-
9273
def check_content(
9374
expected_content: Iterable[str], actual_content: Iterable[str]
9475
) -> None:

0 commit comments

Comments
 (0)