CVE-2024-45233
Description
The TYPO3 powermail extension before 12.4.0 allows unauthenticated attackers to manipulate form data due to missing access checks in the OutputController.
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
The TYPO3 powermail extension before 12.4.0 allows unauthenticated attackers to manipulate form data due to missing access checks in the OutputController.
Root
Cause The powermail extension for TYPO3, up to version 12.3.5, contains broken access control in the OutputController. Several actions such as showAction and editAction lacked sufficient access checks, allowing direct invocation without verifying permissions [1]. Code commits show that these actions were missing checks that were later added, such as FrontendUtility::isAllowedToView and FrontendUtility::isAllowedToEdit [2][3].
Exploitation
An unauthenticated attacker can exploit this vulnerability by directly calling vulnerable actions in the OutputController, provided the Powermail Frontend plugins are active. No authentication is required, and the attack can be performed remotely [1].
Impact
Successful exploitation allows the attacker to edit, update, delete, or export data from persisted forms. This can lead to unauthorized modification or disclosure of form submissions, potentially compromising user data or integrity of the form data [1].
Mitigation
The issue is fixed in powermail versions 7.5.0, 8.5.0, 10.9.0, and 12.4.0. Users should update to these or later versions. As a workaround, if updating is not immediately possible, disabling the Powermail Frontend plugins may prevent exploitation [1].
AI Insight generated on May 20, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
in2code/powermailPackagist | < 7.5.0 | 7.5.0 |
in2code/powermailPackagist | >= 8.0.0, < 8.5.0 | 8.5.0 |
in2code/powermailPackagist | >= 9.0.0, < 10.9.0 | 10.9.0 |
in2code/powermailPackagist | >= 11.0.0, < 12.4.0 | 12.4.0 |
Affected products
2- TYPO3/powermaildescription
Patches
404a010c40092[SECURITY] Prevent information disclosure in OutputController
13 files changed · +132 −316
Classes/Controller/OutputController.php+35 −34 modified@@ -32,7 +32,7 @@ class OutputController extends AbstractController { /** - * @return void + * @return ResponseInterface * @throws InvalidQueryException * @noinspection PhpUnused */ @@ -60,11 +60,15 @@ public function listAction(): ResponseInterface /** * @param Mail $mail - * @return void + * @return ResponseInterface * @noinspection PhpUnused */ public function showAction(Mail $mail): ResponseInterface { + if (!FrontendUtility::isAllowedToView($this->settings, $mail)) { + return new ForwardResponse('list'); + } + $fieldArray = $this->getFieldList($this->settings['single']['fields']); $this->view->assignMultiple( [ @@ -78,11 +82,20 @@ public function showAction(Mail $mail): ResponseInterface /** * @param Mail|null $mail - * @return void + * @return ResponseInterface * @noinspection PhpUnused */ public function editAction(Mail $mail = null): ResponseInterface { + if (!FrontendUtility::isAllowedToEdit($this->settings, $mail)) { + $this->addFlashmessage( + LocalizationUtility::translate('PowermailFrontendEditFailed'), + '', + \TYPO3\CMS\Core\Type\ContextualFeedbackSeverity::ERROR + ); + return new ForwardResponse('list'); + } + $fieldArray = $this->getFieldList($this->settings['edit']['fields']); $this->view->assignMultiple( [ @@ -107,23 +120,13 @@ public function editAction(Mail $mail = null): ResponseInterface */ public function initializeUpdateAction() { - $arguments = $this->request->getArguments(); - if (!FrontendUtility::isAllowedToEdit($this->settings, $arguments['field']['__identity'])) { - $this->controllerContext = $this->buildControllerContext(); - $this->addFlashmessage( - LocalizationUtility::translate('PowermailFrontendEditFailed'), - '', - \TYPO3\CMS\Core\Type\ContextualFeedbackSeverity::ERROR - ); - return new ForwardResponse('list'); - } $this->reformatParamsForAction(); } /** * @param Mail $mail * @ExtbaseAnnotation\Validate("In2code\Powermail\Domain\Validator\InputValidator", param="mail") - * @return void + * @return ResponseInterface * @throws StopActionException * @throws IllegalObjectTypeException * @throws UnknownObjectException @@ -132,40 +135,38 @@ public function initializeUpdateAction() */ public function updateAction(Mail $mail): ResponseInterface { + if (!FrontendUtility::isAllowedToEdit($this->settings, $mail)) { + $this->addFlashmessage( + LocalizationUtility::translate('PowermailFrontendEditFailed'), + '', + \TYPO3\CMS\Core\Type\ContextualFeedbackSeverity::ERROR + ); + return new ForwardResponse('list'); + } + $this->uploadService->uploadAllFiles(); $this->mailRepository->update($mail); $this->addFlashmessage(LocalizationUtility::translate('PowermailFrontendEditSuccessful')); return $this->redirect('edit', null, null, ['mail' => $mail]); } /** - * @return void - * @throws DBALException - * @throws Exception + * @param Mail $mail + * @return ResponseInterface + * @throws IllegalObjectTypeException * @noinspection PhpUnused */ - public function initializeDeleteAction() + public function deleteAction(Mail $mail): ResponseInterface { - $arguments = $this->request->getArguments(); - if (!FrontendUtility::isAllowedToEdit($this->settings, $arguments['mail'])) { - $this->controllerContext = $this->buildControllerContext(); + if (!FrontendUtility::isAllowedToEdit($this->settings, $mail)) { $this->addFlashmessage( LocalizationUtility::translate('PowermailFrontendDeleteFailed'), '', \TYPO3\CMS\Core\Type\ContextualFeedbackSeverity::ERROR ); return new ForwardResponse('list'); } - } - /** - * @param Mail $mail - * @return void - * @throws IllegalObjectTypeException - * @noinspection PhpUnused - */ - public function deleteAction(Mail $mail): ResponseInterface - { $this->assignMultipleActions(); $this->mailRepository->remove($mail); $this->addFlashmessage(LocalizationUtility::translate('PowermailFrontendDeleteSuccessful')); @@ -174,7 +175,7 @@ public function deleteAction(Mail $mail): ResponseInterface /** * @param array $export Field Array with mails and format - * @return void + * @return ResponseInterface * @throws InvalidQueryException * @noinspection PhpUnused */ @@ -202,7 +203,7 @@ public function exportAction(array $export = []): ResponseInterface /** * @param QueryResult|null $mails mails objects * @param array $fields uid field list - * @return void + * @return ResponseInterface * @noinspection PhpUnused */ public function exportXlsAction(QueryResult $mails = null, array $fields = []): ResponseInterface @@ -215,7 +216,7 @@ public function exportXlsAction(QueryResult $mails = null, array $fields = []): /** * @param QueryResult|null $mails mails objects * @param array $fields uid field list - * @return void + * @return ResponseInterface * @noinspection PhpUnused */ public function exportCsvAction(QueryResult $mails = null, array $fields = []): ResponseInterface @@ -226,7 +227,7 @@ public function exportCsvAction(QueryResult $mails = null, array $fields = []): } /** - * @return void + * @return ResponseInterface * @throws InvalidQueryException * @noinspection PhpUnused */
Classes/Utility/FrontendUtility.php+15 −0 modified@@ -148,6 +148,21 @@ public static function isAllowedToEdit(array $settings, $mail): bool return false; } + public static function isAllowedToView(array $settings, Mail $mail): bool + { + $feUser = ObjectUtility::getTyposcriptFrontendController()->fe_user->user['uid'] ?? 0; + if ( + $feUser === 0 || + ( + (int)$settings['list']['showownonly'] === 1 + && $mail->getFeuser()->getUid() !== $feUser + ) + ) { + return false; + } + return true; + } + /** * Is a frontend user logged in *
Documentation/Changelog/Readme.md+4 −3 modified@@ -7,9 +7,10 @@ breaking changes and how to handle them | Version | Release Date | Description | |-----------------|--------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| 12.3.5 | 2024-06-07 | Bugfix release: Please see commit history for changes. | -| 12.3.3 / 12.3.4 | 2024-06-03 | Bugfix release: Please see commit history for changes. | -| 12.3.2 | 2024-05-08 | Bugfix release: Please see commit history for changes. | +| [!!!] 12.4.0 | 2024-08-21 | Security release: Harden access checks to mail records, remove export and rss views completely without any replacement. | +| 12.3.5 | 2024-06-07 | Bugfix release: Please see commit history for changes. | +| 12.3.3 / 12.3.4 | 2024-06-03 | Bugfix release: Please see commit history for changes. | +| 12.3.2 | 2024-05-08 | Bugfix release: Please see commit history for changes. | | 12.3.1 | 2024-04-10 | Bugfix release: improved sql_mode compatibility | | 12.3.0 | 2024-03-20 | php 8.3 compatibility, Bugfix. | | 12.2.1 | 2024-03-06 | Bugfix release Please see commit history for changes. |
Documentation/Changelog/UpgradeInstructions.md+9 −0 modified@@ -1,5 +1,14 @@ # Upgrade Instructions and breaking changes +## Version 12.4.0 + +### Breaking Change + +We removed the export and rss functionality completely without any replacement, because there is no +reliable security concept behind it and is not easy to fix. + +If you need this, please contact [in2code](https://www.in2code.de/en/contact/) for paid assistance or implement it yourself. + ## Version 12.0.0 ### Upgrade - Wizards
ext_localconf.php+4 −4 modified@@ -34,10 +34,10 @@ 'Powermail', 'Pi2', [ - \In2code\Powermail\Controller\OutputController::class => 'list, show, export, rss' + \In2code\Powermail\Controller\OutputController::class => 'list, show' ], [ - \In2code\Powermail\Controller\OutputController::class => 'list, export, rss' + \In2code\Powermail\Controller\OutputController::class => 'list' ], \TYPO3\CMS\Extbase\Utility\ExtensionUtility::PLUGIN_TYPE_CONTENT_ELEMENT ); @@ -58,10 +58,10 @@ 'Powermail', 'Pi4', [ - \In2code\Powermail\Controller\OutputController::class => 'list, show, edit, update, export, rss, delete' + \In2code\Powermail\Controller\OutputController::class => 'list, show, edit, update, delete' ], [ - \In2code\Powermail\Controller\OutputController::class => 'list, edit, update, export, rss, delete' + \In2code\Powermail\Controller\OutputController::class => 'list, edit, update, delete' ], \TYPO3\CMS\Extbase\Utility\ExtensionUtility::PLUGIN_TYPE_CONTENT_ELEMENT );
Resources/Private/Partials/Output/Export.html+0 −63 removed@@ -1,63 +0,0 @@ -{namespace vh=In2code\Powermail\ViewHelpers} - -<f:if condition="{settings.list.export}"> - <div class="form-group btn-group pull-right" role="group"> - <f:if condition="{vh:condition.isStringInString(haystack:settings.list.export, needle:'xls')}"> - <f:form - action="export" - name="export" - class="visible-xs-inline-block visible-sm-inline-block visible-md-inline-block visible-lg-inline-block" - additionalParams="{type:31311}"> - - <f:form.submit - value="XLS" - class="btn btn-primary" /> - - <f:form.hidden - name="export[fields]" - value="{vh:string.implodeField(objects:mails)}" - id="export_mails" /> - <f:form.hidden - name="export[format]" - value="xls" - id="export_format" /> - </f:form> - </f:if> - - <f:if condition="{vh:condition.isStringInString(haystack:settings.list.export, needle:'csv')}"> - <f:form - action="export" - name="export" - class="visible-xs-inline-block visible-sm-inline-block visible-md-inline-block visible-lg-inline-block" - additionalParams="{type:31312}"> - - <f:form.submit - value="CSV" - class="btn btn-primary" /> - - <f:form.hidden - name="export[fields]" - value="{vh:string.implodeField(objects:mails)}" - id="export_mails" /> - <f:form.hidden - name="export[format]" - value="csv" - id="export_format" /> - </f:form> - </f:if> - - - <f:if condition="{vh:condition.isStringInString(haystack:settings.list.export, needle:'rss')}"> - <f:form - action="rss" - name="export" - class="visible-xs-inline-block visible-sm-inline-block visible-md-inline-block visible-lg-inline-block" - additionalParams="{type:31319}"> - - <f:form.submit - value="RSS" - class="btn btn-primary" /> - </f:form> - </f:if> - </div> -</f:if>
Resources/Private/Templates/Output/Edit.html+47 −55 modified@@ -14,69 +14,61 @@ <div class="powermail_frontend edit"> <f:if condition="{mail}"> <f:then> - <f:if condition="{vh:condition.isAllowedToEdit(settings:settings, mail:mail)}"> + <f:form + action="update" + name="field" + object="{mail}" + enctype="multipart/form-data" + additionalAttributes="{vh:validation.enableJavascriptValidationAndAjax(form:mail.form)}" + class="powermail_form powermail_form_{mail.form.uid} {form.css} {settings.styles.framework.formClasses}"> - <f:then> - <f:form - action="update" - name="field" - object="{mail}" - enctype="multipart/form-data" - additionalAttributes="{vh:validation.enableJavascriptValidationAndAjax(form:mail.form)}" - class="powermail_form powermail_form_{mail.form.uid} {form.css} {settings.styles.framework.formClasses}"> + <fieldset class="powermail_fieldset"> - <fieldset class="powermail_fieldset"> + <h3>{mail.form.title}</h3> + <f:render partial="Misc/FormError" arguments="{form:mail.form}" /> - <h3>{mail.form.title}</h3> - <f:render partial="Misc/FormError" arguments="{form:mail.form}" /> + <f:for each="{mail.form.pages}" as="page" iteration="pageIteration"> + <legend class="powermail_legend"> + <f:if condition="{pageIteration.isFirst}">{page.title}</f:if> + </legend> - <f:for each="{mail.form.pages}" as="page" iteration="pageIteration"> - <legend class="powermail_legend"> - <f:if condition="{pageIteration.isFirst}">{page.title}</f:if> - </legend> + <f:for each="{page.fields}" as="field"> + <f:for each="{selectedFields}" as="selectedField"> + <f:if condition="{selectedField} == {field}"> + <f:if condition="{field.advancedFieldType}"> + <f:then> + <f:render partial="Form/Field/{vh:string.upper(string:field.type)}" arguments="{_all}" /> + </f:then> + <f:else> + <f:render partial="Output/EditHidden" arguments="{_all}" /> + </f:else> + </f:if> + </f:if> + </f:for> + </f:for> + </f:for> - <f:for each="{page.fields}" as="field"> - <f:for each="{selectedFields}" as="selectedField"> - <f:if condition="{selectedField} == {field}"> - <f:if condition="{field.advancedFieldType}"> - <f:then> - <f:render partial="Form/Field/{vh:string.upper(string:field.type)}" arguments="{_all}" /> - </f:then> - <f:else> - <f:render partial="Output/EditHidden" arguments="{_all}" /> - </f:else> - </f:if> - </f:if> - </f:for> - </f:for> - </f:for> + <f:form.hidden + name="mail[form]" + value="{mail.form.uid}" + class="powermail_form_uid" /> - <f:form.hidden - name="mail[form]" - value="{mail.form.uid}" - class="powermail_form_uid" /> + <div class="{settings.styles.framework.fieldAndLabelWrappingClasses}"> + <div class="{settings.styles.framework.fieldWrappingClasses} {settings.styles.framework.offsetClasses}"> + <f:form.submit + value="{f:translate(key:'PowermailFrontendEditSubmit')}" + class="powermail_field powermail_submit btn btn-primary" /> + </div> + </div> - <div class="{settings.styles.framework.fieldAndLabelWrappingClasses}"> - <div class="{settings.styles.framework.fieldWrappingClasses} {settings.styles.framework.offsetClasses}"> - <f:form.submit - value="{f:translate(key:'PowermailFrontendEditSubmit')}" - class="powermail_field powermail_submit btn btn-primary" /> - </div> - </div> + </fieldset> + </f:form> + </f:then> - </fieldset> - </f:form> - </f:then> - - <f:else> - <p><f:translate key="PowermailFrontendEditFailed">No Mail given</f:translate></p> - </f:else> - </f:if> - </f:then> - <f:else> - <p><f:translate key="PowermailFrontendEditNoMail">Wrong Permissions</f:translate></p> - </f:else> - </f:if> + <f:else> + <p><f:translate key="PowermailFrontendEditFailed">No Mail given</f:translate></p> + </f:else> + </f:if> <f:link.page pageUid="{listPid}" class="powermail_frontend_back"> <f:translate key="Back" />
Resources/Private/Templates/Output/ExportCsv.html+0 −46 removed@@ -1,46 +0,0 @@ -{namespace vh=In2code\Powermail\ViewHelpers} -<f:layout name="Export" /> - -Render Powermail CSV Export -{mails} All Mails for exporting -{fields} Fields to export (drag'n drop settings in module) - -<f:section name="main"><vh:string.utf8ToUtf16><vh:string.trim> - - <f:for each="{mails}" as="mail" iteration="index"> - <f:if condition="{index.isFirst}"> - <f:for each="{fields}" as="field"> - "<vh:string.removeQuote>{field.title}</vh:string.removeQuote>"; - </f:for> - <br /> - </f:if> - - - <f:for each="{fields}" as="field"> - <f:for each="{mail.answers}" as="answer"> - <f:if condition="{field} == {answer.field}"> - - <f:if condition="{vh:condition.isArray(val: '{answer.value}')}"> - <f:else> - "<vh:string.removeQuote><vh:string.sanitizeCsvCell>{answer.value}</vh:string.sanitizeCsvCell></vh:string.removeQuote>"; - </f:else> - <f:then> - "<vh:string.removeQuote> - <vh:string.sanitizeCsvCell> - <f:for each="{answer.value}" as="singleValue"> - <f:if condition="{singleValue}"> - {singleValue}, - </f:if> - </f:for> - </vh:string.sanitizeCsvCell> - </vh:string.removeQuote>"; - </f:then> - </f:if> - - </f:if> - </f:for> - </f:for> - <br /> - </f:for> - -</vh:string.trim></vh:string.utf8ToUtf16></f:section>
Resources/Private/Templates/Output/ExportXls.html+0 −53 removed@@ -1,53 +0,0 @@ -{namespace vh=In2code\Powermail\ViewHelpers} -<f:layout name="Export" /> - -Render Powermail XLS Export -{mails} All Mails for exporting -{fields} Fields to export (drag'n drop settings in module) - -<f:section name="main"><html> - <head> - <meta http-equiv="content-type" content="text/html; charset=utf-8"> - </head> - <body> - <table> - <f:for each="{mails}" as="mail" iteration="index"> - <f:if condition="{index.isFirst}"> - <tr> - <f:for each="{fields}" as="field"> - <th> - {field.title} - </th> - </f:for> - </tr> - </f:if> - - - <tr> - <f:for each="{fields}" as="field"> - <td> - <f:for each="{mail.answers}" as="answer"> - <f:if condition="{field} == {answer.field}"> - <f:if condition="{vh:condition.isArray(val: '{answer.value}')}"> - <f:then> - <f:for each="{answer.value}" as="singleValue"> - <f:if condition="{singleValue}"> - <vh:string.sanitizeCsvCell>{singleValue}</vh:string.sanitizeCsvCell>, - </f:if> - </f:for> - </f:then> - <f:else> - <vh:string.sanitizeCsvCell>{answer.value}</vh:string.sanitizeCsvCell> - </f:else> - </f:if> - </f:if> - </f:for> - </td> - </f:for> - </tr> - </f:for> - </table> - </body> -</html> - -</f:section>
Resources/Private/Templates/Output/List.html+2 −4 modified@@ -15,8 +15,6 @@ <f:render partial="Output/Search" arguments="{_all}" /> - <f:render partial="Output/Export" arguments="{_all}" /> - <f:if condition="{mails}"> <f:then> <table class="table table-striped table-hover table-responsive"> @@ -93,13 +91,13 @@ </f:if> <f:if condition="{vh:condition.isAllowedToEdit(settings:settings, mail:mail)}"> - <f:link.action action="edit" pageUid="{editPid}" arguments="{mail:mail}"> + <f:link.action action="edit" pluginName="Pi4" pageUid="{editPid}" arguments="{mail:mail}"> <f:translate key="PowermailFrontendEditView">Edit</f:translate> </f:link.action> </f:if> <f:if condition="{vh:condition.isAllowedToEdit(settings:settings, mail:mail)}"> - <f:link.action action="delete" pageUid="{editPid}" arguments="{mail:mail}"> + <f:link.action action="delete" pluginName="Pi4" pageUid="{editPid}" arguments="{mail:mail}"> <f:translate key="PowermailFrontendDeleteView">Delete</f:translate> </f:link.action> </f:if>
Resources/Private/Templates/Output/Rss.html+0 −37 removed@@ -1,37 +0,0 @@ -{namespace vh=In2code\Powermail\ViewHelpers} -<f:layout name="Export" /> - -RSS Feed for Powermail_Frontend - -<f:section name="main"><?xml version="1.0" encoding="ISO-8859-1" ?> -<?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?> - <rss xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:sy="http://purl.org/rss/1.0/modules/syndication/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" version="2.0"> - - <channel> - <title>{settings.rss.title}</title> - <link>{settings.rss.link}</link> - <description>{settings.rss.description}</description> - <language>{settings.rss.language}</language> - <copyright>{settings.rss.copyright}</copyright> - <sy:updatePeriod>{settings.rss.updatePeriod}</sy:updatePeriod> - <sy:updateFrequency>{settings.rss.updateFrequency}</sy:updateFrequency> - - <f:for each="{mails}" as="mail"> - <item> - <title>{mail.senderName}</title> - <description> - <f:for each="{mail.answers}" as="answer"> - <f:if condition="{vh:condition.isArray(val: '{answer.value}')}"> - <f:else> - {answer.field.title}: {answer.value}; - </f:else> - </f:if> - </f:for> - </description> - <link><vh:string.encode><f:uri.action pageUid="{singlePid}" absolute="1" action="show" arguments="{mail: mail}" /></vh:string.encode></link> - <pubDate><f:format.date format="D, d M Y H:i:s +0200">{mail.crdate}</f:format.date></pubDate> - </item> - </f:for> - </channel> - </rss> -</f:section>
Tests/Behavior/Features/Mod1/Basic.feature+16 −16 modified@@ -1,17 +1,17 @@ # Features/Mod1/Basic.feature -@Mod1 @Mod1Basic -Feature: Basic - Basically checks if the backend module exists - - @javascript - Scenario: Login into backend - Given I am on "/typo3/" - Then the sourcecode should contain 'typo3-login-logo' - When I fill in "username" with "editor" - When I fill in "p_field" with "editor" - And I press "t3-login-submit" - - And I wait "3" seconds - Given I am on "/typo3/module/web/Powermail" - - Then the response should contain "typo3-backend-module-router" +#@Mod1 @Mod1Basic +#Feature: Basic +# Basically checks if the backend module exists +# +# @javascript +# Scenario: Login into backend +# Given I am on "/typo3/" +# Then the sourcecode should contain 'typo3-login-logo' +# When I fill in "username" with "editor" +# When I fill in "p_field" with "editor" +# And I press "t3-login-submit" +# +# And I wait "3" seconds +# Given I am on "/typo3/module/web/Powermail" +# +# Then the response should contain "typo3-backend-module-router"
Tests/Behavior/Features/Pi2/List.feature+0 −1 modified@@ -25,7 +25,6 @@ Feature: List Then I should see "Olli" Then the sourcecode should contain '<li class="disabled">' Then the sourcecode should contain '<a href="#">Z</a>' - Then the sourcecode should contain '<input class="btn btn-primary" type="submit" value="XLS">' @Pi2ListFilterEmpty Scenario: Check empty Filter over List View
6e94ec5e0c7b[!!!][SECURITY] Prevent possible information disclosure OutputController
13 files changed · +79 −325
Classes/Controller/OutputController.php+32 −101 modified@@ -65,12 +65,16 @@ public function listAction(): ResponseInterface /** * @param Mail $mail - * @return void + * @return ResponseInterface * @noinspection PhpUnused * @throws Exception */ public function showAction(Mail $mail): ResponseInterface { + if (!FrontendUtility::isAllowedToView($this->settings, $mail)) { + return (new ForwardResponse('list')); + } + $fieldArray = $this->getFieldList($this->settings['single']['fields']); $this->view->assignMultiple( [ @@ -84,12 +88,21 @@ public function showAction(Mail $mail): ResponseInterface /** * @param Mail $mail - * @return void + * @return ResponseInterface * @noinspection PhpUnused * @throws Exception */ public function editAction(Mail $mail = null): ResponseInterface { + if (!FrontendUtility::isAllowedToEdit($this->settings, $mail)) { + $this->addFlashmessage( + LocalizationUtility::translate('PowermailFrontendEditFailed'), + '', + AbstractMessage::ERROR + ); + return new ForwardResponse('list'); + } + $fieldArray = $this->getFieldList($this->settings['edit']['fields']); $this->view->assignMultiple( [ @@ -118,142 +131,60 @@ public function editAction(Mail $mail = null): ResponseInterface */ public function initializeUpdateAction() { - $arguments = $this->request->getArguments(); - if (!FrontendUtility::isAllowedToEdit($this->settings, $arguments['field']['__identity'])) { - $this->controllerContext = $this->buildControllerContext(); - $this->addFlashmessage( - LocalizationUtility::translate('PowermailFrontendEditFailed'), - '', - AbstractMessage::ERROR - ); - return new ForwardResponse('list'); - } $this->reformatParamsForAction(); } /** * @param Mail $mail * @ExtbaseAnnotation\Validate("In2code\Powermail\Domain\Validator\InputValidator", param="mail") - * @return void + * @return ResponseInterface * @throws StopActionException * @throws UnsupportedRequestTypeException * @throws IllegalObjectTypeException * @throws UnknownObjectException * @throws \Exception * @noinspection PhpUnused */ - public function updateAction(Mail $mail): void + public function updateAction(Mail $mail): ResponseInterface { + if (!FrontendUtility::isAllowedToEdit($this->settings, $mail)) { + $this->addFlashmessage( + LocalizationUtility::translate('PowermailFrontendEditFailed'), + '', + AbstractMessage::ERROR + ); + return new ForwardResponse('list'); + } + $this->uploadService->uploadAllFiles(); $this->mailRepository->update($mail); $this->addFlashmessage(LocalizationUtility::translate('PowermailFrontendEditSuccessful')); - $this->redirect('edit', null, null, ['mail' => $mail]); + return (new ForwardResponse('edit'))->withArguments(['mail' => $mail]); } /** - * @return void - * @throws DBALException - * @throws Exception - * @throws StopActionException + * @param Mail $mail + * @return ResponseInterface + * @throws IllegalObjectTypeException * @noinspection PhpUnused */ - public function initializeDeleteAction() + public function deleteAction(Mail $mail): ResponseInterface { - $arguments = $this->request->getArguments(); - if (!FrontendUtility::isAllowedToEdit($this->settings, $arguments['mail'])) { - $this->controllerContext = $this->buildControllerContext(); + if (!FrontendUtility::isAllowedToEdit($this->settings, $mail)) { $this->addFlashmessage( LocalizationUtility::translate('PowermailFrontendDeleteFailed'), '', AbstractMessage::ERROR ); return new ForwardResponse('list'); } - } - /** - * @param Mail $mail - * @return void - * @throws IllegalObjectTypeException - * @noinspection PhpUnused - */ - public function deleteAction(Mail $mail): ResponseInterface - { $this->assignMultipleActions(); $this->mailRepository->remove($mail); $this->addFlashmessage(LocalizationUtility::translate('PowermailFrontendDeleteSuccessful')); return $this->htmlResponse(); } - /** - * @param array $export Field Array with mails and format - * @return void - * @throws InvalidQueryException - * @throws StopActionException - * @throws Exception - * @noinspection PhpUnused - */ - public function exportAction(array $export = []): ResponseInterface - { - if (!$this->settings['list']['export']) { - return $this->htmlResponse(null); - } - $mails = $this->mailRepository->findByUidList($export['fields']); - - // get field array for output - if ($this->settings['list']['fields']) { - $fieldArray = GeneralUtility::trimExplode(',', $this->settings['list']['fields'], true); - } else { - $fieldArray = $this->formRepository->getFieldUidsFromForm((int)$this->settings['main']['form']); - } - $fields = $this->fieldRepository->findByUids($fieldArray); - - if ($export['format'] === 'xls') { - return (new ForwardResponse('exportXls'))->withArguments(['mails' => $mails, 'fields' => $fields]); - } - return (new ForwardResponse('exportCsv'))->withArguments(['mails' => $mails, 'fields' => $fields]); - return $this->htmlResponse(); - } - - /** - * @param QueryResult $mails mails objects - * @param array $fields uid field list - * @return void - * @noinspection PhpUnused - */ - public function exportXlsAction(QueryResult $mails = null, array $fields = []): ResponseInterface - { - $this->view->assign('mails', $mails); - $this->view->assign('fields', $fields); - return $this->htmlResponse(); - } - - /** - * @param QueryResult $mails mails objects - * @param array $fields uid field list - * @return void - * @noinspection PhpUnused - */ - public function exportCsvAction(QueryResult $mails = null, array $fields = []): ResponseInterface - { - $this->view->assign('mails', $mails); - $this->view->assign('fields', $fields); - return $this->htmlResponse(); - } - - /** - * @return void - * @throws InvalidQueryException - * @noinspection PhpUnused - */ - public function rssAction(): ResponseInterface - { - $mails = $this->mailRepository->findListBySettings($this->settings, $this->piVars); - $this->view->assign('mails', $mails); - $this->assignMultipleActions(); - return $this->htmlResponse(); - } - /** * @return void */
Classes/Utility/FrontendUtility.php+15 −0 modified@@ -154,6 +154,21 @@ public static function isAllowedToEdit(array $settings, $mail): bool return false; } + public static function isAllowedToView(array $settings, Mail $mail): bool + { + $feUser = ObjectUtility::getTyposcriptFrontendController()->fe_user->user['uid'] ?? 0; + if ( + $feUser === 0 || + ( + (int)$settings['list']['showownonly'] === 1 + && $mail->getFeuser()->getUid() !== $feUser + ) + ) { + return false; + } + return true; + } + /** * Is a frontend user logged in *
Documentation/Changelog/Readme.md+1 −1 modified@@ -7,7 +7,7 @@ breaking changes and how to handle them | Version | Release Date | Description | |--------------|--------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| 12.3.1 | 2024-05-08 | Bugfix release | +| [!!!] 10.9.0 | 2024-08-21 | Security: Harden access checks to sent mails, remove export views completely without replacement | | 10.8.2 | 2024-07-03 | Bugfix release: Prevent exception for double opt in confirmation clicks | | 10.8.1 | 2023-05-08 | Bugfix release | | 10.8.0 | 2023-03-26 | Compatibility Release for EXT:powermail_cleaner (TYPO3 V11) |
Documentation/Changelog/UpgradeInstructions.md+10 −0 modified@@ -1,5 +1,15 @@ # Upgrade Instructions and breaking changes +## Version 10.9.0 + +### Breaking Change + +We removed the export and rss functionality completely without any replacement, because there is no +reliable security concept behind it and is not easy to fix. + +If you need this, please contact [in2code](https://www.in2code.de/en/contact/) for paid assistance or implement it yourself. + + ## Version 10.7.4 If you want to contribute, the URLs for the development instance changed slightly. The TYPO3 version was added
ext_localconf.php+2 −2 modified@@ -32,10 +32,10 @@ 'Powermail', 'Pi2', [ - \In2code\Powermail\Controller\OutputController::class => 'list, show, edit, update, export, rss, delete' + \In2code\Powermail\Controller\OutputController::class => 'list, show, edit, update, delete' ], [ - \In2code\Powermail\Controller\OutputController::class => 'list, edit, update, export, rss, delete' + \In2code\Powermail\Controller\OutputController::class => 'list, edit, update, delete' ] );
Resources/Private/Partials/Output/Export.html+0 −63 removed@@ -1,63 +0,0 @@ -{namespace vh=In2code\Powermail\ViewHelpers} - -<f:if condition="{settings.list.export}"> - <div class="form-group btn-group pull-right" role="group"> - <f:if condition="{vh:condition.isStringInString(haystack:settings.list.export, needle:'xls')}"> - <f:form - action="export" - name="export" - class="visible-xs-inline-block visible-sm-inline-block visible-md-inline-block visible-lg-inline-block" - additionalParams="{type:31311}"> - - <f:form.submit - value="XLS" - class="btn btn-primary" /> - - <f:form.hidden - name="export[fields]" - value="{vh:string.implodeField(objects:mails)}" - id="export_mails" /> - <f:form.hidden - name="export[format]" - value="xls" - id="export_format" /> - </f:form> - </f:if> - - <f:if condition="{vh:condition.isStringInString(haystack:settings.list.export, needle:'csv')}"> - <f:form - action="export" - name="export" - class="visible-xs-inline-block visible-sm-inline-block visible-md-inline-block visible-lg-inline-block" - additionalParams="{type:31312}"> - - <f:form.submit - value="CSV" - class="btn btn-primary" /> - - <f:form.hidden - name="export[fields]" - value="{vh:string.implodeField(objects:mails)}" - id="export_mails" /> - <f:form.hidden - name="export[format]" - value="csv" - id="export_format" /> - </f:form> - </f:if> - - - <f:if condition="{vh:condition.isStringInString(haystack:settings.list.export, needle:'rss')}"> - <f:form - action="rss" - name="export" - class="visible-xs-inline-block visible-sm-inline-block visible-md-inline-block visible-lg-inline-block" - additionalParams="{type:31319}"> - - <f:form.submit - value="RSS" - class="btn btn-primary" /> - </f:form> - </f:if> - </div> -</f:if>
Resources/Private/Templates/Output/ExportCsv.html+0 −46 removed@@ -1,46 +0,0 @@ -{namespace vh=In2code\Powermail\ViewHelpers} -<f:layout name="Export" /> - -Render Powermail CSV Export -{mails} All Mails for exporting -{fields} Fields to export (drag'n drop settings in module) - -<f:section name="main"><vh:string.utf8ToUtf16><vh:string.trim> - - <f:for each="{mails}" as="mail" iteration="index"> - <f:if condition="{index.isFirst}"> - <f:for each="{fields}" as="field"> - "<vh:string.removeQuote>{field.title}</vh:string.removeQuote>"; - </f:for> - <br /> - </f:if> - - - <f:for each="{fields}" as="field"> - <f:for each="{mail.answers}" as="answer"> - <f:if condition="{field} == {answer.field}"> - - <f:if condition="{vh:condition.isArray(val: '{answer.value}')}"> - <f:else> - "<vh:string.removeQuote><vh:string.sanitizeCsvCell>{answer.value}</vh:string.sanitizeCsvCell></vh:string.removeQuote>"; - </f:else> - <f:then> - "<vh:string.removeQuote> - <vh:string.sanitizeCsvCell> - <f:for each="{answer.value}" as="singleValue"> - <f:if condition="{singleValue}"> - {singleValue}, - </f:if> - </f:for> - </vh:string.sanitizeCsvCell> - </vh:string.removeQuote>"; - </f:then> - </f:if> - - </f:if> - </f:for> - </f:for> - <br /> - </f:for> - -</vh:string.trim></vh:string.utf8ToUtf16></f:section>
Resources/Private/Templates/Output/ExportXls.html+0 −53 removed@@ -1,53 +0,0 @@ -{namespace vh=In2code\Powermail\ViewHelpers} -<f:layout name="Export" /> - -Render Powermail XLS Export -{mails} All Mails for exporting -{fields} Fields to export (drag'n drop settings in module) - -<f:section name="main"><html> - <head> - <meta http-equiv="content-type" content="text/html; charset=utf-8"> - </head> - <body> - <table> - <f:for each="{mails}" as="mail" iteration="index"> - <f:if condition="{index.isFirst}"> - <tr> - <f:for each="{fields}" as="field"> - <th> - {field.title} - </th> - </f:for> - </tr> - </f:if> - - - <tr> - <f:for each="{fields}" as="field"> - <td> - <f:for each="{mail.answers}" as="answer"> - <f:if condition="{field} == {answer.field}"> - <f:if condition="{vh:condition.isArray(val: '{answer.value}')}"> - <f:then> - <f:for each="{answer.value}" as="singleValue"> - <f:if condition="{singleValue}"> - <vh:string.sanitizeCsvCell>{singleValue}</vh:string.sanitizeCsvCell>, - </f:if> - </f:for> - </f:then> - <f:else> - <vh:string.sanitizeCsvCell>{answer.value}</vh:string.sanitizeCsvCell> - </f:else> - </f:if> - </f:if> - </f:for> - </td> - </f:for> - </tr> - </f:for> - </table> - </body> -</html> - -</f:section>
Resources/Private/Templates/Output/List.html+0 −2 modified@@ -15,8 +15,6 @@ <f:render partial="Output/Search" arguments="{_all}" /> - <f:render partial="Output/Export" arguments="{_all}" /> - <f:if condition="{mails}"> <f:then> <table class="table table-striped table-hover table-responsive">
Resources/Private/Templates/Output/Rss.html+0 −37 removed@@ -1,37 +0,0 @@ -{namespace vh=In2code\Powermail\ViewHelpers} -<f:layout name="Export" /> - -RSS Feed for Powermail_Frontend - -<f:section name="main"><?xml version="1.0" encoding="ISO-8859-1" ?> -<?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?> - <rss xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:sy="http://purl.org/rss/1.0/modules/syndication/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" version="2.0"> - - <channel> - <title>{settings.rss.title}</title> - <link>{settings.rss.link}</link> - <description>{settings.rss.description}</description> - <language>{settings.rss.language}</language> - <copyright>{settings.rss.copyright}</copyright> - <sy:updatePeriod>{settings.rss.updatePeriod}</sy:updatePeriod> - <sy:updateFrequency>{settings.rss.updateFrequency}</sy:updateFrequency> - - <f:for each="{mails}" as="mail"> - <item> - <title>{mail.senderName}</title> - <description> - <f:for each="{mail.answers}" as="answer"> - <f:if condition="{vh:condition.isArray(val: '{answer.value}')}"> - <f:else> - {answer.field.title}: {answer.value}; - </f:else> - </f:if> - </f:for> - </description> - <link><vh:string.encode><f:uri.action pageUid="{singlePid}" absolute="1" action="show" arguments="{mail: mail}" /></vh:string.encode></link> - <pubDate><f:format.date format="D, d M Y H:i:s +0200">{mail.crdate}</f:format.date></pubDate> - </item> - </f:for> - </channel> - </rss> -</f:section>
Tests/Behavior/Features/Mod1/Basic.feature+16 −16 modified@@ -1,17 +1,17 @@ # Features/Mod1/Basic.feature -@Mod1 @Mod1Basic -Feature: Basic - Basically checks if the backend module exists - - @javascript - Scenario: Login into backend - Given I am on "/typo3/index.php" - Then the sourcecode should contain 'typo3-login-logo' - When I fill in "username" with "editor" - When I fill in "p_field" with "editor" - And I press "t3-login-submit" - - And I wait "5" seconds - Given I am on "/typo3/module/web/PowermailM1" - - Then the response should contain "typo3-backend-module-router" +#@Mod1 @Mod1Basic +#Feature: Basic +# Basically checks if the backend module exists +# +# @javascript +# Scenario: Login into backend +# Given I am on "/typo3/index.php" +# Then the sourcecode should contain 'typo3-login-logo' +# When I fill in "username" with "editor" +# When I fill in "p_field" with "editor" +# And I press "t3-login-submit" +# +# And I wait "5" seconds +# Given I am on "/typo3/module/web/PowermailM1" +# +# Then the response should contain "typo3-backend-module-router"
Tests/Behavior/Features/Pi2/List.feature+2 −3 modified@@ -25,15 +25,14 @@ Feature: List Then I should see "Olli" Then the sourcecode should contain '<li class="disabled">' Then the sourcecode should contain '<a href="#">Z</a>' - Then the sourcecode should contain '<input class="btn btn-primary" type="submit" value="XLS">' @Pi2ListFilterEmpty Scenario: Check empty Filter over List View Given I am on "/powermail/pi2/standard/list" When I fill in "tx_powermail_pi2[filter][_all]" with "öoijasd908püuß980asdöijo" And I press "Jetzt Filtern" - Then I should see "Keine Mails vorhanden" - Then I should see "Bitte bearbeiten Sie Ihre Filter-Einstellungen." + Then I should see "Keine Mails gefunden" + Then I should see "Bitte passen Sie Ihre Filtereinstellungen an." @Pi2ListFilter Scenario: Check empty Filter over List View
Tests/Behavior/Features/Pi2/NoTypoScript.feature+1 −1 modified@@ -4,4 +4,4 @@ Feature: NoTypoScript Scenario: Check if No-TypoScript-Message appears Given I am on "/powermail/pi2/list-ohne-ts" - Then I should see "Keine TypoScript-Konfiguration gefunden" + Then I should see "TypoScript benötigt. Sind Sie sicher, dass Sie die erforderlichen Static Templates eingebunden haben?"
f56f8eefe151[SECURITY] Prevent information disclosure in OutputController
2 files changed · +28 −0
Classes/Controller/OutputController.php+13 −0 modified@@ -54,6 +54,10 @@ public function listAction() */ public function showAction(Mail $mail) { + if (!FrontendUtility::isAllowedToView($this->settings, $mail)) { + $this->forward('list'); + } + $fieldArray = $this->getFieldList($this->settings['single']['fields']); $this->view->assignMultiple( [ @@ -70,6 +74,15 @@ public function showAction(Mail $mail) */ public function editAction(Mail $mail = null) { + if (!FrontendUtility::isAllowedToEdit($this->settings, $mail)) { + $this->controllerContext = $this->buildControllerContext(); + $this->addFlashmessage( + LocalizationUtility::translate('PowermailFrontendEditFailed'), + '', + AbstractMessage::ERROR + ); + $this->forward('list'); + } $fieldArray = $this->getFieldList($this->settings['edit']['fields']); $this->view->assignMultiple( [
Classes/Utility/FrontendUtility.php+15 −0 modified@@ -139,6 +139,21 @@ public static function isAllowedToEdit($settings, $mail) return false; } + public static function isAllowedToView(array $settings, Mail $mail): bool + { + $feUser = ObjectUtility::getTyposcriptFrontendController()->fe_user->user['uid'] ?? 0; + if ( + $feUser === 0 || + ( + (int)$settings['list']['showownonly'] === 1 + && $mail->getFeuser()->getUid() !== $feUser + ) + ) { + return false; + } + return true; + } + /** * Is a frontend user logged in *
2c8a1bf7669e[SECURITY] Prevent information disclosure in OutputController
2 files changed · +49 −1
Classes/Controller/OutputController.php+34 −1 modified@@ -13,7 +13,6 @@ use TYPO3\CMS\Core\Configuration\Exception\ExtensionConfigurationPathDoesNotExistException; use TYPO3\CMS\Core\Messaging\AbstractMessage; use TYPO3\CMS\Core\Utility\GeneralUtility; -use TYPO3\CMS\Extbase\Annotation as ExtbaseAnnotation; use TYPO3\CMS\Extbase\Mvc\Exception\InvalidArgumentNameException; use TYPO3\CMS\Extbase\Mvc\Exception\NoSuchArgumentException; use TYPO3\CMS\Extbase\Mvc\Exception\StopActionException; @@ -65,6 +64,10 @@ public function listAction(): void */ public function showAction(Mail $mail): void { + if (!FrontendUtility::isAllowedToView($this->settings, $mail)) { + $this->forward('list'); + } + $fieldArray = $this->getFieldList($this->settings['single']['fields']); $this->view->assignMultiple( [ @@ -75,6 +78,36 @@ public function showAction(Mail $mail): void $this->assignMultipleActions(); } + /** + * @return void + * @throws InvalidArgumentNameException + * @throws InvalidQueryException + * @throws InvalidSlotException + * @throws InvalidSlotReturnException + * @throws NoSuchArgumentException + * @throws StopActionException + * @throws DBALException + * @throws ExtensionConfigurationExtensionNotConfiguredException + * @throws ExtensionConfigurationPathDoesNotExistException + * @throws Exception + * @throws DeprecatedException + * @noinspection PhpUnused + */ + public function initializeEditAction(): void + { + $arguments = $this->request->getArguments(); + if (!FrontendUtility::isAllowedToEdit($this->settings, $arguments['field']['__identity'])) { + $this->controllerContext = $this->buildControllerContext(); + $this->addFlashmessage( + LocalizationUtility::translate('PowermailFrontendEditFailed'), + '', + AbstractMessage::ERROR + ); + $this->forward('list'); + } + $this->reformatParamsForAction(); + } + /** * @param Mail $mail * @return void
Classes/Utility/FrontendUtility.php+15 −0 modified@@ -152,6 +152,21 @@ public static function isAllowedToEdit(array $settings, $mail): bool return false; } + public static function isAllowedToView(array $settings, Mail $mail): bool + { + $feUser = ObjectUtility::getTyposcriptFrontendController()->fe_user->user['uid'] ?? 0; + if ( + $feUser === 0 || + ( + (int)$settings['list']['showownonly'] === 1 + && $mail->getFeuser()->getUid() !== $feUser + ) + ) { + return false; + } + return true; + } + /** * Is a frontend user logged in *
Vulnerability mechanics
Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
7- github.com/advisories/GHSA-9jqr-5x45-pgw8ghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2024-45233ghsaADVISORY
- github.com/in2code-de/powermail/commit/04a010c4009202e8e1b4c72accd4d7b2771b80b3ghsaWEB
- github.com/in2code-de/powermail/commit/2c8a1bf7669eb0661e8a93164f57e4b653ac3408ghsaWEB
- github.com/in2code-de/powermail/commit/6e94ec5e0c7b553c467b826df1b922db6c2ad08eghsaWEB
- github.com/in2code-de/powermail/commit/f56f8eefe151ad67cbd32c21f1106953b8e4f19fghsaWEB
- typo3.org/security/advisory/typo3-ext-sa-2024-006ghsaWEB
News mentions
0No linked articles in our index yet.