onebox 1.7.3 → 1.7.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. checksums.yaml +4 -4
  2. data/lib/onebox/engine.rb +2 -2
  3. data/lib/onebox/engine/audio_onebox.rb +8 -2
  4. data/lib/onebox/engine/audioboom_onebox.rb +2 -3
  5. data/lib/onebox/engine/bandcamp_onebox.rb +4 -2
  6. data/lib/onebox/engine/coub_onebox.rb +2 -1
  7. data/lib/onebox/engine/douban_onebox.rb +11 -10
  8. data/lib/onebox/engine/five_hundred_px_onebox.rb +2 -1
  9. data/lib/onebox/engine/flickr_onebox.rb +2 -1
  10. data/lib/onebox/engine/gfycat_onebox.rb +5 -3
  11. data/lib/onebox/engine/giphy_onebox.rb +4 -2
  12. data/lib/onebox/engine/google_calendar_onebox.rb +13 -9
  13. data/lib/onebox/engine/google_maps_onebox.rb +2 -2
  14. data/lib/onebox/engine/image_onebox.rb +9 -4
  15. data/lib/onebox/engine/imgur_onebox.rb +55 -34
  16. data/lib/onebox/engine/mixcloud_onebox.rb +2 -1
  17. data/lib/onebox/engine/replit_onebox.rb +2 -3
  18. data/lib/onebox/engine/sketchfab_onebox.rb +4 -2
  19. data/lib/onebox/engine/slides_onebox.rb +2 -1
  20. data/lib/onebox/engine/soundcloud_onebox.rb +2 -1
  21. data/lib/onebox/engine/standard_embed.rb +3 -3
  22. data/lib/onebox/engine/steam_store_onebox.rb +4 -2
  23. data/lib/onebox/engine/video_onebox.rb +7 -2
  24. data/lib/onebox/engine/vimeo_onebox.rb +2 -1
  25. data/lib/onebox/engine/whitelisted_generic_onebox.rb +8 -3
  26. data/lib/onebox/engine/youku_onebox.rb +5 -12
  27. data/lib/onebox/file_type_finder.rb +7 -7
  28. data/lib/onebox/helpers.rb +8 -8
  29. data/lib/onebox/layout.rb +1 -1
  30. data/lib/onebox/onebox_sanitize_config.rb +24 -0
  31. data/lib/onebox/preview.rb +9 -2
  32. data/lib/onebox/version.rb +1 -1
  33. data/onebox.gemspec +1 -0
  34. data/spec/lib/onebox/engine/audio_onebox_spec.rb +0 -4
  35. data/spec/lib/onebox/engine/image_onebox_spec.rb +0 -4
  36. data/spec/lib/onebox/engine_spec.rb +1 -7
  37. data/spec/lib/onebox/preview_spec.rb +15 -0
  38. data/spec/spec_helper.rb +2 -6
  39. data/templates/_layout.mustache +1 -1
  40. data/templates/douban.mustache +1 -1
  41. data/templates/googledocs.mustache +2 -2
  42. data/templates/whitelistedgeneric.mustache +1 -1
  43. data/templates/wikipedia.mustache +1 -1
  44. data/templates/xkcd.mustache +1 -1
  45. metadata +17 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 67ec2675eb6687bee752534aef6d1e610545717b
4
- data.tar.gz: cd82452275130c85076cb9078f25650ac9c73444
3
+ metadata.gz: 31e3e276aeb010031132237bf3e98b6b75901de0
4
+ data.tar.gz: 1619ea75ad1e8b795b6a932f6b2deabdcbb51f73
5
5
  SHA512:
6
- metadata.gz: 557f8fbdfd7e33e247b5cd82ffc89f1484b57ba075e6c1ea412382be1e846c9aaa08e14777dec3799b5ce76dc61881a01909665087b21c4bd1775aa84f18f65b
7
- data.tar.gz: 77c61683b419f729ba6d3b506b8c3bd960ae40f5b52662fb8d6e150605caea7f33e83c8aac7a3fdf9f82f4d1c92c045a571e7df2d5633c4457691e0067b735ae
6
+ metadata.gz: 4e2b1ef745998ec0cb12dee9b7454e19b834fc9fa7b895c95e8b4bb17b18926abbf1cbae6ce77d9d36a8440ab8bd460a070514f31aa4d56fd0c9a660a3c72eb4
7
+ data.tar.gz: d3ee07279fede7a2ef9dd8ebbd9410d19434c002a113950797a01afb1ae6690a12835bea7525ee358bcfc8a7f9f22ceb2dbfe4d2fa213c2a0b50da8cd592cd61
data/lib/onebox/engine.rb CHANGED
@@ -42,8 +42,8 @@ module Onebox
42
42
  @timeout = timeout || Onebox.options.timeout
43
43
  end
44
44
 
45
- # raises error if not defined in onebox engine. This is the output method for
46
- # an engine.
45
+ # raises error if not defined in onebox engine.
46
+ # This is the output method for an engine.
47
47
  def to_html
48
48
  fail NoMethodError, "Engines need to implement this method"
49
49
  end
@@ -10,8 +10,14 @@ module Onebox
10
10
  end
11
11
 
12
12
  def to_html
13
- url = ::Onebox::Helpers.normalize_url_for_output(@url)
14
- "<audio controls><source src='#{url}'><a href='#{url}'>#{url}</a></audio>"
13
+ escaped_url = ::Onebox::Helpers.normalize_url_for_output(@url)
14
+
15
+ <<-HTML
16
+ <audio controls>
17
+ <source src="#{escaped_url}">
18
+ <a href="#{escaped_url}">#{@url}</a>
19
+ </audio>
20
+ HTML
15
21
  end
16
22
  end
17
23
  end
@@ -9,11 +9,10 @@ module Onebox
9
9
 
10
10
  def placeholder_html
11
11
  oembed = get_oembed
12
-
13
- # we want the image to have the same dimensions as the embedded html
12
+ escaped_src = ::Onebox::Helpers.normalize_url_for_output(oembed[:thumbnail_url])
14
13
 
15
14
  <<-HTML
16
- <img src="#{oembed[:thumbnail_url]}" style="max-width: #{oembed[:width]}px; max-height: #{oembed[:height]}px;" #{Helpers.title_attr(oembed)}>
15
+ <img src="#{escaped_src}" style="max-width: #{oembed[:width]}px; max-height: #{oembed[:height]}px;" #{Helpers.title_attr(oembed)}>
17
16
  HTML
18
17
  end
19
18
 
@@ -9,15 +9,17 @@ module Onebox
9
9
 
10
10
  def placeholder_html
11
11
  og = get_opengraph
12
- "<img src='#{og[:image]}' height='#{og[:video_height]}' #{Helpers.title_attr(og)}>"
12
+ escaped_src = ::Onebox::Helpers.normalize_url_for_output(og[:image])
13
+ "<img src='#{escaped_src}' height='#{og[:video_height]}' #{Helpers.title_attr(og)}>"
13
14
  end
14
15
 
15
16
  def to_html
16
17
  og = get_opengraph
17
18
  src = og[:video_secure_url] || og[:video]
19
+ escaped_src = ::Onebox::Helpers.normalize_url_for_output(src)
18
20
 
19
21
  <<-HTML
20
- <iframe src="#{src}"
22
+ <iframe src="#{escaped_src}"
21
23
  width="#{og[:video_width]}"
22
24
  height="#{og[:video_height]}"
23
25
  scrolling="no"
@@ -9,7 +9,8 @@ module Onebox
9
9
 
10
10
  def placeholder_html
11
11
  oembed = get_oembed
12
- "<img src='#{oembed[:thumbnail_url]}' height='#{oembed[:thumbnail_height]}' width='#{oembed[:thumbnail_width]}' #{Helpers.title_attr(oembed)}>"
12
+ escaped_src = ::Onebox::Helpers.normalize_url_for_output(oembed[:thumbnail_url])
13
+ "<img src='#{escaped_src}' height='#{oembed[:thumbnail_height]}' width='#{oembed[:thumbnail_width]}' #{Helpers.title_attr(oembed)}>"
13
14
  end
14
15
 
15
16
  def to_html
@@ -9,14 +9,15 @@ module Onebox
9
9
 
10
10
  private
11
11
 
12
- def data
13
- {
14
- link: link,
15
- title: raw.css('title').text.gsub("\n",'').strip(),
16
- image: raw.css('img[rel*="v:"]').first['src'],
17
- description: raw.css('meta[name=description]').first['content'],
18
- }
19
- end
20
- end
21
- end
12
+ def data
13
+ {
14
+ link: link,
15
+ title: raw.css('title').text.gsub("\n",'').strip(),
16
+ image: raw.css('img[rel*="v:"]').first['src'],
17
+ description: raw.css('meta[name=description]').first['content'],
18
+ }
19
+ end
20
+
21
+ end
22
+ end
22
23
  end
@@ -9,7 +9,8 @@ module Onebox
9
9
 
10
10
  def to_html
11
11
  og = get_opengraph
12
- "<img src='#{og[:image]}' width='#{og[:image_width]}' height='#{og[:image_height]}' #{Helpers.title_attr(og)}>"
12
+ escaped_src = ::Onebox::Helpers.normalize_url_for_output(og[:image])
13
+ "<img src='#{escaped_src}' width='#{og[:image_width]}' height='#{og[:image_height]}' #{Helpers.title_attr(og)}>"
13
14
  end
14
15
 
15
16
  end
@@ -9,7 +9,8 @@ module Onebox
9
9
 
10
10
  def to_html
11
11
  og = get_opengraph
12
- "<img src='#{og[:image]}' width='#{og[:image_width]}' height='#{og[:image_height]}' #{Helpers.title_attr(og)}>"
12
+ escaped_src = ::Onebox::Helpers.normalize_url_for_output(og[:image])
13
+ "<img src='#{escaped_src}' width='#{og[:image_width]}' height='#{og[:image_height]}' #{Helpers.title_attr(og)}>"
13
14
  end
14
15
 
15
16
  end
@@ -10,9 +10,10 @@ module Onebox
10
10
  def to_html
11
11
  oembed = get_oembed
12
12
  src = Nokogiri::HTML::fragment(oembed[:html]).at_css("iframe")["src"]
13
+ escaped_src = ::Onebox::Helpers.normalize_url_for_output(src)
13
14
 
14
15
  <<-HTML
15
- <iframe src="#{src}"
16
+ <iframe src="#{escaped_src}"
16
17
  width="#{oembed[:width]}"
17
18
  height="#{oembed[:height]}"
18
19
  scrolling="no"
@@ -23,10 +24,11 @@ module Onebox
23
24
  end
24
25
 
25
26
  def placeholder_html
26
- opengraph = get_opengraph
27
+ og = get_opengraph
28
+ escaped_src = ::Onebox::Helpers.normalize_url_for_output(og[:image])
27
29
 
28
30
  <<-HTML
29
- <img src="#{opengraph[:image]}" width=""#{opengraph[:image_width]}" height=""#{opengraph[:image_height]}">
31
+ <img src="#{escaped_src}" width="#{og[:image_width]}" height="#{og[:image_height]}">
30
32
  HTML
31
33
  end
32
34
 
@@ -9,10 +9,12 @@ module Onebox
9
9
 
10
10
  def to_html
11
11
  oembed = get_oembed
12
+ escaped_url = ::Onebox::Helpers.normalize_url_for_output(oembed[:url])
13
+ escaped_src = ::Onebox::Helpers.normalize_url_for_output(oembed[:image])
12
14
 
13
15
  <<-HTML
14
- <a href="#{oembed[:url]}" target="_blank">
15
- <img src="#{oembed[:image]}" width="#{oembed[:width]}" height="#{oembed[:height]}" #{Helpers.title_attr(oembed)}>
16
+ <a href="#{escaped_url}" target="_blank">
17
+ <img src="#{escaped_src}" width="#{oembed[:width]}" height="#{oembed[:height]}" #{Helpers.title_attr(oembed)}>
16
18
  </a>
17
19
  HTML
18
20
  end
@@ -8,18 +8,22 @@ module Onebox
8
8
 
9
9
  def to_html
10
10
  url = @url.split('&').first
11
- "<iframe src='#{url}&rm=minimal' style='border: 0' width='800' height='600' frameborder='0' scrolling='no'>#{placeholder_html}</iframe>"
11
+ escaped_src = ::Onebox::Helpers.normalize_url_for_output(url)
12
+ "<iframe src='#{src}&rm=minimal' style='border: 0' width='800' height='600' frameborder='0' scrolling='no'>#{placeholder_html}</iframe>"
12
13
  end
13
14
 
14
15
  def placeholder_html
15
- <<HTML
16
- <div placeholder><div class='gdocs-onebox gdocs-onebox-splash' style='display:table-cell;vertical-align:middle;width:800px;height:600px'>
17
- <div style='text-align:center;'>
18
- <div class='gdocs-onebox-logo g-calendar-logo'></div>
19
- <p>Google Calendar</p>
20
- </div></div></div>
21
- HTML
16
+ <<-HTML
17
+ <div placeholder>
18
+ <div class='gdocs-onebox gdocs-onebox-splash' style='display:table-cell;vertical-align:middle;width:800px;height:600px'>
19
+ <div style='text-align:center;'>
20
+ <div class='gdocs-onebox-logo g-calendar-logo'></div>
21
+ <p>Google Calendar</p>
22
+ </div>
23
+ </div>
24
+ </div>
25
+ HTML
22
26
  end
23
27
  end
24
28
  end
25
- end
29
+ end
@@ -52,9 +52,9 @@ module Onebox
52
52
  end
53
53
 
54
54
  def placeholder_html
55
- width = @placeholder_width || 690
55
+ width = @placeholder_width || 690
56
56
  height = @placeholder_height || 400
57
- "<img src=\"#{CGI.escapeHTML(@placeholder)}\" width=\"#{width}\" height=\"#{height}\"/>"
57
+ "<img src='#{@placeholder}' width='#{width}' height='#{height}'/>"
58
58
  end
59
59
 
60
60
  private
@@ -11,12 +11,17 @@ module Onebox
11
11
 
12
12
  def to_html
13
13
  # Fix Dropbox image links
14
- if /^https:\/\/www.dropbox.com\/s\//.match @url
15
- @url.gsub!("https://www.dropbox.com","https://dl.dropboxusercontent.com")
14
+ if @url[/^https:\/\/www.dropbox.com\/s\//]
15
+ @url.sub!("https://www.dropbox.com", "https://dl.dropboxusercontent.com")
16
16
  end
17
17
 
18
- escaped = Onebox::Helpers.normalize_url_for_output(url)
19
- "<a href='#{escaped}' target='_blank'><img src='#{escaped}'></a>"
18
+ escaped_url = ::Onebox::Helpers.normalize_url_for_output(@url)
19
+
20
+ <<-HTML
21
+ <a href="#{escaped_url}" target="_blank">
22
+ <img src="#{escaped_url}">
23
+ </a>
24
+ HTML
20
25
  end
21
26
  end
22
27
  end
@@ -8,46 +8,67 @@ module Onebox
8
8
  always_https
9
9
 
10
10
  def to_html
11
- imgur_data = get_imgur_data
12
- return "<video width='#{imgur_data[:"video:width"]}' height='#{imgur_data[:"video:height"]}' #{Helpers.title_attr(imgur_data)} controls autoplay loop><source src='#{imgur_data[:"video:secure_url"]}' type='video/mp4'><source src='#{imgur_data[:"video:secure_url"].gsub('mp4', 'webm')}' type='video/webm'></video>" if imgur_data[:"video:secure_url"]
13
- return "<div class='onebox imgur-album'><a href='#{url}' target='_blank'><span class='outer-box' style='width:#{imgur_data[:"image:width"]}px'><span class='inner-box'><span class='album-title'>[Album] #{imgur_data[:title]}</span></span></span><img src='#{get_secure_link(imgur_data[:image])}' #{Helpers.title_attr(imgur_data)} height='#{imgur_data[:"image:height"]}' width='#{imgur_data[:"image:width"]}'></a></div>" if is_album?
14
- return "<a href='#{url}' target='_blank'><img src='#{get_secure_link(imgur_data[:image])}' #{Helpers.title_attr(imgur_data)} alt='Imgur' height='#{imgur_data[:"image:height"]}' width='#{imgur_data[:"image:width"]}'></a>" if imgur_data[:image]
15
- return nil
16
- end
17
-
18
- def placeholder_html
19
- imgur_data = get_imgur_data
20
- return "<video width='#{imgur_data[:"video:width"]}' height='#{imgur_data[:"video:height"]}' #{Helpers.title_attr(imgur_data)} controls autoplay loop><source src='#{imgur_data[:"video:secure_url"]}' type='video/mp4'><source src='#{imgur_data[:"video:secure_url"].gsub('mp4', 'webm')}' type='video/webm'></video>" if imgur_data[:"video:secure_url"]
21
- return "<img src='#{get_secure_link(imgur_data[:image])}' #{Helpers.title_attr(imgur_data)} alt='Imgur' height='#{imgur_data[:"image:height"]}' width='#{imgur_data[:"image:width"]}'>"
22
- return nil
11
+ og = get_opengraph
12
+ return video_html(og) if !Onebox::Helpers::blank?(og[:video_secure_url])
13
+ return album_html(og) if is_album?
14
+ return image_html(og) if !Onebox::Helpers::blank?(og[:image])
15
+ nil
23
16
  end
24
17
 
25
18
  private
26
- def get_imgur_data
27
- response = Onebox::Helpers.fetch_response(url)
28
- html = Nokogiri::HTML(response.body)
29
- imgur_data = {}
30
- html.css('meta').each do |m|
31
- if m.attribute('property') && m.attribute('property').to_s.match(/^og:/i)
32
- m_content = m.attribute('content').to_s.strip
33
- m_property = m.attribute('property').to_s.gsub('og:', '')
34
- imgur_data[m_property.to_sym] = m_content
35
- end
19
+
20
+ def video_html(og)
21
+ escaped_src = ::Onebox::Helpers.normalize_url_for_output(og[:video_secure_url])
22
+
23
+ <<-HTML
24
+ <video width='#{og[:video_width]}' height='#{og[:video_height]}' #{Helpers.title_attr(og)} controls loop>
25
+ <source src='#{escaped_src}' type='video/mp4'>
26
+ <source src='#{escaped_src.gsub('mp4', 'webm')}' type='video/webm'>
27
+ </video>
28
+ HTML
36
29
  end
37
- return imgur_data
38
- end
39
30
 
40
- def is_album?
41
- oembed_data = Onebox::Helpers.symbolize_keys(::MultiJson.load(Onebox::Helpers.fetch_response("http://api.imgur.com/oembed.json?url=#{url}").body))
42
- imgur_data_id = Nokogiri::HTML(oembed_data[:html]).xpath("//blockquote").attr("data-id")
43
- return !!(imgur_data_id.to_s =~ /a\//)
44
- end
31
+ def album_html(og)
32
+ escaped_url = ::Onebox::Helpers.normalize_url_for_output(url)
33
+ escaped_src = ::Onebox::Helpers.normalize_url_for_output(get_secure_link(og[:image]))
34
+
35
+ <<-HTML
36
+ <div class='onebox imgur-album'>
37
+ <a href='#{escaped_url}' target='_blank'>
38
+ <span class='outer-box' style='width:#{og[:image_width]}px'>
39
+ <span class='inner-box'>
40
+ <span class='album-title'>[Album] #{og[:title]}</span>
41
+ </span>
42
+ </span>
43
+ <img src='#{escaped_src}' #{Helpers.title_attr(og)} height='#{og[:image_height]}' width='#{og[:image_width]}'>
44
+ </a>
45
+ </div>
46
+ HTML
47
+ end
48
+
49
+ def is_album?
50
+ oembed_data = Onebox::Helpers.symbolize_keys(::MultiJson.load(Onebox::Helpers.fetch_response("http://api.imgur.com/oembed.json?url=#{url}").body))
51
+ imgur_data_id = Nokogiri::HTML(oembed_data[:html]).xpath("//blockquote").attr("data-id")
52
+ imgur_data_id.to_s[/a\//]
53
+ end
54
+
55
+ def image_html(og)
56
+ escaped_url = ::Onebox::Helpers.normalize_url_for_output(url)
57
+ escaped_src = ::Onebox::Helpers.normalize_url_for_output(get_secure_link(og[:image]))
58
+
59
+ <<-HTML
60
+ <a href='#{escaped_url}' target='_blank'>
61
+ <img src='#{escaped_src}' #{Helpers.title_attr(og)} alt='Imgur' height='#{og[:image_height]}' width='#{og[:image_width]}'>
62
+ </a>
63
+ HTML
64
+ end
65
+
66
+ def get_secure_link(link)
67
+ secure_link = URI(link)
68
+ secure_link.scheme = 'https'
69
+ secure_link.to_s
70
+ end
45
71
 
46
- def get_secure_link(link)
47
- secure_link = URI(link)
48
- secure_link.scheme = 'https'
49
- secure_link.to_s
50
- end
51
72
  end
52
73
  end
53
74
  end
@@ -9,7 +9,8 @@ module Onebox
9
9
 
10
10
  def placeholder_html
11
11
  oembed = get_oembed
12
- "<img src='#{oembed[:image]}' height='#{oembed[:height]}' #{Helpers.title_attr(oembed)}>"
12
+ escaped_src = ::Onebox::Helpers.normalize_url_for_output(oembed[:image])
13
+ "<img src='#{escaped_src}' height='#{oembed[:height]}' #{Helpers.title_attr(oembed)}>"
13
14
  end
14
15
 
15
16
  def to_html
@@ -9,11 +9,10 @@ module Onebox
9
9
 
10
10
  def placeholder_html
11
11
  oembed = get_oembed
12
-
13
- # we want the image to have the same dimensions as the embedded html
12
+ escaped_src = ::Onebox::Helpers.normalize_url_for_output(oembed[:thumbnail_url])
14
13
 
15
14
  <<-HTML
16
- <img src="#{oembed[:thumbnail_url]}" style="max-width: #{oembed[:width]}px; max-height: #{oembed[:height]}px;" #{Helpers.title_attr(oembed)}>
15
+ <img src="#{escaped_src}" style="max-width: #{oembed[:width]}px; max-height: #{oembed[:height]}px;" #{Helpers.title_attr(oembed)}>
17
16
  HTML
18
17
  end
19
18
 
@@ -11,9 +11,10 @@ module Onebox
11
11
  opengraph = get_opengraph
12
12
 
13
13
  src = opengraph[:video_url].gsub("?autostart=1", "")
14
+ escaped_src = ::Onebox::Helpers.normalize_url_for_output(src)
14
15
 
15
16
  <<-HTML
16
- <iframe src="#{src}"
17
+ <iframe src="#{escaped_src}"
17
18
  width="#{opengraph[:video_width]}"
18
19
  height="#{opengraph[:video_height]}"
19
20
  scrolling="no"
@@ -25,7 +26,8 @@ module Onebox
25
26
 
26
27
  def placeholder_html
27
28
  opengraph = get_opengraph
28
- "<img src='#{opengraph[:image]}'>"
29
+ escaped_src = ::Onebox::Helpers.normalize_url_for_output(opengraph[:image])
30
+ "<img src='#{escaped_src}'>"
29
31
  end
30
32
 
31
33
  end
@@ -21,7 +21,8 @@ module Onebox
21
21
  end
22
22
 
23
23
  def placeholder_html
24
- "<img src='#{raw[:image]}'>"
24
+ escaped_src = ::Onebox::Helpers.normalize_url_for_output(raw[:image])
25
+ "<img src='#{escaped_src}'>"
25
26
  end
26
27
 
27
28
  end
@@ -13,7 +13,8 @@ module Onebox
13
13
 
14
14
  def placeholder_html
15
15
  return if Onebox::Helpers.blank?(oembed_data[:thumbnail_url])
16
- "<img src='#{oembed_data[:thumbnail_url]}' #{Helpers.title_attr(oembed_data)}>"
16
+ escaped_src = ::Onebox::Helpers.normalize_url_for_output(oembed_data[:thumbnail_url])
17
+ "<img src='#{escaped_src}' #{Helpers.title_attr(oembed_data)}>"
17
18
  end
18
19
 
19
20
  private
@@ -95,14 +95,14 @@ module Onebox
95
95
  html_doc.css('meta').each do |m|
96
96
  if (m["property"] && m["property"][/^og:(.+)$/i]) || (m["name"] && m["name"][/^og:(.+)$/i])
97
97
  value = (m["content"] || m["value"]).to_s
98
- og[$1.tr('-:','_').to_sym] ||= CGI.escapeHTML(value) unless Onebox::Helpers::blank?(value)
98
+ og[$1.tr('-:','_').to_sym] ||= value unless Onebox::Helpers::blank?(value)
99
99
  end
100
100
  end
101
101
 
102
102
  # Attempt to retrieve the title from the meta tag
103
103
  title_element = html_doc.at_css('title')
104
104
  if title_element && title_element.text
105
- og[:title] ||= CGI.escapeHTML(title_element.text) unless Onebox::Helpers.blank?(title_element.text)
105
+ og[:title] ||= title_element.text unless Onebox::Helpers.blank?(title_element.text)
106
106
  end
107
107
 
108
108
  og
@@ -116,7 +116,7 @@ module Onebox
116
116
  html_doc.css('meta').each do |m|
117
117
  if (m["property"] && m["property"][/^twitter:(.+)$/i]) || (m["name"] && m["name"][/^twitter:(.+)$/i])
118
118
  value = (m["content"] || m["value"]).to_s
119
- twitter[$1.tr('-:','_').to_sym] ||= CGI.escapeHTML(value) unless Onebox::Helpers::blank?(value)
119
+ twitter[$1.tr('-:','_').to_sym] ||= value unless Onebox::Helpers::blank?(value)
120
120
  end
121
121
  end
122
122
 
@@ -9,11 +9,12 @@ module Onebox
9
9
 
10
10
  def placeholder_html
11
11
  og = get_opengraph
12
+ escaped_src = ::Onebox::Helpers.normalize_url_for_output(og[:image])
12
13
  <<-HTML
13
14
  <div style='width:100%; height:190px; background-color:#262626; color:#9e9e9e; margin:15px 0;'>
14
15
  <div style='padding:10px'>
15
16
  <h3 style='color:#fff; margin:10px 0 10px 5px;'>#{og[:title]}</h3>
16
- <img src='#{og[:image]}' style='float:left; max-width:184px; margin:5px 15px 0 5px'/>
17
+ <img src='escaped_src' style='float:left; max-width:184px; margin:5px 15px 0 5px'/>
17
18
  <p>#{og[:description]}</p>
18
19
  </div>
19
20
  </div>
@@ -22,9 +23,10 @@ module Onebox
22
23
 
23
24
  def to_html
24
25
  iframe_url = @url.gsub('/app/', '/widget/')
26
+ escaped_src = ::Onebox::Helpers.normalize_url_for_output(iframe_url)
25
27
 
26
28
  <<-HTML
27
- <iframe src='#{iframe_url}'
29
+ <iframe src='#{escaped_src}'
28
30
  frameborder='0'
29
31
  width='100%'
30
32
  height='190'>
@@ -10,8 +10,13 @@ module Onebox
10
10
  end
11
11
 
12
12
  def to_html
13
- url = ::Onebox::Helpers.normalize_url_for_output(@url)
14
- "<video width='100%' height='100%' controls><source src='#{url}'><a href='#{url}'>#{url}</a></video>"
13
+ escaped_url = ::Onebox::Helpers.normalize_url_for_output(@url)
14
+ <<-HTML
15
+ <video width='100%' height='100%' controls>
16
+ <source src='#{escaped_url}'>
17
+ <a href='#{escaped_url}'>#{@url}</a>
18
+ </video>
19
+ HTML
15
20
  end
16
21
  end
17
22
  end
@@ -9,7 +9,8 @@ module Onebox
9
9
 
10
10
  def placeholder_html
11
11
  oembed = get_oembed
12
- "<img src='#{oembed[:thumbnail_url]}' width='#{oembed[:thumbnail_width]}' height='#{oembed[:thumbnail_height]}' #{Helpers.title_attr(oembed)}>"
12
+ escaped_src = ::Onebox::Helpers.normalize_url_for_output(oembed[:thumbnail_url])
13
+ "<img src='#{escaped_src}' width='#{oembed[:thumbnail_width]}' height='#{oembed[:thumbnail_height]}' #{Helpers.title_attr(oembed)}>"
13
14
  end
14
15
 
15
16
  def to_html
@@ -278,13 +278,18 @@ module Onebox
278
278
  def image_html
279
279
  return if Onebox::Helpers.blank?(data[:image])
280
280
 
281
+ escaped_src = ::Onebox::Helpers.normalize_url_for_output(data[:image])
282
+
281
283
  alt = data[:description] || data[:title]
282
284
  width = data[:image_width] || data[:thumbnail_width] || data[:width]
283
285
  height = data[:image_height] || data[:thumbnail_height] || data[:height]
284
- "<img src='#{data[:image]}' alt='#{alt}' width='#{width}' height='#{height}'>"
286
+
287
+ "<img src='#{escaped_src}' alt='#{alt}' width='#{width}' height='#{height}'>"
285
288
  end
286
289
 
287
290
  def video_html
291
+ escaped_src = ::Onebox::Helpers.normalize_url_for_output(data[:video])
292
+
288
293
  if data[:video_type] == "video/mp4"
289
294
  <<-HTML
290
295
  <video title='#{data[:title]}'
@@ -292,12 +297,12 @@ module Onebox
292
297
  height='#{data[:video_height]}'
293
298
  style='max-width:100%'
294
299
  controls=''>
295
- <source src='#{data[:video]}'>
300
+ <source src='#{escaped_src}'>
296
301
  </video>
297
302
  HTML
298
303
  else
299
304
  <<-HTML
300
- <iframe src='#{data[:video]}'
305
+ <iframe src='#{escaped_src}'
301
306
  title='#{data[:title]}'
302
307
  width='#{data[:video_width]}'
303
308
  height='#{data[:video_height]}'
@@ -11,28 +11,21 @@ module Onebox
11
11
  # * http://v.youku.com/v_show/id_XMTQ5MjgyMjMyOA==.html?from=y1.3-tech-index3-232-10183.89969-89963.3-1
12
12
  def video_id
13
13
  match = uri.path.match(/\/v_show\/id_([a-zA-Z0-9_=\-]+)(\.html)?.*/)
14
- return match[1] if match && match[1]
15
-
16
- nil
14
+ match && match[1]
17
15
  rescue
18
- return nil
16
+ nil
19
17
  end
20
18
 
21
19
  def to_html
22
- "<embed width='570' height='360' src='https://players.youku.com/player.php/sid/#{video_id}/v.swf' wmode='transparent' allowFullScreen='true' quality='high' align='middle' allowScriptAccess='always' type='application/x-shockwave-flash'></embed>"
23
- end
24
-
25
- def placeholder_html
26
- to_html
20
+ "<embed width='570' height='360' src='https://players.youku.com/player.php/sid/#{video_id}/v.swf' type='application/x-shockwave-flash'></embed>"
27
21
  end
28
22
 
29
23
  private
30
24
 
31
- # Note: May throw! Make sure to rescue.
32
25
  def uri
33
26
  @_uri ||= URI(@url)
34
27
  end
35
28
 
36
- end
37
- end
29
+ end
30
+ end
38
31
  end
@@ -1,6 +1,6 @@
1
1
  module Onebox
2
2
  module FileTypeFinder
3
-
3
+
4
4
  # In general, most of file extension names would be recognized
5
5
  # by Highlights.js. However, some need to be checked in other
6
6
  # ways, either because they just aren't included, because they
@@ -19,7 +19,7 @@ module Onebox
19
19
  ".simplecov" => "rb", # Not official, but seems commonly found
20
20
  ".sty" => "tex"
21
21
  }
22
-
22
+
23
23
  # Some extensionless files for which we know the type
24
24
  # These should all be stored LOWERCASE, just for consistency.
25
25
  # The ones that I know of also include the ".lock" fake extension.
@@ -28,7 +28,7 @@ module Onebox
28
28
  # FIRST by their types and THEN by their names.
29
29
  @extensionless_files = {
30
30
  "cmake.in" => "cmake",
31
-
31
+
32
32
  "gruntfile" => "js",
33
33
  "gulpfile" => "js",
34
34
 
@@ -43,7 +43,7 @@ module Onebox
43
43
  "rakefile" => "rb",
44
44
  "thorfile" => "rb",
45
45
  "vagrantfile" => "rb",
46
-
46
+
47
47
  "boxfile" => "yaml" # Not currently (2014-11) in Highlight.js
48
48
  }
49
49
 
@@ -51,7 +51,7 @@ module Onebox
51
51
  lower_name = file_name.downcase
52
52
  # First check against the known lists of "special" files and extensions.
53
53
  return @extensionless_files[lower_name] if @extensionless_files.has_key?(lower_name)
54
-
54
+
55
55
  @long_file_types.each { |extension,type|
56
56
  return type if lower_name.end_with?(extension)
57
57
  }
@@ -60,10 +60,10 @@ module Onebox
60
60
  # but add one so we don't return the "." itself.
61
61
  dot_spot = lower_name.rindex(".")
62
62
  return lower_name[(dot_spot+1)..-1] if dot_spot
63
-
63
+
64
64
  # If we couldn't figure it out from the name,
65
65
  # let the highlighter figure it out from the content.
66
- return ""
66
+ ""
67
67
  end
68
68
  end
69
69
  end
@@ -19,9 +19,8 @@ module Onebox
19
19
  raise Net::HTTPError.new('HTTP redirect too deep', location) if limit == 0
20
20
 
21
21
  uri = URI(location)
22
- if !uri.host
23
- uri = URI("#{domain}#{location}")
24
- end
22
+ uri = URI("#{domain}#{location}") if !uri.host
23
+
25
24
  http = Net::HTTP.new(uri.host, uri.port)
26
25
  http.open_timeout = Onebox.options.connect_timeout
27
26
  http.read_timeout = Onebox.options.timeout
@@ -32,10 +31,10 @@ module Onebox
32
31
 
33
32
  response = http.request_get(uri.request_uri,headers)
34
33
 
35
- cookie = response.get_fields('set-cookie')
36
- if (cookie)
37
- header = {'cookie' => cookie.join("")}
34
+ if cookie = response.get_fields('set-cookie')
35
+ header = { 'cookie' => cookie.join }
38
36
  end
37
+
39
38
  header = nil unless header.is_a? Hash
40
39
 
41
40
  case response
@@ -63,15 +62,16 @@ module Onebox
63
62
  end
64
63
 
65
64
  def self.title_attr(meta)
66
- (meta && !blank?(meta[:title])) ? "title='#{CGI.escapeHTML(meta[:title])}'" : ""
65
+ (meta && !blank?(meta[:title])) ? "title='#{meta[:title]}'" : ""
67
66
  end
68
67
 
69
68
  def self.normalize_url_for_output(url)
69
+ return "" unless url
70
70
  url = url.dup
71
71
  # expect properly encoded url, remove any unsafe chars
72
72
  url.gsub!("'", "&apos;")
73
73
  url.gsub!('"', "&quot;")
74
- url.gsub!(/[^a-zA-Z0-9%\-`._~:\/?#\[\]@!$&'\(\)*+,;=]/, "")
74
+ url.gsub!(/[^\w\-`._~:\/?#\[\]@!$&'\(\)*+,;=]/, "")
75
75
  url
76
76
  end
77
77
 
data/lib/onebox/layout.rb CHANGED
@@ -42,7 +42,7 @@ module Onebox
42
42
  end
43
43
 
44
44
  def link
45
- record[:link]
45
+ ::Onebox::Helpers.normalize_url_for_output(record[:link])
46
46
  end
47
47
 
48
48
  def domain
@@ -0,0 +1,24 @@
1
+ class Sanitize
2
+ module Config
3
+
4
+ HTTP_PROTOCOLS ||= ['http', 'https', :relative].freeze
5
+
6
+ ONEBOX ||= freeze_config merge(RELAXED,
7
+ elements: RELAXED[:elements] + %w[audio embed iframe source video],
8
+
9
+ attributes: merge(RELAXED[:attributes],
10
+ 'audio' => %w[controls],
11
+ 'embed' => %w[height src type width],
12
+ 'iframe' => %w[allowfullscreen frameborder height scrolling src width],
13
+ 'source' => %w[src type],
14
+ 'video' => %w[controls height loop width],
15
+ ),
16
+
17
+ protocols: merge(RELAXED[:protocols],
18
+ 'embed' => { 'src' => HTTP_PROTOCOLS },
19
+ 'iframe' => { 'src' => HTTP_PROTOCOLS },
20
+ 'source' => { 'src' => HTTP_PROTOCOLS },
21
+ ),
22
+ )
23
+ end
24
+ end
@@ -1,3 +1,6 @@
1
+ require "sanitize"
2
+ require_relative "onebox_sanitize_config"
3
+
1
4
  module Onebox
2
5
  class Preview
3
6
  attr_reader :cache
@@ -11,14 +14,14 @@ module Onebox
11
14
 
12
15
  def to_s
13
16
  return "" unless engine
14
- process_html(engine_html)
17
+ sanitize process_html engine_html
15
18
  rescue *Onebox::Preview.web_exceptions
16
19
  ""
17
20
  end
18
21
 
19
22
  def placeholder_html
20
23
  return "" unless engine
21
- process_html(engine.placeholder_html)
24
+ sanitize process_html engine.placeholder_html
22
25
  rescue *Onebox::Preview.web_exceptions
23
26
  ""
24
27
  end
@@ -62,6 +65,10 @@ module Onebox
62
65
  html
63
66
  end
64
67
 
68
+ def sanitize(html)
69
+ Sanitize.fragment(html, Sanitize::Config::ONEBOX)
70
+ end
71
+
65
72
  def engine
66
73
  return nil unless @engine_class
67
74
  @engine ||= @engine_class.new(@url, cache)
@@ -1,3 +1,3 @@
1
1
  module Onebox
2
- VERSION = "1.7.3"
2
+ VERSION = "1.7.4"
3
3
  end
data/onebox.gemspec CHANGED
@@ -24,6 +24,7 @@ Gem::Specification.new do |spec|
24
24
  spec.add_runtime_dependency 'moneta', '~> 0.8'
25
25
  spec.add_runtime_dependency 'htmlentities', '~> 4.3.4'
26
26
  spec.add_runtime_dependency 'fast_blank', '>= 1.0.0'
27
+ spec.add_runtime_dependency 'sanitize'
27
28
 
28
29
  spec.add_development_dependency 'bundler', '~> 1.7'
29
30
  spec.add_development_dependency 'rake', '~> 10.4'
@@ -28,8 +28,4 @@ describe Onebox::Engine::AudioOnebox do
28
28
  it "includes a fallback direct link to the audio" do
29
29
  expect(Onebox.preview('http://kolber.github.io/audiojs/demos/mp3/juicy.mp3').to_s).to match(/<a.*mp3/)
30
30
  end
31
-
32
- it "correctly escapes single quotes" do
33
- expect(Onebox.preview("http://test.com/test'ing.mp3").to_s).not_to match(/test'ing/)
34
- end
35
31
  end
@@ -36,8 +36,4 @@ describe Onebox::Engine::ImageOnebox do
36
36
  it "includes a direct link to the image" do
37
37
  expect(Onebox.preview('http://www.discourse.org/images/logo.png').to_s).to match(/<a.*png/)
38
38
  end
39
-
40
- it "doesn't inline single quotes" do
41
- expect(Onebox.preview("http://host/path/to/Image'withquote.png").to_s).to match(/Image&apos;withquote/)
42
- end
43
39
  end
@@ -18,18 +18,12 @@ describe Onebox::Engine do
18
18
  end
19
19
 
20
20
  describe "#link" do
21
- before { allow(Onebox::View).to receive(:template) { %|this shold be a template| } }
21
+ before { allow(Onebox::View).to receive(:template) { %|this should be a template| } }
22
22
 
23
23
  it "escapes `link`" do
24
24
  html = OneboxEngineExample.new(%|http://foo.com/'?a=1&b=2|).to_html
25
25
  expect(html).not_to match(/&(?!amp;)(?!#39;)/)
26
26
  end
27
-
28
- it "escapes xss" do
29
- skip 'this is checking the wrong thing'
30
- html = OneboxEngineExample.new(%|http://foo.com/'?%20onmouseover=alert(/foo/)|).to_html
31
- expect(html).not_to include(%|onmouseover=alert(/foo/)|)
32
- end
33
27
  end
34
28
 
35
29
  describe "#record" do
@@ -82,4 +82,19 @@ describe Onebox::Preview do
82
82
  expect(preview.send(:engine)).to be_an(Onebox::Engine)
83
83
  end
84
84
  end
85
+
86
+ describe "xss" do
87
+ let(:xss) { "wat' onerror='alert(/XSS/)" }
88
+ let(:img_html) { "<img src='#{xss}'>" }
89
+
90
+ it "prevents XSS" do
91
+ preview = described_class.new(preview_url)
92
+ preview.expects(:engine_html).returns(img_html)
93
+
94
+ result = preview.to_s
95
+ expect(result).not_to match(/onerror/)
96
+ end
97
+
98
+ end
99
+
85
100
  end
data/spec/spec_helper.rb CHANGED
@@ -26,10 +26,6 @@ shared_context "engines" do
26
26
  let(:html) { @html }
27
27
  let(:data) { @data }
28
28
  let(:link) { @link }
29
-
30
- def escaped_data(key)
31
- CGI.escapeHTML(data[key])
32
- end
33
29
  end
34
30
 
35
31
  shared_examples_for "an engine" do
@@ -64,7 +60,7 @@ shared_examples_for "a layout engine" do
64
60
  end
65
61
 
66
62
  it "includes title" do
67
- expect(html).to include(escaped_data(:title))
63
+ expect(html).to include(data[:title])
68
64
  end
69
65
 
70
66
  it "includes link" do
@@ -76,7 +72,7 @@ shared_examples_for "a layout engine" do
76
72
  end
77
73
 
78
74
  it "includes domain" do
79
- expect(html).to include(%|class="domain" href="#{escaped_data(:domain)}|)
75
+ expect(html).to include(%|class="domain" href="#{data[:domain]}|)
80
76
  end
81
77
  end
82
78
  end
@@ -1,6 +1,6 @@
1
1
  <aside class="onebox {{subname}}">
2
2
  <header class="source">
3
- <a href="{{{link}}}" target='_blank'>{{domain}}</a>
3
+ <a href="{{link}}" target='_blank'>{{domain}}</a>
4
4
  </header>
5
5
  <article class="onebox-body">
6
6
  {{{view}}}
@@ -1,5 +1,5 @@
1
1
  <img src="{{image}}" class="thumbnail"/>
2
2
 
3
- <h3><a href='{{{link}}}' target='_blank'>{{title}}</a></h3>
3
+ <h3><a href='{{link}}' target='_blank'>{{title}}</a></h3>
4
4
 
5
5
  <p>{{description}}</p>
@@ -1,5 +1,5 @@
1
- <a href='{{{link}}}' target="_blank"><span class='googledocs-onebox-logo g-{{type}}-logo'></span></a>
1
+ <a href='{{link}}' target="_blank"><span class='googledocs-onebox-logo g-{{type}}-logo'></span></a>
2
2
 
3
- <h3><a href='{{{link}}}' target="_blank">{{title}}</a></h3>
3
+ <h3><a href='{{link}}' target="_blank">{{title}}</a></h3>
4
4
 
5
5
  <p>{{description}}</p>
@@ -1,5 +1,5 @@
1
1
  {{#image}}<img src="{{image}}" width="{{image_width}}" height="{{image_height}}" class="thumbnail"/>{{/image}}
2
2
 
3
- <h3><a href='{{{link}}}' target="_blank">{{title}}</a></h3>
3
+ <h3><a href='{{link}}' target="_blank">{{title}}</a></h3>
4
4
 
5
5
  <p>{{description}}</p>
@@ -1,5 +1,5 @@
1
1
  {{#image}}<img src="{{image}}" class="thumbnail"/>{{/image}}
2
2
 
3
- <h3><a href='{{{link}}}' target='_blank'>{{title}}</a></h3>
3
+ <h3><a href='{{link}}' target='_blank'>{{title}}</a></h3>
4
4
 
5
5
  <p>{{description}}</p>
@@ -1,4 +1,4 @@
1
- <h3><a href='{{{link}}}' target='_blank'>{{title}}</a></h3>
1
+ <h3><a href='{{link}}' target='_blank'>{{title}}</a></h3>
2
2
 
3
3
  <img src="{{image}}" />
4
4
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: onebox
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.7.3
4
+ version: 1.7.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Joanna Zeta
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2017-01-11 00:00:00.000000000 Z
13
+ date: 2017-01-18 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: multi_json
@@ -96,6 +96,20 @@ dependencies:
96
96
  - - ">="
97
97
  - !ruby/object:Gem::Version
98
98
  version: 1.0.0
99
+ - !ruby/object:Gem::Dependency
100
+ name: sanitize
101
+ requirement: !ruby/object:Gem::Requirement
102
+ requirements:
103
+ - - ">="
104
+ - !ruby/object:Gem::Version
105
+ version: '0'
106
+ type: :runtime
107
+ prerelease: false
108
+ version_requirements: !ruby/object:Gem::Requirement
109
+ requirements:
110
+ - - ">="
111
+ - !ruby/object:Gem::Version
112
+ version: '0'
99
113
  - !ruby/object:Gem::Dependency
100
114
  name: bundler
101
115
  requirement: !ruby/object:Gem::Requirement
@@ -361,6 +375,7 @@ files:
361
375
  - lib/onebox/layout.rb
362
376
  - lib/onebox/layout_support.rb
363
377
  - lib/onebox/matcher.rb
378
+ - lib/onebox/onebox_sanitize_config.rb
364
379
  - lib/onebox/preview.rb
365
380
  - lib/onebox/status_check.rb
366
381
  - lib/onebox/template_support.rb