svg.go 1.7 KB

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