Skip to content

Commit d925c94

Browse files
authored
Merge pull request #326 from bact/release-4.1.1
Fix FSCT3Checker failed to report components without required info
2 parents f1b6ca3 + dab7529 commit d925c94

File tree

9 files changed

+127
-34
lines changed

9 files changed

+127
-34
lines changed

CHANGELOG.md

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,28 @@ All notable changes to this project will be documented in this file.
1111
The format is based on [Keep a Changelog][keepachangelog]
1212
and this project adheres to [Semantic Versioning][semver].
1313

14+
## [4.1.1] - 2025-11-18
15+
16+
This version primarily focused on improving the HTML output.
17+
18+
The HTML output is now organized into four distinct div blocks,
19+
each with a specific CSS class for easy styling and targeting:
20+
21+
- Errors (parsing, etc.): `<div class="conformance-err">`
22+
- Conformance results: `<div class="conformance-res">`
23+
- Components missing required information: `<div class="conformance-mis">`
24+
- Detailed validation information: `<div class="conformance-val">`
25+
26+
Note: Python 3.9 support will be dropped in the next major release.
27+
This is likely the final version supporting it.
28+
29+
### Fixed
30+
31+
- Fix FSCT3Checker that do not report components without required information
32+
([#326][])
33+
34+
[#326]: https://github.com/spdx/ntia-conformance-checker/pull/326
35+
1436
## [4.1.0] - 2025-11-17
1537

1638
This version primarily focused on improving the HTML output.
@@ -159,6 +181,7 @@ Thanks to @goneall, @licquia, and @kestewart for mentoring @linynjosh.
159181
[#1]: https://github.com/spdx/ntia-conformance-checker/pull/1
160182
[keepachangelog]: https://keepachangelog.com/en/1.1.0/
161183
[semver]: https://semver.org/spec/v2.0.0.html
184+
[4.1.1]: https://github.com/spdx/ntia-conformance-checker/releases/tag/v4.1.1
162185
[4.1.0]: https://github.com/spdx/ntia-conformance-checker/releases/tag/v4.1.0
163186
[4.0.0]: https://github.com/spdx/ntia-conformance-checker/releases/tag/v4.0.0
164187
[3.2.0]: https://github.com/spdx/ntia-conformance-checker/releases/tag/v3.2.0

CITATION.cff

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,5 +49,5 @@ keywords:
4949
- NTIA
5050
- CISA
5151
license: Apache-2.0
52-
version: 4.1.0
53-
date-released: '2025-11-17'
52+
version: 4.1.1
53+
date-released: '2025-11-18'

README.md

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@ This tool determines whether a [SPDX](https://spdx.dev/) software bill of
1111
materials (SBOM) document contains informational items as required by a
1212
certain specification.
1313

14+
A web-based version of the tool is available (no installation needed) at:
15+
<https://tools.spdx.org/app/ntia_checker/>
16+
1417
## Conformance
1518

1619
Currently, the supported specifications are:
@@ -77,7 +80,7 @@ if you work with multiple Python versions.
7780
it lets you keep a project's dependencies in a single environment
7881
or create separate environments for testing with different Python versions.
7982

80-
## CLI Usage
83+
## CLI usage
8184

8285
```text
8386
usage: sbomcheck [OPTIONS] FILE
@@ -143,7 +146,7 @@ Use `-h` for help:
143146
sbomcheck -h
144147
```
145148

146-
## Usage as a Library
149+
## Usage as a library
147150

148151
`ntia-conformance-checker` can also be imported as a library. For example:
149152

@@ -164,13 +167,23 @@ Specific properties and methods for a particular specification can be found
164167
at the checker for that specification. For example, `NTIAChecker` class
165168
at [`ntia_checker.py`](ntia_conformance_checker/ntia_checker.py).
166169

167-
## Online Usage
170+
## Online usage
168171

169172
With the SPDX Online Tool, you can check the SBOM conformance without the need
170173
to install the Python package.
171174

172175
Go to this page: <https://tools.spdx.org/app/ntia_checker/>.
173176

177+
## HTML output
178+
179+
The HTML output is organized into four distinct div blocks,
180+
each with a specific CSS class for easy styling and targeting:
181+
182+
- Errors (parsing, etc.): `<div class="conformance-err">`
183+
- Conformance results: `<div class="conformance-res">`
184+
- Components missing required information: `<div class="conformance-mis">`
185+
- Detailed validation information: `<div class="conformance-val">`
186+
174187
## History
175188

176189
- The project is the result of an initial [Google Summer of Code (GSoC)][gsoc]

ntia_conformance_checker/fsct_checker.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ class FSCT3Checker(BaseChecker):
2525
https://www.cisa.gov/resources-tools/resources/framing-software-component-transparency-2024
2626
"""
2727

28-
MIN_COMPONENTS = [
28+
MIN_ELEMENTS = [
2929
"name",
3030
"version",
3131
"identifier",

ntia_conformance_checker/ntia_checker.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,6 @@ def __init__(
4646

4747
if self.doc:
4848
self.compliant = self.check_compliance()
49-
5049
# for backward compatibility
5150
self.ntia_minimum_elements_compliant = self.compliant
5251

ntia_conformance_checker/report.py

Lines changed: 23 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -190,33 +190,33 @@ def report_html(
190190

191191
# Parsing error
192192
if rc.parsing_error:
193+
report.append("<div class='conformance-err'>")
193194
report.append(
194-
"<p class='conformance-err'>"
195-
"The document couldn't be parsed; check couldn't be performed."
195+
"<p class='conformance-err-label'>"
196+
"The document couldn't be parsed; check couldn't be performed.<br />"
197+
"The following parsing errors were raised:"
196198
"</p>"
197199
)
198-
if rc.parsing_error:
199-
report.append(
200-
"<p class='conformance-err-details-label'>"
201-
"The following parsing errors were raised:"
202-
"</p>"
203-
)
204-
report.append("<ul class='conformance-err-details'>")
205-
for err in rc.parsing_error:
206-
report.append(f"<li>{err}</li>")
207-
report.append("</ul>")
200+
report.append("<ul class='conformance-err-list'>")
201+
for err in rc.parsing_error:
202+
report.append(f"<li>{err}</li>")
203+
report.append("</ul>")
204+
report.append("</div>")
208205
return "\n".join(report)
209206

210207
# Unsupported compliance standard
211208
if rc.compliance_standard not in SUPPORTED_COMPLIANCE_STANDARDS:
212209
report.append(
213-
"<p class='conformance-err'>"
210+
"<div class='conformance-err'>"
211+
"<p class='conformance-err-label'>"
214212
f"Unsupported compliance standard {rc.compliance_standard!r}"
215213
"</p>"
214+
"</div>"
216215
)
217216
return "\n".join(report)
218217

219218
# Compliance results
219+
report.append("<div class='conformance-res'>")
220220
report.append(
221221
"<h2 class='conformance-res-title'>"
222222
f"{SUPPORTED_COMPLIANCE_STANDARDS_DESC[rc.compliance_standard]}"
@@ -240,36 +240,38 @@ def report_html(
240240
report.append("</tbody>")
241241
report.append("</table>")
242242

243+
report.append("</div>") # End of conformance-res
244+
243245
# Components without required information
244246
if rc.components_without_info:
247+
report.append("<div class='conformance-mis'>")
245248
report.append(
246-
"<p class='conformance-missing-label'>"
249+
"<p class='conformance-mis-label'>"
247250
"Missing required information in these components:"
248251
"</p>"
249252
)
250-
report.append("<ul class='conformance-missing-list'>")
253+
report.append("<ul class='conformance-mis-list'>")
251254
for component_name, components in rc.components_without_info:
252255
report.append(
253256
f"<li>{component_name} ({len(components)}): "
254257
f"{', '.join(components)}</li>"
255258
)
256259
report.append("</ul>")
260+
report.append("</div>")
257261

258262
# Validation messages
259263
if rc.validation_messages:
260-
report.append(
261-
"<p class='conformance-val'>"
262-
"The document is not valid according to the SBOM specification"
263-
f' ("{rc.sbom_spec}").'
264-
"</p>"
265-
)
264+
report.append("<div class='conformance-val'>")
266265
report.append(
267266
"<p class='conformance-val-label'>"
267+
"The document is not valid according to the SBOM specification"
268+
f' ("{rc.sbom_spec}").<br />'
268269
"The following violations were found:"
269270
"</p>"
270271
)
271272
report.append(
272273
get_validation_messages_html(rc.validation_messages, verbose=verbose)
273274
)
275+
report.append("</div>")
274276

275277
return "\n".join(report)

ntia_conformance_checker/spdx3_utils.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ def validate_spdx3_data(
8181
if not isinstance(root_element, (spdx3.Bom, spdx3.software_Sbom)):
8282
error_msg = (
8383
"The root element must be of type Bom or software_Sbom. "
84-
f"Found: {type(root_element)}"
84+
f"Found: r{type(root_element)}"
8585
)
8686
root_element_id = getattr(root_element, "spdxId", None)
8787
context = ValidationContext(parent_id=doc_id, spdx_id=root_element_id)

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ build-backend = "setuptools.build_meta"
88

99
[project]
1010
name = "ntia_conformance_checker"
11-
version = "4.1.0"
11+
version = "4.1.1"
1212
authors = [
1313
{ name = "Josh Lin", email = "[email protected]" },
1414
{ name = "John Speed Meyers", email = "[email protected]" },

tests/test_checker.py

Lines changed: 60 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# SPDX-FileCopyrightText: 2024 SPDX contributors
1+
# SPDX-FileCopyrightText: 2024-2025 SPDX contributors
22
# SPDX-FileType: SOURCE
33
# SPDX-License-Identifier: Apache-2.0
44

@@ -429,6 +429,7 @@ def test_sbomchecker_output_html():
429429

430430
got = sbom.output_html()
431431
expected = (
432+
"<div class='conformance-res'>\n"
432433
"<h2 class='conformance-res-title'>"
433434
"2021 NTIA SBOM Minimum Elements Conformance Results"
434435
"</h2>\n"
@@ -453,11 +454,66 @@ def test_sbomchecker_output_html():
453454
"<td class='conformance-res-tab-v'>True</td></tr>\n"
454455
"</tbody>\n"
455456
"</table>\n"
456-
"<p class='conformance-missing-label'>"
457+
"</div>\n"
458+
"<div class='conformance-mis'>\n"
459+
"<p class='conformance-mis-label'>"
457460
"Missing required information in these components:</p>\n"
458-
"<ul class='conformance-missing-list'>\n"
461+
"<ul class='conformance-mis-list'>\n"
459462
"<li>supplier (3): xyz, curl, openssl</li>\n"
460-
"</ul>"
463+
"</ul>\n"
464+
"</div>"
465+
)
466+
467+
assert got == expected
468+
469+
470+
def test_sbomchecker_fsct3_output_html():
471+
filepath = os.path.join(
472+
os.path.dirname(__file__), "data", "other_tests", "SPDXSBOMExample.spdx.yml"
473+
)
474+
sbom = sbom_checker.SbomChecker(filepath, compliance="fsct3-min")
475+
476+
got = sbom.output_html()
477+
expected = (
478+
"<div class='conformance-res'>\n"
479+
"<h2 class='conformance-res-title'>"
480+
"2024 CISA Framing Software Component Transparency"
481+
" (minimum expectation) Conformance Results"
482+
"</h2>\n"
483+
"<h3 class='conformance-res-status'>Conformant: False</h3>\n"
484+
"<table class='conformance-res-tab'>\n"
485+
"<thead><tr><th>Requirement</th>"
486+
"<th>Conformant</th></tr></thead>\n"
487+
"<tbody>\n"
488+
"<tr><td class='conformance-res-tab-r'>All component names provided?</td>"
489+
"<td class='conformance-res-tab-v'>True</td></tr>\n"
490+
"<tr><td class='conformance-res-tab-r'>All component versions provided?</td>"
491+
"<td class='conformance-res-tab-v'>True</td></tr>\n"
492+
"<tr><td class='conformance-res-tab-r'>All component identifiers provided?</td>"
493+
"<td class='conformance-res-tab-v'>True</td></tr>\n"
494+
"<tr><td class='conformance-res-tab-r'>All component suppliers provided?</td>"
495+
"<td class='conformance-res-tab-v'>False</td></tr>\n"
496+
"<tr><td class='conformance-res-tab-r'>All component concluded license provided?</td>"
497+
"<td class='conformance-res-tab-v'>False</td></tr>\n"
498+
"<tr><td class='conformance-res-tab-r'>All component copyright notice provided?</td>"
499+
"<td class='conformance-res-tab-v'>True</td></tr>\n"
500+
"<tr><td class='conformance-res-tab-r'>SBOM author name provided?</td>"
501+
"<td class='conformance-res-tab-v'>True</td></tr>\n"
502+
"<tr><td class='conformance-res-tab-r'>SBOM creation timestamp provided?</td>"
503+
"<td class='conformance-res-tab-v'>True</td></tr>\n"
504+
"<tr><td class='conformance-res-tab-r'>Dependency relationships provided?</td>"
505+
"<td class='conformance-res-tab-v'>True</td></tr>\n"
506+
"</tbody>\n"
507+
"</table>\n"
508+
"</div>\n"
509+
"<div class='conformance-mis'>\n"
510+
"<p class='conformance-mis-label'>"
511+
"Missing required information in these components:</p>\n"
512+
"<ul class='conformance-mis-list'>\n"
513+
"<li>supplier (3): xyz, curl, openssl</li>\n"
514+
"<li>concluded_license (3): xyz, curl, openssl</li>\n"
515+
"</ul>\n"
516+
"</div>"
461517
)
462518

463519
assert got == expected

0 commit comments

Comments
 (0)