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.
1 | <?php |
||||
2 | /** |
||||
3 | * CMS Pico - Create websites using Pico CMS for Nextcloud. |
||||
4 | * |
||||
5 | * @copyright Copyright (c) 2017, Maxence Lange (<[email protected]>) |
||||
6 | * @copyright Copyright (c) 2019, Daniel Rudolf (<[email protected]>) |
||||
7 | * |
||||
8 | * @license GNU AGPL version 3 or any later version |
||||
9 | * |
||||
10 | * This program is free software: you can redistribute it and/or modify |
||||
11 | * it under the terms of the GNU Affero General Public License as |
||||
12 | * published by the Free Software Foundation, either version 3 of the |
||||
13 | * License, or (at your option) any later version. |
||||
14 | * |
||||
15 | * This program is distributed in the hope that it will be useful, |
||||
16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
18 | * GNU Affero General Public License for more details. |
||||
19 | * |
||||
20 | * You should have received a copy of the GNU Affero General Public License |
||||
21 | * along with this program. If not, see <http://www.gnu.org/licenses/>. |
||||
22 | */ |
||||
23 | |||||
24 | declare(strict_types=1); |
||||
25 | |||||
26 | namespace OCA\CMSPico\Service; |
||||
27 | |||||
28 | use OCA\CMSPico\AppInfo\Application; |
||||
29 | use OCA\CMSPico\Exceptions\PageInvalidPathException; |
||||
30 | use OCA\CMSPico\Exceptions\PageNotFoundException; |
||||
31 | use OCA\CMSPico\Exceptions\PageNotPermittedException; |
||||
32 | use OCA\CMSPico\Exceptions\PicoRuntimeException; |
||||
33 | use OCA\CMSPico\Exceptions\ThemeNotCompatibleException; |
||||
34 | use OCA\CMSPico\Exceptions\ThemeNotFoundException; |
||||
35 | use OCA\CMSPico\Exceptions\WebsiteInvalidFilesystemException; |
||||
36 | use OCA\CMSPico\Exceptions\WebsiteNotPermittedException; |
||||
37 | use OCA\CMSPico\Files\StorageFolder; |
||||
38 | use OCA\CMSPico\Model\PicoPage; |
||||
39 | use OCA\CMSPico\Model\Website; |
||||
40 | use OCA\CMSPico\Model\WebsiteRequest; |
||||
41 | use OCA\CMSPico\Pico; |
||||
42 | use OCP\Files\InvalidPathException; |
||||
43 | use OCP\Files\NotFoundException; |
||||
44 | use OCP\Files\NotPermittedException; |
||||
45 | use OCP\ILogger; |
||||
46 | |||||
47 | class PicoService |
||||
48 | { |
||||
49 | /** @var string */ |
||||
50 | public const DIR_TEMPLATES = 'templates'; |
||||
51 | |||||
52 | /** @var string */ |
||||
53 | public const DIR_CONFIG = 'config'; |
||||
54 | |||||
55 | /** @var string */ |
||||
56 | public const DIR_PLUGINS = 'plugins'; |
||||
57 | |||||
58 | /** @var string */ |
||||
59 | public const DIR_THEMES = 'themes'; |
||||
60 | |||||
61 | /** @var string */ |
||||
62 | public const DIR_CONTENT = 'content'; |
||||
63 | |||||
64 | /** @var string */ |
||||
65 | public const DIR_ASSETS = 'assets'; |
||||
66 | |||||
67 | /** @var string */ |
||||
68 | public const CONTENT_EXT = '.md'; |
||||
69 | |||||
70 | /** @var ILogger */ |
||||
71 | private $logger; |
||||
72 | |||||
73 | /** @var AssetsService */ |
||||
74 | private $assetsService; |
||||
75 | |||||
76 | /** @var ThemesService */ |
||||
77 | private $themesService; |
||||
78 | |||||
79 | /** @var PluginsService */ |
||||
80 | private $pluginsService; |
||||
81 | |||||
82 | /** @var FileService */ |
||||
83 | private $fileService; |
||||
84 | |||||
85 | /** @var MiscService */ |
||||
86 | private $miscService; |
||||
87 | |||||
88 | /** |
||||
89 | * PicoService constructor. |
||||
90 | * |
||||
91 | * @param ILogger $logger |
||||
92 | * @param AssetsService $assetsService |
||||
93 | * @param ThemesService $themesService |
||||
94 | * @param PluginsService $pluginsService |
||||
95 | * @param FileService $fileService |
||||
96 | * @param MiscService $miscService |
||||
97 | */ |
||||
98 | 1 | public function __construct( |
|||
99 | ILogger $logger, |
||||
100 | AssetsService $assetsService, |
||||
101 | ThemesService $themesService, |
||||
102 | PluginsService $pluginsService, |
||||
103 | FileService $fileService, |
||||
104 | MiscService $miscService |
||||
105 | ) { |
||||
106 | 1 | $this->logger = $logger; |
|||
107 | 1 | $this->assetsService = $assetsService; |
|||
108 | 1 | $this->themesService = $themesService; |
|||
109 | 1 | $this->pluginsService = $pluginsService; |
|||
110 | 1 | $this->fileService = $fileService; |
|||
111 | 1 | $this->miscService = $miscService; |
|||
112 | 1 | } |
|||
113 | |||||
114 | /** |
||||
115 | * @param WebsiteRequest $websiteRequest |
||||
116 | * |
||||
117 | * @return PicoPage |
||||
118 | * @throws WebsiteInvalidFilesystemException |
||||
119 | * @throws WebsiteNotPermittedException |
||||
120 | * @throws ThemeNotFoundException |
||||
121 | * @throws ThemeNotCompatibleException |
||||
122 | * @throws PageInvalidPathException |
||||
123 | * @throws PageNotFoundException |
||||
124 | * @throws PageNotPermittedException |
||||
125 | * @throws PicoRuntimeException |
||||
126 | */ |
||||
127 | 6 | public function getPage(WebsiteRequest $websiteRequest): PicoPage |
|||
128 | { |
||||
129 | 6 | $website = $websiteRequest->getWebsite(); |
|||
130 | 6 | $page = $websiteRequest->getPage(); |
|||
131 | |||||
132 | try { |
||||
133 | 6 | $websiteRequest->assertViewerAccess(self::DIR_CONTENT . '/' . ($page ?: 'index') . self::CONTENT_EXT); |
|||
134 | |||||
135 | 5 | $this->themesService->assertValidTheme($website->getTheme()); |
|||
136 | |||||
137 | 5 | $pico = new Pico( |
|||
138 | 5 | $website->getWebsitePath(), |
|||
139 | 5 | $this->getConfigPath(), |
|||
140 | 5 | $this->pluginsService->getPluginsPath(), |
|||
141 | 5 | $this->themesService->getThemesPath(), |
|||
142 | 5 | false |
|||
143 | ); |
||||
144 | |||||
145 | try { |
||||
146 | 5 | $this->setupPico($websiteRequest, $pico); |
|||
147 | 5 | $this->loadPicoPlugins($pico); |
|||
148 | |||||
149 | 5 | $output = $pico->run(); |
|||
150 | } catch (WebsiteInvalidFilesystemException $e) { |
||||
151 | throw $e; |
||||
152 | } catch (InvalidPathException | NotFoundException | NotPermittedException $e) { |
||||
153 | throw $e; |
||||
154 | } catch (\Exception $e) { |
||||
155 | $exception = new PicoRuntimeException($e); |
||||
156 | $this->logger->logException($exception, [ 'app' => Application::APP_NAME ]); |
||||
0 ignored issues
–
show
|
|||||
157 | throw $exception; |
||||
158 | } |
||||
159 | |||||
160 | 5 | $picoPage = new PicoPage($websiteRequest, $pico, $output); |
|||
161 | |||||
162 | 5 | $picoPagePath = self::DIR_CONTENT . '/' . $picoPage->getRelativePath() . self::CONTENT_EXT; |
|||
163 | 5 | $websiteRequest->assertViewerAccess($picoPagePath, $picoPage->getMeta()); |
|||
164 | 1 | } catch (InvalidPathException $e) { |
|||
165 | throw new PageInvalidPathException($website->getSite(), $page, $e); |
||||
166 | 1 | } catch (NotFoundException $e) { |
|||
167 | throw new PageNotFoundException($website->getSite(), $page, $e); |
||||
168 | 1 | } catch (NotPermittedException $e) { |
|||
169 | throw new PageNotPermittedException($website->getSite(), $page, $e); |
||||
170 | } |
||||
171 | |||||
172 | 5 | return $picoPage; |
|||
173 | } |
||||
174 | |||||
175 | /** |
||||
176 | * @param WebsiteRequest $websiteRequest |
||||
177 | * @param Pico $pico |
||||
178 | * |
||||
179 | * @throws WebsiteInvalidFilesystemException |
||||
180 | */ |
||||
181 | 5 | private function setupPico(WebsiteRequest $websiteRequest, Pico $pico): void |
|||
182 | { |
||||
183 | 5 | $website = $websiteRequest->getWebsite(); |
|||
184 | |||||
185 | 5 | $pico->setRequestUrl($websiteRequest->getPage()); |
|||
186 | 5 | $pico->setNextcloudWebsite($websiteRequest); |
|||
187 | |||||
188 | 5 | $pico->setConfig( |
|||
189 | [ |
||||
190 | 5 | 'site_title' => $website->getName(), |
|||
191 | 5 | 'base_url' => $this->getWebsiteUrl($websiteRequest), |
|||
192 | 'rewrite_url' => true, |
||||
193 | 5 | 'debug' => \OC::$server->getConfig()->getSystemValue('debug', false), |
|||
0 ignored issues
–
show
The function
OC\Server::getConfig() has been deprecated: 20.0.0
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
This function has been deprecated. The supplier of the function has supplied an explanatory message. The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead. ![]() |
|||||
194 | 5 | 'timezone' => $website->getTimeZone(), |
|||
195 | 5 | 'theme' => $website->getTheme(), |
|||
196 | 5 | 'themes_url' => $this->themesService->getThemesUrl(), |
|||
197 | 5 | 'content_dir' => $this->getContentPath($website), |
|||
198 | 5 | 'content_ext' => self::CONTENT_EXT, |
|||
199 | 5 | 'assets_dir' => $this->assetsService->getAssetsPath($website), |
|||
200 | 5 | 'assets_url' => $this->assetsService->getAssetsUrl($websiteRequest), |
|||
201 | 5 | 'plugins_url' => $this->pluginsService->getPluginsUrl(), |
|||
202 | 5 | 'nextcloud_site' => $website->getSite(), |
|||
203 | ] |
||||
204 | ); |
||||
205 | 5 | } |
|||
206 | |||||
207 | /** |
||||
208 | * @param Pico $pico |
||||
209 | */ |
||||
210 | 5 | private function loadPicoPlugins(Pico $pico): void |
|||
211 | { |
||||
212 | $includeClosure = static function (string $pluginFile) { |
||||
213 | /** @noinspection PhpIncludeInspection */ |
||||
214 | 5 | require_once($pluginFile); |
|||
215 | 5 | }; |
|||
216 | |||||
217 | 5 | foreach ($this->pluginsService->getPlugins() as $pluginData) { |
|||
218 | 5 | if ($pluginData['compat']) { |
|||
219 | 5 | $pluginFile = $pluginData['name'] . '/' . $pluginData['name'] . '.php'; |
|||
220 | 5 | $includeClosure($this->pluginsService->getPluginsPath() . $pluginFile); |
|||
221 | |||||
222 | 5 | $pico->loadPlugin($pluginData['name']); |
|||
223 | } |
||||
224 | } |
||||
225 | 5 | } |
|||
226 | |||||
227 | /** |
||||
228 | * @param Website $website |
||||
229 | * @param string $absolutePath |
||||
230 | * |
||||
231 | * @return array |
||||
232 | * @throws WebsiteInvalidFilesystemException |
||||
233 | * @throws InvalidPathException |
||||
234 | */ |
||||
235 | 5 | public function getRelativePath(Website $website, string $absolutePath): array |
|||
236 | { |
||||
237 | 5 | $folder = $website->getWebsiteFolder(); |
|||
238 | 5 | $basePath = $website->getWebsitePath(); |
|||
239 | |||||
240 | try { |
||||
241 | 5 | $relativePath = $this->miscService->getRelativePath($absolutePath, $basePath); |
|||
242 | 5 | } catch (InvalidPathException $e) { |
|||
243 | 5 | $folder = $this->pluginsService->getPluginsFolder(); |
|||
244 | 5 | $basePath = $this->pluginsService->getPluginsPath(); |
|||
245 | |||||
246 | try { |
||||
247 | 5 | $relativePath = $this->miscService->getRelativePath($absolutePath, $basePath); |
|||
248 | 5 | } catch (InvalidPathException $e) { |
|||
249 | 5 | $folder = $this->themesService->getThemesFolder(); |
|||
250 | 5 | $basePath = $this->themesService->getThemesPath(); |
|||
251 | |||||
252 | try { |
||||
253 | 5 | $relativePath = $this->miscService->getRelativePath($absolutePath, $basePath); |
|||
254 | 5 | } catch (InvalidPathException $e) { |
|||
255 | 5 | $folder = $this->getConfigFolder(); |
|||
256 | 5 | $basePath = $this->getConfigPath(); |
|||
257 | |||||
258 | try { |
||||
259 | 5 | $relativePath = $this->miscService->getRelativePath($absolutePath, $basePath); |
|||
260 | } catch (InvalidPathException $e) { |
||||
261 | // the file is neither in the content nor assets, plugins, themes or config folder |
||||
262 | // Pico mustn't have access to any other directory |
||||
263 | throw new InvalidPathException(); |
||||
264 | } |
||||
265 | } |
||||
266 | } |
||||
267 | } |
||||
268 | |||||
269 | 5 | return [ $folder, rtrim($basePath, '/'), $relativePath ]; |
|||
270 | } |
||||
271 | |||||
272 | /** |
||||
273 | * @param Website $website |
||||
274 | * |
||||
275 | * @return StorageFolder |
||||
276 | * @throws WebsiteInvalidFilesystemException |
||||
277 | */ |
||||
278 | 5 | public function getContentFolder(Website $website): StorageFolder |
|||
279 | { |
||||
280 | try { |
||||
281 | /** @var StorageFolder $websiteFolder */ |
||||
282 | 5 | $websiteFolder = $website->getWebsiteFolder()->getFolder(PicoService::DIR_CONTENT)->fakeRoot(); |
|||
283 | 5 | return $websiteFolder; |
|||
284 | } catch (InvalidPathException | NotFoundException $e) { |
||||
285 | throw new WebsiteInvalidFilesystemException($website->getSite(), $e); |
||||
286 | } |
||||
287 | } |
||||
288 | |||||
289 | /** |
||||
290 | * @param Website $website |
||||
291 | * |
||||
292 | * @return string |
||||
293 | * @throws WebsiteInvalidFilesystemException |
||||
294 | */ |
||||
295 | 5 | public function getContentPath(Website $website): string |
|||
296 | { |
||||
297 | try { |
||||
298 | 5 | return $this->getContentFolder($website)->getLocalPath() . '/'; |
|||
299 | } catch (InvalidPathException | NotFoundException $e) { |
||||
300 | throw new WebsiteInvalidFilesystemException($website->getSite(), $e); |
||||
301 | } |
||||
302 | } |
||||
303 | |||||
304 | /** |
||||
305 | * @return StorageFolder |
||||
306 | */ |
||||
307 | 5 | public function getConfigFolder(): StorageFolder |
|||
308 | { |
||||
309 | /** @var StorageFolder $configFolder */ |
||||
310 | 5 | $configFolder = $this->fileService->getAppDataFolder(self::DIR_CONFIG)->fakeRoot(); |
|||
311 | 5 | return $configFolder; |
|||
312 | } |
||||
313 | |||||
314 | /** |
||||
315 | * @return string |
||||
316 | */ |
||||
317 | 5 | public function getConfigPath(): string |
|||
318 | { |
||||
319 | 5 | return $this->fileService->getAppDataFolderPath(self::DIR_CONFIG); |
|||
320 | } |
||||
321 | |||||
322 | /** |
||||
323 | * @param WebsiteRequest $websiteRequest |
||||
324 | * |
||||
325 | * @return string |
||||
326 | */ |
||||
327 | 5 | public function getWebsiteUrl(WebsiteRequest $websiteRequest): string |
|||
328 | { |
||||
329 | 5 | $website = $websiteRequest->getWebsite(); |
|||
330 | 5 | return $website->getWebsiteUrl($websiteRequest->isProxyRequest()); |
|||
331 | } |
||||
332 | } |
||||
333 |
This function has been deprecated. The supplier of the function has supplied an explanatory message.
The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.