Skip to content

Commit 1558c41

Browse files
committed
rework the analysis rendering
Signed-off-by: tdruez <tdruez@aboutcode.org>
1 parent 373a1ab commit 1558c41

3 files changed

Lines changed: 82 additions & 69 deletions

File tree

product_portfolio/templates/product_portfolio/tabs/tab_packages_vulnerabilities.html

Lines changed: 43 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -102,37 +102,49 @@
102102
</td>
103103
{% endif %}
104104
<td>
105-
{% if vulnerability.vulnerability_analysis %}
106-
{% if vulnerability.vulnerability_analysis.state %}
107-
<strong>{{ vulnerability.vulnerability_analysis.get_state_display }}</strong>
108-
{% endif %}
109-
{% if vulnerability.vulnerability_analysis.detail %}
110-
<span data-bs-toggle="popover" data-bs-placement="right" data-bs-trigger="hover focus" data-bs-html="true" data-bs-content="{{ vulnerability.vulnerability_analysis.detail|linebreaksbr }}">
111-
<i class="fa-solid fa-circle-info text-muted"></i>
112-
</span>
105+
{% with analysis=vulnerability.vulnerability_analysis %}
106+
{% if analysis %}
107+
<div class="d-flex align-items-center flex-wrap gap-2">
108+
{% if analysis.state %}
109+
<span class="badge {{ analysis.state_badge_class }}">
110+
{{ analysis.get_state_display }}
111+
</span>
112+
{% endif %}
113+
{% if analysis.is_reachable %}
114+
<i class="fa-solid fa-circle-radiation text-danger" data-bs-toggle="tooltip" title="Vulnerability is reachable"></i>
115+
{% elif analysis.is_reachable is False %}
116+
<i class="fa-solid fa-bug-slash text-body-secondary" data-bs-toggle="tooltip" title="Vulnerability is NOT reachable"></i>
117+
{% endif %}
118+
</div>
119+
{% if analysis.justification %}
120+
<div class="small text-body-secondary mt-1">{{ analysis.get_justification_display }}</div>
121+
{% endif %}
122+
{% if analysis.detail %}
123+
<div class="small text-body-secondary mt-1" data-bs-toggle="popover" data-bs-placement="right" data-bs-trigger="hover focus" data-bs-html="true" data-bs-content="{{ analysis.detail|linebreaksbr }}">
124+
<i class="fa-solid fa-circle-info text-muted me-1"></i>{{ analysis.detail|truncatechars:70 }}
125+
</div>
126+
{% endif %}
127+
{% if analysis.response_labels %}
128+
<div class="d-flex flex-wrap gap-1 mt-1">
129+
{% for response in analysis.response_labels %}
130+
<span class="badge bg-light text-body-secondary border fw-normal">{{ response }}</span>
131+
{% endfor %}
132+
</div>
133+
{% endif %}
134+
{% if analysis.applied_by_preset %}
135+
<div class="small text-body-tertiary mt-1 pt-1 border-top">
136+
<i class="fas fa-bolt text-warning fa-xs me-1"></i>{% trans "Auto-applied:" %} {{ analysis.applied_by_preset.name }}
137+
</div>
138+
{% elif analysis.last_modified_by %}
139+
<div class="small text-body-tertiary mt-1 pt-1 border-top">
140+
<i class="fas fa-user fa-xs me-1"></i>{{ analysis.last_modified_by }}
141+
&middot; {{ analysis.last_updated|date:"M j, Y" }}
142+
</div>
143+
{% endif %}
144+
{% else %}
145+
<span class="text-body-tertiary small fst-italic">{% trans "Not analyzed" %}</span>
113146
{% endif %}
114-
{% endif %}
115-
</td>
116-
<td>
117-
{% if vulnerability.vulnerability_analysis.justification %}
118-
{{ vulnerability.vulnerability_analysis.get_justification_display }}
119-
{% endif %}
120-
</td>
121-
<td>
122-
{% if vulnerability.vulnerability_analysis.responses %}
123-
<ul class="ps-3 m-0">
124-
{% for response in vulnerability.vulnerability_analysis.responses %}
125-
<li>{{ response }}</li>
126-
{% endfor %}
127-
</ul>
128-
{% endif %}
129-
</td>
130-
<td class="text-center">
131-
{% if vulnerability.vulnerability_analysis.is_reachable %}
132-
<i class="fa-solid fa-circle-radiation text-danger fs-6" data-bs-toggle="tooltip" title="Vulnerability is reachable"></i>
133-
{% elif vulnerability.vulnerability_analysis.is_reachable is False %}
134-
<i class="fa-solid fa-bug-slash" data-bs-toggle="tooltip" title="Vulnerability is NOT reachable"></i>
135-
{% endif %}
147+
{% endwith %}
136148
</td>
137149
<td class="p-1 text-nowrap">
138150
<span data-bs-toggle="modal"
@@ -170,7 +182,7 @@
170182
{% endif %}
171183
{% empty %}
172184
<tr>
173-
<td colspan="{% if has_triage_rulesets %}8{% else %}7{% endif %}">
185+
<td colspan="{% if has_triage_rulesets %}5{% else %}4{% endif %}">
174186
No results.
175187
{% if filterset.is_active %}
176188
<a href="#" hx-get="{{ request.path }}?all=true#{{ tab_id }}" hx-target="{{ tab_id_html }}">

product_portfolio/tests/test_views.py

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -419,18 +419,19 @@ def test_product_portfolio_tab_vulnerability_view_analysis_rendering(self):
419419

420420
expected = """
421421
<td>
422-
<strong>Resolved</strong>
423-
<span data-bs-toggle="popover" data-bs-placement="right" data-bs-trigger="hover focus"
424-
data-bs-html="true" data-bs-content="detail">
425-
<i class="fa-solid fa-circle-info text-muted"></i>
426-
</span>
427-
</td>
428-
<td>Code Not Present</td>
429-
<td>
430-
<ul class="ps-3 m-0">
431-
<li>can_not_fix</li>
432-
<li>rollback</li>
433-
</ul>
422+
<div class="d-flex align-items-center flex-wrap gap-2">
423+
<span class="badge bg-success-subtle text-success-emphasis">Resolved</span>
424+
</div>
425+
<div class="small text-body-secondary mt-1">Code Not Present</div>
426+
<div class="small text-body-secondary mt-1" data-bs-toggle="popover"
427+
data-bs-placement="right" data-bs-trigger="hover focus"
428+
data-bs-html="true" data-bs-content="detail">
429+
<i class="fa-solid fa-circle-info text-muted me-1"></i>detail
430+
</div>
431+
<div class="d-flex flex-wrap gap-1 mt-1">
432+
<span class="badge bg-light text-body-secondary border fw-normal">Can Not Fix</span>
433+
<span class="badge bg-light text-body-secondary border fw-normal">Rollback</span>
434+
</div>
434435
</td>
435436
"""
436437
self.assertContains(response, expected, html=True)

product_portfolio/views.py

Lines changed: 26 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,16 @@
174174
"fa-exclamation-circle",
175175
)
176176

177+
ANALYSIS_STATE_STYLES = {
178+
"exploitable": "bg-danger-subtle text-danger-emphasis",
179+
"in_triage": "bg-warning-subtle text-warning-emphasis",
180+
"resolved": "bg-success-subtle text-success-emphasis",
181+
"resolved_with_pedigree": "bg-success-subtle text-success-emphasis",
182+
"not_affected": "bg-secondary-subtle text-secondary-emphasis",
183+
"false_positive": "bg-secondary-subtle text-secondary-emphasis",
184+
}
185+
ANALYSIS_STATE_DEFAULT_STYLE = "bg-secondary-subtle text-secondary-emphasis"
186+
177187

178188
class BaseProductViewMixin:
179189
model = Product
@@ -1252,43 +1262,30 @@ class ProductTabVulnerabilitiesView(
12521262
),
12531263
Header(
12541264
"vulnerability_analyses__state",
1255-
_("Status"),
1256-
help_text=_("Exploitability analysis status"),
1257-
filter="vulnerability_analyses__state",
1258-
),
1259-
Header(
1260-
"vulnerability_analyses__justification",
1261-
_("Justification"),
1262-
help_text=_("The rationale of why the impact analysis state was asserted."),
1263-
filter="vulnerability_analyses__justification",
1264-
),
1265-
Header(
1266-
"vulnerability_analyses__responses",
1267-
_("Responses"),
1265+
_("Analysis"),
12681266
help_text=_(
1269-
"A response to the vulnerability by the manufacturer, supplier, or project "
1270-
"responsible for the affected component or service."
1267+
"Exploitability analysis: status, justification, responses and reachability."
12711268
),
1272-
filter="responses",
1273-
),
1274-
Header(
1275-
"vulnerability_analyses__is_reachable",
1276-
_("Reach"),
1277-
help_text=_(
1278-
"Indicates whether the vulnerability is reachable in the context of "
1279-
"this product package."
1280-
),
1281-
filter="is_reachable",
1269+
filter="vulnerability_analyses__state",
12821270
),
12831271
)
12841272

12851273
def attach_vulnerability_analyses(self, page_obj):
12861274
"""Set the matching VulnerabilityAnalysis instance on each prefetched vulnerability."""
1275+
response_labels = dict(VulnerabilityAnalysis.Response.choices)
1276+
12871277
for product_package in page_obj.object_list:
12881278
for vulnerability in product_package.package.affected_by_vulnerabilities.all():
12891279
for analysis in vulnerability.vulnerability_analyses.all():
12901280
if analysis.product_package_id == product_package.id:
12911281
vulnerability.vulnerability_analysis = analysis
1282+
analysis.state_badge_class = ANALYSIS_STATE_STYLES.get(
1283+
analysis.state, ANALYSIS_STATE_DEFAULT_STYLE
1284+
)
1285+
analysis.response_labels = [
1286+
response_labels.get(response, response)
1287+
for response in analysis.responses or []
1288+
]
12921289
break
12931290

12941291
REACHABILITY_FILTER_MAP = {"yes": True, "no": False, "unknown": None}
@@ -1384,8 +1381,11 @@ def get_context_data(self, **kwargs):
13841381
risk_threshold = product.get_vulnerabilities_risk_threshold()
13851382

13861383
base_productpackage_qs = product.get_vulnerable_productpackages(risk_threshold)
1384+
vulnerability_analyses_qs = VulnerabilityAnalysis.objects.select_related(
1385+
"last_modified_by", "applied_by_preset"
1386+
)
13871387
vulnerability_qs = Vulnerability.objects.prefetch_related(
1388-
"vulnerability_analyses"
1388+
Prefetch("vulnerability_analyses", queryset=vulnerability_analyses_qs)
13891389
).order_by(F("risk_score").desc(nulls_last=True))
13901390
package_qs = (
13911391
Package.objects.all()

0 commit comments

Comments
 (0)