Skip to content

Nickname-keyed target selection resolves first match, with no trust ranking or ambiguity error #1709

Description

@RodsF1

Surfaced while reviewing #1708, which fixes the rendering half of the same problem (a trusted key renaming onto a name someone else uses). This is the targeting half, and it is untouched by that PR.

The behaviour

UnifiedPeerService.getPeerID(for:) walks the peer array and returns the first peer whose displayName or nickname matches, normalized:

let target = nickname.normalizedNickname
for peer in peers {
    if peer.displayName.normalizedNickname == target || peer.nickname.normalizedNickname == target {
        return peer.peerID
    }
}

No trust ranking, no verified-first preference, and no ambiguity error when two peers answer to the same name. Iteration order decides.

Every command that takes a name goes through it:

line command
CommandProcessor.swift:209 /msg
:264 /hug, /slap
:338 /block
:385 /unblock
:445 /ping, /trace
:548 /fav, /unfav

Why it is worth its own issue

/fav is the one that costs something irreversible. It persists the durable favourite linkage and notifies the peer, which shares your Nostr public key — so /fav medic resolving to the wrong medic discloses a long-term identifier to someone you did not mean to give it to, and there is no undo for a key already sent. /block fails in the other direction: it silently blocks the wrong person and leaves the intended one unblocked.

The #abcd disambiguation does not cover this. PeerDisplayNameResolver counts collisions only among connected peers and only suffixes connected peers, so when the real peer is offline or out of range there is no suffix to type, and the typed bare name resolves to whoever is first. Offline favourite rows bypass the resolver entirely.

Not a spoofing bug on its own

Worth being precise about severity: this needs no key compromise and confers no trust badge — seals stay keyed by fingerprint. It is that a name is not a unique key and is being used as one. It becomes materially worse in combination with #1708's scenario, where the colliding name is deliberately chosen.

Possible shapes, in rough order of cost

  1. Return an ambiguity result when more than one peer matches, and have the commands ask which, rather than silently picking. Most correct, most UI.
  2. Prefer verified, then vouched, then connected, on a tie — cheap, but it makes trust decide routing, which may be undesirable in itself.
  3. Refuse a bare name when it is ambiguous and require the #abcd form, extending the suffix to the offline case so there is always something to type.
  4. Gate /fav specifically, since it is the one with an irreversible side effect, and leave the rest.

Happy to implement whichever shape you prefer; I have not assumed one.

Found by @RodsF1 while reviewing @shubhambhandari126's #1708; raised there first and split out at their suggestion.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions