Fix production relay onboarding safety

Co-authored-by: npub1cl47vfhsqpqy9pwndphpm36vcp7vvz5h2js4qpqm5yewzj7nutkq7xyw8c <c7ebe626f000404285d3686e1dc74cc07cc60a9754a150041ba132e14bd3e2ec@buzz.block.builderlab.xyz>
Signed-off-by: npub1cl47vfhsqpqy9pwndphpm36vcp7vvz5h2js4qpqm5yewzj7nutkq7xyw8c <c7ebe626f000404285d3686e1dc74cc07cc60a9754a150041ba132e14bd3e2ec@buzz.block.builderlab.xyz>
This commit is contained in:
npub1cl47vfhsqpqy9pwndphpm36vcp7vvz5h2js4qpqm5yewzj7nutkq7xyw8c
2026-07-23 10:55:26 -07:00
parent 12293366fd
commit dc8d7b824f
4 changed files with 48 additions and 21 deletions

View File

@@ -54,6 +54,8 @@ test("signed-build relay defaults auto-connect during first-run onboarding", ()
);
assert.equal(shouldAutoConnectDefaultRelay("ws://localhost:3000"), false);
assert.equal(shouldAutoConnectDefaultRelay("ws://127.0.0.1:3000"), false);
assert.equal(shouldAutoConnectDefaultRelay("ws://[::1]:3000"), false);
assert.equal(shouldAutoConnectDefaultRelay("ws://0.0.0.0:3000"), false);
assert.equal(shouldAutoConnectDefaultRelay("not a valid relay"), false);
});

View File

@@ -81,8 +81,11 @@ export function loadCommunities(): Community[] {
}
}
export function saveCommunities(communities: Community[]): void {
setLocalStorageItemWithRecovery(COMMUNITIES_KEY, JSON.stringify(communities));
export function saveCommunities(communities: Community[]): boolean {
return setLocalStorageItemWithRecovery(
COMMUNITIES_KEY,
JSON.stringify(communities),
);
}
export function clearCommunityStorage(storage: Storage = localStorage): void {
@@ -97,8 +100,8 @@ export function loadActiveCommunityId(): string | null {
return localStorage.getItem(ACTIVE_COMMUNITY_KEY);
}
export function saveActiveCommunityId(id: string): void {
setLocalStorageItemWithRecovery(ACTIVE_COMMUNITY_KEY, id);
export function saveActiveCommunityId(id: string): boolean {
return setLocalStorageItemWithRecovery(ACTIVE_COMMUNITY_KEY, id);
}
export function normalizeRelayUrl(url: string): string {
@@ -111,7 +114,9 @@ export function normalizeRelayUrl(url: string): string {
export function shouldAutoConnectDefaultRelay(relayUrl: string): boolean {
try {
const parsed = new URL(normalizeRelayUrl(relayUrl));
return parsed.hostname !== "localhost" && parsed.hostname !== "127.0.0.1";
return !["localhost", "127.0.0.1", "[::1]", "0.0.0.0"].includes(
parsed.hostname,
);
} catch {
return false;
}
@@ -145,7 +150,7 @@ export function initFirstCommunity(
relayUrl: string,
pubkey: string,
name?: string,
): Community {
): Community | null {
const normalizedUrl = normalizeRelayUrl(relayUrl);
const trimmedName = name?.trim();
const community: Community = {
@@ -155,7 +160,16 @@ export function initFirstCommunity(
pubkey,
addedAt: new Date().toISOString(),
};
saveCommunities([community]);
saveActiveCommunityId(community.id);
const didSaveCommunities = saveCommunities([community]);
const didSaveActiveCommunity =
didSaveCommunities && saveActiveCommunityId(community.id);
// The quota-recovery helper reports failure instead of throwing. Do not let
// callers reload unless both writes landed; otherwise first launch can loop.
if (!didSaveCommunities || !didSaveActiveCommunity) {
clearCommunityStorage();
return null;
}
return community;
}

View File

@@ -107,9 +107,20 @@ export function useCommunityInit(
) {
const identity = await getIdentity();
if (cancelled) return;
initFirstCommunity(defaultRelayUrl, identity.pubkey);
if (!cancelled) {
const community = initFirstCommunity(
defaultRelayUrl,
identity.pubkey,
);
if (community && !cancelled) {
window.location.reload();
return;
}
if (!cancelled) {
setResult({
isReady: false,
needsSetup: true,
defaultRelayUrl,
});
}
return;
}

View File

@@ -571,7 +571,7 @@ test("first-community choices route join, create, owner, and member intents", as
);
}, BLANK_TYLER_IDENTITY.pubkey);
await installMockBridge(page, undefined, {
relayWsUrl: "wss://default.example.com",
relayWsUrl: "ws://localhost:3000",
skipOnboardingSeed: true,
skipCommunitySeed: true,
});
@@ -657,7 +657,7 @@ test("first-community owner can connect an existing hosted community", async ({
],
},
{
relayWsUrl: "wss://default.example.com",
relayWsUrl: "ws://localhost:3000",
skipOnboardingSeed: true,
skipCommunitySeed: true,
},
@@ -715,7 +715,7 @@ test("first-community owner can create and connect a hosted community", async ({
page,
{},
{
relayWsUrl: "wss://default.example.com",
relayWsUrl: "ws://localhost:3000",
skipOnboardingSeed: true,
skipCommunitySeed: true,
},
@@ -789,7 +789,7 @@ test("hosted community address line stays within the card for a long name", asyn
page,
{},
{
relayWsUrl: "wss://default.example.com",
relayWsUrl: "ws://localhost:3000",
skipOnboardingSeed: true,
skipCommunitySeed: true,
},
@@ -860,7 +860,7 @@ test("first-community reports a created community without a relay address", asyn
},
},
{
relayWsUrl: "wss://default.example.com",
relayWsUrl: "ws://localhost:3000",
skipOnboardingSeed: true,
skipCommunitySeed: true,
},
@@ -891,7 +891,7 @@ test("first-community X cancels a pending sign-in", async ({ page }) => {
page,
{ builderlabLoginDelayMs: 5_000 },
{
relayWsUrl: "wss://default.example.com",
relayWsUrl: "ws://localhost:3000",
skipOnboardingSeed: true,
skipCommunitySeed: true,
},
@@ -933,7 +933,7 @@ test("first-community owner can replace a mismatched account identity", async ({
builderlabIdentity: { pubkey_hex: "f".repeat(64) },
},
{
relayWsUrl: "wss://default.example.com",
relayWsUrl: "ws://localhost:3000",
skipOnboardingSeed: true,
skipCommunitySeed: true,
},
@@ -983,7 +983,7 @@ test("first-community explains when the local identity belongs to another accoun
builderlabBindError: { code: "pubkey_already_bound" },
},
{
relayWsUrl: "wss://default.example.com",
relayWsUrl: "ws://localhost:3000",
skipOnboardingSeed: true,
skipCommunitySeed: true,
},
@@ -1024,7 +1024,7 @@ test("back clears Builderlab auth before returning to first-community choices",
builderlabIdentity: { pubkey_hex: BLANK_TYLER_IDENTITY.pubkey },
},
{
relayWsUrl: "wss://default.example.com",
relayWsUrl: "ws://localhost:3000",
skipOnboardingSeed: true,
skipCommunitySeed: true,
},
@@ -1112,7 +1112,7 @@ test("first-community direct join reaches profile", async ({ page }) => {
);
}, BLANK_TYLER_IDENTITY.pubkey);
await installMockBridge(page, undefined, {
relayWsUrl: "wss://onboarding.communities.buzz.xyz",
relayWsUrl: "ws://localhost:3000",
skipOnboardingSeed: true,
skipCommunitySeed: true,
});
@@ -1167,7 +1167,7 @@ test("first-community direct join cancel returns to request access", async ({
page,
{ applyCommunityDelayMs: 5_000 },
{
relayWsUrl: "wss://onboarding.communities.buzz.xyz",
relayWsUrl: "ws://localhost:3000",
skipOnboardingSeed: true,
skipCommunitySeed: true,
},