Implement Unified Embeds for Stackblitz URLs (#16313)

* building

* complete implementation and add specs

* handle params "?"

* changes

* optimization
This commit is contained in:
Arit Amana 2022-01-26 17:10:38 -05:00 committed by GitHub
parent feebae6ae3
commit 337657e53d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 47 additions and 32 deletions

View file

@ -1,14 +1,18 @@
class StackblitzTag < LiquidTagBase
PARTIAL = "liquids/stackblitz".freeze
ID_REGEXP = /\A[a-zA-Z0-9\-]{0,60}\z/
VIEW_OPTION_REGEXP = /\Aview=(preview|editor|both)\z/
FILE_OPTION_REGEXP = /\Afile=(.*)\z/
REGISTRY_REGEXP = %r{https://stackblitz.com/edit/(?<id>[\w\-]{,60})(?<params>\?.*)?}
ID_REGEXP = /\A(?<id>[\w\-]{,60})\Z/
REGEXP_OPTIONS = [REGISTRY_REGEXP, ID_REGEXP].freeze
# rubocop:disable Layout/LineLength
PARAM_REGEXP = /\A(view=(preview|editor|both))|(file=(.*))|(embed=1)|(hideExplorer=1)|(hideNavigation=1)|(theme=(default|light|dark))|(ctl=1)|(devtoolsheight=\d)\Z/
# rubocop:enable Layout/LineLength
def initialize(_tag_name, id, _parse_context)
def initialize(_tag_name, input, _parse_context)
super
@id = parse_id(id)
@view = parse_input(id, method(:valid_view?))
@file = parse_input(id, method(:valid_file?))
stripped_input = strip_tags(input)
unescaped_input = CGI.unescape_html(stripped_input)
@id, @params = parsed_input(unescaped_input)
@height = 500
end
@ -17,8 +21,7 @@ class StackblitzTag < LiquidTagBase
partial: PARTIAL,
locals: {
id: @id,
view: @view,
file: @file,
params: @params,
height: @height
},
)
@ -26,35 +29,34 @@ class StackblitzTag < LiquidTagBase
private
def valid_id?(id)
id =~ ID_REGEXP
def parsed_input(input)
id, *params = input.split
match = pattern_match_for(id, REGEXP_OPTIONS)
raise StandardError, I18n.t("liquid_tags.stackblitz_tag.invalid_stackblitz_id") unless match
return [match[:id], nil] unless params_present?(match) || params
build_link_with_params(match[:id], (params_present?(match) || params))
end
def parse_id(input)
input_no_space = input.split.first
raise StandardError, I18n.t("liquid_tags.stackblitz_tag.invalid_stackblitz_id") unless valid_id?(input_no_space)
def params_present?(match)
return unless match.names.include?("params")
input_no_space
match[:params].delete("?")
end
def parse_input(input, validator)
inputs = input.split
def build_link_with_params(id, params)
params = params.split("&") if params.is_a?(String)
vetted_params = params.select { |param| valid_param(param) }.join("&")
# Validation
validated_views = inputs.filter_map { |input_option| validator.call(input_option) }
raise StandardError, I18n.t("liquid_tags.stackblitz_tag.invalid_options") unless validated_views.length.between?(0,
1)
validated_views.length.zero? ? "" : validated_views.join.to_s
[id, vetted_params]
end
def valid_view?(option)
option.match(VIEW_OPTION_REGEXP)
end
def valid_file?(option)
option.match(FILE_OPTION_REGEXP)
def valid_param(param)
(param =~ PARAM_REGEXP)&.zero?
end
end
Liquid::Template.register_tag("stackblitz", StackblitzTag)
UnifiedEmbed.register(StackblitzTag, regexp: StackblitzTag::REGISTRY_REGEXP)

View file

@ -1,5 +1,5 @@
<iframe
src="https://stackblitz.com/edit/<%= id %>?embed=1&<%= view %>&<%= file %>"
src="https://stackblitz.com/edit/<%= id %>?<%= params %>"
width="100%"
height="<%= height %>"
scrolling="no"

View file

@ -47,9 +47,9 @@ RSpec.describe StackblitzTag, type: :liquid_tag do
end.not_to raise_error
end
it "parses stackblitz if with a view and file parameter" do
it "parses stackblitz id with a view and file parameter" do
liquid = generate_new_liquid(stackblitz_id_with_view_and_file)
expect(liquid.render).to include("https://stackblitz.com/edit/ball-demo?embed=1&view=preview&file=style.css")
expect(liquid.render).to include("https://stackblitz.com/edit/ball-demo?view=preview&amp;file=style.css")
end
it "rejects XSS attempts" do

View file

@ -56,6 +56,12 @@ RSpec.describe UnifiedEmbed::Registry do
"https://open.spotify.com/show/3sRrtlRiByFrOC49vPwP8L?si=c3c6c1180afe4094",
]
valid_stackblitz_url_formats = [
"https://stackblitz.com/edit/web-platform-3tqbd4",
"https://stackblitz.com/edit/web-platform-3tqbd4?embed=1&file=index.html&hideExplorer=1&hideNavigation=1&theme=dark",
"https://stackblitz.com/edit/web-platform-3tqbd4?embed=1&file=index.html&theme=light",
]
valid_twitch_url_formats = [
"https://clips.twitch.tv/embed?clip=SpeedyVivaciousDolphinKappaRoss-IQl5YslMAGKbMOGM&parent=www.example.com",
"https://player.twitch.tv/?video=1222841752&parent=www.example.com",
@ -195,6 +201,13 @@ RSpec.describe UnifiedEmbed::Registry do
end
end
it "returns StackblitzTag for a valid stackblitz url" do
valid_stackblitz_url_formats.each do |url|
expect(described_class.find_liquid_tag_for(link: url))
.to eq(StackblitzTag)
end
end
it "returns StackeryTag for a stackery url" do
expect(described_class.find_liquid_tag_for(link: "https://app.stackery.io/editor/design?owner=stackery&repo=quickstart-ruby&file=template.yaml"))
.to eq(StackeryTag)