diff --git a/src/Eccube/Resource/template/default/Mail/contact_mail.twig b/src/Eccube/Resource/template/default/Mail/contact_mail.twig index a5b643baa35..f13770a36c3 100644 --- a/src/Eccube/Resource/template/default/Mail/contact_mail.twig +++ b/src/Eccube/Resource/template/default/Mail/contact_mail.twig @@ -8,7 +8,7 @@ http://www.ec-cube.co.jp/ For the full copyright and license information, please view the LICENSE file that was distributed with this source code. #} -{% autoescape false %} +{% autoescape 'safe_textmail' %} ※本メールは自動配信メールです。 ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ diff --git a/src/Eccube/Resource/template/default/Mail/customer_withdraw_mail.twig b/src/Eccube/Resource/template/default/Mail/customer_withdraw_mail.twig index ffc3d37ab0b..0f5f6719d7d 100644 --- a/src/Eccube/Resource/template/default/Mail/customer_withdraw_mail.twig +++ b/src/Eccube/Resource/template/default/Mail/customer_withdraw_mail.twig @@ -8,7 +8,7 @@ http://www.ec-cube.co.jp/ For the full copyright and license information, please view the LICENSE file that was distributed with this source code. #} -{% autoescape false %} +{% autoescape 'safe_textmail' %} ※本メールは自動配信メールです。 ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ diff --git a/src/Eccube/Resource/template/default/Mail/entry_complete.twig b/src/Eccube/Resource/template/default/Mail/entry_complete.twig index 0c4cac1db52..fb53e87da33 100644 --- a/src/Eccube/Resource/template/default/Mail/entry_complete.twig +++ b/src/Eccube/Resource/template/default/Mail/entry_complete.twig @@ -8,7 +8,7 @@ http://www.ec-cube.co.jp/ For the full copyright and license information, please view the LICENSE file that was distributed with this source code. #} -{% autoescape false %} +{% autoescape 'safe_textmail' %} ※本メールは自動配信メールです。 ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ diff --git a/src/Eccube/Resource/template/default/Mail/entry_confirm.twig b/src/Eccube/Resource/template/default/Mail/entry_confirm.twig index 29b0c2b2878..b4cdc738324 100644 --- a/src/Eccube/Resource/template/default/Mail/entry_confirm.twig +++ b/src/Eccube/Resource/template/default/Mail/entry_confirm.twig @@ -8,7 +8,7 @@ http://www.ec-cube.co.jp/ For the full copyright and license information, please view the LICENSE file that was distributed with this source code. #} -{% autoescape false %} +{% autoescape 'safe_textmail' %} ※本メールは自動配信メールです。 ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ diff --git a/src/Eccube/Resource/template/default/Mail/forgot_mail.twig b/src/Eccube/Resource/template/default/Mail/forgot_mail.twig index b4322c92155..8cc348d5f03 100644 --- a/src/Eccube/Resource/template/default/Mail/forgot_mail.twig +++ b/src/Eccube/Resource/template/default/Mail/forgot_mail.twig @@ -8,7 +8,7 @@ http://www.ec-cube.co.jp/ For the full copyright and license information, please view the LICENSE file that was distributed with this source code. #} -{% autoescape false %} +{% autoescape 'safe_textmail' %} ※本メールは自動配信メールです。 ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ diff --git a/src/Eccube/Resource/template/default/Mail/order.twig b/src/Eccube/Resource/template/default/Mail/order.twig index 66134d7569d..71eed2f8994 100644 --- a/src/Eccube/Resource/template/default/Mail/order.twig +++ b/src/Eccube/Resource/template/default/Mail/order.twig @@ -8,7 +8,7 @@ http://www.ec-cube.co.jp/ For the full copyright and license information, please view the LICENSE file that was distributed with this source code. #} -{% autoescape false %} +{% autoescape 'safe_textmail' %} {{ Order.name01 }} {{ Order.name02 }} 様 この度はご注文いただき誠にありがとうございます。下記ご注文内容にお間違えがないかご確認下さい。 diff --git a/src/Eccube/Resource/template/default/Mail/reset_complete_mail.twig b/src/Eccube/Resource/template/default/Mail/reset_complete_mail.twig index 7130bafc043..93d50ee5274 100644 --- a/src/Eccube/Resource/template/default/Mail/reset_complete_mail.twig +++ b/src/Eccube/Resource/template/default/Mail/reset_complete_mail.twig @@ -8,7 +8,7 @@ http://www.ec-cube.co.jp/ For the full copyright and license information, please view the LICENSE file that was distributed with this source code. #} -{% autoescape false %} +{% autoescape 'safe_textmail' %} ※本メールは自動配信メールです。 ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ diff --git a/src/Eccube/Resource/template/default/Mail/shipping_notify.twig b/src/Eccube/Resource/template/default/Mail/shipping_notify.twig index b015f63a5ce..ead936b8bfd 100644 --- a/src/Eccube/Resource/template/default/Mail/shipping_notify.twig +++ b/src/Eccube/Resource/template/default/Mail/shipping_notify.twig @@ -8,7 +8,7 @@ http://www.ec-cube.co.jp/ For the full copyright and license information, please view the LICENSE file that was distributed with this source code. #} -{% autoescape false %} +{% autoescape 'safe_textmail' %} {{ Order.name01 }} {{ Order.name02 }} 様 お客さまがご注文された以下の商品を発送いたしました。商品の到着まで、今しばらくお待ちください。 diff --git a/src/Eccube/Twig/Extension/SafeTextmailEscaperExtension.php b/src/Eccube/Twig/Extension/SafeTextmailEscaperExtension.php new file mode 100644 index 00000000000..d62b63bb752 --- /dev/null +++ b/src/Eccube/Twig/Extension/SafeTextmailEscaperExtension.php @@ -0,0 +1,29 @@ +getExtension(EscaperExtension::class)->setEscaper( + 'safe_textmail', function ($twig, $string, $charset) { + return str_replace(['<', '>'], ['<', '>'], $string); + } + ); + } +} diff --git a/tests/Eccube/Tests/Service/CsvImportServiceTest.php b/tests/Eccube/Tests/Service/CsvImportServiceTest.php index 36b5e625437..560dd703546 100644 --- a/tests/Eccube/Tests/Service/CsvImportServiceTest.php +++ b/tests/Eccube/Tests/Service/CsvImportServiceTest.php @@ -90,6 +90,7 @@ public function testReadCsvFileWithManualColumnHeaders() public function testReadCsvFileWithTrailingBlankLines() { $file = new \SplFileObject(__DIR__.'/../../../Fixtures/data_blank_lines.csv'); + $CsvImportService = new CsvImportService($file); $CsvImportService->setColumnHeaders(['id', 'number', 'description']); diff --git a/tests/Eccube/Tests/Web/Admin/Order/ShippingControllerTest.php b/tests/Eccube/Tests/Web/Admin/Order/ShippingControllerTest.php index 82cd2688cfe..e1d005b80bb 100644 --- a/tests/Eccube/Tests/Web/Admin/Order/ShippingControllerTest.php +++ b/tests/Eccube/Tests/Web/Admin/Order/ShippingControllerTest.php @@ -208,6 +208,47 @@ public function testSendNotifyMail() self::assertEquals([$Order->getEmail() => null], $Message->getTo()); } + public function testSendNotifyMailWithSanitize() + { + $this->client->enableProfiler(); + $Customer = $this->createCustomer(); + $Customer->setName01(''); + + $Order = $this->createOrder($Customer); + /** @var Shipping $Shipping */ + $Shipping = $Order->getShippings()->first(); + + $shippingDate = new \DateTime(); + $Shipping->setShippingDate($shippingDate); + $this->entityManager->persist($Shipping); + $this->entityManager->flush(); + + $this->client->request( + 'PUT', + $this->generateUrl('admin_shipping_notify_mail', ['id' => $Shipping->getId()]) + ); + + $this->assertTrue($this->client->getResponse()->isSuccessful()); + + $Messages = $this->getMailCollector(false)->getMessages(); + self::assertEquals(1, count($Messages)); + + /** @var \Swift_Message $Message */ + $Message = $Messages[0]; + + self::assertRegExp('/\[.*?\] 商品出荷のお知らせ/', $Message->getSubject()); + self::assertEquals([$Order->getEmail() => null], $Message->getTo()); + + $this->assertContains('<Sanitize&>', $Message->getBody(), 'テキストメールがサニタイズされている'); + + $MultiPart = $Message->getChildren(); + foreach ($MultiPart as $Part) { + if ($Part->getContentType() == 'text/html') { + $this->assertContains('<Sanitize&>', $Part->getBody(), 'HTMLメールがサニタイズされている'); + } + } + } + public function testNotSendNotifyMail() { $this->client->enableProfiler(); diff --git a/tests/Eccube/Tests/Web/Admin/Setting/Shop/MailControllerTest.php b/tests/Eccube/Tests/Web/Admin/Setting/Shop/MailControllerTest.php index a522475e721..7e9dbc55ddb 100644 --- a/tests/Eccube/Tests/Web/Admin/Setting/Shop/MailControllerTest.php +++ b/tests/Eccube/Tests/Web/Admin/Setting/Shop/MailControllerTest.php @@ -27,6 +27,9 @@ public function tearDown() if (file_exists($themeDir.'/Mail/order.twig')) { unlink($themeDir.'/Mail/order.twig'); } + if (file_exists($themeDir.'/Mail/order.html.twig')) { + unlink($themeDir.'/Mail/order.html.twig'); + } parent::tearDown(); } diff --git a/tests/Eccube/Tests/Web/ContactControllerTest.php b/tests/Eccube/Tests/Web/ContactControllerTest.php index 892c7ed9dbf..fc07745b682 100644 --- a/tests/Eccube/Tests/Web/ContactControllerTest.php +++ b/tests/Eccube/Tests/Web/ContactControllerTest.php @@ -98,6 +98,43 @@ public function testComplete() $this->verify(); } + public function testCompleteWithSanitize() + { + $this->client->enableProfiler(); + $form = $this->createFormData(); + $form['name']['name01'] .= ''; + $crawler = $this->client->request( + 'POST', + $this->generateUrl('contact'), + ['contact' => $form, + 'mode' => 'complete', ] + ); + + $this->assertTrue($this->client->getResponse()->isRedirect($this->generateUrl('contact_complete'))); + + $BaseInfo = $this->entityManager->find(BaseInfo::class, 1); + + $mailCollector = $this->getMailCollector(false); + $this->assertEquals(1, $mailCollector->getMessageCount()); + + $collectedMessages = $mailCollector->getMessages(); + /** @var \Swift_Message $Message */ + $Message = $collectedMessages[0]; + + $this->expected = '['.$BaseInfo->getShopName().'] お問い合わせを受け付けました。'; + $this->actual = $Message->getSubject(); + $this->verify(); + + $this->assertContains('<Sanitize&>', $Message->getBody(), 'テキストメールがサニタイズされている'); + + $MultiPart = $Message->getChildren(); + foreach ($MultiPart as $Part) { + if ($Part->getContentType() == 'text/html') { + $this->assertContains('<Sanitize&>', $Part->getBody(), 'HTMLメールがサニタイズされている'); + } + } + } + /** * 必須項目のみのテストケース * diff --git a/tests/Eccube/Tests/Web/EntryControllerTest.php b/tests/Eccube/Tests/Web/EntryControllerTest.php index 89a5e90fa48..cdcac487b99 100644 --- a/tests/Eccube/Tests/Web/EntryControllerTest.php +++ b/tests/Eccube/Tests/Web/EntryControllerTest.php @@ -163,6 +163,43 @@ public function testCompleteWithActivate() $this->verify(); } + public function testCompleteWithActivateWithMultipartSanitize() + { + $BaseInfo = $this->entityManager->getRepository(\Eccube\Entity\BaseInfo::class)->get(); + $BaseInfo->setOptionCustomerActivate(1); + $this->entityManager->flush(); + + $client = $this->client; + $form = $this->createFormData(); + $form['name']['name01'] .= ''; // サニタイズ対象の文字列 + $crawler = $client->request('POST', + $this->generateUrl('entry'), + [ + 'entry' => $form, + 'mode' => 'complete', + ] + ); + + $this->assertTrue($client->getResponse()->isRedirect($this->generateUrl('entry_complete'))); + + $collectedMessages = $this->getMailCollector(false)->getMessages(); + /** @var \Swift_Message $Message */ + $Message = $collectedMessages[0]; + + $this->expected = '['.$BaseInfo->getShopName().'] 会員登録のご確認'; + $this->actual = $Message->getSubject(); + $this->verify(); + + $this->assertContains('<Sanitize&>', $Message->getBody(), 'テキストメールがサニタイズされている'); + + $MultiPart = $Message->getChildren(); + foreach ($MultiPart as $Part) { + if ($Part->getContentType() == 'text/html') { + $this->assertContains('<Sanitize&>', $Part->getBody(), 'HTMLメールがサニタイズされている'); + } + } + } + public function testRoutingComplete() { $client = $this->client; @@ -192,6 +229,37 @@ public function testActivate() $this->verify(); } + public function testActivateWithSanitize() + { + $BaseInfo = $this->entityManager->getRepository(\Eccube\Entity\BaseInfo::class)->get(); + $Customer = $this->createCustomer(); + $Customer->setName01(''); + $secret_key = $Customer->getSecretKey(); + $Status = $this->entityManager->getRepository('Eccube\Entity\Master\CustomerStatus')->find(CustomerStatus::NONACTIVE); + $Customer->setStatus($Status); + $this->entityManager->flush(); + + $client = $this->client; + $client->request('GET', $this->generateUrl('entry_activate', ['secret_key' => $secret_key])); + + $this->assertTrue($client->getResponse()->isSuccessful()); + $collectedMessages = $this->getMailCollector(false)->getMessages(); + /** @var \Swift_Message $Message */ + $Message = $collectedMessages[0]; + $this->expected = '['.$BaseInfo->getShopName().'] 会員登録が完了しました。'; + $this->actual = $Message->getSubject(); + $this->verify(); + + $this->assertContains('<Sanitize&>', $Message->getBody(), 'テキストメールがサニタイズされている'); + + $MultiPart = $Message->getChildren(); + foreach ($MultiPart as $Part) { + if ($Part->getContentType() == 'text/html') { + $this->assertContains('<Sanitize&>', $Part->getBody(), 'HTMLメールがサニタイズされている'); + } + } + } + public function testActivateWithNotFound() { $this->client->request('GET', $this->generateUrl('entry_activate', ['secret_key' => 'aaaaa'])); diff --git a/tests/Eccube/Tests/Web/Mypage/WithdrawControllerTest.php b/tests/Eccube/Tests/Web/Mypage/WithdrawControllerTest.php index 2737eba51a5..025b230cbae 100644 --- a/tests/Eccube/Tests/Web/Mypage/WithdrawControllerTest.php +++ b/tests/Eccube/Tests/Web/Mypage/WithdrawControllerTest.php @@ -93,6 +93,46 @@ public function testIndexWithPostComplete() $this->verify(); } + public function testIndexWithPostCompleteWithSanitize() + { + $this->Customer->setName01(''); + $this->entityManager->flush(); + + $this->client->enableProfiler(); + $this->logInTo($this->Customer); + + $crawler = $this->client->request( + 'POST', + $this->generateUrl('mypage_withdraw'), + [ + 'form' => ['_token' => 'dummy'], + 'mode' => 'complete', + ] + ); + + $this->assertRegExp('/@dummy.dummy/', $this->Customer->getEmail()); + + $this->assertTrue($this->client->getResponse()->isRedirect($this->generateUrl('mypage_withdraw_complete'))); + + $Messages = $this->getMailCollector(false)->getMessages(); + /** @var \Swift_Message $Message */ + $Message = $Messages[0]; + + $BaseInfo = $this->entityManager->getRepository(\Eccube\Entity\BaseInfo::class)->get(); + $this->expected = '['.$BaseInfo->getShopName().'] 退会手続きのご完了'; + $this->actual = $Message->getSubject(); + $this->verify(); + + $this->assertContains('<Sanitize&>', $Message->getBody(), 'テキストメールがサニタイズされている'); + + $MultiPart = $Message->getChildren(); + foreach ($MultiPart as $Part) { + if ($Part->getContentType() == 'text/html') { + $this->assertContains('<Sanitize&>', $Part->getBody(), 'HTMLメールがサニタイズされている'); + } + } + } + public function testComplete() { $this->client->request( diff --git a/tests/Eccube/Tests/Web/ShoppingControllerTest.php b/tests/Eccube/Tests/Web/ShoppingControllerTest.php index bb37999432e..3f4f819915f 100644 --- a/tests/Eccube/Tests/Web/ShoppingControllerTest.php +++ b/tests/Eccube/Tests/Web/ShoppingControllerTest.php @@ -571,6 +571,69 @@ public function testCompleteWithChangeDeliveryName() $this->verify(); } + /** + * カート→購入確認画面→完了画面(テキストメールサニタイズ) + */ + public function testCompleteWithSanitize() + { + $Customer = $this->createCustomer(); + $Customer->setName01(''); + $this->entityManager->flush(); + + // カート画面 + $this->scenarioCartIn($Customer); + + // 手続き画面 + $crawler = $this->scenarioConfirm($Customer); + $this->expected = 'ご注文手続き'; + $this->actual = $crawler->filter('.ec-pageHeader h1')->text(); + $this->verify(); + + // 確認画面 + $crawler = $this->scenarioComplete( + $Customer, + $this->generateUrl('shopping_confirm'), + [ + [ + 'Delivery' => 1, + 'DeliveryTime' => null, + ], + ] + ); + + $this->expected = 'ご注文内容のご確認'; + $this->actual = $crawler->filter('.ec-pageHeader h1')->text(); + $this->verify(); + + // 完了画面 + $crawler = $this->scenarioComplete( + $Customer, + $this->generateUrl('shopping_checkout'), + [], + true + ); + + $this->assertTrue($this->client->getResponse()->isRedirect($this->generateUrl('shopping_complete'))); + + $BaseInfo = $this->baseInfoRepository->get(); + $mailCollector = $this->getMailCollector(false); + $Messages = $mailCollector->getMessages(); + $Message = $Messages[0]; + + $this->expected = '['.$BaseInfo->getShopName().'] ご注文ありがとうございます'; + $this->actual = $Message->getSubject(); + $this->verify(); + + $this->assertContains('<Sanitize&>', $Message->getBody(), 'テキストメールがサニタイズされている'); + + $MultiPart = $Message->getChildren(); + foreach ($MultiPart as $Part) { + if ($Part->getContentType() == 'text/html') { + $this->assertContains('<Sanitize&>', $Part->getBody(), 'HTMLメールがサニタイズされている'); + } + } + } + /** * Check can use point when has payment limit *