diff --git a/composer.json b/composer.json
index 1cf3959f53175d2ab1d608dd62df5e07be0e532d..20f6b6f6a5f292d3840148efcdb63b2693776297 100644
--- a/composer.json
+++ b/composer.json
@@ -92,6 +92,7 @@
 		"codeception/codeception": "^4.1.22",
 		"codeception/lib-asserts": "^1.13.2",
 		"codeception/module-asserts": "^1.3.1",
+		"codeception/module-cli": "^1.1",
 		"codeception/module-filesystem": "^1.0.3",
 		"codeception/module-webdriver": "^1.2.1",
 		"composer/package-versions-deprecated": "^1.11.99.2",
diff --git a/composer.lock b/composer.lock
index e3dfdf304e40844c3af22a546fc71c136817398a..b7e5ab19f553e2fcecbfbaad0dc7521e8325fd73 100644
--- a/composer.lock
+++ b/composer.lock
@@ -4,7 +4,7 @@
         "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
         "This file is @generated automatically"
     ],
-    "content-hash": "5f735b56f523e7891b4c7a42c483dcdb",
+    "content-hash": "18ef972ae8ceffb3b4f8e1a1aef18269",
     "packages": [
         {
             "name": "bacon/bacon-qr-code",
@@ -5445,6 +5445,53 @@
             },
             "time": "2020-10-21T16:48:15+00:00"
         },
+        {
+            "name": "codeception/module-cli",
+            "version": "1.1.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/Codeception/module-cli.git",
+                "reference": "1f841ad4a1d43e5d9e60a43c4cc9e5af8008024f"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/Codeception/module-cli/zipball/1f841ad4a1d43e5d9e60a43c4cc9e5af8008024f",
+                "reference": "1f841ad4a1d43e5d9e60a43c4cc9e5af8008024f",
+                "shasum": ""
+            },
+            "require": {
+                "codeception/codeception": "*@dev",
+                "php": ">=5.6.0 <9.0"
+            },
+            "conflict": {
+                "codeception/codeception": "<4.0"
+            },
+            "type": "library",
+            "autoload": {
+                "classmap": [
+                    "src/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Michael Bodnarchuk"
+                }
+            ],
+            "description": "Codeception module for testing basic shell commands and shell output",
+            "homepage": "http://codeception.com/",
+            "keywords": [
+                "codeception"
+            ],
+            "support": {
+                "issues": "https://github.com/Codeception/module-cli/issues",
+                "source": "https://github.com/Codeception/module-cli/tree/1.1.1"
+            },
+            "time": "2020-12-26T16:56:19+00:00"
+        },
         {
             "name": "codeception/module-filesystem",
             "version": "1.0.3",
diff --git a/typo3/sysext/core/Tests/Acceptance/Application.suite.yml b/typo3/sysext/core/Tests/Acceptance/Application.suite.yml
index fada464757823ed0f3cdee38f9cd0d48e2b59ffd..a9a73cf344fad3706a8a55ba93d70a207a6a26c8 100644
--- a/typo3/sysext/core/Tests/Acceptance/Application.suite.yml
+++ b/typo3/sysext/core/Tests/Acceptance/Application.suite.yml
@@ -13,6 +13,7 @@ modules:
             editor: ff83dfd81e20b34c27d3e97771a4525a
             admin: 886526ce72b86870739cc41991144ec1
     - Asserts
+    - Codeception\Module\Cli
 
 extensions:
     enabled:
diff --git a/typo3/sysext/core/Tests/Acceptance/Application/Cli/CommandCest.php b/typo3/sysext/core/Tests/Acceptance/Application/Cli/CommandCest.php
new file mode 100644
index 0000000000000000000000000000000000000000..7ddaac9926b13623a1df736963716a511a2e5476
--- /dev/null
+++ b/typo3/sysext/core/Tests/Acceptance/Application/Cli/CommandCest.php
@@ -0,0 +1,74 @@
+<?php
+
+declare(strict_types=1);
+
+/*
+ * This file is part of the TYPO3 CMS project.
+ *
+ * It is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, either version 2
+ * of the License, or any later version.
+ *
+ * For the full copyright and license information, please read the
+ * LICENSE.txt file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+
+namespace TYPO3\CMS\Core\Tests\Acceptance\Application\Cli;
+
+use TYPO3\CMS\Core\Tests\Acceptance\Support\ApplicationTester;
+
+/**
+ * Tests the styleguide backend module can be loaded
+ */
+class CommandCest
+{
+    protected string $typo3Cli = '../../../../bin/typo3 ';
+
+    /**
+     * @param ApplicationTester $I
+     */
+    public function runCommand(ApplicationTester $I): void
+    {
+        foreach ($this->commandTestDataProvider() as $command => $expectedCode) {
+            $I->runShellCommand($this->typo3Cli . $command, false);
+            $I->seeResultCodeIs($expectedCode);
+        }
+    }
+
+    /**
+     * Test cli commands for their exit status
+     *
+     * 'site:show' and 'mailer:spool:send' fail
+     * due to missing configuration or unpredictable
+     * params.
+     */
+    protected function commandTestDataProvider(): array
+    {
+        return [
+            'cleanup:flexforms' => 0,
+            'cleanup:deletedrecords' => 0,
+            'cleanup:multiplereferencedfiles --dry-run --update-refindex' => 0,
+            'cleanup:lostfiles --dry-run --update-refindex' => 0,
+            'cleanup:missingfiles --dry-run --update-refindex' => 0,
+            'cleanup:missingrelations --dry-run --update-refindex' => 0,
+            'cleanup:orphanrecords' => 0,
+            'cleanup:previewlinks' => 0,
+            'cleanup:versions' => 0,
+            'extension:list' => 0,
+            'extension:deactivate workspaces' => 0,
+            'extension:activate workspaces' => 0,
+            'language:update' => 0,
+            'mailer:spool:send' => 1,
+            'redirects:checkintegrity' => 0,
+            'redirects:cleanup' => 0,
+            'referenceindex:update --check' => 0,
+            'scheduler:run' => 0,
+            'site:list' => 0,
+            'site:show' => 1,
+            'syslog:list' => 0,
+            'upgrade:list' => 0,
+        ];
+    }
+}
diff --git a/typo3/sysext/core/Tests/Acceptance/Application/DbCheck/DbCheckModuleCest.php b/typo3/sysext/core/Tests/Acceptance/Application/DbCheck/DbCheckModuleCest.php
index c70e1975ebb9c71bb0ba15d22f607682bf149070..3c7aa211603bfcf8fac7a154feb80e7e7eb3ad86 100644
--- a/typo3/sysext/core/Tests/Acceptance/Application/DbCheck/DbCheckModuleCest.php
+++ b/typo3/sysext/core/Tests/Acceptance/Application/DbCheck/DbCheckModuleCest.php
@@ -105,10 +105,10 @@ class DbCheckModuleCest
         $this->goToPageAndSeeHeadline($I, 'Manage Reference Index', 'Manage Reference Index');
 
         $I->click('Check reference index');
-        $I->waitForElement('.alert-danger');
+        $I->waitForElement('.alert');
 
         $I->click('Update reference index');
-        $I->waitForElement('.alert-danger');
+        $I->waitForElement('.alert');
 
         $I->click('Check reference index');
         $I->waitForElement('.alert-success');
diff --git a/typo3/sysext/install/Classes/Command/LanguagePackCommand.php b/typo3/sysext/install/Classes/Command/LanguagePackCommand.php
index 70ea56423cdc9baacaa9438343b169906126c7a2..2ee1d110af23b5adf2346e6f2a2c13fdf7873218 100644
--- a/typo3/sysext/install/Classes/Command/LanguagePackCommand.php
+++ b/typo3/sysext/install/Classes/Command/LanguagePackCommand.php
@@ -126,7 +126,7 @@ class LanguagePackCommand extends Command
         }
         $languagePackService->setLastUpdatedIsoCode($isos);
         $progressBar->finish();
-
+        $output->writeln('');
         // Flush language cache
         GeneralUtility::makeInstance(CacheManager::class)->getCache('l10n')->flush();
 
diff --git a/typo3/sysext/lowlevel/Classes/Command/ListSysLogCommand.php b/typo3/sysext/lowlevel/Classes/Command/ListSysLogCommand.php
index b5a9e8c5c8146c85d5095394e33d2bdcefb85854..e8d62e242c966f042c0dd9459cfb00066e54794c 100644
--- a/typo3/sysext/lowlevel/Classes/Command/ListSysLogCommand.php
+++ b/typo3/sysext/lowlevel/Classes/Command/ListSysLogCommand.php
@@ -89,7 +89,7 @@ class ListSysLogCommand extends Command
                 $row['uid'],
                 BackendUtility::datetime($row['tstamp']),
                 $userInformation,
-                sprintf($row['details'], $logData[0], $logData[1], $logData[2], $logData[3], $logData[4], $logData[5])
+                sprintf($row['details'], ($logData[0] ?? ''), ($logData[1] ?? ''), ($logData[2] ?? ''), ($logData[3] ?? ''), ($logData[4] ?? ''), ($logData[5] ?? ''))
             ];
 
             if ($showDetails) {
diff --git a/typo3/sysext/lowlevel/Classes/Command/MissingRelationsCommand.php b/typo3/sysext/lowlevel/Classes/Command/MissingRelationsCommand.php
index db1ec7aed5984eb9031f7abae7052062ae497362..a91680560cbf87c5a9847961ef679a0b14667d5c 100644
--- a/typo3/sysext/lowlevel/Classes/Command/MissingRelationsCommand.php
+++ b/typo3/sysext/lowlevel/Classes/Command/MissingRelationsCommand.php
@@ -278,7 +278,7 @@ If you want to get more detailed information, use the --verbose option.')
             // Compile info string for location of reference:
             $infoString = $this->formatReferenceIndexEntryToString($rec);
             // Handle missing file:
-            if ($existingRecords[$idx]['uid']) {
+            if ($existingRecords[$idx]['uid'] ?? false) {
                 // Record exists, but is a reference to an offline version
                 if ((int)($existingRecords[$idx]['t3ver_oid'] ?? 0) > 0) {
                     if ($isSoftReference) {
diff --git a/typo3/sysext/lowlevel/Classes/Command/OrphanRecordsCommand.php b/typo3/sysext/lowlevel/Classes/Command/OrphanRecordsCommand.php
index 32e964ab197178f130f759c0c271a39e8ce4cc8f..1aed3ab91c796e1d4416949ee6be4751e35ab7a2 100644
--- a/typo3/sysext/lowlevel/Classes/Command/OrphanRecordsCommand.php
+++ b/typo3/sysext/lowlevel/Classes/Command/OrphanRecordsCommand.php
@@ -99,7 +99,7 @@ Manual repair suggestions:
         $orphans = [];
         foreach (array_keys($GLOBALS['TCA']) as $tableName) {
             $idList = [0];
-            if (is_array($allRecords[$tableName]) && !empty($allRecords[$tableName])) {
+            if (is_array($allRecords[$tableName] ?? false) && !empty($allRecords[$tableName])) {
                 $idList = $allRecords[$tableName];
             }
             // Select all records that are NOT connected
diff --git a/typo3/sysext/redirects/Classes/Service/IntegrityService.php b/typo3/sysext/redirects/Classes/Service/IntegrityService.php
index af7fdb301582cb4231268504407d368d47a57778..2627430cb154d964b7886ee83398b9ee2b338fe1 100644
--- a/typo3/sysext/redirects/Classes/Service/IntegrityService.php
+++ b/typo3/sysext/redirects/Classes/Service/IntegrityService.php
@@ -126,7 +126,7 @@ class IntegrityService
         $row = $queryBuilder->execute()->fetchAssociative();
 
         $subPages = $this->getSlugsOfSubPages($site->getRootPageId());
-        $pages = array_merge([$site->getBase()->getPath() . '/', $row['slug']], $subPages);
+        $pages = array_merge([$site->getBase()->getPath() . '/', ($row['slug'] ?? '')], $subPages);
         return array_unique($pages);
     }
 
diff --git a/typo3/sysext/workspaces/Classes/Command/WorkspaceVersionRecordsCommand.php b/typo3/sysext/workspaces/Classes/Command/WorkspaceVersionRecordsCommand.php
index 7a47e7d65bf8d84670b4652b15e56a83701de666..36ee89f7e5c98b8fd4416beb7d4f4194628e4e9b 100644
--- a/typo3/sysext/workspaces/Classes/Command/WorkspaceVersionRecordsCommand.php
+++ b/typo3/sysext/workspaces/Classes/Command/WorkspaceVersionRecordsCommand.php
@@ -298,7 +298,7 @@ class WorkspaceVersionRecordsCommand extends Command
                         ->execute();
                     while ($rowSub = $result->fetchAssociative()) {
                         // Add any versions of those records
-                        $versions = BackendUtility::selectVersionsOfRecord($tableName, $rowSub['uid'], 'uid,t3ver_wsid' . ($GLOBALS['TCA'][$tableName]['ctrl']['delete'] ? ',' . $GLOBALS['TCA'][$tableName]['ctrl']['delete'] : ''), null, true);
+                        $versions = BackendUtility::selectVersionsOfRecord($tableName, $rowSub['uid'], 'uid,t3ver_wsid' . (($GLOBALS['TCA'][$tableName]['ctrl']['delete'] ?? false) ? ',' . $GLOBALS['TCA'][$tableName]['ctrl']['delete'] : ''), null, true);
                         if (is_array($versions)) {
                             foreach ($versions as $verRec) {
                                 if (!$verRec['_CURRENT_VERSION']) {