- FormatHTML renders with goldmark html.WithUnsafe(), as Winter's Markdown::parse passes raw HTML through, so an admin save, an import or an API write no longer turns imported HTML into raw HTML omitted - rejectUnsafe still runs after the render and refuses script, iframe, object and embed tags, event handlers and javascript, vbscript and data URLs; API response shapes are unchanged - TestFormatHTMLKeepsRawHTML covers HTML paragraphs and a raw footer
66 lines
2.2 KiB
Go
66 lines
2.2 KiB
Go
package classes
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
"regexp"
|
|
"strings"
|
|
|
|
"github.com/yuin/goldmark"
|
|
"github.com/yuin/goldmark/extension"
|
|
"github.com/yuin/goldmark/parser"
|
|
"github.com/yuin/goldmark/renderer/html"
|
|
)
|
|
|
|
var (
|
|
markdownEngine = goldmark.New(
|
|
goldmark.WithExtensions(extension.Footnote, extension.Table),
|
|
goldmark.WithParserOptions(parser.WithAttribute()),
|
|
// Winter's Markdown::parse passes raw HTML through, so imported HTML
|
|
// bodies round-trip unchanged (D-03, PHP parity); rejectUnsafe gates
|
|
// the rendered result on every write path.
|
|
goldmark.WithRendererOptions(html.WithUnsafe()),
|
|
)
|
|
|
|
markdownUnsafeTag = regexp.MustCompile(`(?i)<(?:script|iframe|object|embed)\b`)
|
|
markdownEventHandler = regexp.MustCompile(`(?i)\son[a-z]+\s*=`)
|
|
markdownDangerousURL = regexp.MustCompile(`(?i)(?:javascript|vbscript|data):`)
|
|
)
|
|
|
|
// FormatHTML converts journal post markdown to stored HTML using goldmark
|
|
// footnote, table, and attribute extensions. Raw HTML in the source passes
|
|
// through unchanged; the result then goes through the same rejectUnsafe gate
|
|
// as cabana.RenderMarkdown. It does not call cabana.RenderMarkdown, whose
|
|
// admin preview stays without unsafe HTML.
|
|
func FormatHTML(src string) (string, error) {
|
|
var buf bytes.Buffer
|
|
if err := markdownEngine.Convert([]byte(strings.TrimSpace(src)), &buf); err != nil {
|
|
return "", fmt.Errorf("journal: markdown: %w", err)
|
|
}
|
|
out := buf.String()
|
|
if err := rejectUnsafe(out); err != nil {
|
|
return "", err
|
|
}
|
|
return out, nil
|
|
}
|
|
|
|
// CheckHTML applies the FormatHTML safety gate to HTML that is stored as is,
|
|
// such as category content. It refuses raw script, iframe, object and embed
|
|
// tags, event handler attributes and javascript, vbscript or data URL schemes.
|
|
func CheckHTML(doc string) error {
|
|
return rejectUnsafe(doc)
|
|
}
|
|
|
|
func rejectUnsafe(doc string) error {
|
|
if markdownUnsafeTag.MatchString(doc) {
|
|
return fmt.Errorf("journal: rendered HTML contains raw unsafe tags")
|
|
}
|
|
if markdownEventHandler.MatchString(doc) {
|
|
return fmt.Errorf("journal: rendered HTML contains event handlers")
|
|
}
|
|
if markdownDangerousURL.MatchString(doc) {
|
|
return fmt.Errorf("journal: rendered HTML contains a dangerous URL scheme")
|
|
}
|
|
return nil
|
|
}
|