From: Toshi MARUYAMA Date: Thu, 7 Dec 2017 12:15:45 +0000 (+0000) Subject: Merged r17060 from trunk to 3.4-stable (#27516) X-Git-Tag: 3.4.4~29 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=76dd10bd78bd43a458a9e72f6cc492e7e109f7c1;p=redmine.git Merged r17060 from trunk to 3.4-stable (#27516) mercurial: reject malicious command argument We've got a security report from the Phabricator team, which basically says --config and --debugger arguments can be injected anywhere to lead to an arbitrary command execution. https://secure.phabricator.com/rPa7921a4448093d00defa8bd18f35b8c8f8bf3314 This is a fundamental issue of the argument parsing rules in Mercurial, which allows extensions to populate their parsing rules and such extensions can be loaded by "--config extensions.=". There's a chicken and egg problem. We're working on hardening the parsing rules, but which won't come in by default as it would be a behavior change. This patch adds a verification to reject malicious command arguments as a last ditch. The subsequent patches will fix the problem in more appropriate way. Contributed by Yuya Nishihara. git-svn-id: http://svn.redmine.org/redmine/branches/3.4-stable@17066 e93f8b46-1217-0410-a6f0-8f06a7374b81 --- diff --git a/lib/redmine/scm/adapters/mercurial_adapter.rb b/lib/redmine/scm/adapters/mercurial_adapter.rb index 064849f94..2c7121356 100644 --- a/lib/redmine/scm/adapters/mercurial_adapter.rb +++ b/lib/redmine/scm/adapters/mercurial_adapter.rb @@ -32,6 +32,8 @@ module Redmine # raised if hg command exited with error, e.g. unknown revision. class HgCommandAborted < CommandFailed; end + # raised if bad command argument detected before executing hg. + class HgCommandArgumentError < CommandFailed; end class << self def client_command @@ -286,8 +288,21 @@ module Redmine end end + # command options which may be processed earlier, by faulty parser in hg + HG_EARLY_BOOL_ARG = /^--(debugger|profile|traceback)$/ + HG_EARLY_LIST_ARG = /^(--(config|cwd|repo(sitory)?)\b|-R)/ + private_constant :HG_EARLY_BOOL_ARG, :HG_EARLY_LIST_ARG + # Runs 'hg' command with the given args def hg(*args, &block) + # as of hg 4.4.1, early parsing of bool options is not terminated at '--' + if args.any? { |s| s =~ HG_EARLY_BOOL_ARG } + raise HgCommandArgumentError, "malicious command argument detected" + end + if args.take_while { |s| s != '--' }.any? { |s| s =~ HG_EARLY_LIST_ARG } + raise HgCommandArgumentError, "malicious command argument detected" + end + repo_path = root_url || url full_args = ['-R', repo_path, '--encoding', 'utf-8'] full_args << '--config' << "extensions.redminehelper=#{HG_HELPER_EXT}" diff --git a/test/unit/lib/redmine/scm/adapters/mercurial_adapter_test.rb b/test/unit/lib/redmine/scm/adapters/mercurial_adapter_test.rb index 0512cc74c..e0458ce55 100644 --- a/test/unit/lib/redmine/scm/adapters/mercurial_adapter_test.rb +++ b/test/unit/lib/redmine/scm/adapters/mercurial_adapter_test.rb @@ -21,6 +21,7 @@ class MercurialAdapterTest < ActiveSupport::TestCase HELPERS_DIR = Redmine::Scm::Adapters::MercurialAdapter::HELPERS_DIR TEMPLATE_NAME = Redmine::Scm::Adapters::MercurialAdapter::TEMPLATE_NAME TEMPLATE_EXTENSION = Redmine::Scm::Adapters::MercurialAdapter::TEMPLATE_EXTENSION + HgCommandArgumentError = Redmine::Scm::Adapters::MercurialAdapter::HgCommandArgumentError REPOSITORY_PATH = repository_path('mercurial') CHAR_1_HEX = "\xc3\x9c" @@ -443,6 +444,24 @@ class MercurialAdapterTest < ActiveSupport::TestCase assert_equal "UTF-8", adpt2.path_encoding end + def test_bad_early_options + assert_raise HgCommandArgumentError do + @adapter.diff('sources/welcome_controller.rb', '--config=alias.rhdiff=!xterm') + end + assert_raise HgCommandArgumentError do + @adapter.entries('--debugger') + end + assert_raise HgCommandArgumentError do + @adapter.revisions(nil, nil, nil, limit: '--repo=otherrepo') + end + assert_raise HgCommandArgumentError do + @adapter.nodes_in_branch('default', limit: '--repository=otherrepo') + end + assert_raise HgCommandArgumentError do + @adapter.nodes_in_branch('-Rotherrepo') + end + end + private def test_hgversion_for(hgversion, version)