Skip to content
This repository has been archived by the owner on Dec 13, 2024. It is now read-only.

Commit

Permalink
Fix small bugs with new frontend
Browse files Browse the repository at this point in the history
* Strip queries in web server backend for passing them to search_vulns
* Don't store vulns with stripped query in search_vulns
* Fix no-wrap for CVSS popup and CVE column
* Fix rounded table corners in Firefox
* Fix pressing enter to search when CPE suggestions popup is loading
* Fix suggestion caching issue leading to equivalent CPEs not being used
  • Loading branch information
ra1nb0rn committed Mar 19, 2024
1 parent 48ac6b9 commit b0cc188
Show file tree
Hide file tree
Showing 6 changed files with 91 additions and 34 deletions.
8 changes: 4 additions & 4 deletions search_vulns.py
Original file line number Diff line number Diff line change
Expand Up @@ -420,10 +420,10 @@ def search_vulns(query, db_cursor=None, software_match_threshold=CPE_SEARCH_THRE

# if given query is not already a CPE, try to retrieve a CPE that matches
# the query or create alternative CPEs that could match the query
query = query.strip()
cpe, pot_cpes = query, []
if not MATCH_CPE_23_RE.match(query):
cpe_search_results = search_cpes(query, count=CPE_SEARCH_COUNT, threshold=software_match_threshold, config=config['cpe_search'])
query_stripped = query.strip()
cpe, pot_cpes = query_stripped, []
if not MATCH_CPE_23_RE.match(query_stripped):
cpe_search_results = search_cpes(query_stripped, count=CPE_SEARCH_COUNT, threshold=software_match_threshold, config=config['cpe_search'])

if not cpe_search_results['cpes']:
return {query: {'cpe': None, 'vulns': {}, 'pot_cpes': cpe_search_results['pot_cpes']}}
Expand Down
13 changes: 11 additions & 2 deletions web_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@
_load_config,
search_vulns as search_vulns_call,
CPE_SEARCH_THRESHOLD_MATCH,
CPE_SEARCH_COUNT
CPE_SEARCH_COUNT,
MATCH_CPE_23_RE
)
from cpe_search.cpe_search import search_cpes

Expand All @@ -28,8 +29,12 @@ def cpe_suggestions():
query = request.args.get('query')
if not query:
return "No query provided", 400
query = query.strip()
query_lower = query.lower()

if MATCH_CPE_23_RE.match(query_lower):
return [(query_lower, -1)]

if query_lower in CPE_SUGGESTIONS_CACHE:
return jsonify(CPE_SUGGESTIONS_CACHE[query_lower])

Expand All @@ -55,6 +60,7 @@ def search_vulns():
query = request.args.get('query')
if not query:
return "No query provided", 400
query = query.strip()

if url_query_string in VULN_RESULTS_CACHE:
return VULN_RESULTS_CACHE[url_query_string]
Expand Down Expand Up @@ -85,11 +91,14 @@ def search_vulns():

if cpe_suggestions:
query_cpe = cpe_suggestions[0][0]
is_good_cpe = False # query was never issued as CPE --> use CPE deprecations and equivalences
vulns = search_vulns_call(query_cpe, db_cursor=db_cursor, add_other_exploit_refs=True, ignore_general_cpe_vulns=ignore_general_cpe_vulns, include_single_version_vulns=include_single_version_vulns, is_good_cpe=is_good_cpe, config=config)
vulns = {query: {'cpe': vulns[query_cpe]['cpe'], 'vulns': vulns[query_cpe]['vulns'], 'pot_cpes': cpe_suggestions}}
else:
vulns = search_vulns_call(query, db_cursor=db_cursor, add_other_exploit_refs=True, ignore_general_cpe_vulns=ignore_general_cpe_vulns, include_single_version_vulns=include_single_version_vulns, is_good_cpe=is_good_cpe, config=config)
CPE_SUGGESTIONS_CACHE[query.lower()] = vulns[query]['pot_cpes']
query_lower = query.lower()
if not MATCH_CPE_23_RE.match(query_lower):
CPE_SUGGESTIONS_CACHE[query_lower] = vulns[query]['pot_cpes']

db_cursor.close()
db_conn.close()
Expand Down
34 changes: 34 additions & 0 deletions web_server_files/static/css/daisyui.css
Original file line number Diff line number Diff line change
Expand Up @@ -991,6 +991,12 @@ html {
--tw-bg-opacity: 1;
background-color: var(--fallback-b2,oklch(var(--b2)/var(--tw-bg-opacity)));
}

.table-zebra tr.hover:hover,
.table-zebra tr.hover:nth-child(even):hover {
--tw-bg-opacity: 1;
background-color: var(--fallback-b3,oklch(var(--b3)/var(--tw-bg-opacity)));
}
}

.btn {
Expand Down Expand Up @@ -1549,6 +1555,11 @@ html {
background-color: var(--fallback-b1,oklch(var(--b1)/var(--tw-bg-opacity)));
}

.table-zebra tbody tr:nth-child(even) :where(.table-pin-cols tr th) {
--tw-bg-opacity: 1;
background-color: var(--fallback-b2,oklch(var(--b2)/var(--tw-bg-opacity)));
}

.btm-nav > *.disabled,
.btm-nav > *[disabled] {
pointer-events: none;
Expand Down Expand Up @@ -2207,6 +2218,13 @@ html {
background-color: var(--fallback-b2,oklch(var(--b2)/var(--tw-bg-opacity)));
}

.table-zebra tr.active,
.table-zebra tr.active:nth-child(even),
.table-zebra-zebra tbody tr:nth-child(even) {
--tw-bg-opacity: 1;
background-color: var(--fallback-b3,oklch(var(--b3)/var(--tw-bg-opacity)));
}

.table :where(thead, tbody) :where(tr:not(:last-child)),
.table :where(thead, tbody) :where(tr:first-child:last-child) {
border-bottom-width: 1px;
Expand Down Expand Up @@ -2659,6 +2677,10 @@ html {
min-width: 100%;
}

.max-w-96 {
max-width: 24rem;
}

.-translate-x-1\/2 {
--tw-translate-x: -50%;
transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));
Expand Down Expand Up @@ -2704,6 +2726,10 @@ html {
overflow-y: auto;
}

.whitespace-nowrap {
white-space: nowrap;
}

.text-nowrap {
text-wrap: nowrap;
}
Expand All @@ -2720,6 +2746,14 @@ html {
border-radius: 0.5rem;
}

.rounded-tl-xl {
border-top-left-radius: 0.75rem;
}

.rounded-bl-xl {
border-bottom-left-radius: 0.75rem;
}

.border-none {
border-style: none;
}
Expand Down
17 changes: 11 additions & 6 deletions web_server_files/static/css/search_vulns.css
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@


/* Uncertain vulnerability table row (adapted from DaisyUI CSS) */
tr.uncertain-vuln {
tr.uncertain-vuln td {
color: var(--fallback-bc,oklch(var(--bc)/0.75));
background-color: var(--fallback-wa,oklch(var(--wa)/0.15));
}
Expand Down Expand Up @@ -53,18 +53,19 @@ tr.uncertain-vuln {


/* Rounded table */

.table-rounded thead tr th:first-child {
border-radius: 0.8rem 0 0 0;
}

.table-rounded thead tr {
border-radius: 0.8rem 0 0 0.8rem;
}

.table-rounded thead tr th:last-child {
border-radius: 0 0.8rem 0 0;
}

.table-rounded thead tr th:first-child:last-child {
border-radius: 0.8rem 0.8rem 0 0;
}

.table-rounded tr:last-child td:first-child {
border-radius: 0 0 0 0.8rem;
}
Expand All @@ -73,9 +74,13 @@ tr.uncertain-vuln {
border-radius: 0 0 0.8rem 0;
}

.table-rounded tr:last-child td:first-child:last-child {
border-radius: 0 0 0.8rem 0.8rem;
}


/* Overwrite Tailwind striping for uncetain-vuln table row */
.my-table-zebra tbody tr:nth-child(even):not(.uncertain-vuln) {
.my-table-zebra tbody tr:nth-child(even):not(.uncertain-vuln) td {
--tw-bg-opacity: 1;
background-color: var(--fallback-b2,oklch(var(--b2)/var(--tw-bg-opacity)));
}
51 changes: 30 additions & 21 deletions web_server_files/static/js/search_vulns.js
Original file line number Diff line number Diff line change
Expand Up @@ -96,10 +96,10 @@ function createVulnTableRowHtml(idx, vuln) {
if (vuln.vuln_match_reason == "general_cpe" || vuln.vuln_match_reason == "single_higher_version_cpe")
uncertain_vuln_class = "uncertain-vuln";

vuln_row_html += `<tr class="${uncertain_vuln_class}">`;
vuln_row_html += `<tr class="${uncertain_vuln_class} border-none">`;

if (selectedColumns.includes('cve')) {
vuln_row_html += `<td class="text-nowrap pr-2 relative"><a href="${htmlEntities(vuln["href"])}" target="_blank" style="color: inherit;">${vuln["id"]}&nbsp;&nbsp;<i class="fa-solid fa-up-right-from-square" style="font-size: 0.92rem"></i></a>`;
vuln_row_html += `<td class="text-nowrap whitespace-nowrap pr-2 relative"><a href="${htmlEntities(vuln["href"])}" target="_blank" style="color: inherit;">${vuln["id"]}&nbsp;&nbsp;<i class="fa-solid fa-up-right-from-square" style="font-size: 0.92rem"></i></a>`;
if (vuln.vuln_match_reason == "general_cpe")
vuln_row_html += `<br><center><span data-tooltip-target="tooltip-general-${idx}" data-tooltip-placement="bottom"><i class="fas fa-info-circle text-warning"></i></span><div id="tooltip-general-${idx}" role="tooltip" class="tooltip relative z-10 w-80 p-2 text-sm invisible rounded-lg shadow-sm opacity-0 bg-base-300" style="white-space:pre-wrap">This vulnerability affects the queried software in general and could be a false positive.<div class="tooltip-arrow" data-popper-arrow></div></div></center>`;
else if (vuln.vuln_match_reason == "single_higher_version_cpe")
Expand All @@ -121,7 +121,7 @@ function createVulnTableRowHtml(idx, vuln) {
cvss_badge_css = "badge-medium";
else if (cvss < 4.0 && cvss >= 0.1)
cvss_badge_css = "badge-low";
vuln_row_html += `<td class="text-nowrap"><div class="dropdown dropdown-hover"><div class="badge p-1.5 border-none badge-cvss ${cvss_badge_css} text-center ${uncertain_vuln_class}" tabindex="0">${vuln["cvss"]}&nbsp;(v${vuln["cvss_ver"]})</div><div tabindex="0" class="dropdown-content menu m-0 p-1 shadow bg-base-300 rounded-box"><div class="btn btn-ghost btn-xs" onclick="copyToClipboardCVSS(this)"><span><span><i class="fa-solid fa-clipboard"></i></span>&nbsp;&nbsp;<b>${cvss_vector}</b></span></div></div></div></td>`;
vuln_row_html += `<td class="text-nowrap whitespace-nowrap"><div class="dropdown dropdown-hover"><div class="badge p-1.5 border-none badge-cvss ${cvss_badge_css} text-center ${uncertain_vuln_class}" tabindex="0">${vuln["cvss"]}&nbsp;(v${vuln["cvss_ver"]})</div><div tabindex="0" class="dropdown-content menu m-0 p-1 shadow bg-base-300 rounded-box"><div class="btn btn-ghost btn-xs" onclick="copyToClipboardCVSS(this)"><span><span><i class="fa-solid fa-clipboard"></i></span>&nbsp;&nbsp;<b>${cvss_vector}</b></span></div></div></div></td>`;
}

if (selectedColumns.includes('descr')) {
Expand All @@ -142,7 +142,7 @@ function createVulnTableRowHtml(idx, vuln) {
exploits.push(`<a href="${vuln.exploits[j].replace('"', '&quot;')}" target="_blank" style="color: inherit;">${htmlEntities(exploit_url_show)}</a>`);
}
}
vuln_row_html += `<td class="text-nowrap">${exploits.join("<br>")}</td>`;
vuln_row_html += `<td class="text-nowrap whitespace-nowrap">${exploits.join("<br>")}</td>`;
}

vuln_row_html += "</tr>";
Expand Down Expand Up @@ -194,19 +194,19 @@ function renderSearchResults(reloadFilterDropdown) {
}

vulns_html = '<table class="table table-sm my-table-zebra table-rounded">';
vulns_html += '<thead class="bg-base-300">';
vulns_html += '<thead>';
vulns_html += '<tr>'
if (selectedColumns.includes('cve')) {
vulns_html += `<th onclick="${sortFunctionCVEID}" style="white-space: nowrap;">CVE-ID&nbsp;&nbsp;${sortIconCVEID}</th>`;
vulns_html += `<th class="bg-base-300" onclick="${sortFunctionCVEID}" style="white-space: nowrap;">CVE-ID&nbsp;&nbsp;${sortIconCVEID}</th>`;
}
if (selectedColumns.includes('cvss')) {
vulns_html += `<th onclick="${sortFunctionCVSS}" style="white-space: nowrap;">CVSS&nbsp;&nbsp;${sortIconCVSS}</th>`;
vulns_html += `<th class="bg-base-300" onclick="${sortFunctionCVSS}" style="white-space: nowrap;">CVSS&nbsp;&nbsp;${sortIconCVSS}</th>`;
}
if (selectedColumns.includes('descr')) {
vulns_html += '<th>Description</th>'
vulns_html += '<th class="bg-base-300">Description</th>'
}
if (selectedColumns.includes('expl')) {
vulns_html += `<th onclick="${sortFunctionExploits}" style="white-space: nowrap;">Exploits&nbsp;&nbsp;${sortIconExploits}</th>`;
vulns_html += `<th class="bg-base-300" onclick="${sortFunctionExploits}" style="white-space: nowrap;">Exploits&nbsp;&nbsp;${sortIconExploits}</th>`;
}
vulns_html += "</tr></thead>";
vulns_html += "<tbody>";
Expand All @@ -227,7 +227,7 @@ function renderSearchResults(reloadFilterDropdown) {
}

// add CVE to filter
filter_vulns_html += `<div class="form-control filter-cves"><label class="label cursor-pointer py-1 gap-4"><span class="label-text text-nowrap">${vulns[i]["id"]}</span><input type="checkbox" class="checkbox" onclick="changeFilterCVEs()" ${checked_html} /></label></div>`;
filter_vulns_html += `<div class="form-control filter-cves"><label class="label cursor-pointer py-1 gap-4"><span class="label-text text-nowrap whitespace-nowrap">${vulns[i]["id"]}</span><input type="checkbox" class="checkbox" onclick="changeFilterCVEs()" ${checked_html} /></label></div>`;
}
vulns_html += "</tbody></table>";
if (has_vulns)
Expand Down Expand Up @@ -426,8 +426,12 @@ function buildTextualReprFromCPE(cpe) {
cpe_parts[4] = cpe_parts[4].substring(1);

product_title = cpe_parts[3].split('_').map(w => w[0].toUpperCase() + w.substring(1).toLowerCase()).join(' ') + ' ';
if (cpe_parts[4] && cpe_parts[4] != cpe_parts[3])
product_title += cpe_parts[4].split('_').map(w => w[0].toUpperCase() + w.substring(1).toLowerCase()).join(' ') + ' ';
if (cpe_parts[4] && cpe_parts[4] != cpe_parts[3]) {
var append_words = cpe_parts[4].split('_').map(w => w[0].toUpperCase() + w.substring(1).toLowerCase()).join(' ') + ' ';
append_words = append_words.replace(/^[\W_]+/, '');
append_words = append_words.charAt(0).toUpperCase() + append_words.slice(1);
product_title += append_words;
}

if (cpe_parts[5] && cpe_parts[5] != '-' && cpe_parts[5] != '*')
product_title += cpe_parts[5] + ' ';
Expand All @@ -437,7 +441,7 @@ function buildTextualReprFromCPE(cpe) {

cpe_parts.slice(7).forEach(cpe_part => {
if (cpe_part && cpe_part != '-' && cpe_part != '*') {
cpe_part = cpe_part[0].toUpperCase() + cpe_part.substring(1);
var cpe_part = cpe_part[0].toUpperCase() + cpe_part.substring(1);
cpe_condition += cpe_part.split('_').map(w => w[0].toUpperCase() + w.substring(1).toLowerCase()).join(' ') + ' ';
}
});
Expand Down Expand Up @@ -788,7 +792,7 @@ function doneTypingQuery () {
else if (cpeInfos.length > 0) {
var dropdownContent = '<ul class="menu menu-md p-1 bg-base-200 rounded-box">';
cpeInfos.forEach(function (cpeInfo) {
dropdownContent += `<li><a class="text-nowrap" onmousedown="location.href = '${window.location.pathname}?query=${encodeURIComponent(htmlEntities(cpeInfo[0]))}&is-good-cpe=false'">${htmlEntities(cpeInfo[0])}</a></li>`;
dropdownContent += `<li><a class="text-nowrap whitespace-nowrap" onmousedown="location.href = '${window.location.pathname}?query=${encodeURIComponent(htmlEntities(cpeInfo[0]))}&is-good-cpe=false'">${htmlEntities(cpeInfo[0])}</a></li>`;
});
dropdownContent += '</ul>';
$('#cpeSuggestions').html(dropdownContent);
Expand All @@ -807,7 +811,7 @@ function doneTypingQuery () {
console.log(errorMsg);
$("#buttonSearchVulns").removeClass("btn-disabled");
}
})
});
}

function closeCPESuggestions() {
Expand All @@ -820,8 +824,11 @@ function closeCPESuggestions() {
// enables the user to press return on the query text field to make the query
$("#query").keypress(function (event) {
var keycode = (event.keyCode ? event.keyCode : event.which);
if (keycode == "13" && !$("#buttonSearchVulns").hasClass("btn-disabled"))
$("#buttonSearchVulns").click();
if (keycode == "13") {
if (!$("#cpeSuggestions").hasClass("hidden") || !$("#buttonSearchVulns").hasClass("btn-disabled")) {
$("#buttonSearchVulns").click();
}
}
});

// set theme
Expand Down Expand Up @@ -861,10 +868,12 @@ queryInput.on('keyup', function () {
});

// on keydown, clear the typing countdown and hide dropdown
queryInput.on('keydown', function () {
clearTimeout(doneTypingQueryTimer);
$('#cpeSuggestions').addClass("hidden");
$('#cpeSuggestions').html();
queryInput.on('keydown', function (event) {
if (event.keyCode !== undefined && event.keyCode != 13) {
clearTimeout(doneTypingQueryTimer);
$('#cpeSuggestions').addClass("hidden");
$('#cpeSuggestions').html();
}
});

// focus on query input field at the beginning
Expand Down
2 changes: 1 addition & 1 deletion web_server_files/templates/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -218,7 +218,7 @@
</button>

<!-- Include classes only used by frontend JS for compiler
<div class="hidden text-error text-warning badge border-none text-success btn-xs text-warning loading loading-spinner bg-warning text-right tooltip relative z-10 w-80 p-2 p-1 text-sm invisible rounded-lg shadow-sm opacity-0 bg-base-300 table table-sm table-rounded btn-disabled mr-1 mr-2 shadow-base-300 btn-success btn-outline ml-1 ml-2 sm:ml-1 sm:ml-2 divider divider-info text-lg text-info text-left grid place-items-center list-disc text-nowrap p-1.5">
<div class="hidden text-error text-warning badge border-none text-success btn-xs text-warning loading loading-spinner bg-warning text-right tooltip relative z-10 w-80 p-2 p-1 text-sm invisible rounded-lg shadow-sm opacity-0 bg-base-300 table table-sm table-rounded btn-disabled mr-1 mr-2 shadow-base-300 btn-success btn-outline ml-1 ml-2 sm:ml-1 sm:ml-2 divider divider-info text-lg text-info text-left grid place-items-center list-disc text-nowrap whitespace-nowrap p-1.5">
</div>
-->

Expand Down

0 comments on commit b0cc188

Please sign in to comment.