This project does not seem to handle request data directly as such no vulnerable execution paths were found.
include
, or for example
via PHP's auto-loading mechanism.
These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
1 | <?php |
||
2 | namespace Elgg; |
||
3 | |||
4 | /** |
||
5 | * Simplecache handler |
||
6 | * |
||
7 | * @access private |
||
8 | * |
||
9 | * @package Elgg.Core |
||
10 | */ |
||
11 | class CacheHandler { |
||
12 | |||
13 | protected $config; |
||
14 | |||
15 | /** |
||
16 | * Constructor |
||
17 | * |
||
18 | * @param \stdClass $config Elgg config object |
||
19 | */ |
||
20 | 6 | public function __construct($config) { |
|
21 | 6 | $this->config = $config; |
|
22 | 6 | } |
|
23 | |||
24 | /** |
||
25 | * Handle a request for a cached view |
||
26 | * |
||
27 | * @param array $get_vars $_GET variables |
||
28 | * @param array $server_vars $_SERVER variables |
||
29 | * @return void |
||
30 | */ |
||
31 | public function handleRequest($get_vars, $server_vars) { |
||
32 | if (empty($get_vars['request'])) { |
||
33 | $this->send403(); |
||
34 | } |
||
35 | $request = $this->parseRequestVar($get_vars['request']); |
||
36 | if (!$request) { |
||
0 ignored issues
–
show
|
|||
37 | $this->send403(); |
||
38 | } |
||
39 | $ts = $request['ts']; |
||
40 | $view = $request['view']; |
||
41 | $viewtype = $request['viewtype']; |
||
42 | |||
43 | $this->sendContentType($view); |
||
44 | |||
45 | // this may/may not have to connect to the DB |
||
46 | $this->setupSimplecache(); |
||
47 | |||
48 | if (!$this->config->simplecache_enabled) { |
||
49 | $this->loadEngine(); |
||
50 | if (!_elgg_is_view_cacheable($view)) { |
||
51 | $this->send403(); |
||
52 | } else { |
||
53 | echo $this->renderView($view, $viewtype); |
||
54 | } |
||
55 | exit; |
||
56 | } |
||
57 | |||
58 | $etag = "\"$ts\""; |
||
59 | // If is the same ETag, content didn't change. |
||
60 | View Code Duplication | if (isset($server_vars['HTTP_IF_NONE_MATCH'])) { |
|
61 | // strip -gzip for #9427 |
||
62 | $if_none_match = str_replace('-gzip', '', trim($server_vars['HTTP_IF_NONE_MATCH'])); |
||
63 | if ($if_none_match === $etag) { |
||
64 | header("HTTP/1.1 304 Not Modified"); |
||
65 | header("ETag: $etag"); |
||
66 | exit; |
||
67 | } |
||
68 | } |
||
69 | |||
70 | $filename = $this->config->dataroot . 'views_simplecache/' . md5("$viewtype|$view"); |
||
71 | if (file_exists($filename)) { |
||
72 | $this->sendCacheHeaders($etag); |
||
73 | readfile($filename); |
||
74 | exit; |
||
75 | } |
||
76 | |||
77 | $this->loadEngine(); |
||
78 | |||
79 | elgg_set_viewtype($viewtype); |
||
80 | if (!_elgg_is_view_cacheable($view)) { |
||
81 | $this->send403(); |
||
82 | } |
||
83 | |||
84 | $cache_timestamp = (int)_elgg_services()->config->get('lastcache'); |
||
85 | |||
86 | if ($cache_timestamp == $ts) { |
||
87 | $this->sendCacheHeaders($etag); |
||
88 | |||
89 | $content = $this->getProcessedView($view, $viewtype); |
||
90 | |||
91 | $dir_name = $this->config->dataroot . 'views_simplecache/'; |
||
92 | if (!is_dir($dir_name)) { |
||
93 | mkdir($dir_name, 0700); |
||
94 | } |
||
95 | |||
96 | file_put_contents($filename, $content); |
||
97 | } else { |
||
98 | // if wrong timestamp, don't send HTTP cache |
||
99 | $content = $this->renderView($view, $viewtype); |
||
100 | } |
||
101 | |||
102 | echo $content; |
||
103 | exit; |
||
104 | } |
||
105 | |||
106 | /** |
||
107 | * Parse a request |
||
108 | * |
||
109 | * @param string $request_var Request URL |
||
110 | * @return array Cache parameters (empty array if failure) |
||
111 | */ |
||
112 | 6 | public function parseRequestVar($request_var) { |
|
113 | // no '..' |
||
114 | 6 | if (false !== strpos($request_var, '..')) { |
|
115 | 1 | return array(); |
|
116 | } |
||
117 | // only alphanumeric characters plus /, ., -, and _ |
||
118 | 5 | if (preg_match('#[^a-zA-Z0-9/\.\-_]#', $request_var)) { |
|
119 | 1 | return array(); |
|
120 | } |
||
121 | |||
122 | // testing showed regex to be marginally faster than array / string functions over 100000 reps |
||
123 | // it won't make a difference in real life and regex is easier to read. |
||
124 | // <ts>/<viewtype>/<name/of/view.and.dots>.<type> |
||
125 | 4 | if (!preg_match('#^/?([0-9]+)/([^/]+)/(.+)$#', $request_var, $matches)) { |
|
126 | 2 | return array(); |
|
127 | } |
||
128 | |||
129 | return array( |
||
130 | 2 | 'ts' => $matches[1], |
|
131 | 2 | 'viewtype' => $matches[2], |
|
132 | 2 | 'view' => $matches[3], |
|
133 | 2 | ); |
|
134 | } |
||
135 | |||
136 | /** |
||
137 | * Do a minimal engine load |
||
138 | * |
||
139 | * @return void |
||
140 | */ |
||
141 | protected function setupSimplecache() { |
||
142 | if (!empty($this->config->dataroot) && isset($this->config->simplecache_enabled)) { |
||
143 | return; |
||
144 | } |
||
145 | |||
146 | $db_config = new Database\Config($this->config); |
||
147 | $db = new Database($db_config, new Logger(new PluginHooksService())); |
||
148 | |||
149 | try { |
||
150 | $rows = $db->getData(" |
||
151 | SELECT `name`, `value` |
||
152 | FROM {$db->getTablePrefix()}datalists |
||
153 | WHERE `name` IN ('dataroot', 'simplecache_enabled') |
||
154 | "); |
||
155 | if (!$rows) { |
||
0 ignored issues
–
show
The expression
$rows of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.
This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent. Consider making the comparison explicit by using ![]() |
|||
156 | $this->send403('Cache error: unable to get the data root'); |
||
157 | } |
||
158 | } catch (\DatabaseException $e) { |
||
159 | if (0 === strpos($e->getMessage(), "Elgg couldn't connect")) { |
||
160 | $this->send403('Cache error: unable to connect to database server'); |
||
161 | } else { |
||
162 | $this->send403('Cache error: unable to connect to Elgg database'); |
||
163 | } |
||
164 | exit; // unnecessary, but helps PhpStorm understand |
||
165 | } |
||
166 | |||
167 | foreach ($rows as $row) { |
||
168 | $this->config->{$row->name} = $row->value; |
||
169 | } |
||
170 | |||
171 | if (empty($this->config->dataroot)) { |
||
172 | $this->send403('Cache error: unable to get the data root'); |
||
173 | } |
||
174 | } |
||
175 | |||
176 | /** |
||
177 | * Send cache headers |
||
178 | * |
||
179 | * @param string $etag ETag value |
||
180 | * @return void |
||
181 | */ |
||
182 | protected function sendCacheHeaders($etag) { |
||
183 | header('Expires: ' . gmdate('D, d M Y H:i:s \G\M\T', strtotime("+6 months")), true); |
||
184 | header("Pragma: public", true); |
||
185 | header("Cache-Control: public", true); |
||
186 | header("ETag: $etag"); |
||
187 | } |
||
188 | |||
189 | /** |
||
190 | * Send content type |
||
191 | * |
||
192 | * @param string $view The view name |
||
193 | * @return void |
||
194 | */ |
||
195 | protected function sendContentType($view) { |
||
196 | $segments = explode('/', $view, 2); |
||
197 | View Code Duplication | switch ($segments[0]) { |
|
198 | case 'css': |
||
199 | header("Content-Type: text/css;charset=utf-8"); |
||
200 | break; |
||
201 | case 'js': |
||
202 | header('Content-Type: text/javascript;charset=utf-8'); |
||
203 | break; |
||
204 | default: |
||
205 | header('Content-Type: text/html;charset=utf-8'); |
||
206 | } |
||
207 | } |
||
208 | |||
209 | /** |
||
210 | * Get the contents of a view for caching |
||
211 | * |
||
212 | * @param string $view The view name |
||
213 | * @param string $viewtype The viewtype |
||
214 | * @return string |
||
215 | * @see CacheHandler::renderView() |
||
216 | */ |
||
217 | protected function getProcessedView($view, $viewtype) { |
||
218 | $content = $this->renderView($view, $viewtype); |
||
219 | |||
220 | $hook_type = _elgg_get_view_filetype($view); |
||
221 | $hook_params = array( |
||
222 | 'view' => $view, |
||
223 | 'viewtype' => $viewtype, |
||
224 | 'view_content' => $content, |
||
225 | ); |
||
226 | return _elgg_services()->hooks->trigger('simplecache:generate', $hook_type, $hook_params, $content); |
||
227 | } |
||
228 | |||
229 | /** |
||
230 | * Render a view for caching |
||
231 | * |
||
232 | * @param string $view The view name |
||
233 | * @param string $viewtype The viewtype |
||
234 | * @return string |
||
235 | */ |
||
236 | protected function renderView($view, $viewtype) { |
||
237 | elgg_set_viewtype($viewtype); |
||
238 | |||
239 | if (!elgg_view_exists($view)) { |
||
240 | $this->send403(); |
||
241 | } |
||
242 | |||
243 | // disable error reporting so we don't cache problems |
||
244 | _elgg_services()->config->set('debug', null); |
||
245 | |||
246 | // @todo elgg_view() checks if the page set is done (isset($CONFIG->pagesetupdone)) and |
||
247 | // triggers an event if it's not. Calling elgg_view() here breaks submenus |
||
248 | // (at least) because the page setup hook is called before any |
||
249 | // contexts can be correctly set (since this is called before page_handler()). |
||
250 | // To avoid this, lie about $CONFIG->pagehandlerdone to force |
||
251 | // the trigger correctly when the first view is actually being output. |
||
252 | _elgg_services()->config->set('pagesetupdone', true); |
||
253 | |||
254 | return elgg_view($view); |
||
255 | } |
||
256 | |||
257 | /** |
||
258 | * Load the complete Elgg engine |
||
259 | * |
||
260 | * @return void |
||
261 | */ |
||
262 | protected function loadEngine() { |
||
263 | require_once dirname(dirname(dirname(__FILE__))) . "/start.php"; |
||
264 | } |
||
265 | |||
266 | /** |
||
267 | * Send an error message to requestor |
||
268 | * |
||
269 | * @param string $msg Optional message text |
||
270 | * @return void |
||
271 | */ |
||
272 | protected function send403($msg = 'Cache error: bad request') { |
||
273 | header('HTTP/1.1 403 Forbidden'); |
||
274 | echo $msg; |
||
275 | exit; |
||
276 | } |
||
277 | } |
||
278 | |||
279 |
This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.
Consider making the comparison explicit by using
empty(..)
or! empty(...)
instead.