# UNDF: UNDF-2026-000000090
# UNDF: (pending)
# consul-0001: makeMeshGatewayPeerFilterChain — peerNames O(S×B×P) slice lookup

## CWE-407 — Algorithmic Complexity

| Field | Value |
|-------|-------|
| ID | consul-0001 |
| Severity | MEDIUM |
| Ecosystem | consul |
| Package | agent/xds |
| File | `agent/xds/listeners.go` |
| Lines | 2282–2286 |
| Complexity | O(S×B×P) — services × bundles × peers per service |
| Hot path | Mesh gateway listener rebuild on every topology change |

## Defect

`makeMeshGatewayPeerFilterChain` receives `peerNames []string` (the set of peer names
for this exported service) and iterates over all peering trust bundles, calling
`stringslice.Contains(peerNames, bundle.PeerName)` for each bundle:

```go
// agent/xds/listeners.go
for _, bundle := range cfgSnap.MeshGateway.PeeringTrustBundles {   // O(B)
    if stringslice.Contains(peerNames, bundle.PeerName) {           // O(P) scan
        peerBundles = append(peerBundles, bundle)
    }
}
```

`stringslice.Contains` is a linear scan:

```go
// lib/stringslice/stringslice.go
func Contains(l []string, s string) bool {
    for _, v := range l {
        if v == s {
            return true
        }
    }
    return false
}
```

The outer caller iterates over all exported services:

```go
for _, svc := range cfgSnap.MeshGatewayValidExportedServices() {   // O(S)
    peerNames := cfgSnap.MeshGateway.ExportedServicesWithPeers[svc]
    filterChain, err := s.makeMeshGatewayPeerFilterChain(cfgSnap, svc, peerNames, chain)
```

Total: O(S × B × P). In a production Consul cluster with S=50 services, B=20 trust
bundles, P=20 peers per service, each listener rebuild costs 50×20×20 = 20,000
string comparisons instead of 50×20 = 1,000 map lookups.

## Fix

Convert `peerNames` to a `map[string]bool` before the bundle loop:

```go
// Before:
var peerBundles []*pbpeering.PeeringTrustBundle
for _, bundle := range cfgSnap.MeshGateway.PeeringTrustBundles {
    if stringslice.Contains(peerNames, bundle.PeerName) {
        peerBundles = append(peerBundles, bundle)
    }
}

// After:
peerNameSet := make(map[string]struct{}, len(peerNames))
for _, p := range peerNames {
    peerNameSet[p] = struct{}{}
}
var peerBundles []*pbpeering.PeeringTrustBundle
for _, bundle := range cfgSnap.MeshGateway.PeeringTrustBundles {
    if _, ok := peerNameSet[bundle.PeerName]; ok {
        peerBundles = append(peerBundles, bundle)
    }
}
```

Alternatively, `ExportedServicesWithPeers` could store `map[string]struct{}` instead of
`[]string`, propagating O(1) lookup to all callers.

## Speedup

| S (services) | B (bundles) | P (peers) | Before (ops) | After (ops) | Speedup |
|---|---|---|---|---|---|
| 10 | 10 | 10 | 1,000 | 200 | 5× |
| 50 | 20 | 20 | 20,000 | 2,000 | 10× |
| 100 | 50 | 50 | 250,000 | 10,000 | 25× |
