From 47eadb77be3a6d72d9685277d383c3fcb1b44693 Mon Sep 17 00:00:00 2001 From: Anthony Ronning <101225832+AnthonyRonning@users.noreply.github.com> Date: Fri, 19 Jun 2026 23:22:18 +0000 Subject: [PATCH] Allow dev attestation on loopback ports --- src/lib/attestation.ts | 18 +++++++---- src/lib/test/integration/attestation.test.ts | 34 +++++++++++++++++++- 2 files changed, 45 insertions(+), 7 deletions(-) diff --git a/src/lib/attestation.ts b/src/lib/attestation.ts index 23f5403..f6160e9 100644 --- a/src/lib/attestation.ts +++ b/src/lib/attestation.ts @@ -246,6 +246,17 @@ const FakeAttestationDocumentSchema = z.object({ type FakeAttestationDocument = z.infer; +const LOCAL_DEVELOPMENT_API_HOSTS = new Set(["127.0.0.1", "localhost", "0.0.0.0", "[::1]"]); + +export function isLocalDevelopmentApiUrl(apiUrl: string): boolean { + try { + const url = new URL(apiUrl); + return url.protocol === "http:" && LOCAL_DEVELOPMENT_API_HOSTS.has(url.hostname.toLowerCase()); + } catch { + return false; + } +} + async function fakeAuthenticate( attestationDocumentBase64: string ): Promise { @@ -269,12 +280,7 @@ export async function verifyAttestation( const apiUrl = explicitApiUrl || getApiUrl(); // With a local backend we get a fake attestation document, so we'll just pretend to authenticate it - if ( - apiUrl && - (apiUrl === "http://127.0.0.1:3000" || - apiUrl === "http://localhost:3000" || - apiUrl === "http://0.0.0.0:3000") - ) { + if (apiUrl && isLocalDevelopmentApiUrl(apiUrl)) { console.log("DEV MODE: Using fake attestation document"); const fakeDocument = await fakeAuthenticate(attestationDocumentBase64); return fakeDocument as AttestationDocument; diff --git a/src/lib/test/integration/attestation.test.ts b/src/lib/test/integration/attestation.test.ts index 2696ab7..a0ac3fd 100644 --- a/src/lib/test/integration/attestation.test.ts +++ b/src/lib/test/integration/attestation.test.ts @@ -1,5 +1,10 @@ import { expect, test } from "bun:test"; -import { createSigStructure, parseDocumentData, parseDocumentPayload } from "../../attestation"; +import { + createSigStructure, + isLocalDevelopmentApiUrl, + parseDocumentData, + parseDocumentPayload +} from "../../attestation"; import { encode } from "@stablelib/base64"; const HARDCODED_TEST_ATTESTATION_DOCUMENT = @@ -27,3 +32,30 @@ test("Makes CoseSign1 bytes correctly", async () => { expect(encode(new Uint8Array(hash))).toBe(EXPECTED_SIGNATURE_STRUCTURE_DIGEST); }); + +test("Recognizes local development API URLs independent of port", () => { + const localApiUrls = [ + "http://127.0.0.1:31110", + "http://localhost:31110/", + "http://0.0.0.0:31110", + "http://[::1]:31110" + ]; + + for (const apiUrl of localApiUrls) { + expect(isLocalDevelopmentApiUrl(apiUrl)).toBe(true); + } +}); + +test("Does not recognize production or invalid API URLs as local development URLs", () => { + const nonLocalApiUrls = [ + "https://api.opensecret.cloud", + "https://localhost:31110", + "http://api.opensecret.cloud", + "localhost:31110", + "not a url" + ]; + + for (const apiUrl of nonLocalApiUrls) { + expect(isLocalDevelopmentApiUrl(apiUrl)).toBe(false); + } +});