pstream is dead; long live pstream taciturnaxolotl.github.io/pstream-ng/
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

sanitize custom passphrase + requirements

Pas 95f27565 1e635c36

+56 -10
+3 -1
src/assets/locales/en.json
··· 155 155 "custom": "Custom", 156 156 "customPassphraseLabel": "Custom Passphrase", 157 157 "customPassphrasePlaceholder": "Enter your custom passphrase", 158 - "useCustomPassphrase": "Use Custom Passphrase" 158 + "useCustomPassphrase": "Use Custom Passphrase", 159 + "invalidPassphraseCharacters": "Invalid passphrase characters. Only English letters, numbers 1-10, and normal symbols are allowed.", 160 + "passphraseTooShort": "Passphrase must be at least 8 characters long." 159 161 }, 160 162 "hasAccount": "Already have an account? <0>Login here.</0>", 161 163 "login": {
+53 -9
src/components/form/PassphraseDisplay.tsx
··· 18 18 const [showCustomInput, setShowCustomInput] = useState(false); 19 19 const [customPassphrase, setCustomPassphrase] = useState(""); 20 20 const [isShiftHeld, setIsShiftHeld] = useState(false); 21 + const [validationError, setValidationError] = useState(""); 21 22 const isMounted = useMountedState(); 22 23 23 24 const timeout = useRef<ReturnType<typeof setTimeout>>(); 25 + 26 + const validPassphraseRegex = 27 + /^[a-zA-Z0-9\s\-_.,!?@#$%^&*()+=:;"'<>[\]{}|\\/`~]+$/; 24 28 25 29 function copyMnemonic() { 26 30 copy(props.mnemonic); ··· 41 45 } 42 46 } 43 47 48 + function validatePassphrase(passphrase: string): boolean { 49 + if (passphrase.length < 8) { 50 + setValidationError(t("auth.generate.passphraseTooShort")); 51 + return false; 52 + } 53 + if (!validPassphraseRegex.test(passphrase)) { 54 + setValidationError(t("auth.generate.invalidPassphraseCharacters")); 55 + return false; 56 + } 57 + setValidationError(""); 58 + return true; 59 + } 60 + 61 + function handleCustomPassphraseChange(value: string) { 62 + setCustomPassphrase(value); 63 + // Clear validation error when user starts typing 64 + if (validationError) { 65 + setValidationError(""); 66 + } 67 + } 68 + 44 69 function handleCustomPassphraseSubmit() { 45 70 if (customPassphrase.trim()) { 46 - props.onCustomPassphrase?.(customPassphrase.trim()); 47 - setShowCustomInput(false); 48 - setCustomPassphrase(""); 71 + if (validatePassphrase(customPassphrase.trim())) { 72 + props.onCustomPassphrase?.(customPassphrase.trim()); 73 + setShowCustomInput(false); 74 + setCustomPassphrase(""); 75 + setValidationError(""); 76 + } 49 77 } 50 78 } 51 79 80 + function handleCancelCustomInput() { 81 + setShowCustomInput(false); 82 + setCustomPassphrase(""); 83 + setValidationError(""); 84 + } 85 + 86 + function handleShowCustomInput() { 87 + setShowCustomInput(true); 88 + } 89 + 52 90 useEffect(() => { 53 91 document.addEventListener("keydown", handleKeyDown); 54 92 document.addEventListener("keyup", handleKeyUp); ··· 69 107 <button 70 108 type="button" 71 109 className="text-authentication-copyText hover:text-authentication-copyTextHover transition-colors flex gap-2 items-center cursor-pointer" 72 - onClick={() => setShowCustomInput(false)} 110 + onClick={handleCancelCustomInput} 73 111 > 74 112 <Icon icon={Icons.X} className="text-xs" /> 75 113 <span className="text-sm">{t("actions.cancel")}</span> ··· 78 116 <div className="px-4 py-4"> 79 117 <AuthInputBox 80 118 value={customPassphrase} 81 - onChange={setCustomPassphrase} 119 + // eslint-disable-next-line react/jsx-no-bind 120 + onChange={handleCustomPassphraseChange} 82 121 placeholder={t("auth.generate.customPassphrasePlaceholder")} 83 122 passwordToggleable 84 123 className="mb-4" 85 124 /> 125 + {validationError && ( 126 + <p className="text-authentication-errorText text-sm mb-4"> 127 + {validationError} 128 + </p> 129 + )} 86 130 <button 87 131 type="button" 88 - className="w-full bg-authentication-inputBg hover:bg-authentication-inputBg/80 text-white font-medium py-2 px-4 rounded-lg transition-colors" 132 + className="w-full bg-authentication-inputBg hover:bg-authentication-inputBg/80 text-white font-medium py-2 px-4 rounded-lg transition-colors disabled:opacity-50 disabled:cursor-not-allowed" 89 133 onClick={handleCustomPassphraseSubmit} 90 - disabled={!customPassphrase.trim()} 134 + disabled={!customPassphrase.trim() || !!validationError} 91 135 > 92 136 {t("auth.generate.useCustomPassphrase")} 93 137 </button> ··· 111 155 ? "opacity-100 scale-100" 112 156 : "opacity-0 scale-95 pointer-events-none" 113 157 }`} 114 - onClick={() => setShowCustomInput(true)} 158 + onClick={handleShowCustomInput} 115 159 title={t("auth.generate.useCustomPassphrase")} 116 160 > 117 161 <Icon icon={Icons.EDIT} className="text-xs" /> ··· 120 164 <button 121 165 type="button" 122 166 className="text-authentication-copyText hover:text-authentication-copyTextHover transition-colors flex gap-2 items-center cursor-pointer" 123 - onClick={() => copyMnemonic()} 167 + onClick={copyMnemonic} 124 168 > 125 169 <Icon 126 170 icon={hasCopied ? Icons.CHECKMARK : Icons.COPY}