Skip to content

Commit ee8cf4a

Browse files
committed
Preserve tmpdir world-writable guard
1 parent d2ec6e2 commit ee8cf4a

2 files changed

Lines changed: 30 additions & 5 deletions

File tree

lib/tmpdir.rb

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -36,10 +36,12 @@ def self.tmpdir
3636
# We call File.writable?, not stat.writable?, because you can't tell if a dir is actually
3737
# writable just from stat; OS mechanisms other than user/group/world bits can affect this.
3838
#
39-
# However, in some container environments (e.g. Kubernetes with read-only root filesystem
40-
# and emptyDir volumes), File.writable? may return false even though the directory is
41-
# actually writable via mounted volumes. Fall back to stat.writable? in that case.
42-
if stat.writable?
39+
# However, File.writable? can be a false negative, so preserve the
40+
# world-writable non-sticky guard below before falling back to stat.writable?.
41+
if stat.world_writable? && !stat.sticky?
42+
warn "#{name} is world-writable: #{dir}"
43+
next
44+
elsif stat.writable?
4345
warn "#{name}: File.writable? reports not writable but file mode bits suggest writable, using anyway: #{dir}"
4446
break dir
4547
end

test/test_tmpdir.rb

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,9 +90,32 @@ def test_writable_fallback_to_stat
9090
end
9191
end
9292

93+
def test_writable_fallback_rejects_world_writable_non_sticky
94+
omit "no meaning on this platform" if /mswin|mingw/ =~ RUBY_PLATFORM
95+
Dir.mktmpdir do |tmpdir|
96+
envs = %w[TMPDIR TMP TEMP]
97+
oldenv = envs.each_with_object({}) {|v, h| h[v] = ENV.delete(v)}
98+
begin
99+
ENV[envs[0]] = tmpdir
100+
File.chmod(0777, tmpdir)
101+
102+
original_writable = File.method(:writable?)
103+
File.define_singleton_method(:writable?) do |path|
104+
path == tmpdir ? false : original_writable.call(path)
105+
end
106+
107+
assert_not_equal(tmpdir, assert_warn(/is world-writable/) { Dir.tmpdir })
108+
ensure
109+
File.define_singleton_method(:writable?, original_writable) if original_writable
110+
File.chmod(0755, tmpdir)
111+
ENV.update(oldenv)
112+
end
113+
end
114+
end
115+
93116
def test_writable_fallback_both_fail
94117
omit "no meaning on this platform" if /mswin|mingw/ =~ RUBY_PLATFORM
95-
omit "root can write to any directory" if Process.uid == 0
118+
omit "root can write to any directory" if Process.euid == 0
96119
Dir.mktmpdir do |tmpdir|
97120
envs = %w[TMPDIR TMP TEMP]
98121
oldenv = envs.each_with_object({}) {|v, h| h[v] = ENV.delete(v)}

0 commit comments

Comments
 (0)