# ejabberd — CWE-407 Disclosure Brief
**2026-03-27 · Patch available — awaiting upstream merge**

## Finding

Two O(n²) defects in ejabberd's message archiving and shared roster modules. Both use `lists:member/2` in hot paths — one on every archived message, the other on every group membership status subscription. Measured at 250× and 2,500× respectively. Patches ready for upstream review.

## The Defects

**ejabberd-0001 (PATCHED — HIGH):** `src/mod_mam.erl:1029`

```erlang
%% Inside message archiving hot path — per archived message:
lists:member(LPeer, Always) orelse lists:member(LPeer, Never)
%% O(N×M) — N messages × M always/never list entries
```

`lists:member/2` performs O(N) linear scan over the `Always`/`Never` lists on every archived message. For N messages and M filter list entries: **O(N × M)**. **Measured ratio: 250×.**

**ejabberd-0002 (PATCHED — HIGH):** `mod_shared_roster.erl:356`

```erlang
%% Inside is_user_in_group() — per subscription stanza:
lists:member(User, GroupMembers)
%% O(N_group×msg) — group size × messages
```

`lists:member/2` scan over group members on every shared roster subscription stanza. **Measured ratio: 2,500×.**

## Complexity Proof

**ejabberd-0001:** For M=250 always/never filter entries:
- Per message: O(M) scan
- Fixed: pre-built `gb_sets` or `sets` → O(log M) or O(1)
- **250× measured ratio.**

**ejabberd-0002:** For group size G=2500:
- Per stanza: O(G) scan
- Fixed: `gb_sets` set → O(log G)
- **2,500× measured ratio.**

## Impact

**ejabberd-0001** affects all ejabberd servers with mod_mam (Message Archive Management) enabled — the XMPP standard for message history. High-traffic XMPP servers with large always/never filter lists hit worst case on every message.

**ejabberd-0002** affects all ejabberd servers with mod_shared_roster enabled — a widely-used ejabberd module that automatically populates contact lists from group membership. Large XMPP deployments (corporate XMPP servers, FOSS communities) use shared roster extensively.

## The Fix

**ejabberd-0001:** Replace `lists:member` with `gb_sets:is_member`:

```erlang
%% Before
lists:member(LPeer, Always) orelse lists:member(LPeer, Never)

%% After
%% CWE-407 fix: gb_sets for O(log N) membership instead of O(N) lists:member scan.
gb_sets:is_member(LPeer, AlwaysSet) orelse gb_sets:is_member(LPeer, NeverSet)
```

**ejabberd-0002:** Pre-build `gb_sets:from_list(GroupMembers)` before the subscription loop.

## Patch

`defects/ejabberd/patch/ejabberd-0001-0002-mam-roster-sets.patch`

## What We Ask

1. Confirm receipt and assign a GitHub Security Advisory or issue reference.
2. Validate the patch against your mod_mam and mod_shared_roster test suites.
3. Assess CVE eligibility — ejabberd-0002 measured at 2,500× on large group rosters.
4. Coordinate a disclosure date — we are targeting 90 days from first contact.

Contact: see cover email. This brief is confidential until coordinated disclosure.
