Files
Recordly/electron/macosDistributionPolicy.test.mjs

140 lines
4.6 KiB
JavaScript

import { describe, expect, it } from "vitest";
import {
assertValidAppleTeamId,
collectArchitectureErrors,
collectCodeSigningMetadataErrors,
collectEntitlementErrors,
expectedMachOArchitecture,
hasMachOMagic,
parseLipoArchitectures,
} from "../scripts/macos-distribution-policy.mjs";
const validCodeSigningDetails = `
Identifier=dev.recordly.app
CodeDirectory v=20500 size=123 flags=0x10000(runtime) hashes=3+7 location=embedded
Authority=Developer ID Application: Recordly Developer (A1B2C3D4E5)
Authority=Developer ID Certification Authority
Authority=Apple Root CA
Timestamp=Aug 9, 2026 at 10:00:00
TeamIdentifier=A1B2C3D4E5
`;
describe("macOS distribution signing policy", () => {
it("accepts a valid Developer ID signature", () => {
expect(collectCodeSigningMetadataErrors(validCodeSigningDetails, "A1B2C3D4E5")).toEqual([]);
});
it("rejects a wrong identity, team, timestamp, runtime, and bundle identifier", () => {
const details = `
Identifier=dev.example.app
CodeDirectory v=20400 size=123 flags=0x0(none)
Authority=Apple Development: Example (Z9Y8X7W6V5)
Timestamp=none
TeamIdentifier=Z9Y8X7W6V5
`;
expect(collectCodeSigningMetadataErrors(details, "A1B2C3D4E5")).toEqual([
"unexpected bundle identifier: dev.example.app",
"the leaf signing authority is not Developer ID Application",
"unexpected TeamIdentifier: Z9Y8X7W6V5",
"secure signing timestamp is missing",
"hardened runtime flag is missing",
]);
});
it("validates the expected Apple team ID shape", () => {
expect(() => assertValidAppleTeamId("A1B2C3D4E5")).not.toThrow();
expect(() => assertValidAppleTeamId("short")).toThrow(/exactly 10/);
});
});
describe("macOS distribution entitlement policy", () => {
const validEntitlements = {
"com.apple.security.cs.allow-jit": true,
"com.apple.security.cs.allow-unsigned-executable-memory": true,
"com.apple.security.cs.disable-library-validation": true,
"com.apple.security.device.audio-input": true,
"com.apple.security.device.camera": true,
};
it("accepts the intended production entitlements", () => {
expect(collectEntitlementErrors(validEntitlements)).toEqual([]);
});
it("rejects missing capabilities and debug attachment", () => {
expect(
collectEntitlementErrors({
...validEntitlements,
"com.apple.security.cs.allow-jit": false,
"com.apple.security.get-task-allow": true,
}),
).toEqual([
"required entitlement is missing or disabled: com.apple.security.cs.allow-jit",
"distribution build must not enable com.apple.security.get-task-allow",
]);
});
it("rejects unreviewed root runtime exceptions", () => {
expect(
collectEntitlementErrors({
...validEntitlements,
"com.apple.security.cs.allow-dyld-environment-variables": true,
}),
).toEqual([
"unexpected root application entitlement: com.apple.security.cs.allow-dyld-environment-variables",
]);
});
it("rejects disabled but unreviewed entitlement keys", () => {
expect(
collectEntitlementErrors({
...validEntitlements,
"com.apple.security.get-task-allow": false,
}),
).toEqual(["unexpected root application entitlement: com.apple.security.get-task-allow"]);
});
});
describe("macOS distribution architecture policy", () => {
it("recognizes thin and universal Mach-O magic bytes without parsing file output", () => {
for (const header of [
[0xfe, 0xed, 0xfa, 0xce],
[0xcf, 0xfa, 0xed, 0xfe],
[0xca, 0xfe, 0xba, 0xbe],
[0xbf, 0xba, 0xfe, 0xca],
]) {
expect(hasMachOMagic(Uint8Array.from(header))).toBe(true);
}
expect(hasMachOMagic(Uint8Array.from([0x7f, 0x45, 0x4c, 0x46]))).toBe(false);
expect(hasMachOMagic(Uint8Array.from([0xfe, 0xed, 0xfa]))).toBe(false);
});
it("parses thin and fat lipo output", () => {
expect(parseLipoArchitectures("Non-fat file: App is architecture: arm64")).toEqual([
"arm64",
]);
expect(
parseLipoArchitectures("Architectures in the fat file: App are: x86_64 arm64"),
).toEqual(["x86_64", "arm64"]);
});
it("uses path-specific helper architecture before the build architecture", () => {
expect(
expectedMachOArchitecture("app/electron/native/bin/darwin-arm64/helper", "x64"),
).toBe("arm64");
expect(
expectedMachOArchitecture("app/electron/native/bin/darwin-x64/helper", "arm64"),
).toBe("x86_64");
expect(expectedMachOArchitecture("Recordly.app/Contents/MacOS/Recordly", "x64")).toBe(
"x86_64",
);
});
it("reports a binary that lacks the required architecture", () => {
expect(
collectArchitectureErrors("Recordly.app/Contents/MacOS/Recordly", "arm64", "x64"),
).toEqual(["Recordly.app/Contents/MacOS/Recordly does not contain x86_64 (found: arm64)"]);
});
});