From c4d8d76a4c1a84c72bdb8faf45c8139fc8cef75d Mon Sep 17 00:00:00 2001 From: Lucas Berger Date: Wed, 17 Jun 2026 20:25:18 -0400 Subject: [PATCH] fix(19): WR-01 parse reset-admin flags explicitly and stop echoing username --- apps/api/scripts/reset-admin.ts | 49 ++++++++++++++++++++++++++------- 1 file changed, 39 insertions(+), 10 deletions(-) diff --git a/apps/api/scripts/reset-admin.ts b/apps/api/scripts/reset-admin.ts index ec1675d..3bda451 100644 --- a/apps/api/scripts/reset-admin.ts +++ b/apps/api/scripts/reset-admin.ts @@ -51,16 +51,43 @@ function hashPassword(password: string): string { } // ── CLI arg parsing (no new deps — process.argv only) ─────────────────────────────────── -function parseArgs(argv: string[]): Record { - const result: Record = {}; +// WR-01: support both `--key=value` and `--key value`, and parse values EXPLICITLY rather +// than inferring an empty string whenever the next token starts with '--'. The old heuristic +// coerced `--password --foo` (and a legitimately `--`-prefixed or empty password) silently to +// ''. Here, known value-taking flags (--username, --password) always consume the next token +// verbatim as their value; the only boolean flag (--dry-run) takes no value. This keeps a +// password that begins with '--', or an intentionally empty password, intact. +const VALUE_FLAGS = new Set(['username', 'password']); +const BOOLEAN_FLAGS = new Set(['dry-run']); + +function parseArgs(argv: string[]): Record { + const result: Record = {}; for (let i = 0; i < argv.length; i++) { const arg = argv[i]; - if (arg.startsWith('--')) { - const key = arg.slice(2); - const value = argv[i + 1] !== undefined && !argv[i + 1].startsWith('--') ? argv[i + 1] : ''; - result[key] = value; - if (value) i++; // skip the value token + if (!arg.startsWith('--')) continue; + + const eq = arg.indexOf('='); + if (eq !== -1) { + // `--key=value` form — value is everything after the first '=', taken verbatim + // (so `--password=--weird` and `--password=` both work correctly). + result[arg.slice(2, eq)] = arg.slice(eq + 1); + continue; } + + const key = arg.slice(2); + if (BOOLEAN_FLAGS.has(key)) { + result[key] = ''; // presence-only flag; detected via hasOwnProperty + continue; + } + if (VALUE_FLAGS.has(key)) { + // Consume the NEXT token verbatim as the value — even if it starts with '--' or is + // empty. If there is no next token, record undefined (genuinely absent, not ''). + result[key] = argv[i + 1]; + if (i + 1 < argv.length) i++; // skip the consumed value token + continue; + } + // Unknown flag — record presence with no value (forward-compatible, no crash). + result[key] = ''; } return result; } @@ -120,7 +147,8 @@ try { // Existing local_credentials row — update password and ensure is_admin userId = lcRows[0].user_id; await conn.execute('UPDATE users SET is_admin = true, claimed = true WHERE id = ?', [userId]); - console.log(`[reset-admin] Found existing user id=${userId} for username="${username}"`); + // WR-01: do not echo the username — log only the resolved user id (no credential data). + console.log(`[reset-admin] Found existing user id=${userId}`); } else { // No existing row — insert a new user const displayName = username; @@ -130,7 +158,8 @@ try { [displayName], ); userId = (insertResult as unknown as { insertId: number }).insertId; - console.log(`[reset-admin] Created new user id=${userId} for username="${username}"`); + // WR-01: do not echo the username — log only the resolved user id. + console.log(`[reset-admin] Created new user id=${userId}`); } // ── Upsert local_credentials row ───────────────────────────────────────────────────── @@ -142,7 +171,7 @@ try { [userId, username, passwordHash], ); - console.log(`[reset-admin] Local credential upserted for user id=${userId} username="${username}"`); + console.log(`[reset-admin] Local credential upserted for user id=${userId}`); console.log(`[reset-admin] Done. User id=${userId} is now a local admin.`); } finally { await conn.end();