# test.build/core/validators/security.validator.test.ts

> 80 lines of code and 0 definitions.

Tree: Codebase Testing
Language: typescript
Canonical: https://banes-lab.com/anatomy/testing#file-testing-test-build-core-validators-security-validator-test-ts
Source text: https://banes-lab.com/source/testing/test.build/core/validators/security.validator.test.ts.txt

Listed in [test.build/core/validators](https://banes-lab.com/api/source/testing/test.build/core/validators.md), after [test.build/core/validators/schema.validator.test.ts](https://banes-lab.com/source/testing/test.build/core/validators/schema.validator.test.ts.md) and before [test.build/core/validators/site.validator.test.ts](https://banes-lab.com/source/testing/test.build/core/validators/site.validator.test.ts.md).

## Contained in

- [test.build/core/validators](https://banes-lab.com/anatomy/testing/folder-testing-test-build-core-validators.md)

## Source

```typescript
import { type TestKey, testKey } from "../adapters/security.fixture.ts";
import {
    UNSIGNED_SECURITY_TXT,
    WANTED_SECURITY_CONTACT,
    WANTED_SECURITY_EXPIRY,
    malformedSecurityField,
} from "@banes-lab/build-scripts/configuration/strings/site.strings.ts";
import { afterAll, beforeAll, describe, expect, it, vi } from "vitest";
import {
    checkSecurityTxt,
    checkSignature,
    securityFindings,
} from "@banes-lab/build-scripts/core/validators/security.validator.ts";
import { MISSING_BUILD_FILE } from "@banes-lab/build-scripts/configuration/strings/validation.strings.ts";
import { signText } from "@banes-lab/build-scripts/core/adapters/security.adapter.ts";

const { BUILT } = vi.hoisted(() => ({ BUILT: new Map<string, string>() }));

vi.mock("@banes-lab/build-scripts/core/loaders/build.loader.ts", async (original) => ({
    ...(await original<object>()),
    readOrNull: (file: string) => BUILT.get(file) ?? null,
}));

const now = new Date("2026-10-08T00:00:00Z");
const site = "https://example.test";
const canonical = `Canonical: ${site}/.well-known/security.txt`;
const encryption = `Encryption: ${site}/.well-known/pgp-key.txt`;

describe("checkSecurityTxt", () => {
    it("accepts a mailto contact, its public key, an expiry within a year and its own canonical address", () => {
        const text = ["Contact: mailto:a@example.test", encryption, "Expires: 2027-10-01T00:00:00.000Z", canonical].join(
            "\n",
        );
        expect(checkSecurityTxt("s.txt", text, site, now)).toStrictEqual([]);
    });

    it("reports a missing or foreign public key address", () => {
        const text = ["Contact: mailto:a@example.test", "Expires: 2027-10-01T00:00:00.000Z", canonical].join("\n");
        expect(checkSecurityTxt("s.txt", text, site, now)).toStrictEqual([
            { file: "s.txt", message: malformedSecurityField("Encryption", "", `${site}/.well-known/pgp-key.txt`) },
        ]);
    });

    it("reports a contact without mailto, a lapsed or distant expiry and a foreign canonical", () => {
        const lapsed = ["Contact: a@example.test", encryption, "Expires: 2026-01-01T00:00:00.000Z", canonical].join("\n");
        expect(checkSecurityTxt("s.txt", lapsed, site, now)).toStrictEqual([
            {
                file: "s.txt",
                message: malformedSecurityField("Contact", "a@example.test", WANTED_SECURITY_CONTACT),
            },
            {
                file: "s.txt",
                message: malformedSecurityField("Expires", "2026-01-01T00:00:00.000Z", WANTED_SECURITY_EXPIRY),
            },
        ]);
        const distant = ["Contact: mailto:a@example.test", encryption, "Expires: 2028-01-01T00:00:00.000Z"].join("\n");
        expect(checkSecurityTxt("s.txt", distant, site, now)).toHaveLength(2);
    });
});

describe("checkSignature", () => {
    let key: TestKey | null = null;

    beforeAll(() => {
        key = testKey();
    });

    afterAll(() => {
        key?.dispose();
    });

    it("passes a text signed by the served key, and reports an unsigned one", () => {
        const security = signText(key?.encoded ?? "", `${canonical}\n`);
        expect(checkSignature("s.txt", security.signed, security.publicKey)).toStrictEqual([]);
        expect(checkSignature("s.txt", `${canonical}\n`, security.publicKey)).toStrictEqual([
            { file: "s.txt", message: UNSIGNED_SECURITY_TXT },
        ]);
    });

    it("reads the built security.txt and key, passes a signed file and reports a missing one", () => {
        const text = ["Contact: mailto:a@example.test", encryption, "Expires: 2027-10-01T00:00:00.000Z", canonical, ""];
        const security = signText(key?.encoded ?? "", text.join("\n"));
        BUILT.set(".well-known/security.txt", security.signed);
        BUILT.set(".well-known/pgp-key.txt", security.publicKey);
        expect(securityFindings(site, now)).toStrictEqual([]);
        BUILT.clear();
        expect(securityFindings(site, now)).toStrictEqual([
            { file: ".well-known/security.txt", message: MISSING_BUILD_FILE },
        ]);
    });
});
```
