sanitize_config.rb 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120
  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. gemini
  20. ).freeze
  21. CLASS_WHITELIST_TRANSFORMER = lambda do |env|
  22. node = env[:node]
  23. class_list = node['class']&.split(/[\t\n\f\r ]/)
  24. return unless class_list
  25. class_list.keep_if do |e|
  26. next true if /^(h|p|u|dt|e)-/.match?(e) # microformats classes
  27. next true if /^(mention|hashtag)$/.match?(e) # semantic classes
  28. next true if /^(ellipsis|invisible)$/.match?(e) # link formatting classes
  29. end
  30. node['class'] = class_list.join(' ')
  31. end
  32. TRANSLATE_TRANSFORMER = lambda do |env|
  33. node = env[:node]
  34. node.remove_attribute('translate') unless node['translate'] == 'no'
  35. end
  36. UNSUPPORTED_HREF_TRANSFORMER = lambda do |env|
  37. return unless env[:node_name] == 'a'
  38. current_node = env[:node]
  39. scheme = if current_node['href'] =~ Sanitize::REGEX_PROTOCOL
  40. Regexp.last_match(1).downcase
  41. else
  42. :relative
  43. end
  44. current_node.replace(Nokogiri::XML::Text.new(current_node.text, current_node.document)) unless LINK_PROTOCOLS.include?(scheme)
  45. end
  46. UNSUPPORTED_ELEMENTS_TRANSFORMER = lambda do |env|
  47. return unless %w(h6).include?(env[:node_name])
  48. current_node = env[:node]
  49. current_node.name = 'strong'
  50. current_node.wrap('<p></p>')
  51. end
  52. MASTODON_STRICT ||= freeze_config(
  53. elements: %w(p br span a abbr del pre blockquote code b strong i em h1 h2 h3 h4 h5 ul ol li img u),
  54. attributes: {
  55. 'abbr' => %w(title),
  56. 'blockquote' => %w(cite),
  57. 'img' => %w(src alt),
  58. 'a' => %w(href rel class translate title),
  59. 'span' => %w(class translate),
  60. 'ol' => %w(start reversed),
  61. 'li' => %w(value),
  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. TRANSLATE_TRANSFORMER,
  73. UNSUPPORTED_ELEMENTS_TRANSFORMER,
  74. UNSUPPORTED_HREF_TRANSFORMER,
  75. ]
  76. )
  77. MASTODON_OEMBED ||= freeze_config(
  78. elements: %w(audio embed iframe source video),
  79. attributes: {
  80. 'audio' => %w(controls),
  81. 'embed' => %w(height src type width),
  82. 'iframe' => %w(allowfullscreen frameborder height scrolling src width),
  83. 'source' => %w(src type),
  84. 'video' => %w(controls height loop width),
  85. },
  86. protocols: {
  87. 'embed' => { 'src' => HTTP_PROTOCOLS },
  88. 'iframe' => { 'src' => HTTP_PROTOCOLS },
  89. 'source' => { 'src' => HTTP_PROTOCOLS },
  90. },
  91. add_attributes: {
  92. 'iframe' => { 'sandbox' => 'allow-scripts allow-same-origin allow-popups allow-popups-to-escape-sandbox allow-forms' },
  93. }
  94. )
  95. end
  96. end