ugcpolicy.go 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207
  1. package utils
  2. import (
  3. "github.com/microcosm-cc/bluemonday"
  4. "regexp"
  5. )
  6. // copied from bluemonday's GitHub repostiory, with some adaptations
  7. func UGCPolicy() *bluemonday.Policy {
  8. p := bluemonday.NewPolicy()
  9. ///////////////////////
  10. // Global attributes //
  11. ///////////////////////
  12. // "class" is not permitted as we are not allowing users to style their own
  13. // content
  14. p.AllowStandardAttributes()
  15. //////////////////////////////
  16. // Global URL format policy //
  17. //////////////////////////////
  18. p.AllowStandardURLs()
  19. ////////////////////////////////
  20. // Declarations and structure //
  21. ////////////////////////////////
  22. // "xml" "xslt" "DOCTYPE" "html" "head" are not permitted as we are
  23. // expecting user generated content to be a fragment of HTML and not a full
  24. // document.
  25. //////////////////////////
  26. // Sectioning root tags //
  27. //////////////////////////
  28. // "article" and "aside" are permitted and takes no attributes
  29. p.AllowElements("article", "aside")
  30. // "body" is not permitted as we are expecting user generated content to be a fragment
  31. // of HTML and not a full document.
  32. // "details" is permitted, including the "open" attribute which can either
  33. // be blank or the value "open".
  34. p.AllowAttrs(
  35. "open",
  36. ).Matching(regexp.MustCompile(`(?i)^(|open)$`)).OnElements("details")
  37. // "fieldset" is not permitted as we are not allowing forms to be created.
  38. // "figure" is permitted and takes no attributes
  39. p.AllowElements("figure")
  40. // "nav" is not permitted as it is assumed that the site (and not the user)
  41. // has defined navigation elements
  42. // "section" is permitted and takes no attributes
  43. p.AllowElements("section")
  44. // "summary" is permitted and takes no attributes
  45. p.AllowElements("summary")
  46. //////////////////////////
  47. // Headings and footers //
  48. //////////////////////////
  49. // "footer" is not permitted as we expect user content to be a fragment and
  50. // not structural to this extent
  51. // "h1" through "h6" are permitted and take no attributes
  52. p.AllowElements("h1", "h2", "h3", "h4", "h5", "h6")
  53. // "header" is not permitted as we expect user content to be a fragment and
  54. // not structural to this extent
  55. // "hgroup" is permitted and takes no attributes
  56. p.AllowElements("hgroup")
  57. /////////////////////////////////////
  58. // Content grouping and separating //
  59. /////////////////////////////////////
  60. // "blockquote" is permitted, including the "cite" attribute which must be
  61. // a standard URL.
  62. p.AllowAttrs("cite").OnElements("blockquote")
  63. // "br" "div" "hr" "p" "span" "wbr" are permitted and take no attributes
  64. p.AllowElements("br", "div", "hr", "p", "span", "wbr")
  65. ///////////
  66. // Links //
  67. ///////////
  68. // "a" is permitted
  69. p.AllowAttrs("href").OnElements("a")
  70. // "area" is permitted along with the attributes that map image maps work
  71. p.AllowAttrs("name").Matching(
  72. regexp.MustCompile(`^([\p{L}\p{N}_-]+)$`),
  73. ).OnElements("map")
  74. p.AllowAttrs("alt").Matching(bluemonday.Paragraph).OnElements("area")
  75. p.AllowAttrs("coords").Matching(
  76. regexp.MustCompile(`^([0-9]+,)+[0-9]+$`),
  77. ).OnElements("area")
  78. p.AllowAttrs("href").OnElements("area")
  79. p.AllowAttrs("rel").Matching(bluemonday.SpaceSeparatedTokens).OnElements("area")
  80. p.AllowAttrs("shape").Matching(
  81. regexp.MustCompile(`(?i)^(default|circle|rect|poly)$`),
  82. ).OnElements("area")
  83. // "img" is permitted
  84. p.AllowImages()
  85. p.AllowAttrs("usemap").Matching(
  86. regexp.MustCompile(`(?i)^#[\p{L}\p{N}_-]+$`),
  87. ).OnElements("img")
  88. p.AllowAttrs("src").Matching(
  89. regexp.MustCompile(`(?i)^\/[\p{L}\p{N}\/\.]+$`),
  90. ).OnElements("img")
  91. p.AllowAttrs("style").OnElements("img")
  92. p.AllowAttrs("alt").OnElements("img")
  93. // "link" is not permitted
  94. /////////////////////
  95. // Phrase elements //
  96. /////////////////////
  97. // The following are all inline phrasing elements
  98. p.AllowElements("abbr", "acronym", "cite", "code", "dfn", "em",
  99. "figcaption", "mark", "s", "samp", "strong", "sub", "sup", "var")
  100. // "q" is permitted and "cite" is a URL and handled by URL policies
  101. p.AllowAttrs("cite").OnElements("q")
  102. // "time" is permitted
  103. p.AllowAttrs("datetime").Matching(bluemonday.ISO8601).OnElements("time")
  104. ////////////////////
  105. // Style elements //
  106. ////////////////////
  107. // block and inline elements that impart no semantic meaning but style the
  108. // document
  109. p.AllowElements("b", "i", "pre", "small", "strike", "tt", "u")
  110. // "style" is not permitted as we are not yet sanitising CSS and it is an
  111. // XSS attack vector
  112. //////////////////////
  113. // HTML5 Formatting //
  114. //////////////////////
  115. // "bdi" "bdo" are permitted
  116. p.AllowAttrs("dir").Matching(bluemonday.Direction).OnElements("bdi", "bdo")
  117. // "rp" "rt" "ruby" are permitted
  118. p.AllowElements("rp", "rt", "ruby")
  119. ///////////////////////////
  120. // HTML5 Change tracking //
  121. ///////////////////////////
  122. // "del" "ins" are permitted
  123. p.AllowAttrs("cite").Matching(bluemonday.Paragraph).OnElements("del", "ins")
  124. p.AllowAttrs("datetime").Matching(bluemonday.ISO8601).OnElements("del", "ins")
  125. ///////////
  126. // Lists //
  127. ///////////
  128. p.AllowLists()
  129. ////////////
  130. // Tables //
  131. ////////////
  132. p.AllowTables()
  133. ///////////
  134. // Forms //
  135. ///////////
  136. // By and large, forms are not permitted. However there are some form
  137. // elements that can be used to present data, and we do permit those
  138. //
  139. // "button" "fieldset" "input" "keygen" "label" "output" "select" "datalist"
  140. // "textarea" "optgroup" "option" are all not permitted
  141. // "meter" is permitted
  142. p.AllowAttrs(
  143. "value",
  144. "min",
  145. "max",
  146. "low",
  147. "high",
  148. "optimum",
  149. ).Matching(bluemonday.Number).OnElements("meter")
  150. // "progress" is permitted
  151. p.AllowAttrs("value", "max").Matching(bluemonday.Number).OnElements("progress")
  152. return p
  153. }