svg.go 1.5 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283
  1. package svg
  2. import (
  3. "io"
  4. "strings"
  5. "github.com/tdewolff/parse/v2"
  6. "github.com/tdewolff/parse/v2/xml"
  7. "github.com/imgproxy/imgproxy/v3/imagedata"
  8. "github.com/imgproxy/imgproxy/v3/imagetype"
  9. )
  10. func Sanitize(data imagedata.ImageData) (imagedata.ImageData, error) {
  11. r := data.Reader()
  12. l := xml.NewLexer(parse.NewInput(r))
  13. buf, cancel := imagedata.BorrowBuffer()
  14. ignoreTag := 0
  15. var curTagName string
  16. for {
  17. tt, tdata := l.Next()
  18. if ignoreTag > 0 {
  19. switch tt {
  20. case xml.ErrorToken:
  21. cancel()
  22. return nil, l.Err()
  23. case xml.EndTagToken, xml.StartTagCloseVoidToken:
  24. ignoreTag--
  25. case xml.StartTagToken:
  26. ignoreTag++
  27. }
  28. continue
  29. }
  30. switch tt {
  31. case xml.ErrorToken:
  32. if l.Err() != io.EOF {
  33. cancel()
  34. return nil, l.Err()
  35. }
  36. newData := imagedata.NewFromBytesWithFormat(
  37. imagetype.SVG,
  38. buf.Bytes(),
  39. )
  40. newData.AddCancel(cancel)
  41. return newData, nil
  42. case xml.StartTagToken:
  43. curTagName = strings.ToLower(string(l.Text()))
  44. if curTagName == "script" {
  45. ignoreTag++
  46. continue
  47. }
  48. buf.Write(tdata)
  49. case xml.AttributeToken:
  50. attrName := strings.ToLower(string(l.Text()))
  51. if _, unsafe := unsafeAttrs[attrName]; unsafe {
  52. continue
  53. }
  54. if curTagName == "use" && (attrName == "href" || attrName == "xlink:href") {
  55. val := strings.TrimSpace(strings.Trim(string(l.AttrVal()), `"'`))
  56. if len(val) > 0 && val[0] != '#' {
  57. continue
  58. }
  59. }
  60. buf.Write(tdata)
  61. default:
  62. buf.Write(tdata)
  63. }
  64. }
  65. }