Skip to content

Commit

Permalink
Fix WebDAV traversal
Browse files Browse the repository at this point in the history
Fix a traversal error that happens when traversing a WebDAV resource and the virtual host monster is used.

Fixes #195
  • Loading branch information
ale-rt committed Feb 26, 2024
1 parent 9d79be1 commit 5895439
Show file tree
Hide file tree
Showing 3 changed files with 84 additions and 0 deletions.
2 changes: 2 additions & 0 deletions news/195.bugfix
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Fix a traversal error that happens when traversing a WebDAV resource and the virtual host monster is used.
[ale-rt]
9 changes: 9 additions & 0 deletions plone/dexterity/browser/traversal.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from plone.dexterity.filerepresentation import FolderDataResource
from plone.dexterity.interfaces import DAV_FOLDER_DATA_ID
from plone.dexterity.interfaces import IDexterityContent
from Products.SiteAccess.VirtualHostMonster import VirtualHostMonster
from webdav.NullResource import NullResource
from zope.component import adapter
from zope.publisher.interfaces.browser import IBrowserRequest
Expand Down Expand Up @@ -38,6 +39,14 @@ def publishTraverse(self, request, name):

defaultTraversal = super().publishTraverse(request, name)

if isinstance(defaultTraversal, VirtualHostMonster):
# If we are traversing to a VHM, we want to just return it immediately.
# For WebDAV requests, the check that controls if the parent
# of the traversed object is the same as the context
# will most probably fail because VHM parent will usually be
# the Zope App object.
return defaultTraversal

# If this is a WebDAV PUT/PROPFIND/PROPPATCH request, don't acquire
# things. If we did, we couldn't create a new object with PUT, for
# example, because the acquired object would shadow the NullResource
Expand Down
73 changes: 73 additions & 0 deletions plone/dexterity/tests/test_webdav.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
from ZPublisher.Iterators import IStreamIterator

import re
import unittest


XML_PROLOG = b'<?xml version="1.0" encoding="utf-8" ?>'
Expand Down Expand Up @@ -1261,3 +1262,75 @@ def __browser_default__(self, request):
),
traversal.browserDefault(request),
)


class TestDexterityPublishTraverse(unittest.TestCase):

def setUp(self):
"""Inspired by webdav.tests.testPUT_factory.TestPUTFactory"""
from Testing.makerequest import makerequest

import Zope2

# Create a basic data structure
self.app = makerequest(Zope2.app())

self.app.manage_addFolder("folder", "")
self.folder = self.app.folder

self.folder.manage_addFolder("subfolder", "")
self.subfolder = self.folder.subfolder

@property
def get_request(self):
request = self.app.REQUEST
request["PARENTS"] = [self.app]
return request

@property
def lock_request(self):
lock_request = self.get_request.clone()
lock_request["REQUEST_METHOD"] = "LOCK"
lock_request.maybe_webdav_client = True
return lock_request

def test_get_subfolder(self):
traversal = DexterityPublishTraverse(self.folder, None)
traversed = traversal.publishTraverse(self.get_request, "subfolder")
self.assertEqual(traversed, self.subfolder)

def test_lock_subfolder(self):
traversal = DexterityPublishTraverse(self.folder, None)
traversed = traversal.publishTraverse(self.lock_request, "subfolder")
self.assertEqual(traversed, self.subfolder)

def test_get_acquired(self):
traversal = DexterityPublishTraverse(self.subfolder, None)
traversed = traversal.publishTraverse(self.get_request, "folder")
self.assertEqual(traversed, self.folder)

def test_lock_acquired(self):
"""Ensure we are protected against acquisition:
traversing to an acquired object should return a NullResource
"""
from webdav.NullResource import NullResource

traversal = DexterityPublishTraverse(self.subfolder, None)
traversed = traversal.publishTraverse(self.lock_request, "folder")
self.assertIsInstance(traversed, NullResource)

def test_get_vhm(self):
"""Ensure we can handle virtual hosting with regular requests"""
from Products.SiteAccess.VirtualHostMonster import VirtualHostMonster

traversal = DexterityPublishTraverse(self.folder, None)
traversed = traversal.publishTraverse(self.get_request, "virtual_hosting")
self.assertIsInstance(traversed, VirtualHostMonster)

def test_lock_vhm(self):
"""Ensure we can handle virtual hosting with dav requests"""
from Products.SiteAccess.VirtualHostMonster import VirtualHostMonster

traversal = DexterityPublishTraverse(self.folder, None)
traversed = traversal.publishTraverse(self.lock_request, "virtual_hosting")
self.assertIsInstance(traversed, VirtualHostMonster)

0 comments on commit 5895439

Please sign in to comment.