Disgusting hacks to allow video embed and links

... before we either use a different sanitization lib or figure out how to move the sanitization before the markdown renderer without destroying plain text
This commit is contained in:
Nassim Jahnke 2022-07-24 13:08:43 +02:00
parent d21dd69987
commit 4dc4c37559
No known key found for this signature in database
GPG Key ID: 6BE3B555EBC5982B
5 changed files with 86 additions and 25 deletions

View File

@ -130,16 +130,16 @@ async function requireGlobalPerm(authStore: ReturnType<typeof useAuthStore>, to:
permissions: toNamedPermission(to.meta.requireGlobalPerm as string[]),
}).catch((e) => {
try {
console.log("erro!", e);
console.log("error!", e);
handleRequestError(e, useContext(), useI18n());
} catch (e2) {
console.log("error while checking perm", e);
console.log("encountered additional error while error handling", e2);
}
});
console.log("result", check);
console.debug("result", check);
if (check && (check.type !== PermissionType.GLOBAL || !check.result)) {
console.log("404?");
console.debug("404?");
return useErrorRedirect(to, 404, "Not found");
}
}

View File

@ -26,7 +26,7 @@ import com.vladsch.flexmark.parser.Parser;
import com.vladsch.flexmark.util.ast.Node;
import com.vladsch.flexmark.util.data.MutableDataSet;
import io.papermc.hangar.config.hangar.HangarConfig;
import io.papermc.hangar.util.HtmlSanitizerUtil;
import io.papermc.hangar.util.HtmlSanitizer;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.springframework.beans.factory.annotation.Autowired;
@ -40,10 +40,12 @@ public class MarkdownService {
private final Parser markdownParser;
private final MutableDataSet options;
private final HangarConfig config;
private final HtmlSanitizer sanitizer;
@Autowired
public MarkdownService(HangarConfig config) {
public MarkdownService(HangarConfig config, final HtmlSanitizer sanitizer) {
this.config = config;
this.sanitizer = sanitizer;
options = new MutableDataSet()
.set(AnchorLinkExtension.ANCHORLINKS_TEXT_SUFFIX, "<svg class=\"ml-2 text-xl\" preserveAspectRatio=\"xMidYMid meet\" viewBox=\"0 0 24 24\" width=\"1.2em\" height=\"1.2em\"><path fill=\"currentColor\" d=\"M10.59 13.41c.41.39.41 1.03 0 1.42c-.39.39-1.03.39-1.42 0a5.003 5.003 0 0 1 0-7.07l3.54-3.54a5.003 5.003 0 0 1 7.07 0a5.003 5.003 0 0 1 0 7.07l-1.49 1.49c.01-.82-.12-1.64-.4-2.42l.47-.48a2.982 2.982 0 0 0 0-4.24a2.982 2.982 0 0 0-4.24 0l-3.53 3.53a2.982 2.982 0 0 0 0 4.24m2.82-4.24c.39-.39 1.03-.39 1.42 0a5.003 5.003 0 0 1 0 7.07l-3.54 3.54a5.003 5.003 0 0 1-7.07 0a5.003 5.003 0 0 1 0-7.07l1.49-1.49c-.01.82.12 1.64.4 2.43l-.47.47a2.982 2.982 0 0 0 0 4.24a2.982 2.982 0 0 0 4.24 0l3.53-3.53a2.982 2.982 0 0 0 0-4.24a.973.973 0 0 1 0-1.42Z\"></path></svg>")
@ -62,12 +64,13 @@ public class MarkdownService {
AutolinkExtension.create(),
AnchorLinkExtension.create(),
StrikethroughExtension.create(),
TaskListExtension.create(),
TablesExtension.create(),
TypographicExtension.create(),
WikiLinkExtension.create(),
EmojiExtension.create(),
FootnoteExtension.create(),
//TODO readd after sanitization is fixed
//TaskListExtension.create(),
//FootnoteExtension.create(),
AdmonitionExtension.create(),
GitLabExtension.create(),
YouTubeLinkExtension.create(),
@ -102,7 +105,7 @@ public class MarkdownService {
// Render markdown and then sanitize html
input = htmlRenderer.render(markdownParser.parse(input));
return HtmlSanitizerUtil.sanitize(input);
return sanitizer.sanitize(input);
}
static class RenderSettings {

View File

@ -0,0 +1,71 @@
package io.papermc.hangar.util;
import io.papermc.hangar.config.hangar.HangarConfig;
import java.util.List;
import org.owasp.html.HtmlPolicyBuilder;
import org.owasp.html.HtmlStreamEventReceiver;
import org.owasp.html.HtmlStreamEventReceiverWrapper;
import org.owasp.html.PolicyFactory;
import org.owasp.html.Sanitizers;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public final class HtmlSanitizer {
private final PolicyFactory sanitizer;
@Autowired
public HtmlSanitizer(final HangarConfig config) {
//TODO remove this disgusting hack when we can put the sanitizer before the markdown renderer
final PolicyFactory links = new HtmlPolicyBuilder().withPostprocessor((HtmlStreamEventReceiver r) -> new HtmlStreamEventReceiverWrapper(r) {
@Override
public void openTag(final String elementName, final List<String> attrs) {
if (!"a".equals(elementName)) {
super.openTag(elementName, attrs);
return;
}
for (int i = 0, n = attrs.size(); i < n; i += 2) {
if (!"href".equals(attrs.get(i))) {
continue;
}
final String url = attrs.get(i + 1);
final String safeUrl = config.security.makeSafe(url);
if (!url.equals(safeUrl)) {
attrs.set(i + 1, safeUrl);
}
}
super.openTag(elementName, attrs);
}
}).allowStandardUrlProtocols().allowElements("a").allowAttributes("href", "id", "class").onElements("a").toFactory();
final PolicyFactory iframes = new HtmlPolicyBuilder().withPostprocessor((HtmlStreamEventReceiver r) -> new HtmlStreamEventReceiverWrapper(r) {
@Override
public void openTag(final String elementName, final List<String> attrs) {
if (!"iframe".equals(elementName)) {
super.openTag(elementName, attrs);
return;
}
for (int i = 0, n = attrs.size(); i < n; i += 2) {
if ("src".equals(attrs.get(i)) && !attrs.get(i + 1).startsWith("https://www.youtube-nocookie.com/embed/")) {
// Only allow YouTube video embeds
return;
}
}
super.openTag(elementName, attrs);
}
}).allowUrlProtocols("https").allowElements("iframe").allowAttributes("src", "allowfullscreen", "border", "height", "width", "frameborder", "allow", "class").onElements("iframe").toFactory();
final PolicyFactory images = new HtmlPolicyBuilder().allowStandardUrlProtocols().allowElements("img", "svg", "path")
.allowAttributes("alt", "src", "border", "height", "width", "preserveAspectRatio", "viewBox", "class").onElements("img", "svg") // TODO don't allow class attribute
.allowAttributes("fill", "d").onElements("path").toFactory();
sanitizer = Sanitizers.FORMATTING.and(Sanitizers.BLOCKS).and(Sanitizers.TABLES).and(Sanitizers.STYLES)
.and(images).and(links).and(iframes).and(new HtmlPolicyBuilder().allowElements("pre", "details", "summary", "hr").toFactory());
}
public String sanitize(final String input) {
return sanitizer.sanitize(input);
}
}

View File

@ -1,17 +0,0 @@
package io.papermc.hangar.util;
import org.owasp.html.HtmlPolicyBuilder;
import org.owasp.html.PolicyFactory;
import org.owasp.html.Sanitizers;
public final class HtmlSanitizerUtil {
private static final PolicyFactory IMAGES = new HtmlPolicyBuilder().allowUrlProtocols("https").allowElements("img")
.allowAttributes("alt", "src").onElements("img").allowAttributes("border", "height", "width").onElements("img").toFactory();
private static final PolicyFactory SANITIZER = Sanitizers.FORMATTING.and(Sanitizers.BLOCKS).and(IMAGES).and(Sanitizers.TABLES).and(Sanitizers.STYLES)
.and(new HtmlPolicyBuilder().allowElements("pre", "details", "summary").toFactory());
public static String sanitize(final String input) {
return SANITIZER.sanitize(input);
}
}

View File

@ -165,6 +165,10 @@ hangar:
safe-download-hosts:
- "github.com"
- "youtu.be"
- "youtube.com"
- "papermc.io"
- "discord.gg"
- "markdownguide.org"
discourse:
enabled: false