chahinebrini 6962e09403 feat(devices): Windows 11 DoH protection — reg-file endpoint + tests
- Add server/utils/regfile.ts: generateWindowsDohRegFile() producing
  UTF-16 LE + BOM .reg content for DohWellKnownServers registry path.
  label and dohTemplate values are properly escape'd (\, ", \n, \r, \t).
- Add GET /api/devices/:id/profile.reg — public, windows-platform-gated,
  returns octet-stream with Content-Disposition attachment.
- Update enroll.post.ts: downloadUrl is now platform-aware
  (windows → .reg, all others → .mobileconfig).
- Add tests/devices/regfile.test.ts: 13 tests covering BOM, CRLF,
  token embed, subkey naming, AutoUpgradeFlag, label escaping (", \, \n),
  and labelToSlug edge cases. All 111 tests pass.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-11 04:48:51 +02:00

84 lines
2.7 KiB
TypeScript

/**
* Windows Registry file generation for DoH (DNS-over-HTTPS) protection setup.
*
* Windows 11 native DoH client configuration via registry:
* - HKLM\SYSTEM\CurrentControlSet\Services\Dnscache\Parameters\DohWellKnownServers
* - Subkey per DoH server with DohTemplate + AutoUpgradeFlag
*
* Registry file encoding: UTF-16 LE with BOM (\xFF\xFE).
* Line endings: CRLF (\r\n) — required by regedit.exe.
*
* Escape rules for .reg string values:
* \ → \\
* " → \"
* (newlines, tabs in string values would also need escaping but label is
* limited to printable user input, so \n/\t coverage is defensive.)
*/
/** Escape a string for use inside a .reg double-quoted value. */
function regEscape(str: string): string {
return str
.replace(/\\/g, "\\\\") // must be first
.replace(/"/g, '\\"')
.replace(/\r/g, "\\r")
.replace(/\n/g, "\\n")
.replace(/\t/g, "\\t");
}
export interface WindowsDohRegOpts {
/** ProtectedDevice.id — not used in file body but available for future use. */
deviceId: string;
/** 32-char hex DNS token — used in DoH URL. */
dnsToken: string;
/** User-set device label, e.g. "Büro-PC". */
label: string;
}
/**
* Generates the text content of a Windows .reg file that registers a
* ReBreak DoH server in the Windows 11 DoH well-known-servers list.
*
* Returns a UTF-16 LE Buffer with BOM — ready to write as .reg file or send
* as HTTP response body. Windows regedit.exe requires this encoding.
*/
export function generateWindowsDohRegFile(
opts: WindowsDohRegOpts,
): Buffer {
const { dnsToken, label } = opts;
const tokenPrefix = dnsToken.slice(0, 8);
const subkeyName = `rebreak-${tokenPrefix}`;
const escapedLabel = regEscape(label);
// RFC 8484 URI template — {?dns} is the query parameter for GET requests.
// Windows DoH client replaces {?dns} with ?dns=<base64url-encoded-query>.
const dohTemplate = `https://dns.rebreak.org/api/dns/${dnsToken}/dns-query{?dns}`;
const isoDate = new Date().toISOString().slice(0, 10);
const CRLF = "\r\n";
const lines = [
"Windows Registry Editor Version 5.00",
"",
`; ReBreak DNS-over-HTTPS Filter — Device: ${escapedLabel}`,
`; Token: ${tokenPrefix}`,
`; Generated: ${isoDate}`,
"",
"[HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services\\Dnscache\\Parameters\\DohWellKnownServers]",
"",
`[HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services\\Dnscache\\Parameters\\DohWellKnownServers\\${subkeyName}]`,
`"DohTemplate"="${regEscape(dohTemplate)}"`,
'"AutoUpgradeFlag"=dword:00000001',
"",
];
const text = lines.join(CRLF);
// UTF-16 LE BOM: 0xFF 0xFE
const bom = Buffer.from([0xff, 0xfe]);
const body = Buffer.from(text, "utf16le");
return Buffer.concat([bom, body]);
}