')
- .append(`| ${user.username} | `)
- .append(`${user.role} | `)
- .append(`
-
-
- | `);
+ .append($('').text(user.username))
+ .append($(' | ').text(user.role))
+ .append(actions);
tbody.append(row);
});
diff --git a/src/rust/lqosd/src/node_manager/js_build/src/dashlets/circuit_capacity_dash.js b/src/rust/lqosd/src/node_manager/js_build/src/dashlets/circuit_capacity_dash.js
index ea33900d..72ffa0d1 100644
--- a/src/rust/lqosd/src/node_manager/js_build/src/dashlets/circuit_capacity_dash.js
+++ b/src/rust/lqosd/src/node_manager/js_build/src/dashlets/circuit_capacity_dash.js
@@ -1,5 +1,5 @@
import {BaseDashlet} from "../lq_js_common/dashboard/base_dashlet";
-import {clearDashDiv, simpleRowHtml, theading} from "../helpers/builders";
+import {clearDashDiv, simpleRowTrustedHtml, theading} from "../helpers/builders";
import {formatRtt, formatPercent} from "../helpers/scaling";
import {redactCell} from "../helpers/redact";
import {TimedCache} from "../lq_js_common/helpers/timed_cache";
@@ -87,9 +87,9 @@ export class CircuitCapacityDash extends BaseDashlet {
linkCol.appendChild(link);
row.appendChild(linkCol);
- row.appendChild(simpleRowHtml(formatPercent(c.capacity[0]*100)));
- row.appendChild(simpleRowHtml(formatPercent(c.capacity[1]*100)));
- row.appendChild(simpleRowHtml(formatRtt(c.rtt)));
+ row.appendChild(simpleRowTrustedHtml(formatPercent(c.capacity[0]*100)));
+ row.appendChild(simpleRowTrustedHtml(formatPercent(c.capacity[1]*100)));
+ row.appendChild(simpleRowTrustedHtml(formatRtt(c.rtt)));
tbody.appendChild(row);
count++;
diff --git a/src/rust/lqosd/src/node_manager/js_build/src/dashlets/ltsShaperStatus.js b/src/rust/lqosd/src/node_manager/js_build/src/dashlets/ltsShaperStatus.js
index 3b12f4fc..d24b787e 100644
--- a/src/rust/lqosd/src/node_manager/js_build/src/dashlets/ltsShaperStatus.js
+++ b/src/rust/lqosd/src/node_manager/js_build/src/dashlets/ltsShaperStatus.js
@@ -1,5 +1,5 @@
import {BaseDashlet} from "../lq_js_common/dashboard/base_dashlet";
-import {clearDiv, simpleRow, simpleRowHtml, theading} from "../helpers/builders";
+import {clearDiv, simpleRow, simpleRowTrustedHtml, theading} from "../helpers/builders";
import {get_ws_client} from "../pubsub/ws";
const wsClient = get_ws_client();
@@ -74,7 +74,7 @@ export class LtsShaperStatus extends BaseDashlet {
} else if (row.last_seen_seconds_ago > 120) {
color = "orange";
}
- tr.appendChild(simpleRowHtml(`■`));
+ tr.appendChild(simpleRowTrustedHtml(`■`));
tr.appendChild(simpleRow(row.name));
tr.appendChild(simpleRow(row.last_seen_seconds_ago + "s"));
tbody.appendChild(tr);
diff --git a/src/rust/lqosd/src/node_manager/js_build/src/dashlets/top10_downloaders.js b/src/rust/lqosd/src/node_manager/js_build/src/dashlets/top10_downloaders.js
index 52a4f875..c08e162c 100644
--- a/src/rust/lqosd/src/node_manager/js_build/src/dashlets/top10_downloaders.js
+++ b/src/rust/lqosd/src/node_manager/js_build/src/dashlets/top10_downloaders.js
@@ -1,4 +1,11 @@
-import {clearDashDiv, simpleRow, simpleRowHtml, theading, TopNTableFromMsgData} from "../helpers/builders";
+import {
+ clearDashDiv,
+ simpleLinkRow,
+ simpleRow,
+ simpleRowTrustedHtml,
+ theading,
+ TopNTableFromMsgData,
+} from "../helpers/builders";
import {TimedCache} from "../lq_js_common/helpers/timed_cache";
import {periodNameToSeconds} from "../helpers/time_periods";
import {formatRetransmit, formatRtt} from "../helpers/scaling";
@@ -94,15 +101,19 @@ export class Top10Downloaders extends DashletBaseInsight {
//console.log(row);
let tr = document.createElement("tr");
tr.classList.add("small");
- tr.appendChild(simpleRowHtml("" + row.circuit_name + ""));
+ tr.appendChild(simpleLinkRow(
+ `circuit.html?circuit=${encodeURIComponent(row.circuit_hash ?? "")}`,
+ row.circuit_name,
+ true,
+ ));
tr.appendChild(simpleRow(scaleNumber(toNumber(row.bytes_down, 0) * 1000000, 0)));
if (row.rtt !== null) {
- tr.appendChild(simpleRowHtml(formatRtt(row.rtt)));
+ tr.appendChild(simpleRowTrustedHtml(formatRtt(row.rtt)));
} else {
tr.appendChild(simpleRow("-"));
}
if (row.rxmit !== null) {
- tr.appendChild(simpleRowHtml(formatRetransmit(row.rxmit)));
+ tr.appendChild(simpleRowTrustedHtml(formatRetransmit(row.rxmit)));
} else {
tr.appendChild(simpleRow("-"));
}
diff --git a/src/rust/lqosd/src/node_manager/js_build/src/dashlets/top10flows_bytes.js b/src/rust/lqosd/src/node_manager/js_build/src/dashlets/top10flows_bytes.js
index c5c10d39..3a633f59 100644
--- a/src/rust/lqosd/src/node_manager/js_build/src/dashlets/top10flows_bytes.js
+++ b/src/rust/lqosd/src/node_manager/js_build/src/dashlets/top10flows_bytes.js
@@ -1,4 +1,4 @@
-import {clearDashDiv, simpleRow, simpleRowHtml, theading} from "../helpers/builders";
+import {clearDashDiv, simpleRow, simpleRowTrustedHtml, theading} from "../helpers/builders";
import {scaleNumber, toNumber} from "../lq_js_common/helpers/scaling";
import {formatRtt} from "../helpers/scaling";
import {TrimToFit} from "../lq_js_common/helpers/text_utils";
@@ -202,27 +202,27 @@ export class Top10FlowsBytes extends DashletBaseInsight {
tr.classList.add("small");
tr.appendChild(simpleRow(row.circuit_name || "", true));
tr.appendChild(simpleRow(row.protocol));
- tr.appendChild(simpleRowHtml(scaleNumber(row.bytes_down)));
- tr.appendChild(simpleRowHtml(scaleNumber(row.bytes_up)));
+ tr.appendChild(simpleRowTrustedHtml(scaleNumber(row.bytes_down)));
+ tr.appendChild(simpleRowTrustedHtml(scaleNumber(row.bytes_up)));
if (row.rtt_down === null) {
- tr.appendChild(simpleRowHtml("-"));
+ tr.appendChild(simpleRowTrustedHtml("-"));
} else {
- tr.appendChild(simpleRowHtml(formatRtt(row.rtt_down)));
+ tr.appendChild(simpleRowTrustedHtml(formatRtt(row.rtt_down)));
}
if (row.rtt_up === null) {
- tr.appendChild(simpleRowHtml("-"));
+ tr.appendChild(simpleRowTrustedHtml("-"));
} else {
- tr.appendChild(simpleRowHtml(formatRtt(row.rtt_up)));
+ tr.appendChild(simpleRowTrustedHtml(formatRtt(row.rtt_up)));
}
if (row.rxmit_down === null) {
- tr.appendChild(simpleRowHtml("-"));
+ tr.appendChild(simpleRowTrustedHtml("-"));
} else {
- tr.appendChild(simpleRowHtml(formatRetransmit(row.rxmit_down)));
+ tr.appendChild(simpleRowTrustedHtml(formatRetransmit(row.rxmit_down)));
}
if (row.rxmit_up === null) {
- tr.appendChild(simpleRowHtml("-"));
+ tr.appendChild(simpleRowTrustedHtml("-"));
} else {
- tr.appendChild(simpleRowHtml(formatRetransmit(row.rxmit_up)));
+ tr.appendChild(simpleRowTrustedHtml(formatRetransmit(row.rxmit_up)));
}
const asnCell = document.createElement("td");
asnCell.classList.add("lqos-asn-cell");
diff --git a/src/rust/lqosd/src/node_manager/js_build/src/dashlets/top_tree_summary.js b/src/rust/lqosd/src/node_manager/js_build/src/dashlets/top_tree_summary.js
index 7eb3e231..cfc47aea 100644
--- a/src/rust/lqosd/src/node_manager/js_build/src/dashlets/top_tree_summary.js
+++ b/src/rust/lqosd/src/node_manager/js_build/src/dashlets/top_tree_summary.js
@@ -1,4 +1,4 @@
-import {clearDiv, simpleRowHtml, theading} from "../helpers/builders";
+import {clearDiv, simpleRowTrustedHtml, theading} from "../helpers/builders";
import {formatThroughput, formatRetransmit, formatCakeStat} from "../helpers/scaling";
import {toNumber} from "../lq_js_common/helpers/scaling";
import {DashletBaseInsight} from "./insight_dashlet_base";
@@ -89,8 +89,8 @@ export class TopTreeSummary extends DashletBaseInsight {
const tpDown = toNumber(r[1].current_throughput[0], 0) * 8;
const tpUp = toNumber(r[1].current_throughput[1], 0) * 8;
const maxThroughput = effectiveMax(r[1]);
- row.appendChild(simpleRowHtml(formatThroughput(tpDown, maxThroughput[0])));
- row.appendChild(simpleRowHtml(formatThroughput(tpUp, maxThroughput[1])));
+ row.appendChild(simpleRowTrustedHtml(formatThroughput(tpDown, maxThroughput[0])));
+ row.appendChild(simpleRowTrustedHtml(formatThroughput(tpUp, maxThroughput[1])));
const tcpPacketsDown = retransmitPacketsForNode(r[1], 0);
const tcpPacketsUp = retransmitPacketsForNode(r[1], 1);
@@ -98,19 +98,19 @@ export class TopTreeSummary extends DashletBaseInsight {
const retransmitsUp = toNumber(r[1].current_retransmits[1], 0);
if (tcpPacketsDown > 0) {
- row.appendChild(simpleRowHtml(formatRetransmit(retransmitsDown / tcpPacketsDown)))
+ row.appendChild(simpleRowTrustedHtml(formatRetransmit(retransmitsDown / tcpPacketsDown)))
} else {
- row.appendChild(simpleRowHtml(""));
+ row.appendChild(simpleRowTrustedHtml(""));
}
if (tcpPacketsUp > 0) {
- row.appendChild(simpleRowHtml(formatRetransmit(retransmitsUp / tcpPacketsUp)))
+ row.appendChild(simpleRowTrustedHtml(formatRetransmit(retransmitsUp / tcpPacketsUp)))
} else {
- row.appendChild(simpleRowHtml(""));
+ row.appendChild(simpleRowTrustedHtml(""));
}
- row.appendChild(simpleRowHtml(formatCakeStat(r[1].current_marks[0])))
- row.appendChild(simpleRowHtml(formatCakeStat(r[1].current_marks[1])))
- row.appendChild(simpleRowHtml(formatCakeStat(r[1].current_drops[0])))
- row.appendChild(simpleRowHtml(formatCakeStat(r[1].current_drops[1])))
+ row.appendChild(simpleRowTrustedHtml(formatCakeStat(r[1].current_marks[0])))
+ row.appendChild(simpleRowTrustedHtml(formatCakeStat(r[1].current_marks[1])))
+ row.appendChild(simpleRowTrustedHtml(formatCakeStat(r[1].current_drops[0])))
+ row.appendChild(simpleRowTrustedHtml(formatCakeStat(r[1].current_drops[1])))
target.appendChild(row);
});
}
diff --git a/src/rust/lqosd/src/node_manager/js_build/src/dashlets/tree_capacity_dash.js b/src/rust/lqosd/src/node_manager/js_build/src/dashlets/tree_capacity_dash.js
index 308d379f..e70567b4 100644
--- a/src/rust/lqosd/src/node_manager/js_build/src/dashlets/tree_capacity_dash.js
+++ b/src/rust/lqosd/src/node_manager/js_build/src/dashlets/tree_capacity_dash.js
@@ -1,4 +1,4 @@
-import {clearDashDiv, simpleRowHtml, theading} from "../helpers/builders";
+import {clearDashDiv, simpleRowTrustedHtml, theading} from "../helpers/builders";
import {formatRtt, formatPercent} from "../helpers/scaling";
import {DashletBaseInsight} from "./insight_dashlet_base";
@@ -74,9 +74,9 @@ export class TreeCapacityDash extends DashletBaseInsight {
linkCol.appendChild(link);
row.appendChild(linkCol);
- row.appendChild(simpleRowHtml(formatPercent(down*100)));
- row.appendChild(simpleRowHtml(formatPercent(up*100)));
- row.appendChild(simpleRowHtml(formatRtt(node.rtt)));
+ row.appendChild(simpleRowTrustedHtml(formatPercent(down*100)));
+ row.appendChild(simpleRowTrustedHtml(formatPercent(up*100)));
+ row.appendChild(simpleRowTrustedHtml(formatRtt(node.rtt)));
tbody.appendChild(row);
});
diff --git a/src/rust/lqosd/src/node_manager/js_build/src/dashlets/worst10_downloaders.js b/src/rust/lqosd/src/node_manager/js_build/src/dashlets/worst10_downloaders.js
index 85c46f07..aebf4643 100644
--- a/src/rust/lqosd/src/node_manager/js_build/src/dashlets/worst10_downloaders.js
+++ b/src/rust/lqosd/src/node_manager/js_build/src/dashlets/worst10_downloaders.js
@@ -1,4 +1,11 @@
-import {clearDashDiv, simpleRow, simpleRowHtml, theading, TopNTableFromMsgData} from "../helpers/builders";
+import {
+ clearDashDiv,
+ simpleLinkRow,
+ simpleRow,
+ simpleRowTrustedHtml,
+ theading,
+ TopNTableFromMsgData,
+} from "../helpers/builders";
import {TimedCache} from "../lq_js_common/helpers/timed_cache";
import {periodNameToSeconds} from "../helpers/time_periods";
import {formatRetransmit, formatRtt} from "../helpers/scaling";
@@ -94,15 +101,19 @@ export class Worst10Downloaders extends DashletBaseInsight {
//console.log(row);
let tr = document.createElement("tr");
tr.classList.add("small");
- tr.appendChild(simpleRowHtml("" + row.circuit_name + ""));
+ tr.appendChild(simpleLinkRow(
+ `circuit.html?circuit=${encodeURIComponent(row.circuit_hash ?? "")}`,
+ row.circuit_name,
+ true,
+ ));
tr.appendChild(simpleRow(scaleNumber(toNumber(row.bytes_down, 0) * 1000000, 0)));
if (row.rtt !== null) {
- tr.appendChild(simpleRowHtml(formatRtt(row.rtt)));
+ tr.appendChild(simpleRowTrustedHtml(formatRtt(row.rtt)));
} else {
tr.appendChild(simpleRow("-"));
}
if (row.rxmit !== null) {
- tr.appendChild(simpleRowHtml(formatRetransmit(row.rxmit)));
+ tr.appendChild(simpleRowTrustedHtml(formatRetransmit(row.rxmit)));
} else {
tr.appendChild(simpleRow("-"));
}
diff --git a/src/rust/lqosd/src/node_manager/js_build/src/dashlets/worst10_retransmits.js b/src/rust/lqosd/src/node_manager/js_build/src/dashlets/worst10_retransmits.js
index 7d9d8730..84211db2 100644
--- a/src/rust/lqosd/src/node_manager/js_build/src/dashlets/worst10_retransmits.js
+++ b/src/rust/lqosd/src/node_manager/js_build/src/dashlets/worst10_retransmits.js
@@ -1,4 +1,11 @@
-import {clearDashDiv, simpleRow, simpleRowHtml, theading, TopNTableFromMsgData} from "../helpers/builders";
+import {
+ clearDashDiv,
+ simpleLinkRow,
+ simpleRow,
+ simpleRowTrustedHtml,
+ theading,
+ TopNTableFromMsgData,
+} from "../helpers/builders";
import {TimedCache} from "../lq_js_common/helpers/timed_cache";
import {periodNameToSeconds} from "../helpers/time_periods";
import {formatRetransmit, formatRtt, retransmitFractionFromSample} from "../helpers/scaling";
@@ -98,15 +105,19 @@ export class Worst10Retransmits extends DashletBaseInsight {
//console.log(row);
let tr = document.createElement("tr");
tr.classList.add("small");
- tr.appendChild(simpleRowHtml("" + row.circuit_name + ""));
+ tr.appendChild(simpleLinkRow(
+ `circuit.html?circuit=${encodeURIComponent(row.circuit_hash ?? "")}`,
+ row.circuit_name,
+ true,
+ ));
tr.appendChild(simpleRow(scaleNumber(toNumber(row.bytes_down, 0) * 1000000, 0)));
if (row.rtt !== null) {
- tr.appendChild(simpleRowHtml(formatRtt(row.rtt)));
+ tr.appendChild(simpleRowTrustedHtml(formatRtt(row.rtt)));
} else {
tr.appendChild(simpleRow("-"));
}
if (row.rxmit !== null) {
- tr.appendChild(simpleRowHtml(formatRetransmit(row.rxmit)));
+ tr.appendChild(simpleRowTrustedHtml(formatRetransmit(row.rxmit)));
} else {
tr.appendChild(simpleRow("-"));
}
diff --git a/src/rust/lqosd/src/node_manager/js_build/src/helpers/builders.js b/src/rust/lqosd/src/node_manager/js_build/src/helpers/builders.js
index b73bb3cb..320b3791 100644
--- a/src/rust/lqosd/src/node_manager/js_build/src/helpers/builders.js
+++ b/src/rust/lqosd/src/node_manager/js_build/src/helpers/builders.js
@@ -7,6 +7,7 @@ import {
import {scaleNanos} from "../lq_js_common/helpers/scaling";
import {redactCell} from "./redact";
import {disposeTooltipsWithin, enableTooltipsWithin} from "../lq_js_common/helpers/tooltips";
+export {safeRelativeHref, simpleLinkRow} from "./safe_dom.mjs";
export function heading5Icon(icon, text) {
let h5 = document.createElement("h5");
@@ -42,7 +43,7 @@ export function simpleRow(text, redact=false) {
return td;
}
-export function simpleRowHtml(text) {
+export function simpleRowTrustedHtml(text) {
let td = document.createElement("td");
td.innerHTML = text;
return td;
diff --git a/src/rust/lqosd/src/node_manager/js_build/src/helpers/builders.test.mjs b/src/rust/lqosd/src/node_manager/js_build/src/helpers/builders.test.mjs
new file mode 100644
index 00000000..89310f50
--- /dev/null
+++ b/src/rust/lqosd/src/node_manager/js_build/src/helpers/builders.test.mjs
@@ -0,0 +1,75 @@
+import assert from "node:assert/strict";
+import test from "node:test";
+
+import {safeRelativeHref, simpleLinkRow} from "./safe_dom.mjs";
+
+class FakeClassList {
+ constructor() {
+ this.values = [];
+ }
+
+ add(...classes) {
+ this.values.push(...classes);
+ }
+
+ contains(className) {
+ return this.values.includes(className);
+ }
+}
+
+class FakeElement {
+ constructor(tagName) {
+ this.tagName = tagName.toUpperCase();
+ this.children = [];
+ this.classList = new FakeClassList();
+ this._textContent = "";
+ this._href = "";
+ }
+
+ appendChild(child) {
+ this.children.push(child);
+ }
+
+ set href(value) {
+ this._href = String(value);
+ }
+
+ get href() {
+ return this._href;
+ }
+
+ set textContent(value) {
+ this._textContent = String(value);
+ }
+
+ get textContent() {
+ return this._textContent;
+ }
+}
+
+test("safeRelativeHref rejects scriptable URL protocols", () => {
+ assert.equal(safeRelativeHref("javascript:alert(1)"), "#");
+ assert.equal(safeRelativeHref(" data:text/html,boom"), "#");
+ assert.equal(safeRelativeHref("https://example.invalid/circuit"), "#");
+ assert.equal(safeRelativeHref("//example.invalid/circuit"), "#");
+ assert.equal(safeRelativeHref("circuit.html?circuit=123"), "circuit.html?circuit=123");
+});
+
+test("simpleLinkRow renders operator text as text content", () => {
+ globalThis.document = {
+ createElement: (tagName) => new FakeElement(tagName),
+ };
+
+ const row = simpleLinkRow(
+ "javascript:alert(1)",
+ " ",
+ true,
+ );
+ const link = row.children[0];
+
+ assert.equal(row.tagName, "TD");
+ assert.equal(link.tagName, "A");
+ assert.equal(link.href, "#");
+ assert.equal(link.textContent, " ");
+ assert.equal(link.classList.contains("redactable"), true);
+});
diff --git a/src/rust/lqosd/src/node_manager/js_build/src/helpers/safe_dom.mjs b/src/rust/lqosd/src/node_manager/js_build/src/helpers/safe_dom.mjs
new file mode 100644
index 00000000..62ed25b6
--- /dev/null
+++ b/src/rust/lqosd/src/node_manager/js_build/src/helpers/safe_dom.mjs
@@ -0,0 +1,20 @@
+export function safeRelativeHref(href) {
+ const value = String(href ?? "");
+ const trimmed = value.trimStart();
+ if (/^[a-z][a-z0-9+.-]*:/i.test(trimmed) || trimmed.startsWith("//")) {
+ return "#";
+ }
+ return value;
+}
+
+export function simpleLinkRow(href, text, redact=false) {
+ let td = document.createElement("td");
+ let link = document.createElement("a");
+ link.href = safeRelativeHref(href);
+ link.textContent = text ?? "";
+ if (redact) {
+ link.classList.add("redactable");
+ }
+ td.appendChild(link);
+ return td;
+}
diff --git a/src/rust/lqosd/src/node_manager/js_build/src/template.js b/src/rust/lqosd/src/node_manager/js_build/src/template.js
index 62bdb72c..f556c064 100644
--- a/src/rust/lqosd/src/node_manager/js_build/src/template.js
+++ b/src/rust/lqosd/src/node_manager/js_build/src/template.js
@@ -1,4 +1,4 @@
-import {clearDiv} from "./helpers/builders";
+import {clearDiv, safeRelativeHref} from "./helpers/builders";
import {initRedact} from "./helpers/redact";
import {initDayNightMode} from "./helpers/dark_mode";
import {initColorBlind} from "./helpers/colorblind";
@@ -794,11 +794,23 @@ function renderSearchResults(data) {
let c = document.createElement("td");
if (item.Circuit !== undefined) {
- c.innerHTML = " " + item.Circuit.name + "";
+ c.appendChild(searchResultLink(
+ `/circuit.html?id=${encodeURIComponent(item.Circuit.id ?? "")}`,
+ ["fa", "fa-user"],
+ item.Circuit.name,
+ ));
} else if (item.Device !== undefined) {
- c.innerHTML = " " + item.Device.name + "";
+ c.appendChild(searchResultLink(
+ `/circuit.html?id=${encodeURIComponent(item.Device.circuit_id ?? "")}`,
+ ["fa", "fa-computer"],
+ item.Device.name,
+ ));
} else if (item.Site !== undefined) {
- c.innerHTML = " " + item.Site.name + "";
+ c.appendChild(searchResultLink(
+ `/tree.html?parent=${encodeURIComponent(item.Site.idx ?? "")}`,
+ ["fa", "fa-building"],
+ item.Site.name,
+ ));
} else {
console.log(item);
c.innerText = item;
@@ -814,6 +826,18 @@ function renderSearchResults(data) {
searchResults.appendChild(wrap);
}
+function searchResultLink(href, iconClasses, label) {
+ const link = document.createElement("a");
+ link.classList.add("nav-link", "redactable");
+ link.href = safeRelativeHref(href);
+
+ const icon = document.createElement("i");
+ icon.classList.add(...iconClasses);
+ link.appendChild(icon);
+ link.appendChild(document.createTextNode(` ${label ?? ""}`));
+ return link;
+}
+
function doSearch(search) {
if (search.length > 2) {
lastSearchTerm = search;
diff --git a/src/rust/lqosd/src/node_manager/js_build/src/tree.js b/src/rust/lqosd/src/node_manager/js_build/src/tree.js
index e3f4827c..e5e545f8 100644
--- a/src/rust/lqosd/src/node_manager/js_build/src/tree.js
+++ b/src/rust/lqosd/src/node_manager/js_build/src/tree.js
@@ -1,4 +1,4 @@
-import {clearDiv, clientTableHeader, formatLastSeen, simpleRow, simpleRowHtml, theading} from "./helpers/builders";
+import {clearDiv, clientTableHeader, formatLastSeen, simpleRow, simpleRowTrustedHtml, theading} from "./helpers/builders";
import {
formatCakeStat, formatCakeStatPercent,
formatRetransmit, formatRetransmitFraction, retransmitFractionFromSample,
@@ -1870,22 +1870,22 @@ function renderAttachedCircuitsRows(rows) {
tr.appendChild(ipCell);
tr.appendChild(simpleRow(formatLastSeen(toNumber(circuit.last_seen_nanos, 0))));
- tr.appendChild(simpleRowHtml(formatThroughput(toNumber(circuit.bytes_per_second?.down, 0) * 8, toNumber(circuit.plan_mbps?.down, 0))));
- tr.appendChild(simpleRowHtml(formatThroughput(toNumber(circuit.bytes_per_second?.up, 0) * 8, toNumber(circuit.plan_mbps?.up, 0))));
+ tr.appendChild(simpleRowTrustedHtml(formatThroughput(toNumber(circuit.bytes_per_second?.down, 0) * 8, toNumber(circuit.plan_mbps?.down, 0))));
+ tr.appendChild(simpleRowTrustedHtml(formatThroughput(toNumber(circuit.bytes_per_second?.up, 0) * 8, toNumber(circuit.plan_mbps?.up, 0))));
if (toNumber(circuit.rtt_current_p50_nanos?.down, 0) > 0) {
- tr.appendChild(simpleRowHtml(formatRtt(toNumber(circuit.rtt_current_p50_nanos?.down, 0) / 1_000_000)));
+ tr.appendChild(simpleRowTrustedHtml(formatRtt(toNumber(circuit.rtt_current_p50_nanos?.down, 0) / 1_000_000)));
} else {
tr.appendChild(simpleRow("-"));
}
if (toNumber(circuit.rtt_current_p50_nanos?.up, 0) > 0) {
- tr.appendChild(simpleRowHtml(formatRtt(toNumber(circuit.rtt_current_p50_nanos?.up, 0) / 1_000_000)));
+ tr.appendChild(simpleRowTrustedHtml(formatRtt(toNumber(circuit.rtt_current_p50_nanos?.up, 0) / 1_000_000)));
} else {
tr.appendChild(simpleRow("-"));
}
- tr.appendChild(simpleRowHtml(formatRetransmit(retransmitFractionFromSample(circuit.tcp_retransmit_sample?.down))));
- tr.appendChild(simpleRowHtml(formatRetransmit(retransmitFractionFromSample(circuit.tcp_retransmit_sample?.up))));
+ tr.appendChild(simpleRowTrustedHtml(formatRetransmit(retransmitFractionFromSample(circuit.tcp_retransmit_sample?.down))));
+ tr.appendChild(simpleRowTrustedHtml(formatRetransmit(retransmitFractionFromSample(circuit.tcp_retransmit_sample?.up))));
tbody.appendChild(tr);
});
diff --git a/src/rust/lqosd/src/node_manager/js_build/test-node-manager.sh b/src/rust/lqosd/src/node_manager/js_build/test-node-manager.sh
index 85f5d6de..613fb5a7 100755
--- a/src/rust/lqosd/src/node_manager/js_build/test-node-manager.sh
+++ b/src/rust/lqosd/src/node_manager/js_build/test-node-manager.sh
@@ -10,6 +10,7 @@ fi
echo "[test-node-manager] Running frontend contract tests..."
node --test \
+ "${SCRIPT_DIR}/src/helpers/builders.test.mjs" \
"${SCRIPT_DIR}/src/config/shaped_device_wire.test.mjs" \
"${SCRIPT_DIR}/src/config/config_interface_mtu.test.mjs" \
"${SCRIPT_DIR}/src/config/ssl_redirect.test.mjs" \
|