@@ -31,9 +31,33 @@ def exitstatus
3131 end
3232
3333 def git ( cmd , path = Dir . pwd , options = { } )
34+ reject_git_config_pollution! ( cmd , path )
3435 sh ( "git #{ cmd } " , options . merge ( dir : path ) )
3536 end
3637
38+ # A local `git config` write in a directory without a `.git` makes git
39+ # discover an enclosing repository, which can be the rubygems checkout
40+ # itself, polluting its (possibly worktree-shared) `.git/config` with
41+ # fixture identities. Only allow local config writes inside tmp/.
42+ def reject_git_config_pollution! ( cmd , path )
43+ require "shellwords"
44+ args = cmd . to_s . shellsplit
45+ return unless args . first == "config"
46+ return if args . any? { |a | [ "--global" , "--system" , "-f" , "--file" ] . include? ( a ) || a . start_with? ( "--file=" ) }
47+ return if args . any? { |a | [ "--get" , "--get-all" , "--get-regexp" , "--get-urlmatch" , "--list" , "-l" ] . include? ( a ) }
48+
49+ # Required lazily because this file is loaded in every spawned ruby
50+ # before RubygemsVersionManager switches RubyGems, where loading extra
51+ # default gems (pathname, through support/path) breaks the setup.
52+ require_relative "path"
53+ dir = File . expand_path ( path . to_s )
54+ tmp_root = Spec ::Path . tmp_root . to_s
55+ return if dir == tmp_root || dir . start_with? ( tmp_root + File ::SEPARATOR )
56+
57+ raise "Refusing to run `git #{ cmd } ` in #{ dir } : " \
58+ "a local git config write outside tmp/ could end up in the checkout's own .git/config"
59+ end
60+
3761 def sh ( cmd , options = { } )
3862 dir = options [ :dir ]
3963 env = options [ :env ] || { }
0 commit comments