DomainAliasStorage::buildPatterns() enumerates wildcard match candidates for loadByHostname(). The hand-rolled loop misses some masks:

  • 4-segment hostnames: 13 of 14 useful patterns generated. Mask 9 (*.two.example.* — wildcards on both ends, literals in the middle) is unreachable.
  • 5-segment hostnames: 19 of 30 useful patterns generated. 11 masks unreachable.

Replace the loop with a deterministic bitmask enumeration that produces exactly 2^N − 2 patterns for an N-segment hostname (every wildcard mask except all-literal — the exact hostname, handled separately by getPatterns() — and all-wildcard, which would match anything and is not a useful candidate).

Practical impact in 3.x

Bounded. The validator's single-host-wildcard cap (still in place at 3.x — see #3588169) means stored multi-host-wildcard aliases cannot exist via the normal save path. The previously-unreachable patterns this MR enumerates can only have a stored alias if config was imported via direct YAML edit or migrated from D6/D7. So the user-visible "aliases now match" effect is small for typical 3.x sites.

The full benefit lands once a future issue raises the host-wildcard cap (out of scope here, depends on DomainAliasPatternResolver::replaceWildcards() hardening). This MR is the prerequisite refactor: no point in raising the cap if the candidate enumeration is incomplete.

The change is still a strict improvement in 3.x:

  • Cleaner code (5-branch hand-roll → 1 bitmask loop), generalises to any segment count.
  • array_unique() call in getPatterns() is now provably unnecessary and dropped (the bitmask emits no duplicates).
  • Test fixtures lock in the new coverage so the next host-cap-raise MR doesn't have to re-derive what buildPatterns() should emit.

Proposed fix

Replace the hand-rolled five-branch loop with a deterministic bitmask enumeration:

private function buildPatterns(array $parts): array {
  $count = count($parts);
  $patterns = [];
  // Iterate over every wildcard mask except all-literal
  // (mask 0, the exact hostname, handled separately by
  // getPatterns()) and all-wildcard (which would match
  // everything and is therefore not a useful candidate).
  for ($mask = 1; $mask < (1 << $count) - 1; $mask++) {
    $temp = $parts;
    for ($j = 0; $j < $count; $j++) {
      if ($mask & (1 << $j)) {
        $temp[$j] = '*';
      }
    }
    $patterns[] = implode('.', $temp);
  }
  return $patterns;
}

Test plan

  • Extend DomainAliasSortTest:
    • one.two.example.com:80 fixture: all 14 wildcard masks (mask 9 added; the duplicate entry removed; mask 11 added).
    • New a.b.c.d.e:80 fixture: 91 patterns covering every mask for a 5-segment hostname.

Out of scope (separate issues)

  • Raising the host-wildcard limit to match the README's "max 3" claim.
  • DomainAliasPatternResolver substitution hardening (prerequisite for raising the cap).

Audit context

Surfaced while auditing #3588155 (sort comparator and port-pattern priority). The omission is logically separate from the sort/priority work; landed here as its own issue.

Issue fork domain-3588168

Command icon 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

mably created an issue. See original summary.

mably’s picture

Issue summary: View changes
mably’s picture

Issue summary: View changes

mably’s picture

Status: Active » Needs review
mably’s picture

Issue summary: View changes

  • mably committed c9ae16e2 on 3.x
    fix: #3588168 DomainAliasStorage::buildPatterns() generates an...
mably’s picture

Assigned: mably » Unassigned
Priority: Major » Normal
Status: Needs review » Fixed

Now that this issue is closed, review the contribution record.

As a contributor, attribute any organization that helped you, or if you volunteered your own time.

Maintainers, credit people who helped resolve this issue.

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.