-
Notifications
You must be signed in to change notification settings - Fork 89
add CycloneDX attestation export endpoint #759
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
203b952
9cd5986
1dc11ef
df0e59a
9bfc941
0f27de6
d485b67
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,30 +1,30 @@ | ||
| # name: Test-e2e | ||
| # on: [push, pull_request] | ||
| # jobs: | ||
| # build: | ||
| # name: Test-e2e | ||
| # runs-on: ubuntu-latest | ||
| # timeout-minutes: 10 | ||
| # steps: | ||
| # - name: Check out code | ||
| # uses: actions/checkout@v4 | ||
| # - uses: actions/setup-python@v4 | ||
| # with: | ||
| # python-version: '3.11.4' | ||
| # cache: 'pip' | ||
| # - uses: actions/setup-node@v3 | ||
| # with: | ||
| # cache: 'yarn' | ||
| # node-version: 'v20.12.1' | ||
| # - name: Install dependencies | ||
| # run: | | ||
| # sudo apt-get update | ||
| # sudo apt-get install -y python3-setuptools python3-pip python3-virtualenv chromium-browser libgbm1 | ||
| # make install | ||
| # - name: DB setup | ||
| # run: | | ||
| # make migrate-upgrade | ||
| # python cre.py --upstream_sync | ||
| # # - name: Run app and e2e tests | ||
| # run: | | ||
| # make e2e | ||
| name: Test-e2e | ||
| on: [push, pull_request] | ||
| jobs: | ||
| build: | ||
| name: Test-e2e | ||
| runs-on: ubuntu-latest | ||
| timeout-minutes: 10 | ||
| steps: | ||
| - name: Check out code | ||
| uses: actions/checkout@v4 | ||
| - uses: actions/setup-python@v4 | ||
| with: | ||
| python-version: '3.11.4' | ||
| cache: 'pip' | ||
| - uses: actions/setup-node@v3 | ||
| with: | ||
| cache: 'yarn' | ||
| node-version: 'v20.12.1' | ||
| - name: Install dependencies | ||
| run: | | ||
| sudo apt-get update | ||
| sudo apt-get install -y python3-setuptools python3-pip python3-virtualenv chromium-browser libgbm1 | ||
| make install | ||
| - name: DB setup | ||
| run: | | ||
| make migrate-upgrade | ||
| python cre.py --upstream_sync | ||
| # - name: Run app and e2e tests | ||
| run: | | ||
| make e2e |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2,13 +2,15 @@ | |
|
|
||
| # silence mypy for the routes file | ||
| import csv | ||
| from datetime import datetime, timezone | ||
| from functools import wraps | ||
| import json | ||
| import logging | ||
| import os | ||
| import io | ||
| import pathlib | ||
| import urllib.parse | ||
| import uuid | ||
| from alive_progress import alive_bar | ||
| from typing import Any | ||
| from application.utils import oscal_utils, redis | ||
|
|
@@ -66,6 +68,68 @@ class SupportedFormats(Enum): | |
| JSON = "json" | ||
| YAML = "yaml" | ||
| OSCAL = "oscal" | ||
| CycloneDX = "cyclonedx" | ||
|
|
||
|
|
||
| def _document_attestation_url(document: defs.Document) -> str: | ||
| if document.doctype == defs.Credoctypes.CRE and document.id: | ||
| return f"https://www.opencre.org/cre/{document.id}" | ||
| if getattr(document, "hyperlink", ""): | ||
| return document.hyperlink | ||
| identifier = str(document.id) if getattr(document, "id", None) else document.name | ||
| return f"https://www.opencre.org/node/{document.doctype.value.lower()}/{urllib.parse.quote(identifier)}" | ||
|
|
||
|
|
||
| def _document_to_cyclonedx_component(document: defs.Document) -> dict[str, Any]: | ||
| component = { | ||
| "type": "data", | ||
| "name": document.name, | ||
| "bom-ref": f"{document.doctype.value}:{document.id or document.name}", | ||
| "properties": [ | ||
| {"name": "opencre:doctype", "value": document.doctype.value}, | ||
| {"name": "opencre:id", "value": str(document.id or "")}, | ||
| ], | ||
| "externalReferences": [ | ||
| { | ||
| "type": "attestation", | ||
| "url": _document_attestation_url(document), | ||
| "comment": "OpenCRE source attestation", | ||
| } | ||
| ], | ||
| } | ||
| if document.description: | ||
| component["description"] = document.description | ||
| if document.tags: | ||
| component["properties"].append( | ||
| { | ||
| "name": "opencre:tags", | ||
| "value": ",".join(tag for tag in document.tags if tag), | ||
| } | ||
| ) | ||
| return component | ||
|
|
||
|
|
||
| def _documents_to_cyclonedx(documents: list[defs.Document]) -> dict[str, Any]: | ||
| return { | ||
| "bomFormat": "CycloneDX", | ||
| "specVersion": "1.6", | ||
| "serialNumber": f"urn:uuid:{uuid.uuid4()}", | ||
| "version": 1, | ||
| "metadata": { | ||
| "timestamp": datetime.now(timezone.utc) | ||
| .replace(microsecond=0) | ||
| .isoformat() | ||
| .replace("+00:00", "Z"), | ||
| "component": { | ||
| "type": "application", | ||
| "name": "OpenCRE", | ||
| "externalReferences": [ | ||
| {"type": "website", "url": "https://www.opencre.org/"} | ||
| ], | ||
| }, | ||
| }, | ||
| "components": [_document_to_cyclonedx_component(doc) for doc in documents], | ||
| } | ||
|
|
||
|
|
||
| def extend_cre_with_tag_links( | ||
|
|
@@ -137,6 +201,10 @@ def find_cre(creid: str = None, crename: str = None) -> Any: # refer | |
|
|
||
| elif opt_format == SupportedFormats.OSCAL.value: | ||
| result = {"data": json.loads(oscal_utils.document_to_oscal(cre))} | ||
| elif opt_format == SupportedFormats.CycloneDX.value: | ||
| result = _documents_to_cyclonedx([cre]) | ||
| elif opt_format is not None: | ||
| abort(400, "Unsupported format") | ||
|
|
||
| return jsonify(result) | ||
| abort(404, "CRE does not exist") | ||
|
|
@@ -227,6 +295,8 @@ def find_node_by_name( | |
|
|
||
| elif opt_format == SupportedFormats.OSCAL.value: | ||
| return jsonify(json.loads(oscal_utils.list_to_oscal(nodes))) | ||
| elif opt_format == SupportedFormats.CycloneDX.value: | ||
| return jsonify(_documents_to_cyclonedx(nodes)) | ||
|
|
||
| # if opt_osib: | ||
| # result["osib"] = odefs.cre2osib(nodes).todict() | ||
|
|
@@ -263,6 +333,8 @@ def find_document_by_tag() -> Any: | |
| return write_csv(docs=docs).getvalue().encode("utf-8") | ||
| elif opt_format == SupportedFormats.OSCAL.value: | ||
| return jsonify(json.loads(oscal_utils.list_to_oscal(documents))) | ||
| elif opt_format == SupportedFormats.CycloneDX.value: | ||
| return jsonify(_documents_to_cyclonedx(documents)) | ||
|
|
||
| return jsonify(result) | ||
| abort(404, "Tag does not exist") | ||
|
|
@@ -423,6 +495,8 @@ def text_search() -> Any: | |
| return write_csv(docs=docs).getvalue().encode("utf-8") | ||
| elif opt_format == SupportedFormats.OSCAL.value: | ||
| return jsonify(json.loads(oscal_utils.list_to_oscal(documents))) | ||
| elif opt_format == SupportedFormats.CycloneDX.value: | ||
| return jsonify(_documents_to_cyclonedx(documents)) | ||
|
Comment on lines
496
to
+499
|
||
|
|
||
| res = [doc.todict() for doc in documents] | ||
| return jsonify(res) | ||
|
|
@@ -457,6 +531,8 @@ def find_root_cres() -> Any: | |
| return write_csv(docs=docs).getvalue().encode("utf-8") | ||
| elif opt_format == SupportedFormats.OSCAL.value: | ||
| return jsonify(json.loads(oscal_utils.list_to_oscal(documents))) | ||
| elif opt_format == SupportedFormats.CycloneDX.value: | ||
| return jsonify(_documents_to_cyclonedx(documents)) | ||
|
|
||
| return jsonify(result) | ||
| abort(404, "No root CREs") | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.