Fix the password length limit when not setting a new password

Relates #1446
This commit is contained in:
Audric Ackermann 2021-01-22 16:29:02 +11:00
parent eadfbc9f6d
commit 43ec14e741
14 changed files with 156 additions and 185 deletions

View File

@ -1915,7 +1915,7 @@
"message": "Failed to set password" "message": "Failed to set password"
}, },
"passwordLengthError": { "passwordLengthError": {
"message": "Password must be between 6 and 50 characters long", "message": "Password must be between 6 and 64 characters long",
"description": "Error string shown to the user when password doesn't meet length criteria" "description": "Error string shown to the user when password doesn't meet length criteria"
}, },
"passwordTypeError": { "passwordTypeError": {

View File

@ -1698,7 +1698,7 @@
"message": "Échec de la définition du mot de passe" "message": "Échec de la définition du mot de passe"
}, },
"passwordLengthError": { "passwordLengthError": {
"message": "Le mot de passe doit avoir une longueur comprise entre 6 et 50 caractères", "message": "Le mot de passe doit avoir une longueur comprise entre 6 et 64 caractères",
"description": "Error string shown to the user when password doesn't meet length criteria" "description": "Error string shown to the user when password doesn't meet length criteria"
}, },
"passwordTypeError": { "passwordTypeError": {

View File

@ -1698,7 +1698,7 @@
"message": "Failed to set password" "message": "Failed to set password"
}, },
"passwordLengthError": { "passwordLengthError": {
"message": "Password must be between 6 and 50 characters long", "message": "Password must be between 6 and 64 characters long",
"description": "Error string shown to the user when password doesn't meet length criteria" "description": "Error string shown to the user when password doesn't meet length criteria"
}, },
"passwordTypeError": { "passwordTypeError": {

View File

@ -40,7 +40,6 @@ window.CONSTANTS = {
MAX_USERNAME_LENGTH: 20, MAX_USERNAME_LENGTH: 20,
}; };
window.passwordUtil = require('./ts/util/passwordUtils');
window.Signal.Logs = require('./js/modules/logs'); window.Signal.Logs = require('./js/modules/logs');
window.resetDatabase = () => { window.resetDatabase = () => {

View File

@ -173,7 +173,6 @@ window.setPassword = (passPhrase, oldPhrase) =>
ipc.send('set-password', passPhrase, oldPhrase); ipc.send('set-password', passPhrase, oldPhrase);
}); });
window.passwordUtil = require('./ts/util/passwordUtils');
window.libsession = require('./ts/session'); window.libsession = require('./ts/session');
window.getMessageController = window.getMessageController =

View File

@ -237,8 +237,7 @@ export class Lightbox extends React.Component<Props> {
} }
if (current.paused) { if (current.paused) {
// tslint:disable-next-line no-floating-promises void current.play();
current.play();
} else { } else {
current.pause(); current.pause();
} }
@ -272,7 +271,7 @@ export class Lightbox extends React.Component<Props> {
</div> </div>
</div> </div>
<div style={styles.controls}> <div style={styles.controls}>
<Flex flexGrow={1}> <Flex flex="1 1 auto">
<IconButton <IconButton
type="close" type="close"
onClick={this.onClose} onClick={this.onClose}

View File

@ -14,6 +14,7 @@ import { SessionSpinner } from './SessionSpinner';
import { StringUtils, ToastUtils } from '../../session/utils'; import { StringUtils, ToastUtils } from '../../session/utils';
import { lightTheme } from '../../state/ducks/SessionTheme'; import { lightTheme } from '../../state/ducks/SessionTheme';
import { ConversationController } from '../../session/conversations'; import { ConversationController } from '../../session/conversations';
import { PasswordUtil } from '../../util';
enum SignInMode { enum SignInMode {
Default, Default,
@ -454,7 +455,6 @@ export class RegistrationTabs extends React.Component<any, State> {
error={this.state.passwordErrorString} error={this.state.passwordErrorString}
type="password" type="password"
placeholder={window.i18n('enterOptionalPassword')} placeholder={window.i18n('enterOptionalPassword')}
maxLength={window.CONSTANTS.MAX_PASSWORD_LENGTH}
onValueChanged={(val: string) => { onValueChanged={(val: string) => {
this.onPasswordChanged(val); this.onPasswordChanged(val);
}} }}
@ -470,7 +470,6 @@ export class RegistrationTabs extends React.Component<any, State> {
error={passwordsDoNotMatch} error={passwordsDoNotMatch}
type="password" type="password"
placeholder={window.i18n('confirmPassword')} placeholder={window.i18n('confirmPassword')}
maxLength={window.CONSTANTS.MAX_PASSWORD_LENGTH}
onValueChanged={(val: string) => { onValueChanged={(val: string) => {
this.onPasswordVerifyChanged(val); this.onPasswordVerifyChanged(val);
}} }}
@ -592,7 +591,7 @@ export class RegistrationTabs extends React.Component<any, State> {
return; return;
} }
const error = window.passwordUtil.validatePassword(input, window.i18n); const error = PasswordUtil.validatePassword(input, window.i18n);
if (error) { if (error) {
this.setState({ this.setState({
passwordErrorString: error, passwordErrorString: error,

View File

@ -2,7 +2,7 @@ import React from 'react';
import { SessionModal } from './SessionModal'; import { SessionModal } from './SessionModal';
import { SessionButton, SessionButtonColor } from './SessionButton'; import { SessionButton, SessionButtonColor } from './SessionButton';
import { PasswordUtil } from '../../util/'; import { missingCaseError, PasswordUtil } from '../../util/';
import { ToastUtils } from '../../session/utils'; import { ToastUtils } from '../../session/utils';
import { toast } from 'react-toastify'; import { toast } from 'react-toastify';
import { SessionToast, SessionToastType } from './SessionToast'; import { SessionToast, SessionToastType } from './SessionToast';
@ -46,8 +46,6 @@ class SessionPasswordModalInner extends React.Component<Props, State> {
this.onPasswordInput = this.onPasswordInput.bind(this); this.onPasswordInput = this.onPasswordInput.bind(this);
this.onPasswordConfirmInput = this.onPasswordConfirmInput.bind(this); this.onPasswordConfirmInput = this.onPasswordConfirmInput.bind(this);
this.onPaste = this.onPaste.bind(this);
} }
public componentDidMount() { public componentDidMount() {
@ -86,8 +84,6 @@ class SessionPasswordModalInner extends React.Component<Props, State> {
}} }}
placeholder={placeholders[0]} placeholder={placeholders[0]}
onKeyUp={this.onPasswordInput} onKeyUp={this.onPasswordInput}
maxLength={window.CONSTANTS.MAX_PASSWORD_LENGTH}
onPaste={this.onPaste}
/> />
{action !== PasswordAction.Remove && ( {action !== PasswordAction.Remove && (
<input <input
@ -95,8 +91,6 @@ class SessionPasswordModalInner extends React.Component<Props, State> {
id="password-modal-input-confirm" id="password-modal-input-confirm"
placeholder={placeholders[1]} placeholder={placeholders[1]}
onKeyUp={this.onPasswordConfirmInput} onKeyUp={this.onPasswordConfirmInput}
maxLength={window.CONSTANTS.MAX_PASSWORD_LENGTH}
onPaste={this.onPaste}
/> />
)} )}
</div> </div>
@ -108,7 +102,7 @@ class SessionPasswordModalInner extends React.Component<Props, State> {
<SessionButton <SessionButton
text={window.i18n('ok')} text={window.i18n('ok')}
buttonColor={confirmButtonColor} buttonColor={confirmButtonColor}
onClick={async () => this.setPassword(onOk)} onClick={this.setPassword}
/> />
<SessionButton <SessionButton
@ -145,8 +139,106 @@ class SessionPasswordModalInner extends React.Component<Props, State> {
); );
} }
/**
* Returns false and set the state error field in the input is not a valid password
* or returns true
*/
private validatePassword(firstPassword: string) {
// if user did not fill the first password field, we can't do anything
const errorFirstInput = PasswordUtil.validatePassword(
firstPassword,
window.i18n
);
if (errorFirstInput !== null) {
this.setState({
error: errorFirstInput,
});
return false;
}
return true;
}
private async handleActionSet(
enteredPassword: string,
enteredPasswordConfirm: string
) {
// be sure both password are valid
if (!this.validatePassword(enteredPassword)) {
return;
}
// no need to validate second password. we just need to check that enteredPassword is valid, and that both password matches
if (enteredPassword !== enteredPasswordConfirm) {
this.setState({
error: window.i18n('setPasswordInvalid'),
});
return;
}
await window.setPassword(enteredPassword, null);
ToastUtils.pushToastSuccess(
'setPasswordSuccessToast',
window.i18n('setPasswordTitle'),
window.i18n('setPasswordToastDescription'),
SessionIconType.Lock
);
this.props.onOk(this.props.action);
this.closeDialog();
}
private async handleActionChange(oldPassword: string, newPassword: string) {
// We don't validate oldPassword on change: this is validate on the validatePasswordHash below
// we only validate the newPassword here
if (!this.validatePassword(newPassword)) {
return;
}
const isValidWithStoredInDB = Boolean(
await this.validatePasswordHash(oldPassword)
);
if (!isValidWithStoredInDB) {
this.setState({
error: window.i18n('changePasswordInvalid'),
});
return;
}
await window.setPassword(newPassword, oldPassword);
ToastUtils.pushToastSuccess(
'setPasswordSuccessToast',
window.i18n('changePasswordTitle'),
window.i18n('changePasswordToastDescription'),
SessionIconType.Lock
);
this.props.onOk(this.props.action);
this.closeDialog();
}
private async handleActionRemove(oldPassword: string) {
// We don't validate oldPassword on change: this is validate on the validatePasswordHash below
const isValidWithStoredInDB = Boolean(
await this.validatePasswordHash(oldPassword)
);
if (!isValidWithStoredInDB) {
this.setState({
error: window.i18n('removePasswordInvalid'),
});
return;
}
await window.setPassword(null, oldPassword);
ToastUtils.pushToastWarning(
'setPasswordSuccessToast',
window.i18n('removePasswordTitle'),
window.i18n('removePasswordToastDescription')
);
this.props.onOk(this.props.action);
this.closeDialog();
}
// tslint:disable-next-line: cyclomatic-complexity // tslint:disable-next-line: cyclomatic-complexity
private async setPassword(onSuccess?: any) { private async setPassword() {
const { action } = this.props; const { action } = this.props;
const { const {
currentPasswordEntered, currentPasswordEntered,
@ -155,106 +247,28 @@ class SessionPasswordModalInner extends React.Component<Props, State> {
const { Set, Remove, Change } = PasswordAction; const { Set, Remove, Change } = PasswordAction;
// Trim leading / trailing whitespace for UX // Trim leading / trailing whitespace for UX
const enteredPassword = (currentPasswordEntered || '').trim(); const firstPasswordEntered = (currentPasswordEntered || '').trim();
const enteredPasswordConfirm = (currentPasswordConfirmEntered || '').trim(); const secondPasswordEntered = (currentPasswordConfirmEntered || '').trim();
// if user did not fill the first password field, we can't do anything switch (action) {
const errorFirstInput = PasswordUtil.validatePassword( case Set: {
enteredPassword, await this.handleActionSet(firstPasswordEntered, secondPasswordEntered);
window.i18n
);
if (errorFirstInput !== null) {
this.setState({
error: errorFirstInput,
});
return;
}
// if action is Set or Change, we need a valid ConfirmPassword
if (action === Set || action === Change) {
const errorSecondInput = PasswordUtil.validatePassword(
enteredPasswordConfirm,
window.i18n
);
if (errorSecondInput !== null) {
this.setState({
error: errorSecondInput,
});
return; return;
} }
} case Change: {
await this.handleActionChange(
// Passwords match or remove password successful firstPasswordEntered,
const newPassword = action === Remove ? null : enteredPasswordConfirm; secondPasswordEntered
const oldPassword = action === Set ? null : enteredPassword; );
return;
// Check if password match, when setting, changing or removing }
let valid; case Remove: {
if (action === Set) { await this.handleActionRemove(firstPasswordEntered);
valid = enteredPassword === enteredPasswordConfirm; return;
} else {
valid = Boolean(await this.validatePasswordHash(oldPassword));
}
if (!valid) {
let str;
switch (action) {
case Set:
str = window.i18n('setPasswordInvalid');
break;
case Change:
str = window.i18n('changePasswordInvalid');
break;
case Remove:
str = window.i18n('removePasswordInvalid');
break;
default:
throw new Error(`Invalid action ${action}`);
} }
this.setState({
error: str,
});
return;
}
await window.setPassword(newPassword, oldPassword);
let title;
let description;
switch (action) {
case Set:
title = window.i18n('setPasswordTitle');
description = window.i18n('setPasswordToastDescription');
break;
case Change:
title = window.i18n('changePasswordTitle');
description = window.i18n('changePasswordToastDescription');
break;
case Remove:
title = window.i18n('removePasswordTitle');
description = window.i18n('removePasswordToastDescription');
break;
default: default:
throw new Error(`Invalid action ${action}`); throw missingCaseError(action);
} }
if (action !== Remove) {
ToastUtils.pushToastSuccess(
'setPasswordSuccessToast',
title,
description,
SessionIconType.Lock
);
} else {
ToastUtils.pushToastWarning(
'setPasswordSuccessToast',
title,
description
);
}
onSuccess(this.props.action);
this.closeDialog();
} }
private closeDialog() { private closeDialog() {
@ -263,26 +277,9 @@ class SessionPasswordModalInner extends React.Component<Props, State> {
} }
} }
private onPaste(event: any) {
const clipboard = event.clipboardData.getData('text');
if (clipboard.length > window.CONSTANTS.MAX_PASSWORD_LENGTH) {
const title = String(
window.i18n(
'pasteLongPasswordToastTitle',
window.CONSTANTS.MAX_PASSWORD_LENGTH
)
);
ToastUtils.pushToastWarning('passwordModal', title);
}
// Prevent pating into input
return false;
}
private async onPasswordInput(event: any) { private async onPasswordInput(event: any) {
if (event.key === 'Enter') { if (event.key === 'Enter') {
return this.setPassword(this.props.onOk); return this.setPassword();
} }
const currentPasswordEntered = event.target.value; const currentPasswordEntered = event.target.value;
@ -291,7 +288,7 @@ class SessionPasswordModalInner extends React.Component<Props, State> {
private async onPasswordConfirmInput(event: any) { private async onPasswordConfirmInput(event: any) {
if (event.key === 'Enter') { if (event.key === 'Enter') {
return this.setPassword(this.props.onOk); return this.setPassword();
} }
const currentPasswordConfirmEntered = event.target.value; const currentPasswordConfirmEntered = event.target.value;

View File

@ -32,7 +32,6 @@ class SessionPasswordPromptInner extends React.PureComponent<
}; };
this.onKeyUp = this.onKeyUp.bind(this); this.onKeyUp = this.onKeyUp.bind(this);
this.onPaste = this.onPaste.bind(this);
this.initLogin = this.initLogin.bind(this); this.initLogin = this.initLogin.bind(this);
this.initClearDataView = this.initClearDataView.bind(this); this.initClearDataView = this.initClearDataView.bind(this);
@ -72,8 +71,6 @@ class SessionPasswordPromptInner extends React.PureComponent<
defaultValue="" defaultValue=""
placeholder={' '} placeholder={' '}
onKeyUp={this.onKeyUp} onKeyUp={this.onKeyUp}
maxLength={window.CONSTANTS.MAX_PASSWORD_LENGTH}
onPaste={this.onPaste}
ref={this.inputRef} ref={this.inputRef}
/> />
); );
@ -135,24 +132,6 @@ class SessionPasswordPromptInner extends React.PureComponent<
event.preventDefault(); event.preventDefault();
} }
public onPaste(event: any) {
const clipboard = event.clipboardData.getData('text');
if (clipboard.length > window.CONSTANTS.MAX_PASSWORD_LENGTH) {
this.setState({
error: String(
window.i18n(
'pasteLongPasswordToastTitle',
window.CONSTANTS.MAX_PASSWORD_LENGTH
)
),
});
}
// Prevent pasting into input
return false;
}
public async onLogin(passPhrase: string) { public async onLogin(passPhrase: string) {
const passPhraseTrimmed = passPhrase.trim(); const passPhraseTrimmed = passPhrase.trim();

View File

@ -4,6 +4,7 @@ import { SessionModal } from './SessionModal';
import { SessionButton } from './SessionButton'; import { SessionButton } from './SessionButton';
import { ToastUtils } from '../../session/utils'; import { ToastUtils } from '../../session/utils';
import { DefaultTheme, withTheme } from 'styled-components'; import { DefaultTheme, withTheme } from 'styled-components';
import { PasswordUtil } from '../../util';
interface Props { interface Props {
onClose: any; onClose: any;
@ -77,7 +78,6 @@ class SessionSeedModalInner extends React.Component<Props, State> {
} }
private renderPasswordView() { private renderPasswordView() {
const maxPasswordLen = 64;
const error = this.state.error; const error = this.state.error;
const i18n = window.i18n; const i18n = window.i18n;
const { onClose } = this.props; const { onClose } = this.props;
@ -90,7 +90,6 @@ class SessionSeedModalInner extends React.Component<Props, State> {
id="seed-input-password" id="seed-input-password"
placeholder={i18n('password')} placeholder={i18n('password')}
onKeyUp={this.onEnter} onKeyUp={this.onEnter}
maxLength={maxPasswordLen}
/> />
{error && ( {error && (
@ -143,8 +142,8 @@ class SessionSeedModalInner extends React.Component<Props, State> {
private confirmPassword() { private confirmPassword() {
const passwordHash = this.state.passwordHash; const passwordHash = this.state.passwordHash;
const passwordValue = jQuery('#seed-input-password').val(); const passwordValue = jQuery('#seed-input-password').val();
const isPasswordValid = window.passwordUtil.matchesHash( const isPasswordValid = PasswordUtil.matchesHash(
passwordValue, passwordValue as string,
passwordHash passwordHash
); );

View File

@ -7,7 +7,7 @@ import {
SessionButtonColor, SessionButtonColor,
SessionButtonType, SessionButtonType,
} from '../SessionButton'; } from '../SessionButton';
import { BlockedNumberController } from '../../../util'; import { BlockedNumberController, PasswordUtil } from '../../../util';
import { ToastUtils } from '../../../session/utils'; import { ToastUtils } from '../../../session/utils';
import { ConversationLookupType } from '../../../state/ducks/conversations'; import { ConversationLookupType } from '../../../state/ducks/conversations';
import { StateType } from '../../../state/reducer'; import { StateType } from '../../../state/reducer';
@ -172,8 +172,7 @@ class SettingsViewInner extends React.Component<SettingsViewProps, State> {
type="password" type="password"
id="password-lock-input" id="password-lock-input"
defaultValue="" defaultValue=""
placeholder={' '} placeholder="Password"
maxLength={window.CONSTANTS.MAX_PASSWORD_LENGTH}
/> />
<div className="spacer-xs" /> <div className="spacer-xs" />
@ -211,7 +210,7 @@ class SettingsViewInner extends React.Component<SettingsViewProps, State> {
// Check if the password matches the hash we have stored // Check if the password matches the hash we have stored
const hash = await window.Signal.Data.getPasswordHash(); const hash = await window.Signal.Data.getPasswordHash();
if (hash && !window.passwordUtil.matchesHash(enteredPassword, hash)) { if (hash && !PasswordUtil.matchesHash(enteredPassword, hash)) {
this.setState({ this.setState({
pwdLockError: window.i18n('invalidPassword'), pwdLockError: window.i18n('invalidPassword'),
}); });

View File

@ -1,17 +1,16 @@
const { assert } = require('chai'); import { assert } from 'chai';
import { PasswordUtil } from '../../../../util';
const passwordUtil = require('../../ts/util/passwordUtils');
describe('Password Util', () => { describe('Password Util', () => {
describe('hash generation', () => { describe('hash generation', () => {
it('generates the same hash for the same phrase', () => { it('generates the same hash for the same phrase', () => {
const first = passwordUtil.generateHash('phrase'); const first = PasswordUtil.generateHash('phrase');
const second = passwordUtil.generateHash('phrase'); const second = PasswordUtil.generateHash('phrase');
assert.strictEqual(first, second); assert.strictEqual(first, second);
}); });
it('generates different hashes for different phrases', () => { it('generates different hashes for different phrases', () => {
const first = passwordUtil.generateHash('0'); const first = PasswordUtil.generateHash('0');
const second = passwordUtil.generateHash('1'); const second = PasswordUtil.generateHash('1');
assert.notStrictEqual(first, second); assert.notStrictEqual(first, second);
}); });
}); });
@ -19,12 +18,12 @@ describe('Password Util', () => {
describe('hash matching', () => { describe('hash matching', () => {
it('returns true for the same hash', () => { it('returns true for the same hash', () => {
const phrase = 'phrase'; const phrase = 'phrase';
const hash = passwordUtil.generateHash(phrase); const hash = PasswordUtil.generateHash(phrase);
assert.isTrue(passwordUtil.matchesHash(phrase, hash)); assert.isTrue(PasswordUtil.matchesHash(phrase, hash));
}); });
it('returns false for different hashes', () => { it('returns false for different hashes', () => {
const hash = passwordUtil.generateHash('phrase'); const hash = PasswordUtil.generateHash('phrase');
assert.isFalse(passwordUtil.matchesHash('phrase2', hash)); assert.isFalse(PasswordUtil.matchesHash('phrase2', hash));
}); });
}); });
@ -44,26 +43,26 @@ describe('Password Util', () => {
'#'.repeat(50), '#'.repeat(50),
]; ];
valid.forEach(pass => { valid.forEach(pass => {
assert.isNull(passwordUtil.validatePassword(pass)); assert.isNull(PasswordUtil.validatePassword(pass));
}); });
}); });
it('should return an error if password is not a string', () => { it('should return an error if password is not a string', () => {
const invalid = [0, 123456, [], {}, null, undefined]; const invalid = [0, 123456, [], {}, null, undefined] as any;
invalid.forEach(pass => { invalid.forEach((pass: any) => {
assert.strictEqual( assert.strictEqual(
passwordUtil.validatePassword(pass), PasswordUtil.validatePassword(pass),
'Password must be a string' 'Password must be a string'
); );
}); });
}); });
it('should return an error if password is not between 6 and 50 characters', () => { it('should return an error if password is not between 6 and 64 characters', () => {
const invalid = ['a', 'abcde', '#'.repeat(51), '#'.repeat(100)]; const invalid = ['a', 'abcde', '#'.repeat(51), '#'.repeat(100)];
invalid.forEach(pass => { invalid.forEach(pass => {
assert.strictEqual( assert.strictEqual(
passwordUtil.validatePassword(pass), PasswordUtil.validatePassword(pass),
'Password must be between 6 and 50 characters long' 'Password must be between 6 and 64 characters long'
); );
}); });
}); });
@ -82,7 +81,7 @@ describe('Password Util', () => {
]; ];
invalid.forEach(pass => { invalid.forEach(pass => {
assert.strictEqual( assert.strictEqual(
passwordUtil.validatePassword(pass), PasswordUtil.validatePassword(pass),
'Password must only contain letters, numbers and symbols' 'Password must only contain letters, numbers and symbols'
); );
}); });

View File

@ -3,7 +3,7 @@ import { LocalizerType } from '../types/Util';
const ERRORS = { const ERRORS = {
TYPE: 'Password must be a string', TYPE: 'Password must be a string',
LENGTH: 'Password must be between 6 and 50 characters long', LENGTH: 'Password must be between 6 and 64 characters long',
CHARACTER: 'Password must only contain letters, numbers and symbols', CHARACTER: 'Password must only contain letters, numbers and symbols',
}; };
@ -17,7 +17,7 @@ export const generateHash = (phrase: string) => phrase && sha512(phrase.trim());
export const matchesHash = (phrase: string | null, hash: string) => export const matchesHash = (phrase: string | null, hash: string) =>
phrase && sha512(phrase.trim()) === hash.trim(); phrase && sha512(phrase.trim()) === hash.trim();
export const validatePassword = (phrase: string, i18n: LocalizerType) => { export const validatePassword = (phrase: string, i18n?: LocalizerType) => {
if (typeof phrase !== 'string') { if (typeof phrase !== 'string') {
return i18n ? i18n('passwordTypeError') : ERRORS.TYPE; return i18n ? i18n('passwordTypeError') : ERRORS.TYPE;
} }
@ -27,7 +27,10 @@ export const validatePassword = (phrase: string, i18n: LocalizerType) => {
return i18n ? i18n('noGivenPassword') : ERRORS.LENGTH; return i18n ? i18n('noGivenPassword') : ERRORS.LENGTH;
} }
if (trimmed.length < 6 || trimmed.length > 50) { if (
trimmed.length < 6 ||
trimmed.length > window.CONSTANTS.MAX_PASSWORD_LENGTH
) {
return i18n ? i18n('passwordLengthError') : ERRORS.LENGTH; return i18n ? i18n('passwordLengthError') : ERRORS.LENGTH;
} }

1
ts/window.d.ts vendored
View File

@ -72,7 +72,6 @@ declare global {
lokiSnodeAPI: LokiSnodeAPI; lokiSnodeAPI: LokiSnodeAPI;
mnemonic: RecoveryPhraseUtil; mnemonic: RecoveryPhraseUtil;
onLogin: any; onLogin: any;
passwordUtil: any;
resetDatabase: any; resetDatabase: any;
restart: any; restart: any;
seedNodeList: any; seedNodeList: any;