// Pure validation for the box user's password, shared by the Node action and // unit tests. Kept free of Convex imports so it tests without the harness. export type BoxPasswordValidation = | { ok: true; value: string | null } | { ok: false; error: string }; // null/'' is an explicit clear (sudo reverts to NOPASSWD). The applied value // travels to `chpasswd` as a `user:password` stdin line, so control characters // (including newline/tab) are rejected; colons are fine — chpasswd splits on // the first colon only. export const normalizeBoxPassword = ( password: string | null | undefined, ): BoxPasswordValidation => { if (password == null || password === '') return { ok: true, value: null }; if (password.length < 8) { return { ok: false, error: 'Password must be at least 8 characters.' }; } if (password.length > 128) { return { ok: false, error: 'Password must be at most 128 characters.' }; } // eslint-disable-next-line no-control-regex -- rejecting control chars is the point if (/[\u0000-\u001f\u007f]/.test(password)) { return { ok: false, error: 'Password must not contain control characters.', }; } return { ok: true, value: password }; };