diff --git a/app/helpers/better_together/content_helper.rb b/app/helpers/better_together/content_helper.rb new file mode 100644 index 000000000..8e4802924 --- /dev/null +++ b/app/helpers/better_together/content_helper.rb @@ -0,0 +1,36 @@ +# frozen_string_literal: true + +require 'nokogiri' +require 'uri' + +module BetterTogether + # Helper methods for content rendering + module ContentHelper + ALLOWED_TAGS = %w[ + a abbr b blockquote br cite code dd dl dt em i li ol p pre q s small strong sub sup u ul + h1 h2 h3 h4 h5 h6 img span div iframe + ].freeze + + ALLOWED_ATTRIBUTES = %w[ + href title target rel src alt class id width height frameborder allow allowfullscreen + ].freeze + + YOUTUBE_DOMAINS = %w[ + youtube.com www.youtube.com m.youtube.com youtu.be + youtube-nocookie.com www.youtube-nocookie.com + ].freeze + + def safe_html(html) + sanitized = sanitize(html.to_s, tags: ALLOWED_TAGS, attributes: ALLOWED_ATTRIBUTES) + fragment = Nokogiri::HTML::DocumentFragment.parse(sanitized) + fragment.css('iframe').each do |iframe| + src = iframe['src'] + next unless src + + uri = URI.parse(src) rescue nil + iframe.remove unless uri && YOUTUBE_DOMAINS.include?(uri.host) + end + fragment.to_html.html_safe + end + end +end diff --git a/app/views/better_together/content/blocks/_html.html.erb b/app/views/better_together/content/blocks/_html.html.erb index 67de1b803..92d57d3e6 100644 --- a/app/views/better_together/content/blocks/_html.html.erb +++ b/app/views/better_together/content/blocks/_html.html.erb @@ -1,6 +1,6 @@ <%= render layout: 'better_together/content/blocks/block', locals: { block: html } do %> <%= cache html.cache_key_with_version do %> - <%= sanitize_block_html(html.content) %> + <%= safe_html(html.content) %> <% end %> <% end %> diff --git a/spec/helpers/better_together/content_helper_spec.rb b/spec/helpers/better_together/content_helper_spec.rb new file mode 100644 index 000000000..f207a8e3b --- /dev/null +++ b/spec/helpers/better_together/content_helper_spec.rb @@ -0,0 +1,43 @@ +# frozen_string_literal: true + +require 'rails_helper' + +module BetterTogether + RSpec.describe ContentHelper, type: :helper do + describe '#safe_html' do + it 'escapes dangerous tags' do + input = "

Hello

" + output = helper.safe_html(input) + expect(output).to include('

Hello

') + expect(output).not_to include('Bold') + expect(output).to include('link') + end + + it 'allows youtube iframes' do + input = '' + output = helper.safe_html(input) + expect(output).to include('