mirror of
https://github.com/espressif/esp-idf-sbom.git
synced 2025-08-06 15:18:59 +08:00
feat: add a JSON schema for a vulnerability report in JSON format
The esp-idf-sbom tool enables the creation of vulnerability reports in JSON format for easy integration with other tools. Previously, the JSON report did not include versioning or a schema. This update introduces versioning for the JSON report and adds a JSON schema to validate the generated report. Signed-off-by: Frantisek Hrbata <frantisek.hrbata@espressif.com>
This commit is contained in:
@ -13,6 +13,7 @@ from rich.table import Table
|
||||
from esp_idf_sbom import __version__
|
||||
from esp_idf_sbom.libsbom import log, nvd, utils
|
||||
|
||||
REPORT_VERSION = 1
|
||||
empty_record = {
|
||||
'vulnerable': '',
|
||||
'pkg_name': '',
|
||||
@ -53,6 +54,7 @@ def show(records: List[Dict[str,str]],
|
||||
|
||||
# Get summary
|
||||
summary: Dict[str, Any] = {
|
||||
'version': REPORT_VERSION,
|
||||
'date': datetime.datetime.utcnow().strftime('%Y-%m-%dT%H:%M:%SZ'),
|
||||
'database': database,
|
||||
'tool': {
|
||||
|
241
report_schema.json
Normal file
241
report_schema.json
Normal file
@ -0,0 +1,241 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"$id": "https://github.com/espressif/esp-idf-sbom/blob/master/report_schema.json",
|
||||
"title": "Vulnerability report",
|
||||
"description": "JSON format report schema for check command",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"version": {
|
||||
"type": "integer",
|
||||
"description": "Report format version."
|
||||
},
|
||||
"date": {
|
||||
"type": "string",
|
||||
"description": "Date the report was created."
|
||||
},
|
||||
"database": {
|
||||
"type": "string",
|
||||
"description": "Database source and version used for scanning."
|
||||
},
|
||||
"tool": {
|
||||
"type": "object",
|
||||
"description": "Information about tool used for scanning.",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string",
|
||||
"description": "Tool name."
|
||||
},
|
||||
"version": {
|
||||
"type": "string",
|
||||
"description": "Tool version."
|
||||
},
|
||||
"cmdl": {
|
||||
"type": "string",
|
||||
"description": "Command line including all the arguments used to invoke the tool."
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"cmdl",
|
||||
"name",
|
||||
"version"
|
||||
],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"project": {
|
||||
"type": "object",
|
||||
"description": "Details of project for which this report is created.",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string",
|
||||
"description": "Project name."
|
||||
},
|
||||
"version": {
|
||||
"type": "string",
|
||||
"description": "Project version."
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"name",
|
||||
"version"
|
||||
],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"cves_summary": {
|
||||
"type": "object",
|
||||
"description": "Overview of identified vulnerabilities.",
|
||||
"properties": {
|
||||
"critical": {
|
||||
"description": "Summary of identified critical vulnerabilities.",
|
||||
"$ref": "#/$defs/cves_type_summary"
|
||||
},
|
||||
"high": {
|
||||
"description": "Summary of identified high vulnerabilities.",
|
||||
"$ref": "#/$defs/cves_type_summary"
|
||||
},
|
||||
"medium": {
|
||||
"description": "Summary of identified medium vulnerabilities.",
|
||||
"$ref": "#/$defs/cves_type_summary"
|
||||
},
|
||||
"low": {
|
||||
"description": "Summary of identified low vulnerabilities.",
|
||||
"$ref": "#/$defs/cves_type_summary"
|
||||
},
|
||||
"unknown": {
|
||||
"description": "Summary of identified unknown vulnerabilities.",
|
||||
"$ref": "#/$defs/cves_type_summary"
|
||||
},
|
||||
"total_cves_count": {
|
||||
"description": "Total cound of identified vulnerabilities.",
|
||||
"type": "integer"
|
||||
},
|
||||
"packages_count": {
|
||||
"description": "Number of packages with identified vulnerabilities.",
|
||||
"type": "integer"
|
||||
},
|
||||
"all_cves": {
|
||||
"description": "List of CVEs for all identified vulnerabilities.",
|
||||
"$ref": "#/$defs/array_of_strings"
|
||||
},
|
||||
"all_packages": {
|
||||
"description": "List of all packages with identified vulnerabilities.",
|
||||
"$ref": "#/$defs/array_of_strings"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"all_cves",
|
||||
"all_packages",
|
||||
"critical",
|
||||
"high",
|
||||
"low",
|
||||
"medium",
|
||||
"packages_count",
|
||||
"total_cves_count",
|
||||
"unknown"
|
||||
],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"records": {
|
||||
"type": "array",
|
||||
"description": "Comprehensive list of identified vulnerabilities and packages.",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"vulnerable": {
|
||||
"type": "string",
|
||||
"description": "Vulnerability status for given package. YES - vulnerable, NO - not vulnerable, MAYBE - vulnerability found based on keyword search, EXCLUDED - vulnerability found, but is applicable, SKIPPED - package was not scanned for vulnerabilities."
|
||||
},
|
||||
"pkg_name": {
|
||||
"type": "string",
|
||||
"description": "Package name."
|
||||
},
|
||||
"pkg_version": {
|
||||
"type": "string",
|
||||
"description": "Package version."
|
||||
},
|
||||
"cve_id": {
|
||||
"type": "string",
|
||||
"description": "CVE ID of indentified vulnerability if any."
|
||||
},
|
||||
"cvss_base_score": {
|
||||
"type": "string",
|
||||
"description": "CVSS base score, empty if no vulnerability is identified for given package."
|
||||
},
|
||||
"cvss_base_severity": {
|
||||
"type": "string",
|
||||
"description": "CVSS base severity, empty if no vulnerability is identified for given package."
|
||||
},
|
||||
"cvss_version": {
|
||||
"type": "string",
|
||||
"description": "CVSS version, empty if no vulnerability is identified for given package."
|
||||
},
|
||||
"cvss_vector_string": {
|
||||
"type": "string",
|
||||
"description": "CVSS vector string, empty if no vulnerability is identified for given package."
|
||||
},
|
||||
"cpe": {
|
||||
"type": "string",
|
||||
"description": "CPE used for scanning, empty if not available."
|
||||
},
|
||||
"keyword": {
|
||||
"type": "string",
|
||||
"description": "Keyword used for scanning, empty if not available."
|
||||
},
|
||||
"cve_link": {
|
||||
"type": "string",
|
||||
"description": "NVD CVE URL, empty if no vulnerability is identified for given package."
|
||||
},
|
||||
"cve_desc": {
|
||||
"type": "string",
|
||||
"description": "CVE description, empty if no vulnerability is identified for given package."
|
||||
},
|
||||
"exclude_reason": {
|
||||
"type": "string",
|
||||
"description": "Explanation why package is not affected, empty if no vulnerability is identified for given package."
|
||||
},
|
||||
"status": {
|
||||
"type": "string",
|
||||
"description": "NVD vulnerability status, empty if no vulnerability is identified for given package."
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"cpe",
|
||||
"cve_desc",
|
||||
"cve_id",
|
||||
"cve_link",
|
||||
"cvss_base_score",
|
||||
"cvss_base_severity",
|
||||
"cvss_vector_string",
|
||||
"cvss_version",
|
||||
"exclude_reason",
|
||||
"keyword",
|
||||
"pkg_name",
|
||||
"pkg_version",
|
||||
"status",
|
||||
"vulnerable"
|
||||
],
|
||||
"additionalProperties": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"cves_summary",
|
||||
"database",
|
||||
"date",
|
||||
"project",
|
||||
"records",
|
||||
"tool"
|
||||
],
|
||||
"additionalProperties": false,
|
||||
"$defs": {
|
||||
"array_of_strings": {
|
||||
"type": "array",
|
||||
"description": "Array of strings.",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"cves_type_summary": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"count": {
|
||||
"type": "integer",
|
||||
"description": "Total number of CVEs with the given severity."
|
||||
},
|
||||
"cves": {
|
||||
"$ref": "#/$defs/array_of_strings",
|
||||
"description": "List of CVEs with the given severity."
|
||||
},
|
||||
"packages": {
|
||||
"$ref": "#/$defs/array_of_strings",
|
||||
"description": "List of packages affected by CVEs with the given severity."
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"count",
|
||||
"cves",
|
||||
"packages"
|
||||
],
|
||||
"additionalProperties": false
|
||||
}
|
||||
}
|
||||
}
|
1
setup.py
1
setup.py
@ -56,6 +56,7 @@ setup(
|
||||
'pytest',
|
||||
'commitizen',
|
||||
'spdx-tools>=v0.8.0rc1',
|
||||
'jsonschema',
|
||||
],
|
||||
},
|
||||
classifiers=[
|
||||
|
@ -1,6 +1,7 @@
|
||||
# SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
import shutil
|
||||
@ -12,6 +13,7 @@ from tempfile import TemporaryDirectory
|
||||
from textwrap import dedent
|
||||
|
||||
import pytest
|
||||
from jsonschema import validate
|
||||
|
||||
IDF_PY_PATH = Path(os.environ['IDF_PATH']) / 'tools' / 'idf.py'
|
||||
|
||||
@ -450,3 +452,24 @@ def test_local_db() -> None:
|
||||
assert re.search(r'YES.+CVE-2021-31572', p.stdout) is not None
|
||||
|
||||
manifest.unlink()
|
||||
|
||||
|
||||
def test_validate_report_json(hello_world_build: Path) -> None:
|
||||
"""Generate SPDX SBOM, scan it for vulnerabilities, generate report in JSON format and validate it with JSON schema."""
|
||||
tmpdir = TemporaryDirectory()
|
||||
tmpdir_path = Path(tmpdir.name)
|
||||
sbom_path = tmpdir_path / 'sbom.spdx'
|
||||
report_path = tmpdir_path / 'report.json'
|
||||
schema_path = Path(__file__).resolve().parent.parent / 'report_schema.json'
|
||||
proj_desc_path = hello_world_build / 'build' / 'project_description.json'
|
||||
|
||||
run([sys.executable, '-m', 'esp_idf_sbom', 'create', '--output', sbom_path, proj_desc_path], check=True)
|
||||
|
||||
run([sys.executable, '-m', 'esp_idf_sbom', 'check', '--local-db',
|
||||
'--format', 'json', '--output', report_path, sbom_path], check=True)
|
||||
|
||||
with open(report_path, 'r') as report_file, open(schema_path, 'r') as schema_file:
|
||||
json_data = json.load(report_file)
|
||||
schema_data = json.load(schema_file)
|
||||
|
||||
validate(instance=json_data, schema=schema_data)
|
||||
|
Reference in New Issue
Block a user