{"record":{"id":"83a08a44cb00c026","repo":"siyuan-note/siyuan","slug":"svg-contains-text-outside-the-root-element","errorCode":null,"errorMessage":"svg contains text outside the root element","messagePattern":"svg contains text outside the root element","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/util/misc.go","lineNumber":426,"sourceCode":"\t\t\t\tif depth == 0 {\n\t\t\t\t\trootClosed = true\n\t\t\t\t}\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\ttyped.Name = preserveXMLName(typed.Name)\n\t\t\tif err = encoder.EncodeToken(typed); err != nil {\n\t\t\t\treturn \"\", fmt.Errorf(\"render svg failed: %w\", err)\n\t\t\t}\n\t\t\tdepth--\n\t\t\tif depth == 0 {\n\t\t\t\trootClosed = true\n\t\t\t}\n\t\tcase xml.CharData:\n\t\t\tif skipDepth > 0 {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif (!rootSeen || rootClosed) && strings.TrimSpace(string(typed)) != \"\" {\n\t\t\t\treturn \"\", fmt.Errorf(\"svg contains text outside the root element\")\n\t\t\t}\n\t\t\tif rootSeen && !rootClosed {\n\t\t\t\tif err = encoder.EncodeToken(typed); err != nil {\n\t\t\t\t\treturn \"\", fmt.Errorf(\"render svg failed: %w\", err)\n\t\t\t\t}\n\t\t\t}\n\t\tcase xml.Comment:\n\t\t\tif skipDepth == 0 && rootSeen && !rootClosed {\n\t\t\t\tif err = encoder.EncodeToken(typed); err != nil {\n\t\t\t\t\treturn \"\", fmt.Errorf(\"render svg failed: %w\", err)\n\t\t\t\t}\n\t\t\t}\n\t\tcase xml.Directive:\n\t\t\tif !isBenignSVGDoctype(string(typed)) {\n\t\t\t\treturn \"\", fmt.Errorf(\"svg directives are not allowed\")\n\t\t\t}\n\t\t\t// 良性 DOCTYPE 声明不写入输出，与 XML 声明（ProcInst）的处理方式一致，不影响浏览器渲染\n\t\tcase xml.ProcInst:","sourceCodeStart":408,"sourceCodeEnd":444,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/8641553a1f07374001902d3ce773285db1292b2d/kernel/util/misc.go#L408-L444","documentation":"Non-whitespace character data was found either before the root element opened or after it closed. Valid standalone SVG allows text only inside the <svg> root; stray text around it (or between multiple documents) is rejected both because it is not a valid SVG image and as an anti-smuggling measure.","triggerScenarios":"'hello<svg>...</svg>' (text before root), '<svg>...</svg>trailing text', two SVGs concatenated back to back with text in between, or an HTML wrapper with visible text around the svg tag.","commonSituations":"Pasting an SVG copied from a webpage along with surrounding text; concatenating icon files; log/console text accidentally included in the paste.","solutions":["Delete the non-whitespace text outside the <svg>...</svg> range before sanitizing","Extract just the svg element (regex or string slicing on the first '<svg' and last '</svg>') when accepting arbitrary pastes","Do not concatenate multiple SVG documents; use <svg> with multiple children or an <symbol>/<use> structure instead","Trim input and check that it starts with '<svg' or an allowed prolog (xml decl, comment, DOCTYPE)"],"exampleFix":"// before\nSanitizeSVG(\"icon: <svg>...</svg>\") // text outside the root element\n// after\nSanitizeSVG(\"<svg xmlns=\\\"http://www.w3.org/2000/svg\\\">...</svg>\")","handlingStrategy":"validation","validationCode":"func extractSVGDoc(input string) (string, bool) {\n\tstart := strings.Index(input, \"<svg\")\n\tend := strings.LastIndex(input, \"</svg>\")\n\tif start < 0 || end < 0 || end <= start { return \"\", false }\n\treturn input[start : end+len(\"</svg>\")], true\n}","typeGuard":null,"tryCatchPattern":"doc, ok := extractSVGDoc(userInput)\nif !ok { return errors.New(\"no complete svg element found in input\") }\nclean, err := util.SanitizeSVG(doc)\nif err != nil { return err }","preventionTips":["Slice user pastes down to the <svg>...</svg> span before sanitizing","Reject concatenations of multiple SVG documents","Trim and inspect surrounding text when accepting clipboard content","Strip prose/logs accidentally captured with the icon"],"tags":["svg","xml","validation","sanitization"],"backgroundTag":"schema-validation-failed","analyzedSha":"8641553a1f07374001902d3ce773285db1292b2d","analyzedAt":"2026-09-11T16:08:28.414Z","contentChangedAt":"2026-09-11T16:08:28.414Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}