|
1 | 1 | from typing import Optional |
2 | 2 |
|
3 | 3 | from application.core.models import Observation |
4 | | -from application.core.queries.observation import get_current_modifying_observation_log |
| 4 | +from application.core.queries.observation import get_current_observation_log |
5 | 5 | from application.vex.services.csaf_generator_helpers import ( |
6 | 6 | get_product_or_relationship_id, |
7 | 7 | get_vulnerability_ecosystem, |
|
18 | 18 | CSAFNote, |
19 | 19 | CSAFProductStatus, |
20 | 20 | CSAFReference, |
| 21 | + CSAFThreat, |
21 | 22 | CSAFVulnerability, |
22 | 23 | ) |
23 | 24 |
|
@@ -123,39 +124,49 @@ def set_flag_or_threat(vulnerability: CSAFVulnerability, observation: Observatio |
123 | 124 | vex_status = map_status(observation.current_status) |
124 | 125 | if vex_status == CSAF_Status.CSAF_STATUS_NOT_AFFECTED: |
125 | 126 | product_or_relationship_id = get_product_or_relationship_id(observation) |
126 | | - observation_log = get_current_modifying_observation_log(observation) |
127 | | - if observation_log and observation_log.vex_justification: |
| 127 | + # get_current_observation_log (unlike get_current_modifying_observation_log) excludes empty log |
| 128 | + # entries, e.g. the ones created when a rego rule is removed. Those have an empty |
| 129 | + # vex_justification and comment and would otherwise shadow the real assessment, leaving the |
| 130 | + # product in known_not_affected without any impact statement (CSAF mandatory test 6.1.27.9). |
| 131 | + observation_log = get_current_observation_log(observation) |
| 132 | + |
| 133 | + # Prefer the observation's authoritative current justification, fall back to the log. |
| 134 | + justification = observation.current_vex_justification |
| 135 | + if not justification and observation_log: |
| 136 | + justification = observation_log.vex_justification |
| 137 | + csaf_label = map_vex_justification_to_csaf_openvex_justification(justification) |
| 138 | + if csaf_label: |
128 | 139 | found = False |
129 | 140 | for flag in vulnerability.flags: |
130 | | - if flag.label == map_vex_justification_to_csaf_openvex_justification(observation_log.vex_justification): |
| 141 | + if flag.label == csaf_label: |
131 | 142 | if product_or_relationship_id not in flag.product_ids: |
132 | 143 | flag.product_ids.append(product_or_relationship_id) |
133 | 144 | found = True |
134 | 145 | break |
135 | 146 | if not found: |
136 | 147 | csaf_flag = CSAFFlag( |
137 | | - label=map_vex_justification_to_csaf_openvex_justification(observation_log.vex_justification), |
| 148 | + label=csaf_label, |
138 | 149 | product_ids=[product_or_relationship_id], |
139 | 150 | ) |
140 | 151 | vulnerability.flags.append(csaf_flag) |
141 | 152 |
|
142 | | - |
143 | | -# else: |
144 | | -# category = "impact" |
145 | | -# details = ( |
146 | | -# observation_log.comment if observation_log and observation_log.comment else "No justification available" |
147 | | -# ) |
148 | | -# found = False |
149 | | -# for threat in vulnerability.threats: |
150 | | -# if threat.category == category and threat.details == details: |
151 | | -# if product_or_relationship_id not in threat.product_ids: |
152 | | -# threat.product_ids.append(product_or_relationship_id) |
153 | | -# found = True |
154 | | -# break |
155 | | -# if not found: |
156 | | -# threat = CSAFThreat( |
157 | | -# category=category, |
158 | | -# details=details, |
159 | | -# product_ids=[product_or_relationship_id], |
160 | | -# ) |
161 | | -# vulnerability.threats.append(threat) |
| 153 | + # Surface the assessment comment as a free-text impact threat whenever it exists, so the human |
| 154 | + # explanation is kept alongside the machine-readable flag. With neither a flag nor a comment |
| 155 | + # the product has no impact statement and validation fails, which is intended. |
| 156 | + if observation_log and observation_log.comment: |
| 157 | + category = "impact" |
| 158 | + details = observation_log.comment |
| 159 | + found = False |
| 160 | + for threat in vulnerability.threats: |
| 161 | + if threat.category == category and threat.details == details: |
| 162 | + if product_or_relationship_id not in threat.product_ids: |
| 163 | + threat.product_ids.append(product_or_relationship_id) |
| 164 | + found = True |
| 165 | + break |
| 166 | + if not found: |
| 167 | + threat = CSAFThreat( |
| 168 | + category=category, |
| 169 | + details=details, |
| 170 | + product_ids=[product_or_relationship_id], |
| 171 | + ) |
| 172 | + vulnerability.threats.append(threat) |
0 commit comments