Docker Image bauen und veroeffentlichen / build-and-push (push) Successful in 2m20s
An earlier change rejected obfuscated entries such as
"info [á] wabenhain.com" in Admin -> Inhalte and replaced them with the
default address. The obfuscation was intentional, so:
- The admin accepts the entry as written (plain or obfuscated with
[at], (at), {ät}, [á], <@>, [dot], [punkt]) and stores it unchanged;
only entries that yield no valid address are rejected.
- lib/contact-email.ts turns the entry into the real address for
mailto links and DHL labels.
- Public pages never contain the address in clear text: footer,
contact page and every email in legal texts (Impressum, Datenschutz,
Widerruf) render "info [at] wabenhain.com" plus an XOR-encoded payload
with a fresh random key; the browser builds the clickable mailto link
after load (components/protected-email.tsx).
Verified with a production build: the raw HTML of /, /kontakt,
/impressum, /datenschutz and /widerrufsbelehrung contains no plain
address, and after load each page shows mailto:[email protected].
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
57 lines
2.3 KiB
TypeScript
57 lines
2.3 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
|
import { decodeEmailPayload, displayEmail, encodeEmailPayload, normalizeContactEmail, protectEmail } from "./contact-email";
|
|
|
|
describe("normalizeContactEmail", () => {
|
|
it.each([
|
|
["[email protected]", "[email protected]"],
|
|
[" [email protected] ", "[email protected]"],
|
|
["info [á] wabenhain.com", "[email protected]"],
|
|
["info [at] wabenhain.com", "[email protected]"],
|
|
["info(at)wabenhain(dot)com", "[email protected]"],
|
|
["info {ät} wabenhain [punkt] com", "[email protected]"],
|
|
["info [AT] wabenhain [DOT] com", "[email protected]"],
|
|
["hallo<@>wabenhain.com", "[email protected]"],
|
|
])("turns %j into a usable address", (input, expected) => {
|
|
expect(normalizeContactEmail(input)).toBe(expected);
|
|
});
|
|
|
|
it.each(["", "hallo", "info [á] ", "info @ wabenhain", "a@[email protected]", "info [á] wabenhain.com <script>"])("rejects %j", (input) => {
|
|
expect(normalizeContactEmail(input)).toBeNull();
|
|
});
|
|
});
|
|
|
|
describe("displayEmail", () => {
|
|
it("shows a readable, not directly harvestable form", () => {
|
|
expect(displayEmail("[email protected]")).toBe("info [at] wabenhain.com");
|
|
});
|
|
});
|
|
|
|
describe("email payload", () => {
|
|
it("round-trips and never contains the address in clear text", () => {
|
|
const payload = encodeEmailPayload("[email protected]", new Uint8Array([7, 42, 199, 3]));
|
|
expect(payload).not.toContain("@");
|
|
expect(payload).not.toContain("wabenhain");
|
|
expect(payload).not.toContain("info");
|
|
expect(decodeEmailPayload(payload)).toBe("[email protected]");
|
|
});
|
|
|
|
it("supports non-ASCII addresses", () => {
|
|
const payload = encodeEmailPayload("jü[email protected]", new Uint8Array([1, 2, 3]));
|
|
expect(decodeEmailPayload(payload)).toBe("jü[email protected]");
|
|
});
|
|
|
|
it("returns null for damaged payloads", () => {
|
|
expect(decodeEmailPayload("kaputt")).toBeNull();
|
|
expect(decodeEmailPayload("0102.zz")).toBeNull();
|
|
expect(decodeEmailPayload("")).toBeNull();
|
|
});
|
|
|
|
it("uses a fresh key per call", () => {
|
|
const a = protectEmail("[email protected]");
|
|
const b = protectEmail("[email protected]");
|
|
expect(a.payload).not.toBe(b.payload);
|
|
expect(decodeEmailPayload(a.payload)).toBe("[email protected]");
|
|
expect(a.fallback).toBe("info [at] wabenhain.com");
|
|
});
|
|
});
|