ignition icon indicating copy to clipboard operation
ignition copied to clipboard

Blocked by CSP package

Open liamkeily opened this issue 2 years ago • 9 comments

We are using https://github.com/spatie/laravel-csp on our app, which is causing ignition to display a blank page with the following console error.

Refused to execute inline script because it violates the following Content Security Policy directive

This seems to happen because the <script> and <style> tags injected by the Ignition package don't use csp_nonce().

liamkeily avatar Nov 07 '22 11:11 liamkeily

I can confirm that adding nonce="<?= csp_nonce(); ?>" to various places in https://github.com/spatie/ignition/blob/main/resources/views/errorPage.php got the error page displaying, although still a few wonky bits to chase down

I wonder if there's a simpler way to disable CSP for Ignition error pages

liamkeily avatar Nov 07 '22 11:11 liamkeily

Hi @liamkeily, thanks for reporting this! At first glance it seems like the CSP package is a bit too eager adding CSP headers to Ignition responses... I'm gonna leave this open for anyone else coming across this issue (and if anyone wants to submit a PR to either spatie/laravel-csp or this package, feel free!).

AlexVanderbist avatar Feb 28 '23 16:02 AlexVanderbist

I ended up solving this (and some other things) by extending https://github.com/spatie/laravel-csp/blob/main/src/Policies/Policy.php and implementing the following method

 public function shouldBeApplied(Request $request, Response $response): bool
    {
        if (config('app.debug') && (
            $response->isClientError() || // Ignition
            $response->isServerError() || // Ignition
            Vite::isRunningHot() || // Vite
            (config('telescope.enabled') && str_starts_with($request->path(), 'telescope')) // Telescope
        )) {
            return false;
        }

        return parent::shouldBeApplied($request, $response);
    }

Perhaps there's a better approach but it did the job

liamkeily avatar Feb 28 '23 17:02 liamkeily

Dear contributor,

because this issue seems to be inactive for quite some time now, I've automatically closed it. If you feel this issue deserves some attention from my human colleagues feel free to reopen it.

spatie-bot avatar Jun 29 '23 10:06 spatie-bot

I wish to re-open this hoping to find a better solution.

nelsonmelecio avatar Nov 03 '23 15:11 nelsonmelecio

I ended up adding another middleware to take care of nonce in every inline scripts tags in the errorPage.php. This will only be triggered when a response received either isClientError() or isServerError().

namespace App\Http\Middleware;

use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;

class AddNonceToInlineScripts
{
    public function handle($request, Closure $next)
    {
        $response = $next($request);

        if ($response->isClientError() || $response->isServerError()) {
            $content = $response->getContent();
            $nonce = csp_nonce();
            $contentWithNonce = $this->addNonceToInlineScripts($content, $nonce);

            $response->setContent($contentWithNonce);
        }

        return $response;
    }

    protected function addNonceToInlineScripts($content, $nonce)
    {
        return preg_replace_callback('/<script(.*?)>(.*?)<\/script>/is', function ($match) use ($nonce) {
            $attributes = $match[1];
            $scriptContent = $match[2];

            if (!str_contains($attributes, 'nonce')) {
                $attributes .= " nonce=\"$nonce\"";
            }

            return "<script$attributes>$scriptContent</script>";
        }, $content);
    }
}

Using a single nonce value for all script tags within a page is possible and is a valid approach to adhere to a Content Security Policy (CSP). In CSP, the nonce attribute is used to specify which scripts are allowed to execute based on a matching nonce value. If you use the same nonce attribute for all script tags, the CSP will consider them all as trusted and allow them to execute.

In this code, I can only generate one nonce value using csp_nonce() function (even calling it multiple times). Overall, it still works...

You can apply the same approach for inline style.

Hope it helps!

nelsonmelecio avatar Nov 03 '23 16:11 nelsonmelecio

Here is the change that integrates ignition with csp package - https://github.com/spatie/ignition/pull/462.

BR0kEN- avatar Nov 29 '23 14:11 BR0kEN-

an other temporary solution would be to bind Spatie\Ignition\Ignition class to a custom implementation via Laravel service container. Implementation should override renderException() method in order to use blade renderer so nonce="{{csp_nonce()}}" can be used on script/styles tags (make sure to create a blade template mimicking the one being used by ignition).

// in AppServiceProvider.php

public function register() {
  $this->app->bind(\Spatie\Ignition\Ignition::class, \App\YourCustomIgnition::class);
}

// in YourCustomIgnition.php

public function renderException(Throwable $throwable, ?Report $report = null): void
    {
        $this->setUpFlare();

        $report ??= $this->createReport($throwable);

        $viewModel = new ErrorPageViewModel(
            $throwable,
            $this->ignitionConfig,
            $report,
            $this->solutionProviderRepository->getSolutionsForThrowable($throwable),
            $this->solutionTransformerClass,
            $this->customHtmlHead,
            $this->customHtmlBody,
        );

        echo view('your.custom.template', ['viewModel' => $viewModel])->render();
        // or simply include your php file here utilizing $viewModel variable
        // include resource_path('your.custom.template');
    }

lexod avatar Jan 12 '24 05:01 lexod

I stopped with another one temporarily (the method of Spatie\Csp\Policies\Policy):

    public function shouldBeApplied(Request $request, Response $response): bool
    {
        if ((
            parent::shouldBeApplied($request, $response)
            && !$this->app->environment('testing')
            && !$request->isXmlHttpRequest()
        )) {
            // @todo PSSPP1-1793: Remove this processing when resolved.
            // https://github.com/spatie/ignition/pull/462
            if ((
                $response->isServerError()
                && ($content = $response->getContent())
                && \str_contains($content, '<script')
            )) {
                $response->setContent(
                    \preg_replace(
                        '/(<script)(.+?>)/usm',
                        "$1 nonce='".\csp_nonce()."'$2",
                        $content,
                    ),
                );
            }

            return true;
        }

        return false;
    }

BR0kEN- avatar Feb 02 '24 15:02 BR0kEN-

Hi @freekmurze why is it closed ? is there any solutions inside ignition ? or there should be used workarounds like https://github.com/spatie/ignition/issues/186#issuecomment-1888466267

oprudkyi avatar May 25 '24 10:05 oprudkyi