facebook / facebook/capi-param-builder

PHP: getEtldPlusOne() resolves apex hosts under multi-label eTLDs to the public suffix, so _fbp/_fbc are never stored

Open Beginner friendly
#40 0 comments 0 reactions 0 assignees View on GitHub
Dominant language
JavaScript
Stars
33
Forks
9
PR merge metrics
No merged PRs in 30d

Description

### Summary

For an apex host under a multi-label eTLD — `example.co.uk`, `example.com.au`, `example.co.jp` — `ParamBuilder` resolves the eTLD+1 to the public suffix itself (`co.uk`) and emits `_fbp` / `_fbc` with `domain=co.uk`. Browsers reject cookies scoped to a public suffix, so the cookies are never stored: every request mints a new browser id and CAPI receives no stable `_fbp`.

Single-label eTLDs (`example.com`) are unaffected, and the same host with a `www.` prefix is resolved correctly, which is probably why this has gone unnoticed.

### Reproduction

```php
processRequest($host, array(), array(), null);
foreach ($pb->getCookiesToSet() as $c) {
printf("host=%-22s %s domain=%s value=%s\n", $host, $c->name, $c->domain, $c->value);
}
}
```

Output with `facebook/capi-param-builder-php` 1.3.1 (and current `main`):

```
host=example.co.uk _fbp domain=co.uk value=fb.1.…
host=www.example.co.uk _fbp domain=example.co.uk value=fb.2.…
host=example.com _fbp domain=example.com value=fb.1.…
```

Expected for the first row: `domain=example.co.uk`.

### Cause

In `getEtldPlusOne()`, the domain-list branch confirms the candidate is a suffix of the host and then requires the preceding character to be a dot:

```php
if ($host[$lastOccurrence - 1] === '.') {
return $domain_candidate;
}
```

When the candidate *is* the host, `$lastOccurrence` is `0`, so this reads `$host[-1]` — which since PHP 7.1 is the **last** character of the string, not a boundary check. It is `'k'` for `example.co.uk`, the candidate is discarded, and control falls through to:

```php
$slice = explode(".", $host);
if (count($slice) > 2) {
return substr($host, strpos($host, '.') + 1);
}
```

which strips the first label and yields `co.uk`. For a two-label host the fallback returns the host, which is why `.com` sites look fine.

### Suggested fix

Accept the exact match in the domain-list branch:

```php
if ($lastOccurrence === 0 || $host[$lastOccurrence - 1] === '.') {
return $domain_candidate;
}
```

That makes the configured domain list authoritative, as intended. The label-stripping fallback is still wrong for multi-label suffixes when no list is supplied, so it may be worth refusing to return a candidate that consists solely of a known public suffix.

### Impact

`facebook-for-woocommerce` (3.7.6, latest) constructs the builder as `new \FacebookAds\ParamBuilder( array( get_site_url() ) )` and passes `$cookie->domain` straight to `setcookie()`, so every WooCommerce store on a `.co.uk`-style apex domain is currently unable to persist `_fbp` or `_fbc`. It also hands the same domain to the browser in the consent-release path. Verified on a live store: `set-cookie: _fbp=…; path=/; domain=co.uk`.

Contributor guide

Open the contributing guide

Research direction

Start at ParamBuilder::getEtldPlusOne() and run the supplied reproduction with example.co.uk, www.example.co.uk, and example.com to trace the domain-list branch. Done means apex hosts under multi-label eTLDs return the full host as the cookie domain, while the www and single-label cases retain their expected domains.

Written by the indexing model from the issue text.

Assessment

Tech stack
php
Domain
backend
Issue type
Bug
Difficulty
2/5
Estimated time
1-3 hours
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
72/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.