Skip to content

Commit

Permalink
Add support for CycloneDX 1.6 outputs and inputs (#1165)
Browse files Browse the repository at this point in the history
Signed-off-by: tdruez <[email protected]>
  • Loading branch information
tdruez authored Apr 16, 2024
1 parent 5b52ce9 commit 9aef059
Show file tree
Hide file tree
Showing 10 changed files with 71 additions and 10 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ Changelog
v34.4.0 (unreleased)
--------------------

- Add support for CycloneDX 1.6 outputs and inputs.
Also, the CycloneDX outputs can be downloaded as 1.6, 1.5, and 1.4 spec versions.

v34.3.0 (2024-04-10)
--------------------

Expand Down
6 changes: 5 additions & 1 deletion scanpipe/api/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,8 @@ def results_download(self, request, *args, **kwargs):
"""Return the results in the provided `output_format` as an attachment."""
project = self.get_object()
format = request.query_params.get("output_format", "json")
version = request.query_params.get("version")
output_kwargs = {}

if format == "json":
return project_results_json_response(project, as_attachment=True)
Expand All @@ -147,7 +149,9 @@ def results_download(self, request, *args, **kwargs):
elif format == "spdx":
output_file = output.to_spdx(project)
elif format == "cyclonedx":
output_file = output.to_cyclonedx(project)
if version:
output_kwargs["version"] = version
output_file = output.to_cyclonedx(project, **output_kwargs)
elif format == "attribution":
output_file = output.to_attribution(project)
else:
Expand Down
3 changes: 2 additions & 1 deletion scanpipe/pipes/output.py
Original file line number Diff line number Diff line change
Expand Up @@ -712,12 +712,13 @@ def sort_bom_with_schema_ordering(bom_as_dict, schema_version):
return json.dumps(ordered_dict, indent=2)


def to_cyclonedx(project, schema_version=SchemaVersion.V1_5):
def to_cyclonedx(project, version="1.6"):
"""
Generate output for the provided ``project`` in CycloneDX BOM format.
The output file is created in the ``project`` "output/" directory.
Return the path of the generated output file.
"""
schema_version = SchemaVersion.from_version(version)
output_file = project.get_output_file_path("results", "cdx.json")

bom = get_cyclonedx_bom(project)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,14 @@
<a href="{% url 'project_results' project.slug 'spdx' %}" class="dropdown-item">
<strong>SPDX</strong>
</a>
<a href="{% url 'project_results' project.slug 'cyclonedx' %}" class="dropdown-item">
<div class="dropdown-item">
<strong>CycloneDX</strong>
</a>
<div class="has-text-weight-semibold">
<a href="{% url 'project_results' project.slug 'cyclonedx' '1.6' %}" class="tag is-link is-light mr-1">1.6</a>
<a href="{% url 'project_results' project.slug 'cyclonedx' '1.5' %}" class="tag is-link is-light mr-1">1.5</a>
<a href="{% url 'project_results' project.slug 'cyclonedx' '1.4' %}" class="tag is-link is-light">1.4</a>
</div>
</div>
<a href="{% url 'project_results' project.slug 'attribution' %}" class="dropdown-item">
<strong>Attribution</strong>
</a>
Expand Down
26 changes: 23 additions & 3 deletions scanpipe/templates/scanpipe/includes/project_downloads.html
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,29 @@
<a class="tag is-success is-medium" href="{% url 'project_results' project.slug 'spdx' %}">
SPDX <span class="icon ml-1"><i class="fa-solid fa-download"></i></span>
</a>
<a class="tag is-success is-medium" href="{% url 'project_results' project.slug 'cyclonedx' %}">
CycloneDX <span class="icon ml-1"><i class="fa-solid fa-download"></i></span>
</a>
<div class="dropdown is-hoverable">
<div class="dropdown-trigger">
<button class="button tag is-success is-medium" aria-haspopup="true" aria-controls="dropdown-menu-cyclonedx">
<span>CycloneDX</span>
<span class="icon">
<i class="fa-solid fa-download" aria-hidden="true"></i>
</span>
</button>
</div>
<div class="dropdown-menu" id="dropdown-menu-cyclonedx" role="menu">
<div class="dropdown-content">
<a href="{% url 'project_results' project.slug 'cyclonedx' '1.6' %}" class="dropdown-item has-text-weight-semibold">
Spec 1.6 <span class="tag is-link is-light ml-1">Latest</span>
</a>
<a href="{% url 'project_results' project.slug 'cyclonedx' '1.5' %}" class="dropdown-item">
Spec 1.5
</a>
<a href="{% url 'project_results' project.slug 'cyclonedx' '1.4' %}" class="dropdown-item">
Spec 1.4
</a>
</div>
</div>
</div>
<a class="tag is-success is-medium" href="{% url 'project_results' project.slug 'attribution' %}">
Attribution <span class="icon ml-1"><i class="fa-solid fa-download"></i></span>
</a>
Expand Down
4 changes: 2 additions & 2 deletions scanpipe/tests/data/cyclonedx/asgiref-3.3.0.cdx.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"$schema": "http://cyclonedx.org/schema/bom-1.5.schema.json",
"$schema": "http://cyclonedx.org/schema/bom-1.6.schema.json",
"bomFormat": "CycloneDX",
"specVersion": "1.5",
"specVersion": "1.6",
"serialNumber": "urn:uuid:b74fe5df-e965-415e-ba65-f38421a0695d",
"version": 1,
"metadata": {
Expand Down
7 changes: 7 additions & 0 deletions scanpipe/tests/pipes/test_output.py
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,13 @@ def test_scanpipe_pipes_outputs_to_cyclonedx(self, regen=FIXTURES_REGEN):

self.assertJSONEqual(results, expected_location.read_text())

output_file = output.to_cyclonedx(project=project, version="1.5")
results_json = json.loads(output_file.read_text())
self.assertEqual(
"http://cyclonedx.org/schema/bom-1.5.schema.json", results_json["$schema"]
)
self.assertEqual("1.5", results_json["specVersion"])

def test_scanpipe_pipes_outputs_to_spdx(self):
fixtures = self.data_path / "asgiref-3.3.0_fixtures.json"
call_command("loaddata", fixtures, **{"verbosity": 0})
Expand Down
12 changes: 12 additions & 0 deletions scanpipe/tests/test_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -540,6 +540,18 @@ def test_scanpipe_api_project_action_results_download_output_formats(self):
results = json.loads(response_value)
self.assertIn("$schema", sorted(results.keys()))

data = {
"output_format": "cyclonedx",
"version": "1.5",
}
response = self.csrf_client.get(url, data=data)
response_value = response.getvalue()
results = json.loads(response_value)
self.assertEqual(
"http://cyclonedx.org/schema/bom-1.5.schema.json", results["$schema"]
)
self.assertEqual("1.5", results["specVersion"])

data = {"output_format": "xlsx"}
response = self.csrf_client.get(url, data=data)
expected = [
Expand Down
5 changes: 5 additions & 0 deletions scanpipe/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,11 @@
views.pipeline_help_view,
name="pipeline_help",
),
path(
"project/<slug:slug>/results/<str:format>/<str:version>/",
views.ProjectResultsView.as_view(),
name="project_results",
),
path(
"project/<slug:slug>/results/<str:format>/",
views.ProjectResultsView.as_view(),
Expand Down
6 changes: 5 additions & 1 deletion scanpipe/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -1281,6 +1281,8 @@ def get(self, request, *args, **kwargs):
self.object = self.get_object()
project = self.object
format = self.kwargs["format"]
version = self.kwargs.get("version")
output_kwargs = {}

if format == "json":
return project_results_json_response(project, as_attachment=True)
Expand All @@ -1289,7 +1291,9 @@ def get(self, request, *args, **kwargs):
elif format == "spdx":
output_file = output.to_spdx(project)
elif format == "cyclonedx":
output_file = output.to_cyclonedx(project)
if version:
output_kwargs["version"] = version
output_file = output.to_cyclonedx(project, **output_kwargs)
elif format == "attribution":
output_file = output.to_attribution(project)
else:
Expand Down

0 comments on commit 9aef059

Please sign in to comment.