{"record":{"id":"47d68637cd28aef2","repo":"siyuan-note/siyuan","slug":"svg-contains-too-many-tokens","errorCode":null,"errorMessage":"svg contains too many tokens","messagePattern":"svg contains too many tokens","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/util/misc.go","lineNumber":362,"sourceCode":"\tencoder := xml.NewEncoder(&buf)\n\trootSeen := false\n\trootClosed := false\n\tdepth := 0\n\tskipDepth := 0\n\ttokenCount := 0\n\tvar elementStack []xml.Name\n\n\tfor {\n\t\ttoken, err := decoder.RawToken()\n\t\tif err == io.EOF {\n\t\t\tbreak\n\t\t}\n\t\tif err != nil {\n\t\t\treturn \"\", fmt.Errorf(\"parse svg failed: %w\", err)\n\t\t}\n\t\ttokenCount++\n\t\tif tokenCount > maxSVGTokens {\n\t\t\treturn \"\", fmt.Errorf(\"svg contains too many tokens\")\n\t\t}\n\n\t\tswitch typed := token.(type) {\n\t\tcase xml.StartElement:\n\t\t\telementStack = append(elementStack, typed.Name)\n\t\t\tdepth++\n\t\t\tif depth > maxSVGDepth {\n\t\t\t\treturn \"\", fmt.Errorf(\"svg nesting depth exceeds %d\", maxSVGDepth)\n\t\t\t}\n\t\t\tif rootClosed {\n\t\t\t\treturn \"\", fmt.Errorf(\"svg contains multiple root elements\")\n\t\t\t}\n\t\t\tif !rootSeen {\n\t\t\t\tif !strings.EqualFold(typed.Name.Local, \"svg\") {\n\t\t\t\t\treturn \"\", fmt.Errorf(\"root element is not svg\")\n\t\t\t\t}\n\t\t\t\trootSeen = true\n\t\t\t}","sourceCodeStart":344,"sourceCodeEnd":380,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/8641553a1f07374001902d3ce773285db1292b2d/kernel/util/misc.go#L344-L380","documentation":"SanitizeSVG counts every XML token it processes and aborts once the count exceeds maxSVGTokens (1,000,000, kernel/util/misc.go:322). This is a resource-exhaustion guard: a huge or deliberately pathological SVG must not consume unbounded CPU/memory during sanitization. Hitting it means the input is either genuinely enormous or adversarially constructed.","triggerScenarios":"Calling SanitizeSVG with an SVG containing more than one million XML tokens — megabyte-scale icon files, machine-generated SVGs with per-pixel elements, or a crafted denial-of-service payload with millions of nested/sibling elements.","commonSituations":"Importing a very large traced/dotted artwork (auto-tracers emit thousands of paths), pasting an exported map/plot with huge element counts, or a malicious upload intended to hang the kernel.","solutions":["Simplify/optimize the SVG before import (reduce path points, merge shapes, run SVGO or similar)","Split or downscale the artwork — icons should be small vector graphics, not full illustrations","If the source is trusted and legitimately large, increase maxSVGTokens consciously and accept the DoS tradeoff","Reject the asset upstream with a friendlier size check (e.g. file-size or element-count limit) before sanitization"],"exampleFix":"// before\nout, err := util.SanitizeSVG(hugeGeneratedSVG)\n// after\nif countElements(hugeGeneratedSVG) > 100000 {\n    return errors.New(\"icon too complex; please simplify the SVG\")\n}\nout, err := util.SanitizeSVG(hugeGeneratedSVG)","handlingStrategy":"validation","validationCode":"if strings.Count(svg, \"<\") > 100000 { return errors.New(\"svg too complex to import\") }","typeGuard":null,"tryCatchPattern":"out, err := util.SanitizeSVG(svg)\nif err != nil && strings.Contains(err.Error(), \"too many tokens\") {\n    // ask user to simplify/optimize the SVG\n}","preventionTips":["Run SVGO or similar optimization before importing large SVGs","Impose a file-size limit on uploaded icons","Avoid auto-traced artwork as icons; use simplified vector sources"],"tags":["svg","resource-limits","sanitization","denial-of-service"],"backgroundTag":"payload-too-large","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"}