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

security-http (LogoutUrlGenerator) error #47577

Closed
krzyc opened this issue Sep 14, 2022 · 18 comments
Closed

security-http (LogoutUrlGenerator) error #47577

krzyc opened this issue Sep 14, 2022 · 18 comments

Comments

@krzyc
Copy link
Contributor

krzyc commented Sep 14, 2022

Symfony version(s) affected

4.4.44

Description

Method generateLogoutUrl of LogoutUrlGenerator throws "Call to a member function getBaseUrl() on null" error.
It can be reproduced by generating error after authenticating as a user. Error is not present after logging out.
Similar issue is described on #27174
On Symfony 6.1 this problem does not occur on same setup.
Tested using PHP 8.1 (Ubuntu supplied) with built in server.

How to reproduce

Install symfony/website-skeleton:"^4.4" with dev environment
Add db based authentication
Login
Generate error

Possible Solution

Checking for null request in LogoutUrlGenerator - there was previously supplied PR which fixes this problem:
#27175
It was rejected because problem was not reproduced (or should not exist).
But I think that in any case if called function can return null then it should be checked against null before calling its methods.

Additional Context

Call to a member function getBaseUrl() on null
Exception Stack Trace
Error
in vendor/symfony/security-http/Logout/LogoutUrlGenerator.php (line 110)
in vendor/symfony/security-http/Logout/LogoutUrlGenerator.php -> generateLogoutUrl (line 64)
in vendor/symfony/security-bundle/DataCollector/SecurityDataCollector.php -> getLogoutPath (line 136)
in vendor/symfony/http-kernel/Profiler/Profiler.php -> collect (line 178)
in vendor/symfony/http-kernel/EventListener/ProfilerListener.php -> collect (line 100)
in vendor/symfony/event-dispatcher/Debug/WrappedListener.php -> onKernelResponse (line 126)
in vendor/symfony/event-dispatcher/EventDispatcher.php -> __invoke (line 264)
in vendor/symfony/event-dispatcher/EventDispatcher.php -> doDispatch (line 239)
in vendor/symfony/event-dispatcher/EventDispatcher.php -> callListeners (line 73)
in vendor/symfony/event-dispatcher/Debug/TraceableEventDispatcher.php -> dispatch (line 168)
in vendor/symfony/http-kernel/HttpKernel.php -> dispatch (line 192)
in vendor/symfony/http-kernel/HttpKernel.php -> filterResponse (line 245)
in vendor/symfony/http-kernel/HttpKernel.php -> handleThrowable (line 115)
in vendor/symfony/http-kernel/EventListener/DebugHandlersListener.php -> terminateWithException (line 129)
in vendor/symfony/error-handler/ErrorHandler.php :: Symfony\Component\HttpKernel\EventListener{closure} (line 601)
ErrorHandler->handleException()

@xabbuh
Copy link
Member

xabbuh commented Sep 14, 2022

Can you create a small example application that allows to reproduce your issue?

@krzyc
Copy link
Contributor Author

krzyc commented Sep 14, 2022

@xabbuh I have created minimal app to show issue:
https://github.com/krzyc/symfony-issue-47577
Commited with SQLite db - username: admin password: admin

@specky-rum
Copy link

Hi, fwiw we're getting this error too. I'm getting the exact same error trace as the OP whenever there is a 500 error.

Some observations:

  • It only happens on our development version, not on production. I presume this is because we're not displaying the error pages on production. They are reported correctly to our error log though.
  • When this happens on our development version the original error is not logged.
  • It has been happening since we updated Symfony from 4.4.40 to 4.4.44.

@krzyc
Copy link
Contributor Author

krzyc commented Sep 15, 2022

@specky-rum Yes, this error happens in process of collecting data for displaying actual error in dev (Web Profiler) environment. SecurityDataCollector tries to get logout URL, but generator throws error because of no request in RequestStack. So there are two problems: why there is no request in RequestStack at this point (some time ago this problem did not exist so maybe it was introduced in some latest versions of Symfony), and why generateLogoutUrl is not checking for null value which is legal return value for getCurrentRequest so it should be handled in my opinion - then SecurityDataCollector will silently fetch thrown Exception as described in comments (fail silently when the logout URL cannot be generated), but only Exceptions are catched, not errors.

@fnagel
Copy link
Contributor

fnagel commented Sep 15, 2022

Can confirm this issue with symfony/symfony 4.4.45 too.

@mfreimann
Copy link

I too am experiencing the problem whenever an exception occurs.

@wouterj
Copy link
Member

wouterj commented Oct 2, 2022

Thanks to @krzyc's reproducer and git-bisect, I've been able to track this down to this change: #47358

This has been identified in another profiler related issue as well: #47405 (comment)

@Padam87
Copy link
Contributor

Padam87 commented Oct 3, 2022

Also having this issue with 6.1.5

6.1.3 works, thanks @wouterj

    "extra": {
        "symfony": {
            "allow-contrib": false,
            "require": "<=6.1.3"
        }
    }

@dehy
Copy link

dehy commented Oct 3, 2022

Having this exception in 5.4.13

Error:
Call to a member function getBaseUrl() on null

  at /home/adcnx/app-vendor/symfony/security-http/Logout/LogoutUrlGenerator.php:104
  at Symfony\Component\Security\Http\Logout\LogoutUrlGenerator->generateLogoutUrl()
     (/home/adcnx/app-vendor/symfony/security-http/Logout/LogoutUrlGenerator.php:65)
  at Symfony\Component\Security\Http\Logout\LogoutUrlGenerator->getLogoutPath()
     (/home/adcnx/app-vendor/symfony/security-bundle/DataCollector/SecurityDataCollector.php:122)
  at Symfony\Bundle\SecurityBundle\DataCollector\SecurityDataCollector->collect()
     (/home/adcnx/app-vendor/symfony/http-kernel/Profiler/Profiler.php:161)
  at Symfony\Component\HttpKernel\Profiler\Profiler->collect()
     (/home/adcnx/app-vendor/symfony/http-kernel/EventListener/ProfilerListener.php:108)
  at Symfony\Component\HttpKernel\EventListener\ProfilerListener->onKernelResponse()
     (/home/adcnx/app-vendor/symfony/event-dispatcher/Debug/WrappedListener.php:117)
  at Symfony\Component\EventDispatcher\Debug\WrappedListener->__invoke()
     (/home/adcnx/app-vendor/symfony/event-dispatcher/EventDispatcher.php:230)
  at Symfony\Component\EventDispatcher\EventDispatcher->callListeners()
     (/home/adcnx/app-vendor/symfony/event-dispatcher/EventDispatcher.php:59)
  at Symfony\Component\EventDispatcher\EventDispatcher->dispatch()
     (/home/adcnx/app-vendor/symfony/event-dispatcher/Debug/TraceableEventDispatcher.php:154)
  at Symfony\Component\EventDispatcher\Debug\TraceableEventDispatcher->dispatch()
     (/home/adcnx/app-vendor/symfony/http-kernel/HttpKernel.php:186)
  at Symfony\Component\HttpKernel\HttpKernel->filterResponse()
     (/home/adcnx/app-vendor/symfony/http-kernel/HttpKernel.php:239)
  at Symfony\Component\HttpKernel\HttpKernel->handleThrowable()
     (/home/adcnx/app-vendor/symfony/http-kernel/HttpKernel.php:109)
  at Symfony\Component\HttpKernel\HttpKernel->terminateWithException()
     (/home/adcnx/app-vendor/symfony/http-kernel/EventListener/DebugHandlersListener.php:131)
  at Symfony\Component\HttpKernel\EventListener\DebugHandlersListener::Symfony\Component\HttpKernel\EventListener\{closure}()
     (/home/adcnx/app-vendor/symfony/error-handler/ErrorHandler.php:607)
  at Symfony\Component\ErrorHandler\ErrorHandler->handleException()                

@dehy
Copy link

dehy commented Oct 4, 2022

On 6.0.13

Error:
Call to a member function getBaseUrl() on null

  at /home/adcnx/app-vendor/symfony/security-http/Logout/LogoutUrlGenerator.php:97
  at Symfony\Component\Security\Http\Logout\LogoutUrlGenerator->generateLogoutUrl()
     (/home/adcnx/app-vendor/symfony/security-http/Logout/LogoutUrlGenerator.php:60)
  at Symfony\Component\Security\Http\Logout\LogoutUrlGenerator->getLogoutPath()
     (/home/adcnx/app-vendor/symfony/security-bundle/DataCollector/SecurityDataCollector.php:114)
  at Symfony\Bundle\SecurityBundle\DataCollector\SecurityDataCollector->collect()
     (/home/adcnx/app-vendor/symfony/http-kernel/Profiler/Profiler.php:151)
  at Symfony\Component\HttpKernel\Profiler\Profiler->collect()
     (/home/adcnx/app-vendor/symfony/http-kernel/EventListener/ProfilerListener.php:108)
  at Symfony\Component\HttpKernel\EventListener\ProfilerListener->onKernelResponse()
     (/home/adcnx/app-vendor/symfony/event-dispatcher/Debug/WrappedListener.php:111)
  at Symfony\Component\EventDispatcher\Debug\WrappedListener->__invoke()
     (/home/adcnx/app-vendor/symfony/event-dispatcher/EventDispatcher.php:230)
  at Symfony\Component\EventDispatcher\EventDispatcher->callListeners()
     (/home/adcnx/app-vendor/symfony/event-dispatcher/EventDispatcher.php:59)
  at Symfony\Component\EventDispatcher\EventDispatcher->dispatch()
     (/home/adcnx/app-vendor/symfony/event-dispatcher/Debug/TraceableEventDispatcher.php:152)
  at Symfony\Component\EventDispatcher\Debug\TraceableEventDispatcher->dispatch()
     (/home/adcnx/app-vendor/symfony/http-kernel/HttpKernel.php:186)
  at Symfony\Component\HttpKernel\HttpKernel->filterResponse()
     (/home/adcnx/app-vendor/symfony/http-kernel/HttpKernel.php:239)
  at Symfony\Component\HttpKernel\HttpKernel->handleThrowable()
     (/home/adcnx/app-vendor/symfony/http-kernel/HttpKernel.php:109)
  at Symfony\Component\HttpKernel\HttpKernel->terminateWithException()
     (/home/adcnx/app-vendor/symfony/http-kernel/EventListener/DebugHandlersListener.php:125)
  at Symfony\Component\HttpKernel\EventListener\DebugHandlersListener::Symfony\Component\HttpKernel\EventListener\{closure}()
     (/home/adcnx/app-vendor/symfony/error-handler/ErrorHandler.php:541)
  at Symfony\Component\ErrorHandler\ErrorHandler->handleException()
     (/home/adcnx/app-vendor/sentry/sentry/src/ErrorHandler.php:357)
  at Sentry\ErrorHandler->handleException()   

@SimonVanacco
Copy link

For those on the 5.4.x branch, restricting to 5.4.12 works !

  "extra": {
        "symfony": {
            "allow-contrib": false,
            "require": "<=5.4.12"
        }
    }

(thanks @Padam87 for the idea)

@wouterj
Copy link
Member

wouterj commented Oct 6, 2022

Great to see so many people have found this issue, it means we have many candidates to work on a fix :)

In the other issue, a more in-depth search has been done and the root cause has been found: #47405 (comment) (the root cause being that there is no request while collecting data).

Anyone up for a PR to fix the issue?

@andyexeter
Copy link
Contributor

andyexeter commented Oct 12, 2022

@wouterj the SecurityDataCollector already wraps the call to getLogoutPath in a try/catch for any Exception:

$logoutUrl = null;
try {
$logoutUrl = $this->logoutUrlGenerator?->getLogoutPath();
} catch (\Exception) {
// fail silently when the logout URL cannot be generated
}

Changing this to catch any Throwable resolves the issue for me:

$logoutUrl = null;
try {
    $logoutUrl = $this->logoutUrlGenerator?->getLogoutPath();
} catch (\Throwable) {
    // fail silently when the logout URL cannot be generated
}

Would this suffice as a PR if I submitted it?

@krzyc
Copy link
Contributor Author

krzyc commented Oct 12, 2022

@wouterj Personally I think that generating proper exception is cleaner then intercepting error - there is a PR already submitted: https://github.com/symfony/symfony/pull/27175/files
For me not checking for null value, which is legal return value (even if it should not be null at that time), is not correct independently from a problem why there is no request - but this PR was rejected.
@xabbuh What do you think about it? Why we should not check for null request value in generateLogoutUrl? Throwing LogicException looks good to me.

@xabbuh
Copy link
Member

xabbuh commented Oct 12, 2022

Adding the null check works as a first step to me. But we should IMO still fix the underlying issue as currently if I understand this correctly the collector cannot collect all data that it is supposed to be able to collect.

@curry684
Copy link
Contributor

For those that, like me, end up in a situation where this is hiding exceptions but you cannot downgrade without breaking other features, just add some code directly in vendor/symfony/security-http/Logout/LogoutUrlGenerator at line 97 (for 6.1.5):

            $request = $this->requestStack->getCurrentRequest();

            // These lines fix it until your next Composer update
            if (!$request) {
                return 'nobody cares';
            }
            $url = UrlGeneratorInterface::ABSOLUTE_URL === $referenceType ? $request->getUriForPath($logoutPath) : $request->getBaseUrl().$logoutPath;

@krzyc
Copy link
Contributor Author

krzyc commented Oct 13, 2022

Adding the null check works as a first step to me. But we should IMO still fix the underlying issue as currently if I understand this correctly the collector cannot collect all data that it is supposed to be able to collect.

This problem originates in terminateWithException of HttpKernel. Pushing request to requestStack before calling handleThrowable (https://github.com/symfony/http-kernel/blob/6.1/HttpKernel.php#L109) in terminateWithException function fixes problem. @xabbuh Do you think this is correct solution? (I have submitted a PR). It is called by exceptionHandler from DebugHandlersListener.

Before cited commit #47358 requestStack was not empty because it was popped in finishRequest.

nicolas-grekas added a commit that referenced this issue Oct 18, 2022
… exception (krzyc)

This PR was squashed before being merged into the 4.4 branch.

Discussion
----------

[HttpKernel] Fix empty request stack when terminating with exception

| Q             | A
| ------------- | ---
| Branch?       | 4.4, 5.4, 6.0, 6.1, 6.2
| Bug fix?      | yes
| New feature?  | no
| Deprecations? | no
| Tickets       | Fix #47577
| License       | MIT
| Doc PR        |

After #47358 the RequestStack is empty when request terminates with exception, which prevents SecurityDataCollector to generate logout URL and generates fatal error.

Commits
-------

e4d6e7b [HttpKernel] Fix empty request stack when terminating with exception
@fnagel
Copy link
Contributor

fnagel commented Oct 21, 2022

For those on the 4.4.x branch, restricting to 4.4.44 works!

  "extra": {
        "symfony": {
            "allow-contrib": false,
            "require": "<=4.4.44"
        }
    }

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests