"use strict"; const test = require("node:test"); const assert = require("node:assert/strict"); const fs = require("node:fs"); const os = require("node:os"); const path = require("node:path"); const { uuidCommands, launcherUuid, prepareMacNetworkIdentity, verifyMacNetworkIdentity } = require("../scripts/macNetworkIdentity"); function thin({ little = true, bits = 64, cpu = 0x100000c } = {}) { const header = bits === 64 ? 32 : 28; const bytes = Buffer.alloc(header + 24 + 16, 0); const write = (value, offset) => little ? bytes.writeUInt32LE(value, offset) : bytes.writeUInt32BE(value, offset); write(bits === 64 ? 0xfeedfacf : 0xfeedface, 0); write(cpu, 4); write(2, 12); write(1, 16); write(24, 20); write(0x1b, header); write(24, header + 4); bytes.fill(0xab, header + 8, header + 24); bytes.fill(0xcd, header + 24); return bytes; } function universal({ little = false, fat64 = false } = {}) { const slices = [thin(), thin({ cpu: 0x1000007 })]; const stride = fat64 ? 32 : 20; const header = 8 + slices.length * stride; const bytes = Buffer.alloc(header + slices.reduce((sum, slice) => sum + slice.length, 0)); const write = (value, offset) => little ? bytes.writeUInt32LE(value, offset) : bytes.writeUInt32BE(value, offset); const write64 = (value, offset) => little ? bytes.writeBigUInt64LE(BigInt(value), offset) : bytes.writeBigUInt64BE(BigInt(value), offset); write(fat64 ? 0xcafebabf : 0xcafebabe, 0); write(2, 4); let offset = header; slices.forEach((slice, i) => { const cursor = 8 + i * stride; write(slice.readUInt32LE(4), cursor); if (fat64) { write64(offset, cursor + 8); write64(slice.length, cursor + 16); } else { write(offset, cursor + 8); write(slice.length, cursor + 12); } slice.copy(bytes, offset); offset += slice.length; }); return bytes; } test("launcher UUID replacement preserves all other bytes for thin and universal Mach-O files", () => { for (const little of [true, false]) { for (const bytes of [thin({ little }), thin({ little, bits: 32 }), universal({ little }), universal({ little, fat64: true })]) { const original = Buffer.from(bytes); const commands = uuidCommands(bytes); for (const entry of commands) launcherUuid(["de.ch-j.servermanager", "main", "payload"], entry).copy(bytes, entry.offset); for (const entry of commands) original.copy(bytes, entry.offset, entry.offset, entry.offset + 16); assert.deepEqual(bytes, original); } } }); test("UUIDs are deterministic and differ across apps, payloads, launchers and architectures", () => { const entry = uuidCommands(thin())[0]; const identity = ["de.ch-j.servermanager", "main", "payload"]; const expected = launcherUuid(identity, entry); assert.deepEqual(launcherUuid(identity, entry), expected); for (const changed of [["other.app", "main", "payload"], [identity[0], "helper", "payload"], [identity[0], "main", "new-payload"]]) { assert.notDeepEqual(launcherUuid(changed, entry), expected); } assert.notDeepEqual(launcherUuid(identity, { ...entry, cpuType: 0x1000007 }), expected); assert.equal(expected[6] >> 4, 8); assert.equal(expected[8] >> 6, 2); }); test("malformed Mach-O files fail before they can be patched", () => { assert.throws(() => uuidCommands(Buffer.from("not a Mach-O"))); const missing = thin(); missing.writeUInt32LE(0x19, 32); assert.throws(() => uuidCommands(missing), /exactly one LC_UUID/); const badCommand = thin(); badCommand.writeUInt32LE(0, 36); assert.throws(() => uuidCommands(badCommand), /load command/); assert.throws(() => uuidCommands(thin().subarray(0, 50)), /bounds/); const badSlice = universal(); badSlice.writeUInt32BE(1, 16); assert.throws(() => uuidCommands(badSlice), /Overlapping/); const hugeSlice = universal({ fat64: true }); hugeSlice.writeBigUInt64BE(2n ** 60n, 16); assert.throws(() => uuidCommands(hugeSlice), /bounds/); }); test("packaging validates plist, payload and all launchers and is idempotent", { skip: process.platform !== "darwin" }, (t) => { const root = fs.mkdtempSync(path.join(os.tmpdir(), "chj-network-identity-")); t.after(() => fs.rmSync(root, { recursive: true, force: true })); const app = path.join(root, "Test.app"); const expected = { appId: "de.ch-j.servermanager", version: "0.0.1" }; function bundle(dir, id) { fs.mkdirSync(path.join(dir, "Contents", "MacOS"), { recursive: true }); fs.writeFileSync(path.join(dir, "Contents", "Info.plist"), ` CFBundleIdentifier${id} CFBundleExecutableTest CFBundleShortVersionString0.0.1 NSLocalNetworkUsageDescriptionSSH access to local servers. `); const executable = path.join(dir, "Contents", "MacOS", "Test"); fs.writeFileSync(executable, universal(), { mode: 0o755 }); return executable; } const executable = bundle(app, expected.appId); bundle(path.join(app, "Contents", "Frameworks", "Helper.app"), `${expected.appId}.helper`); const resources = path.join(app, "Contents", "Resources"); fs.mkdirSync(resources); fs.writeFileSync(path.join(resources, "app.asar"), "first payload"); assert.throws(() => verifyMacNetworkIdentity(app, expected), /LC_UUID/); const original = fs.readFileSync(executable); assert.throws(() => prepareMacNetworkIdentity(app, { ...expected, appId: "wrong.id" }), /bundle ID/); assert.throws(() => prepareMacNetworkIdentity(app, { ...expected, version: "9.9.9" }), /version/); assert.deepEqual(fs.readFileSync(executable), original); const prepared = prepareMacNetworkIdentity(app, expected); assert.equal(new Set(prepared.map((entry) => entry.uuid)).size, 4); const patched = fs.readFileSync(executable); assert.deepEqual(verifyMacNetworkIdentity(app, expected), prepared); assert.deepEqual(prepareMacNetworkIdentity(app, expected), prepared); assert.deepEqual(fs.readFileSync(executable), patched); assert.equal(fs.statSync(executable).mode & 0o777, 0o755); fs.writeFileSync(path.join(resources, "app.asar"), "second payload"); assert.throws(() => verifyMacNetworkIdentity(app, expected), /LC_UUID/); assert.notDeepEqual(prepareMacNetworkIdentity(app, expected), prepared); });