svg.go 1.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384
  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. data.Headers(),
  40. )
  41. newData.AddCancel(cancel)
  42. return newData, nil
  43. case xml.StartTagToken:
  44. curTagName = strings.ToLower(string(l.Text()))
  45. if curTagName == "script" {
  46. ignoreTag++
  47. continue
  48. }
  49. buf.Write(tdata)
  50. case xml.AttributeToken:
  51. attrName := strings.ToLower(string(l.Text()))
  52. if _, unsafe := unsafeAttrs[attrName]; unsafe {
  53. continue
  54. }
  55. if curTagName == "use" && (attrName == "href" || attrName == "xlink:href") {
  56. val := strings.TrimSpace(strings.Trim(string(l.AttrVal()), `"'`))
  57. if len(val) > 0 && val[0] != '#' {
  58. continue
  59. }
  60. }
  61. buf.Write(tdata)
  62. default:
  63. buf.Write(tdata)
  64. }
  65. }
  66. }