reset_local_password

reset_local_password

Resets the local macOS/Windows account password. IMPORTANT: requires admin privileges. Always confirm with the user before executing. Use only for local accounts — not for Active Directory accounts.

Metadata

Name

reset_local_password

Updated

3 weeks ago

Source

GitHub source

Risk

Critical

Requires consent

true

Affected scope

system

Code

/**
 * mcp/skills/resetLocalPassword.ts — reset_local_password skill
 *
 * Resets the local macOS/Windows account password. IMPORTANT: requires admin
 * privileges. Always confirm with the user before executing. Use only for
 * local accounts — not for Active Directory accounts.
 *
 * Platform strategy
 * -----------------
 * darwin  `dscl . -passwd /Users/{username} {newPassword}` (only if not dryRun)
 * win32   PowerShell Set-LocalUser -Name {username} -Password (ConvertTo-SecureString ...)
 *
 * Smoke test
 *   npx tsx -r dotenv/config mcp/skills/resetLocalPassword.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: "reset_local_password",
  description:
    "Resets the local macOS/Windows account password. " +
    "IMPORTANT: requires admin privileges. Always confirm with the user before executing. " +
    "Use only for local accounts — not for Active Directory accounts.",
  riskLevel:       "critical",
  destructive:     true,
  requiresConsent: true,
  supportsDryRun:  true,
  affectedScope:   ["system"],
  auditRequired:   true,
  escalationHint:  {
    darwin: "sudo dscl . -passwd /Users/<username> '<newPassword>'  # substitute the user's real username and a strong temporary password; user should change at next login",
    win32:  "Set-LocalUser -Name '<username>' -Password (ConvertTo-SecureString '<newPassword>' -AsPlainText -Force)  # run from elevated PowerShell",
  },
  sensitiveParams: ["new_password"] as const,
  schema: {
    username: z
      .string()
      .describe("Username whose password to reset"),
    new_password: z
      .string()
      .describe("New password to set"),
    dryRun: z
      .boolean()
      .optional()
      .describe("If true, validate inputs without changing password. Default: true"),
  },
} as const;

// -- Types --------------------------------------------------------------------

interface ResetResult {
  username: string;
  success:  boolean;
  dryRun:   boolean;
  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: 10 * 1024 * 1024 },
  );
  return stdout.trim();
}

// -- darwin implementation ----------------------------------------------------

async function resetLocalPasswordDarwin(
  username:    string,
  newPassword: string,
  dryRun:      boolean,
): Promise<ResetResult> {
  const safeName = username.replace(/'/g, `'\''`);
  try {
    await execAsync(
      `dscl . -read /Users/'${safeName}' UniqueID 2>/dev/null`,
      { maxBuffer: 1 * 1024 * 1024, shell: "/bin/bash" },
    );
  } catch {
    return {
      username,
      success: false,
      dryRun,
      message: `User '${username}' not found in local directory.`,
    };
  }

if (!newPassword || newPassword.length === 0) {
    return {
      username,
      success: false,
      dryRun,
      message: "New password must not be empty.",
    };
  }

if (dryRun) {
    return {
      username,
      success: true,
      dryRun:  true,
      message: `Dry run: user '${username}' exists. Password would be reset. Run with dryRun=false to apply.`,
    };
  }

try {
    const safePassword = newPassword.replace(/'/g, `'\''`);
    await execAsync(
      `dscl . -passwd /Users/'${safeName}' '${safePassword}'`,
      { maxBuffer: 1 * 1024 * 1024, shell: "/bin/bash" },
    );
    return {
      username,
      success: true,
      dryRun:  false,
      message: `Password for '${username}' has been reset successfully.`,
    };
  } catch (err) {
    const msg = (err as Error).message ?? "Unknown error";
    return {
      username,
      success: false,
      dryRun:  false,
      message: `Failed to reset password for '${username}': ${msg.replace(/password/gi, "[password]")}`,
    };
  }
}

// -- win32 implementation -----------------------------------------------------

async function resetLocalPasswordWin32(
  username:    string,
  newPassword: string,
  dryRun:      boolean,
): Promise<ResetResult> {
  const ps_check = `
$ErrorActionPreference = 'Stop'
try {
  $u = Get-LocalUser -Name '${username.replace(/'/g, "''")}' -ErrorAction Stop
  Write-Output "EXISTS:$($u.Name)"
} catch {
  Write-Output "NOTFOUND"
}`.trim();

let checkResult = "";
  try {
    checkResult = await runPS(ps_check);
  } catch {
    return { username, success: false, dryRun, message: `Could not verify user '${username}'.` };
  }

if (checkResult.includes("NOTFOUND")) {
    return { username, success: false, dryRun, message: `User '${username}' not found.` };
  }

if (!newPassword || newPassword.length === 0) {
    return { username, success: false, dryRun, message: "New password must not be empty." };
  }

const safeUser = username.replace(/'/g, "''");
  const safePw   = newPassword.replace(/'/g, "''");
  const ps_reset = `
$ErrorActionPreference = 'Stop'
$secPw = ConvertTo-SecureString -String '${safePw}' -AsPlainText -Force
Set-LocalUser -Name '${safeUser}' -Password $secPw
Write-Output "OK"`.trim();

try {
    const result = await runPS(ps_reset);
    if (result.includes("OK")) {
      return { username, success: true, dryRun: false, message: `Password for '${username}' has been reset successfully.` };
    }
    return { username, success: false, dryRun: false, message: "Password reset command did not confirm success." };
  } catch (err) {
    const msg = (err as Error).message ?? "Unknown error";
    return {
      username,
      success: false,
      dryRun:  false,
      message: `Failed to reset password for '${username}': ${msg.replace(/password/gi, "[password]")}`,
    };
  }
}

// -- Exported run function ----------------------------------------------------

export async function run({
  username,
  new_password: newPassword,
  dryRun = true,
}: {
  username:     string;
  new_password: string;
  dryRun?:      boolean;
}) {
  if (!username) throw new Error("[reset_local_password] username is required");
  if (!newPassword) throw new Error("[reset_local_password] new_password is required");

const platform = os.platform();

const result = platform === "win32"
    ? await resetLocalPasswordWin32(username, newPassword, dryRun)
    : await resetLocalPasswordDarwin(username, newPassword, dryRun);

return { platform, ...result };
}

// -- CLI smoke test -----------------------------------------------------------

if (false) {
  run({ username: "testuser", new_password: "TestPass123!", dryRun: true })
    .then(r => console.log(JSON.stringify(r, null, 2)))
    .catch((err: Error) => { console.error(err.message); process.exit(1); });
}