Add support for CSS links in generated HTML outputs

This commit is contained in:
2026-06-13 22:16:15 -05:00
parent c84d8868d1
commit b10a8bd194
11 changed files with 213 additions and 13 deletions

View File

@@ -2,8 +2,10 @@ package config
import (
"fmt"
"net/url"
"regexp"
"strings"
"unicode"
"gitea.maximumdirect.net/eric/distributor/internal/link"
)
@@ -281,9 +283,15 @@ func ValidatePublishTransformPolicy(publish PublishPolicy, transform Transform)
if transform.MarkdownToHTML.Input != "" && !transform.MarkdownToHTML.Enabled {
return fmt.Errorf("transform.markdown_to_html.input requires transform.markdown_to_html.enabled to be true")
}
if transform.MarkdownToHTML.CssHref != "" && !transform.MarkdownToHTML.Enabled {
return fmt.Errorf("transform.markdown_to_html.css_href requires transform.markdown_to_html.enabled to be true")
}
if transform.MarkdownToHTML.Input != "" && mode != TransformModeIndex {
return fmt.Errorf("transform.markdown_to_html.input is only valid when mode is %s", TransformModeIndex)
}
if err := validateCSSHref(transform.MarkdownToHTML.CssHref); err != nil {
return fmt.Errorf("transform.markdown_to_html.css_href %w", err)
}
if transform.MarkdownToHTML.Enabled && !publish.HTML {
return fmt.Errorf("transform.markdown_to_html.enabled requires publish.html to be true")
}
@@ -293,6 +301,49 @@ func ValidatePublishTransformPolicy(publish PublishPolicy, transform Transform)
return nil
}
func validateCSSHref(value string) error {
if value == "" {
return nil
}
for _, character := range value {
if unicode.IsControl(character) || unicode.IsSpace(character) {
return fmt.Errorf("must not contain whitespace or control characters")
}
}
if strings.ContainsAny(value, "\\<>\"'") {
return fmt.Errorf("must not contain backslashes or HTML-sensitive characters")
}
if strings.HasPrefix(value, "//") {
return fmt.Errorf("must not be scheme-relative")
}
parsed, err := url.Parse(value)
if err != nil {
return fmt.Errorf("must be a valid URL reference: %w", err)
}
if parsed.Fragment != "" {
return fmt.Errorf("must not include a fragment")
}
if parsed.Scheme != "" {
if parsed.Scheme != "http" && parsed.Scheme != "https" {
return fmt.Errorf("scheme must be http or https")
}
if parsed.Host == "" {
return fmt.Errorf("host is required for absolute URLs")
}
if parsed.User != nil {
return fmt.Errorf("must not include userinfo")
}
return nil
}
if parsed.Host != "" {
return fmt.Errorf("must not be scheme-relative")
}
if parsed.Path == "" {
return fmt.Errorf("relative URL path is required")
}
return nil
}
func validatePathMapping(errs ValidationErrors, context string, mapping PathMapping) ValidationErrors {
if mapping.Mode != PathMappingPreserveRelative && mapping.Mode != PathMappingFixed {
errs = append(errs, context+".mode must be "+PathMappingPreserveRelative+" or "+PathMappingFixed)