Switch from zxcvbn to @zxcvbn-ts (#11611)

Co-authored-by: Richard van der Hoff <1389908+richvdh@users.noreply.github.com>
This commit is contained in:
Michael Telatynski 2023-09-19 22:34:41 +01:00 committed by GitHub
parent 5cb8cbd893
commit 38315b5e2b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
48 changed files with 4537 additions and 4251 deletions

View file

@ -14,44 +14,65 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import zxcvbn, { ZXCVBNFeedbackWarning } from "zxcvbn";
import { zxcvbn, zxcvbnOptions, ZxcvbnResult, TranslationKeys } from "@zxcvbn-ts/core";
import * as zxcvbnCommonPackage from "@zxcvbn-ts/language-common";
import * as zxcvbnEnPackage from "@zxcvbn-ts/language-en";
import { MatrixClient } from "matrix-js-sdk/src/matrix";
import { _t, _td, TranslationKey } from "../languageHandler";
import { _t } from "../languageHandler";
import { MatrixClientPeg } from "../MatrixClientPeg";
import SdkConfig from "../SdkConfig";
const ZXCVBN_USER_INPUTS = ["riot", "matrix"];
zxcvbnOptions.setOptions({
dictionary: {
...zxcvbnCommonPackage.dictionary,
...zxcvbnEnPackage.dictionary,
userInputs: ["riot", "matrix", "element", SdkConfig.get().brand],
},
graphs: zxcvbnCommonPackage.adjacencyGraphs,
useLevenshteinDistance: true,
});
// Translations for zxcvbn's suggestion strings
_td("Use a few words, avoid common phrases");
_td("No need for symbols, digits, or uppercase letters");
_td("Use a longer keyboard pattern with more turns");
_td("Avoid repeated words and characters");
_td("Avoid sequences");
_td("Avoid recent years");
_td("Avoid years that are associated with you");
_td("Avoid dates and years that are associated with you");
_td("Capitalization doesn't help very much");
_td("All-uppercase is almost as easy to guess as all-lowercase");
_td("Reversed words aren't much harder to guess");
_td("Predictable substitutions like '@' instead of 'a' don't help very much");
_td("Add another word or two. Uncommon words are better.");
// and warnings
_td('Repeats like "aaa" are easy to guess');
_td('Repeats like "abcabcabc" are only slightly harder to guess than "abc"');
_td("Sequences like abc or 6543 are easy to guess");
_td("Recent years are easy to guess");
_td("Dates are often easy to guess");
_td("This is a top-10 common password");
_td("This is a top-100 common password");
_td("This is a very common password");
_td("This is similar to a commonly used password");
_td("A word by itself is easy to guess");
_td("Names and surnames by themselves are easy to guess");
_td("Common names and surnames are easy to guess");
_td("Straight rows of keys are easy to guess");
_td("Short keyboard patterns are easy to guess");
function getTranslations(): TranslationKeys {
return {
warnings: {
straightRow: _t("zxcvbn|warnings|straightRow"),
keyPattern: _t("zxcvbn|warnings|keyPattern"),
simpleRepeat: _t("zxcvbn|warnings|simpleRepeat"),
extendedRepeat: _t("zxcvbn|warnings|extendedRepeat"),
sequences: _t("zxcvbn|warnings|sequences"),
recentYears: _t("zxcvbn|warnings|recentYears"),
dates: _t("zxcvbn|warnings|dates"),
topTen: _t("zxcvbn|warnings|topTen"),
topHundred: _t("zxcvbn|warnings|topHundred"),
common: _t("zxcvbn|warnings|common"),
similarToCommon: _t("zxcvbn|warnings|similarToCommon"),
wordByItself: _t("zxcvbn|warnings|wordByItself"),
namesByThemselves: _t("zxcvbn|warnings|namesByThemselves"),
commonNames: _t("zxcvbn|warnings|commonNames"),
userInputs: _t("zxcvbn|warnings|userInputs"),
pwned: _t("zxcvbn|warnings|pwned"),
},
suggestions: {
l33t: _t("zxcvbn|suggestions|l33t"),
reverseWords: _t("zxcvbn|suggestions|reverseWords"),
allUppercase: _t("zxcvbn|suggestions|allUppercase"),
capitalization: _t("zxcvbn|suggestions|capitalization"),
dates: _t("zxcvbn|suggestions|dates"),
recentYears: _t("zxcvbn|suggestions|recentYears"),
associatedYears: _t("zxcvbn|suggestions|associatedYears"),
sequences: _t("zxcvbn|suggestions|sequences"),
repeated: _t("zxcvbn|suggestions|repeated"),
longerKeyboardPattern: _t("zxcvbn|suggestions|longerKeyboardPattern"),
anotherWord: _t("zxcvbn|suggestions|anotherWord"),
useWords: _t("zxcvbn|suggestions|useWords"),
noNeed: _t("zxcvbn|suggestions|noNeed"),
pwned: _t("zxcvbn|suggestions|pwned"),
},
// We don't utilise the time estimation at this time so just pass through the English translations here
timeEstimation: zxcvbnEnPackage.translations.timeEstimation,
};
}
/**
* Wrapper around zxcvbn password strength estimation
@ -59,7 +80,7 @@ _td("Short keyboard patterns are easy to guess");
* (obviously) which is large.
*
* @param {string} password Password to score
* @param matrixClient the client of the logged in user, if any
* @param matrixClient the client of the logged-in user, if any
* @param userInputs additional strings such as the user's name which should be considered a bad password component
* @returns {object} Score result with `score` and `feedback` properties
*/
@ -67,10 +88,12 @@ export function scorePassword(
matrixClient: MatrixClient | null,
password: string,
userInputs: string[] = [],
): zxcvbn.ZXCVBNResult | null {
): ZxcvbnResult | null {
if (password.length === 0) return null;
const inputs = [...userInputs, ...ZXCVBN_USER_INPUTS];
// copy the supplied array before modifying it
const inputs = [...userInputs];
if (matrixClient) {
inputs.push(matrixClient.getUserIdLocalpart()!);
}
@ -82,6 +105,8 @@ export function scorePassword(
// This is fine
}
zxcvbnOptions.setTranslations(getTranslations());
let zxcvbnResult = zxcvbn(password, inputs);
// Work around https://github.com/dropbox/zxcvbn/issues/216
if (password.includes(" ")) {
@ -89,15 +114,5 @@ export function scorePassword(
if (resultNoSpaces.score < zxcvbnResult.score) zxcvbnResult = resultNoSpaces;
}
for (let i = 0; i < zxcvbnResult.feedback.suggestions.length; ++i) {
// translate suggestions - we ensure we mark them as `_td` at the top of this file
// https://github.com/dropbox/zxcvbn/issues/284 will be a better approach when it lands
zxcvbnResult.feedback.suggestions[i] = _t(zxcvbnResult.feedback.suggestions[i] as TranslationKey);
}
// and warning, if any
if (zxcvbnResult.feedback.warning) {
zxcvbnResult.feedback.warning = _t(zxcvbnResult.feedback.warning) as ZXCVBNFeedbackWarning;
}
return zxcvbnResult;
}