From 753e5bed99803b0eac6242cbea6a10ab53acad92 Mon Sep 17 00:00:00 2001 From: Ghost_Hardware <125590745+injuryreserve1@users.noreply.github.com> Date: Thu, 9 Jul 2026 00:00:07 +0500 Subject: [PATCH 1/6] fix: use crypto.getRandomValues for secure password generation --- src/pages/tools/string/password-generator/service.ts | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/pages/tools/string/password-generator/service.ts b/src/pages/tools/string/password-generator/service.ts index fe20a6b..23edc91 100644 --- a/src/pages/tools/string/password-generator/service.ts +++ b/src/pages/tools/string/password-generator/service.ts @@ -30,9 +30,15 @@ export function generatePassword(options: InitialValuesType): string { } let pwd = ''; + const array = new Uint32Array(length); + + const cryptoInstance = + typeof window !== 'undefined' ? window.crypto : globalThis.crypto; + cryptoInstance.getRandomValues(array); + for (let i = 0; i < length; i++) { - const idx = Math.floor(Math.random() * charset.length); - pwd += charset[idx]; + const charIndex = array[i] % charset.length; + pwd += charset[charIndex]; } return pwd; } From 169f9e1630e9007c56104603974b64e26bdc78d8 Mon Sep 17 00:00:00 2001 From: Chesterkxng Date: Tue, 14 Jul 2026 15:32:31 +0200 Subject: [PATCH 2/6] chore: updated locales json to declare the max length --- public/locales/de/string.json | 2 +- public/locales/en/string.json | 2 +- public/locales/es/string.json | 2 +- public/locales/fr/string.json | 2 +- public/locales/hi/string.json | 2 +- public/locales/ja/string.json | 2 +- public/locales/nl/string.json | 2 +- public/locales/pt/string.json | 2 +- public/locales/ru/string.json | 2 +- public/locales/zh/string.json | 2 +- 10 files changed, 10 insertions(+), 10 deletions(-) diff --git a/public/locales/de/string.json b/public/locales/de/string.json index dbf0e8b..12063f0 100644 --- a/public/locales/de/string.json +++ b/public/locales/de/string.json @@ -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 (<= 256)", "lengthPlaceholder": "z.B. 12", "optionsTitle": "Passwortoptionen", "resultTitle": "Generiertes Passwort", diff --git a/public/locales/en/string.json b/public/locales/en/string.json index 86e5bf2..61a7b14 100644 --- a/public/locales/en/string.json +++ b/public/locales/en/string.json @@ -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 (<= 256)", "lengthPlaceholder": "e.g. 12", "optionsTitle": "Password Options", "resultTitle": "Generated Password", diff --git a/public/locales/es/string.json b/public/locales/es/string.json index 217823b..b4412ef 100644 --- a/public/locales/es/string.json +++ b/public/locales/es/string.json @@ -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 (<= 256)", "lengthPlaceholder": "p. ej. 12", "optionsTitle": "Opciones de contraseña", "resultTitle": "Contraseña generada", diff --git a/public/locales/fr/string.json b/public/locales/fr/string.json index 5dfeb88..dee8124 100644 --- a/public/locales/fr/string.json +++ b/public/locales/fr/string.json @@ -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 (<= 256)", "lengthPlaceholder": "par exemple 12", "optionsTitle": "Options de mot de passe", "resultTitle": "Mot de passe généré", diff --git a/public/locales/hi/string.json b/public/locales/hi/string.json index 0482213..bd795c5 100644 --- a/public/locales/hi/string.json +++ b/public/locales/hi/string.json @@ -84,7 +84,7 @@ "includeNumbers": "संख्याएँ शामिल करें (0-9)", "includeSymbols": "विशेष वर्ण शामिल करें", "includeUppercase": "बड़े अक्षर (A-Z) शामिल करें", - "lengthDesc": "पासवर्ड की लंबाई", + "lengthDesc": "पासवर्ड की लंबाई (<= 256)", "lengthPlaceholder": "उदाहरणार्थ 12", "optionsTitle": "पासवर्ड विकल्प", "resultTitle": "जनरेट किया गया पासवर्ड", diff --git a/public/locales/ja/string.json b/public/locales/ja/string.json index ca7d31a..0055aeb 100644 --- a/public/locales/ja/string.json +++ b/public/locales/ja/string.json @@ -84,7 +84,7 @@ "includeNumbers": "数字を含める(0~9)", "includeSymbols": "特殊文字を含める", "includeUppercase": "大文字(A~Z)を含める", - "lengthDesc": "パスワードの長さ", + "lengthDesc": "パスワードの長さ (<= 256)", "lengthPlaceholder": "例:12", "optionsTitle": "パスワードオプション", "resultTitle": "生成されたパスワード", diff --git a/public/locales/nl/string.json b/public/locales/nl/string.json index 53dd230..9cb40b8 100644 --- a/public/locales/nl/string.json +++ b/public/locales/nl/string.json @@ -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 (<= 256)", "lengthPlaceholder": "bijv. 12", "optionsTitle": "Wachtwoordopties", "resultTitle": "gegenereerd wachtwoord", diff --git a/public/locales/pt/string.json b/public/locales/pt/string.json index 8d10bae..41b005d 100644 --- a/public/locales/pt/string.json +++ b/public/locales/pt/string.json @@ -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 (<= 256)", "lengthPlaceholder": "por exemplo 12", "optionsTitle": "Opções de senha", "resultTitle": "Senha gerada", diff --git a/public/locales/ru/string.json b/public/locales/ru/string.json index 714ae84..dc6c390 100644 --- a/public/locales/ru/string.json +++ b/public/locales/ru/string.json @@ -84,7 +84,7 @@ "includeNumbers": "Включить цифры (0-9)", "includeSymbols": "Включить специальные символы", "includeUppercase": "Включайте заглавные буквы (A–Z)", - "lengthDesc": "Длина пароля", + "lengthDesc": "Длина пароля (<= 256)", "lengthPlaceholder": "например, 12", "optionsTitle": "Параметры пароля", "resultTitle": "Сгенерированный пароль", diff --git a/public/locales/zh/string.json b/public/locales/zh/string.json index 89593c6..b73d28c 100644 --- a/public/locales/zh/string.json +++ b/public/locales/zh/string.json @@ -84,7 +84,7 @@ "includeNumbers": "包含数字 (0-9)", "includeSymbols": "包含特殊字符", "includeUppercase": "包含大写字母(A-Z)", - "lengthDesc": "密码长度", + "lengthDesc": "密码长度 (<= 256)", "lengthPlaceholder": "例如 12", "optionsTitle": "密码选项", "resultTitle": "生成的密码", From 880913deec0801125fd3d2ff4367c88fd516f164 Mon Sep 17 00:00:00 2001 From: Chesterkxng Date: Tue, 14 Jul 2026 15:39:25 +0200 Subject: [PATCH 3/6] chore: updated locales json to declare the min length too --- public/locales/de/string.json | 2 +- public/locales/en/string.json | 2 +- public/locales/es/string.json | 2 +- public/locales/fr/string.json | 2 +- public/locales/hi/string.json | 2 +- public/locales/ja/string.json | 2 +- public/locales/nl/string.json | 2 +- public/locales/pt/string.json | 2 +- public/locales/ru/string.json | 2 +- public/locales/zh/string.json | 2 +- 10 files changed, 10 insertions(+), 10 deletions(-) diff --git a/public/locales/de/string.json b/public/locales/de/string.json index 12063f0..112e57e 100644 --- a/public/locales/de/string.json +++ b/public/locales/de/string.json @@ -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 (<= 256)", + "lengthDesc": "Länge des Passwortes (>3 & <257)", "lengthPlaceholder": "z.B. 12", "optionsTitle": "Passwortoptionen", "resultTitle": "Generiertes Passwort", diff --git a/public/locales/en/string.json b/public/locales/en/string.json index 61a7b14..9a77078 100644 --- a/public/locales/en/string.json +++ b/public/locales/en/string.json @@ -84,7 +84,7 @@ "includeNumbers": "Include numbers (0-9)", "includeSymbols": "Include special characters", "includeUppercase": "Include uppercase letters (A-Z)", - "lengthDesc": "Length of the password (<= 256)", + "lengthDesc": "Length of the password (>3 & <257)", "lengthPlaceholder": "e.g. 12", "optionsTitle": "Password Options", "resultTitle": "Generated Password", diff --git a/public/locales/es/string.json b/public/locales/es/string.json index b4412ef..d05af9b 100644 --- a/public/locales/es/string.json +++ b/public/locales/es/string.json @@ -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 (<= 256)", + "lengthDesc": "Longitud de la contraseña (>3 & <257)", "lengthPlaceholder": "p. ej. 12", "optionsTitle": "Opciones de contraseña", "resultTitle": "Contraseña generada", diff --git a/public/locales/fr/string.json b/public/locales/fr/string.json index dee8124..36877b4 100644 --- a/public/locales/fr/string.json +++ b/public/locales/fr/string.json @@ -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 (<= 256)", + "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é", diff --git a/public/locales/hi/string.json b/public/locales/hi/string.json index bd795c5..5e6b8c1 100644 --- a/public/locales/hi/string.json +++ b/public/locales/hi/string.json @@ -84,7 +84,7 @@ "includeNumbers": "संख्याएँ शामिल करें (0-9)", "includeSymbols": "विशेष वर्ण शामिल करें", "includeUppercase": "बड़े अक्षर (A-Z) शामिल करें", - "lengthDesc": "पासवर्ड की लंबाई (<= 256)", + "lengthDesc": "पासवर्ड की लंबाई (>3 & <257)", "lengthPlaceholder": "उदाहरणार्थ 12", "optionsTitle": "पासवर्ड विकल्प", "resultTitle": "जनरेट किया गया पासवर्ड", diff --git a/public/locales/ja/string.json b/public/locales/ja/string.json index 0055aeb..d4cfca2 100644 --- a/public/locales/ja/string.json +++ b/public/locales/ja/string.json @@ -84,7 +84,7 @@ "includeNumbers": "数字を含める(0~9)", "includeSymbols": "特殊文字を含める", "includeUppercase": "大文字(A~Z)を含める", - "lengthDesc": "パスワードの長さ (<= 256)", + "lengthDesc": "パスワードの長さ (>3 & <257)", "lengthPlaceholder": "例:12", "optionsTitle": "パスワードオプション", "resultTitle": "生成されたパスワード", diff --git a/public/locales/nl/string.json b/public/locales/nl/string.json index 9cb40b8..0e58278 100644 --- a/public/locales/nl/string.json +++ b/public/locales/nl/string.json @@ -84,7 +84,7 @@ "includeNumbers": "Inclusief cijfers (0-9)", "includeSymbols": "Speciale tekens toevoegen", "includeUppercase": "Inclusief hoofdletters (A-Z)", - "lengthDesc": "Lengte van het wachtwoord (<= 256)", + "lengthDesc": "Lengte van het wachtwoord (>3 & <257)", "lengthPlaceholder": "bijv. 12", "optionsTitle": "Wachtwoordopties", "resultTitle": "gegenereerd wachtwoord", diff --git a/public/locales/pt/string.json b/public/locales/pt/string.json index 41b005d..e398a3a 100644 --- a/public/locales/pt/string.json +++ b/public/locales/pt/string.json @@ -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 (<= 256)", + "lengthDesc": "Comprimento da senha (>3 & <257)", "lengthPlaceholder": "por exemplo 12", "optionsTitle": "Opções de senha", "resultTitle": "Senha gerada", diff --git a/public/locales/ru/string.json b/public/locales/ru/string.json index dc6c390..1d92124 100644 --- a/public/locales/ru/string.json +++ b/public/locales/ru/string.json @@ -84,7 +84,7 @@ "includeNumbers": "Включить цифры (0-9)", "includeSymbols": "Включить специальные символы", "includeUppercase": "Включайте заглавные буквы (A–Z)", - "lengthDesc": "Длина пароля (<= 256)", + "lengthDesc": "Длина пароля (>3 & <257)", "lengthPlaceholder": "например, 12", "optionsTitle": "Параметры пароля", "resultTitle": "Сгенерированный пароль", diff --git a/public/locales/zh/string.json b/public/locales/zh/string.json index b73d28c..856bd9f 100644 --- a/public/locales/zh/string.json +++ b/public/locales/zh/string.json @@ -84,7 +84,7 @@ "includeNumbers": "包含数字 (0-9)", "includeSymbols": "包含特殊字符", "includeUppercase": "包含大写字母(A-Z)", - "lengthDesc": "密码长度 (<= 256)", + "lengthDesc": "密码长度 (>3 & <257)", "lengthPlaceholder": "例如 12", "optionsTitle": "密码选项", "resultTitle": "生成的密码", From 439ff71b65dd2610cadffd1300c1f31a1baceb7d Mon Sep 17 00:00:00 2001 From: Chesterkxng Date: Tue, 14 Jul 2026 15:40:09 +0200 Subject: [PATCH 4/6] chore: min and max props added to password length field --- .../tools/string/password-generator/index.tsx | 20 ++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/src/pages/tools/string/password-generator/index.tsx b/src/pages/tools/string/password-generator/index.tsx index 9d7f62d..253241a 100644 --- a/src/pages/tools/string/password-generator/index.tsx +++ b/src/pages/tools/string/password-generator/index.tsx @@ -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 + }} /> From 6dc87edb780f6b2d865982ea5f26f02dd292e135 Mon Sep 17 00:00:00 2001 From: Chesterkxng Date: Tue, 14 Jul 2026 15:41:12 +0200 Subject: [PATCH 5/6] chore: - better edge cases handling - use of crypto for security --- .../string/password-generator/service.ts | 89 +++++++++++++------ 1 file changed, 60 insertions(+), 29 deletions(-) diff --git a/src/pages/tools/string/password-generator/service.ts b/src/pages/tools/string/password-generator/service.ts index 23edc91..35f7235 100644 --- a/src/pages/tools/string/password-generator/service.ts +++ b/src/pages/tools/string/password-generator/service.ts @@ -1,44 +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 = ''; - const array = new Uint32Array(length); + const password: string[] = []; - const cryptoInstance = - typeof window !== 'undefined' ? window.crypto : globalThis.crypto; - cryptoInstance.getRandomValues(array); - - for (let i = 0; i < length; i++) { - const charIndex = array[i] % charset.length; - pwd += charset[charIndex]; + // 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(''); } From 53e6d2285e4dc3a6b29ee06e15f3626f99405d16 Mon Sep 17 00:00:00 2001 From: Chesterkxng Date: Tue, 14 Jul 2026 15:46:42 +0200 Subject: [PATCH 6/6] chore: updated test case (min now 4 char) --- .../password-generator/password-generator.service.test.ts | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/pages/tools/string/password-generator/password-generator.service.test.ts b/src/pages/tools/string/password-generator/password-generator.service.test.ts index e6bb634..042ab93 100644 --- a/src/pages/tools/string/password-generator/password-generator.service.test.ts +++ b/src/pages/tools/string/password-generator/password-generator.service.test.ts @@ -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);