Skip to content

Commit

Permalink
Resolve errors in WebApplication for PHPStan Level 9 (#148)
Browse files Browse the repository at this point in the history
Fixes these two

```
 ------ ---------------------------------------------------------------------------------------------------------------
  Line   app/Application/WebApplication.php
 ------ ---------------------------------------------------------------------------------------------------------------
  30     Parameter #2 $actionName of method MichalSpacekCz\Http\SecurityHeaders::setCsp() expects string, mixed given.
  44     Part $uri (mixed) of encapsed string cannot be cast to string.
 ------ ---------------------------------------------------------------------------------------------------------------
```

by using
1. the new spaze/csp-config API
2. existing objects to get the URL to redirect to

For #147
  • Loading branch information
spaze authored Jun 13, 2023
2 parents 3beb733 + a59e652 commit 2b5286c
Show file tree
Hide file tree
Showing 15 changed files with 117 additions and 84 deletions.
16 changes: 6 additions & 10 deletions site/app/Application/WebApplication.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,17 @@

namespace MichalSpacekCz\Application;

use MichalSpacekCz\Http\ContentSecurityPolicy\CspValues;
use MichalSpacekCz\Http\SecurityHeaders;
use Nette\Application\Application;
use Nette\Application\Request;
use Nette\Application\UI\Presenter;
use Nette\Http\IRequest;
use Nette\Http\IResponse;

class WebApplication
{

public function __construct(
private readonly IRequest $httpRequest,
private readonly IResponse $httpResponse,
private readonly SecurityHeaders $securityHeaders,
private readonly Application $application,
Expand All @@ -24,11 +25,6 @@ public function __construct(
public function run(): void
{
$this->redirectToSecure();

$this->application->onRequest[] = function (Application $sender, Request $request): void {
$action = $request->getParameter(Presenter::ACTION_KEY) ?? Presenter::DEFAULT_ACTION;
$this->securityHeaders->setCsp($request->getPresenterName(), $action);
};
$this->application->onResponse[] = function (): void {
$this->securityHeaders->sendHeaders();
};
Expand All @@ -38,10 +34,10 @@ public function run(): void

private function redirectToSecure(): void
{
$uri = $_SERVER['REQUEST_URI'];
if ($_SERVER['HTTP_HOST'] !== $this->fqdn) {
$this->securityHeaders->setDefaultCsp()->sendHeaders();
$this->httpResponse->redirect("https://{$this->fqdn}{$uri}", IResponse::S301_MovedPermanently);
$this->securityHeaders->sendHeaders(CspValues::Default);
$url = $this->httpRequest->getUrl()->withScheme('https')->withHost($this->fqdn);
$this->httpResponse->redirect($url->getAbsoluteUrl(), IResponse::S301_MovedPermanently);
exit();
}
}
Expand Down
4 changes: 2 additions & 2 deletions site/app/Articles/Blog/BlogPostPreview.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
use MichalSpacekCz\Formatter\TexyFormatter;
use MichalSpacekCz\Training\Dates;
use Nette\Bridges\ApplicationLatte\DefaultTemplate;
use Spaze\ContentSecurityPolicy\Config;
use Spaze\ContentSecurityPolicy\CspConfig;

class BlogPostPreview
{
Expand All @@ -15,7 +15,7 @@ public function __construct(
private readonly TexyFormatter $texyFormatter,
private readonly BlogPosts $blogPosts,
private readonly Dates $trainingDates,
private readonly Config $contentSecurityPolicy,
private readonly CspConfig $contentSecurityPolicy,
) {
}

Expand Down
2 changes: 1 addition & 1 deletion site/app/Form/PostFormFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
use Nette\Forms\Controls\TextInput;
use Nette\Utils\Html;
use Nette\Utils\Json;
use Spaze\ContentSecurityPolicy\Config as CspConfig;
use Spaze\ContentSecurityPolicy\CspConfig;
use stdClass;

class PostFormFactory
Expand Down
12 changes: 12 additions & 0 deletions site/app/Http/ContentSecurityPolicy/CspValues.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?php
declare(strict_types = 1);

namespace MichalSpacekCz\Http\ContentSecurityPolicy;

enum CspValues
{

case Specific;
case Default;

}
39 changes: 16 additions & 23 deletions site/app/Http/SecurityHeaders.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,17 @@
namespace MichalSpacekCz\Http;

use MichalSpacekCz\Application\LocaleLinkGeneratorInterface;
use MichalSpacekCz\Http\ContentSecurityPolicy\CspValues;
use Nette\Application\Application;
use Nette\Application\UI\Presenter;
use Nette\Http\IRequest;
use Nette\Http\IResponse;
use Nette\Http\UrlImmutable;
use Spaze\ContentSecurityPolicy\Config;
use Spaze\ContentSecurityPolicy\CspConfig;

class SecurityHeaders
{

private string $presenterName;
private string $actionName;

/** @var array<string|string[]> */
private readonly array $permissionsPolicy;

Expand All @@ -25,7 +25,8 @@ class SecurityHeaders
public function __construct(
private readonly IRequest $httpRequest,
private readonly IResponse $httpResponse,
private readonly Config $contentSecurityPolicy,
private readonly Application $application,
private readonly CspConfig $contentSecurityPolicy,
private readonly LocaleLinkGeneratorInterface $localeLinkGenerator,
array $permissionsPolicy,
) {
Expand Down Expand Up @@ -68,13 +69,20 @@ public function getPermissionsPolicyHeader(): string
}


public function sendHeaders(): void
public function sendHeaders(CspValues $cspValues = CspValues::Specific): void
{
$header = $this->contentSecurityPolicy->getHeader($this->presenterName, $this->actionName);
if ($cspValues === CspValues::Specific) {
/** @var Presenter $presenter */
$presenter = $this->application->getPresenter();
$actionName = $presenter->getAction(true);
} else {
$actionName = $this->contentSecurityPolicy->getDefaultKey();
}
$header = $this->contentSecurityPolicy->getHeader($actionName);
if (!empty($header)) {
$this->httpResponse->setHeader('Content-Security-Policy', $header);
}
$header = $this->contentSecurityPolicy->getHeaderReportOnly($this->presenterName, $this->actionName);
$header = $this->contentSecurityPolicy->getHeaderReportOnly($actionName);
if ($header) {
$this->httpResponse->setHeader('Content-Security-Policy-Report-Only', $header);
}
Expand All @@ -83,21 +91,6 @@ public function sendHeaders(): void
}


public function setCsp(string $presenterName, string $actionName): self
{
$this->presenterName = $presenterName;
$this->actionName = $actionName;
return $this;
}


public function setDefaultCsp(): self
{
$this->presenterName = $this->actionName = $this->contentSecurityPolicy->getDefaultKey();
return $this;
}


/**
* Generates Access-Control-Allow-Origin header, if there's an Origin request header, and it matches any source link.
*
Expand Down
2 changes: 1 addition & 1 deletion site/app/Www/Presenters/PostPresenter.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
use MichalSpacekCz\Articles\Blog\BlogPosts;
use MichalSpacekCz\ShouldNotHappenException;
use MichalSpacekCz\Training\Dates;
use Spaze\ContentSecurityPolicy\Config as CspConfig;
use Spaze\ContentSecurityPolicy\CspConfig;

class PostPresenter extends BasePresenter
{
Expand Down
2 changes: 1 addition & 1 deletion site/composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
"nette/utils": "^4.0",
"paragonie/halite": "^5.1",
"php-parallel-lint/php-console-color": "^1.0.1",
"spaze/csp-config": "^3.0",
"spaze/csp-config": "^4.0",
"spaze/encryption": "^1.0",
"spaze/feed-exports": "^1.0",
"spaze/mysql-session-handler": "^2.3",
Expand Down
14 changes: 7 additions & 7 deletions site/composer.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion site/config/extensions.neon
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@ extensions:
sessionHandler: Spaze\Session\DI\MysqlSessionHandlerExtension
translation: Contributte\Translation\DI\TranslationExtension
nonceGenerator: Spaze\NonceGenerator\Bridges\Nette\GeneratorExtension
contentSecurityPolicy: Spaze\ContentSecurityPolicy\Bridges\Nette\ConfigExtension
contentSecurityPolicy: Spaze\ContentSecurityPolicy\Bridges\Nette\CspConfigExtension
subresourceIntegrity: Spaze\SubresourceIntegrity\Bridges\Nette\Extension
svgIcons: Spaze\SvgIcons\NetteExtension
22 changes: 19 additions & 3 deletions site/tests/Http/SecurityHeadersTest.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,10 @@ namespace MichalSpacekCz\Http;

use MichalSpacekCz\Test\Http\Response;
use MichalSpacekCz\Test\Http\SecurityHeadersFactory;
use Spaze\ContentSecurityPolicy\Config;
use Nette\Application\Application;
use Nette\Application\IPresenterFactory;
use Nette\Application\UI\Presenter;
use Spaze\ContentSecurityPolicy\CspConfig;
use Tester\Assert;
use Tester\TestCase;

Expand All @@ -18,8 +21,10 @@ class SecurityHeadersTest extends TestCase

public function __construct(
private readonly Response $httpResponse,
private readonly Config $cspConfig,
private readonly CspConfig $cspConfig,
private readonly SecurityHeadersFactory $securityHeadersFactory,
private readonly IPresenterFactory $presenterFactory,
private readonly Application $application,
) {
}

Expand Down Expand Up @@ -48,7 +53,18 @@ class SecurityHeadersTest extends TestCase
'https://example.com',
],
]);
$securityHeaders->setCsp('Foo', 'bar');

/** @var Presenter $presenter */
$presenter = $this->presenterFactory->createPresenter('Www:Homepage'); // Has to be a real presenter
/** @noinspection PhpInternalEntityUsedInspection */
$presenter->setParent(null, 'Foo'); // Set the name and also rename it
$presenter->changeAction('bar');
Assert::same(':Foo:bar', $presenter->getAction(true));
Assert::with($this->application, function () use ($presenter): void {
/** @noinspection PhpDynamicFieldDeclarationInspection $this is $this->application */
$this->presenter = $presenter;
});

$securityHeaders->sendHeaders();
$expected = [
'content-security-policy' => "script-src 'none' example.com; form-action 'self'",
Expand Down
14 changes: 7 additions & 7 deletions site/vendor/composer/installed.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 5 additions & 5 deletions site/vendor/composer/installed.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
'name' => 'spaze/michalspacek.cz',
'pretty_version' => 'dev-main',
'version' => 'dev-main',
'reference' => 'e552a20e643b1c9b07e6daca9fdac775d2265616',
'reference' => '3beb733213e45241a19880b1aab1a4fc3311e986',
'type' => 'project',
'install_path' => __DIR__ . '/../../',
'aliases' => array(),
Expand Down Expand Up @@ -406,9 +406,9 @@
'dev_requirement' => true,
),
'spaze/csp-config' => array(
'pretty_version' => 'v3.0.0',
'version' => '3.0.0.0',
'reference' => '4eb006ac43f4640a4bc1ba943cdc4f300d584aa0',
'pretty_version' => 'v4.0.0',
'version' => '4.0.0.0',
'reference' => 'f19c06347389a4b03558e0d3b6b61e1b4107c250',
'type' => 'library',
'install_path' => __DIR__ . '/../spaze/csp-config',
'aliases' => array(),
Expand All @@ -435,7 +435,7 @@
'spaze/michalspacek.cz' => array(
'pretty_version' => 'dev-main',
'version' => 'dev-main',
'reference' => 'e552a20e643b1c9b07e6daca9fdac775d2265616',
'reference' => '3beb733213e45241a19880b1aab1a4fc3311e986',
'type' => 'project',
'install_path' => __DIR__ . '/../../',
'aliases' => array(),
Expand Down
Loading

0 comments on commit 2b5286c

Please sign in to comment.