sanitize_config.rb 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116
  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(h1 h2 h3 h4 h5 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 del pre blockquote code b strong u i em ul ol li),
  54. attributes: {
  55. 'a' => %w(href rel class translate),
  56. 'span' => %w(class translate),
  57. 'ol' => %w(start reversed),
  58. 'li' => %w(value),
  59. },
  60. add_attributes: {
  61. 'a' => {
  62. 'rel' => 'nofollow noopener noreferrer',
  63. 'target' => '_blank',
  64. },
  65. },
  66. protocols: {},
  67. transformers: [
  68. CLASS_WHITELIST_TRANSFORMER,
  69. TRANSLATE_TRANSFORMER,
  70. UNSUPPORTED_ELEMENTS_TRANSFORMER,
  71. UNSUPPORTED_HREF_TRANSFORMER,
  72. ]
  73. )
  74. MASTODON_OEMBED ||= freeze_config(
  75. elements: %w(audio embed iframe source video),
  76. attributes: {
  77. 'audio' => %w(controls),
  78. 'embed' => %w(height src type width),
  79. 'iframe' => %w(allowfullscreen frameborder height scrolling src width),
  80. 'source' => %w(src type),
  81. 'video' => %w(controls height loop width),
  82. },
  83. protocols: {
  84. 'embed' => { 'src' => HTTP_PROTOCOLS },
  85. 'iframe' => { 'src' => HTTP_PROTOCOLS },
  86. 'source' => { 'src' => HTTP_PROTOCOLS },
  87. },
  88. add_attributes: {
  89. 'iframe' => { 'sandbox' => 'allow-scripts allow-same-origin allow-popups allow-popups-to-escape-sandbox allow-forms' },
  90. }
  91. )
  92. end
  93. end