FIX: Update GitImporter to match main (#18974)

Fixes:

 * An issue where the git url gets updated when it shouldn't,
 * Fetching from gitlab,
This commit is contained in:
Daniel Waterworth 2022-11-14 12:28:00 -06:00 committed by GitHub
parent e67d5f222c
commit c0971b19cb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 51 additions and 83 deletions

View File

@ -8,8 +8,7 @@ class ThemeStore::GitImporter
attr_reader :url
def initialize(url, private_key: nil, branch: nil)
@url = url
@clone_url = GitUrl.normalize(url)
@url = GitUrl.normalize(url)
@temp_folder = "#{Pathname.new(Dir.tmpdir).realpath}/discourse_theme_#{SecureRandom.hex}"
@private_key = private_key
@branch = branch
@ -84,12 +83,12 @@ class ThemeStore::GitImporter
def clone!
begin
@clone_uri = URI.parse(@clone_url)
@uri = URI.parse(@url)
rescue URI::Error
raise_import_error!
end
case @clone_uri&.scheme
case @uri&.scheme
when "http", "https"
clone_http!
when "ssh"
@ -109,49 +108,53 @@ class ThemeStore::GitImporter
args << "clone"
if @branch.present?
args.concat(["--single-branch", "-b", @branch])
args.concat(["-b", @branch])
end
args.concat([@clone_url, @temp_folder])
args.concat([@url, @temp_folder])
args
end
def clone_http!
uris = [@uri]
begin
@clone_uri = FinalDestination.resolve(@clone_uri.to_s)
resolved_uri = FinalDestination.resolve(@uri.to_s)
if resolved_uri && resolved_uri != @uri
uris.unshift(resolved_uri)
end
rescue
raise_import_error!
# If this fails, we can stil attempt to clone using the original URI
end
@clone_url = @clone_uri.to_s
uris.each do |uri|
@uri = uri
@url = @uri.to_s
unless ["http", "https"].include?(@clone_uri.scheme)
raise_import_error!
unless ["http", "https"].include?(@uri.scheme)
raise_import_error!
end
addresses = FinalDestination::SSRFDetector.lookup_and_filter_ips(@uri.host)
unless addresses.empty?
env = { "GIT_TERMINAL_PROMPT" => "0" }
args = clone_args(
"http.followRedirects" => "false",
"http.curloptResolve" => "#{@uri.host}:#{@uri.port}:#{addresses.join(',')}",
)
begin
Discourse::Utils.execute_command(env, *args, timeout: COMMAND_TIMEOUT_SECONDS)
return
rescue RuntimeError
end
end
end
begin
addresses = FinalDestination::SSRFDetector.lookup_and_filter_ips(@clone_uri.host)
rescue FinalDestination::SSRFDetector::DisallowedIpError
raise_import_error!
end
if addresses.empty?
raise_import_error!
end
env = { "GIT_TERMINAL_PROMPT" => "0" }
args = clone_args(
"http.followRedirects" => "false",
"http.curloptResolve" => "#{@clone_uri.host}:#{@clone_uri.port}:#{addresses.join(',')}",
)
begin
Discourse::Utils.execute_command(env, *args, timeout: COMMAND_TIMEOUT_SECONDS)
rescue RuntimeError
raise_import_error!
end
raise_import_error!
end
def clone_ssh!
@ -162,31 +165,13 @@ class ThemeStore::GitImporter
with_ssh_private_key do |ssh_folder|
# Use only the specified SSH key
env = { 'GIT_SSH_COMMAND' => "ssh -i #{ssh_folder}/id_rsa -o IdentitiesOnly=yes -o IdentityFile=#{ssh_folder}/id_rsa -o StrictHostKeyChecking=no" }
args = clone_args
begin
addresses = FinalDestination::SSRFDetector.lookup_and_filter_ips(@clone_uri.host)
rescue FinalDestination::SSRFDetector::DisallowedIpError
Discourse::Utils.execute_command(env, *args, timeout: COMMAND_TIMEOUT_SECONDS)
rescue RuntimeError
raise_import_error!
end
timeout_at = Time.zone.now + COMMAND_TIMEOUT_SECONDS
addresses.each do |address|
remaining_timeout = timeout_at - Time.zone.now
raise_import_error! if remaining_timeout < 0
@clone_uri.host = address
@clone_url = @clone_uri.to_s
args = clone_args
begin
return Discourse::Utils.execute_command(env, *args, timeout: remaining_timeout)
rescue RuntimeError
end
end
raise_import_error!
end
end

View File

@ -5,22 +5,17 @@
require 'rails_helper'
require 'theme_store/git_importer'
describe ThemeStore::GitImporter do
context "#import" do
let(:http_url) { "http://github.com/example/example.git" }
let(:https_url) { "https://github.com/example/example.git" }
RSpec.describe ThemeStore::GitImporter do
describe "#import" do
let(:url) { "https://github.com/example/example.git" }
let(:trailing_slash_url) { "https://github.com/example/example/" }
let(:ssh_url) { "git@github.com:example/example.git" }
let(:branch) { "dev" }
before do
freeze_time
hex = "xxx"
SecureRandom.stubs(:hex).returns(hex)
FinalDestination.stubs(:resolve).with(http_url).returns(URI.parse(http_url))
FinalDestination.stubs(:resolve).with(https_url).returns(URI.parse(https_url))
FinalDestination.stubs(:resolve).with(url).returns(URI.parse(url))
FinalDestination::SSRFDetector.stubs(:lookup_and_filter_ips).with("github.com").returns(["192.0.2.100"])
@temp_folder = "#{Pathname.new(Dir.tmpdir).realpath}/discourse_theme_#{hex}"
@ssh_folder = "#{Pathname.new(Dir.tmpdir).realpath}/discourse_theme_ssh_#{hex}"
@ -31,22 +26,10 @@ describe ThemeStore::GitImporter do
.expects(:execute_command)
.with(
{ "GIT_TERMINAL_PROMPT" => "0" },
"git", "-c", "http.followRedirects=false", "-c", "http.curloptResolve=github.com:80:192.0.2.100", "clone", "http://github.com/example/example.git", @temp_folder, { timeout: 20 }
"git", "-c", "http.followRedirects=false", "-c", "http.curloptResolve=github.com:443:192.0.2.100", "clone", "https://github.com/example/example.git", @temp_folder, timeout: 20
)
importer = ThemeStore::GitImporter.new(http_url)
importer.import!
end
it "imports https urls" do
Discourse::Utils
.expects(:execute_command)
.with(
{ "GIT_TERMINAL_PROMPT" => "0" },
"git", "-c", "http.followRedirects=false", "-c", "http.curloptResolve=github.com:443:192.0.2.100", "clone", "https://github.com/example/example.git", @temp_folder, { timeout: 20 }
)
importer = ThemeStore::GitImporter.new(https_url)
importer = ThemeStore::GitImporter.new(url)
importer.import!
end
@ -55,7 +38,7 @@ describe ThemeStore::GitImporter do
.expects(:execute_command)
.with(
{ "GIT_TERMINAL_PROMPT" => "0" },
"git", "-c", "http.followRedirects=false", "-c", "http.curloptResolve=github.com:443:192.0.2.100", "clone", "https://github.com/example/example.git", @temp_folder, { timeout: 20 }
"git", "-c", "http.followRedirects=false", "-c", "http.curloptResolve=github.com:443:192.0.2.100", "clone", "https://github.com/example/example.git", @temp_folder, timeout: 20
)
importer = ThemeStore::GitImporter.new(trailing_slash_url)
@ -67,22 +50,22 @@ describe ThemeStore::GitImporter do
.expects(:execute_command)
.with(
{ "GIT_SSH_COMMAND" => "ssh -i #{@ssh_folder}/id_rsa -o IdentitiesOnly=yes -o IdentityFile=#{@ssh_folder}/id_rsa -o StrictHostKeyChecking=no" },
"git", "clone", "ssh://git@192.0.2.100/example/example.git", @temp_folder, { timeout: 20 }
"git", "clone", "ssh://git@github.com/example/example.git", @temp_folder, timeout: 20
)
importer = ThemeStore::GitImporter.new(ssh_url, private_key: "private_key")
importer.import!
end
it "imports https urls with a particular branch" do
it "imports http urls with a particular branch" do
Discourse::Utils
.expects(:execute_command)
.with(
{ "GIT_TERMINAL_PROMPT" => "0" },
"git", "-c", "http.followRedirects=false", "-c", "http.curloptResolve=github.com:443:192.0.2.100", "clone", "--single-branch", "-b", branch, "https://github.com/example/example.git", @temp_folder, { timeout: 20 }
"git", "-c", "http.followRedirects=false", "-c", "http.curloptResolve=github.com:443:192.0.2.100", "clone", "-b", branch, "https://github.com/example/example.git", @temp_folder, timeout: 20
)
importer = ThemeStore::GitImporter.new(https_url, branch: branch)
importer = ThemeStore::GitImporter.new(url, branch: branch)
importer.import!
end
@ -91,7 +74,7 @@ describe ThemeStore::GitImporter do
.expects(:execute_command)
.with(
{ "GIT_SSH_COMMAND" => "ssh -i #{@ssh_folder}/id_rsa -o IdentitiesOnly=yes -o IdentityFile=#{@ssh_folder}/id_rsa -o StrictHostKeyChecking=no" },
"git", "clone", "--single-branch", "-b", branch, "ssh://git@192.0.2.100/example/example.git", @temp_folder, { timeout: 20 }
"git", "clone", "-b", branch, "ssh://git@github.com/example/example.git", @temp_folder, timeout: 20
)
importer = ThemeStore::GitImporter.new(ssh_url, private_key: "private_key", branch: branch)