Skip to content
Merged
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
54 changes: 53 additions & 1 deletion src/Cms/Maintenance/MaintenanceFilter.php
Original file line number Diff line number Diff line change
Expand Up @@ -80,10 +80,36 @@ private function renderMaintenancePage(): string
// Check for custom view
if( $this->_CustomView && file_exists( $this->_CustomView ) )
{
// Validate path to prevent directory traversal attacks
$customViewPath = $this->_CustomView;
$realPath = realpath( $customViewPath );

// For virtual filesystems (like vfsStream in tests), realpath may return false
// In such cases, perform basic validation on the original path
if( $realPath === false )
{
// Validate that the path doesn't contain directory traversal patterns
if( $this->containsDirectoryTraversal( $customViewPath ) )
{
throw new \RuntimeException( 'Invalid custom view path: directory traversal detected' );
}
$realPath = $customViewPath;
}
else
{
// For real filesystem, ensure the resolved path is within the application's resources directory
$basePath = realpath( __DIR__ . '/../../../resources' );

if( $basePath !== false && strpos( $realPath, $basePath ) !== 0 )
{
throw new \RuntimeException( 'Invalid custom view path: path must be within the resources directory' );
}
}

$message = $this->_Manager->getMessage();
$retryAfter = $this->_Manager->getRetryAfter();
ob_start();
include $this->_CustomView;
include $realPath;
return ob_get_clean();
}

Expand Down Expand Up @@ -226,6 +252,32 @@ private function getDefaultMaintenancePage(): string
HTML;
}

/**
* Check if a path contains directory traversal patterns
*
* @param string $path Path to validate
* @return bool True if directory traversal detected
*/
private function containsDirectoryTraversal( string $path ): bool
{
// Normalize path separators
$normalized = str_replace( '\\', '/', $path );

// Check for directory traversal pattern: .. as a directory component
// Split by / and check if any component is exactly '..'
$parts = explode( '/', $normalized );

foreach( $parts as $part )
{
if( $part === '..' )
{
return true;
}
}

return false;
}

/**
* Get the client's IP address
*
Expand Down
Loading