Skip to content

Commit 4b3fce9

Browse files
committed
Add functions to normalize Winodws & Unix paths
The purpose of these functions is to be able to join file/dir paths safely without trailing slashes, basically for the same reason as normalize_uri. Some modules are really buggy when merging paths, so instead of letting them do it, it's better to use these functions.
1 parent de6e2ef commit 4b3fce9

File tree

2 files changed

+101
-1
lines changed

2 files changed

+101
-1
lines changed

lib/rex/file.rb

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,53 @@ 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+
# Check the items (fie/dir) individually
39+
s = s.split(/\\/)
40+
41+
# Parse the path prefix
42+
prefix = (s[0] || '').gsub(/[\*<>\?\/]/, '')
43+
if prefix.empty?
44+
prefix = '\\'
45+
elsif prefix =~ /^\w:$/ and s.length == 1
46+
prefix += '\\'
47+
end
48+
49+
# Delete the original prefix. We want the new one later.
50+
s.delete_at(0)
51+
52+
# Filter out all the reserved characters
53+
s.map! {|e| e.gsub(/["\*:<>\?\\\/|]/, '') }
54+
55+
# Put the modified prefix back
56+
s.insert(0, prefix)
57+
58+
# And then safely join the items
59+
s *= '\\'
60+
end
61+
62+
#
63+
# This method cleans the supplied path of directory traversal sequences
1864
# It must accept path/with/..a/folder../starting/or/ending/in/two/dots
1965
# but clean ../something as well as path/with/..\traversal
2066
#

spec/lib/rex/file_utils_spec.rb

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
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 path without reserved characters" do
16+
described_class.normalize_win_path('C:\\', 'Windows:').should eq("C:\\Windows")
17+
described_class.normalize_win_path('C:\\Windows???\\test').should eq("C:\\Windows\\test")
18+
end
19+
20+
it "should convert a path without double slashes" do
21+
described_class.normalize_win_path('C:\\\\\\', 'Windows').should eq("C:\\Windows")
22+
described_class.normalize_win_path('C:\\\\\\Hello World\\\\whatever.txt').should eq("C:\\Hello World\\whatever.txt")
23+
described_class.normalize_win_path('C:\\\\').should eq("C:\\")
24+
end
25+
26+
it "should parse UNC path format as an array" do
27+
described_class.normalize_win_path('\\\\127.0.0.1', 'C$').should eq("\\\\127.0.0.1\\C$")
28+
described_class.normalize_win_path('\\\\127.0.0.1\\C$').should eq("\\\\127.0.0.1\\C$")
29+
end
30+
31+
it "should parse a relative path in Windows format" do
32+
described_class.normalize_win_path('\\\\127.0.0.1', 'C$').should eq("\\\\127.0.0.1\\C$")
33+
described_class.normalize_win_path('\\\\127.0.0.1\\C$').should eq("\\\\127.0.0.1\\C$")
34+
end
35+
end
36+
37+
context ".normalize_unix_path" do
38+
it "should convert an absolute path as an array into Unix format" do
39+
described_class.normalize_unix_path('/etc', '/passwd').should eq("/etc/passwd")
40+
end
41+
42+
it "should convert an absolute path as a string into Windows format" do
43+
described_class.normalize_unix_path('/etc/passwd').should eq('/etc/passwd')
44+
end
45+
46+
it "should convert a path without double slashes" do
47+
described_class.normalize_unix_path('//etc////passwd').should eq("/etc/passwd")
48+
described_class.normalize_unix_path('/etc////', 'passwd').should eq('/etc/passwd')
49+
end
50+
end
51+
52+
end
53+
end
54+

0 commit comments

Comments
 (0)