diff --git a/stubs/zxcvbn/METADATA.toml b/stubs/zxcvbn/METADATA.toml new file mode 100644 index 000000000..2e800d80f --- /dev/null +++ b/stubs/zxcvbn/METADATA.toml @@ -0,0 +1 @@ +version = "4.4.*" diff --git a/stubs/zxcvbn/zxcvbn/__init__.pyi b/stubs/zxcvbn/zxcvbn/__init__.pyi new file mode 100644 index 000000000..f363552bd --- /dev/null +++ b/stubs/zxcvbn/zxcvbn/__init__.pyi @@ -0,0 +1,18 @@ +import datetime +from collections.abc import Iterable +from decimal import Decimal +from typing_extensions import TypedDict + +from .feedback import _Feedback +from .matching import _Match +from .time_estimates import _TimeEstimate + +class _Result(_TimeEstimate, TypedDict): + password: str + guesses: Decimal + guesses_log10: float + sequence: list[_Match] + calc_time: datetime.timedelta + feedback: _Feedback + +def zxcvbn(password: str, user_inputs: Iterable[object] | None = ...) -> _Result: ... diff --git a/stubs/zxcvbn/zxcvbn/adjacency_graphs.pyi b/stubs/zxcvbn/zxcvbn/adjacency_graphs.pyi new file mode 100644 index 000000000..8b1e8e823 --- /dev/null +++ b/stubs/zxcvbn/zxcvbn/adjacency_graphs.pyi @@ -0,0 +1,5 @@ +from typing_extensions import TypeAlias + +_Graph: TypeAlias = dict[str, list[str | None]] + +ADJACENCY_GRAPHS: dict[str, _Graph] diff --git a/stubs/zxcvbn/zxcvbn/feedback.pyi b/stubs/zxcvbn/zxcvbn/feedback.pyi new file mode 100644 index 000000000..31b8e63b9 --- /dev/null +++ b/stubs/zxcvbn/zxcvbn/feedback.pyi @@ -0,0 +1,12 @@ +from collections.abc import Sequence +from typing_extensions import Literal, TypedDict + +from .matching import _Match + +class _Feedback(TypedDict): + warning: str + suggestions: list[str] + +def get_feedback(score: Literal[0, 1, 2, 3, 4], sequence: Sequence[_Match]) -> _Feedback: ... +def get_match_feedback(match: _Match, is_sole_match: bool) -> _Feedback: ... +def get_dictionary_match_feedback(match: _Match, is_sole_match: bool) -> _Feedback: ... diff --git a/stubs/zxcvbn/zxcvbn/frequency_lists.pyi b/stubs/zxcvbn/zxcvbn/frequency_lists.pyi new file mode 100644 index 000000000..08445004b --- /dev/null +++ b/stubs/zxcvbn/zxcvbn/frequency_lists.pyi @@ -0,0 +1 @@ +FREQUENCY_LISTS: dict[str, list[str]] diff --git a/stubs/zxcvbn/zxcvbn/matching.pyi b/stubs/zxcvbn/zxcvbn/matching.pyi new file mode 100644 index 000000000..8adf326a1 --- /dev/null +++ b/stubs/zxcvbn/zxcvbn/matching.pyi @@ -0,0 +1,94 @@ +from collections.abc import Iterable, Mapping +from decimal import Decimal +from typing import Any, Pattern +from typing_extensions import Literal, NotRequired, TypedDict + +from .adjacency_graphs import _Graph + +class _Match(TypedDict): + pattern: Literal["dictionary", "spatial", "repeat", "sequence", "regex", "date"] + token: str + i: int + j: int + guesses: NotRequired[int] # all patterns except 'date' + guesses_log10: NotRequired[float] # all patterns except 'date' + + # pattern == 'date' + separator: NotRequired[str] + year: NotRequired[int] + month: NotRequired[int] + day: NotRequired[int] + + # pattern == 'dictionary' + matched_word: NotRequired[str] + dictionary_name: NotRequired[str] + l33t: NotRequired[bool] + reversed: NotRequired[bool] + rank: NotRequired[int] + base_guesses: NotRequired[int | Decimal] # Decimal for 'repeat', see below + uppercase_variations: NotRequired[int] + l33t_variations: NotRequired[int] + + # pattern == 'spatial' + turns: NotRequired[int] + + # pattern == 'repeat' + base_token: NotRequired[str] + # base_guesses: NotRequired[Decimal] + base_matches: NotRequired[list[Any]] # Any = _Match, https://github.com/python/mypy/issues/731 + repeat_count: NotRequired[float] + + # pattern == 'regex' + regex_name: NotRequired[str] + +def build_ranked_dict(ordered_list: Iterable[str]) -> dict[str, int]: ... + +RANKED_DICTIONARIES: dict[str, dict[str, int]] + +def add_frequency_lists(frequency_lists_: Mapping[str, Iterable[str]]) -> None: ... + +GRAPHS: dict[str, dict[str, list[str | None]]] +L33T_TABLE: dict[str, list[str]] +REGEXEN: dict[str, Pattern[str]] +DATE_MAX_YEAR: int +DATE_MIN_YEAR: int +DATE_SPLITS: dict[int, list[list[int]]] + +def omnimatch(password: str, _ranked_dictionaries: dict[str, dict[str, int]] = ...) -> list[_Match]: ... +def dictionary_match(password: str, _ranked_dictionaries: dict[str, dict[str, int]] = ...) -> list[_Match]: ... +def reverse_dictionary_match(password: str, _ranked_dictionaries: dict[str, dict[str, int]] = ...) -> list[_Match]: ... +def relevant_l33t_subtable(password: str, table: Mapping[str, Iterable[str]]) -> dict[str, list[str]]: ... +def enumerate_l33t_subs(table: Mapping[str, Iterable[str]]) -> list[dict[str, str]]: ... +def translate(string: str, chr_map: Mapping[str, str]) -> str: ... +def l33t_match( + password: str, _ranked_dictionaries: dict[str, dict[str, int]] = ..., _l33t_table: dict[str, list[str]] = ... +) -> list[_Match]: ... +def repeat_match(password: str, _ranked_dictionaries: dict[str, dict[str, int]] = ...) -> list[_Match]: ... +def spatial_match( + password: str, _graphs: dict[str, _Graph] = ..., _ranked_dictionaries: dict[str, dict[str, int]] = ... +) -> list[_Match]: ... + +SHIFTED_RX: Pattern[str] + +def spatial_match_helper(password: str, graph: _Graph, graph_name: str) -> list[_Match]: ... + +MAX_DELTA: int + +def sequence_match(password: str, _ranked_dictionaries: dict[str, dict[str, int]] = ...) -> list[_Match]: ... +def regex_match( + password: str, _regexen: dict[str, Pattern[str]] = ..., _ranked_dictionaries: dict[str, dict[str, int]] = ... +) -> list[_Match]: ... +def date_match(password: str, _ranked_dictionaries: dict[str, dict[str, int]] = ...) -> list[_Match]: ... + +class _DM(TypedDict): + month: int + day: int + +class _DMY(TypedDict): + year: int + month: int + day: int + +def map_ints_to_dmy(ints: tuple[int, int, int]) -> _DMY | None: ... +def map_ints_to_dm(ints: tuple[int, int]) -> _DM | None: ... +def two_to_four_digit_year(year: int) -> int: ... diff --git a/stubs/zxcvbn/zxcvbn/scoring.pyi b/stubs/zxcvbn/zxcvbn/scoring.pyi new file mode 100644 index 000000000..8567b357b --- /dev/null +++ b/stubs/zxcvbn/zxcvbn/scoring.pyi @@ -0,0 +1,47 @@ +from collections.abc import Iterable +from decimal import Decimal +from typing import Pattern +from typing_extensions import TypedDict + +from .adjacency_graphs import _Graph +from .matching import _Match + +def calc_average_degree(graph: _Graph) -> float: ... + +BRUTEFORCE_CARDINALITY: int +MIN_GUESSES_BEFORE_GROWING_SEQUENCE: int +MIN_SUBMATCH_GUESSES_SINGLE_CHAR: int +MIN_SUBMATCH_GUESSES_MULTI_CHAR: int +MIN_YEAR_SPACE: int +REFERENCE_YEAR: int + +class _GuessesResult(TypedDict): + password: str + guesses: int + guesses_log10: float + sequence: list[_Match] + +def nCk(n: int, k: int) -> float: ... +def most_guessable_match_sequence(password: str, matches: Iterable[_Match], _exclude_additive: bool = ...) -> _GuessesResult: ... +def estimate_guesses(match: _Match, password: str) -> Decimal: ... +def bruteforce_guesses(match: _Match) -> int: ... +def dictionary_guesses(match: _Match) -> int: ... +def repeat_guesses(match: _Match) -> Decimal: ... +def sequence_guesses(match: _Match) -> int: ... +def regex_guesses(match: _Match) -> int | None: ... +def date_guesses(match: _Match) -> int: ... + +KEYBOARD_AVERAGE_DEGREE: float +KEYPAD_AVERAGE_DEGREE: float +KEYBOARD_STARTING_POSITIONS: int +KEYPAD_STARTING_POSITIONS: int + +def spatial_guesses(match: _Match) -> int: ... + +START_UPPER: Pattern[str] +END_UPPER: Pattern[str] +ALL_UPPER: Pattern[str] +ALL_LOWER: Pattern[str] + +def uppercase_variations(match: _Match) -> int: ... +def l33t_variations(match: _Match) -> int: ... diff --git a/stubs/zxcvbn/zxcvbn/time_estimates.pyi b/stubs/zxcvbn/zxcvbn/time_estimates.pyi new file mode 100644 index 000000000..deafec467 --- /dev/null +++ b/stubs/zxcvbn/zxcvbn/time_estimates.pyi @@ -0,0 +1,24 @@ +from decimal import Decimal +from typing_extensions import Literal, TypedDict + +class _TimeEstimate(TypedDict): + crack_times_seconds: _CrackTimeSeconds + crack_times_display: _CrackTimesDisplay + score: Literal[0, 1, 2, 3, 4] + +class _CrackTimeSeconds(TypedDict): + online_throttling_100_per_hour: Decimal + online_no_throttling_10_per_second: Decimal + offline_slow_hashing_1e4_per_second: Decimal + offline_fast_hashing_1e10_per_second: Decimal + +class _CrackTimesDisplay(TypedDict): + online_throttling_100_per_hour: str + online_no_throttling_10_per_second: str + offline_slow_hashing_1e4_per_second: str + offline_fast_hashing_1e10_per_second: str + +def estimate_attack_times(guesses: Decimal | float) -> _TimeEstimate: ... +def guesses_to_score(guesses: Decimal) -> Literal[0, 1, 2, 3, 4]: ... +def display_time(seconds: float) -> str: ... +def float_to_decimal(f: float) -> Decimal: ...