mirror of
https://github.com/discourse/discourse.git
synced 2024-11-26 04:56:18 +08:00
FIX: When following redirects before cloning, use the first git request (#19269)
This is closer to git's redirect following behaviour. We prevented git
following redirects when we clone in order to prevent SSRF attacks.
Follow-up-to: 291bbc4fb9
This commit is contained in:
parent
aea492df5e
commit
d9364a272e
|
@ -67,7 +67,7 @@ class FinalDestination
|
||||||
|
|
||||||
@status = :ready
|
@status = :ready
|
||||||
@follow_canonical = @opts[:follow_canonical]
|
@follow_canonical = @opts[:follow_canonical]
|
||||||
@http_verb = http_verb(@force_get_hosts, @follow_canonical)
|
@http_verb = @opts[:http_verb] || http_verb(@force_get_hosts, @follow_canonical)
|
||||||
@cookie = nil
|
@cookie = nil
|
||||||
@limited_ips = []
|
@limited_ips = []
|
||||||
@verbose = @opts[:verbose] || false
|
@verbose = @opts[:verbose] || false
|
||||||
|
@ -82,8 +82,8 @@ class FinalDestination
|
||||||
20
|
20
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.resolve(url)
|
def self.resolve(url, opts = nil)
|
||||||
new(url).resolve
|
new(url, opts).resolve
|
||||||
end
|
end
|
||||||
|
|
||||||
def http_verb(force_get_hosts, follow_canonical)
|
def http_verb(force_get_hosts, follow_canonical)
|
||||||
|
|
|
@ -77,6 +77,25 @@ class ThemeStore::GitImporter
|
||||||
|
|
||||||
protected
|
protected
|
||||||
|
|
||||||
|
def redirected_uri
|
||||||
|
first_clone_uri = @uri.dup
|
||||||
|
first_clone_uri.path.gsub!(/\/\z/, "")
|
||||||
|
first_clone_uri.path += "/info/refs"
|
||||||
|
first_clone_uri.query = "service=git-upload-pack"
|
||||||
|
|
||||||
|
redirected_uri = FinalDestination.resolve(first_clone_uri.to_s, http_verb: :get)
|
||||||
|
|
||||||
|
if redirected_uri&.path.ends_with?("/info/refs")
|
||||||
|
redirected_uri.path.gsub!(/\/info\/refs\z/, "")
|
||||||
|
redirected_uri.query = nil
|
||||||
|
redirected_uri
|
||||||
|
else
|
||||||
|
@uri
|
||||||
|
end
|
||||||
|
rescue
|
||||||
|
@uri
|
||||||
|
end
|
||||||
|
|
||||||
def raise_import_error!
|
def raise_import_error!
|
||||||
raise RemoteTheme::ImportError.new(I18n.t("themes.import_error.git"))
|
raise RemoteTheme::ImportError.new(I18n.t("themes.import_error.git"))
|
||||||
end
|
end
|
||||||
|
@ -117,19 +136,7 @@ class ThemeStore::GitImporter
|
||||||
end
|
end
|
||||||
|
|
||||||
def clone_http!
|
def clone_http!
|
||||||
uris = [@uri]
|
@uri = redirected_uri
|
||||||
|
|
||||||
begin
|
|
||||||
resolved_uri = FinalDestination.resolve(@uri.to_s)
|
|
||||||
if resolved_uri && resolved_uri != @uri
|
|
||||||
uris.unshift(resolved_uri)
|
|
||||||
end
|
|
||||||
rescue
|
|
||||||
# If this fails, we can stil attempt to clone using the original URI
|
|
||||||
end
|
|
||||||
|
|
||||||
uris.each do |uri|
|
|
||||||
@uri = uri
|
|
||||||
@url = @uri.to_s
|
@url = @uri.to_s
|
||||||
|
|
||||||
unless ["http", "https"].include?(@uri.scheme)
|
unless ["http", "https"].include?(@uri.scheme)
|
||||||
|
@ -148,15 +155,11 @@ class ThemeStore::GitImporter
|
||||||
|
|
||||||
begin
|
begin
|
||||||
Discourse::Utils.execute_command(env, *args, timeout: COMMAND_TIMEOUT_SECONDS)
|
Discourse::Utils.execute_command(env, *args, timeout: COMMAND_TIMEOUT_SECONDS)
|
||||||
return
|
|
||||||
rescue RuntimeError
|
rescue RuntimeError
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
raise_import_error!
|
|
||||||
end
|
|
||||||
|
|
||||||
def clone_ssh!
|
def clone_ssh!
|
||||||
unless @private_key.present?
|
unless @private_key.present?
|
||||||
raise_import_error!
|
raise_import_error!
|
||||||
|
|
|
@ -6,6 +6,7 @@ require 'theme_store/git_importer'
|
||||||
RSpec.describe ThemeStore::GitImporter do
|
RSpec.describe ThemeStore::GitImporter do
|
||||||
describe "#import" do
|
describe "#import" do
|
||||||
let(:url) { "https://github.com/example/example.git" }
|
let(:url) { "https://github.com/example/example.git" }
|
||||||
|
let(:first_fetch_url) { "https://github.com/example/example.git/info/refs?service=git-upload-pack" }
|
||||||
let(:trailing_slash_url) { "https://github.com/example/example/" }
|
let(:trailing_slash_url) { "https://github.com/example/example/" }
|
||||||
let(:ssh_url) { "git@github.com:example/example.git" }
|
let(:ssh_url) { "git@github.com:example/example.git" }
|
||||||
let(:branch) { "dev" }
|
let(:branch) { "dev" }
|
||||||
|
@ -13,8 +14,17 @@ RSpec.describe ThemeStore::GitImporter do
|
||||||
before do
|
before do
|
||||||
hex = "xxx"
|
hex = "xxx"
|
||||||
SecureRandom.stubs(:hex).returns(hex)
|
SecureRandom.stubs(:hex).returns(hex)
|
||||||
FinalDestination.stubs(:resolve).with(url).returns(URI.parse(url))
|
|
||||||
FinalDestination::SSRFDetector.stubs(:lookup_and_filter_ips).with("github.com").returns(["192.0.2.100"])
|
FinalDestination::SSRFDetector
|
||||||
|
.stubs(:lookup_and_filter_ips)
|
||||||
|
.with("github.com")
|
||||||
|
.returns(["192.0.2.100"])
|
||||||
|
|
||||||
|
FinalDestination
|
||||||
|
.stubs(:resolve)
|
||||||
|
.with(first_fetch_url, http_verb: :get)
|
||||||
|
.returns(URI.parse(first_fetch_url))
|
||||||
|
|
||||||
@temp_folder = "#{Pathname.new(Dir.tmpdir).realpath}/discourse_theme_#{hex}"
|
@temp_folder = "#{Pathname.new(Dir.tmpdir).realpath}/discourse_theme_#{hex}"
|
||||||
@ssh_folder = "#{Pathname.new(Dir.tmpdir).realpath}/discourse_theme_ssh_#{hex}"
|
@ssh_folder = "#{Pathname.new(Dir.tmpdir).realpath}/discourse_theme_ssh_#{hex}"
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in New Issue
Block a user