Skip to content

Commit 78264ff

Browse files
committed
[GR-18163] Fix file permissions predicates for superuser
PullRequest: truffleruby/3429
2 parents 545cdd6 + 0c8bf49 commit 78264ff

File tree

9 files changed

+147
-2
lines changed

9 files changed

+147
-2
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ Bug fixes:
88

99
* Fix `StringIO` to set position correctly after reading multi-byte characters (#2207, @aardvark179).
1010
* Update `Process` methods to use `module_function` (@bjfish).
11+
* Fix `File::Stat`'s `#executable?` and `#executable_real?` predicates that unconditionally returned `true` for a superuser (#2690, @andrykonchin).
1112

1213
Compatibility:
1314

spec/mspec/lib/mspec/guards/superuser.rb

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,20 @@ def match?
66
end
77
end
88

9+
class RealSuperUserGuard < SpecGuard
10+
def match?
11+
Process.uid == 0
12+
end
13+
end
14+
915
def as_superuser(&block)
1016
SuperUserGuard.new.run_if(:as_superuser, &block)
1117
end
1218

19+
def as_real_superuser(&block)
20+
RealSuperUserGuard.new.run_if(:as_real_superuser, &block)
21+
end
22+
1323
def as_user(&block)
1424
SuperUserGuard.new.run_unless(:as_user, &block)
1525
end

spec/ruby/shared/file/executable.rb

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,41 @@
3939
-> { @object.send(@method, nil) }.should raise_error(TypeError)
4040
-> { @object.send(@method, false) }.should raise_error(TypeError)
4141
end
42+
43+
platform_is_not :windows do
44+
as_superuser do
45+
context "when run by a superuser" do
46+
before :each do
47+
@file = tmp('temp3.txt')
48+
touch @file
49+
end
50+
51+
after :each do
52+
rm_r @file
53+
end
54+
55+
it "returns true if file owner has permission to execute" do
56+
File.chmod(0766, @file)
57+
@object.send(@method, @file).should == true
58+
end
59+
60+
it "returns true if group has permission to execute" do
61+
File.chmod(0676, @file)
62+
@object.send(@method, @file).should == true
63+
end
64+
65+
it "returns true if other have permission to execute" do
66+
File.chmod(0667, @file)
67+
@object.send(@method, @file).should == true
68+
end
69+
70+
it "return false if nobody has permission to execute" do
71+
File.chmod(0666, @file)
72+
@object.send(@method, @file).should == false
73+
end
74+
end
75+
end
76+
end
4277
end
4378

4479
describe :file_executable_missing, shared: true do

spec/ruby/shared/file/executable_real.rb

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,41 @@
3737
-> { @object.send(@method, nil) }.should raise_error(TypeError)
3838
-> { @object.send(@method, false) }.should raise_error(TypeError)
3939
end
40+
41+
platform_is_not :windows do
42+
as_real_superuser do
43+
context "when run by a real superuser" do
44+
before :each do
45+
@file = tmp('temp3.txt')
46+
touch @file
47+
end
48+
49+
after :each do
50+
rm_r @file
51+
end
52+
53+
it "returns true if file owner has permission to execute" do
54+
File.chmod(0766, @file)
55+
@object.send(@method, @file).should == true
56+
end
57+
58+
it "returns true if group has permission to execute" do
59+
File.chmod(0676, @file)
60+
@object.send(@method, @file).should == true
61+
end
62+
63+
it "returns true if other have permission to execute" do
64+
File.chmod(0667, @file)
65+
@object.send(@method, @file).should == true
66+
end
67+
68+
it "return false if nobody has permission to execute" do
69+
File.chmod(0666, @file)
70+
@object.send(@method, @file).should == false
71+
end
72+
end
73+
end
74+
end
4075
end
4176

4277
describe :file_executable_real_missing, shared: true do

spec/ruby/shared/file/readable.rb

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,22 @@
2424
it "accepts an object that has a #to_path method" do
2525
@object.send(@method, mock_to_path(@file2)).should == true
2626
end
27+
28+
platform_is_not :windows do
29+
as_superuser do
30+
context "when run by a superuser" do
31+
it "returns true unconditionally" do
32+
file = tmp('temp.txt')
33+
touch file
34+
35+
File.chmod(0333, file)
36+
@object.send(@method, file).should == true
37+
38+
rm_r file
39+
end
40+
end
41+
end
42+
end
2743
end
2844

2945
describe :file_readable_missing, shared: true do

spec/ruby/shared/file/readable_real.rb

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,22 @@
1414
it "accepts an object that has a #to_path method" do
1515
File.open(@file,'w') { @object.send(@method, mock_to_path(@file)).should == true }
1616
end
17+
18+
platform_is_not :windows do
19+
as_real_superuser do
20+
context "when run by a real superuser" do
21+
it "returns true unconditionally" do
22+
file = tmp('temp.txt')
23+
touch file
24+
25+
File.chmod(0333, file)
26+
@object.send(@method, file).should == true
27+
28+
rm_r file
29+
end
30+
end
31+
end
32+
end
1733
end
1834

1935
describe :file_readable_real_missing, shared: true do

spec/ruby/shared/file/writable.rb

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,22 @@
1919
it "accepts an object that has a #to_path method" do
2020
File.open(@file,'w') { @object.send(@method, mock_to_path(@file)).should == true }
2121
end
22+
23+
platform_is_not :windows do
24+
as_superuser do
25+
context "when run by a superuser" do
26+
it "returns true unconditionally" do
27+
file = tmp('temp.txt')
28+
touch file
29+
30+
File.chmod(0555, file)
31+
@object.send(@method, file).should == true
32+
33+
rm_r file
34+
end
35+
end
36+
end
37+
end
2238
end
2339

2440
describe :file_writable_missing, shared: true do

spec/ruby/shared/file/writable_real.rb

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,22 @@
2424
-> { @object.send(@method, nil) }.should raise_error(TypeError)
2525
-> { @object.send(@method, false) }.should raise_error(TypeError)
2626
end
27+
28+
platform_is_not :windows do
29+
as_real_superuser do
30+
context "when run by a real superuser" do
31+
it "returns true unconditionally" do
32+
file = tmp('temp.txt')
33+
touch file
34+
35+
File.chmod(0555, file)
36+
@object.send(@method, file).should == true
37+
38+
rm_r file
39+
end
40+
end
41+
end
42+
end
2743
end
2844

2945
describe :file_writable_real_missing, shared: true do

src/main/ruby/truffleruby/core/stat.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -131,14 +131,14 @@ def directory?
131131
end
132132

133133
def executable?
134-
return true if superuser?
134+
return mode & S_IXUGO != 0 if superuser?
135135
return mode & S_IXUSR != 0 if owned?
136136
return mode & S_IXGRP != 0 if grpowned?
137137
mode & S_IXOTH != 0
138138
end
139139

140140
def executable_real?
141-
return true if rsuperuser?
141+
return mode & S_IXUGO != 0 if rsuperuser?
142142
return mode & S_IXUSR != 0 if rowned?
143143
return mode & S_IXGRP != 0 if rgrpowned?
144144
mode & S_IXOTH != 0

0 commit comments

Comments
 (0)