onebox 1.7.3 → 1.7.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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