Coverage for /home/benjarobin/Bootlin/projects/Schneider-Electric-Senux/sbom-cve-check/src/sbom_cve_check/export/export_yocto.py: 97%

61 statements  

« prev     ^ index     » next       coverage.py v7.11.1, created at 2025-11-28 15:37 +0100

1# -*- coding: utf-8 -*- 

2# SPDX-License-Identifier: GPL-2.0-only 

3 

4import json 

5import pathlib 

6from collections.abc import Generator 

7from datetime import UTC 

8from typing import Any 

9 

10from ..cve_db.annot_aggregate import AggregateAnnotEntry 

11from ..sbom.component import CompBuild 

12from ..sbom.sbom_base import Sbom 

13from ..vuln.cve import CveVexStatus 

14from ..vuln.cvss import CvssMetric, CvssVersion 

15from .export_base import BaseExport 

16from .registry import register_export 

17 

18 

19def _group_cvss_metrics_by_vers(metric: CvssMetric) -> CvssVersion: 

20 return ( 

21 CvssVersion.V3_0 if (metric.cvss_ver == CvssVersion.V3_1) else metric.cvss_ver 

22 ) 

23 

24 

25def _get_cvss_metric_score( 

26 metrics: dict[CvssVersion, tuple[CvssMetric, ...]], vers: CvssVersion 

27) -> str: 

28 m = metrics.get(vers) 

29 return f"{m[0].score:.1f}" if m else "0.0" 

30 

31 

32def _get_modified_date(annotation: AggregateAnnotEntry) -> str | None: 

33 d = annotation.date_modified 

34 if d: 

35 return d.astimezone(UTC).replace(tzinfo=None).isoformat(timespec="milliseconds") 

36 return None 

37 

38 

39def _get_cve_status(annotation: AggregateAnnotEntry) -> str | None: 

40 s = annotation.vex_assessment.status 

41 if s == CveVexStatus.FIXED: 

42 return "Patched" 

43 if s in (CveVexStatus.AFFECTED, CveVexStatus.UNDER_INVESTIGATION): 

44 return "Unpatched" 

45 if s == CveVexStatus.NOT_AFFECTED: 

46 return "Ignored" 

47 return None 

48 

49 

50def _gen_issue_info(annotation: AggregateAnnotEntry) -> dict[str, str | None]: 

51 metrics = annotation.group_cvss_metrics(key=_group_cvss_metrics_by_vers) 

52 metric_vector = ( 

53 metrics.get(CvssVersion.V4_0) 

54 or metrics.get(CvssVersion.V3_0) 

55 or metrics.get(CvssVersion.V2_0) 

56 ) 

57 attack_vector = "UNKNOWN" 

58 vector_str = None 

59 if metric_vector: 

60 vector_str = metric_vector[0].vector_str 

61 enum_av = metric_vector[0].decode_vector().get("AV") 

62 if enum_av is not None: 

63 attack_vector = enum_av.name 

64 

65 return { 

66 "id": annotation.identifier.id, 

67 "status": _get_cve_status(annotation), 

68 "link": f"https://nvd.nist.gov/vuln/detail/{annotation.identifier.id}", 

69 "summary": annotation.description or "", 

70 "scorev2": _get_cvss_metric_score(metrics, CvssVersion.V2_0), 

71 "scorev3": _get_cvss_metric_score(metrics, CvssVersion.V3_0), 

72 "scorev4": _get_cvss_metric_score(metrics, CvssVersion.V4_0), 

73 "modified": _get_modified_date(annotation), 

74 "vector": attack_vector, 

75 "vectorString": vector_str, 

76 "detail": annotation.vex_assessment.status_notes, 

77 } 

78 

79 

80@register_export("yocto-cve-check-manifest") 

81class YoctoCveCheckExport(BaseExport): 

82 def __init__(self, sbom: Sbom, out_path: pathlib.Path) -> None: 

83 super().__init__(sbom, out_path) 

84 self._packages: list[dict[str, Any]] = [] 

85 

86 def start_export(self) -> Generator[None, None, None]: 

87 yield 

88 json_obj = {"version": 1, "package": self._packages} 

89 with self._out_path.open("w", encoding="utf-8") as f: 

90 json.dump(json_obj, f, indent=2) 

91 

92 def export_comp_info( 

93 self, comp_build: CompBuild 

94 ) -> Generator[None, tuple[bool, AggregateAnnotEntry], None]: 

95 issues = [] 

96 try: 

97 while True: 

98 is_filtered, annotation = yield 

99 if not is_filtered: 

100 issues.append(_gen_issue_info(annotation)) 

101 finally: 

102 cves_in_rec = "Yes" if issues else "No" 

103 

104 self._packages.append( 

105 { 

106 "name": comp_build.build_name, 

107 "layer": "", 

108 "version": comp_build.version, 

109 "products": [ 

110 {"product": n, "cvesInRecord": cves_in_rec} 

111 for n in sorted({c.name for c in comp_build.identifiers}) 

112 ], 

113 "issue": issues, 

114 "cpes": [ 

115 str(c.build_cpe(version=comp_build.version)) 

116 for c in comp_build.identifiers 

117 ], 

118 } 

119 )