fix(front): fix XSS because of bad operations when setting the group's summary

Group summary (HTML) is properly sanitized by the backend, but for groups we did a special operation
before setting the HTML in the Vue app. This is now removed

Signed-off-by: Thomas Citharel <tcit@tcit.fr>
This commit is contained in:
Thomas Citharel 2023-12-06 08:47:28 +01:00
parent 935799f123
commit ded59bec27
No known key found for this signature in database
GPG key ID: A061B9DDE0CA0773
3 changed files with 3 additions and 16 deletions

View file

@ -40,7 +40,7 @@
<div <div
class="mb-2 line-clamp-3" class="mb-2 line-clamp-3"
dir="auto" dir="auto"
v-html="saneSummary" v-html="group.summary"
v-if="showSummary" v-if="showSummary"
/> />
<div> <div>
@ -91,7 +91,6 @@ import { addressFullName } from "@/types/address.model";
import { useI18n } from "vue-i18n"; import { useI18n } from "vue-i18n";
import AccountGroup from "vue-material-design-icons/AccountGroup.vue"; import AccountGroup from "vue-material-design-icons/AccountGroup.vue";
import Account from "vue-material-design-icons/Account.vue"; import Account from "vue-material-design-icons/Account.vue";
import { htmlToText } from "@/utils/html";
import { computed } from "vue"; import { computed } from "vue";
import LinkOrRouterLink from "../core/LinkOrRouterLink.vue"; import LinkOrRouterLink from "../core/LinkOrRouterLink.vue";
@ -108,8 +107,6 @@ const props = withDefaults(
const { t } = useI18n({ useScope: "global" }); const { t } = useI18n({ useScope: "global" });
const saneSummary = computed(() => htmlToText(props.group.summary ?? ""));
const isInternal = computed(() => { const isInternal = computed(() => {
return props.isRemoteGroup && props.isLoggedIn === false; return props.isRemoteGroup && props.isLoggedIn === false;
}); });

View file

@ -60,9 +60,9 @@
</div> </div>
</div> </div>
<div <div
class="mt-3 prose dark:prose-invert lg:prose-xl line-clamp-2" class="mt-3 prose dark:prose-invert lg:prose-xl prose-p:m-0 line-clamp-2"
v-if="member.parent.summary" v-if="member.parent.summary"
v-html="htmlToText(member.parent.summary)" v-html="member.parent.summary"
/> />
</div> </div>
<div> <div>
@ -95,7 +95,6 @@ import DotsHorizontal from "vue-material-design-icons/DotsHorizontal.vue";
import AccountGroup from "vue-material-design-icons/AccountGroup.vue"; import AccountGroup from "vue-material-design-icons/AccountGroup.vue";
import AccountCircle from "vue-material-design-icons/AccountCircle.vue"; import AccountCircle from "vue-material-design-icons/AccountCircle.vue";
import Tag from "@/components/TagElement.vue"; import Tag from "@/components/TagElement.vue";
import { htmlToText } from "@/utils/html";
import { useI18n } from "vue-i18n"; import { useI18n } from "vue-i18n";
defineProps<{ defineProps<{

View file

@ -2,15 +2,6 @@ export function nl2br(text: string): string {
return text.replace(/(?:\r\n|\r|\n)/g, "<br>"); return text.replace(/(?:\r\n|\r|\n)/g, "<br>");
} }
export function htmlToText(html: string) {
const template = document.createElement("template");
const trimmedHTML = html.trim();
template.innerHTML = trimmedHTML;
const text = template.content.textContent;
template.remove();
return text;
}
export const getValueFromMeta = (name: string): string | null => { export const getValueFromMeta = (name: string): string | null => {
const element = document.querySelector(`meta[name="${name}"]`); const element = document.querySelector(`meta[name="${name}"]`);
if (element && element.getAttribute("content")) { if (element && element.getAttribute("content")) {