Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow users to change cookieNameStrict in configuration #189

Open
wants to merge 22 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ jobs:
strategy:
matrix:
os: [ubuntu-latest, windows-latest, macOS-latest]
php: ['7.1', '7.2', '7.3', '7.4', '8.0']
php: ['7.2', '7.3', '7.4', '8.0']
# sapi: ['php', 'php-cgi']

fail-fast: false
Expand Down Expand Up @@ -38,7 +38,7 @@ jobs:
- uses: actions/checkout@v2
- uses: shivammathur/setup-php@v2
with:
php-version: 7.1
php-version: 7.2
coverage: none
extensions: fileinfo, intl

Expand Down
1 change: 0 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
language: php
php:
- 7.1
- 7.2
- 7.3
- 7.4
Expand Down
2 changes: 1 addition & 1 deletion appveyor.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ install:
# Install PHP
- IF EXIST c:\php (SET PHP=0) ELSE (mkdir c:\php)
- IF %PHP%==1 cd c:\php
- IF %PHP%==1 curl https://windows.php.net/downloads/releases/archives/php-7.1.0-Win32-VC14-x64.zip --output php.zip
- IF %PHP%==1 curl https://windows.php.net/downloads/releases/archives/php-7.2.28-Win32-VC15-x64.zip --output php.zip
- IF %PHP%==1 7z x php.zip >nul
- IF %PHP%==1 echo extension_dir=ext >> php.ini
- IF %PHP%==1 echo extension=php_openssl.dll >> php.ini
Expand Down
4 changes: 2 additions & 2 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
}
],
"require": {
"php": ">=7.1 <8.1",
"php": ">=7.2 <8.1",
"nette/utils": "^3.1"
},
"require-dev": {
Expand All @@ -42,7 +42,7 @@
},
"extra": {
"branch-alias": {
"dev-master": "3.0-dev"
"dev-master": "3.1-dev"
}
}
}
2 changes: 1 addition & 1 deletion readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ Installation:
composer require nette/http
```

It requires PHP version 7.1 and supports PHP up to 8.0.
It requires PHP version 7.2 and supports PHP up to 8.0.


HTTP Request
Expand Down
10 changes: 6 additions & 4 deletions src/Bridges/HttpDI/HttpExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,8 @@ public function getConfigSchema(): Nette\Schema\Schema
'csp' => Expect::arrayOf('array|scalar|null'), // Content-Security-Policy
'cspReportOnly' => Expect::arrayOf('array|scalar|null'), // Content-Security-Policy-Report-Only
'featurePolicy' => Expect::arrayOf('array|scalar|null'), // Feature-Policy
'cookieSecure' => Expect::anyOf(null, true, false, 'auto'), // true|false|auto Whether the cookie is available only through HTTPS
'cookieSecure' => Expect::anyOf(null, true, false, 'auto')->default('auto'), // true|false|auto Whether the cookie is available only through HTTPS
'cookieNameStrict' => Expect::anyOf(Expect::string(), Expect::bool(), null)->default('nette-samesite'),
]);
}

Expand All @@ -55,7 +56,8 @@ public function loadConfiguration()
->addSetup('setProxy', [$config->proxy]);

$builder->addDefinition($this->prefix('request'))
->setFactory('@Nette\Http\RequestFactory::fromGlobals');
->setFactory('@Nette\Http\RequestFactory::fromGlobals')
->addSetup('setCookieNameStrict', [$config->cookieNameStrict]);

$response = $builder->addDefinition($this->prefix('response'))
->setFactory(Nette\Http\Response::class);
Expand Down Expand Up @@ -121,8 +123,8 @@ private function sendHeaders()
}

$this->initialization->addBody(
'Nette\Http\Helpers::initCookie($this->getService(?), $response);',
[$this->prefix('request')]
'Nette\Http\Helpers::initCookie($this->getService(?), $response, ?);',
[$this->prefix('request'), $config->cookieNameStrict]
);
}

Expand Down
4 changes: 1 addition & 3 deletions src/Http/FileUpload.php
Original file line number Diff line number Diff line change
Expand Up @@ -62,9 +62,7 @@ public function __construct(?array $value)


/**
* Returns the original file name as submitted by the browser. Do not trust the value returned by this method.
* A client could send a malicious filename with the intention to corrupt or hack your application.
* Alias for getUntrustedName()
* @deprecated use getUntrustedName()
*/
public function getName(): string
{
Expand Down
6 changes: 3 additions & 3 deletions src/Http/Helpers.php
Original file line number Diff line number Diff line change
Expand Up @@ -52,10 +52,10 @@ public static function ipMatch(string $ip, string $mask): bool
}


public static function initCookie(IRequest $request, IResponse $response)
public static function initCookie(IRequest $request, IResponse $response, $cookieName = self::STRICT_COOKIE_NAME)
{
if (!$request->getCookie(self::STRICT_COOKIE_NAME)) {
$response->setCookie(self::STRICT_COOKIE_NAME, '1', 0, '/', null, null, true, 'Strict');
if (!$request->getCookie($cookieName)) {
$response->setCookie($cookieName, '1', 0, '/', null, null, true, 'Strict');
}
}
}
26 changes: 22 additions & 4 deletions src/Http/Request.php
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,9 @@ class Request implements IRequest
/** @var callable|null */
private $rawBodyCallback;

/** @var string */
private $cookieNameStrict;


public function __construct(
UrlScript $url,
Expand All @@ -81,6 +84,17 @@ public function __construct(
$this->remoteAddress = $remoteAddress;
$this->remoteHost = $remoteHost;
$this->rawBodyCallback = $rawBodyCallback;
$this->cookieNameStrict = Helpers::STRICT_COOKIE_NAME;
}


/**
* Setter for cookieNameStrict
* @param string $name
*/
public function setCookieNameStrict(string $name)
{
$this->cookieNameStrict = $name;
}


Expand Down Expand Up @@ -142,11 +156,15 @@ public function getPost(string $key = null)

/**
* Returns uploaded file.
* @return FileUpload|array|null
* @param string|string[] $key
* @return ?FileUpload
*/
public function getFile(string $key)
public function getFile($key)
{
return $this->files[$key] ?? null;
$res = Nette\Utils\Arrays::get($this->files, $key, null);
return $res instanceof FileUpload
? $res
: null;
}


Expand Down Expand Up @@ -249,7 +267,7 @@ public function isSecured(): bool
*/
public function isSameSite(): bool
{
return isset($this->cookies[Helpers::STRICT_COOKIE_NAME]);
return isset($this->cookies[$this->cookieNameStrict]);
}


Expand Down
5 changes: 4 additions & 1 deletion src/Http/RequestFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -163,8 +163,11 @@ private function getGetPostCookie(Url $url): array
$list[$key][$k] = $v;
$list[] = &$list[$key][$k];

} else {
} elseif (is_string($v)) {
$list[$key][$k] = (string) preg_replace('#[^' . self::CHARS . ']+#u', '', $v);

} else {
throw new Nette\InvalidStateException(sprintf('Invalid value in $_POST/$_COOKIE in key %s, expected string, %s given.', "'$k'", gettype($v)));
}
}
}
Expand Down
1 change: 1 addition & 0 deletions src/Http/Session.php
Original file line number Diff line number Diff line change
Expand Up @@ -466,6 +466,7 @@ public function setCookieParameters(
/** @deprecated */
public function getCookieParameters(): array
{
trigger_error(__METHOD__ . '() is deprecated.', E_USER_DEPRECATED);
return session_get_cookie_params();
}

Expand Down
16 changes: 10 additions & 6 deletions src/Http/Url.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,13 @@
* Mutable representation of a URL.
*
* <pre>
* scheme user password host port basePath relativeUrl
* | | | | | | |
* /--\ /--\ /------\ /-------\ /--\/--\/----------------------------\
* scheme user password host port path query fragment
* | | | | | | | |
* /--\ /--\ /------\ /-------\ /--\/------------\ /--------\ /------\
* http://john:x0y17575@nette.org:8042/en/manual.php?name=param#fragment <-- absoluteUrl
* \__________________________/\____________/^\________/^\______/
* | | | |
* authority path query fragment
* \______\__________________________/
* | |
* hostUrl authority
* </pre>
*
* @property string $scheme
Expand Down Expand Up @@ -316,19 +316,22 @@ public function getHostUrl(): string
}


/** @deprecated */
public function getBasePath(): string
{
$pos = strrpos($this->path, '/');
return $pos === false ? '' : substr($this->path, 0, $pos + 1);
}


/** @deprecated */
public function getBaseUrl(): string
{
return $this->getHostUrl() . $this->getBasePath();
}


/** @deprecated */
public function getRelativeUrl(): string
{
return substr($this->getAbsoluteUrl(), strlen($this->getBaseUrl()));
Expand Down Expand Up @@ -360,6 +363,7 @@ public function isEqual($url): bool
/**
* Transforms URL to canonical form.
* @return static
* @deprecated
*/
public function canonicalize()
{
Expand Down
3 changes: 2 additions & 1 deletion src/Http/UrlScript.php
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,8 @@ public function withPath(string $path, string $scriptPath = '')
{
$dolly = clone $this;
$dolly->scriptPath = $scriptPath;
return call_user_func([$dolly, 'parent::withPath'], $path);
$parent = \Closure::fromCallable([UrlImmutable::class, 'withPath'])->bindTo($dolly);
return $parent($path);
}


Expand Down
38 changes: 38 additions & 0 deletions tests/Http.DI/HttpExtension.sameSiteProtectionCustom.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<?php

declare(strict_types=1);

use Nette\Bridges\HttpDI\HttpExtension;
use Nette\DI;
use Tester\Assert;


require __DIR__ . '/../bootstrap.php';

if (PHP_SAPI === 'cli') {
Tester\Environment::skip('Headers are not testable in CLI mode');
}


$compiler = new DI\Compiler;
$compiler->addExtension('http', new HttpExtension);
$loader = new DI\Config\Loader;
$config = $loader->load(Tester\FileMock::create(<<<'EOD'
http:
cookieNameStrict: test-samesite
EOD
, 'neon'));

// protection is enabled by default
eval($compiler->addConfig($config)->compile());

$container = new Container;
$container->initialize();

$headers = headers_list();
Assert::contains(
PHP_VERSION_ID >= 70300
? 'Set-Cookie: test-samesite=1; path=/; HttpOnly; SameSite=Strict'
: 'Set-Cookie: test-samesite=1; path=/; SameSite=Strict; HttpOnly',
$headers
);
2 changes: 1 addition & 1 deletion tests/Http/Request.files.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -111,4 +111,4 @@ Assert::false(isset($request->files['file0']));
Assert::true(isset($request->files['file1']));

Assert::null($request->getFile('empty1'));
Assert::same([null], $request->getFile('empty2'));
Assert::null($request->getFile('empty2'));
34 changes: 34 additions & 0 deletions tests/Http/Request.invalidType.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<?php

/**
* Test: Nette\Http\Request invalid data.
*/

declare(strict_types=1);

use Nette\Http;
use Tester\Assert;


require __DIR__ . '/../bootstrap.php';


test('invalid POST', function () {
$_POST = [
'int' => 1,
];

Assert::exception(function () {
(new Http\RequestFactory)->fromGlobals();
}, Nette\InvalidStateException::class, 'Invalid value in $_POST/$_COOKIE in key \'int\', expected string, integer given.');
});


test('invalid COOKIE', function () {
$_POST = [];
$_COOKIE = ['x' => [1]];

Assert::exception(function () {
(new Http\RequestFactory)->fromGlobals();
}, Nette\InvalidStateException::class, 'Invalid value in $_POST/$_COOKIE in key \'0\', expected string, integer given.');
});