fix: address security vulnerabilities from audit
- Path traversal: confine avatar file serving to avatar_dir (defaults to config file directory); validate on both read and write - UDP owner injection: server-configured owner now takes precedence over UDP-supplied value, matching the documented intent - Open redirect: reject non-relative next= values after login - Stored XSS: enable Jinja2 autoescape on all template environments; add escHtml() helper in live.html and apply to all innerHTML sinks sourced from network data (host names, addrs, states, log messages) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -321,9 +321,15 @@
|
||||
var c = 0;
|
||||
var HBD_VERSION = "{{ hbd_version }}";
|
||||
|
||||
function escHtml(s) {
|
||||
var d = document.createElement('div');
|
||||
d.textContent = String(s);
|
||||
return d.innerHTML;
|
||||
}
|
||||
|
||||
function hostNameHtml(data) {
|
||||
var rawName = data.raw_name || data.name.replace(/<[^>]+>/g, '').replace('*', '').trim();
|
||||
var nameHtml = data.name;
|
||||
var nameHtml = escHtml(data.name);
|
||||
if (!data.hbc_version || data.hbc_version !== HBD_VERSION) {
|
||||
nameHtml += ' 🥀';
|
||||
}
|
||||
@@ -410,11 +416,11 @@
|
||||
c_critical.innerHTML = "";
|
||||
}
|
||||
|
||||
c_ipv4addr.innerHTML = data.connections[0].addr;
|
||||
c_ipv4state.innerHTML = data.connections[0].state;
|
||||
c_ipv4addr.innerHTML = escHtml(data.connections[0].addr);
|
||||
c_ipv4state.innerHTML = escHtml(data.connections[0].state);
|
||||
if (data.connections.length > 1) {
|
||||
c_ipv6addr.innerHTML = data.connections[1].addr;
|
||||
c_ipv6state.innerHTML = data.connections[1].state;
|
||||
c_ipv6addr.innerHTML = escHtml(data.connections[1].addr);
|
||||
c_ipv6state.innerHTML = escHtml(data.connections[1].state);
|
||||
}
|
||||
var table = document.getElementById("ntablebody"); // find table to append to
|
||||
table.appendChild(row); // append row to table
|
||||
@@ -477,7 +483,7 @@
|
||||
|
||||
for (var i = 0; i < data.connections.length; i++) {
|
||||
// Offset by 2 for the warning/critical count columns
|
||||
name_idx[data.name].cells[3 + i * 4].innerHTML = data.connections[i].addr;
|
||||
name_idx[data.name].cells[3 + i * 4].innerHTML = escHtml(data.connections[i].addr);
|
||||
name_idx[data.name].cells[6 + i * 4].innerHTML = formatTS(
|
||||
data.connections[i].statetime
|
||||
);
|
||||
@@ -497,7 +503,7 @@
|
||||
state = '<span class="state-overdue">overdue</span>';
|
||||
latency = "-";
|
||||
} else {
|
||||
state = "<b>" + data.connections[i].state + "</b>";
|
||||
state = "<b>" + escHtml(data.connections[i].state) + "</b>";
|
||||
latency = "-";
|
||||
}
|
||||
}
|
||||
@@ -558,12 +564,12 @@
|
||||
+ ' ' + _p(_d.getHours()) + ':' + _p(_d.getMinutes()) + ':' + _p(_d.getSeconds());
|
||||
var lvl = (msg.level || "INFO").toLowerCase();
|
||||
var hostVal = msg.host || '';
|
||||
var html = '<div class="log-entry log-' + lvl + '" data-level="' + lvl + '" data-host="' + hostVal.replace(/"/g, '"') + '">';
|
||||
var html = '<div class="log-entry log-' + escHtml(lvl) + '" data-level="' + escHtml(lvl) + '" data-host="' + escHtml(hostVal) + '">';
|
||||
html += '<span class="log-ts">' + ts_str + '</span>';
|
||||
html += '<span class="log-level">' + (msg.level || "") + '</span>';
|
||||
if (msg.host) html += '<span class="log-host">' + msg.host + '</span>';
|
||||
if (msg.service) html += '<span class="log-service">' + msg.service + '</span>';
|
||||
html += '<span class="log-msg">' + msg.message + '</span>';
|
||||
html += '<span class="log-level">' + escHtml(msg.level || "") + '</span>';
|
||||
if (msg.host) html += '<span class="log-host">' + escHtml(msg.host) + '</span>';
|
||||
if (msg.service) html += '<span class="log-service">' + escHtml(msg.service) + '</span>';
|
||||
html += '<span class="log-msg">' + escHtml(msg.message) + '</span>';
|
||||
html += '</div>';
|
||||
msgs.insertAdjacentHTML(state.history ? "beforeend" : "afterbegin", html);
|
||||
applyLogFilters();
|
||||
|
||||
Reference in New Issue
Block a user