Problem/Motivation
We want to not allow people either via REST or UI to link to a URL they don't have access to, because this would result in some form of information disclosure,
for example via the URL alias.
Proposed resolution
Move the current access related validation logic from the form into a new access validation constraint.
Remaining tasks
None.
User interface changes
None.
API changes
None.
Original report by @dawehner
| Comment | File | Size | Author |
|---|---|---|---|
| #67 | 2419693-67.patch | 12.43 KB | upchuk |
| #67 | interdiff-67.txt | 1.45 KB | upchuk |
| #65 | 2419693-65.patch | 12.51 KB | upchuk |
| #65 | interdiff-65.txt | 3.28 KB | upchuk |
| #64 | 2419693-64.patch | 12.39 KB | upchuk |
Comments
Comment #1
upchuk commentedWill take a stab at it.
D
Comment #2
upchuk commentedHere's a patch, no tests yet (not sure if any are needed).
One question: the error message of the `LinkTypeConstraint` plugin fits perfectly well with the error message for the new Access constraint so I copied it from there. Should we not adjust it in `LinkTypeConstraint` to better reflect the validation error? Not sure why this:
or you do not have access to itis in it :)D
Comment #4
upchuk commentedComment #5
effulgentsia commentedThat's a UX goal rather than a security one. And core's only link widget already provides this desired UX. So this isn't fixing a bug or addressing security. But, it is a good code move that will make it easier for contrib widgets and REST services to have the desired UX by default, so recategorizing as a Major task.
Comment #6
upchuk commentedOne of the reasons of the fail in the interdiff :)
Starting the bot for the other ones because I noticed something weird when I was doing partial local testing. While it was testing validation of links, at some point it was using the
urlinput element instead of textfield with an internal URI. According to the widget this shouldn't happen...D
Comment #8
upchuk commentedAlright, found the problem. When I stripped the widget of the validation method, I also removed its call from the `#element_validate` render key. This means that the default Url validation kicked in before the constrain and that provides a different error message. I replaced it with an empty array so the constraint is used instead.
Comment #9
upchuk commentedComment #10
dawehnerIt would be great to expand the test coverage of the LinkFieldTest with an API only test checking that the validation is also fired.
Comment #11
yched commentedre #8:
Would be worth a comment then :-)
Also, minor : [] rather than array() ?
Comment #12
upchuk commented@dawehner Will do. My next step was to try and see how I can create the entities through the API to test the constraint.
@yched Totally right. About the array, I was wondering myself :)
D
Comment #13
upchuk commentedYeah, there is a problem with the validation using the API..
The problem is the following: when creating entities using the link field and through a form widget, there is a value massage method that calls the
getUserEnteredStringAsUrimethod to transform a schemaless URI into the defaultuser-pathschema. The problem with that is that when you create a simple entity like so:...the massaging doesn't happen and the Url::fromUri method throws an exception. And this means that entities using the link field and which do not actively implement some sort of hook to transform schemaless URI's into the default schema, will be broken for creation through the API.
Any thoughts on where best to implement this value massaging?
D
Comment #14
upchuk commentedI actually saw inside a menu_link_content test this entity_create usage:
So yeah, obviously you can prefix internal paths such as aliases with the schema. But I;m not sure it's a good thing. Why are we asking the "API user" to provide more information than the "form user"? And not to mention the dependency it puts on knowing whether the schema is necessary or not. Is there no way we can make this happen somewhere on entity_create level?
Something like this maybe? :)
This way we can also remove this from the form.
Comment #15
yched commentedThis is 100% legit. When you work on an entity at the api level, you work with raw values, the same you would read from an $entity loaded from db.
Then widgets provide UX, that doesnt affect the shape of the values when working with entities in code.
Comment #16
upchuk commentedAlrighty.
Here is the updated test to include also API level validation.
Comment #17
upchuk commentedDon't mind the patches in the previous comment, there are some obvious testing mistakes in it :)
These should be better. The interdiff is from #8.
Comment #20
upchuk commentedYeah, turns out that #2417647: Add leading slash to paths within 'user-path:' URIs, to allow 'user-path:' URIs to point to the <none> route added a slash to the user-path scheme and I hadn't pulled since then :) Let's see if it works now.
Interdiff still from #8;
Comment #22
dawehnerAwesome, this looks really great!
Can we count the expected amount of failures?
What about using String::format() here?
Comment #23
upchuk commentedHey there,
Counting the failure is tricky because I noticed a different number of fails for the different invalid URIs.
if I create an entity with an invalid URI in the link field I get 3 violations (2 from the 2 constraints of the Link module and one from the PrimitiveTypeConstraint). In the test however, sometimes it's only one, sometimes all 3...
I corrected the expectation message to use String::format() in the meantime. What do you suggest about the first issue? Is it important to know the number of constraints?
Comment #24
dawehnerWell, there should be a deterministic result of the amount of validation errors, shouldn't there?
Comment #25
wim leersLet's instead just remove
#element_validatealtogether.Either s/uri/URI/, or just s/uri//.
Why not delete the first line and do
$value->getUrl()on the second line?What exactly can throw that exception? We didn't have that before.
You could also have used double quotes, then there is no need for the escaping. But, that doesn't really matter.
Missing trailing period.
Should have a blank line before it.
But actually, I don't think we want any of the changes to
LinkFieldTest, because that is an integration test. I think we want a unit test similar to\Drupal\Tests\Core\Validation\Plugin\Validation\Constraint\PrimitiveTypeConstraintValidatorTest.Comment #26
upchuk commentedAs discussed with @Wim Leers, the following actions will be taken:
1. Leave as it is but add comment to reflect the need to keep #element_validate as an empty array
2, 3. Make the corrections
4. Exception being thrown by the Url::fromUri method so try..catch needs to stay
5,6,7. Make the corrections
Additionally, will need to create a unit test for LinkAccessConstraint which also means injecting the current user into the constraint class in order to mock it.
Another additionally, as discussed with @dawehner, will need to change the error message of LinkTypeConstraint to better reflect what it does. Integration tests for this constraint will follow in another issue.
Comment #27
upchuk commentedJust triggering the bot to see if anything fails..
Comment #29
wim leersLet's change it to
[].Why split this up in separate classes? What's the gain?
Comment #30
upchuk commentedHey @Wim,
The validator had to be split from the constraint in order for the injection to work. The constraint is a plugin and gets built with the ContainerFactoryPluginInterface and the validator can implement the ContainerInjectionInterface. If they are both in one class, then they will get built twice with 2 different
create()methods.D
Comment #31
wim leersI see :) Funky restriction, but seems legit. Thanks for the explanation!
Comment #32
upchuk commentedAlright, starting from scratch basically with
LinkAccessConstraintand separate validator class that focus only on the access aspect. Rest remains inside theLinkWidgetfor nowLinkTypeConstraintto better reflect what actually it doesUnit tests are coming as well.
Comment #34
upchuk commentedTrying again with more consolidated non-access related error messages.
Comment #36
upchuk commentedAnother test, hopefully this should pass :)
Comment #38
upchuk commentedHere we go again. This time there is a unit test for the
LinkAccessConstrainValidator.If test is green, the whole patch can be reviewed manually.
Comment #39
upchuk commentedLeft 'validation2' by accident. Will fix it after manual review if the automated test passes.
Comment #40
wim leersLooking close! :)
I'm not sure if we want to change the messaging here, because it amounts to a subtle information disclosure.
I think we'll have to keep the messages the same.
@dawehner, @effulgentsia, please confirm.
Nit: let's swap these two, then all "use" statements are ordered alphabetically.
Needs fully qualified name.
Capital D.
Nit: blank line between last method's closing brace and the class' closing brace.
Nit: No blank line here.
s/true/TRUE/
s/false/FALSE/
s/URL/Url/, because we're referring to a specific classname.
Let's add a
->with()line to clarify which permission this is mocking.I think "user_admin" may be a bit confusing, what about "may_link_any_page", or something like that?
Comment #41
upchuk commentedHey Wim, thanks for the review.
Will make all the changes.
Regarding the first one (changing the message): I changed the messaging because it doesn't reflect reality (doesn't do any sort of access check) and that exact message fits perfectly with the new
LinkAccessConstraint. Additionally, since integration testing is not possible by checking the type of constraint that added a violation, we have to rely on the message it throws (still don't know why that is though :) ). This is yet another reason why the two messages need to be separate. You'll notice I also changed it in theLinkTypeConstraint. This I discussed already with @dawehner, maybe you remember :)D
Comment #42
wim leersDid you discuss the information disclosure aspect with dawehner? I don't remember, sorry. (Too many things happening at the same time in the whole menu links/URI set of issues.)
Comment #43
dawehner@Wim Leers
Given that you can also just use the HTTP level to determine whether something exists or not, I think its fine to show a different message.
In case someone wants to change that and set 404==403 its already required to write some code, so this would be just additional effort.
Comment #44
wim leersAh, right, that was our conclusion.
Then please ignore #40.1! :)
Comment #45
upchuk commentedHere we go. Hope it still passes :)
Let me know if you spot any other issues.
Comment #46
wim leersLooks perfect.
Comment #47
dawehnerMh okay, let's adapt the issue summary
Comment #49
upchuk commentedAlright, here it is, a small error creeped in that was caught in the meantime by #2418237: Fix incorrect @covers in PHPUnit tests I guess.
Comment #50
wim leersBack to RTBC.
Comment #51
dawehner+1
Comment #52
amateescu commentedA lowercase false creeped in here, can be fixed on commit though.
Comment #53
alexpottI think this message should be
The path '@uri' is inaccessible.'. We already have the minor information disclosure.Comment #54
upchuk commentedHere we go. Hope it still passes :)
Comment #56
upchuk commentedHere we go again :)
Comment #57
dawehnerHa, we even have test coverage for the string, good to know!
Comment #58
alexpottI think this constraint should have no opinion if the url is not valid. So something like this:
Comment #59
alexpottThere is consensus that this methods are called provider and have a doc block that points to the method(s) they provide data for.
Comment #60
upchuk commentedHey there,
Made the changes. Additionally, I also added some docblock info to the test class and method.
Comment #62
upchuk commentedThis was the problem. Fixed now by changing to
providerValidate(to match the changed method name)Comment #63
dawehnerSadly sadly the documentation standard says we document those lines as wlel.
can't you remove the continue and replace it with an else?
Comment #64
upchuk commentedHere you go.
Comment #65
upchuk commentedHere we go @YesCT.
Comment #66
upchuk commentedComment #67
upchuk commentedHere we are.
Comment #68
yesct commentedlooks like the concerns have been addressed. (and some nits we discussed in irc)
Comment #69
webchickAlex Pott has been all up in this issue's bizness, so assigning to him.
Comment #70
alexpottThis issue addresses a major bug and is allowed per https://www.drupal.org/core/beta-changes. Committed 9d4d62d and pushed to 8.0.x. Thanks!
Comment #72
wim leersUpchuk++ — thanks! :)