Problem/Motivation
When content editors create links in WYSIWYG fields with an invalid value in the
href attribute, the HTML extractor in Linkchecker
picks up this value as a URL candidate. The value passes HTML extraction but fails URI
validation when $link->save() is called, because the url field
is of type uri and Drupal's Uri class throws an exception for
invalid hosts containing spaces or special characters.
This causes drush linkchecker:check to crash with:
In Uri.php line 76: Invalid host: "https://<WRONG HREF VALUE>"
The entire check process is aborted, preventing all subsequent links from being
checked.
Steps to reproduce
- In a WYSIWYG text field, create a link where the
hrefattribute
contains an invalid value such as a string "Lorem ipsum". - Run
drush linkchecker:analyze– this succeeds and stores the
invalid URL. - Run
drush linkchecker:check– this crashes with an
InvalidArgumentExceptionfromUri.php.
Proposed resolution
Wrap $link->save() in LinkExtractorService::saveLink()
with a try/catch(\Throwable) block. If saving fails due to an invalid URI,
log a warning message containing the URL, entity type, entity ID and field name so
administrators can locate and fix the invalid content. The invalid link is then skipped
and the check process continues normally.
Remaining tasks
- Review and testing
| Comment | File | Size | Author |
|---|
Issue fork linkchecker-3604202
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 #4
andrerb commentedComment #5
andrerb commentedComment #6
nitinkumar_7 commentedI have reviwed MR The fix looks good, need to improve the testInvalidUrl() test to be more explicit and precise, update the test to add a stronger assertion that specifically verifies the invalid URL was skipped
Core fix : injects LoggerInterface into LinkExtractorService, adds try/catch(\Throwable) around $link->save() in saveLink(), and registers the logger dependency in linkchecker.services.yml
Regression test : adds testInvalidUrl() to LinkcheckerLinkExtractorServiceTest with explicit assertions that valid URLs are saved and the malformed URL is skipped without crashing
Comment #7
andrerb commentedIf you are also applying the patch from https://www.drupal.org/project/linkchecker/issues/3366753, use this alternative patch instead: 3604202-invalid-href-attribute-in-wysiwyg-with-3366753.patch
Comment #8
andrerb commentedComment #9
andrerb commentedComment #10
codebymikey commentedAttached a related issue that was recently committed, but doesn't address when the space is within the host.
I also think the code can be simplified a bit more by just using the
UrlHelperor theUriclass to check if the URL is valid rather than using our own logic.There's also the
linkchecker_watchdog_log()method which should avoid making changes to the services and allow this patch to easily apply against others.I'll pick these changes up.
Comment #11
codebymikey commentedSimplified the changes and added a simple test case.
Comment #12
nitinkumar_7 commentedComment #13
codebymikey commentedAttached a simplified version of the patch that works with less conflicts against other patches.
In particular the
Utilsimport used in #3616064: Provide a way to check specific links immediatelyComment #14
ericgsmith commentedAlso experiencing this after semi recent guzzle changes. It is a huge inconvenience that the checker cron stops completely.
I have not spent much time looking through this module, however the fix looks simple enough and includes tests. Have applied and tested on a fresh install.
Created a node with the body field:
Observed the link was visible as unchecked after saving. Attempted to run
drush linkchecker:checkwhich failedApplied patch and now the check completes and I can see the link in the link report correctly showing as "Invalid host: "this is a test" " in the error column.
I did wonder if we wanted to future proof a bit more for expanding which exception is caught? Given we just call getMessage on the exception, should we catch any
Throwableto make this a bit more robust?Aside from that though - I think this is an important fix and good for me. Setting to RTBC but may be worth thinking about the above.
Comment #15
codebymikey commentedI think the rest of the code is simple enough that it shouldn't need the generic catch. The only other exceptions that need to be caught would be within the request itself, which is being handled in #3469263: Links never checked.
Comment #16
ericgsmith commentedSounds good to me - thanks @codebymikey