Problem/Motivation
The link widget seems to rely purely on native browser side validation for checking the validity of external URLs. It is possible to enter any input after the URL scheme or nothing but the scheme. Any of the following inputs passes validation:
https:https://this is some bad inputhttps://www.google.com, https://www.bing.com
Steps to replicate:
- Add a link field on the "Article" node type with the option "Allowed link type" set to "External links only".
- Create an article, enter "http:" or "irc:" for the URL, and submit the form.
- Result: the invalid URL is accepted.
This was originally reported by idimopoulos.
Proposed resolution
There are two proposals
1) Add validation for punycode and magnet links in /core/modules/link/src/Plugin/Validation/Constraint/LinkExternalProtocolsConstraintValidator
or
2) Use the Symfony Url Validator, #34. This was proposed 6 years ago in #295021: Expand/augment UrlHelper::isValid() to improve correctness and handle more URL types and 4 years ago in #2691099: Improve external URL validation in many ways
Remaining tasks
Choose a proposed resolution and if the 1) then decide if these changes should be in UrlHelper See #21
User interface changes
API changes
Data model changes
Release notes snippet
| Comment | File | Size | Author |
|---|---|---|---|
| #6 | http_external_link_accepted_success.png | 160.79 KB | kavithad |
| #6 | irc_external_link_accepted_success.png | 161.42 KB | kavithad |
| #6 | Link_field_configuration.png | 158.13 KB | kavithad |
| #6 | Article_contenttype_fields.png | 162.72 KB | kavithad |
Issue fork drupal-2652236
Show commands
Start within a Git clone of the project using the version control instructions.
Or, if you do not have SSH keys set up on git.drupalcode.org:
Comments
Comment #2
pfrenssenComment #3
pfrenssenComment #4
pfrenssenComment #5
kavithad commentedComment #6
kavithad commentedFollowed the steps mentioned in the issue description to reproduce the issue. However the reported issue seems to be working fine in Drupal 8.x.0. Attached the screenshots for reference.
Comment #7
pfrenssen@kavithad, thanks for testing!
However you seem to have tested with valid URLs like "irc://irc.server.org/channel", not the invalid URLs such as "irc:" that are mentioned in the issue summary.
Comment #8
swentel commentedRelated to #2573635: Url::fromUri() should accept protocol-relative URLs - although not entirely the same.
Comment #9
mac_weber commentedI added some code for it that also validates Punycode.
While working on it I realize this is a deeper problem in the class
UrlHelper, but we better solve it here first because that class will need some deeper changes in order to be a really good validation. For example, maybe taking help from SymfonyUrlValidatorand other improvements.Comment #10
mac_weber commentedI opened a new issue, as this validation maybe needs a bigger rewrite: #2691099: Improve external URL validation in many ways, but we can still apply this patch to fix this bug until the other issue is discussed.
Comment #12
mac_weber commentedMagnet links do not work with
parse_url($uri, PHP_URL_HOST);, they need to be parsed byparse_url($uri, PHP_URL_QUERY);Comment #13
dawehnerLet's ensure to expand the test coverage.
Comment #14
mac_weber commented@dawehner I added tests for Punycode (in UTF-8) and for some malformed URLs.
I could not add a malformed URL like
irc://because in this case there is an exception thrown by the fileUrl.phpat line 303:I could not manage to make the test understand it is expected this is a malformed URL. So it still needs a test for
irc://.Comment #15
mac_weber commentedin a chat with @dawehner at IRC, if I understand correctly, handling
irc://in these tests at the patch #14 would require changes in that class and this is out of scope of this issue.Then, marking patch #14 as 'needs review'.
Comment #18
mac_weber commentedremoved the test that we are not going to implement.
Comment #20
pmchristensen commentedLooks really good and very nice with a solution for better link validation. I have run the patch against 8.3.x which is the current Drupal 8 Core development and run phpunit test also. Everything is looking really good and therefore I have changed status. Lets get this patch ported right?
Comment #21
cilefen commentedThe comment seems superfluous because from the following line this is obvious. But maybe some people would like it.
This comment is superfluous.
This comment is pointless. There are other comments that serve no purpose.
This sentence doesn't make much sense.
Why use a relative protocol and https on consecutive lines?
I have read the comments but I am missing the point as to why these changes do not belong in UrlHelper itself. This issue summary needs a rewrite to explain what is actually going on with this patch. And I am wondering if the test coverage covers all paths.
Comment #23
pguillard commentedThis patch contains :
Comment #24
pguillard commentedAdded 2 tests for common protocol typos like htp:// and http:/
Comment #26
chenderson commentedGood work so far, however I am not sure what is trying to be achieved. Yes the patch will return invalid for irc: but you can still get http://invalid accepted. Is that meant to be the case? Feels like the task needs to be a bit clearer.
There is a small issue with double spacing after equals sign.
Also would it not make sense to return the violation if $is_invalid_protocol return true and save doing the additional checks thereafter?
Comment #27
dinesh18 commentedHere is an updated patch and interdiff which implemented as per #26
Comment #30
super_romeo commentedError:
@see http://php.net/manual/en/function.idn-to-ascii.php
Patch fixed.
Comment #34
ghost of drupal pastPerhaps using the Symfony Url validator could be useful? I just added it to our codebase , commit attached, it should be quite easy to adjust it for core. Things I had trouble with:
%value(UrlValidator::validatesets{{ value }}whichDrupalTranslator::processParameterstranslates to%value)LinkUriValidConstraintneeds to extendUrlotherwise we get a type error, it's not enough to extendConstraint.try-catchis necessary to avoid the errors caught by other validators throwing an exception.Comment #36
quietone commentedFound this issue while working on #3151047: Expand LinkWidget test coverage for the Bug Smash Initiative.
The next steps here is to decide on which of the proposed resolutions to implement. Setting to NR for that discussion.
Comment #37
jibranLet's start from #34.
Comment #39
quietone commentedDrupal\Tests\link\Functional\LinkFieldTest::testURLValidation is failing on the test of these internal links
And I suspect the other tests are failing on
$url3 = '#net';So, it is not playing nicely with internals that don't start with a '/'.Comment #40
ghost of drupal pastOh dear, if you are starting from there, you should use https://gist.github.com/chx/e6cf5783b76e3e9637e9d6b756f99cf6 it's much better.
Comment #41
quietone commentedThanks!
Here is a patch using that constraint.
Comment #43
ghost of drupal pastEdit: No idea what's going on here, just quickly added a
+ ['scheme' => '']to make that error go away. I am not going to work on this, I am afraid.Comment #44
quietone commentedWorking on fixing the tests.
Comment #47
vakulrai commentedAdding A patch for 9.2.x. Made few changes to the tests.
Please review.
Comment #48
vakulrai commentedFixed Cspell issues in #47 and adding inter diff.
Comment #50
logickal commentedPatch in #48 wasn't applying and had two PHPCS failures. Resolved and re-rolled.
Comment #51
logickal commentedPrevious patch was bogus, please ignore. Corrected patch attached.
Comment #53
ghost of drupal pastThanks for your work on this. When $url becomes empty you get a notice, so I'd recommend wrapping
if (!$url = $urlObject->setAbsolute()->toString()) { return; }like this. As I said before, Smartsheet runs this in production, so we already hit the bugs :)Comment #54
meenakshi_j commentedFixed the fails of #51 and wrapped $urlObject->setAbsolute()->toString() the line.
Comment #55
quietone commentedStarted a review and immediately made a missing interdiff. Looking at it there are out of scope changes as well as changes to a test, which should not be happening. So, I made a new patch.
What I am confused about is that now these two URLs are considered invalid
But the page they came from states they are valid.
Comment #57
gribnif commentedPerhaps there is something unique about my test setup, but even with the latest patch installed I continue to get an exception rather than a nicely formatted error message when processing an internal URI that is missing the leading slash, such as
node/1. I suggest adding a test for this.Comment #60
yivanov commentedReroll patch for 9.4.x
Comment #62
gaurav_manerkar commentedCurrently multiple error messages are getting displayed when `link text` is enabled.
More information is described in issue https://www.drupal.org/project/drupal/issues/3077149
I have rerolled patch #60 by adding a fix for error message display. Apply this patch along with patch from above issue.
Comment #63
gaurav_manerkar commentedReroll patch for #62
Comment #64
gaurav_manerkar commentedReroll patch for #62
Comment #65
ameymudras commented@gaurav moving to needs review on your behalf, also triggering tests
Comment #66
ameymudras commentedDid a quick code review and the following issues were observed
Maybe we should wrap this statement as its 167 chars
The comment is not descriptive and also we could just log the exception message
Comment #67
pradhumanjain2311 commentedTry to address comment #66.
Please review.
Comment #70
uddeshy2 commentedadding working patch for drupal 10.3.
Comment #71
uddeshy2 commentedComment #72
needs-review-queue-bot commentedThe Needs Review Queue Bot tested this issue.
While you are making the above changes, we recommend that you convert this patch to a merge request. Merge requests are preferred over patches. Be sure to hide the old patch files as well. (Converting an issue to a merge request without other contributions to the issue will not receive credit.)
Comment #73
dcam commentedI closed #2935307: Link field doesn't sufficiently validate input as a duplicate. I updated the issue summary with info uncovered by that issue.
Comment #76
riyas_nr commentedIn the previous patch the
URLconstraints failed because the child constraint object was passed directly to the parent validator.Updates in this patch:
This ensures external links are properly validated and the behavior is covered by tests.
Comment #77
dcam commented@riyas_nr I haven't done an in-depth review of the MR yet, but I notice we're adding a condition for
mailto:URLs. There's no test coverage for it though. Is it possible to add a couple of test cases toLinkItemUrlValidationTest::getTestLinks()for them?Comment #78
riyas_nr commentedAdded test coverage for
mailtoscheme. Moving to NR.Comment #79
dcam commentedComment #80
smustgrave commentedAppears to be some open feedback on the MR
Comment #81
dcam commentedYes, I'm still working on the review.
Comment #82
dcam commentedRemoving the Needs Tests tag because I think that's probably covered now.
I have a couple of problems with the new constraint and validator. So I went back through the history of this issue and discovered that they stem from this code being imported from a custom project.
First, the constraint class name is
LinkUriValidConstraint, but its message says "The path %value contains invalid characters." Checking for invalid characters is certainly part of validation, but is that all of it? Or does the Symfony constraint also check URL format? The Symfony docs say "Validates that a value is a valid URL string." It seems like the constraint's label and message need to be updated.Second, the validator class is doing two separate things:
mailtoURLsYet both cases end up with the same constraint message about invalid characters, even though the
mailtovalidation may fail for other reasons. We could add a second message to the constraint, but I think I'd prefer to move this code to separate constraint and validator classes.Comment #83
dcam commentedI just realized that the issue summary doesn't mention validating
email:hrefs at all. So everything related to that is probably out-of-scope.