feat(linkUtils): add httpUrlHrefOrNull method for safe URL normalization and update NomadNetworkPage to utilize it

This commit is contained in:
Ivan
2026-04-23 04:44:25 -05:00
parent d51ad49ed4
commit c59e50a24e
3 changed files with 29 additions and 3 deletions
@@ -444,6 +444,7 @@
<script>
import MicronParser from "../../js/MicronParser";
import LinkUtils from "../../js/LinkUtils";
import { renderNomadPageByPath } from "../../js/NomadPageRenderer";
import DialogUtils from "../../js/DialogUtils";
import WebSocketConnection from "../../js/WebSocketConnection";
@@ -1625,9 +1626,10 @@ export default {
console.log(fieldData);
// open http urls in new tab
if (url.startsWith("http://") || url.startsWith("https://")) {
window.open(url, "_blank");
const httpHref =
typeof url === "string" ? LinkUtils.httpUrlHrefOrNull(url.trim()) : null;
if (httpHref) {
window.open(httpHref, "_blank", "noopener,noreferrer");
return;
}
+7
View File
@@ -25,6 +25,13 @@ function httpUrlHrefOrNull(core) {
}
export default class LinkUtils {
/**
* Returns canonical http(s) href or null if the string is not a safe remote URL.
*/
static httpUrlHrefOrNull(core) {
return httpUrlHrefOrNull(core);
}
static protectAnchors(text) {
const anchors = [];
const protectedText = text.replace(/<a\b[^>]*>[\s\S]*?<\/a>/gi, (anchor) => {
+17
View File
@@ -163,4 +163,21 @@ describe("LinkUtils.js", () => {
expect(Date.now() - start).toBeLessThan(200);
});
});
describe("httpUrlHrefOrNull", () => {
it("returns canonical https href", () => {
expect(LinkUtils.httpUrlHrefOrNull("https://example.com/path")).toBe(
"https://example.com/path"
);
});
it("returns null for javascript: payloads that start with https-looking junk", () => {
expect(LinkUtils.httpUrlHrefOrNull("https://example.com javascript:alert(1)")).toBeNull();
});
it("returns null for non-http schemes", () => {
expect(LinkUtils.httpUrlHrefOrNull("javascript:alert(1)")).toBeNull();
expect(LinkUtils.httpUrlHrefOrNull("file:///etc/passwd")).toBeNull();
});
});
});