CVE-2016-15019: Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal') (CWE-22)
value: A vulnerability was found in tombh jekbox. It has been rated as problematic. This issue affects some unknown processing of the file lib/server.rb. The manipulation leads to exposure of information through directory listing. The attack may be initiated remotely. The patch is named 64eb2677671018fc08b96718b81e3dbc83693190. It is recommended to apply a patch to fix this issue. The associated identifier of this vulnerability is VDB-218375.
CWE description: The product uses external input to construct a pathname that is intended to identify a file or directory that is located underneath a restricted parent directory, but the product does not properly neutralize special elements within the pathname that can cause the pathname to resolve to a location that is outside of the restricted directory.
CVSS v3 base score: 7.5
Published: 2023-01-15T19:15Z
Repository: https://github.com/tombh/jekbox
Language: Ruby
Files changed: lib/file_handler.rb, lib/server.rb, spec/fixtures/jekbox.example.com/_site/deep/index.html, spec/server_spec.rb
Commit date: 2016-02-01 13:08:18 +0100
Commit message: Ensure that requests ending with a slash and without try looking for an index.html file. And prevent malicious directory traversing

Diff: diff --git a/lib/file_handler.rb b/lib/file_handler.rb
index 68b8dc3..c83f8eb 100644
--- a/lib/file_handler.rb
+++ b/lib/file_handler.rb
@@ -1,16 +1,15 @@
+# Useful file methods
 class FileHandler
   def self.media_type(filename)
     extension = ::File.extname(filename)
-
     Rack::Mime.mime_type(extension)
   end
 
   def self.file_info(path)
     info = {
-      :body => ::File.read(path),
-      :time => ::File.mtime(path).httpdate
+      body: ::File.read(path),
+      time: ::File.mtime(path).httpdate
     }
-
     info
   end
 end
diff --git a/lib/server.rb b/lib/server.rb
index 62b3034..ec7871b 100644
--- a/lib/server.rb
+++ b/lib/server.rb
@@ -8,30 +8,32 @@ class Server
     build_response
     @response
   end
 
   def file_path
-    if @request.path_info.end_with?('/')
-      normalized = @request.path_info + 'index.html'
-    else
-      normalized = @request.path_info
-    end
     @root = File.join Jekbox::DROPBOX_PATH, @request.host
-    File.join @root, '_site', normalized
+    path = File.expand_path File.join @root, '_site', @request.path_info
+    with_index = File.join path, 'index.html'
+    if File.file? path
+      path
+    elsif File.file? with_index
+      with_index
+    end
   end
 
   def build_response
     @file = file_path
-    if File.exist? @file
+    if @file
       build_file_response
     else
       build_404
     end
     build_head if @request.head?
   end
 
   def build_file_response
+    fail unless @file.include? @root # Prevent malicious path requests
     file_info = FileHandler.file_info @file
     body = file_info[:body]
     time = file_info[:time]
     @headers = { 'Last-Modified' => time }
 
diff --git a/spec/fixtures/jekbox.example.com/_site/foo.css b/spec/fixtures/jekbox.example.com/_site/assets/foo.css
similarity index 100%
rename from spec/fixtures/jekbox.example.com/_site/foo.css
rename to spec/fixtures/jekbox.example.com/_site/assets/foo.css
diff --git a/spec/fixtures/jekbox.example.com/_site/deep/index.html b/spec/fixtures/jekbox.example.com/_site/deep/index.html
new file mode 100644
index 0000000..c7be334
--- /dev/null
+++ b/spec/fixtures/jekbox.example.com/_site/deep/index.html
@@ -0,0 +1 @@
+Deep page
diff --git a/spec/server_spec.rb b/spec/server_spec.rb
index db6f749..67f16a5 100644
--- a/spec/server_spec.rb
+++ b/spec/server_spec.rb
@@ -9,16 +9,34 @@ describe Server do
   before :each do
     stub_const('Jekbox::DROPBOX_PATH', File.join(Jekbox::PROJECT_ROOT, 'spec/fixtures'))
   end
 
   describe 'Find paths' do
-    it 'should find the index.html file when a URL without a file is requested' do
-      get 'http://jekbox.example.com'
-      expect(last_response.body).to eq "The index page\n"
+    context 'Finding the index.html file when a URL without a file is requested' do
+      it 'should use index.html when root is requested' do
+        get 'http://jekbox.example.com'
+        expect(last_response.body).to eq "The index page\n"
+      end
+
+      it 'should use index.html when a / is present' do
+        get 'http://jekbox.example.com/deep/'
+        expect(last_response.body).to eq "Deep page\n"
+      end
+
+      it 'should use index.html when a / is not present' do
+        get 'http://jekbox.example.com/deep'
+        expect(last_response.body).to eq "Deep page\n"
+      end
     end
 
     it 'should find a normal file' do
-      get 'http://jekbox.example.com/foo.css'
+      get 'http://jekbox.example.com/assets/foo.css'
       expect(last_response.body).to eq "body {}\n"
     end
+
+    it 'should find a normal file' do
+      expect do
+        get 'http://jekbox.example.com/../../../spec_helper.rb'
+      end.to raise_error RuntimeError
+    end
   end
 end

Vulnerable code: 
      :body => ::File.read(path),
      :time => ::File.mtime(path).httpdate

    if @request.path_info.end_with?('/')
      normalized = @request.path_info + 'index.html'
    else
      normalized = @request.path_info
    end
    File.join @root, '_site', normalized
    if File.exist? @file
    it 'should find the index.html file when a URL without a file is requested' do
      get 'http://jekbox.example.com'
      expect(last_response.body).to eq "The index page\n"
      get 'http://jekbox.example.com/foo.css'
Fixed code: # Useful file methods
      body: ::File.read(path),
      time: ::File.mtime(path).httpdate
    path = File.expand_path File.join @root, '_site', @request.path_info
    with_index = File.join path, 'index.html'
    if File.file? path
      path
    elsif File.file? with_index
      with_index
    end
    if @file
    fail unless @file.include? @root # Prevent malicious path requests
Deep page
    context 'Finding the index.html file when a URL without a file is requested' do
      it 'should use index.html when root is requested' do
        get 'http://jekbox.example.com'
        expect(last_response.body).to eq "The index page\n"
      end

      it 'should use index.html when a / is present' do
        get 'http://jekbox.example.com/deep/'
        expect(last_response.body).to eq "Deep page\n"
      end

      it 'should use index.html when a / is not present' do
        get 'http://jekbox.example.com/deep'
        expect(last_response.body).to eq "Deep page\n"
      end
      get 'http://jekbox.example.com/assets/foo.css'

    it 'should find a normal file' do
      expect do
        get 'http://jekbox.example.com/../../../spec_helper.rb'
      end.to raise_error RuntimeError
    end