diff --git a/apps/cli/src/program.ts b/apps/cli/src/program.ts index 9eca236a3a..8bca024b41 100644 --- a/apps/cli/src/program.ts +++ b/apps/cli/src/program.ts @@ -298,9 +298,12 @@ export class Program { .option("-p, --passphrase", "Generate a passphrase.") .option("--length ", "Length of the password.") .option("--words ", "Number of words.") + .option("--minNumber ", "Minimum number of numeric characters.") + .option("--minSpecial ", "Minimum number of special characters.") .option("--separator ", "Word separator.") .option("-c, --capitalize", "Title case passphrase.") .option("--includeNumber", "Passphrase includes number.") + .option("--ambiguous", "Avoid ambiguous characters.") .on("--help", () => { writeLn("\n Notes:"); writeLn(""); diff --git a/apps/cli/src/tools/generate.command.ts b/apps/cli/src/tools/generate.command.ts index bd9ad88a04..30436e7db7 100644 --- a/apps/cli/src/tools/generate.command.ts +++ b/apps/cli/src/tools/generate.command.ts @@ -1,5 +1,6 @@ import { StateService } from "@bitwarden/common/platform/abstractions/state.service"; import { PasswordGenerationServiceAbstraction } from "@bitwarden/common/tools/generator/password"; +import { PasswordGeneratorOptions } from "@bitwarden/common/tools/generator/password/password-generator-options"; import { Response } from "../models/response"; import { StringResponse } from "../models/response/string.response"; @@ -13,7 +14,7 @@ export class GenerateCommand { async run(cmdOptions: Record): Promise { const normalizedOptions = new Options(cmdOptions); - const options = { + const options: PasswordGeneratorOptions = { uppercase: normalizedOptions.uppercase, lowercase: normalizedOptions.lowercase, number: normalizedOptions.number, @@ -24,6 +25,9 @@ export class GenerateCommand { numWords: normalizedOptions.words, capitalize: normalizedOptions.capitalize, includeNumber: normalizedOptions.includeNumber, + minNumber: normalizedOptions.minNumber, + minSpecial: normalizedOptions.minSpecial, + ambiguous: normalizedOptions.ambiguous, }; const enforcedOptions = (await this.stateService.getIsAuthenticated()) @@ -47,6 +51,9 @@ class Options { words: number; capitalize: boolean; includeNumber: boolean; + minNumber: number; + minSpecial: number; + ambiguous: boolean; constructor(passedOptions: Record) { this.uppercase = CliUtils.convertBooleanOption(passedOptions?.uppercase); @@ -55,10 +62,13 @@ class Options { this.special = CliUtils.convertBooleanOption(passedOptions?.special); this.capitalize = CliUtils.convertBooleanOption(passedOptions?.capitalize); this.includeNumber = CliUtils.convertBooleanOption(passedOptions?.includeNumber); - this.length = passedOptions?.length != null ? parseInt(passedOptions?.length, null) : 14; + this.ambiguous = CliUtils.convertBooleanOption(passedOptions?.ambiguous); + this.length = CliUtils.convertNumberOption(passedOptions?.length, 14); this.type = passedOptions?.passphrase ? "passphrase" : "password"; - this.separator = passedOptions?.separator == null ? "-" : passedOptions.separator + ""; - this.words = passedOptions?.words != null ? parseInt(passedOptions.words, null) : 3; + this.separator = CliUtils.convertStringOption(passedOptions?.separator, "-"); + this.words = CliUtils.convertNumberOption(passedOptions?.words, 3); + this.minNumber = CliUtils.convertNumberOption(passedOptions?.minNumber, 1); + this.minSpecial = CliUtils.convertNumberOption(passedOptions?.minSpecial, 1); if (!this.uppercase && !this.lowercase && !this.special && !this.number) { this.lowercase = true; diff --git a/apps/cli/src/utils.ts b/apps/cli/src/utils.ts index f8780dbec6..5d77f6d373 100644 --- a/apps/cli/src/utils.ts +++ b/apps/cli/src/utils.ts @@ -253,4 +253,20 @@ export class CliUtils { static convertBooleanOption(optionValue: any) { return optionValue || optionValue === "" ? true : false; } + + static convertNumberOption(optionValue: any, defaultValue: number) { + try { + if (optionValue != null) { + const numVal = parseInt(optionValue); + return !Number.isNaN(numVal) ? numVal : defaultValue; + } + return defaultValue; + } catch { + return defaultValue; + } + } + + static convertStringOption(optionValue: any, defaultValue: string) { + return optionValue != null ? String(optionValue) : defaultValue; + } }