59 lines
2.9 KiB
TypeScript
59 lines
2.9 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
|
import { emailMatchesPattern } from "../app/utils/emailPattern.js";
|
|
|
|
// emailMatchesPattern backs the ADMIN_EMAILS and WHITELISTED_EMAILS gates.
|
|
// Property under test: a pattern matches the whole address, never a substring.
|
|
describe("emailMatchesPattern", () => {
|
|
it("matches an address that equals the operator pattern exactly", () => {
|
|
expect(emailMatchesPattern("admin@company.com", "admin@company.com")).toBe(true);
|
|
});
|
|
|
|
it("matches a leading-@ domain shorthand against addresses at that domain", () => {
|
|
expect(emailMatchesPattern("@company.com", "alice@company.com")).toBe(true);
|
|
expect(emailMatchesPattern("@company.com", "bob@company.com")).toBe(true);
|
|
});
|
|
|
|
it("rejects a look-alike address that merely contains the pattern", () => {
|
|
// A look-alike address embeds the pattern as a substring; an unanchored
|
|
// match would wrongly accept it.
|
|
expect(emailMatchesPattern("admin@company.com", "evil@admin@company.com.attacker.com")).toBe(
|
|
false
|
|
);
|
|
expect(emailMatchesPattern("@company.com", "evil@company.com.attacker.com")).toBe(false);
|
|
expect(emailMatchesPattern("@company.com", "alice@sub.company.com")).toBe(false);
|
|
});
|
|
|
|
it("rejects a trailing-garbage address (pattern is only a prefix)", () => {
|
|
expect(emailMatchesPattern("admin@company.com", "admin@company.computer-evil.com")).toBe(false);
|
|
});
|
|
|
|
it("rejects a leading-garbage address (pattern is only a suffix)", () => {
|
|
expect(emailMatchesPattern("admin@company.com", "not-admin@company.com")).toBe(false);
|
|
});
|
|
|
|
it("preserves top-level alternation as whole-string alternatives", () => {
|
|
// Guards against anchoring without the non-capturing group, which would
|
|
// turn `^a|b$` into anchored-a OR anchored-b and break multi-address configs.
|
|
const pattern = "alice@x.com|bob@x.com";
|
|
expect(emailMatchesPattern(pattern, "alice@x.com")).toBe(true);
|
|
expect(emailMatchesPattern(pattern, "bob@x.com")).toBe(true);
|
|
expect(emailMatchesPattern(pattern, "eve@x.com")).toBe(false);
|
|
// ...and alternation must not become a substring match either.
|
|
expect(emailMatchesPattern(pattern, "eve+alice@x.com.evil.com")).toBe(false);
|
|
});
|
|
|
|
it("expands domain shorthand inside simple top-level alternation", () => {
|
|
const pattern = "alice@x.com|@company.com";
|
|
expect(emailMatchesPattern(pattern, "alice@x.com")).toBe(true);
|
|
expect(emailMatchesPattern(pattern, "carol@company.com")).toBe(true);
|
|
expect(emailMatchesPattern(pattern, "carol@company.com.evil.com")).toBe(false);
|
|
});
|
|
|
|
it("accepts patterns that already carry their own anchors", () => {
|
|
// Operators who already wrote a fully-anchored pattern keep working:
|
|
// ^(?:^...$)$ accepts exactly the same strings.
|
|
expect(emailMatchesPattern("^ops@company\\.com$", "ops@company.com")).toBe(true);
|
|
expect(emailMatchesPattern("^ops@company\\.com$", "ops@company.com.evil.com")).toBe(false);
|
|
});
|
|
});
|