Allow href="data:..." in config flow step description (#25559)

* Allow href="data:..." in config flow step description

* Update src/dialogs/config-flow/show-dialog-config-flow.ts
This commit is contained in:
Petar Petrov 2025-05-26 14:56:11 +03:00 committed by GitHub
parent 18aaa44d2d
commit da8d43f5d1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 48 additions and 24 deletions

View File

@ -26,6 +26,9 @@ class HaMarkdownElement extends ReactiveElement {
@property({ attribute: "allow-svg", type: Boolean }) public allowSvg = false; @property({ attribute: "allow-svg", type: Boolean }) public allowSvg = false;
@property({ attribute: "allow-data-url", type: Boolean })
public allowDataUrl = false;
@property({ type: Boolean }) public breaks = false; @property({ type: Boolean }) public breaks = false;
@property({ type: Boolean, attribute: "lazy-images" }) public lazyImages = @property({ type: Boolean, attribute: "lazy-images" }) public lazyImages =
@ -66,6 +69,7 @@ class HaMarkdownElement extends ReactiveElement {
return hash({ return hash({
content: this.content, content: this.content,
allowSvg: this.allowSvg, allowSvg: this.allowSvg,
allowDataUrl: this.allowDataUrl,
breaks: this.breaks, breaks: this.breaks,
}); });
} }
@ -79,6 +83,7 @@ class HaMarkdownElement extends ReactiveElement {
}, },
{ {
allowSvg: this.allowSvg, allowSvg: this.allowSvg,
allowDataUrl: this.allowDataUrl,
} }
); );

View File

@ -8,6 +8,9 @@ export class HaMarkdown extends LitElement {
@property({ attribute: "allow-svg", type: Boolean }) public allowSvg = false; @property({ attribute: "allow-svg", type: Boolean }) public allowSvg = false;
@property({ attribute: "allow-data-url", type: Boolean })
public allowDataUrl = false;
@property({ type: Boolean }) public breaks = false; @property({ type: Boolean }) public breaks = false;
@property({ type: Boolean, attribute: "lazy-images" }) public lazyImages = @property({ type: Boolean, attribute: "lazy-images" }) public lazyImages =
@ -23,6 +26,7 @@ export class HaMarkdown extends LitElement {
return html`<ha-markdown-element return html`<ha-markdown-element
.content=${this.content} .content=${this.content}
.allowSvg=${this.allowSvg} .allowSvg=${this.allowSvg}
.allowDataUrl=${this.allowDataUrl}
.breaks=${this.breaks} .breaks=${this.breaks}
.lazyImages=${this.lazyImages} .lazyImages=${this.lazyImages}
.cache=${this.cache} .cache=${this.cache}

View File

@ -73,7 +73,12 @@ export const showConfigFlowDialog = (
); );
return description return description
? html` ? html`
<ha-markdown allow-svg breaks .content=${description}></ha-markdown> <ha-markdown
.allowDataUrl=${step.handler === "zwave_js"}
allow-svg
breaks
.content=${description}
></ha-markdown>
` `
: ""; : "";
}, },

View File

@ -7,34 +7,13 @@ import { filterXSS, getDefaultWhiteList } from "xss";
let whiteListNormal: IWhiteList | undefined; let whiteListNormal: IWhiteList | undefined;
let whiteListSvg: IWhiteList | undefined; let whiteListSvg: IWhiteList | undefined;
// Override the default `onTagAttr` behavior to only render
// our markdown checkboxes.
// Returning undefined causes the default measure to be taken
// in the xss library.
const onTagAttr = (
tag: string,
name: string,
value: string
): string | undefined => {
if (tag === "input") {
if (
(name === "type" && value === "checkbox") ||
name === "checked" ||
name === "disabled"
) {
return undefined;
}
return "";
}
return undefined;
};
const renderMarkdown = async ( const renderMarkdown = async (
content: string, content: string,
markedOptions: MarkedOptions, markedOptions: MarkedOptions,
hassOptions: { hassOptions: {
// Do not allow SVG on untrusted content, it allows XSS. // Do not allow SVG on untrusted content, it allows XSS.
allowSvg?: boolean; allowSvg?: boolean;
allowDataUrl?: boolean;
} = {} } = {}
): Promise<string> => { ): Promise<string> => {
if (!whiteListNormal) { if (!whiteListNormal) {
@ -70,10 +49,41 @@ const renderMarkdown = async (
} else { } else {
whiteList = whiteListNormal; whiteList = whiteListNormal;
} }
if (hassOptions.allowDataUrl && whiteList.a) {
whiteList.a.push("download");
}
return filterXSS(await marked(content, markedOptions), { return filterXSS(await marked(content, markedOptions), {
whiteList, whiteList,
onTagAttr, onTagAttr: (
tag: string,
name: string,
value: string
): string | undefined => {
// Override the default `onTagAttr` behavior to only render
// our markdown checkboxes.
// Returning undefined causes the default measure to be taken
// in the xss library.
if (tag === "input") {
if (
(name === "type" && value === "checkbox") ||
name === "checked" ||
name === "disabled"
) {
return undefined;
}
return "";
}
if (
hassOptions.allowDataUrl &&
tag === "a" &&
name === "href" &&
value.startsWith("data:")
) {
return `href="${value}"`;
}
return undefined;
},
}); });
}; };