Skip to content

Commit eb5f280

Browse files
committed
fix: emit CSAF impact statement for all known_not_affected products
1 parent 7171334 commit eb5f280

1 file changed

Lines changed: 36 additions & 25 deletions

File tree

backend/application/vex/services/csaf_generator_vulnerability.py

Lines changed: 36 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
from typing import Optional
22

33
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
55
from application.vex.services.csaf_generator_helpers import (
66
get_product_or_relationship_id,
77
get_vulnerability_ecosystem,
@@ -18,6 +18,7 @@
1818
CSAFNote,
1919
CSAFProductStatus,
2020
CSAFReference,
21+
CSAFThreat,
2122
CSAFVulnerability,
2223
)
2324

@@ -123,39 +124,49 @@ def set_flag_or_threat(vulnerability: CSAFVulnerability, observation: Observatio
123124
vex_status = map_status(observation.current_status)
124125
if vex_status == CSAF_Status.CSAF_STATUS_NOT_AFFECTED:
125126
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:
128139
found = False
129140
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:
131142
if product_or_relationship_id not in flag.product_ids:
132143
flag.product_ids.append(product_or_relationship_id)
133144
found = True
134145
break
135146
if not found:
136147
csaf_flag = CSAFFlag(
137-
label=map_vex_justification_to_csaf_openvex_justification(observation_log.vex_justification),
148+
label=csaf_label,
138149
product_ids=[product_or_relationship_id],
139150
)
140151
vulnerability.flags.append(csaf_flag)
141152

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

Comments
 (0)