From c186a46910431020e8efc425dec2133e7a99fa9a Mon Sep 17 00:00:00 2001 From: Bianca Nenciu Date: Wed, 25 Jan 2023 19:17:21 +0200 Subject: [PATCH] SECURITY: Prevent XSS in local oneboxes (#20008) Co-authored-by: OsamaSayegh --- ...20_trigger_post_rebake_local_onebox_xss.rb | 22 +++++++ lib/oneboxer.rb | 19 ++++-- spec/lib/oneboxer_spec.rb | 60 +++++++++++++++++++ 3 files changed, 97 insertions(+), 4 deletions(-) create mode 100644 db/post_migrate/20230105153520_trigger_post_rebake_local_onebox_xss.rb diff --git a/db/post_migrate/20230105153520_trigger_post_rebake_local_onebox_xss.rb b/db/post_migrate/20230105153520_trigger_post_rebake_local_onebox_xss.rb new file mode 100644 index 00000000000..e9981e4d2c0 --- /dev/null +++ b/db/post_migrate/20230105153520_trigger_post_rebake_local_onebox_xss.rb @@ -0,0 +1,22 @@ +# frozen_string_literal: true + +class TriggerPostRebakeLocalOneboxXss < ActiveRecord::Migration[7.0] + def up + val = + DB.query_single( + "SELECT value FROM site_settings WHERE name = 'content_security_policy'", + ).first + + return if val == nil || val == "t" + + DB.exec(<<~SQL) + UPDATE posts + SET baked_version = NULL + WHERE cooked LIKE '%#{URI(url).to_s}" + normalized_url = ::Onebox::Helpers.normalize_url_for_output(URI(url).to_s) + html = html.presence || "#{normalized_url}" { onebox: html, preview: html } end @@ -355,18 +356,28 @@ module Oneboxer "" end + normalized_url = ::Onebox::Helpers.normalize_url_for_output(url) case File.extname(URI(url).path || "") when VIDEO_REGEX <<~HTML HTML when AUDIO_REGEX - "" + <<~HTML + + HTML end end diff --git a/spec/lib/oneboxer_spec.rb b/spec/lib/oneboxer_spec.rb index ae969d015ed..7e23517425a 100644 --- a/spec/lib/oneboxer_spec.rb +++ b/spec/lib/oneboxer_spec.rb @@ -198,6 +198,66 @@ RSpec.describe Oneboxer do "

http://test.localhost/new?%27class=black

", ) end + + it "escapes URLs of local audio uploads" do + result = + described_class.onebox_raw( + "#{Discourse.base_url}/uploads/default/original/1X/a1c31803be81b85ecafc4f77b1008eee9b3b82f4.wav#'<>", + ) + expect(result[:onebox]).to eq(<<~HTML) + + HTML + expect(result[:preview]).to eq(<<~HTML) + + HTML + end + + it "escapes URLs of local video uploads" do + result = + described_class.onebox_raw( + "#{Discourse.base_url}/uploads/default/original/1X/a1c31803be81b85ecafc4f77b1008eee9b3b82f4.mp4#'<>", + ) + expect(result[:onebox]).to eq(<<~HTML) + + HTML + expect(result[:preview]).to eq(<<~HTML) + + HTML + end + + it "escapes URLs of generic local links" do + result = described_class.onebox_raw("#{Discourse.base_url}/g/somegroup#'onerror='") + expect(result[:onebox]).to eq( + "http://test.localhost/g/somegroup#'onerror='", + ) + expect(result[:preview]).to eq( + "http://test.localhost/g/somegroup#'onerror='", + ) + end end describe ".external_onebox" do