Files
SnowLuma/.pnpm-store/v11/files/31/17e83e4056a59f440a4f125e72dd128fce77582150c933a0eef46f97fd347e829d20ef49896c02dcc88b37c2bcbb9ef0d4042c3143f6b574752916d1acb928
T
TanTan 2a69b23b47
Dev Build / cleanup (push) Canceled after 0s
Dev Build / typecheck (push) Canceled after 0s
Dev Build / build (linux-arm64, ubuntu-24.04-arm, linux-arm64) (push) Canceled after 0s
Dev Build / build (linux-x64, ubuntu-latest, linux-x64) (push) Canceled after 0s
Dev Build / build (win-x64, windows-latest, win32-x64) (push) Canceled after 0s
Dev Build / dispatch-dev-docker (push) Canceled after 0s
docs: 仓库链接指向本镜像仓库
2026-11-13 09:27:07 +08:00

92 lines
3.2 KiB
Plaintext

import getCleanPasswords from './unmunger/getCleanPasswords.mjs';
import MatchDictionary from '../../matching.mjs';
const getExtras = (passwordWithSubs, i, j) => {
const previousChanges = passwordWithSubs.changes.filter(changes => {
return changes.i < i;
});
const iUnsubbed = previousChanges.reduce((value, change) => {
return value - change.letter.length + change.substitution.length;
}, i);
const usedChanges = passwordWithSubs.changes.filter(changes => {
return changes.i >= i && changes.i <= j;
});
const jUnsubbed = usedChanges.reduce((value, change) => {
return value - change.letter.length + change.substitution.length;
}, j - i + iUnsubbed);
const filtered = [];
const subDisplay = [];
usedChanges.forEach(value => {
const existingIndex = filtered.findIndex(t => {
return t.letter === value.letter && t.substitution === value.substitution;
});
if (existingIndex < 0) {
filtered.push({
letter: value.letter,
substitution: value.substitution
});
subDisplay.push(`${value.substitution} -> ${value.letter}`);
}
});
return {
i: iUnsubbed,
j: jUnsubbed,
subs: filtered,
subDisplay: subDisplay.join(', ')
};
};
/*
* -------------------------------------------------------------------------------
* Dictionary l33t matching -----------------------------------------------------
* -------------------------------------------------------------------------------
*/
class MatchL33t extends MatchDictionary {
isAlreadyIncluded(matches, newMatch) {
return matches.some(l33tMatch => {
return Object.entries(l33tMatch).every(([key, value]) => {
return key === 'subs' || value === newMatch[key];
});
});
}
match(matchOptions) {
const matches = [];
const subbedPasswords = getCleanPasswords(matchOptions.password, this.options.l33tMaxSubstitutions, this.options.trieNodeRoot);
let hasFullMatch = false;
subbedPasswords.forEach(subbedPassword => {
if (hasFullMatch) {
return;
}
const matchedDictionary = super.match({
...matchOptions,
password: subbedPassword.password,
useLevenshtein: subbedPassword.isFullSubstitution
});
matchedDictionary.forEach(match => {
if (!hasFullMatch) {
hasFullMatch = match.i === 0 && match.j === matchOptions.password.length - 1;
}
const extras = getExtras(subbedPassword, match.i, match.j);
const token = matchOptions.password.slice(extras.i, extras.j + 1 || 9e9);
const newMatch = {
...match,
l33t: true,
token,
...extras
};
const alreadyIncluded = this.isAlreadyIncluded(matches, newMatch);
// only return the matches that contain an actual substitution
if (token.toLowerCase() !== match.matchedWord && !alreadyIncluded) {
matches.push(newMatch);
}
});
});
// filter single-character l33t matches to reduce noise.
// otherwise '1' matches 'i', '4' matches 'a', both very common English words
// with low dictionary rank.
return matches.filter(match => match.token.length > 1);
}
}
export { MatchL33t as default };
//# sourceMappingURL=l33t.mjs.map