Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 11 additions & 2 deletions apps/pwa/src/lib/moshpit-name.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,10 @@ export function normalizeTld(input) {
// registering the wrong thing.
const label = normalizeLabel(raw);
if (!label) return null;
// All-numeric would be ambiguous against an IPv4 literal in a hostname.
if (/^\d+$/.test(label)) return null;
// All-numeric endings are fine: `.420`, `.187`, `.911` are names people want,
// and an ending on its own is never mistaken for an address. The ambiguity
// with an IPv4 literal belongs to the whole hostname — `1.420` reads as one,
// `blue.420` cannot — so parseMoshpitName rejects that case and this does not.
return label;
}

Expand All @@ -72,6 +74,13 @@ export function parseMoshpitName(input) {
const normalizedLabel = normalizeLabel(label);
const normalizedTld = normalizeTld(tld);
if (!normalizedLabel || !normalizedTld) return null;

// `1.420` is indistinguishable from an abbreviated IPv4 literal — several
// parsers read a two-part dotted number as an address — so a name whose every
// label is numeric is refused. `blue.420` and `420.blue` are unambiguous and
// allowed; it takes both halves being numbers to create the collision.
if (/^\d+$/.test(normalizedLabel) && /^\d+$/.test(normalizedTld)) return null;

return { label: normalizedLabel, tld: normalizedTld };
}

Expand Down
39 changes: 36 additions & 3 deletions apps/pwa/test/moshpit-name.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -23,16 +23,23 @@ test("normalizeTld rejects what could never be a TLD", () => {
assert.equal(normalizeTld("-eggs"), null);
assert.equal(normalizeTld("eggs-"), null);
assert.equal(normalizeTld("egg s"), null);
assert.equal(normalizeTld("123"), null, "ambiguous against an IPv4 literal");
assert.equal(normalizeTld("a".repeat(64)), null);
assert.equal(normalizeTld(null), null);
assert.equal(normalizeTld(undefined), null);
});

test("hostname labels may be numeric even though TLDs may not", () => {
test("either half of a name may be numeric — but not both", () => {
assert.equal(normalizeLabel("123"), "123");
assert.equal(normalizeTld("123"), null);
// .420 and .187 are endings people want, and an ending on its own is never
// mistaken for an address.
assert.equal(normalizeTld("123"), "123");
assert.deepEqual(parseMoshpitName("123.eggs"), { label: "123", tld: "eggs" });
assert.deepEqual(parseMoshpitName("blue.420"), { label: "blue", tld: "420" });

// Both halves numeric is where the IPv4 ambiguity actually lives: several
// parsers read a two-part dotted number as an abbreviated address.
assert.equal(parseMoshpitName("1.420"), null);
assert.equal(parseMoshpitName("192.168"), null);
});

test("reserved names cannot be claimed", () => {
Expand Down Expand Up @@ -92,3 +99,29 @@ test("overriding DNS is opt-in, never the default", () => {
assert.notEqual(resolutionPreference({ registered: true, mode }), "moshpit");
}
});

test("all-numeric endings", async (t) => {
const { normalizeTld, parseMoshpitName } = await import("../src/lib/moshpit-name.mjs");

await t.test("an ending may be all digits", () => {
// .420, .187, .911 are names people want; an ending on its own is never
// mistaken for an address.
for (const [input, expected] of [[".420", "420"], ["187", "187"], [".911", "911"], ["0", "0"]]) {
assert.equal(normalizeTld(input), expected, input);
}
});

await t.test("a name is fine when only one half is numeric", () => {
assert.deepEqual(parseMoshpitName("blue.420"), { label: "blue", tld: "420" });
assert.deepEqual(parseMoshpitName("bud.420"), { label: "bud", tld: "420" });
assert.deepEqual(parseMoshpitName("420.blue"), { label: "420", tld: "blue" });
});

await t.test("both halves numeric is refused — that reads as an address", () => {
// Several parsers read a two-part dotted number as an abbreviated IPv4,
// so `1.420` is genuinely ambiguous where `blue.420` is not.
assert.equal(parseMoshpitName("1.420"), null);
assert.equal(parseMoshpitName("192.168"), null);
assert.equal(parseMoshpitName("0.0"), null);
});
});
6 changes: 4 additions & 2 deletions apps/pwa/test/moshpit-registry.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -226,8 +226,10 @@ test("moshpit registry", { skip: installed ? false : "pwa dependencies not insta
assert.equal(await m.getName("eggs", "123"), null);
});

await t.test("the TLD itself still may not be numeric", async () => {
assert.equal((await m.registerTld({ tld: "123", userId: ALICE })).ok, false);
await t.test("an all-numeric ending can be claimed", async () => {
// .420, .187, .911. The IPv4 ambiguity is a property of a whole hostname,
// not of an ending, so it is enforced by parseMoshpitName instead.
assert.equal((await m.registerTld({ tld: "420", userId: ALICE })).ok, true);
});

await t.test("minting is logged", async () => {
Expand Down
Loading