Skip to content

Commit eff2e66

Browse files
committed
feat(ui): add template for mitigation curation
Signed-off-by: Keshav Priyadarshi <git@keshav.space>
1 parent 8f8a3f6 commit eff2e66

4 files changed

Lines changed: 374 additions & 0 deletions

File tree

vulnerabilities/templates/advisory_todos.html

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,8 @@ <h1>Advisory To-Dos</h1>
129129
<a href="{% url 'todo-severity-detail' todo_id=todo.todo_id %}" class="has-text-info">
130130
{% elif todo.issue_type == "CONFLICTING_WEAKNESSES" %}
131131
<a href="{% url 'todo-weakness-detail' todo_id=todo.todo_id %}" class="has-text-info">
132+
{% elif todo.issue_type == "MISSING_FIXED_BY_PACKAGE" %}
133+
<a href="{% url 'todo-mitigation-detail' todo_id=todo.todo_id %}" class="has-text-info">
132134
{% endif %}
133135
<div class="columns px-1 is-vcentered">
134136
<div class="column has-text-left" style="flex: 0 0 20%;">
Lines changed: 365 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,365 @@
1+
{% extends "base.html" %}
2+
{% load static %}
3+
{% load utils %}
4+
5+
{% block extrahead %}
6+
<style>
7+
.table {
8+
table-layout: fixed !important;
9+
width: 100% !important;
10+
}
11+
12+
.table th {
13+
border-width: 0 1px 1px 1px !important;
14+
border-color: #dbdbdb !important;
15+
background-color: #fff !important;
16+
}
17+
18+
#table-header th {
19+
height: 100px;
20+
padding: 0.75rem 0.5rem !important;
21+
}
22+
23+
#table-header th>div {
24+
display: inline-flex !important;
25+
flex-direction: column !important;
26+
justify-content: space-between !important;
27+
height: 100% !important;
28+
width: 100%;
29+
}
30+
31+
#table-header th:first-child {
32+
width: 120px !important;
33+
}
34+
35+
#table-header th:nth-child(2) {
36+
width: 150px !important;
37+
}
38+
39+
#table-header th:nth-child(n+3) {
40+
width: 150px !important;
41+
}
42+
43+
.table td {
44+
vertical-align: middle !important;
45+
}
46+
47+
#summaries-container p strong {
48+
display: block;
49+
word-break: break-all;
50+
overflow-wrap: break-word;
51+
line-height: 1.3;
52+
margin-bottom: 4px;
53+
}
54+
55+
.summary-text {
56+
line-height: 1.4;
57+
white-space: pre-wrap;
58+
word-break: break-word;
59+
}
60+
61+
div.summary-text a:link,
62+
div.summary-text a {
63+
color: #3273dc !important;
64+
}
65+
66+
div.summary-text a:visited {
67+
color: #8a4de8 !important;
68+
}
69+
70+
div.summary-text a:hover {
71+
color: #0a0a0a !important;
72+
}
73+
74+
.advisory-link {
75+
display: inline-flex;
76+
align-items: center;
77+
gap: 4px;
78+
text-align: left;
79+
text-decoration: underline;
80+
white-space: normal;
81+
flex-wrap: wrap;
82+
}
83+
84+
.table-container {
85+
overflow-x: auto;
86+
overflow-y: auto;
87+
max-height: 800px;
88+
border: 1px solid #dbdbdb;
89+
border-radius: 6px;
90+
}
91+
92+
.has-tooltip-top::before {
93+
transform: translate(-5% , -100%) !important;
94+
}
95+
</style>
96+
{% endblock %}
97+
98+
{% block content %}
99+
<section class="section my-0 pt-3 px-0 mx-0">
100+
<div class="container is-fluid">
101+
<div class="columns is-vcentered mb-5">
102+
<div class="column">
103+
<h1 class="title is-3">Extra Mitigation Curation</h1>
104+
<h2 class="subtitle is-5 has-text-grey">
105+
<a href={% url 'advisory_details' avid %} target="_blank" rel="noopener noreferrer" class="has-text-link">
106+
<span>{{ avid }}</span>
107+
<span class="icon is-small"><i class="fa fa-external-link"></i></span>
108+
</a>
109+
</h2>
110+
</div>
111+
112+
<div class="column is-narrow">
113+
<div class="box p-4 has-background-white shadow-soft" style="min-width: 260px; border-radius: 5px;">
114+
<div class="is-flex is-justify-content-space-between is-align-items-center mb-2">
115+
<span class="is-size-6 has-text-grey-dark uppercase has-text-weight-bold mr-2">
116+
Progress
117+
</span>
118+
<span class="tag is-info is-light has-text-weight-bold" id="progress-text">0 / 0</span>
119+
</div>
120+
<progress class="progress is-info is-small mb-0" id="progress" value="0" max="100"></progress>
121+
</div>
122+
</div>
123+
124+
</div>
125+
126+
<div class="columns">
127+
<div class="column is-3">
128+
<h3 class="title is-5">Advisory Summary</h3>
129+
<div id="summaries-container" style="max-height: 950px; overflow-y: auto;">
130+
{% for avid, text in advisory_summaries.items %}
131+
<div class="notification is-info is-light p-3 mb-3">
132+
<p><strong>{{ avid }}</strong></p>
133+
<div class="text summary-text is-2">{{ text|normalize_links|urlize }}</div>
134+
</div>
135+
{% empty %}
136+
<div class="notification is-light p-3 mb-3 has-text-grey">
137+
No summaries available.
138+
</div>
139+
{% endfor %}
140+
</div>
141+
</div>
142+
143+
<div class="column is-9">
144+
<div class="box">
145+
<div class="mb-4">
146+
<h4 class="title is-4 mb-1" id="current-purl"></h4>
147+
<a id="toggle-conflict" class="is-link is-5" style="display:none;">Show more</a>
148+
</div>
149+
150+
<div class="table-container">
151+
<table class="table is-bordered is-narrow is-fullwidth is-hoverable">
152+
<thead style="position: sticky; top: 0; background: white; z-index: 10;">
153+
<tr id="table-header">
154+
<th class="has-text-weight-bold has-text-centered pt-4 is-size-5">Mitigation Type</th>
155+
<th class="has-text-weight-bold has-text-centered pt-4 is-size-5">Mitigation Data</th>
156+
<th class="has-text-weight-bold has-text-centered pt-4 is-size-5">Mitigation Note</th>
157+
</tr>
158+
</thead>
159+
<tbody id="curation-body">
160+
161+
162+
</tbody>
163+
</table>
164+
</div>
165+
166+
<div class="level mt-5">
167+
<div class="level-left">
168+
<button class="button is-light" id="prev-btn" onclick="app.navigate(-1)">
169+
<span class="icon"><i class="fa fa-chevron-left"></i></span>
170+
<span>Previous Item</span>
171+
</button>
172+
</div>
173+
<div class="level-right">
174+
<button class="button is-info" id="next-btn" onclick="app.navigate(1)">
175+
<span>Next Item</span>
176+
<span class="icon"><i class="fa fa-chevron-right"></i></span>
177+
</button>
178+
<button class="button is-info is-hidden" id="finish-btn" disabled>
179+
<span class="icon"><i class="fa fa-check"></i></span>
180+
<span>Submit</span>
181+
</button>
182+
</div>
183+
</div>
184+
</div>
185+
</div>
186+
</div>
187+
</div>
188+
</section>
189+
{% endblock %}
190+
191+
192+
{% block scripts %}
193+
<script>
194+
const curationItems = {{ curation_items|safe }};
195+
const mitigation_items = [
196+
"upgrade_version",
197+
"upgrade_note",
198+
"downgrade_version",
199+
"downgrade_note",
200+
"patch",
201+
"patch_note",
202+
"config",
203+
"config_note",
204+
"filter_ips_ports",
205+
"filter_ips_ports_note",
206+
"replace_package",
207+
"replace_note",
208+
];
209+
210+
const app = {
211+
currentIndex: 0,
212+
userStates: {},
213+
214+
init() {
215+
this.renderMitigationCuration();
216+
217+
document.querySelector('.summary-text')?.addEventListener('click', (e) => {
218+
if (e.target.closest('a')) {
219+
e.target.target = '_blank';
220+
}
221+
});
222+
},
223+
224+
renderMitigationCuration() {
225+
const total = curationItems.length;
226+
const purl = curationItems[this.currentIndex];
227+
const progPercentage = ((this.currentIndex + 1) / total) * 100;
228+
document.getElementById('progress').value = progPercentage;
229+
document.getElementById('progress-text').innerText = `${this.currentIndex + 1} / ${total}`;
230+
document.getElementById('current-purl').innerText = `No reported fix version for ${purl}`;
231+
232+
if (!this.userStates[this.currentIndex]) {
233+
this.userStates[this.currentIndex] = Object.fromEntries(
234+
mitigation_items.map((key) => [key, ""]
235+
)
236+
);
237+
this.userStates[this.currentIndex]["purl"] = purl;
238+
}
239+
240+
this.renderBody();
241+
this.updateNavButtons();
242+
},
243+
244+
renderBody() {
245+
state = this.userStates[this.currentIndex]
246+
const body = document.getElementById('curation-body');
247+
body.innerHTML = `
248+
<tr>
249+
<td class="has-text-weight-bold"style="word-break: break-all;">
250+
Upgrade to Versions
251+
<span class="icon has-tooltip-multiline has-tooltip-bottom has-tooltip-arrow has-text-weight-normal"
252+
data-tooltip="Specify the version(s) users should upgrade to in order to remediate the vulnerability. Enter one or more fixed versions separated by commas (e.g., 1.0.1, 2.3.4). Use the note field for any additional upgrade guidance or constraints.">
253+
<i class="fa fa-info-circle"></i>
254+
</span>
255+
</td>
256+
<td>
257+
<input class="input" type="text" id="upgrade_version" value="${state.upgrade_version}" placeholder="1.0.1, 3.0.0, ...">
258+
<td>
259+
<textarea class="textarea" id="upgrade_note" rows="1" placeholder="Note..">${state.upgrade_note}</textarea></td>
260+
</td>
261+
</tr>
262+
263+
<tr>
264+
<td class="has-text-weight-bold"style="word-break: break-all;">
265+
Downgrade to Versions
266+
<span class="icon has-tooltip-multiline has-tooltip-top has-tooltip-arrow has-text-weight-normal" data-tooltip="Specify the version(s) users should downgrade to if no fixed upgrade is available. Enter one or more non-vulnerable versions separated by commas (e.g., 1.0.1, 2.3.4). Use the note field for any additional guidance or cautions related to the downgrade.">
267+
<i class="fa fa-info-circle"></i>
268+
</span>
269+
</td>
270+
<td>
271+
<input class="input" type="text" id="downgrade_version" value="${state.downgrade_version}" placeholder="1.0.1, 3.0.0, ...">
272+
<td>
273+
<textarea class="textarea" id="downgrade_note" rows="1" placeholder="Note..">${state.downgrade_note}</textarea></td>
274+
</td>
275+
</tr>
276+
277+
<tr>
278+
<td class="has-text-weight-bold"style="word-break: break-all;">
279+
Apply Patch
280+
<span class="icon has-tooltip-multiline has-tooltip-top has-tooltip-arrow has-text-weight-normal" data-tooltip="Provide a URL to the official patch, hotfix that remediates the vulnerability. Use the note field to include any additional instructions, prerequisites, or known limitations.">
281+
<i class="fa fa-info-circle"></i>
282+
</span>
283+
</td>
284+
<td>
285+
<input class="input" type="text" id="patch" value="${state.patch}" placeholder="Patch URL">
286+
<td>
287+
<textarea class="textarea" id="patch_note" rows="1" placeholder="Note..">${state.patch_note}</textarea></td>
288+
</td>
289+
</tr>
290+
291+
292+
<tr>
293+
<td class="has-text-weight-bold"style="word-break: break-all;">
294+
Change Config
295+
<span class="icon has-tooltip-multiline has-tooltip-top has-tooltip-arrow has-text-weight-normal" data-tooltip="Provide the configuration changes that mitigate the vulnerabilities, such as modifying settings, disabling features, or enabling security controls. Use the note field to include any potential side effects, or additional implementation guidance.">
296+
<i class="fa fa-info-circle"></i>
297+
</span>
298+
</td>
299+
<td>
300+
<textarea class="textarea" id="config" rows="1" placeholder="Configuration details">${state.config}</textarea>
301+
<td>
302+
<textarea class="textarea" id="config_note" rows="1" placeholder="Note..">${state.config_note}</textarea></td>
303+
</td>
304+
</tr>
305+
306+
<tr>
307+
<td class="has-text-weight-bold"style="word-break: break-all;">
308+
Apply Network Restrictions
309+
<span class="icon has-tooltip-multiline has-tooltip-top has-tooltip-arrow has-text-weight-normal" data-tooltip="Specify the network restrictions required to mitigate the vulnerability, such as blocking or limiting access to specific ports, IP addresses, or network ranges. Use the note field to provide firewall rules, scope details, or additional implementation guidance.">
310+
<i class="fa fa-info-circle"></i>
311+
</span>
312+
</td>
313+
<td>
314+
<textarea class="textarea" id="filter_ips_ports" rows="1" placeholder="Details of Ports/IPs to filter">${state.filter_ips_ports}</textarea>
315+
<td>
316+
<textarea class="textarea" id="filter_ips_ports_note" rows="1" placeholder="Note..">${state.filter_ips_ports_note}</textarea></td>
317+
</td>
318+
</tr>
319+
320+
<tr>
321+
<td class="has-text-weight-bold"style="word-break: break-all;">
322+
Replace Package
323+
<span class="icon has-tooltip-multiline has-tooltip-top has-tooltip-arrow has-text-weight-normal" data-tooltip="Specify the Package-URL of the replacement package to use instead of the vulnerable package. Use the note field for compatibility details, or additional guidance.">
324+
<i class="fa fa-info-circle"></i>
325+
</span>
326+
</td>
327+
<td>
328+
<input class="input" type="text" id="replace_package" value="${state.replace_package}" placeholder="pkg:npm/foobar@3.1.0">
329+
<td>
330+
<textarea class="textarea" id="replace_note" rows="1" placeholder="Note..">${state.replace_note}</textarea></td>
331+
</td>
332+
</tr>`;
333+
},
334+
335+
saveChanges() {
336+
this.userStates[this.currentIndex] = {
337+
...this.userStates[this.currentIndex],
338+
...Object.fromEntries(
339+
mitigation_items.map((key) => [
340+
key,
341+
document.getElementById(key)?.value ?? ""
342+
])
343+
),
344+
};
345+
},
346+
347+
navigate(dir) {
348+
this.saveChanges();
349+
console.log(this.userStates[this.currentIndex]);
350+
this.currentIndex += dir;
351+
this.renderMitigationCuration();
352+
},
353+
354+
updateNavButtons() {
355+
document.getElementById('prev-btn').disabled = this.currentIndex === 0;
356+
const isLast = this.currentIndex === curationItems.length - 1;
357+
document.getElementById('next-btn').classList.toggle('is-hidden', isLast);
358+
document.getElementById('finish-btn').classList.toggle('is-hidden', !isLast);
359+
},
360+
361+
};
362+
document.addEventListener('DOMContentLoaded', () => app.init());
363+
364+
</script>
365+
{% endblock %}

vulnerabilities/templates/weakness_curation.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,7 @@
153153

154154
.table-container {
155155
overflow-x: auto;
156+
overflow-y: auto;
156157
max-height: 800px;
157158
border: 1px solid #dbdbdb;
158159
border-radius: 6px;

0 commit comments

Comments
 (0)