
Updated: Comment #2
Problem/Motivation
PathBasedBreadcrumbBuilder uses request matching of the routing system but does not catch all exception types that can be thrown. This can lead to highly obscure errors as the exception bubbles up to the main current request.
Proposed resolution
Catch the missing MethodNotAllowedException in PathBasedBreadcrumbBuilder::getRequestForPath(). We could even catch \Exception there, but is that a good idea?
Remaining tasks
Review the proposed patch.
User interface changes
none.
API changes
none.
Original report by linclark against REST module
If you try to DELETE a node using cookie auth without the CSRF token and no Accept/Content-Type header, the access system treats it like you are requesting an HTML page. It will thus pass the request through the rendering pipeline after the AccessDeniedHttpException is thrown. This results in an unhandled exception during exception handling.
Comment | File | Size | Author |
---|---|---|---|
#11 | 2180425.patch | 20.49 KB | klausi |
#8 | 2180425.patch | 17.71 KB | klausi |
#6 | interdiff.txt | 5.06 KB | dawehner |
#6 | breadcrumb-2180425-6.patch | 17.47 KB | dawehner |
#4 | interdiff.txt | 18.98 KB | dawehner |
Comments
Comment #1
klausiI could reproduce this with cURL from the command line:
Unfortunately I could not reproduce it with PHP in DeleteTest.php by setting "Accept:" in the cURL options, but I tested with hal_json there, maybe it triggers with json.
Comment #2
klausiAfter excessive debugging I found PathBasedBreadcrumbBuilder to be guilty. When the error page is rendered for "/entity/node/1" it tries to match the path to "/entity/node". But the route for that path is restricted to POST requests and is filtered out, which triggers a MethodNotAllowedException. That exception is not caught and breaks further page execution.
Comment #3
klausiklausi opened a new pull request for this issue.
Comment #4
dawehnerAfter just reviewing the patch and some work this ended with a lot of changes in the actual change + test the full class.
Comment #5
klausiGreat, you covered the whole class! I was just too lazy to do that, but this is of course amazing.
Wrong doc sentence, yes this was my fault.
Should probably also describe it as "mocked"?
same here.
Since that test case is identical to the 2 other exceptions we should only write this test code once and use a data provider for the different execption types.
Should be "a user path". You only use "an" if the word is pronounced with a vocal first.
And we still need to decide if we just want to catch \Exception or a useful parent class instead of all those exception types.
Comment #6
dawehnerMachines can't execute this logic :p
I kind of like your approach given that we still allow to have a lot of the unrelated exceptions going through, which is a good DX in general.
Good idea!
Comment #7
klausiThe test logic looks fine now and makes sense to me. Just some minor remaining nitpicks:
@return comment missing.
Comment is out of date now, sine arbitrary exceptions are tested.
Missing doc comment for @return, missing @see doc to point to the test method that uses this data provider.
This class should have a doc block what it is used for, i.e. only for testing purposes. I guess it is ok to not document the methods of the class, since they are only used for testing.
Comment #8
klausiklausi pushed some commits to the pull request.
For an interdiff please see the list of recent commits.
Comment #9
dawehnerGreat, just 6 clicks.
Is there any reason why we don't use proper constructor DI?
Comment #10
Crell CreditAttribution: Crell commentedOnce #9 is resolved I think this is RTBC, but I still don't grok how this situation is coming up in the first place. Protecting against it is nearly free though, so that's fine.
Comment #11
klausiklausi pushed some commits to the pull request.
For an interdiff please see the list of recent commits.
Comment #12
klausiImplemented constructor injection for the current user, interdiff: https://github.com/klausi/drupal/commit/e3b675a87ce4a6ecd07fa8b7512962c3...
Note that we still need the TestPathBasedBreadcrumbBuilder class for unit testing, because of the ->t() and ->l() methods. This class really has a bazillion of dependencies.
Comment #13
dawehnerGreat, thank you!
Comment #15
klausi11: 2180425.patch queued for re-testing.
Comment #16
klausiTest fail caused by a PHP segmentation fault on the testbot:
So I just pretend that I have not seen that, retesting now.
Comment #17
dawehnerIt is green again.
Comment #19
klausi11: 2180425.patch queued for re-testing.
Comment #20
klausiTests passing as expected, back to RTBC.
Comment #22
catchThis is a strange one, but the fix is reasonable and the extra coverage looks great. Committed/pushed to 8.x, thanks!