Issue #3588175: DomainAliasPatternResolver: reject wildcard-count mismatches in cross-pattern resolution
Summary
DomainAliasPatternResolver::resolveAliasPattern() does positional, context-blind substitution of source-pattern captures into target-pattern wildcards. When source and target wildcard counts differ, or when one side has a port wildcard the other doesn't, the old code silently produced broken URLs.
Pre-flight check added: compare host-wildcard counts and port-wildcard presence on both sides before doing the regex match. On mismatch, log a warning and return FALSE; the caller falls back to the canonical hostname (current safe-ish behaviour) instead of issuing a broken redirect.
Issue: #3588175.
Failure modes guarded against
| Case | Source | Target | Old behaviour | New behaviour |
|---|---|---|---|---|
| B | *.*.staging.example.com |
*.canonical.com |
Silently drops the second capture; redirect lands on a URL with a missing segment | Returns FALSE, logs warning |
| C | *.staging.example.com |
*.*.canonical.com |
Leaves a literal * in the redirect URL |
Returns FALSE, logs warning |
| D | *.example.com |
example.com:* |
Substitutes a host capture into the port slot (e.g. example.com:staging) |
Returns FALSE, logs warning |
(Case A — same wildcard count, same slot — keeps working.)
Why this matters now
Today (3.x), the validator caps host wildcards at 1 (#3588169 / merged), so cases B and C with multi-host-wildcard patterns are unreachable through normal admin flows. Case D is reachable today — both patterns can carry one wildcard total under the current cap, just in different slots.
Cases B and C become reachable once the host-wildcard cap is raised (#3588176, depends on this issue). Hardening the resolver here is the prerequisite for that relaxation.
Test plan
- New
DomainAliasPatternResolverTest(Kernel) — 10 test methods, all passing locally:testSameWildcardCountSubstitutes— case A working.testSourceMoreWildcardsRejected— case B.testTargetMoreWildcardsRejected— case C.testHostPortSemanticsMismatchRejected— case D.testNoTargetWildcardsReturnsTarget— trivial pass-through.testNoSourceWildcardsReturnsFalse— no captures available.testNoMatchReturnsFalse— source regex doesn't match active host.testLoopBackRejected— substitution would produce canonical hostname.testPortWildcardOnBothSides— port-wildcard substitution working.testTargetPortWildcardStrippedWhenNoActivePort— existing:*-stripping for default ports preserved.
- Existing
DomainAliasPrefixTest(8 tests, 27 assertions) still passes — the new rejection only triggers on configurations the prior tests didn't exercise. - PHPCS / PHPStan clean.
- CI phpunit run.
Out of scope
- Raising the host-wildcard cap (#3588176, blocked on this).
- Restoring D6/D7 single-character
?wildcard support —?is in a separate deprecation cycle (#3588169) heading toward removal indomain:4.0.0.
Coordination
Independent of !371 (merged) (#3588168 buildPatterns completeness). Cleanly stacks under the eventual cap-raise MR (#3588176).