{"record":{"id":"de94f6d2302e7677","repo":"usememos/memos","slug":"only-http-https-protocols-are-allowed","errorCode":null,"errorMessage":"only http/https protocols are allowed","messagePattern":"only http/https protocols are allowed","errorType":"validation","errorClass":null,"httpStatus":400,"severity":"warning","filePath":"internal/httpgetter/html_meta.go","lineNumber":115,"sourceCode":"\tif len(ips) == 0 {\n\t\treturn nil, errors.New(\"hostname resolved to no addresses\")\n\t}\n\n\treturn ips, nil\n}\n\nfunc isInternalIP(ip net.IP) bool {\n\treturn ip.IsLoopback() || ip.IsPrivate() || ip.IsLinkLocalUnicast() || ip.IsUnspecified()\n}\n\nfunc validateURL(urlStr string) error {\n\tu, err := url.Parse(urlStr)\n\tif err != nil {\n\t\treturn errors.New(\"invalid URL format\")\n\t}\n\n\tif u.Scheme != \"http\" && u.Scheme != \"https\" {\n\t\treturn errors.New(\"only http/https protocols are allowed\")\n\t}\n\n\thost := u.Hostname()\n\tif host == \"\" {\n\t\treturn errors.New(\"empty hostname\")\n\t}\n\n\tif ip := net.ParseIP(host); ip != nil && isInternalIP(ip) {\n\t\treturn errors.Wrap(ErrInternalIP, ip.String())\n\t}\n\n\treturn nil\n}\n\ntype HTMLMeta struct {\n\tTitle       string `json:\"title\"`\n\tDescription string `json:\"description\"`\n\tImage       string `json:\"image\"`","sourceCodeStart":97,"sourceCodeEnd":133,"githubUrl":"https://github.com/usememos/memos/blob/14d757ce1fb31c78590f374bc042f8dbedbc20d7/internal/httpgetter/html_meta.go#L97-L133","documentation":"validateURL rejects any scheme other than http or https. This blocks file://, ftp://, gopher://, data:, and similar schemes from being fetched by the metadata scraper, a standard SSRF hardening measure.","triggerScenarios":"Passing `file:///etc/passwd`, `ftp://host/file`, `data:text/html,...`, or any non-http(s) URL to the HTML metadata getter; also fires on redirect targets with odd schemes.","commonSituations":"Users pasting local file links or protocol links into memos; automated tooling feeding arbitrary user text into the preview endpoint; iOS/Android app share sheets producing unusual URI schemes.","solutions":["Only submit http:// or https:// URLs to the fetcher","Strip or reject non-web links client-side before calling preview APIs","If you need local file content, upload it as an attachment instead of link preview"],"exampleFix":"// before\nGetHTMLMeta(\"file:///home/user/doc.html\")\n// after\nGetHTMLMeta(\"https://example.com/doc.html\")","handlingStrategy":"validation","validationCode":"func isWebURL(u *url.URL) bool { return u.Scheme == \"http\" || u.Scheme == \"https\" }\n\n// gate before fetch:\nif u, err := url.Parse(raw); err != nil || !isWebURL(u) { return nil }","typeGuard":null,"tryCatchPattern":"// Skip previews for non-web schemes rather than erroring\nif err := fetchPreview(u); err != nil {\n  if strings.Contains(err.Error(), \"only http/https\") { return nil }\n  return err\n}","preventionTips":["Filter links to http/https in UI link detection","Never pass file:// or custom-scheme URIs to the fetcher","Validate scheme at the trust boundary (form/API input)"],"tags":["network","url","ssrf","security"],"backgroundTag":null,"analyzedSha":"14d757ce1fb31c78590f374bc042f8dbedbc20d7","analyzedAt":"2026-08-15T09:27:36.538Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}