1<?php 2/** 3 * Mikio CSS/LESS Engine 4 * 5 * @link http://dokuwiki.org/template:mikio 6 * @license GPLv2 7 * @author James Collins 8 */ 9 10require(__DIR__ . '/inc/polyfill-ctype.php'); 11 12if (!class_exists('lessc')) { 13 require(__DIR__ . '/inc/stemmechanics/lesserphp/lessc.inc.php'); 14} 15 16function logInvalidRequest($reason, $input) { 17 error_log("[css.php] $reason | input: $input | IP: " . ($_SERVER['REMOTE_ADDR'] ?? 'unknown')); 18} 19 20try { 21 if (!isset($_GET['css'])) { 22 http_response_code(404); 23 echo "The requested file could not be found"; 24 exit; 25 } 26 27 $cssFileList = explode(',', $_GET['css']); 28 $pluginRoot = realpath(__DIR__ . '/'); 29 $allowedDirs = [ 30 realpath($pluginRoot . '/assets'), 31 realpath($pluginRoot . '/styles') 32 ]; 33 $allowedExtensions = ['css', 'less']; 34 $css = ''; 35 $failed = false; 36 37 foreach ($cssFileList as $rawInput) { 38 // Strip query/hash 39 $cleanInput = explode('?', $rawInput, 2)[0]; 40 $cleanInput = trim(str_replace(['..', '\\'], '', $cleanInput)); 41 if (empty($cleanInput)) { 42 $failed = true; 43 logInvalidRequest("Empty or invalid path", $rawInput); 44 continue; 45 } 46 47 $resolvedPath = realpath($pluginRoot . '/' . $cleanInput); 48 $ext = pathinfo($resolvedPath, PATHINFO_EXTENSION); 49 50 if ( 51 !$resolvedPath || 52 !file_exists($resolvedPath) || 53 !in_array($ext, $allowedExtensions, true) 54 ) { 55 $failed = true; 56 logInvalidRequest("Invalid file or extension", $rawInput); 57 continue; 58 } 59 60 // Confirm file is within allowed directories 61 $allowed = false; 62 foreach ($allowedDirs as $dir) { 63 if (strpos($resolvedPath, $dir) === 0) { 64 $allowed = true; 65 break; 66 } 67 } 68 69 if (!$allowed) { 70 $failed = true; 71 logInvalidRequest("File outside allowed directory", $rawInput); 72 continue; 73 } 74 75 $css .= file_get_contents($resolvedPath); 76 } 77 78 if ($failed) { 79 http_response_code(404); 80 echo "The requested file could not be found"; 81 exit; 82 } 83 84 header('Content-Type: text/css; charset=utf-8'); 85 86 $less = new lessc(); 87 $less->setPreserveComments(false); 88 89 // Optional variables (future-proofed) 90 $rawVars = []; 91 $vars = []; 92 if (isset($rawVars['replacements'])) { 93 foreach ($rawVars['replacements'] as $key => $val) { 94 if (strpos($key, '__') === 0 && substr($key, -2) === '__') { 95 $vars['ini_' . substr($key, 2, -2)] = $val; 96 } 97 } 98 } 99 100 if (!empty($vars)) { 101 $less->setVariables($vars); 102 } 103 104 echo $less->compile($css); 105 106} catch (Exception $e) { 107 error_log("[css.php] Exception: " . $e->getMessage()); 108 http_response_code(500); 109 header('Content-Type: text/css; charset=utf-8'); 110 echo "/* An error occurred while processing the CSS. */"; 111}