You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

118 lines
2.8 KiB

  1. # frozen_string_literal: true
  2. class Sanitize
  3. module Config
  4. HTTP_PROTOCOLS = %w(
  5. http
  6. https
  7. ).freeze
  8. LINK_PROTOCOLS = %w(
  9. http
  10. https
  11. dat
  12. dweb
  13. ipfs
  14. ipns
  15. ssb
  16. gopher
  17. xmpp
  18. magnet
  19. ).freeze
  20. CLASS_WHITELIST_TRANSFORMER = lambda do |env|
  21. node = env[:node]
  22. class_list = node['class']&.split(/[\t\n\f\r ]/)
  23. return unless class_list
  24. class_list.keep_if do |e|
  25. next true if e =~ /^(h|p|u|dt|e)-/ # microformats classes
  26. next true if e =~ /^(mention|hashtag)$/ # semantic classes
  27. next true if e =~ /^(ellipsis|invisible)$/ # link formatting classes
  28. end
  29. node['class'] = class_list.join(' ')
  30. end
  31. UNSUPPORTED_HREF_TRANSFORMER = lambda do |env|
  32. return unless env[:node_name] == 'a'
  33. current_node = env[:node]
  34. scheme = begin
  35. if current_node['href'] =~ Sanitize::REGEX_PROTOCOL
  36. Regexp.last_match(1).downcase
  37. else
  38. :relative
  39. end
  40. end
  41. current_node.replace(current_node.text) unless LINK_PROTOCOLS.include?(scheme)
  42. end
  43. UNSUPPORTED_ELEMENTS_TRANSFORMER = lambda do |env|
  44. return unless %w(h1 h2 h3 h4 h5 h6 blockquote pre ul ol li).include?(env[:node_name])
  45. current_node = env[:node]
  46. case env[:node_name]
  47. when 'li'
  48. current_node.traverse do |node|
  49. next unless %w(p ul ol li).include?(node.name)
  50. node.add_next_sibling('<br>') if node.next_sibling
  51. node.replace(node.children) unless node.text?
  52. end
  53. else
  54. current_node.name = 'p'
  55. end
  56. end
  57. MASTODON_STRICT ||= freeze_config(
  58. elements: %w(p br span a),
  59. attributes: {
  60. 'a' => %w(href rel class),
  61. 'span' => %w(class),
  62. },
  63. add_attributes: {
  64. 'a' => {
  65. 'rel' => 'nofollow noopener noreferrer',
  66. 'target' => '_blank',
  67. },
  68. },
  69. protocols: {},
  70. transformers: [
  71. CLASS_WHITELIST_TRANSFORMER,
  72. UNSUPPORTED_ELEMENTS_TRANSFORMER,
  73. UNSUPPORTED_HREF_TRANSFORMER,
  74. ]
  75. )
  76. MASTODON_OEMBED ||= freeze_config merge(
  77. RELAXED,
  78. elements: RELAXED[:elements] + %w(audio embed iframe source video),
  79. attributes: merge(
  80. RELAXED[:attributes],
  81. 'audio' => %w(controls),
  82. 'embed' => %w(height src type width),
  83. 'iframe' => %w(allowfullscreen frameborder height scrolling src width),
  84. 'source' => %w(src type),
  85. 'video' => %w(controls height loop width),
  86. 'div' => [:data]
  87. ),
  88. protocols: merge(
  89. RELAXED[:protocols],
  90. 'embed' => { 'src' => HTTP_PROTOCOLS },
  91. 'iframe' => { 'src' => HTTP_PROTOCOLS },
  92. 'source' => { 'src' => HTTP_PROTOCOLS }
  93. )
  94. )
  95. end
  96. end