Skip to content

fix(moshpit): never sell or answer for a real top-level domain - #462

Merged
ralyodio merged 1 commit into
mainfrom
fix/no-real-tlds
Aug 30, 2026
Merged

fix(moshpit): never sell or answer for a real top-level domain#462
ralyodio merged 1 commit into
mainfrom
fix/no-real-tlds

Conversation

@ralyodio

Copy link
Copy Markdown
Contributor

.sh was sold for $2 and took the registry off the air

.sh was claimed through the ordinary registration flow by someone who isn't us. It's Saint Helena's ccTLD. It's also where this project's registry lives: pit.moshcode.sh.

Every bridge that fetched the ending list learned to treat the registry as a Moshpit name — stopped forwarding it, and tried to resolve it through the registry it had just made unreachable. A resolver that can't resolve the thing it needs in order to resolve anything.

Observed on an affected desktop: bridge running, listening, correctly routed to, answering NXDOMAIN to the entire internet.

pit.moshcode.sh → NXDOMAIN
alt.2600 → NXDOMAIN
mail-ads.google.com → NXDOMAIN

Moshpit names failed for the same reason clearnet ones did — resolving a name needs a live registry query, and the registry was what was being swallowed. It took the rest of the ccTLD with it: real .sh sites answered a Moshpit parking IP.

RESERVED_TLDS was meant to cover this. It named com, net, org, edu, mil, int, arpa. There are 1438 delegated TLDs.

Both halves

  • The registry refuses to sell an ending IANA delegates — on the single and bulk paths (both funnel through tldRejection), and even for callers passing allowReserved. Reserving .bank is policy and policy has exceptions; answering for .sh is a machine that has lost part of the internet.
  • The bridge refuses to answer for one whatever the registry says it sold. isOurs is now the last word rather than the ending list.

Neither is sufficient alone, and the second is the one that helps anyone today — 622 colliding endings are already registered across 14 owners, and nobody should have to wait for that cleanup before their resolver works.

The list is vendored, not fetched at runtime: a registry that phoned IANA per claim would sell .sh the moment IANA had a bad minute, and a resolver doing it per query would depend on the very thing it exists to avoid needing. scripts/generate-iana-tlds.mjs regenerates it. Two copies, because src/ ships to npm and apps/pwa/ deploys as the Pit and isn't a workspace — a test fails if they drift.

Verified live

Against a real bridge holding all 18224 endings including sh:

name before after
pit.moshcode.sh NXDOMAIN 69.46.46.24
moshcode.sh hijacked 69.46.46.31 (its real record)
moshcoding.sh 69.46.46.103 (fake parking IP) nothing — it genuinely doesn't exist
blue.eggs 69.46.46.103 unchanged
alt.2600 67.205.189.229 unchanged
example.com ok unchanged

One test premise was wrong, not the code

tldRejection("ai") was asserted to be null. .ai is Anguilla's ccTLD — the expectation encoded the bug. Rewritten to test the length rule with an ending that's only about length.

Suite: 2726 tests, 0 failures, 334 skipped (the usual apps/pwa guards).

Not included

Deleting the 622 endings already sold. The guards make them harmless without it, and removing paid claims across 14 owners — 334 of them the project owner's own — is a decision to take deliberately, not as a side effect of a bug fix.

🤖 Generated with Claude Code

https://claude.ai/code/session_01ThnQwoieWt8VR6N7gtgnhp

`.sh` was claimed through the ordinary registration flow, for $2, by someone
who is not us. It is Saint Helena's ccTLD. It is also where this project's own
registry lives: pit.moshcode.sh.

Every bridge that fetched the ending list therefore learned to treat the
registry as a Moshpit name. It stopped forwarding it to the clearnet and tried
to resolve it through the registry it had just made unreachable, which is a
resolver that cannot resolve the thing it needs in order to resolve anything.
The observed state on an affected desktop was a bridge that was running,
listening, correctly routed to, and answering NXDOMAIN to the entire internet:

    pit.moshcode.sh → NXDOMAIN
    alt.2600 → NXDOMAIN
    mail-ads.google.com → NXDOMAIN

Moshpit names failed for the same reason clearnet ones did. Resolving a name
needs a live registry query, and the registry was the thing being swallowed.

It took the rest of the ccTLD with it. On a machine running the bridge as its
resolver, real `.sh` sites answered a Moshpit parking IP.

The static RESERVED_TLDS list was meant to cover this. It named com, net, org,
edu, mil, int and arpa. There are 1438 delegated TLDs.

So both halves are closed:

  - the registry refuses to sell an ending IANA already delegates, on both the
    single and bulk registration paths, and refuses it even for callers passing
    `allowReserved` — reserving `.bank` is policy and policy has exceptions,
    answering for `.sh` is a machine that has lost part of the internet
  - the bridge refuses to answer for one whatever the registry says it sold

Neither is sufficient alone, and the second is the one that helps anyone today:
622 colliding endings are already registered across 14 owners, and a person
running a bridge should not have to wait for that to be cleaned up before their
resolver works. `isOurs` is now the last word rather than the ending list.

The list is vendored, not fetched at run time. A registry that phoned IANA on
every claim would sell `.sh` the moment IANA had a bad minute, and a resolver
that did it per query would depend on the very thing it exists to avoid needing.
`scripts/generate-iana-tlds.mjs` regenerates it; the two copies exist because
`src/` ships to npm and `apps/pwa/` deploys as the Pit and is not a workspace,
and a test fails if they drift.

Verified against a live bridge holding all 18224 endings including `sh`:
pit.moshcode.sh now answers 69.46.46.24 where it used to NXDOMAIN, moshcode.sh
answers its real 69.46.46.31, blue.eggs and alt.2600 are unchanged, and
moshcoding.sh correctly returns nothing because it genuinely does not exist —
rather than a parking IP claiming it does.

One existing test asserted `tldRejection("ai")` was null. `.ai` is Anguilla's
ccTLD; the expectation encoded the bug. Rewritten to test the length rule with
an ending that is only about length.

Not included: deleting the 622 endings already sold. The guards above make them
harmless without it, and removing paid claims across 14 owners is a decision to
take deliberately rather than as a side effect of a bug fix.

Suite: 2726 tests, 0 failures.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ThnQwoieWt8VR6N7gtgnhp
@github-actions

Copy link
Copy Markdown

ThreatCrush Security Scan

40 finding(s) in the 8 file(s) this pull request changes.

HIGH/CRITICAL: 1 | MEDIUM: 39

Severity Rule Location
HIGH tls-verification-disabled src/dns.mjs:766
MEDIUM sql-template-interpolation apps/pwa/src/moshpit.mjs:139
MEDIUM sql-template-interpolation apps/pwa/src/moshpit.mjs:153
MEDIUM sql-template-interpolation apps/pwa/src/moshpit.mjs:179
MEDIUM sql-template-interpolation apps/pwa/src/moshpit.mjs:373
MEDIUM sql-template-interpolation apps/pwa/src/moshpit.mjs:377
MEDIUM sql-template-interpolation apps/pwa/src/moshpit.mjs:422
MEDIUM sql-template-interpolation apps/pwa/src/moshpit.mjs:671
MEDIUM sql-template-interpolation apps/pwa/src/moshpit.mjs:867
MEDIUM sql-template-interpolation apps/pwa/src/moshpit.mjs:869
MEDIUM sql-template-interpolation apps/pwa/src/moshpit.mjs:928
MEDIUM sql-template-interpolation apps/pwa/src/moshpit.mjs:974
MEDIUM sql-template-interpolation apps/pwa/src/moshpit.mjs:1044
MEDIUM sql-template-interpolation apps/pwa/src/moshpit.mjs:1147
MEDIUM sql-template-interpolation apps/pwa/src/moshpit.mjs:1170
MEDIUM sql-template-interpolation apps/pwa/src/moshpit.mjs:1192
MEDIUM sql-template-interpolation apps/pwa/src/moshpit.mjs:1391
MEDIUM sql-template-interpolation apps/pwa/src/moshpit.mjs:1541
MEDIUM sql-template-interpolation apps/pwa/src/moshpit.mjs:1690
MEDIUM sql-template-interpolation apps/pwa/src/moshpit.mjs:1696
MEDIUM sql-template-interpolation apps/pwa/src/moshpit.mjs:1743
MEDIUM sql-template-interpolation apps/pwa/src/moshpit.mjs:1792
MEDIUM sql-template-interpolation apps/pwa/src/moshpit.mjs:1823
MEDIUM sql-template-interpolation apps/pwa/src/moshpit.mjs:1923
MEDIUM sql-template-interpolation apps/pwa/src/moshpit.mjs:2042
MEDIUM sql-template-interpolation apps/pwa/src/moshpit.mjs:2054
MEDIUM sql-template-interpolation apps/pwa/src/moshpit.mjs:2066
MEDIUM sql-template-interpolation apps/pwa/src/moshpit.mjs:2100
MEDIUM sql-template-interpolation apps/pwa/src/moshpit.mjs:2298
MEDIUM sql-template-interpolation apps/pwa/src/moshpit.mjs:2311
MEDIUM sql-template-interpolation apps/pwa/src/moshpit.mjs:2337
MEDIUM sql-template-interpolation apps/pwa/src/moshpit.mjs:2343
MEDIUM sql-template-interpolation apps/pwa/src/moshpit.mjs:2347
MEDIUM sql-template-interpolation apps/pwa/src/moshpit.mjs:2357
MEDIUM sql-template-interpolation apps/pwa/src/moshpit.mjs:2488
MEDIUM sql-template-interpolation apps/pwa/src/moshpit.mjs:2640
MEDIUM sql-template-interpolation apps/pwa/src/moshpit.mjs:2648
MEDIUM sql-template-interpolation apps/pwa/src/moshpit.mjs:2656
MEDIUM sql-template-interpolation apps/pwa/src/moshpit.mjs:2686
MEDIUM sql-template-interpolation apps/pwa/src/moshpit.mjs:2825
48 pre-existing finding(s) elsewhere in the repository — **HIGH/CRITICAL**: 4 | **MEDIUM**: 34 | **LOW**: 10

Not introduced by this pull request. The full set is in the Security tab.

Severity Rule Location
HIGH js-ssrf-outbound-request apps/pwa/public/sw.js:45
HIGH tls-verification-disabled apps/pwa/src/lib/moshpit-gateway.mjs:299
HIGH sh-remote-script-execution install.sh:79
HIGH sh-remote-script-execution install.sh:83
MEDIUM js-unescaped-html-sink apps/pwa/src/routes/moshpit.mjs:2880
MEDIUM js-timing-unsafe-mac-compare apps/pwa/src/routes/settings-sync.mjs:192
MEDIUM js-dynamic-code-execution apps/pwa/test/apikey-mask.test.mjs:129
MEDIUM sql-template-interpolation apps/pwa/test/credits-webhook-event-match.test.mjs:111
MEDIUM sql-template-interpolation apps/pwa/test/credits-webhook-event-match.test.mjs:131
MEDIUM sql-template-interpolation apps/pwa/test/moshpit-terms.test.mjs:207
MEDIUM sql-string-concatenation src/cli-schema.mjs:162
MEDIUM sql-string-concatenation src/cli-schema.mjs:504
MEDIUM sql-string-concatenation src/cli-schema.mjs:630
MEDIUM redos-nested-quantifier src/clients.mjs:29
MEDIUM js-uninitialized-buffer src/pty.mjs:138
MEDIUM js-timing-unsafe-mac-compare src/settings-sync.mjs:397
MEDIUM insecure-temp-file test/dns-disable-restore.test.mjs:93
MEDIUM insecure-temp-file test/dns-disable-restore.test.mjs:314
MEDIUM insecure-temp-file test/mirror-engine-input.test.mjs:89
MEDIUM insecure-temp-file test/mirror-engine-input.test.mjs:90

…and 28 more. Full results in the Security tab.

Snippets are redacted; ThreatCrush never prints matched credential material.

@ralyodio
ralyodio merged commit d4290f7 into main Aug 30, 2026
6 checks passed
@ralyodio ralyodio mentioned this pull request Aug 30, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant