The alias pattern validator counts host stars and the port wildcard :* in the same total when enforcing the "one wildcard" rule. The port wildcard means "any port for this host" — conceptually distinct from a host wildcard — and the rest of the alias pipeline already treats it that way (buildPortPatterns(), DomainAliasPatternResolver, the priority model docs added in #3588155). Co-counting blocks patterns the README documents as supported (e.g. example.*:*, *.com:*, *.example.com:*).
Audit also surfaced inert ? wildcard handling: the validators counted ? toward the wildcard limit and DomainAliasForm translated ? to [^.:] in its "pattern shadows a canonical hostname" warning regex, but the matching pipeline never honors ?. ? was a real single-character wildcard in Domain 6.x/7.x; the 8.x-1.x port silently dropped the matcher and kept the validator. Sites migrated from D7 with ? patterns have had silently inert aliases ever since.
Fix
Two coordinated changes plus a deprecation pathway:
- Split host-wildcard counting from port-wildcard counting in both validators (legacy
DomainAliasValidatorand currentDomainAliasPatternConstraintValidator). Apply the existing single-wildcard limit to host stars only. - Drop inert
?handling from the validators (no longer counted) and from the form's canonical-shadow warning regex.?is now a literal character everywhere, consistent with the matching pipeline. - Deprecation pathway for 4.0.0:
?stays in the character allowlist for backwards compatibility with existing config, but a newhook_ENTITY_TYPE_presave('domain_alias')fires@trigger_error(... E_USER_DEPRECATED)when an alias pattern contains?. Catches every save path. Five@todo Remove in domain:4.0.0markers across the validators and the hook make a future cleanup-grep find every related code path.
Out of scope (separate issues)
- Raising the host-wildcard limit to match the README's "max 3" claim. Cross-pattern resolution in
DomainAliasPatternResolver::replaceWildcards()needs hardening before more host wildcards can land safely. DomainAliasPatternResolversubstitution hardening (prerequisite for raising the cap).- 4.0.0 cleanup itself: drop
?from the allowlist, delete the deprecation hook, simplify the validator comments.
Behavior change
Strict superset; no patterns previously valid are now rejected.
- Patterns combining a single host wildcard with a port suffix (e.g.
example.*:*,*.example.com:*,example.*:8080) now save successfully. - Patterns containing
?save with a deprecation notice. No matching change —?was never honored as a wildcard at request time. The character will be removed from the allowlist in domain:4.0.0; sites should clean up?patterns before upgrading.
Test plan
DomainAliasValidatorTestextended: positive cases forexample.com:*,*.example.com:*,example.*:*,example.*:8080.DomainAliasConstraintTest::testValidPatternsextended with the same combinations.- Existing
*.*.example.comnegative case kept (multi-host-wildcard remains rejected). - All tests pass locally (Drupal 11.x + MySQL).
- PHPCS / PHPStan / cspell clean.
Audit context
Surfaced while auditing #3588155 (sort comparator and port-pattern priority) and #3588168 (buildPatterns enumeration completeness). The original cap was introduced in the very first commit of the alias module (f53a6a7e) with no rationale recorded.
Coordination
No hard dependencies. Independent of #3588168 / !371 (different functions, different test methods). #3588155 / !370 already merged.
Issue fork domain-3588169
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
mably commentedComment #4
mably commentedComment #5
mably commentedComment #7
mably commented