11from typing import Optional
22
3- from application .core .models import Observation
4- from application .core .queries .observation import get_current_observation_log
3+ from application .core .models import Observation , Observation_Log
54from application .vex .services .csaf_generator_helpers import (
65 get_product_or_relationship_id ,
76 get_vulnerability_ecosystem ,
@@ -124,16 +123,21 @@ def set_flag_or_threat(vulnerability: CSAFVulnerability, observation: Observatio
124123 vex_status = map_status (observation .current_status )
125124 if vex_status == CSAF_Status .CSAF_STATUS_NOT_AFFECTED :
126125 product_or_relationship_id = get_product_or_relationship_id (observation )
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 )
126+ # Use the observation log that set the current status, i.e. the assessment. The generic
127+ # "current"/"modifying" observation log helpers are unreliable here: automated log entries
128+ # (for example "Removed product rego rule ...") carry no status but still pass those filters
129+ # (empty vex_remediations is stored inconsistently as "" or NULL) and would then provide a
130+ # misleading justification and comment. Filtering on the current status skips those entries.
131+ assessment_log = (
132+ Observation_Log .objects .filter (observation = observation , status = observation .current_status )
133+ .order_by ("-created" )
134+ .first ()
135+ )
132136
133- # Prefer the observation's authoritative current justification, fall back to the log .
137+ # Prefer the observation's authoritative current justification, fall back to the assessment .
134138 justification = observation .current_vex_justification
135- if not justification and observation_log :
136- justification = observation_log .vex_justification
139+ if not justification and assessment_log :
140+ justification = assessment_log .vex_justification
137141 csaf_label = map_vex_justification_to_csaf_openvex_justification (justification )
138142 if csaf_label :
139143 found = False
@@ -153,9 +157,9 @@ def set_flag_or_threat(vulnerability: CSAFVulnerability, observation: Observatio
153157 # Surface the assessment comment as a free-text impact threat whenever it exists, so the human
154158 # explanation is kept alongside the machine-readable flag. With neither a flag nor a comment
155159 # the product has no impact statement and validation fails, which is intended.
156- if observation_log and observation_log .comment :
160+ if assessment_log and assessment_log .comment :
157161 category = "impact"
158- details = observation_log .comment
162+ details = assessment_log .comment
159163 found = False
160164 for threat in vulnerability .threats :
161165 if threat .category == category and threat .details == details :
0 commit comments