Merge branch 'main' into pr/SandipGyawali/397
This commit is contained in:
@@ -84,7 +84,7 @@
|
||||
"includeNumbers": "Zahlen einschließen (0-9)",
|
||||
"includeSymbols": "Sonderzeichen einschließen",
|
||||
"includeUppercase": "Großbuchstaben einschließen (A-Z)",
|
||||
"lengthDesc": "Länge des Passwortes",
|
||||
"lengthDesc": "Länge des Passwortes (>3 & <257)",
|
||||
"lengthPlaceholder": "z.B. 12",
|
||||
"optionsTitle": "Passwortoptionen",
|
||||
"resultTitle": "Generiertes Passwort",
|
||||
|
||||
@@ -84,7 +84,7 @@
|
||||
"includeNumbers": "Include numbers (0-9)",
|
||||
"includeSymbols": "Include special characters",
|
||||
"includeUppercase": "Include uppercase letters (A-Z)",
|
||||
"lengthDesc": "Length of the password",
|
||||
"lengthDesc": "Length of the password (>3 & <257)",
|
||||
"lengthPlaceholder": "e.g. 12",
|
||||
"optionsTitle": "Password Options",
|
||||
"resultTitle": "Generated Password",
|
||||
|
||||
@@ -84,7 +84,7 @@
|
||||
"includeNumbers": "Incluir números (0-9)",
|
||||
"includeSymbols": "Incluir caracteres especiales",
|
||||
"includeUppercase": "Incluir letras mayúsculas (A-Z)",
|
||||
"lengthDesc": "Longitud de la contraseña",
|
||||
"lengthDesc": "Longitud de la contraseña (>3 & <257)",
|
||||
"lengthPlaceholder": "p. ej. 12",
|
||||
"optionsTitle": "Opciones de contraseña",
|
||||
"resultTitle": "Contraseña generada",
|
||||
|
||||
@@ -84,7 +84,7 @@
|
||||
"includeNumbers": "Inclure des chiffres (0-9)",
|
||||
"includeSymbols": "Inclure des caractères spéciaux",
|
||||
"includeUppercase": "Inclure des lettres majuscules (A-Z)",
|
||||
"lengthDesc": "Longueur du mot de passe",
|
||||
"lengthDesc": "Longueur du mot de passe (>3 & <257)",
|
||||
"lengthPlaceholder": "par exemple 12",
|
||||
"optionsTitle": "Options de mot de passe",
|
||||
"resultTitle": "Mot de passe généré",
|
||||
|
||||
@@ -84,7 +84,7 @@
|
||||
"includeNumbers": "संख्याएँ शामिल करें (0-9)",
|
||||
"includeSymbols": "विशेष वर्ण शामिल करें",
|
||||
"includeUppercase": "बड़े अक्षर (A-Z) शामिल करें",
|
||||
"lengthDesc": "पासवर्ड की लंबाई",
|
||||
"lengthDesc": "पासवर्ड की लंबाई (>3 & <257)",
|
||||
"lengthPlaceholder": "उदाहरणार्थ 12",
|
||||
"optionsTitle": "पासवर्ड विकल्प",
|
||||
"resultTitle": "जनरेट किया गया पासवर्ड",
|
||||
|
||||
@@ -84,7 +84,7 @@
|
||||
"includeNumbers": "数字を含める(0~9)",
|
||||
"includeSymbols": "特殊文字を含める",
|
||||
"includeUppercase": "大文字(A~Z)を含める",
|
||||
"lengthDesc": "パスワードの長さ",
|
||||
"lengthDesc": "パスワードの長さ (>3 & <257)",
|
||||
"lengthPlaceholder": "例:12",
|
||||
"optionsTitle": "パスワードオプション",
|
||||
"resultTitle": "生成されたパスワード",
|
||||
|
||||
@@ -84,7 +84,7 @@
|
||||
"includeNumbers": "Inclusief cijfers (0-9)",
|
||||
"includeSymbols": "Speciale tekens toevoegen",
|
||||
"includeUppercase": "Inclusief hoofdletters (A-Z)",
|
||||
"lengthDesc": "Lengte van het wachtwoord",
|
||||
"lengthDesc": "Lengte van het wachtwoord (>3 & <257)",
|
||||
"lengthPlaceholder": "bijv. 12",
|
||||
"optionsTitle": "Wachtwoordopties",
|
||||
"resultTitle": "gegenereerd wachtwoord",
|
||||
|
||||
@@ -84,7 +84,7 @@
|
||||
"includeNumbers": "Incluir números (0-9)",
|
||||
"includeSymbols": "Incluir caracteres especiais",
|
||||
"includeUppercase": "Incluir letras maiúsculas (A-Z)",
|
||||
"lengthDesc": "Comprimento da senha",
|
||||
"lengthDesc": "Comprimento da senha (>3 & <257)",
|
||||
"lengthPlaceholder": "por exemplo 12",
|
||||
"optionsTitle": "Opções de senha",
|
||||
"resultTitle": "Senha gerada",
|
||||
|
||||
@@ -84,7 +84,7 @@
|
||||
"includeNumbers": "Включить цифры (0-9)",
|
||||
"includeSymbols": "Включить специальные символы",
|
||||
"includeUppercase": "Включайте заглавные буквы (A–Z)",
|
||||
"lengthDesc": "Длина пароля",
|
||||
"lengthDesc": "Длина пароля (>3 & <257)",
|
||||
"lengthPlaceholder": "например, 12",
|
||||
"optionsTitle": "Параметры пароля",
|
||||
"resultTitle": "Сгенерированный пароль",
|
||||
|
||||
@@ -84,7 +84,7 @@
|
||||
"includeNumbers": "包含数字 (0-9)",
|
||||
"includeSymbols": "包含特殊字符",
|
||||
"includeUppercase": "包含大写字母(A-Z)",
|
||||
"lengthDesc": "密码长度",
|
||||
"lengthDesc": "密码长度 (>3 & <257)",
|
||||
"lengthPlaceholder": "例如 12",
|
||||
"optionsTitle": "密码选项",
|
||||
"resultTitle": "生成的密码",
|
||||
|
||||
@@ -77,8 +77,26 @@ export default function PasswordGenerator({ title }: ToolComponentProps) {
|
||||
description={t('passwordGenerator.lengthDesc')}
|
||||
placeholder={t('passwordGenerator.lengthPlaceholder')}
|
||||
value={values.length}
|
||||
onOwnChange={(val) => updateField('length', val)}
|
||||
onOwnChange={(val) => {
|
||||
const length = Number(val);
|
||||
|
||||
if (length > 256) {
|
||||
updateField('length', '256');
|
||||
return;
|
||||
}
|
||||
|
||||
if (length < 4 && val !== '') {
|
||||
updateField('length', '4');
|
||||
return;
|
||||
}
|
||||
|
||||
updateField('length', val);
|
||||
}}
|
||||
type="number"
|
||||
inputProps={{
|
||||
min: 4,
|
||||
max: 256
|
||||
}}
|
||||
/>
|
||||
|
||||
<FormGroup>
|
||||
|
||||
@@ -129,12 +129,6 @@ describe('generatePassword', () => {
|
||||
expect(result).toHaveLength(50);
|
||||
});
|
||||
|
||||
it('should handle edge case with very short length', () => {
|
||||
const options = { ...initialValues, length: '1' };
|
||||
const result = generatePassword(options);
|
||||
expect(result).toHaveLength(1);
|
||||
});
|
||||
|
||||
it('should handle negative length', () => {
|
||||
const options = { ...initialValues, length: '-5' };
|
||||
const result = generatePassword(options);
|
||||
|
||||
@@ -1,38 +1,75 @@
|
||||
import type { InitialValuesType } from './initialValues';
|
||||
|
||||
const LOWERCASE = 'abcdefghijklmnopqrstuvwxyz';
|
||||
const UPPERCASE = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
|
||||
const NUMBERS = '0123456789';
|
||||
const SYMBOLS = '!@#$%^&*()_+~`|}{[]:;?><,./-=';
|
||||
|
||||
const AMBIGUOUS = new Set(['i', 'I', 'l', '0', 'O']);
|
||||
|
||||
const MAX_PASSWORD_LENGTH = 256;
|
||||
|
||||
function randomChar(chars: string): string {
|
||||
const array = new Uint32Array(1);
|
||||
globalThis.crypto.getRandomValues(array);
|
||||
|
||||
return chars[array[0] % chars.length];
|
||||
}
|
||||
|
||||
function shuffle(array: string[]): void {
|
||||
for (let i = array.length - 1; i > 0; i--) {
|
||||
const random = new Uint32Array(1);
|
||||
globalThis.crypto.getRandomValues(random);
|
||||
|
||||
const j = random[0] % (i + 1);
|
||||
|
||||
[array[i], array[j]] = [array[j], array[i]];
|
||||
}
|
||||
}
|
||||
|
||||
function removeAmbiguous(chars: string): string {
|
||||
return [...chars].filter((char) => !AMBIGUOUS.has(char)).join('');
|
||||
}
|
||||
|
||||
export function generatePassword(options: InitialValuesType): string {
|
||||
const length = parseInt(options.length || '', 10);
|
||||
if (isNaN(length) || length <= 0) {
|
||||
const length = Number.parseInt(options.length || '', 10);
|
||||
|
||||
if (Number.isNaN(length) || length <= 0 || length > MAX_PASSWORD_LENGTH) {
|
||||
return '';
|
||||
}
|
||||
|
||||
let charset = '';
|
||||
const lower = 'abcdefghijklmnopqrstuvwxyz';
|
||||
const upper = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
|
||||
const numbers = '0123456789';
|
||||
const symbols = '!@#$%^&*()_+~`|}{[]:;?><,./-=';
|
||||
|
||||
if (options.includeLowercase) charset += lower;
|
||||
if (options.includeUppercase) charset += upper;
|
||||
if (options.includeNumbers) charset += numbers;
|
||||
if (options.includeSymbols) charset += symbols;
|
||||
let pools = [
|
||||
options.includeLowercase ? LOWERCASE : '',
|
||||
options.includeUppercase ? UPPERCASE : '',
|
||||
options.includeNumbers ? NUMBERS : '',
|
||||
options.includeSymbols ? SYMBOLS : ''
|
||||
].filter(Boolean);
|
||||
|
||||
if (options.avoidAmbiguous) {
|
||||
// ambiguous set = i, I, l, 0, O
|
||||
const ambig = new Set(['i', 'I', 'l', '0', 'O']);
|
||||
charset = Array.from(charset)
|
||||
.filter((c) => !ambig.has(c))
|
||||
.join('');
|
||||
pools = pools.map(removeAmbiguous);
|
||||
}
|
||||
|
||||
if (!charset) {
|
||||
return ''; // nothing to pick from
|
||||
// Remove empty pools after filtering
|
||||
pools = pools.filter(Boolean);
|
||||
|
||||
if (pools.length === 0 || length < pools.length) {
|
||||
return '';
|
||||
}
|
||||
|
||||
let pwd = '';
|
||||
for (let i = 0; i < length; i++) {
|
||||
const idx = Math.floor(Math.random() * charset.length);
|
||||
pwd += charset[idx];
|
||||
const password: string[] = [];
|
||||
|
||||
// Guarantee each selected category appears once
|
||||
for (const pool of pools) {
|
||||
password.push(randomChar(pool));
|
||||
}
|
||||
return pwd;
|
||||
|
||||
const charset = pools.join('');
|
||||
|
||||
while (password.length < length) {
|
||||
password.push(randomChar(charset));
|
||||
}
|
||||
|
||||
shuffle(password);
|
||||
|
||||
return password.join('');
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user