siyuan-note/siyuan · error
invalid custom emoji URL
Error message
invalid custom emoji URL
What it means
Returned by downloadCustomEmojiData (system.go:331) when the `url` POST field is not a valid HTTP/HTTPS URL with a host. url.Parse failed, the scheme was something other than http/https (e.g. file://, javascript:, ftp:), or the host was empty. This guard runs before any network request to block non-HTTP schemes and malformed URLs.
Source
Thrown at kernel/api/system.go:331
file, err := fileHeader.Open()
if err != nil {
return nil, err
}
defer file.Close()
return io.ReadAll(io.LimitReader(file, maxCustomEmojiSize+1))
}
rawURL := strings.TrimSpace(c.PostForm("url"))
if rawURL == "" {
return nil, fmt.Errorf("field [file] or [url] must not be empty")
}
return downloadCustomEmojiData(rawURL)
}
func downloadCustomEmojiData(rawURL string) ([]byte, error) {
parsedURL, err := url.Parse(rawURL)
if err != nil || (parsedURL.Scheme != "http" && parsedURL.Scheme != "https") || parsedURL.Host == "" {
return nil, fmt.Errorf("invalid custom emoji URL")
}
response, err := util.NewCustomReqClient().R().Get(parsedURL.String())
if err != nil {
return nil, fmt.Errorf("download custom emoji failed: %w", err)
}
defer response.Body.Close()
if response.StatusCode != http.StatusOK {
return nil, fmt.Errorf("download custom emoji failed with status %d", response.StatusCode)
}
if response.ContentLength > maxCustomEmojiSize {
return nil, fmt.Errorf("custom emoji file is too large")
}
data, err := io.ReadAll(io.LimitReader(response.Body, maxCustomEmojiSize+1))
if err != nil {
return nil, fmt.Errorf("read custom emoji response failed: %w", err)
}View on GitHub (pinned to 251596fc0d)
Solutions
- Supply an absolute http:// or https:// URL with a non-empty host.
- Trim/validate the URL client-side and reject non-http(s) schemes before submitting.
- If the user selected a local file, use the 'file' upload mode instead of the URL mode.
Example fix
// before
fd.append('url', '/local/emoji.png')
// after
fd.append('url', 'https://cdn.example.com/emoji.png') Defensive patterns
Strategy: validation
Validate before calling
function validEmojiUrl(u) {
try { const p = new URL(u); return (p.protocol === 'http:' || p.protocol === 'https:') && !!p.host; } catch { return false; }
} Prevention
- Reject non-http(s) schemes and relative URLs client-side.
- Trim and validate the URL before submit.
- For local images, use the 'file' upload mode rather than a file:// URL.
When it happens
Trigger: Submitting url='ftp://host/x', url='file:///etc/passwd', url='javascript:alert(1)', url='/local/path' (relative), url='example.com/x' (no scheme), or an unparseable string. The check at system.go:330 fires and returns the error at line 331.
Common situations: User pasted a local file path or relative URL. A non-http scheme was used (intentionally or by mistake). Input contained control characters or was not trimmed. Security-relevant: this guard blocks SSRF-via-scheme and local-file exfiltration.
Related errors
- invalid import token
- import path is not sub path of import dir
- missing query param [u]
- decode [u] failed: %s
- parse [u] failed: %s
AI-assisted analysis of siyuan-note/siyuan@251596fc0d (2026-08-12).
Data as JSON: /api/errors/474a84308589e4cf.
Report an issue: GitHub.