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 in domain:4.0.0.

Coordination

Independent of !371 (merged) (#3588168 buildPatterns completeness). Cleanly stacks under the eventual cap-raise MR (#3588176).

Merge request reports

Loading