fix: harden auth rendering and parsing

This commit is contained in:
udo committed 2026-07-10 11:45:40 +00:00
1 parent 65e610d681
commit b034988dd4
10 files changed
+609 -34

No files matched your search

+5 -1
View File
@@ -1,5 +1,7 @@
import "@mariozechner/mini-lit/dist/ThemeToggle.js";
import "@mariozechner/mini-lit/dist/MarkdownBlock.js";
import "./app.css";
import { installMarkdownSecurityPatch } from "./markdown-security-patch.js";
import { html, render, nothing } from "lit";
import { icon } from "@mariozechner/mini-lit";
@@ -19,6 +21,7 @@ import {
void MessageList;
void MessageEditor;
void StreamingMessageContainer;
installMarkdownSecurityPatch();
installAssistantMetadataRenderer();
import type {
ClientMessage,
@@ -27,6 +30,7 @@ import type {
SerializedAgentState,
SessionListItem,
} from "../shared/protocol.js";
import { sanitizeMarkdownLinks } from "../shared/markdown-security.js";
function toolResultText(result: ToolResultMessage | undefined): string {
return result?.content
@@ -234,7 +238,7 @@ function installAssistantMetadataRenderer() {
for (const chunk of this.message.content) {
if (chunk.type === "text" && chunk.text.trim() !== "") {
orderedParts.push(html`<markdown-block .content=${chunk.text}></markdown-block>`);
orderedParts.push(html`<markdown-block .content=${sanitizeMarkdownLinks(chunk.text)}></markdown-block>`);
} else if (chunk.type === "thinking" && chunk.thinking.trim() !== "") {
orderedParts.push(html`<thinking-block .content=${chunk.thinking} .isStreaming=${this.isStreaming}></thinking-block>`);
} else if (chunk.type === "toolCall" && !this.hideToolCalls) {
+33
View File
@@ -0,0 +1,33 @@
import { isSafeMarkdownLink } from "../shared/markdown-security.js";
function sanitizeMarkdownElement(root: ParentNode) {
for (const anchor of root.querySelectorAll("a[href]")) {
const href = anchor.getAttribute("href") || "";
if (isSafeMarkdownLink(href)) continue;
anchor.removeAttribute("href");
anchor.setAttribute("aria-disabled", "true");
anchor.setAttribute("title", "Unsafe link blocked");
}
for (const image of root.querySelectorAll("img[src]")) {
const src = image.getAttribute("src") || "";
if (isSafeMarkdownLink(src) && !src.trim().toLowerCase().startsWith("mailto:") && !src.trim().toLowerCase().startsWith("tel:")) continue;
image.removeAttribute("src");
image.setAttribute("title", "Unsafe image source blocked");
}
}
/**
* mini-lit renders Marked output through unsafeHTML. Sanitize the resulting DOM
* after every markdown-block update so inline links, reference links, autolinks,
* images, thinking blocks, and artifact previews all use one URL policy.
*/
export function installMarkdownSecurityPatch() {
const MarkdownBlock = customElements.get("markdown-block") as any;
if (!MarkdownBlock || MarkdownBlock.prototype.__piWebuiLinkSecurityPatch) return;
MarkdownBlock.prototype.__piWebuiLinkSecurityPatch = true;
const originalUpdated = MarkdownBlock.prototype.updated;
MarkdownBlock.prototype.updated = function (...args: any[]) {
if (typeof originalUpdated === "function") originalUpdated.apply(this, args);
sanitizeMarkdownElement(this);
};
}