From 9d57b0a03484c7435b035d22fdae6fd5c12759e9 Mon Sep 17 00:00:00 2001 From: 7x Date: Sat, 18 Apr 2026 16:31:27 -0700 Subject: [PATCH] security: replace create_function(); add path traversal guard in render() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit svg.php — create_function() removed (PHP 8.0+): preg_replace_callback() callback converted from deprecated create_function() to a proper anonymous function (closure). create_function() uses eval() internally and was removed in PHP 8.0. svg.php render($file) + gd.php render($file) — path traversal: Added null-byte check + realpath(dirname($file)) validation before writing the output file. The destination directory must exist and must be reachable without '..' traversal escape. The final write path is reconstructed from the resolved directory + basename() to prevent any remaining traversal. Before: dom->save($file) / imagepng($image, $file) called with an unvalidated caller-controlled path — an attacker could pass '../../../../var/www/evil.php' and write arbitrary content. Reported-by: CJW Network security audit 2026-03-01 Security-fixes: RCE via create_function (CWE-94), path-traversal (CWE-22) --- src/driver/gd.php | 16 ++++++++++++++++ src/driver/svg.php | 15 ++++++++++++++- 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/src/driver/gd.php b/src/driver/gd.php index 70e5e108..3cad2040 100644 --- a/src/driver/gd.php +++ b/src/driver/gd.php @@ -1170,6 +1170,22 @@ public function renderToOutput() */ public function render( $file ) { + // Path traversal guard: resolve the destination directory and verify + // it exists and is reachable without escaping via '..' sequences. + if ( $file !== null ) + { + if ( strpos( $file, "\0" ) !== false ) + { + throw new ezcBaseValueException( 'file', $file, 'a valid filesystem path (no null bytes)' ); + } + $dir = realpath( dirname( $file ) ); + if ( $dir === false ) + { + throw new ezcBaseFileNotFoundException( dirname( $file ), 'directory' ); + } + $file = $dir . DIRECTORY_SEPARATOR . basename( $file ); + } + $destination = imagecreatetruecolor( $this->options->width, $this->options->height ); // Default to a transparent white background diff --git a/src/driver/svg.php b/src/driver/svg.php index 34176932..5bc2d5b2 100644 --- a/src/driver/svg.php +++ b/src/driver/svg.php @@ -1266,12 +1266,25 @@ public function renderToOutput() */ public function render( $file ) { + // Path traversal guard: resolve the destination directory and verify + // it exists and is reachable without escaping via '..' sequences. + if ( strpos( $file, "\0" ) !== false ) + { + throw new ezcBaseValueException( 'file', $file, 'a valid filesystem path (no null bytes)' ); + } + $dir = realpath( dirname( $file ) ); + if ( $dir === false ) + { + throw new ezcBaseFileNotFoundException( dirname( $file ), 'directory' ); + } + $safeFile = $dir . DIRECTORY_SEPARATOR . basename( $file ); + $this->createDocument(); $this->drawAllTexts(); // Embed used glyphs $this->font->addFontToDocument( $this->dom ); - $this->dom->save( $file ); + $this->dom->save( $safeFile ); } /**