Ctrl K
ComunidadGuías…

app/utils/urls.test.ts

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

Archivo original ↗Preguntar a KiBot
import {
  decodeURIComponentSafe,
  isAllowedLoginRedirect,
  isTruthyQueryValue,
} from "./urls";

describe("decodeURIComponentSafe", () => {
  it("to handle % symbols", () => {
    expect(decodeURIComponentSafe("%")).toBe("%");
    expect(decodeURIComponentSafe("%25")).toBe("%");
  });

  it("to correctly account for encoded symbols", () => {
    expect(decodeURIComponentSafe("%7D")).toBe("}");
    expect(decodeURIComponentSafe("%2F")).toBe("/");
  });

  it("to handle malformed percent encoding", () => {
    // Single % at end of string
    expect(decodeURIComponentSafe("test%")).toBe("test%");

    // % followed by single hex digit
    expect(decodeURIComponentSafe("test%A")).toBe("test%A");

    // % followed by non-hex characters
    expect(decodeURIComponentSafe("test%GG")).toBe("test%GG");
    expect(decodeURIComponentSafe("test%XY")).toBe("test%XY");

    // % followed by space
    expect(decodeURIComponentSafe("test% ")).toBe("test% ");

    // Multiple malformed % symbols
    expect(decodeURIComponentSafe("%test%")).toBe("%test%");
    expect(decodeURIComponentSafe("%A%B")).toBe("%A%B");
  });

  it("to handle mixed valid and invalid percent encoding", () => {
    // Valid encoding mixed with invalid
    expect(decodeURIComponentSafe("%20test%")).toBe(" test%");
    expect(decodeURIComponentSafe("%7D%invalid")).toBe("}%invalid");
    expect(decodeURIComponentSafe("valid%20%")).toBe("valid %");

    // Complex mixed case
    expect(decodeURIComponentSafe("%20%A%2F%")).toBe(" %A/%");
  });

  it("to handle edge cases", () => {
    // Empty string
    expect(decodeURIComponentSafe("")).toBe("");

    // Only % symbols
    expect(decodeURIComponentSafe("%%%")).toBe("%%%");

    // Language specific characters
    expect(decodeURIComponentSafe("%E3%81%AB%E3%81%BB%E3%82%93%E3%81%94")).toBe(
      "にほんご"
    );

    // % at various positions
    expect(decodeURIComponentSafe("%start")).toBe("%start");
    expect(decodeURIComponentSafe("mid%dle")).toBe("mid%dle");
    expect(decodeURIComponentSafe("end%")).toBe("end%");

    // Already encoded %25 should decode to %
    expect(decodeURIComponentSafe("%25%25")).toBe("%%");
  });

  describe("isAllowedLoginRedirect", () => {
    it("should block disallowed paths", () => {
      expect(isAllowedLoginRedirect("/")).toBe(false);
      expect(isAllowedLoginRedirect("/create")).toBe(false);
      expect(isAllowedLoginRedirect("/home")).toBe(false);
      expect(isAllowedLoginRedirect("/logout")).toBe(false);
    });

    it("should block paths starting with /auth/", () => {
      expect(isAllowedLoginRedirect("/auth/")).toBe(false);
      expect(isAllowedLoginRedirect("/auth/login")).toBe(false);
      expect(isAllowedLoginRedirect("/auth/signup")).toBe(false);
      expect(isAllowedLoginRedirect("/auth/callback")).toBe(false);
      expect(isAllowedLoginRedirect("/auth/google")).toBe(false);
      expect(isAllowedLoginRedirect("/auth/slack/callback")).toBe(false);
    });

    it("should block paths starting with /s/", () => {
      expect(isAllowedLoginRedirect("/s/")).toBe(false);
      expect(isAllowedLoginRedirect("/s/share")).toBe(false);
      expect(isAllowedLoginRedirect("/s/public")).toBe(false);
      expect(isAllowedLoginRedirect("/s/document/123")).toBe(false);
      expect(isAllowedLoginRedirect("/s/collection/456")).toBe(false);
    });

    it("should allow valid redirect paths", () => {
      expect(isAllowedLoginRedirect("/documents")).toBe(true);
      expect(isAllowedLoginRedirect("/settings")).toBe(true);
      expect(isAllowedLoginRedirect("/team")).toBe(true);
      expect(isAllowedLoginRedirect("/profile")).toBe(true);
      expect(isAllowedLoginRedirect("/document/123")).toBe(true);
      expect(isAllowedLoginRedirect("/collection/456")).toBe(true);
    });

    it("should strip query strings before checking", () => {
      expect(isAllowedLoginRedirect("/?foo=bar")).toBe(false);
      expect(isAllowedLoginRedirect("/create?id=123")).toBe(false);
      expect(isAllowedLoginRedirect("/home?tab=recent")).toBe(false);
      expect(isAllowedLoginRedirect("/logout?redirect=true")).toBe(false);
      expect(isAllowedLoginRedirect("/auth/login?next=/docs")).toBe(false);
      expect(isAllowedLoginRedirect("/s/share?token=abc")).toBe(false);
      expect(isAllowedLoginRedirect("/documents?search=test")).toBe(true);
      expect(isAllowedLoginRedirect("/settings?tab=profile")).toBe(true);
    });

    it("should handle edge cases", () => {
      expect(isAllowedLoginRedirect("")).toBe(true);
      expect(isAllowedLoginRedirect("/authenticate")).toBe(true); // doesn't start with /auth/
      expect(isAllowedLoginRedirect("/authorization")).toBe(true); // doesn't start with /auth/
      expect(isAllowedLoginRedirect("/authtest")).toBe(true); // doesn't start with /auth/
      expect(isAllowedLoginRedirect("/auth")).toBe(true); // doesn't start with /auth/
      expect(isAllowedLoginRedirect("/share")).toBe(true); // doesn't start with /s/
      expect(isAllowedLoginRedirect("/support")).toBe(true); // doesn't start with /s/
      expect(isAllowedLoginRedirect("/s")).toBe(true); // doesn't start with /s/
    });

    it("should handle paths with fragments", () => {
      expect(isAllowedLoginRedirect("/documents#section1")).toBe(true);
      expect(isAllowedLoginRedirect("/auth/login#forgot")).toBe(false);
      expect(isAllowedLoginRedirect("/s/share#public")).toBe(false);
      expect(isAllowedLoginRedirect("/#top")).toBe(false);
    });

    it("should block protocol-relative paths", () => {
      expect(isAllowedLoginRedirect("//home")).toBe(false);
      expect(isAllowedLoginRedirect("//evil.com")).toBe(false);
      expect(isAllowedLoginRedirect("//evil.com/path")).toBe(false);
      expect(isAllowedLoginRedirect("/\\evil.com")).toBe(false);
      expect(isAllowedLoginRedirect("//documents?foo=bar")).toBe(false);
    });
  });
});

describe("isTruthyQueryValue", () => {
  it("treats common affirmative values as truthy", () => {
    expect(isTruthyQueryValue("true")).toBe(true);
    expect(isTruthyQueryValue("1")).toBe(true);
    expect(isTruthyQueryValue("on")).toBe(true);
    expect(isTruthyQueryValue("yes")).toBe(true);
  });

  it("is case insensitive", () => {
    expect(isTruthyQueryValue("TRUE")).toBe(true);
    expect(isTruthyQueryValue("On")).toBe(true);
    expect(isTruthyQueryValue("Yes")).toBe(true);
  });

  it("treats a bare flag with no value as truthy", () => {
    expect(isTruthyQueryValue("")).toBe(true);
  });

  it("treats absent values as falsy", () => {
    expect(isTruthyQueryValue(null)).toBe(false);
    expect(isTruthyQueryValue(undefined)).toBe(false);
  });

  it("treats negative and unrecognized values as falsy", () => {
    expect(isTruthyQueryValue("false")).toBe(false);
    expect(isTruthyQueryValue("0")).toBe(false);
    expect(isTruthyQueryValue("off")).toBe(false);
    expect(isTruthyQueryValue("no")).toBe(false);
    expect(isTruthyQueryValue("maybe")).toBe(false);
  });
});