From 1cebe7670a3c02087c333d3985708726d1d7433f Mon Sep 17 00:00:00 2001 From: Robin Ward Date: Tue, 10 Sep 2019 12:27:07 -0400 Subject: [PATCH] FEATURE: Allow embedding to ignore HTTP REFERER New site setting: `embed_any_origin` that will send postMessages to wildcard origins `*` instead of the referer. Most of the time you won't want to do this, so the setting is default to `false`. However, there are certain situations where you want to allow embedding to send post messages when there is no HTTP REFERER. For example, if you created a native mobile app and you wanted to embed a list of Discourse topics as HTML. In the code your HTML would be a static file/string, which would not be able to send a referer. In this case, the site setting will allow the embed to work. From a security standpoint we currently only use `postMessage` to send data about the size of the HTML document and scroll position, so it should be enable if required with minimal security ramifications. --- app/controllers/embed_controller.rb | 7 +++++-- app/views/layouts/embed.html.erb | 2 +- config/locales/server.en.yml | 3 ++- config/site_settings.yml | 1 + spec/requests/embed_controller_spec.rb | 19 ++++++++++++++++++- 5 files changed, 27 insertions(+), 5 deletions(-) diff --git a/app/controllers/embed_controller.rb b/app/controllers/embed_controller.rb index c4bde730a55..5490fa59fab 100644 --- a/app/controllers/embed_controller.rb +++ b/app/controllers/embed_controller.rb @@ -8,7 +8,7 @@ class EmbedController < ApplicationController skip_before_action :check_xhr, :preload_json, :verify_authenticity_token before_action :ensure_embeddable, except: [ :info, :topics ] - before_action :get_embeddable_css_class, except: [ :info, :topics ] + before_action :prepare_embeddable, except: [ :info ] before_action :ensure_api_request, only: [ :info ] layout 'embed' @@ -123,10 +123,13 @@ class EmbedController < ApplicationController private - def get_embeddable_css_class + def prepare_embeddable @embeddable_css_class = "" embeddable_host = EmbeddableHost.record_for_url(request.referer) @embeddable_css_class = " class=\"#{embeddable_host.class_name}\"" if embeddable_host.present? && embeddable_host.class_name.present? + + @data_referer = request.referer + @data_referer = '*' if SiteSetting.embed_any_origin? && @data_referer.blank? end def ensure_api_request diff --git a/app/views/layouts/embed.html.erb b/app/views/layouts/embed.html.erb index d1cf2be33eb..b7b714fbc5e 100644 --- a/app/views/layouts/embed.html.erb +++ b/app/views/layouts/embed.html.erb @@ -13,7 +13,7 @@ <%= @topic_view.page_title %> - <%= SiteSetting.title %> <%- end %> - + <%= preload_script 'embed-application' %> <%= yield :head %> diff --git a/config/locales/server.en.yml b/config/locales/server.en.yml index ff522b004ec..9f3b705a251 100644 --- a/config/locales/server.en.yml +++ b/config/locales/server.en.yml @@ -1959,6 +1959,7 @@ en: autohighlight_all_code: "Force apply code highlighting to all preformatted code blocks even when they didn't explicitly specify the language." highlighted_languages: "Included syntax highlighting rules. (Warning: including too many languages may impact performance) see: https://highlightjs.org/static/demo for a demo" + embed_any_origin: "Allow embeddable content regardless of origin. This is required for mobile apps with static HTML." embed_topics_list: "Support HTML embedding of topics lists" embed_truncate: "Truncate the embedded posts." embed_support_markdown: "Support Markdown formatting for embedded posts." @@ -4598,4 +4599,4 @@ en: html_missing_placeholder: "The html template must include %{placeholder}" discord: - not_in_allowed_guild: 'Authentication failed. You are not a member of a permitted Discord guild.' \ No newline at end of file + not_in_allowed_guild: 'Authentication failed. You are not a member of a permitted Discord guild.' diff --git a/config/site_settings.yml b/config/site_settings.yml index c1a6cbfd74f..9a5327f980e 100644 --- a/config/site_settings.yml +++ b/config/site_settings.yml @@ -859,6 +859,7 @@ posting: choices: - 4-spaces-indent - code-fences + embed_any_origin: false embed_topics_list: false embed_truncate: default: true diff --git a/spec/requests/embed_controller_spec.rb b/spec/requests/embed_controller_spec.rb index 8d945dcb448..5f6abb61f4e 100644 --- a/spec/requests/embed_controller_spec.rb +++ b/spec/requests/embed_controller_spec.rb @@ -88,12 +88,29 @@ describe EmbedController do it "returns a list of topics" do topic = Fabricate(:topic) - get '/embed/topics?discourse_embed_id=de-1234', headers: headers + get '/embed/topics?discourse_embed_id=de-1234', headers: { + 'REFERER' => 'https://example.com/evil-trout' + } expect(response.status).to eq(200) expect(response.headers['X-Frame-Options']).to eq("ALLOWALL") expect(response.body).to match("data-embed-id=\"de-1234\"") expect(response.body).to match("data-topic-id=\"#{topic.id}\"") + expect(response.body).to match("data-referer=\"https://example.com/evil-trout\"") end + + it "returns no referer if not supplied" do + get '/embed/topics?discourse_embed_id=de-1234' + expect(response.status).to eq(200) + expect(response.body).to match("data-referer=\"\"") + end + + it "returns * for the referer if `embed_any_origin` is set" do + SiteSetting.embed_any_origin = true + get '/embed/topics?discourse_embed_id=de-1234' + expect(response.status).to eq(200) + expect(response.body).to match("data-referer=\"\\*\"") + end + end end