Skip to content

Commit 838cff7

Browse files
authored
Merge pull request #1677 from magento-thunder/MAGETWO-82057
Fixed issue: MAGETWO-82057 Varnish / Fastly - Magento can cache & return cached cart and similar sensitive data
2 parents dbcc3c4 + eccf644 commit 838cff7

File tree

4 files changed

+202
-13
lines changed

4 files changed

+202
-13
lines changed

app/code/Magento/Customer/Controller/Section/Load.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,8 @@ public function execute()
6464
{
6565
/** @var \Magento\Framework\Controller\Result\Json $resultJson */
6666
$resultJson = $this->resultJsonFactory->create();
67+
$resultJson->setHeader('Cache-Control', 'max-age=0, must-revalidate, no-cache, no-store');
68+
$resultJson->setHeader('Pragma', 'no-cache');
6769
try {
6870
$sectionNames = $this->getRequest()->getParam('sections');
6971
$sectionNames = $sectionNames ? array_unique(\explode(',', $sectionNames)) : null;
Lines changed: 181 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
namespace Magento\Customer\Test\Unit\Controller\Section;
7+
8+
use Magento\Customer\Controller\Section\Load;
9+
use Magento\Customer\CustomerData\Section\Identifier;
10+
use Magento\Customer\CustomerData\SectionPoolInterface;
11+
use Magento\Framework\App\Action\Context;
12+
use Magento\Framework\Controller\Result\Json;
13+
use Magento\Framework\Controller\Result\JsonFactory;
14+
use Magento\Framework\Escaper;
15+
use \PHPUnit_Framework_MockObject_MockObject as MockObject;
16+
use Magento\Framework\App\Request\Http as HttpRequest;
17+
18+
class LoadTest extends \PHPUnit\Framework\TestCase
19+
{
20+
/**
21+
* @var Load
22+
*/
23+
private $loadAction;
24+
25+
/**
26+
* @var Context|MockObject
27+
*/
28+
private $contextMock;
29+
30+
/**
31+
* @var JsonFactory|MockObject
32+
*/
33+
private $resultJsonFactoryMock;
34+
35+
/**
36+
* @var Identifier|MockObject
37+
*/
38+
private $sectionIdentifierMock;
39+
40+
/**
41+
* @var SectionPoolInterface|MockObject
42+
*/
43+
private $sectionPoolMock;
44+
45+
/**
46+
* @var \Magento\Framework\Escaper|MockObject
47+
*/
48+
private $escaperMock;
49+
50+
/**
51+
* @var Json|MockObject
52+
*/
53+
private $resultJsonMock;
54+
55+
/**
56+
* @var HttpRequest|MockObject
57+
*/
58+
private $httpRequestMock;
59+
60+
protected function setUp()
61+
{
62+
$this->contextMock = $this->createMock(Context::class);
63+
$this->resultJsonFactoryMock = $this->createMock(JsonFactory::class);
64+
$this->sectionIdentifierMock = $this->createMock(Identifier::class);
65+
$this->sectionPoolMock = $this->getMockForAbstractClass(SectionPoolInterface::class);
66+
$this->escaperMock = $this->createMock(Escaper::class);
67+
$this->httpRequestMock = $this->createMock(HttpRequest::class);
68+
$this->resultJsonMock = $this->createMock(Json::class);
69+
70+
$this->contextMock->expects($this->once())
71+
->method('getRequest')
72+
->willReturn($this->httpRequestMock);
73+
74+
$this->loadAction = new Load(
75+
$this->contextMock,
76+
$this->resultJsonFactoryMock,
77+
$this->sectionIdentifierMock,
78+
$this->sectionPoolMock,
79+
$this->escaperMock
80+
);
81+
}
82+
83+
/**
84+
* @param $sectionNames
85+
* @param $updateSectionID
86+
* @param $sectionNamesAsArray
87+
* @param $updateIds
88+
* @dataProvider executeDataProvider
89+
*/
90+
public function testExecute($sectionNames, $updateSectionID, $sectionNamesAsArray, $updateIds)
91+
{
92+
$this->resultJsonFactoryMock->expects($this->once())
93+
->method('create')
94+
->willReturn($this->resultJsonMock);
95+
$this->resultJsonMock->expects($this->exactly(2))
96+
->method('setHeader')
97+
->withConsecutive(
98+
['Cache-Control', 'max-age=0, must-revalidate, no-cache, no-store'],
99+
['Pragma', 'no-cache']
100+
);
101+
102+
$this->httpRequestMock->expects($this->exactly(2))
103+
->method('getParam')
104+
->withConsecutive(['sections'], ['update_section_id'])
105+
->willReturnOnConsecutiveCalls($sectionNames, $updateSectionID);
106+
107+
$this->sectionPoolMock->expects($this->once())
108+
->method('getSectionsData')
109+
->with($sectionNamesAsArray, $updateIds)
110+
->willReturn([
111+
'message' => 'some message',
112+
'someKey' => 'someValue'
113+
]);
114+
115+
$this->resultJsonMock->expects($this->once())
116+
->method('setData')
117+
->with([
118+
'message' => 'some message',
119+
'someKey' => 'someValue'
120+
])
121+
->willReturn($this->resultJsonMock);
122+
123+
$this->loadAction->execute();
124+
}
125+
126+
public function executeDataProvider()
127+
{
128+
return [
129+
[
130+
'sectionNames' => 'sectionName1,sectionName2,sectionName3',
131+
'updateSectionID' => 'updateSectionID',
132+
'sectionNamesAsArray' => ['sectionName1', 'sectionName2', 'sectionName3'],
133+
'updateIds' => true
134+
],
135+
[
136+
'sectionNames' => null,
137+
'updateSectionID' => null,
138+
'sectionNamesAsArray' => null,
139+
'updateIds' => false
140+
],
141+
];
142+
}
143+
144+
public function testExecuteWithThrowException()
145+
{
146+
$this->resultJsonFactoryMock->expects($this->once())
147+
->method('create')
148+
->willReturn($this->resultJsonMock);
149+
$this->resultJsonMock->expects($this->exactly(2))
150+
->method('setHeader')
151+
->withConsecutive(
152+
['Cache-Control', 'max-age=0, must-revalidate, no-cache, no-store'],
153+
['Pragma', 'no-cache']
154+
);
155+
156+
$this->httpRequestMock->expects($this->once())
157+
->method('getParam')
158+
->with('sections')
159+
->willThrowException(new \Exception('Some Message'));
160+
161+
$this->resultJsonMock->expects($this->once())
162+
->method('setStatusHeader')
163+
->with(
164+
\Zend\Http\Response::STATUS_CODE_400,
165+
\Zend\Http\AbstractMessage::VERSION_11,
166+
'Bad Request'
167+
);
168+
169+
$this->escaperMock->expects($this->once())
170+
->method('escapeHtml')
171+
->with('Some Message')
172+
->willReturn('Some Message');
173+
174+
$this->resultJsonMock->expects($this->once())
175+
->method('setData')
176+
->with(['message' => 'Some Message'])
177+
->willReturn($this->resultJsonMock);
178+
179+
$this->loadAction->execute();
180+
}
181+
}

app/code/Magento/PageCache/etc/varnish4.vcl

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,10 @@ sub vcl_backend_response {
141141
set beresp.do_gzip = true;
142142
}
143143

144+
if (beresp.http.X-Magento-Debug) {
145+
set beresp.http.X-Magento-Cache-Control = beresp.http.Cache-Control;
146+
}
147+
144148
# cache only successfully responses and 404s
145149
if (beresp.status != 200 && beresp.status != 404) {
146150
set beresp.ttl = 0s;
@@ -152,23 +156,22 @@ sub vcl_backend_response {
152156
return (deliver);
153157
}
154158

155-
if (beresp.http.X-Magento-Debug) {
156-
set beresp.http.X-Magento-Cache-Control = beresp.http.Cache-Control;
157-
}
158-
159159
# validate if we need to cache it and prevent from setting cookie
160160
if (beresp.ttl > 0s && (bereq.method == "GET" || bereq.method == "HEAD")) {
161161
unset beresp.http.set-cookie;
162162
}
163163

164164
# If page is not cacheable then bypass varnish for 2 minutes as Hit-For-Pass
165165
if (beresp.ttl <= 0s ||
166-
beresp.http.Surrogate-control ~ "no-store" ||
167-
(!beresp.http.Surrogate-Control && beresp.http.Vary == "*")) {
168-
# Mark as Hit-For-Pass for the next 2 minutes
166+
beresp.http.Surrogate-control ~ "no-store" ||
167+
(!beresp.http.Surrogate-Control &&
168+
beresp.http.Cache-Control ~ "no-cache|no-store") ||
169+
beresp.http.Vary == "*") {
170+
# Mark as Hit-For-Pass for the next 2 minutes
169171
set beresp.ttl = 120s;
170172
set beresp.uncacheable = true;
171173
}
174+
172175
return (deliver);
173176
}
174177

app/code/Magento/PageCache/etc/varnish5.vcl

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,10 @@ sub vcl_backend_response {
142142
set beresp.do_gzip = true;
143143
}
144144

145+
if (beresp.http.X-Magento-Debug) {
146+
set beresp.http.X-Magento-Cache-Control = beresp.http.Cache-Control;
147+
}
148+
145149
# cache only successfully responses and 404s
146150
if (beresp.status != 200 && beresp.status != 404) {
147151
set beresp.ttl = 0s;
@@ -153,23 +157,22 @@ sub vcl_backend_response {
153157
return (deliver);
154158
}
155159

156-
if (beresp.http.X-Magento-Debug) {
157-
set beresp.http.X-Magento-Cache-Control = beresp.http.Cache-Control;
158-
}
159-
160160
# validate if we need to cache it and prevent from setting cookie
161161
if (beresp.ttl > 0s && (bereq.method == "GET" || bereq.method == "HEAD")) {
162162
unset beresp.http.set-cookie;
163163
}
164164

165165
# If page is not cacheable then bypass varnish for 2 minutes as Hit-For-Pass
166166
if (beresp.ttl <= 0s ||
167-
beresp.http.Surrogate-control ~ "no-store" ||
168-
(!beresp.http.Surrogate-Control && beresp.http.Vary == "*")) {
167+
beresp.http.Surrogate-control ~ "no-store" ||
168+
(!beresp.http.Surrogate-Control &&
169+
beresp.http.Cache-Control ~ "no-cache|no-store") ||
170+
beresp.http.Vary == "*") {
169171
# Mark as Hit-For-Pass for the next 2 minutes
170172
set beresp.ttl = 120s;
171173
set beresp.uncacheable = true;
172174
}
175+
173176
return (deliver);
174177
}
175178

0 commit comments

Comments
 (0)