Code

/**
 * mcp/skills/checkFileVaultStatus.ts — check_filevault_status skill
 *
 * Reports full-disk encryption status — FileVault on macOS, BitLocker on
 * Windows. Use during security compliance verification or when diagnosing
 * boot/authentication issues.
 *
 * Platform strategy
 * -----------------
 * darwin  `fdesetup status` and `fdesetup list`
 * win32   PowerShell Get-BitLockerVolume
 *
 * Smoke test
 *   npx tsx -r dotenv/config mcp/skills/checkFileVaultStatus.ts
 */
 
import * as os       from "os";
import { exec }      from "child_process";
import { promisify } from "util";
import { z }         from "zod";
 
const execAsync = promisify(exec);
 
// -- Meta ---------------------------------------------------------------------
 
export const meta = {
  name: "check_filevault_status",
  description:
    "Reports full-disk encryption status — FileVault on macOS, BitLocker on Windows. " +
    "Use during security compliance verification or when diagnosing boot/authentication issues.",
  riskLevel:       "low",
  destructive:     false,
  requiresConsent: false,
  supportsDryRun:  false,
  affectedScope:   ["user"],
  auditRequired:   false,
  schema: {},
} as const;
 
// -- Types --------------------------------------------------------------------
 
interface FileVaultResult {
  platform:          string;
  enabled:           boolean;
  status:            string;
  encryptionMethod:  string | null;
  encryptionPercent: number | null;
  enabledUsers:      string[];
  message:           string;
}
 
// -- PowerShell helper --------------------------------------------------------
 
async function runPS(script: string): Promise<string> {
  const encoded = Buffer.from(script, "utf16le").toString("base64");
  const { stdout } = await execAsync(
    `powershell.exe -NoProfile -NonInteractive -EncodedCommand ${encoded}`,
    { maxBuffer: 20 * 1024 * 1024 },
  );
  return stdout.trim();
}
 
// -- darwin implementation ----------------------------------------------------
 
async function checkFileVaultDarwin(): Promise<FileVaultResult> {
  // fdesetup status
  let statusOutput = "";
  try {
    ({ stdout: statusOutput } = await execAsync("fdesetup status 2>/dev/null", {
      maxBuffer: 1024 * 1024,
    }));
  } catch (err) {
    statusOutput = (err as { stdout?: string }).stdout ?? "";
  }
 
  const enabled = /FileVault is On/i.test(statusOutput);
  const status  = statusOutput.trim() || "Unknown";
 
  // fdesetup list — may require sudo; best-effort
  let enabledUsers: string[] = [];
  try {
    const { stdout: listOutput } = await execAsync("fdesetup list 2>/dev/null", {
      maxBuffer: 1024 * 1024,
    });
    enabledUsers = listOutput
      .trim()
      .split("\n")
      .filter(Boolean)
      .map((line) => line.split(",")[0].trim())
      .filter(Boolean);
  } catch {
    // May require sudo — silently ignore
  }
 
  return {
    platform:          "darwin",
    enabled,
    status,
    encryptionMethod:  enabled ? "XTS-AES-128" : null,
    encryptionPercent: enabled ? 100 : 0,
    enabledUsers,
    message: enabled
      ? `FileVault is enabled. ${enabledUsers.length} user(s) can unlock the disk.`
      : "FileVault is disabled. Disk is not encrypted.",
  };
}
 
// -- win32 implementation -----------------------------------------------------
 
async function checkFileVaultWin32(): Promise<FileVaultResult> {
  const ps = `
$ErrorActionPreference = 'SilentlyContinue'
$vols = Get-BitLockerVolume | Select-Object MountPoint,EncryptionMethod,VolumeStatus,ProtectionStatus,EncryptionPercentage
if ($vols) { @($vols) | ConvertTo-Json -Depth 2 -Compress } else { '[]' }`.trim();
 
  let enabled           = false;
  let status            = "Unknown";
  let encryptionMethod: string | null  = null;
  let encryptionPercent: number | null = null;
  let message           = "";
 
  try {
    const raw    = await runPS(ps);
    const parsed = JSON.parse(raw ?? "[]") as Array<{
      MountPoint:           string;
      EncryptionMethod:     string;
      VolumeStatus:         string;
      ProtectionStatus:     string;
      EncryptionPercentage: number;
    }>;
 
    if (parsed.length > 0) {
      const systemVol   = parsed.find((v) => v.MountPoint === "C:") ?? parsed[0];
      enabled           = systemVol.ProtectionStatus === "On";
      status            = systemVol.VolumeStatus;
      encryptionMethod  = systemVol.EncryptionMethod || null;
      encryptionPercent = systemVol.EncryptionPercentage ?? null;
      message           = `BitLocker on C: is ${enabled ? "ON" : "OFF"}. ` +
                          `Volume status: ${status}. ` +
                          `${encryptionPercent}% encrypted.`;
    } else {
      message = "No BitLocker volumes found or BitLocker is not available.";
    }
  } catch (err) {
    message = `Error reading BitLocker status: ${(err as Error).message}`;
  }
 
  return {
    platform: "win32",
    enabled,
    status,
    encryptionMethod,
    encryptionPercent,
    enabledUsers: [],
    message,
  };
}
 
// -- Exported run function ----------------------------------------------------
 
export async function run(_args: Record<string, never> = {}) {
  const platform = os.platform();
  return platform === "win32"
    ? checkFileVaultWin32()
    : checkFileVaultDarwin();
}
 
// -- CLI smoke test -----------------------------------------------------------
 
if (false) {
  run({})
    .then(r => console.log(JSON.stringify(r, null, 2)))
    .catch((err: Error) => { console.error(err.message); process.exit(1); });
}