SECURITY: Limit URL length for theme remote (#20787)
This commit is contained in:
parent
165a3217c8
commit
b81767c1b1
|
@ -3,6 +3,8 @@
|
||||||
require "base64"
|
require "base64"
|
||||||
|
|
||||||
class Admin::ThemesController < Admin::AdminController
|
class Admin::ThemesController < Admin::AdminController
|
||||||
|
MAX_REMOTE_LENGTH = 10_000
|
||||||
|
|
||||||
skip_before_action :check_xhr, only: %i[show preview export]
|
skip_before_action :check_xhr, only: %i[show preview export]
|
||||||
before_action :ensure_admin
|
before_action :ensure_admin
|
||||||
|
|
||||||
|
@ -86,6 +88,12 @@ class Admin::ThemesController < Admin::AdminController
|
||||||
render json: @theme.errors, status: :unprocessable_entity
|
render json: @theme.errors, status: :unprocessable_entity
|
||||||
end
|
end
|
||||||
elsif remote = params[:remote]
|
elsif remote = params[:remote]
|
||||||
|
if remote.length > MAX_REMOTE_LENGTH
|
||||||
|
error =
|
||||||
|
I18n.t("themes.import_error.not_allowed_theme", { repo: remote[0..MAX_REMOTE_LENGTH] })
|
||||||
|
return render_json_error(error, status: 422)
|
||||||
|
end
|
||||||
|
|
||||||
begin
|
begin
|
||||||
guardian.ensure_allowed_theme_repo_import!(remote.strip)
|
guardian.ensure_allowed_theme_repo_import!(remote.strip)
|
||||||
rescue Discourse::InvalidAccess
|
rescue Discourse::InvalidAccess
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class UrlHelper
|
class UrlHelper
|
||||||
|
MAX_URL_LENGTH = 100_000
|
||||||
|
|
||||||
# At the moment this handles invalid URLs that browser address bar accepts
|
# At the moment this handles invalid URLs that browser address bar accepts
|
||||||
# where second # is not encoded
|
# where second # is not encoded
|
||||||
#
|
#
|
||||||
|
@ -74,9 +76,10 @@ class UrlHelper
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.normalized_encode(uri)
|
def self.normalized_encode(uri)
|
||||||
validated = nil
|
|
||||||
url = uri.to_s
|
url = uri.to_s
|
||||||
|
|
||||||
|
raise ArgumentError.new(:uri, "URL is too long") if url.length > MAX_URL_LENGTH
|
||||||
|
|
||||||
# Ideally we will jump straight to `Addressable::URI.normalized_encode`. However,
|
# Ideally we will jump straight to `Addressable::URI.normalized_encode`. However,
|
||||||
# that implementation has some edge-case issues like https://github.com/sporkmonger/addressable/issues/472.
|
# that implementation has some edge-case issues like https://github.com/sporkmonger/addressable/issues/472.
|
||||||
# To temporaily work around those issues for the majority of cases, we try parsing with `::URI`.
|
# To temporaily work around those issues for the majority of cases, we try parsing with `::URI`.
|
||||||
|
|
|
@ -164,6 +164,12 @@ RSpec.describe UrlHelper do
|
||||||
"https://test.com/original/3X/b/5/575bcc2886bf7a39684b57ca90be85f7d399bbc7.png?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AK8888999977/20200130/us-west-1/s3/aws4_request&X-Amz-Date=20200130T064355Z&X-Amz-Expires=15&X-Amz-SignedHeaders=host&X-Amz-Security-Token=blahblah+blahblah/blah//////////wEQA==&X-Amz-Signature=test"
|
"https://test.com/original/3X/b/5/575bcc2886bf7a39684b57ca90be85f7d399bbc7.png?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AK8888999977/20200130/us-west-1/s3/aws4_request&X-Amz-Date=20200130T064355Z&X-Amz-Expires=15&X-Amz-SignedHeaders=host&X-Amz-Security-Token=blahblah+blahblah/blah//////////wEQA==&X-Amz-Signature=test"
|
||||||
expect(UrlHelper.normalized_encode(presigned_url)).not_to eq(encoded_presigned_url)
|
expect(UrlHelper.normalized_encode(presigned_url)).not_to eq(encoded_presigned_url)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it "raises error if too long" do
|
||||||
|
expect do UrlHelper.normalized_encode("https://#{"a" * 100_000}.com") end.to raise_error(
|
||||||
|
ArgumentError,
|
||||||
|
)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "#local_cdn_url" do
|
describe "#local_cdn_url" do
|
||||||
|
|
|
@ -241,6 +241,12 @@ RSpec.describe Admin::ThemesController do
|
||||||
expect(response.status).to eq(422)
|
expect(response.status).to eq(422)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it "fails to import with a failing status" do
|
||||||
|
post "/admin/themes/import.json", params: { remote: "https://#{"a" * 10_000}.com" }
|
||||||
|
|
||||||
|
expect(response.status).to eq(422)
|
||||||
|
end
|
||||||
|
|
||||||
it "can lookup a private key by public key" do
|
it "can lookup a private key by public key" do
|
||||||
Discourse.redis.setex("ssh_key_abcdef", 1.hour, "rsa private key")
|
Discourse.redis.setex("ssh_key_abcdef", 1.hour, "rsa private key")
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue