From 3a81c82b287b6ae033dcc2cf3b779439cdcb963a Mon Sep 17 00:00:00 2001 From: dgolant <8434515+dgolant@users.noreply.github.com> Date: Tue, 9 Oct 2018 13:51:59 -0400 Subject: [PATCH] Add liquid tag for Soundcloud (#844) * Add liquid tag for Soundcloud Adds a liquid tag for soundcloud links. Currently does not support customizing autplay, display for comments, or color. * Strip HTML from Soundcloud tag input We want to prevent potential XSS exploits, so we sanitize all link input * Add Soundcloud tag documentation to editor guide --- app/liquid_tags/soundcloud_tag.rb | 46 +++++++++++++++++++ app/views/pages/_editor_guide_text.html.erb | 4 ++ spec/liquid_tags/soundcloud_tag_spec.rb | 43 +++++++++++++++++ .../soundcloud_liquid_tag.approved.html | 6 +++ 4 files changed, 99 insertions(+) create mode 100644 app/liquid_tags/soundcloud_tag.rb create mode 100644 spec/liquid_tags/soundcloud_tag_spec.rb create mode 100644 spec/support/fixtures/approvals/soundcloud_liquid_tag.approved.html diff --git a/app/liquid_tags/soundcloud_tag.rb b/app/liquid_tags/soundcloud_tag.rb new file mode 100644 index 000000000..ce37bac5b --- /dev/null +++ b/app/liquid_tags/soundcloud_tag.rb @@ -0,0 +1,46 @@ +class SoundcloudTag < LiquidTagBase + def initialize(tag_name, link, tokens) + super + @link = parse_link(link) + @height = 166 + end + + def render(_context) + # src = build_src + html = <<-HTML + + HTML + finalize_html(html) + end + + private + + def parse_link(link) + stripped_link = sanitize_link(link) + raise_error unless valid_link?(stripped_link) + stripped_link + end + + def sanitize_link(link) + link = ActionController::Base.helpers.strip_tags(link) + link = ActionController::Base.helpers.sanitize(link) + link.tr(" ", "") + end + + def valid_link?(link) + link.include?("soundcloud.com") + end + + def raise_error + raise StandardError, "Invalid Soundcloud URL" + end +end + +Liquid::Template.register_tag("soundcloud", SoundcloudTag) diff --git a/app/views/pages/_editor_guide_text.html.erb b/app/views/pages/_editor_guide_text.html.erb index 2be5abbc6..4598b01ac 100644 --- a/app/views/pages/_editor_guide_text.html.erb +++ b/app/views/pages/_editor_guide_text.html.erb @@ -144,6 +144,10 @@ {% speakerdeck 7e9f8c0fa0c949bd8025457181913fd0 %} +

Soundcloud Embed

+

Just enter the full URL of the Soundcloud track you are trying to embed.

+ {% soundcloud https://soundcloud.com/user-261265215/dev-to-review-episode-1 %} +

Parsing Liquid Tags as a Code Example

To parse Liquid tags as code, simply wrap it with a single backtick or triple backticks.

`{% mytag %}{{ site.SOMETHING }}{% endmytag %}`

diff --git a/spec/liquid_tags/soundcloud_tag_spec.rb b/spec/liquid_tags/soundcloud_tag_spec.rb new file mode 100644 index 000000000..8ed5a8816 --- /dev/null +++ b/spec/liquid_tags/soundcloud_tag_spec.rb @@ -0,0 +1,43 @@ +require "rails_helper" +require "nokogiri" + +RSpec.describe SoundcloudTag, type: :liquid_template do + describe "#link" do + let(:soundcloud_link) { "https://soundcloud.com/user-261265215/dev-to-review-episode-2" } + let(:evil_string) { "" } + let(:url_segment) { "https://w.soundcloud.com/player/?url" } + + def generate_new_liquid(link) + Liquid::Template.register_tag("soundcloud", SoundcloudTag) + Liquid::Template.parse("{% soundcloud #{link} %}") + end + + def extract_iframe_src(rendered_iframe) + parsed_iframe = Nokogiri.HTML(rendered_iframe) + iframe_src = parsed_iframe.xpath("//iframe/@src") + CGI::parse(iframe_src[0].value) + end + + it "accepts soundcloud link" do + liquid = generate_new_liquid(soundcloud_link) + rendered_soundcloud_iframe = liquid.render + Approvals.verify(rendered_soundcloud_iframe, name: "soundcloud_liquid_tag", format: :html) + end + + it "rejects invalid soundcloud link" do + expect do + generate_new_liquid("invalid_soundcloud_link") + end.to raise_error(StandardError) + end + + it "strips script input" do + allow(ActionController::Base.helpers).to receive(:strip_tags).and_return(evil_string) + + liquid = generate_new_liquid(evil_string) + rendered_soundcloud_iframe = liquid.render + iframe_src = extract_iframe_src(rendered_soundcloud_iframe) + + expect(iframe_src[url_segment]).not_to include("