Skip to content

Commit 9754c3b

Browse files
Merge branch '5.3' into 5.4
* 5.3: Use single quote to escape formulas [SecurityBundle] Default signature_properties to the previous behavior Fix missing extra trusted header in sub-request
2 parents b4c7d18 + 4a1877e commit 9754c3b

File tree

7 files changed

+129
-21
lines changed

7 files changed

+129
-21
lines changed

src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/RememberMeFactory.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,7 @@ public function addConfiguration(NodeDefinition $node)
219219
->requiresAtLeastOneElement()
220220
->info('An array of properties on your User that are used to sign the remember-me cookie. If any of these change, all existing cookies will become invalid.')
221221
->example(['email', 'password'])
222+
->defaultValue(['password'])
222223
->end()
223224
->arrayNode('token_provider')
224225
->beforeNormalization()

src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/RememberMeBundle/Security/UserChangingUserProvider.php

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -21,33 +21,40 @@ class UserChangingUserProvider implements UserProviderInterface
2121
{
2222
private $inner;
2323

24+
public static $changePassword = false;
25+
2426
public function __construct(InMemoryUserProvider $inner)
2527
{
2628
$this->inner = $inner;
2729
}
2830

2931
public function loadUserByUsername($username): UserInterface
3032
{
31-
return $this->inner->loadUserByUsername($username);
33+
return $this->changeUser($this->inner->loadUserByUsername($username));
3234
}
3335

3436
public function loadUserByIdentifier(string $userIdentifier): UserInterface
3537
{
36-
return $this->inner->loadUserByIdentifier($userIdentifier);
38+
return $this->changeUser($this->inner->loadUserByIdentifier($userIdentifier));
3739
}
3840

3941
public function refreshUser(UserInterface $user): UserInterface
4042
{
41-
$user = $this->inner->refreshUser($user);
42-
43-
$alterUser = \Closure::bind(function (InMemoryUser $user) { $user->password = 'foo'; }, null, class_exists(User::class) ? User::class : InMemoryUser::class);
44-
$alterUser($user);
45-
46-
return $user;
43+
return $this->changeUser($this->inner->refreshUser($user));
4744
}
4845

4946
public function supportsClass($class): bool
5047
{
5148
return $this->inner->supportsClass($class);
5249
}
50+
51+
private function changeUser(UserInterface $user): UserInterface
52+
{
53+
if (self::$changePassword) {
54+
$alterUser = \Closure::bind(function (InMemoryUser $user) { $user->password = 'changed!'; }, null, class_exists(User::class) ? User::class : InMemoryUser::class);
55+
$alterUser($user);
56+
}
57+
58+
return $user;
59+
}
5360
}

src/Symfony/Bundle/SecurityBundle/Tests/Functional/RememberMeTest.php

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,15 @@
1111

1212
namespace Symfony\Bundle\SecurityBundle\Tests\Functional;
1313

14+
use Symfony\Bundle\SecurityBundle\Tests\Functional\Bundle\RememberMeBundle\Security\UserChangingUserProvider;
15+
1416
class RememberMeTest extends AbstractWebTestCase
1517
{
18+
protected function setUp(): void
19+
{
20+
UserChangingUserProvider::$changePassword = false;
21+
}
22+
1623
/**
1724
* @dataProvider provideConfigs
1825
*/
@@ -51,11 +58,19 @@ public function testUserChangeClearsCookie()
5158

5259
$this->assertSame(302, $client->getResponse()->getStatusCode());
5360
$cookieJar = $client->getCookieJar();
54-
$this->assertNotNull($cookieJar->get('REMEMBERME'));
61+
$this->assertNotNull($cookie = $cookieJar->get('REMEMBERME'));
62+
63+
UserChangingUserProvider::$changePassword = true;
5564

65+
// change password (through user provider), this deauthenticates the session
5666
$client->request('GET', '/profile');
5767
$this->assertRedirect($client->getResponse(), '/login');
5868
$this->assertNull($cookieJar->get('REMEMBERME'));
69+
70+
// restore the old remember me cookie, it should no longer be valid
71+
$cookieJar->set($cookie);
72+
$client->request('GET', '/profile');
73+
$this->assertRedirect($client->getResponse(), '/login');
5974
}
6075

6176
public function testSessionLessRememberMeLogout()
@@ -121,11 +136,19 @@ public function testLegacyUserChangeClearsCookie()
121136

122137
$this->assertSame(302, $client->getResponse()->getStatusCode());
123138
$cookieJar = $client->getCookieJar();
124-
$this->assertNotNull($cookieJar->get('REMEMBERME'));
139+
$this->assertNotNull($cookie = $cookieJar->get('REMEMBERME'));
140+
141+
UserChangingUserProvider::$changePassword = true;
125142

143+
// change password (through user provider), this deauthenticates the session
126144
$client->request('GET', '/profile');
127145
$this->assertRedirect($client->getResponse(), '/login');
128146
$this->assertNull($cookieJar->get('REMEMBERME'));
147+
148+
// restore the old remember me cookie, it should no longer be valid
149+
$cookieJar->set($cookie);
150+
$client->request('GET', '/profile');
151+
$this->assertRedirect($client->getResponse(), '/login');
129152
}
130153

131154
/**

src/Symfony/Component/HttpKernel/HttpCache/SubRequestHandler.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ public static function handle(HttpKernelInterface $kernel, Request $request, int
3838
'X_FORWARDED_HOST' => $trustedHeaderSet & Request::HEADER_X_FORWARDED_HOST,
3939
'X_FORWARDED_PROTO' => $trustedHeaderSet & Request::HEADER_X_FORWARDED_PROTO,
4040
'X_FORWARDED_PORT' => $trustedHeaderSet & Request::HEADER_X_FORWARDED_PORT,
41+
'X_FORWARDED_PREFIX' => $trustedHeaderSet & Request::HEADER_X_FORWARDED_PREFIX,
4142
];
4243
foreach (array_filter($trustedHeaders) as $name => $key) {
4344
$request->headers->remove($name);

src/Symfony/Component/HttpKernel/Tests/HttpCache/SubRequestHandlerTest.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,13 +42,15 @@ public function testTrustedHeadersAreKept()
4242
$request->headers->set('X-Forwarded-Host', 'Good');
4343
$request->headers->set('X-Forwarded-Port', '1234');
4444
$request->headers->set('X-Forwarded-Proto', 'https');
45+
$request->headers->set('X-Forwarded-Prefix', '/admin');
4546

4647
$kernel = new TestSubRequestHandlerKernel(function ($request, $type, $catch) {
4748
$this->assertSame('127.0.0.1', $request->server->get('REMOTE_ADDR'));
4849
$this->assertSame('10.0.0.2', $request->getClientIp());
4950
$this->assertSame('Good', $request->headers->get('X-Forwarded-Host'));
5051
$this->assertSame('1234', $request->headers->get('X-Forwarded-Port'));
5152
$this->assertSame('https', $request->headers->get('X-Forwarded-Proto'));
53+
$this->assertSame('/admin', $request->headers->get('X-Forwarded-Prefix'));
5254
});
5355

5456
SubRequestHandler::handle($kernel, $request, HttpKernelInterface::MAIN_REQUEST, true);
@@ -64,6 +66,7 @@ public function testUntrustedHeadersAreRemoved()
6466
$request->headers->set('X-Forwarded-Host', 'Evil');
6567
$request->headers->set('X-Forwarded-Port', '1234');
6668
$request->headers->set('X-Forwarded-Proto', 'http');
69+
$request->headers->set('X-Forwarded-Prefix', '/admin');
6770
$request->headers->set('Forwarded', 'Evil2');
6871

6972
$kernel = new TestSubRequestHandlerKernel(function ($request, $type, $catch) {
@@ -72,6 +75,7 @@ public function testUntrustedHeadersAreRemoved()
7275
$this->assertFalse($request->headers->has('X-Forwarded-Host'));
7376
$this->assertFalse($request->headers->has('X-Forwarded-Port'));
7477
$this->assertFalse($request->headers->has('X-Forwarded-Proto'));
78+
$this->assertFalse($request->headers->has('X-Forwarded-Prefix'));
7579
$this->assertSame('for="10.0.0.1";host="localhost";proto=http', $request->headers->get('Forwarded'));
7680
});
7781

@@ -112,12 +116,14 @@ public function testTrustedXForwardedForHeader()
112116
$request->headers->set('X-Forwarded-For', '10.0.0.2');
113117
$request->headers->set('X-Forwarded-Host', 'foo.bar');
114118
$request->headers->set('X-Forwarded-Proto', 'https');
119+
$request->headers->set('X-Forwarded-Prefix', '/admin');
115120

116121
$kernel = new TestSubRequestHandlerKernel(function ($request, $type, $catch) {
117122
$this->assertSame('127.0.0.1', $request->server->get('REMOTE_ADDR'));
118123
$this->assertSame('10.0.0.2', $request->getClientIp());
119124
$this->assertSame('foo.bar', $request->getHttpHost());
120125
$this->assertSame('https', $request->getScheme());
126+
$this->assertSame('/admin', $request->getBaseUrl());
121127
});
122128

123129
SubRequestHandler::handle($kernel, $request, HttpKernelInterface::MAIN_REQUEST, true);

src/Symfony/Component/Serializer/Encoder/CsvEncoder.php

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,8 @@ class CsvEncoder implements EncoderInterface, DecoderInterface
3636

3737
private const UTF8_BOM = "\xEF\xBB\xBF";
3838

39-
private $formulasStartCharacters = ['=', '-', '+', '@'];
39+
private const FORMULAS_START_CHARACTERS = ['=', '-', '+', '@', "\t", "\r"];
40+
4041
private $defaultContext = [
4142
self::DELIMITER_KEY => ',',
4243
self::ENCLOSURE_KEY => '"',
@@ -227,8 +228,8 @@ private function flatten(iterable $array, array &$result, string $keySeparator,
227228
if (is_iterable($value)) {
228229
$this->flatten($value, $result, $keySeparator, $parentKey.$key.$keySeparator, $escapeFormulas);
229230
} else {
230-
if ($escapeFormulas && \in_array(substr((string) $value, 0, 1), $this->formulasStartCharacters, true)) {
231-
$result[$parentKey.$key] = "\t".$value;
231+
if ($escapeFormulas && \in_array(substr((string) $value, 0, 1), self::FORMULAS_START_CHARACTERS, true)) {
232+
$result[$parentKey.$key] = "'".$value;
232233
} else {
233234
// Ensures an actual value is used when dealing with true and false
234235
$result[$parentKey.$key] = false === $value ? 0 : (true === $value ? 1 : $value);

src/Symfony/Component/Serializer/Tests/Encoder/CsvEncoderTest.php

Lines changed: 77 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -257,31 +257,52 @@ public function testEncodeFormulas()
257257

258258
$this->assertSame(<<<'CSV'
259259
0
260-
" =2+3"
260+
'=2+3
261261

262262
CSV
263263
, $this->encoder->encode(['=2+3'], 'csv'));
264264

265265
$this->assertSame(<<<'CSV'
266266
0
267-
" -2+3"
267+
'-2+3
268268

269269
CSV
270270
, $this->encoder->encode(['-2+3'], 'csv'));
271271

272272
$this->assertSame(<<<'CSV'
273273
0
274-
" +2+3"
274+
'+2+3
275275

276276
CSV
277277
, $this->encoder->encode(['+2+3'], 'csv'));
278278

279279
$this->assertSame(<<<'CSV'
280280
0
281-
" @MyDataColumn"
281+
'@MyDataColumn
282282

283283
CSV
284284
, $this->encoder->encode(['@MyDataColumn'], 'csv'));
285+
286+
$this->assertSame(<<<'CSV'
287+
0
288+
"' tab"
289+
290+
CSV
291+
, $this->encoder->encode(["\ttab"], 'csv'));
292+
293+
$this->assertSame(<<<'CSV'
294+
0
295+
"'=1+2"";=1+2"
296+
297+
CSV
298+
, $this->encoder->encode(['=1+2";=1+2'], 'csv'));
299+
300+
$this->assertSame(<<<'CSV'
301+
0
302+
"'=1+2'"" ;,=1+2"
303+
304+
CSV
305+
, $this->encoder->encode(['=1+2\'" ;,=1+2'], 'csv'));
285306
}
286307

287308
public function testDoNotEncodeFormulas()
@@ -313,13 +334,34 @@ public function testDoNotEncodeFormulas()
313334

314335
CSV
315336
, $this->encoder->encode(['@MyDataColumn'], 'csv'));
337+
338+
$this->assertSame(<<<'CSV'
339+
0
340+
" tab"
341+
342+
CSV
343+
, $this->encoder->encode(["\ttab"], 'csv'));
344+
345+
$this->assertSame(<<<'CSV'
346+
0
347+
"=1+2"";=1+2"
348+
349+
CSV
350+
, $this->encoder->encode(['=1+2";=1+2'], 'csv'));
351+
352+
$this->assertSame(<<<'CSV'
353+
0
354+
"=1+2'"" ;,=1+2"
355+
356+
CSV
357+
, $this->encoder->encode(['=1+2\'" ;,=1+2'], 'csv'));
316358
}
317359

318360
public function testEncodeFormulasWithSettingsPassedInContext()
319361
{
320362
$this->assertSame(<<<'CSV'
321363
0
322-
" =2+3"
364+
'=2+3
323365

324366
CSV
325367
, $this->encoder->encode(['=2+3'], 'csv', [
@@ -328,7 +370,7 @@ public function testEncodeFormulasWithSettingsPassedInContext()
328370

329371
$this->assertSame(<<<'CSV'
330372
0
331-
" -2+3"
373+
'-2+3
332374

333375
CSV
334376
, $this->encoder->encode(['-2+3'], 'csv', [
@@ -337,7 +379,7 @@ public function testEncodeFormulasWithSettingsPassedInContext()
337379

338380
$this->assertSame(<<<'CSV'
339381
0
340-
" +2+3"
382+
'+2+3
341383

342384
CSV
343385
, $this->encoder->encode(['+2+3'], 'csv', [
@@ -346,12 +388,39 @@ public function testEncodeFormulasWithSettingsPassedInContext()
346388

347389
$this->assertSame(<<<'CSV'
348390
0
349-
" @MyDataColumn"
391+
'@MyDataColumn
350392

351393
CSV
352394
, $this->encoder->encode(['@MyDataColumn'], 'csv', [
353395
CsvEncoder::ESCAPE_FORMULAS_KEY => true,
354396
]));
397+
398+
$this->assertSame(<<<'CSV'
399+
0
400+
"' tab"
401+
402+
CSV
403+
, $this->encoder->encode(["\ttab"], 'csv', [
404+
CsvEncoder::ESCAPE_FORMULAS_KEY => true,
405+
]));
406+
407+
$this->assertSame(<<<'CSV'
408+
0
409+
"'=1+2"";=1+2"
410+
411+
CSV
412+
, $this->encoder->encode(['=1+2";=1+2'], 'csv', [
413+
CsvEncoder::ESCAPE_FORMULAS_KEY => true,
414+
]));
415+
416+
$this->assertSame(<<<'CSV'
417+
0
418+
"'=1+2'"" ;,=1+2"
419+
420+
CSV
421+
, $this->encoder->encode(['=1+2\'" ;,=1+2'], 'csv', [
422+
CsvEncoder::ESCAPE_FORMULAS_KEY => true,
423+
]));
355424
}
356425

357426
public function testEncodeWithoutHeader()

0 commit comments

Comments
 (0)