Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions lib/aikido/zen/scanners/path_traversal/helpers.rb
Original file line number Diff line number Diff line change
Expand Up @@ -67,11 +67,11 @@ def self.start_with_unsafe_path?(filepath, user_input)

DANGEROUS_PATH_STARTS.each do |dangerous_start|
if normalized_path.start_with?(dangerous_start) && normalized_path.start_with?(normalized_user_input)
# If the user input is the same as the dangerous start, we don't want to flag it
# to prevent false positives.
# e.g., if user input is /etc/ and the path is /etc/passwd, we don't want to flag it,
# as long as the user input does not contain a subdirectory or filename
return false if user_input == dangerous_start || user_input == dangerous_start.chomp("/")
# If the normalized filepath equals the normalized user input, we don't want to flag it
# to prevent false positives when accessing exactly the dangerous directory itself.
# e.g., if user input is /etc and the path is /etc, we don't want to flag it.
# However, if the path is /etc/passwd and user input is /etc, this should be flagged.
return false if normalized_path == normalized_user_input

return true
end
Expand Down
45 changes: 29 additions & 16 deletions test/aikido/zen/scanners/path_traversal_scanner_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -66,39 +66,39 @@ def scan(filepath, input = query)
end

test "linux paths" do
refute_attack "/etc/passwd", "/etc/"
assert_attack "/etc/passwd", "/etc/"
assert_attack "/etc/passwd", "/etc/passwd"
assert_attack "/etc/../etc/passwd", "/etc/../etc/passwd"
assert_attack "/home/user/file.txt", "/home/user"
end

test "common container/cloud directories" do
refute_attack "/app/file.txt", "/app/"
assert_attack "/app/file.txt", "/app/"
assert_attack "/app/file.txt", "/app/file.txt"
assert_attack "/app/../app/file.txt", "/app/../app/file.txt"
assert_attack "/app/user/file.txt", "/app/user"

refute_attack "/code/file.txt", "/code/"
assert_attack "/code/file.txt", "/code/"
assert_attack "/code/file.txt", "/code/file.txt"
assert_attack "/code/../code/file.txt", "/code/../code/file.txt"
assert_attack "/code/user/file.txt", "/code/user"

refute_attack "/data/file.txt", "/data/"
assert_attack "/data/file.txt", "/data/"
assert_attack "/data/file.txt", "/data/file.txt"
assert_attack "/data/../data/file.txt", "/data/../data/file.txt"
assert_attack "/data/user/file.txt", "/data/user"

refute_attack "/rails/file.txt", "/rails/"
assert_attack "/rails/file.txt", "/rails/"
assert_attack "/rails/file.txt", "/rails/file.txt"
assert_attack "/rails/../rails/file.txt", "/rails/../rails/file.txt"
assert_attack "/rails/app/file.txt", "/rails/app"

refute_attack "/workspace/file.txt", "/workspace/"
assert_attack "/workspace/file.txt", "/workspace/"
assert_attack "/workspace/file.txt", "/workspace/file.txt"
assert_attack "/workspace/../workspace/file.txt", "/workspace/../workspace/file.txt"
assert_attack "/workspace/project/file.txt", "/workspace/project"

refute_attack "/workspaces/file.txt", "/workspaces/"
assert_attack "/workspaces/file.txt", "/workspaces/"
assert_attack "/workspaces/file.txt", "/workspaces/file.txt"
assert_attack "/workspaces/../workspaces/file.txt", "/workspaces/../workspaces/file.txt"
assert_attack "/workspaces/project/file.txt", "/workspaces/project"
Expand Down Expand Up @@ -159,13 +159,20 @@ def scan(filepath, input = query)
refute_attack "./~root/file.txt/some-file", "~root/file.txt"
end

test "does not detect if user input path contains no filename or subfolder" do
refute_attack "/etc/app/test.txt", "/etc/"
refute_attack "/etc/app/", "/etc/"
refute_attack "/etc/app/", "/etc"
test "does not detect if user input path equals the filepath" do
refute_attack "/etc/", "/etc/"
refute_attack "/etc", "/etc"
refute_attack "/var/a", "/var/"
refute_attack "/var/a", "/var/a"
end

test "detects attack if user input is a dangerous directory and filepath is a descendant" do
assert_attack "/etc/app/test.txt", "/etc/"
assert_attack "/etc/app/", "/etc/"
assert_attack "/etc/app/", "/etc"
assert_attack "/var/a", "/var/"
end

test "does not detect if user input is not a prefix of filepath" do
refute_attack "/var/a", "/var/b"
refute_attack "/var/a", "/var/b/test.txt"
end
Expand All @@ -185,10 +192,16 @@ def scan(filepath, input = query)
assert_attack "///.///etc/passwd", "///.///etc/passwd"
end

test "normalized paths still trigger false positive prevention for bare root dirs" do
# User input that resolves to just a root dir should still be safe
refute_attack "/etc/./passwd", "/etc"
refute_attack "//etc//passwd", "/etc"
test "normalized paths still trigger false positive prevention for exact matches only" do
# User input that resolves to exactly the same path as filepath should still be safe
refute_attack "/etc", "/etc"
refute_attack "//etc//", "/etc"
end

test "normalized paths detect attacks when filepath is a descendant" do
# User input that resolves to a parent directory should be flagged when filepath is a descendant
assert_attack "/etc/./passwd", "/etc"
assert_attack "//etc//passwd", "/etc"
end

test "it does dected if user input path contains a filename or subfolder" do
Expand Down
Loading