Skip to content

IBX-12127: Fixed valid URLs being marked as invalid by ibexa:check-urls - #809

Open
tbialcz wants to merge 1 commit into
4.6from
ibx-12127-check-urls-head-get-fallback
Open

IBX-12127: Fixed valid URLs being marked as invalid by ibexa:check-urls#809
tbialcz wants to merge 1 commit into
4.6from
ibx-12127-check-urls-head-get-fallback

Conversation

@tbialcz

@tbialcz tbialcz commented Aug 20, 2026

Copy link
Copy Markdown
Contributor
🎫 Issue IBX-12127

Description:

ibexa:check-urls checked links with a bare cURL HEAD request without any headers (since the original implementation, EZP-28505). WAFs like Cloudflare block such requests, so working links were marked as invalid.

Now the request sends browser-like User-Agent/Accept headers, and a failed HEAD is retried once with GET. New optional handler options: method, fallback_to_get, user_agent, headers.

Also fixed in the same code: the https handler was reading http options, and the curl_multi loop could skip trailing URLs.

For QA:

  1. Add RichText links to https://www.tiendanimal.es/ and to a URL that rejects HEAD requests (e.g. a local php -S server returning 403 for HEAD), publish.
  2. Run php bin/console ibexa:check-urls — both links are Valid in Link manager (before the fix: Invalid).
  3. Add a genuinely broken link (404 or dead host), re-run — it is marked Invalid.

Documentation:

Yes — document the new url_checker.handlers.http|https options: method, fallback_to_get, user_agent, headers.

@tbialcz tbialcz added Bug Something isn't working Doc needed The changes require some documentation labels Aug 20, 2026
@tbialcz
tbialcz force-pushed the ibx-12127-check-urls-head-get-fallback branch from 5b182e3 to 9149e7e Compare August 20, 2026 06:23
@tbialcz
tbialcz force-pushed the ibx-12127-check-urls-head-get-fallback branch from 9149e7e to 3fc0d75 Compare August 20, 2026 06:28
@sonarqubecloud

sonarqubecloud Bot commented Aug 20, 2026

Copy link
Copy Markdown

Quality Gate Passed Quality Gate passed

Issues
3 New issues
0 Accepted issues

Measures
0 Security Hotspots
No data about Coverage
0.0% Duplication on New Code

See analysis details on SonarQube Cloud

@tbialcz
tbialcz requested a review from a team August 20, 2026 07:34

@alongosz alongosz left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My general remark - if we're revamping this, can we use something more modern than reinvented-wheel-curl?
Like Symfony HTTP client? Or it doesn't make sense here or it's too much work? Just curious.

Remarks to the current solution, which is still a lot of great work 💪

ignore_certificate: false
method: HEAD
fallback_to_get: true
user_agent: 'Mozilla/5.0 (X11; Linux x86_64; rv:128.0) Gecko/20100101 Firefox/128.0'

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't this be something more reflecting reality?

Suggested change
user_agent: 'Mozilla/5.0 (X11; Linux x86_64; rv:128.0) Gecko/20100101 Firefox/128.0'
user_agent: 'Ibexa DXP URL checker'

Or does Cloudfare treat it as suspicious and it's on purpose like that?

];

/**
* {@inheritdoc}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While at it, drop this too, please.

* Based on https://www.onlineaspect.com/2009/01/26/how-to-use-curl_multi-without-blocking/
*/
public function validate(array $urls)
public function validate(array $urls): void

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Given the refactoring is within the scope of this task, this SonarCloud comment is valid:

Refactor this function to reduce its Cognitive Complexity from 17 to the 15 allowed.

The method is still unreadable ;-)
Perhaps some parts of it can be extracted into more atomic methods?

} else {
// Abort on the first body chunk - the final (post-redirect) status code is already known
// and the body must not be streamed to the output (CURLOPT_RETURNTRANSFER is disabled).
curl_setopt($handler, CURLOPT_WRITEFUNCTION, static function ($handler, string $data): int {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unused $handler and $data arguments can be probably removed, per SonarCloud suggestion.

Comment on lines +26 to +27
/** @var \Ibexa\Contracts\Core\Repository\URLService|\PHPUnit\Framework\MockObject\MockObject */
private $urlService;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Type hint can be formed better here. It's never this or that in case of a mocked type. It's always an intersection. Moreover for PHP 7.4 we explicitly specify the essential/basic type and provide more details in PHPDoc. For 5.0 this can be pure PHP 8 intersection.
So:

Suggested change
/** @var \Ibexa\Contracts\Core\Repository\URLService|\PHPUnit\Framework\MockObject\MockObject */
private $urlService;
/** @var \Ibexa\Contracts\Core\Repository\URLService & \PHPUnit\Framework\MockObject\MockObject */
private URLService $urlService;

Comment on lines +29 to +30
/** @var \Ibexa\Contracts\Core\SiteAccess\ConfigResolverInterface|\PHPUnit\Framework\MockObject\MockObject */
private $configResolver;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The same remark applies here.

Comment on lines 70 to +72
message: '#^Parameter \#2 \$handle of function curl_multi_add_handle expects CurlHandle, resource given\.$#'
identifier: argument.type
count: 2
count: 3

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks like a regression. Count 2->3.

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

Labels

Bug Something isn't working Doc needed The changes require some documentation Ready for review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants