Skip to content

Commit 15dc954

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

1 file changed

Lines changed: 35 additions & 24 deletions

File tree

backend/application/vex/services/csaf_generator_vulnerability.py

Lines changed: 35 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
CSAFNote,
1919
CSAFProductStatus,
2020
CSAFReference,
21+
CSAFThreat,
2122
CSAFVulnerability,
2223
)
2324

@@ -124,38 +125,48 @@ def set_flag_or_threat(vulnerability: CSAFVulnerability, observation: Observatio
124125
if vex_status == CSAF_Status.CSAF_STATUS_NOT_AFFECTED:
125126
product_or_relationship_id = get_product_or_relationship_id(observation)
126127
observation_log = get_current_modifying_observation_log(observation)
127-
if observation_log and observation_log.vex_justification:
128+
# Prefer the observation's authoritative current justification and only fall back to the latest
129+
# modifying observation log when it is empty. The log's vex_justification can be empty even
130+
# though a justification was assessed, because empty log entries (for example the ones created
131+
# when a rego rule is removed) count as "modifying" and shadow the real assessment. That would
132+
# leave the product in known_not_affected without any impact statement (CSAF mandatory test
133+
# 6.1.27.9).
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)
141-
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)
152+
elif observation_log and observation_log.comment:
153+
# CSAF mandatory test 6.1.27.9 requires an impact statement for every product in
154+
# known_not_affected. When no machine-readable vex_justification is set, fall back to
155+
# the assessment comment as a free-text impact threat. Without a comment we emit nothing
156+
# and let validation fail, so the missing justification is caught instead of masked.
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)