Skip to content

Commit 86ab942

Browse files
committed
Land rapid7#2146, Unix and Windows path normalization
2 parents 0928a37 + 8b0aac2 commit 86ab942

File tree

2 files changed

+108
-1
lines changed

2 files changed

+108
-1
lines changed

lib/rex/file.rb

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,54 @@ module Rex
1414
module FileUtils
1515

1616
#
17-
# This methods cleans the supplied path of directory traversal sequences
17+
# This method joins the paths together in Unix format.
18+
#
19+
def self.normalize_unix_path(*strs)
20+
new_str = strs * '/'
21+
new_str = new_str.gsub!("//", "/") while new_str.index("//")
22+
23+
new_str
24+
end
25+
26+
#
27+
# This method joins the paths together in Windows format.
28+
# All reserved characters will be filtered out, including:
29+
# " * : < > ? \ / |
30+
#
31+
def self.normalize_win_path(*strs)
32+
# Convert to the same format so the parsing is easier
33+
s = strs * '\\'
34+
35+
# Filter out double slashes
36+
s = s.gsub(/\\\\/, '\\') while s.index('\\\\')
37+
38+
# Keep the trailing slash if exists
39+
trailing_s = ('\\' if s =~ /\\$/) || ''
40+
41+
# Check the items (fie/dir) individually
42+
s = s.split(/\\/)
43+
44+
# Parse the path prefix
45+
prefix = (s[0] || '').gsub(/[\*<>\?\/]/, '')
46+
47+
# Delete the original prefix. We want the new one later.
48+
s.delete_at(0)
49+
50+
# Filter out all the reserved characters
51+
s.map! {|e| e.gsub(/["\*:<>\?\\\/|]/, '') }
52+
53+
# Put the modified prefix back
54+
s.insert(0, prefix)
55+
56+
# And then safely join the items
57+
s *= '\\'
58+
59+
# Add the trailing slash back if exists
60+
s << trailing_s
61+
end
62+
63+
#
64+
# This method cleans the supplied path of directory traversal sequences
1865
# It must accept path/with/..a/folder../starting/or/ending/in/two/dots
1966
# but clean ../something as well as path/with/..\traversal
2067
#

spec/lib/rex/file_utils_spec.rb

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
require 'rex/file'
2+
3+
describe Rex::FileUtils do
4+
context "Class methods" do
5+
6+
context ".normalize_win_path" do
7+
it "should convert an absolute path as an array into Windows format" do
8+
described_class.normalize_win_path('C:\\', 'hello', 'world').should eq("C:\\hello\\world")
9+
end
10+
11+
it "should convert an absolute path as a string into Windows format" do
12+
described_class.normalize_win_path('C:\\hello\\world').should eq("C:\\hello\\world")
13+
end
14+
15+
it "should convert a relative path" do
16+
described_class.normalize_win_path('/', 'test', 'me').should eq("\\test\\me")
17+
described_class.normalize_win_path('\\temp').should eq("\\temp")
18+
described_class.normalize_win_path('temp').should eq("temp")
19+
end
20+
21+
it "should keep the trailing slash if exists" do
22+
described_class.normalize_win_path('/', 'test', 'me\\').should eq("\\test\\me\\")
23+
described_class.normalize_win_path('\\temp\\').should eq("\\temp\\")
24+
end
25+
26+
it "should convert a path without reserved characters" do
27+
described_class.normalize_win_path('C:\\', 'Windows:').should eq("C:\\Windows")
28+
described_class.normalize_win_path('C:\\Windows???\\test').should eq("C:\\Windows\\test")
29+
end
30+
31+
it "should convert a path without double slashes" do
32+
described_class.normalize_win_path('C:\\\\\\', 'Windows').should eq("C:\\Windows")
33+
described_class.normalize_win_path('C:\\\\\\Hello World\\\\whatever.txt').should eq("C:\\Hello World\\whatever.txt")
34+
described_class.normalize_win_path('C:\\\\').should eq("C:\\")
35+
described_class.normalize_win_path('\\test\\\\test\\\\').should eq("\\test\\test\\")
36+
end
37+
end
38+
39+
context ".normalize_unix_path" do
40+
it "should convert an absolute path as an array into Unix format" do
41+
described_class.normalize_unix_path('/etc', '/passwd').should eq("/etc/passwd")
42+
end
43+
44+
it "should convert an absolute path as a string into Unix format" do
45+
described_class.normalize_unix_path('/etc/passwd').should eq('/etc/passwd')
46+
end
47+
48+
it "should still give me a trailing slash if I have it" do
49+
described_class.normalize_unix_path('/etc/folder/').should eq("/etc/folder/")
50+
end
51+
52+
it "should convert a path without double slashes" do
53+
described_class.normalize_unix_path('//etc////passwd').should eq("/etc/passwd")
54+
described_class.normalize_unix_path('/etc////', 'passwd').should eq('/etc/passwd')
55+
end
56+
end
57+
58+
end
59+
end
60+

0 commit comments

Comments
 (0)