Issues (18)

Security Analysis    not enabled

This project does not seem to handle request data directly as such no vulnerable execution paths were found.

  Cross-Site Scripting
Cross-Site Scripting enables an attacker to inject code into the response of a web-request that is viewed by other users. It can for example be used to bypass access controls, or even to take over other users' accounts.
  File Exposure
File Exposure allows an attacker to gain access to local files that he should not be able to access. These files can for example include database credentials, or other configuration files.
  File Manipulation
File Manipulation enables an attacker to write custom data to files. This potentially leads to injection of arbitrary code on the server.
  Object Injection
Object Injection enables an attacker to inject an object into PHP code, and can lead to arbitrary code execution, file exposure, or file manipulation attacks.
  Code Injection
Code Injection enables an attacker to execute arbitrary code on the server.
  Response Splitting
Response Splitting can be used to send arbitrary responses.
  File Inclusion
File Inclusion enables an attacker to inject custom files into PHP's file loading mechanism, either explicitly passed to include, or for example via PHP's auto-loading mechanism.
  Command Injection
Command Injection enables an attacker to inject a shell command that is execute with the privileges of the web-server. This can be used to expose sensitive data, or gain access of your server.
  SQL Injection
SQL Injection enables an attacker to execute arbitrary SQL code on your database server gaining access to user data, or manipulating user data.
  XPath Injection
XPath Injection enables an attacker to modify the parts of XML document that are read. If that XML document is for example used for authentication, this can lead to further vulnerabilities similar to SQL Injection.
  LDAP Injection
LDAP Injection enables an attacker to inject LDAP statements potentially granting permission to run unauthorized queries, or modify content inside the LDAP tree.
  Header Injection
  Other Vulnerability
This category comprises other attack vectors such as manipulating the PHP runtime, loading custom extensions, freezing the runtime, or similar.
  Regex Injection
Regex Injection enables an attacker to execute arbitrary code in your PHP process.
  XML Injection
XML Injection enables an attacker to read files on your local filesystem including configuration files, or can be abused to freeze your web-server process.
  Variable Injection
Variable Injection enables an attacker to overwrite program variables with custom data, and can lead to further vulnerabilities.
Unfortunately, the security analysis is currently not available for your project. If you are a non-commercial open-source project, please contact support to gain access.

Classes/Loader/ContentObjects.php (1 issue)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
3
/**
4
 * Loading Slots.
5
 */
6
declare(strict_types = 1);
7
8
namespace HDNET\Autoloader\Loader;
9
10
use Doctrine\Common\Annotations\AnnotationReader;
11
use HDNET\Autoloader\Annotation\EnableRichText;
12
use HDNET\Autoloader\Annotation\NoHeader;
13
use HDNET\Autoloader\Annotation\WizardTab;
14
use HDNET\Autoloader\Loader;
15
use HDNET\Autoloader\LoaderInterface;
16
use HDNET\Autoloader\Service\NameMapperService;
17
use HDNET\Autoloader\SmartObjectRegister;
18
use HDNET\Autoloader\Utility\ClassNamingUtility;
19
use HDNET\Autoloader\Utility\FileUtility;
20
use HDNET\Autoloader\Utility\IconUtility;
21
use HDNET\Autoloader\Utility\ModelUtility;
22
use HDNET\Autoloader\Utility\ReflectionUtility;
23
use HDNET\Autoloader\Utility\TranslateUtility;
24
use TYPO3\CMS\Core\Imaging\IconProvider\BitmapIconProvider;
25
use TYPO3\CMS\Core\Imaging\IconProvider\SvgIconProvider;
26
use TYPO3\CMS\Core\Imaging\IconRegistry;
27
use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;
28
use TYPO3\CMS\Core\Utility\GeneralUtility;
29
use TYPO3\CMS\Extbase\Utility\ExtensionUtility;
30
31
/**
32
 * Loading Slots.
33
 */
34
class ContentObjects implements LoaderInterface
35
{
36
    /**
37
     * Prepare the content object loader.
38
     */
39
    public function prepareLoader(Loader $loader, int $type): array
40
    {
41
        $loaderInformation = [];
42
43
        /** @var AnnotationReader $annotationReader */
44
        $annotationReader = GeneralUtility::makeInstance(AnnotationReader::class);
45
46
        $modelPath = ExtensionManagementUtility::extPath($loader->getExtensionKey()) . 'Classes/Domain/Model/Content/';
47
        $models = FileUtility::getBaseFilesInDir($modelPath, 'php');
48
        if (!empty($models)) {
49
            TranslateUtility::assureLabel(
50
                'tt_content.' .
51
                $loader->getExtensionKey() . '.header',
52
                $loader->getExtensionKey(),
53
                $loader->getExtensionKey() . ' (Header)'
54
            );
55
        }
56
        foreach ($models as $model) {
57
            $key = GeneralUtility::camelCaseToLowerCaseUnderscored($model);
58
            $className = ClassNamingUtility::getFqnByPath(
59
                $loader->getVendorName(),
60
                $loader->getExtensionKey(),
61
                'Domain/Model/Content/' . $model
62
            );
63
            if (!$loader->isInstantiableClass($className)) {
64
                continue;
65
            }
66
            $fieldConfiguration = [];
67
            $richTextFields = [];
68
            $noHeader = $this->isTaggedWithNoHeader($className);
69
70
            $reflectionClass = new \ReflectionClass($className);
71
72
            // create labels in the ext_tables run, to have a valid DatabaseConnection
73
            if (LoaderInterface::EXT_TABLES === $type) {
74
                TranslateUtility::assureLabel('wizard.' . $key, $loader->getExtensionKey(), $key . ' (Title)', null, 'tt_content');
75
                TranslateUtility::assureLabel(
76
                    'wizard.' . $key . '.description',
77
                    $loader->getExtensionKey(),
78
                    $key . ' (Description)',
79
                    null,
80
                    'tt_content'
81
                );
82
                $fieldConfiguration = $this->getClassPropertiesInLowerCaseUnderscored($className);
83
                $defaultFields = $this->getDefaultTcaFields($noHeader, null);
84
                $fieldConfiguration = array_diff($fieldConfiguration, $defaultFields);
85
86
                // RTE manipulation
87
                foreach ($reflectionClass->getProperties() as $property) {
88
                    $richTextField = $annotationReader->getPropertyAnnotation($property, EnableRichText::class);
89
                    if (null === $richTextField) {
90
                        continue;
91
                    }
92
93
                    $search = array_search(
94
                        GeneralUtility::camelCaseToLowerCaseUnderscored($property->getName()),
95
                        $fieldConfiguration,
96
                        true
97
                    );
98
                    if (false !== $search) {
99
                        $richTextFields[] = $fieldConfiguration[$search];
100
                    }
101
                }
102
            }
103
104
            $entry = [
105
                'fieldConfiguration' => implode(',', $fieldConfiguration),
106
                'richTextFields' => $richTextFields,
107
                'modelClass' => $className,
108
                'model' => $model,
109
                'icon' => IconUtility::getByModelName($className, false),
110
                'iconExt' => IconUtility::getByModelName($className, true),
111
                'noHeader' => $noHeader,
112
                'tabInformation' => (string)$annotationReader->getClassAnnotation($reflectionClass, WizardTab::class),
113
            ];
114
115
            SmartObjectRegister::register($entry['modelClass']);
116
            $loaderInformation[$key] = $entry;
117
        }
118
119
        $this->checkAndCreateDummyTemplates($loaderInformation, $loader);
120
121
        return $loaderInformation;
122
    }
123
124
    /**
125
     * Run the loading process for the ext_tables.php file.
126
     */
127
    public function loadExtensionTables(Loader $loader, array $loaderInformation): void
128
    {
129
        if (empty($loaderInformation)) {
130
            return;
131
        }
132
        $createWizardHeader = [];
133
        $predefinedWizards = [
134
            'common',
135
            'special',
136
            'forms',
137
            'plugins',
138
        ];
139
140
        // Add the divider
141
        $GLOBALS['TCA']['tt_content']['columns']['CType']['config']['items'][] = [
142
            TranslateUtility::getLllString(
143
                'tt_content.' . $loader->getExtensionKey() . '.header',
144
                $loader->getExtensionKey(),
145
                null,
146
                'tt_content'
147
            ),
148
            '--div--',
149
        ];
150
151
        foreach ($loaderInformation as $e => $config) {
152
            SmartObjectRegister::register($config['modelClass']);
153
            $typeKey = $loader->getExtensionKey() . '_' . $e;
154
155
            ExtensionManagementUtility::addPlugin([
156
                TranslateUtility::getLllOrHelpMessage(
157
                    'content.element.' . $e,
158
                    $loader->getExtensionKey(),
159
                    'tt_content'
160
                ),
161
                $typeKey,
162
                $config['iconExt'],
163
            ], 'CType', $loader->getExtensionKey());
164
165
            if (!isset($GLOBALS['TCA']['tt_content']['types'][$typeKey]['showitem']) || empty($GLOBALS['TCA']['tt_content']['types'][$typeKey]['showitem'])) {
166
                $baseTcaConfiguration = $this->wrapDefaultTcaConfiguration(
167
                    $config['fieldConfiguration'],
168
                    (bool)$config['noHeader']
169
                );
170
171
                if (ExtensionManagementUtility::isLoaded('gridelements')) {
172
                    $baseTcaConfiguration .= ',tx_gridelements_container,tx_gridelements_columns';
173
                }
174
175
                $GLOBALS['TCA']['tt_content']['types'][$typeKey]['showitem'] = $baseTcaConfiguration;
176
            }
177
178
            // RTE
179
            if (isset($config['richTextFields']) && \is_array($config['richTextFields']) && $config['richTextFields']) {
180
                foreach ($config['richTextFields'] as $field) {
181
                    $conf = [
182
                        'config' => [
183
                            'type' => 'text',
184
                            'enableRichtext' => '1',
185
                            'richtextConfiguration' => 'default',
186
                        ],
187
                    ];
188
                    $GLOBALS['TCA']['tt_content']['types'][$typeKey]['columnsOverrides'][$field] = $conf;
189
                }
190
            }
191
192
            IconUtility::addTcaTypeIcon('tt_content', $typeKey, $config['icon']);
193
194
            $tabName = $config['tabInformation'] ?: $loader->getExtensionKey();
195
            if (!\in_array($tabName, $predefinedWizards, true) && !\in_array($tabName, $createWizardHeader, true)) {
196
                $createWizardHeader[] = $tabName;
197
            }
198
199
            /** @var IconRegistry $iconRegistry */
200
            $provider = BitmapIconProvider::class;
201
            if ('svg' === mb_substr(mb_strtolower($config['iconExt']), -3)) {
202
                $provider = SvgIconProvider::class;
203
            }
204
            $iconRegistry = GeneralUtility::makeInstance(IconRegistry::class);
205
            $iconRegistry->registerIcon($tabName . '-' . $typeKey, $provider, ['source' => $config['iconExt']]);
206
207
            ExtensionManagementUtility::addPageTSConfig('
208
mod.wizards.newContentElement.wizardItems.' . $tabName . '.elements.' . $typeKey . ' {
209
    icon = ' . $config['icon'] . '
210
    iconIdentifier = ' . $tabName . '-' . $typeKey . '
211
    title = ' . TranslateUtility::getLllOrHelpMessage('wizard.' . $e, $loader->getExtensionKey()) . '
212
    description = ' . TranslateUtility::getLllOrHelpMessage(
213
                'wizard.' . $e . '.description',
214
                $loader->getExtensionKey()
215
            ) . '
216
    tt_content_defValues {
217
        CType = ' . $typeKey . '
218
    }
219
}
220
mod.wizards.newContentElement.wizardItems.' . $tabName . '.show := addToList(' . $typeKey . ')');
221
            $cObjectConfiguration = [
222
                'extensionKey' => $loader->getExtensionKey(),
223
                'backendTemplatePath' => 'EXT:' . $loader->getExtensionKey() . '/Resources/Private/Templates/Content/' . $config['model'] . 'Backend.html',
224
                'modelClass' => $config['modelClass'],
225
            ];
226
227
            $GLOBALS['TYPO3_CONF_VARS']['AUTOLOADER']['ContentObject'][$loader->getExtensionKey() . '_' . GeneralUtility::camelCaseToLowerCaseUnderscored($config['model'])] = $cObjectConfiguration;
228
        }
229
230
        if ($createWizardHeader) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $createWizardHeader 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 empty(..) or ! empty(...) instead.

Loading history...
231
            foreach ($createWizardHeader as $element) {
232
                ExtensionManagementUtility::addPageTSConfig('
233
mod.wizards.newContentElement.wizardItems.' . $element . ' {
234
    show = *
235
    header = ' . TranslateUtility::getLllOrHelpMessage('wizard.' . $element . '.header', $loader->getExtensionKey()) . '
236
}');
237
            }
238
        }
239
    }
240
241
    /**
242
     * Run the loading process for the ext_localconf.php file.
243
     */
244
    public function loadExtensionConfiguration(Loader $loader, array $loaderInformation): void
245
    {
246
        if (empty($loaderInformation)) {
247
            return;
248
        }
249
        static $loadPlugin = true;
250
        $csc = ExtensionManagementUtility::isLoaded('css_styled_content');
251
        $typoScript = '';
252
253
        if ($loadPlugin) {
254
            $loadPlugin = false;
255
            ExtensionUtility::configurePlugin('HDNET.autoloader', 'Content', ['Content' => 'index'], ['Content' => '']);
256
            if (!$csc) {
257
                $typoScript .= 'tt_content = CASE
258
tt_content.key.field = CType';
259
            }
260
        }
261
        foreach ($loaderInformation as $e => $config) {
262
            $typoScript .= '
263
        tt_content.' . $loader->getExtensionKey() . '_' . $e . ' = COA
264
        tt_content.' . $loader->getExtensionKey() . '_' . $e . ' {
265
            ' . ($config['noHeader'] ? '' : '10 =< lib.stdheader') . '
266
            20 = USER
267
            20 {
268
                userFunc = TYPO3\CMS\Extbase\Core\Bootstrap->run
269
                extensionName = Autoloader
270
                pluginName = Content
271
                vendorName = HDNET
272
                settings {
273
                    contentElement = ' . $config['model'] . '
274
                    extensionKey = ' . $loader->getExtensionKey() . '
275
                    vendorName = ' . $loader->getVendorName() . '
276
                }
277
            }
278
        }
279
        config.tx_extbase.persistence.classes.' . $config['modelClass'] . '.mapping.tableName = tt_content
280
        ';
281
        }
282
283
        if ($csc) {
284
            ExtensionManagementUtility::addTypoScript($loader->getExtensionKey(), 'setup', $typoScript, 43);
285
        } else {
286
            ExtensionManagementUtility::addTypoScriptSetup($typoScript);
287
        }
288
    }
289
290
    /**
291
     * Check if the class is tagged with noHeader.
292
     *
293
     * @param $class
294
     */
295
    protected function isTaggedWithNoHeader($class): bool
296
    {
297
        /** @var AnnotationReader $annotationReader */
298
        $annotationReader = GeneralUtility::makeInstance(AnnotationReader::class);
299
300
        $classNameRef = new \ReflectionClass($class);
301
302
        return null !== $annotationReader->getClassAnnotation($classNameRef, NoHeader::class);
303
    }
304
305
    /**
306
     * Check if the templates are exist and create a dummy, if there
307
     * is no valid template.
308
     */
309
    protected function checkAndCreateDummyTemplates(array $loaderInformation, Loader $loader): void
310
    {
311
        if (empty($loaderInformation)) {
312
            return;
313
        }
314
315
        $siteRelPathPrivate = 'EXT:' . $loader->getExtensionKey() . '/Resources/Private/';
316
        $frontendLayout = GeneralUtility::getFileAbsFileName($siteRelPathPrivate . 'Layouts/Content.html');
317
        if (!is_file($frontendLayout)) {
318
            $this->writeContentTemplateToTarget('FrontendLayout', $frontendLayout);
319
        }
320
        $backendLayout = GeneralUtility::getFileAbsFileName($siteRelPathPrivate . 'Layouts/ContentBackend.html');
321
        if (!is_file($backendLayout)) {
322
            $this->writeContentTemplateToTarget('BackendLayout', $backendLayout);
323
        }
324
325
        foreach ($loaderInformation as $configuration) {
326
            $templatePath = $siteRelPathPrivate . 'Templates/Content/' . $configuration['model'] . '.html';
327
            $absoluteTemplatePath = GeneralUtility::getFileAbsFileName($templatePath);
328
            if (!is_file($absoluteTemplatePath)) {
329
                $beTemplatePath = $siteRelPathPrivate . 'Templates/Content/' . $configuration['model'] . 'Backend.html';
330
                $absoluteBeTemplatePath = GeneralUtility::getFileAbsFileName($beTemplatePath);
331
332
                $this->writeContentTemplateToTarget('Frontend', $absoluteTemplatePath);
333
                $this->writeContentTemplateToTarget('Backend', $absoluteBeTemplatePath);
334
            }
335
        }
336
    }
337
338
    /**
339
     * Write the given content object template to the target path.
340
     *
341
     * @param string $name
342
     * @param string $absoluteTargetPath
343
     */
344
    protected function writeContentTemplateToTarget($name, $absoluteTargetPath): void
345
    {
346
        $template = GeneralUtility::getUrl(ExtensionManagementUtility::extPath(
347
            'autoloader',
348
            'Resources/Private/Templates/ContentObjects/' . $name . '.html'
349
        ));
350
        FileUtility::writeFileAndCreateFolder($absoluteTargetPath, $template);
351
    }
352
353
    /**
354
     * Same as getClassProperties, but the fields are in LowerCaseUnderscored.
355
     *
356
     * @param $className
357
     *
358
     * @return array
359
     */
360
    protected function getClassPropertiesInLowerCaseUnderscored($className)
361
    {
362
        $nameMapperService = GeneralUtility::makeInstance(NameMapperService::class);
363
        $tableName = ModelUtility::getTableName($className);
364
365
        return array_map(function ($value) use ($nameMapperService, $tableName) {
366
            return $nameMapperService->getDatabaseFieldName($tableName, $value);
367
        }, ReflectionUtility::getDeclaringProperties($className));
368
    }
369
370
    /**
371
     * Wrap the given field configuration in the CE default TCA fields.
372
     *
373
     * @param string $configuration
374
     * @param bool   $noHeader
375
     *
376
     * @return string
377
     */
378
    protected function wrapDefaultTcaConfiguration($configuration, $noHeader = false)
379
    {
380
        $languagePrefix = 'LLL:EXT:frontend/Resources/Private/Language/locallang_ttc.xlf';
381
        $languagePrefixCore = 'LLL:EXT:core/Resources/Private/Language/Form/locallang_tabs.xlf';
382
        $configuration = trim($configuration) ? trim($configuration) . ',' : '';
383
384
        return '--palette--;' . $languagePrefix . ':palette.general;general,
385
    ' . ($noHeader ? '' : '--palette--;' . $languagePrefix . ':palette.header;header,') . '
386
    --div--;LLL:EXT:autoloader/Resources/Private/Language/locallang.xlf:contentData,
387
    ' . $configuration . '
388
    --div--;' . $languagePrefix . ':tabs.appearance,
389
    --palette--;;frames,
390
    --palette--;;appearanceLinks,
391
    --div--;' . $languagePrefixCore . ':language,
392
    --palette--;;language,
393
    --div--;' . $languagePrefixCore . ':access,
394
    --palette--;;hidden,
395
    --palette--;' . $languagePrefix . ':palette.access;access,
396
    --div--;' . $languagePrefixCore . ':extended';
397
    }
398
399
    /**
400
     * Get the fields that are in the default configuration.
401
     *
402
     * @param bool        $noHeader
403
     * @param string|null $configuration
404
     *
405
     * @return array
406
     */
407
    protected function getDefaultTcaFields($noHeader, $configuration = null)
408
    {
409
        if (null === $configuration) {
410
            $configuration = $this->wrapDefaultTcaConfiguration('', $noHeader);
411
        }
412
        $defaultFields = [];
413
        // Note: TCA could be missing in install tool checks, so cast the TCA to array
414
        $existingFields = array_keys((array)$GLOBALS['TCA']['tt_content']['columns']);
415
        $parts = GeneralUtility::trimExplode(',', $configuration, true);
416
        foreach ($parts as $fieldConfiguration) {
417
            $fieldConfiguration = GeneralUtility::trimExplode(';', $fieldConfiguration, true);
418
            if (\in_array($fieldConfiguration[0], $existingFields, true)) {
419
                $defaultFields[] = $fieldConfiguration[0];
420
            } elseif ('--palette--' === $fieldConfiguration[0]) {
421
                $paletteConfiguration = $GLOBALS['TCA']['tt_content']['palettes'][$fieldConfiguration[2]]['showitem'];
422
                if (\is_string($paletteConfiguration)) {
423
                    $defaultFields = array_merge(
424
                        $defaultFields,
425
                        $this->getDefaultTcaFields($noHeader, $paletteConfiguration)
426
                    );
427
                }
428
            }
429
        }
430
431
        return $defaultFields;
432
    }
433
}
434