Ctrl K
ComunidadGuías…

test/shared/attachment-url.test.ts

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
import { expect } from "@std/expect";
import { afterEach, beforeEach, describe, it as test } from "@std/testing/bdd";
import { FakeTime } from "@std/testing/time";
import {
  signAttachmentUrl,
  verifyAttachmentUrl,
} from "#shared/attachment-url.ts";
import { matchGroup } from "#test-utils/assertions.ts";
import { setupTestEncryptionKey } from "#test-utils/env.ts";

describe("signAttachmentUrl", () => {
  let fakeTime: FakeTime;

  beforeEach(() => {
    setupTestEncryptionKey();
    fakeTime = new FakeTime(1700000000000);
  });

  afterEach(() => {
    fakeTime.restore();
  });

  test("produces URL with correct path format", async () => {
    const url = await signAttachmentUrl(42, 7);
    expect(url).toMatch(/^\/attachment\/42\?a=7&exp=\d+&sig=.+$/);
  });

  test("includes numeric expiry timestamp roughly 3600s in the future", async () => {
    const url = await signAttachmentUrl(1, 2);
    const exp = Number.parseInt(matchGroup(url, /exp=(\d+)/), 10);
    const nowS = Math.floor(1700000000000 / 1000);
    expect(exp - nowS).toBe(3600);
  });

  test("generates unique signatures for different listing/attendee combos", async () => {
    const url1 = await signAttachmentUrl(1, 2);
    const url2 = await signAttachmentUrl(1, 3);
    const url3 = await signAttachmentUrl(2, 2);
    const sig1 = new URL(`http://x${url1}`).searchParams.get("sig");
    const sig2 = new URL(`http://x${url2}`).searchParams.get("sig");
    const sig3 = new URL(`http://x${url3}`).searchParams.get("sig");
    expect(sig1).not.toBe(sig2);
    expect(sig1).not.toBe(sig3);
    expect(sig2).not.toBe(sig3);
  });

  test("uses base64url encoding in signature", async () => {
    const url = await signAttachmentUrl(1, 2);
    const sig = new URL(`http://x${url}`).searchParams.get("sig")!;
    expect(sig).not.toMatch(/[+/=]/);
  });
});

describe("verifyAttachmentUrl", () => {
  let fakeTime: FakeTime;

  beforeEach(() => {
    setupTestEncryptionKey();
    fakeTime = new FakeTime(1700000000000);
  });

  afterEach(() => {
    fakeTime.restore();
  });

  const signAndVerify = async (midStep?: () => void): Promise<boolean> => {
    const url = await signAttachmentUrl(5, 10);
    const params = new URL(`http://x${url}`).searchParams;
    midStep?.();
    return verifyAttachmentUrl(5, 10, params.get("exp")!, params.get("sig")!);
  };

  test("accepts a freshly signed URL's parameters", async () => {
    expect(await signAndVerify()).toBe(true);
  });

  test("rejects tampered signature", async () => {
    const url = await signAttachmentUrl(5, 10);
    const params = new URL(`http://x${url}`).searchParams;
    const tampered = `${params.get("sig")?.slice(0, -4)}XXXX`;
    const result = await verifyAttachmentUrl(
      5,
      10,
      params.get("exp")!,
      tampered,
    );
    expect(result).toBe(false);
  });

  test("rejects wrong listing ID", async () => {
    const url = await signAttachmentUrl(5, 10);
    const params = new URL(`http://x${url}`).searchParams;
    const result = await verifyAttachmentUrl(
      999,
      10,
      params.get("exp")!,
      params.get("sig")!,
    );
    expect(result).toBe(false);
  });

  test("rejects wrong attendee ID", async () => {
    const url = await signAttachmentUrl(5, 10);
    const params = new URL(`http://x${url}`).searchParams;
    const result = await verifyAttachmentUrl(
      5,
      999,
      params.get("exp")!,
      params.get("sig")!,
    );
    expect(result).toBe(false);
  });

  test("rejects expired URL", async () => {
    expect(await signAndVerify(() => fakeTime.tick(3601 * 1000))).toBe(false);
  });

  test("rejects non-numeric exp", async () => {
    const result = await verifyAttachmentUrl(5, 10, "notanumber", "somesig");
    expect(result).toBe(false);
  });

  test("rejects exp far in the future beyond MAX_AGE_S + 60", async () => {
    const farFutureExp = String(Math.floor(1700000000000 / 1000) + 3600 + 61);
    const result = await verifyAttachmentUrl(5, 10, farFutureExp, "somesig");
    expect(result).toBe(false);
  });

  test("rejects empty string sig", async () => {
    const url = await signAttachmentUrl(5, 10);
    const params = new URL(`http://x${url}`).searchParams;
    const result = await verifyAttachmentUrl(5, 10, params.get("exp")!, "");
    expect(result).toBe(false);
  });
});