package nip23 import ( stdhtml "html" "io" "regexp" "strings" "github.com/gomarkdown/markdown" "github.com/gomarkdown/markdown/ast" "github.com/gomarkdown/markdown/html" "github.com/gomarkdown/markdown/parser" "github.com/microcosm-cc/bluemonday" ) var nostrEveryMatcher = regexp.MustCompile(`nostr:((npub|note|nevent|nprofile|naddr)1[a-z0-9]+)\b`) var renderer = html.NewRenderer(html.RendererOptions{ Flags: html.HrefTargetBlank | html.SkipHTML, RenderNodeHook: func(w io.Writer, node ast.Node, entering bool) (ast.WalkStatus, bool) { switch v := node.(type) { case *ast.HTMLSpan: w.Write([]byte(stdhtml.EscapeString(string(v.Literal)))) return ast.GoToNext, true case *ast.HTMLBlock: w.Write([]byte(stdhtml.EscapeString(string(v.Literal)))) return ast.GoToNext, true } return ast.GoToNext, false }, }) func MarkdownToHTML(md string) string { md = strings.ReplaceAll(md, "\u00A0", " ") // create markdown parser with extensions // this parser is stateful so it must be reinitialized every time doc := parser.NewWithExtensions( parser.AutoHeadingIDs | parser.NoIntraEmphasis | parser.FencedCode | parser.Autolink | parser.Footnotes | parser.SpaceHeadings, ).Parse([]byte(md)) // create HTML renderer with extensions output := string(markdown.Render(doc, renderer)) // sanitize content output = sanitizeXSS(output) return output } func sanitizeXSS(html string) string { p := bluemonday.UGCPolicy() p.RequireNoFollowOnLinks(false) p.AllowElements("video", "source") p.AllowAttrs("controls", "width").OnElements("video") p.AllowAttrs("src", "width").OnElements("source") return p.Sanitize(html) }