Skip to content

Commit 478df76

Browse files
authored
Merge pull request #549 from puppetlabs/GH-535-safe_directories
(GH-535) Fix for safe directories
2 parents 9965b7e + af77ebd commit 478df76

File tree

5 files changed

+88
-6
lines changed

5 files changed

+88
-6
lines changed

README.md

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,8 @@ The vcsrepo module provides a single type with providers to support the followin
3939
* [Subversion](#subversion)
4040

4141
**Note:** `git` is the only vcs provider officially [supported by Puppet Inc.](https://forge.puppet.com/supported)
42-
**Note:** Release v4.0.1 has been removed from the Puppet Forge and was officially re-released as version v5.0.0 as it contained a breaking change. Details available [here](https://puppetlabs.github.io/iac/team/status/developer/2021/06/04/status-update.html)
42+
**Note:** Release v4.0.1 has been removed from the Puppet Forge and was officially re-released as version v5.0.0 as it contained a breaking change.
43+
Details available [here](https://puppetlabs.github.io/iac/team/status/developer/2021/06/04/status-update.html)
4344

4445
<a id="setup"></a>
4546
## Setup
@@ -813,6 +814,22 @@ The includes parameter is only supported when SVN client version is >= 1.6.
813814

814815
For an extensive list of supported operating systems, see [metadata.json](https://github.com/puppetlabs/puppetlabs-vcsrepo/blob/main/metadata.json)
815816

817+
### Response to CVE-2022-24765
818+
819+
The vulnerability described in this CVE could impact users working on multi-user machines.
820+
A malicious actor could create a `.git` directory above the current working directory causing all git invocations to occur outside of a repository to read its configuration.
821+
822+
For a more in-depth description of this vulnerability, check out [this blog post](https://github.blog/2022-04-12-git-security-vulnerability-announced/).
823+
824+
Fixes were released in Git versions 2.35.2 and 1:2.25.1-1ubuntu3.4 respectively.
825+
826+
VCSRepo users were impacted when running newer versions of Git and managing repositories that were owned by a user or group that differed from the user executing Git.
827+
828+
For example, setting the `owner` parameter on a resource would cause Puppet runs to fail with a `Path /destination/path exists and is not the desired repository.` error.
829+
830+
Impacted users are now advised to use the new `safe_directory` parameter on Git resources.
831+
Explicitily setting the value to `true` will add the current path specified on the resource to the `safe.directory` git configuration for the current user (global scope) allowing the Puppet run to continue without error.
832+
816833
<a id="development"></a>
817834
## Development
818835

lib/puppet/provider/vcsrepo/git.rb

Lines changed: 55 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
desc 'Supports Git repositories'
77

88
has_features :bare_repositories, :reference_tracking, :ssh_identity, :multiple_remotes,
9-
:user, :depth, :branch, :submodules
9+
:user, :depth, :branch, :submodules, :safe_directory
1010

1111
def create
1212
check_force
@@ -36,6 +36,7 @@ def create
3636
end
3737

3838
def destroy
39+
remove_safe_directory if safe_directories.include?(@resource.value(:path))
3940
FileUtils.rm_rf(@resource.value(:path))
4041
end
4142

@@ -140,6 +141,7 @@ def working_copy_exists?
140141
end
141142

142143
def exists?
144+
update_safe_directory
143145
working_copy_exists? || bare_exists?
144146
end
145147

@@ -564,6 +566,52 @@ def git_version
564566
exec_git('--version').match(%r{[0-9]+\.[0-9]+\.[0-9]+(\.[0-9]+)?})[0]
565567
end
566568

569+
# @!visibility private
570+
def safe_directories
571+
args = ['config', '--global', '--get-all', 'safe.directory']
572+
begin
573+
d = git_with_identity(*args) || ''
574+
d.split('\n')
575+
.reject { |v| v.empty? }
576+
.map { |v| v.chomp }
577+
rescue Puppet::ExecutionFailure
578+
[]
579+
end
580+
end
581+
582+
# @!visibility private
583+
def update_safe_directory
584+
# If the owner parameter is not set, then we don't need to do anything.
585+
return unless @resource.value(:owner)
586+
587+
if should_add_safe_directory?
588+
add_safe_directory
589+
else
590+
remove_safe_directory
591+
end
592+
end
593+
594+
# @!visibility private
595+
def add_safe_directory
596+
notice("Adding '#{@resource.value(:path)}' to safe directory list")
597+
args = ['config', '--global', '--add', 'safe.directory', @resource.value(:path)]
598+
git_with_identity(*args)
599+
end
600+
601+
# @!visibility private
602+
def remove_safe_directory
603+
notice("Removing '#{@resource.value(:path)}' from safe directory list")
604+
args = ['config', '--global', '--unset', 'safe.directory', @resource.value(:path)]
605+
git_with_identity(*args)
606+
end
607+
608+
# @!visibility private
609+
def should_add_safe_directory?
610+
(@resource.value(:owner) != @resource.value(:user)) && # user and owner should be different
611+
@resource.value(:safe_directory) && # safe_directory should be true
612+
!safe_directories.include?(@resource.value(:path)) # directory should not already be in the list
613+
end
614+
567615
# @!visibility private
568616
def git_with_identity(*args)
569617
if @resource.value(:trust_server_cert) == :true
@@ -599,10 +647,13 @@ def git_with_identity(*args)
599647

600648
# Execute git with the given args, running it as the user specified.
601649
def exec_git(*args)
602-
exec_args = { failonfail: true, combine: true }
650+
exec_args = {
651+
failonfail: true,
652+
combine: true,
653+
custom_environment: { 'HOME' => Dir.home },
654+
}
603655
if @resource.value(:user) && @resource.value(:user) != Facter['id'].value
604-
env = Etc.getpwnam(@resource.value(:user))
605-
exec_args[:custom_environment] = { 'HOME' => env['dir'] }
656+
exec_args[:custom_environment] = { 'HOME' => Dir.home(@resource.value(:user)) }
606657
exec_args[:uid] = @resource.value(:user)
607658
end
608659
Puppet::Util::Execution.execute([:git, args], **exec_args)

lib/puppet/type/vcsrepo.rb

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,9 @@
6161
feature :keep_local_changes,
6262
'The provider supports keeping local changes on files tracked by the repository when changing revision'
6363

64+
feature :safe_directory,
65+
'The provider supports setting a safe directory. This will only be used for newer versions of git.'
66+
6467
ensurable do
6568
desc 'Ensure the version control repository.'
6669
attr_accessor :latest
@@ -112,7 +115,9 @@ def insync?(is)
112115
end
113116

114117
newvalue :absent do
115-
provider.destroy
118+
if provider.exists?
119+
provider.destroy
120+
end
116121
end
117122

118123
newvalue :latest, required_features: [:reference_tracking] do
@@ -301,6 +306,12 @@ def insync?(is)
301306
defaultto :false
302307
end
303308

309+
newparam :safe_directory, required_features: [:safe_directory] do
310+
desc 'Marks the current directory specified by the path parameter as a safe directory.'
311+
newvalues(true, false)
312+
defaultto :false
313+
end
314+
304315
autorequire(:package) do
305316
['git', 'git-core', 'mercurial', 'subversion']
306317
end

spec/acceptance/clone_repo_spec.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
after(:all) do
1111
run_shell("rm -rf #{tmpdir}/testrepo")
12+
run_shell("rm -rf #{tmpdir}/testrepo_owner")
1213
run_shell("rm -rf #{tmpdir}/testrepo_mirror_repo")
1314
end
1415

@@ -225,6 +226,7 @@
225226
provider => git,
226227
source => "file://#{tmpdir}/testrepo.git",
227228
owner => 'vagrant',
229+
safe_directory => true,
228230
}
229231
MANIFEST
230232
it 'clones a repo' do

spec/unit/puppet/provider/vcsrepo/git_spec.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,7 @@ def branch_a_list(include_branch = nil?)
193193
expect(provider).to receive(:path_exists?).and_return(true)
194194
expect(provider).to receive(:path_empty?).and_return(false)
195195
provider.destroy
196+
expect(provider).to receive(:exec_git).with('config', '--global', '--get-all', 'safe.directory')
196197
expect(provider).to receive(:exec_git).with('clone', resource.value(:source), resource.value(:path))
197198
expect(provider).to receive(:update_submodules)
198199
expect(provider).to receive(:update_remote_url).with('origin', resource.value(:source)).and_return false

0 commit comments

Comments
 (0)