From 118f9808b173ffba39885d0591c0ac8fa87175e7 Mon Sep 17 00:00:00 2001 From: ulif Date: Thu, 12 Sep 2024 17:27:27 +0200 Subject: [PATCH] Allow custom wordlists, fix #84. Follow XDG base file specification for configuration files and to find additional wordlist files. --- CHANGES.rst | 10 +++- diceware.1 | 111 +++++++++++++++++++++++++++++++++++++++-- diceware/__init__.py | 22 ++++++-- docs/config.rst | 88 +++++++++++++++++++++++++++++--- docs/manpage.rst | 109 ++++++++++++++++++++++++++++++++++++++-- docs/wordlists.rst | 108 ++++++++++++++++++++++++++++++++------- tests/test_diceware.py | 20 ++++++-- 7 files changed, 427 insertions(+), 41 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index a5742f8..4f415c9 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -14,8 +14,16 @@ We now follow the XDG base directory specification, that tells where config unset, `${HOME}/.config/diceware/diceware.ini`. The traditional location `${HOME}/.diceware.ini` is still supported. +Furthermore we read `${XDG_DATA_HOME}/diceware/` (or +`${HOME}/.local/share/diceware/` if `${XDG_DATA_HOME}` is empty or unset) to +lookup further wordlists. + +New option `--show-wordlist-dirs` lists all directory locations we search for +contained wordlists. + - Officially support Python 3.10 to 3.12. -- Fixed #86: Follow `XDG`_ base directory specification. +- Fixed #86: Follow `XDG `_ base directory specification. +- Fixed #84: Allow to store wordlists also in custom directories. - Use `ruff` as linter, drop `flake8`. - Renew `tox` configuration. - Switch to `pyproject`-based project layout, away from using `setup.py`. diff --git a/diceware.1 b/diceware.1 index 3aeb717..5af6b33 100644 --- a/diceware.1 +++ b/diceware.1 @@ -1,6 +1,6 @@ .\" Man page generated from reStructuredText. . -.TH DICEWARE 1 "July 2024" "diceware 1.0.dev0" "User Commands" +.TH DICEWARE 1 "September 2024" "diceware 1.0.dev0" "User Commands" .SH NAME diceware \- create passphrases . @@ -104,15 +104,118 @@ Number of sides of dice. Default: 6 .UNINDENT .UNINDENT .UNINDENT +.SH ENVIRONMENT VARIABLES +.INDENT 0.0 +.TP +.B \fBXDG_CONFIG_HOME\fP +If set and not empty, this variable determines the directory to use for +user\-local configuration files. We then lookup +\fI${XDG_CONFIG_HOME}/diceware/diceware.ini\fP and values set here override +system\-wide config files. +.TP +.B \fBXDG_CONFIG_DIRS\fP +If set and not empty, this variable is interpreted as colon\-separated list +of directories, that might contain system\-wide configuration files. We +lookup \fI/diceware/diceware.ini\fP for each directory set in +\fI$XDG_CONFIG_DIRS\fP\&. +.TP +.B \fBXDG_DATA_HOME\fP +.INDENT 7.0 +.INDENT 3.5 +If set and not empty, this variable determines a directory to search for +additional wordlists. We then lookup \fI${XDG_DATA_HOME}/diceware\fP for any +existing wordlist files. +.UNINDENT +.UNINDENT +.INDENT 7.0 +.TP +.B \fBXDG_DATA_DIRS\fP +If set and not empty, this variable is interpreted as colon\-separated list +of directories, that might contain additional wordlist files. See below. We +lookup \fI/diceware/\fP then for each directory set in the list. +.UNINDENT +.UNINDENT .SH FILES +.sp +Depending on environment variables set (or not set) we lookup certain +directories for configuration files called \fBdiceware.ini\fP and for wordlist +files. +.SS CONFIGURATION FILES +.sp +Configuration settings for \fBdiceware\fP can be spread over several +configuration files. We parse configuration values from the files given below, +but values set in former files take precedence over values set in latter ones. .INDENT 0.0 .TP .B \fI~/.diceware.ini\fP -Your personal diceware configuration file. +Your personal diceware configuration file. Values set here override values +from any other configuration file. +.TP +.B \fI$XDG_CONFIG_HOME/diceware/diceware.ini\fP +Additional location for your personal diceware configuration. Values set +here will override any system\-wide valid values but can be overridden by +\fI~/.diceware.ini\fP\&. +.TP +.B \fI$HOME/.config/diceware/diceware.ini\fP +Alternative location for diceware configuration, only used if +\fI${XDG_CONFIG_HOME}\fP is empty or unset. +.TP +.B \fI/etc/xdg/diceware/diceware.ini\fP +If \fB$XDG_CONFIG_DIRS\fP is not set or empty, we look here for a system\-wide +configuration file. Values set here take least precedence. .UNINDENT +.SS WORDLIST FILES AND WORDLIST DIRECTORIES: +.sp +\fBdiceware\fP comes with a set of wordlists but enables you to add new wordlists +by putting them into certain directories. The paths where the lists are stored +(including the built\-in ones) is shown using \fB\-\-show\-wordlist\-dirs\fP\&. +.sp +Wordlist files are expected to contain lines with one term on each +line and they must have a certain filenames to be found. +.sp +Wordlist filenames have to follow the pattern: \fBwordlist_.txt\fP +where \fB\fP can be any name consisting of letters, numbers, underscores and +hyphens. For instance \fBwordlist_en_eff.txt\fP is the filename of the EFF +(electronic frontier foundation) word list. \fBen_eff\fP is the name of this list. .sp -\fBdiceware\fP also comes with a set of wordlists. The path where these lists are -stored is showed with \fB\-\-help\fP\&. +We support \fB\&.txt\fP and \fB\&.asc\fP as filename extensions for wordlists, where +\fB\&.txt\fP files are expected to be plain wordlists and \fB\&.asc\fP files should +provide a PGP\-signature. +.sp +If wordlists with the same name are found in different directories then the one +in the directory with the highest precedence is taken only. The following +locations are ordered by precedence (highest first). Therefore built\-in +wordlists cannot be overridden by custom wordlists. You can, however, use +custom wordlists with a different name. +.sp +Directories we look up that do not exist (in part or completely) are silently +skipped when searching for wordlist files. +.INDENT 0.0 +.TP +.B \fI/wordlists/\fP +The directory containing the built\-in wordlists as part of the +installation. These are the wordlists that are always available, regardless +of configuration values and their exact location depends on the +installation location of the \fBdiceware\fP package. +.TP +.B \fI$XDG_DATA_HOME/diceware/\fP +If $XDG_DATA_HOME is set and not empty, we look in this directory for +wordlists. +.TP +.B \fI$HOME/.local/share/diceware/\fP +If $XDG_DATA_HOME is unset or empty, we look into this directory for +wordlists. +.TP +.B \fI/diceware\fP from \fI$XDG_DATA_DIRS\fP +If $XDG_DATA_DIR is set and not empty, it is interpreted as a +colon\-separated list of directories with \fI/diceware\fP appended. So, +\fI/foo/bar:/baz\fP will make us look into \fI/foo/bar/diceware/\fP and +\fI/baz/diceware/\fP in that order. +.TP +.B \fI/usr/local/share/diceware/\fP, \fI/usr/share/diceware\fP +If $XDG_DATA_DIRS is unset or empty, we look into these two directories for +wordlists. +.UNINDENT .SH EXAMPLES .INDENT 0.0 .TP diff --git a/diceware/__init__.py b/diceware/__init__.py index 94053a0..538d64c 100644 --- a/diceware/__init__.py +++ b/diceware/__init__.py @@ -24,7 +24,7 @@ from diceware.config import get_config_dict from diceware.logger import configure from diceware.wordlist import ( - WordList, get_wordlist_path, get_wordlists_dir, get_wordlist_names, + WordList, get_wordlist_path, get_wordlist_dirs, get_wordlist_names, ) #: Special chars inserted on demand @@ -60,6 +60,13 @@ def print_version(): print(GPL_TEXT) +def print_wordlist_dirs(): + """Output all dirs we look up for wordlists. + """ + for entry in get_wordlist_dirs(): + print(entry) + + def get_random_sources(): """Get a dictionary of all entry points called diceware_random_source. @@ -92,7 +99,7 @@ def handle_options(args): defaults = get_config_dict() parser = argparse.ArgumentParser( description="Create a passphrase", - epilog="Wordlists are stored in %s" % get_wordlists_dir() + epilog="Use --show-wordlist-dirs to list directories where you can store custom wordlists." ) parser.add_argument( '-n', '--num', default=6, type=int, @@ -121,7 +128,7 @@ def handle_options(args): metavar="NAME", nargs='*', help=( "Use words from this wordlist. Possible values: `%s'. " - "Wordlists are stored in the folder displayed below. " + "Wordlists are stored in the folders displayed below. " "Default: en_eff" % "', `".join(wordlist_names))) realdice_group = parser.add_argument_group( "Arguments related to `realdice' randomsource", @@ -139,7 +146,11 @@ def handle_options(args): help='Be verbose. Use several times for increased verbosity.') parser.add_argument( '--version', action='store_true', - help='output version information and exit.', + help='Output version information and exit.', + ) + parser.add_argument( + '--show-wordlist-dirs', action='store_true', + help='Output directories we look up to find wordlists and exit.', ) for plugin in plugins.values(): if hasattr(plugin, "update_argparser"): @@ -219,6 +230,9 @@ def main(args=None): if options.version: print_version() raise SystemExit(0) + elif options.show_wordlist_dirs: + print_wordlist_dirs() + raise SystemExit(0) try: print(get_passphrase(options)) except (OSError, IOError) as infile_error: diff --git a/docs/config.rst b/docs/config.rst index c7ff0ab..d0c7516 100644 --- a/docs/config.rst +++ b/docs/config.rst @@ -47,16 +47,92 @@ Please note, that all options must be set within a section ``[diceware]``. -Config File Name and Path -------------------------- +Config Files Name and Path +-------------------------- -Currently, we look for configuration files only in the calling users' -home directory. The file must be called:: +Prior to version 1.0 we looked for a single configuration file in the calling +users' home directory only. The file had to be called:: .diceware.ini -(please note the leading dot). If such a file is missing, build-in -defaults apply. +(please note the leading dot). If such a file were missing, buildt-in +defaults applied. + +Since version 1.0 we look into several additional locations, but values set in +``.diceware.ini`` still always override settings from other configuration files +found. + +In order of precedence (with highest priority first) we look into the following +paths:: + + ${HOME}/.diceware.ini + +Values set here override settings in any of the following files. If +``${XDG_CONFIG_HOME}`` is defined and not empty, we then look into + +:: + + ${XDG_CONFIG_HOME}/diceware/diceware.ini + + +or otherwise into + +:: + + ${HOME}/.config/diceware/diceware.ini + +Finally, if a colon-separated and not empty list of directories is set +in + +:: + + ${XDG_CONFIG_DIRS} + +we look up any directory in this list, appended by + +:: + + /diceware/diceware.ini + + +If none of the above files exist, default settings apply. Using this scheme we +follow the `XDG Base Directory Specification +`_. + + +Examples +........ + +If you set the environment variable ``${XDG_CONFIG_DIRS}`` to ``/foo:/etc``, +and then create a file ``diceware.ini`` in directory +``/foo/diceware/`` with contents like this:: + + [diceware] + num = 2 + +then `diceware` will create passphrases with two terms, except other +configuration files or commandline options overrule this setting. + +Precedence of paths in ``${XDG_CONFIG_DIRS}`` is from least to highest +priority. Therefore, if you create a file ``/etc/diceware/diceware.ini`` with +content + +:: + + [diceware] + num = 4 + +then this setting will override ``num = 2`` from the file above. Still any +setting in ``${XDG_CONFIG_HOME}/diceware/diceware.ini`` or +``${HOME}/.config/diceware/diceware.ini`` as explained above will take +precedence while options set in ``${HOME}/.diceware.ini`` or on the commandline +like + +:: + + diceware -n 6 + +will still have highest priority. Option Values diff --git a/docs/manpage.rst b/docs/manpage.rst index 9f2f0de..924be73 100644 --- a/docs/manpage.rst +++ b/docs/manpage.rst @@ -3,7 +3,7 @@ :subtitle: create passphrases :manual_section: 1 :manual_group: User Commands -:date: July 2024 +:date: September 2024 :version: diceware 1.0.dev0 :author: Written by Uli Fouquet and contributors @@ -73,15 +73,116 @@ options Number of sides of dice. Default: 6 +environment variables +--------------------- + +``XDG_CONFIG_HOME`` + If set and not empty, this variable determines the directory to use for + user-local configuration files. We then lookup + `${XDG_CONFIG_HOME}/diceware/diceware.ini` and values set here override + system-wide config files. + +``XDG_CONFIG_DIRS`` + If set and not empty, this variable is interpreted as colon-separated list + of directories, that might contain system-wide configuration files. We + lookup `/diceware/diceware.ini` for each directory set in + `$XDG_CONFIG_DIRS`. + +``XDG_DATA_HOME`` + If set and not empty, this variable determines a directory to search for + additional wordlists. We then lookup `${XDG_DATA_HOME}/diceware` for any + existing wordlist files. + + ``XDG_DATA_DIRS`` + If set and not empty, this variable is interpreted as colon-separated list + of directories, that might contain additional wordlist files. See below. We + lookup `/diceware/` then for each directory set in the list. + + files ----- +Depending on environment variables set (or not set) we lookup certain +directories for configuration files called ``diceware.ini`` and for wordlist +files. + +CONFIGURATION FILES +................... + +Configuration settings for ``diceware`` can be spread over several +configuration files. We parse configuration values from the files given below, +but values set in former files take precedence over values set in latter ones. + `~/.diceware.ini` - Your personal diceware configuration file. + Your personal diceware configuration file. Values set here override values + from any other configuration file. + +`$XDG_CONFIG_HOME/diceware/diceware.ini` + Additional location for your personal diceware configuration. Values set + here will override any system-wide valid values but can be overridden by + `~/.diceware.ini`. + +`$HOME/.config/diceware/diceware.ini` + Alternative location for diceware configuration, only used if + `${XDG_CONFIG_HOME}` is empty or unset. + + +`/etc/xdg/diceware/diceware.ini` + If ``$XDG_CONFIG_DIRS`` is not set or empty, we look here for a system-wide + configuration file. Values set here take least precedence. + + +WORDLIST FILES AND WORDLIST DIRECTORIES: +........................................ + +``diceware`` comes with a set of wordlists but enables you to add new wordlists +by putting them into certain directories. The paths where the lists are stored +(including the built-in ones) is shown using ``--show-wordlist-dirs``. + +Wordlist files are expected to contain lines with one term on each +line and they must have a certain filenames to be found. + +Wordlist filenames have to follow the pattern: ``wordlist_.txt`` +where ```` can be any name consisting of letters, numbers, underscores and +hyphens. For instance ``wordlist_en_eff.txt`` is the filename of the EFF +(electronic frontier foundation) word list. ``en_eff`` is the name of this list. + +We support ``.txt`` and ``.asc`` as filename extensions for wordlists, where +``.txt`` files are expected to be plain wordlists and ``.asc`` files should +provide a PGP-signature. + +If wordlists with the same name are found in different directories then the one +in the directory with the highest precedence is taken only. The following +locations are ordered by precedence (highest first). Therefore built-in +wordlists cannot be overridden by custom wordlists. You can, however, use +custom wordlists with a different name. + +Directories we look up that do not exist (in part or completely) are silently +skipped when searching for wordlist files. + +`/wordlists/` + The directory containing the built-in wordlists as part of the + installation. These are the wordlists that are always available, regardless + of configuration values and their exact location depends on the + installation location of the ``diceware`` package. + +`$XDG_DATA_HOME/diceware/` + If $XDG_DATA_HOME is set and not empty, we look in this directory for + wordlists. + +`$HOME/.local/share/diceware/` + If $XDG_DATA_HOME is unset or empty, we look into this directory for + wordlists. -``diceware`` also comes with a set of wordlists. The path where these lists are -stored is showed with ``--help``. +`/diceware` from `$XDG_DATA_DIRS` + If $XDG_DATA_DIR is set and not empty, it is interpreted as a + colon-separated list of directories with `/diceware` appended. So, + `/foo/bar:/baz` will make us look into `/foo/bar/diceware/` and + `/baz/diceware/` in that order. +`/usr/local/share/diceware/`, `/usr/share/diceware` + If $XDG_DATA_DIRS is unset or empty, we look into these two directories for + wordlists. examples -------- diff --git a/docs/wordlists.rst b/docs/wordlists.rst index 016846b..58f5cb0 100644 --- a/docs/wordlists.rst +++ b/docs/wordlists.rst @@ -12,7 +12,13 @@ good choice for usual private use. but the `long EFF wordlist`_ (see below), because it is more secure and more comfortable to use. -Currently (v0.10) we provide the following lists: +Currently (v1.0) we provide the following lists: + +- `ca` (8192/2^13 words) + + A list of Catalan words. Compiled by `@jawlenskys`_ from Debian dict file for + Catalan and a selection of most used Catalan Wikipedia words. This list + provides the `prefix property`_. - `de` (7776/6^5 words) @@ -54,17 +60,35 @@ Currently (v0.10) we provide the following lists: A list of english adjectives. This list is relatively short and should be used together with other lists -- for instance the `en_nouns` list -- to provide a sufficient security level. List provided from the - `NaturalLanguagePasswords`_ project. + `NaturalLanguagePasswords`_ project. This list got lots of short terms (good + for comfort, bad for security) and does *not* provide the `prefix property`_. - `en_nouns` (7776/6^5 words) A list of english nouns. Can be used together with other lists -- for instance the `en_adjectives` list to form natural language phrases. List - provided from the `NaturalLanguagePasswords`_ project. + provided from the `NaturalLanguagePasswords`_ project. This list got lots of + short terms (good for comfort, bad for security) and does *not* provide the + `prefix property`_. + +- `es` (8192/2^13 words) + + A list of Spanish words, carefully crafted by `@jawlenskys`_ from Debian dict + file for Spanish and a selection of most used Spanish words from `Corpus de + Referencia del Español Actual (CREA)`_. This list provides the `prefix + property`_. + +- `it` (8192/2^13 words) + + A list of Italian words, Compiled by `@jawlenskys`_ from Debian dict file for + Italian and an `Italian frequency list + ` + generated from TV and movie subtitles. This list provides the `prefix + property`_. - `pt-br` (7776/6^5 words) - A list of brazilian portugese words, carefully crafted by `@drebs`_. This + A list of Brazilian Portugese words, carefully crafted by `@drebs`_. This list contains no overshort words. It also provides the `prefix property`_. @@ -115,8 +139,8 @@ easier to break by checking all char combinations than to try all combinations of words in the wordlist. -Add Own Wordlists ------------------ +Using Custom Wordlists +---------------------- You can use any wordlist you like. Simply give the filename and it will be used:: @@ -127,7 +151,7 @@ will be used:: You can even pipe-in dynamic wordlists. Just use the dash ``-`` as filename:: - $ cat mywordgenerator.sh | diceware - + $ mywordgenerator.sh | diceware - HiHiHelloHiHiHello for instance. @@ -137,18 +161,22 @@ to `diceware`. But, if you want to store a wordlist persistently, you can do so too. -The wordlists we offer for use with `diceware` are all stored in a -single folder. The exact location is output by ``--help`` at the very -end:: +The built-in wordlists we offer for use with `diceware` are all stored in a +single directory. The exact location is output by ``--show-wordlist-dirs`` as +first entry:: - $ diceware --help + $ diceware --show-wordlist-dirs + /path/to/some/directory + /path/to/other/directory ... - Wordlists are stored in /some/path/to/folder -Just put your own wordlists into this folder (here: -``/some/path/to/folder``) and rename the file to something like -``wordlist_MY_SPECIAL_NAME.txt``. Afterwards you can pick your -wordlist by running:: +But also all the other directories listed by this command are looked up for +wordlist files (if they exist). + +You can put your own wordlists into one of these folders (here: +``/path/to/some/directory``, ``/path/to/other/directory``) and rename the file +to something like ``wordlist_MY_SPECIAL_NAME.txt``. Afterwards you can pick +your wordlist by running:: $ diceware -w MY_SPECIAL_NAME @@ -168,8 +196,48 @@ funny characters. In fact we accept regular letters, dashes, numbers, and underscores only. Files that do not follow these naming convention are ignored. -A list of all available wordlist names can also be retrieved with -``--help``. See the ``--wordlist`` explanation. +A list of all available wordlist names can be retrieved with ``--help``. See +the ``--wordlist`` explanation. + + +Where Wordlists are Looked Up +----------------------------- + +Starting with version 1.0 wordlists can be stored in several directories. We +look for wordlists in certain directories only. The list of these directories +depends partly on environment variables. It can be shown with:: + + $ diceware --show-wordlist-dirs + /some/installdir/diceware/wordlists + /home/user/.local/share/diceware + /usr/local/share/diceware + /usr/share/diceware + +and may be different on your machine. Wordlist directories are looked up in the +order listed by ``--show-wordlist-dirs``. Wordlists in former directories +override same-named in latter ones. So, with the order given above, a wordlist +named ``wordlist_foo.txt`` in ``/some/installdir/diceware/wordlists`` will have +precedence over a same-named wordfile located in ``/usr/share/diceware``. + +The ``wordlists/`` directory of the Python package itself is always the first +we look into. + +Afterwards we look up ``${XDG_DATA_HOME}/diceware/`` or, if this environment +variable is not set or empty, ``${HOME}/.local/share/diceware``. + +At the end we look into each of the directories listed in the +colon-separated list in ``${XDG_DATA_DIRS}``, appended by ``/diceware``. So, if +``${XDG_DATA_DIRS}`` is set to ``/foo:/bar:/etc/foo``, we will look into +``/foo/diceware``, ``/bar/diceware`` and ``/etc/foo/diceware`` (in that order) +for wordlists. + +In case the environment variable ``${XDG_DATA_DIRS}`` is not set or empty, we +look into ``/usr/local/share/diceware`` and ``/usr/share/diceware`` instead. + +Under all circumstances we stop looking up wordlist directories, when the first +match (with a given wordlist name) happened. + +All these rules try to follow the `XDG Base Directory Specification`_. Plain Wordlists @@ -243,15 +311,17 @@ automatically done by `diceware`. .. warning:: Diceware does *not* automatically verify PGP-signed files. - .. _`8k wordlist`: http://world.std.com/~reinhold/diceware8k.txt +.. _`Corpus de Referencia del Español Actual (CREA)`: https://corpus.rae.es/lfrecuencias.html .. _`diceware standard wordlist`: http://world.std.com/~reinhold/diceware.wordlist.asc .. _`@drebs`: https://github.com/drebs .. _`Electronic Frontier Foundation`: https://eff.org/ .. _`@Heartsucker`: https://github.com/heartsucker/ .. _`Institut für Deutsche Sprache`: https://www.ids-mannheim.de/derewo +.. _`@jawlenskys`: https://github.com/jawlenskys .. _`long EFF wordlist`: https://www.eff.org/files/2016/07/18/eff_large_wordlist.txt .. _`NaturalLanguagePasswords`: https://github.com/NaturalLanguagePasswords .. _`prefix property`: https://en.wikipedia.org/wiki/Prefix_code .. _`scientific effort`: https://www.eff.org/deeplinks/2016/07/new-wordlists-random-passphrases .. _`securedrop`: https://github.com/freedomofpress/securedrop +.. _`XDG Base Directory Specification`: https://specifications.freedesktop.org/basedir-spec/latest/ diff --git a/tests/test_diceware.py b/tests/test_diceware.py index 9ff2828..545c041 100644 --- a/tests/test_diceware.py +++ b/tests/test_diceware.py @@ -7,7 +7,7 @@ from io import StringIO from errno import EISDIR from diceware import ( - get_wordlists_dir, SPECIAL_CHARS, insert_special_char, get_passphrase, + SPECIAL_CHARS, insert_special_char, get_passphrase, handle_options, main, __version__, print_version, get_random_sources, get_wordlist_names ) @@ -140,6 +140,13 @@ def test_handle_options_dice_sides(self): options = handle_options(['--dice-sides', '21']) assert options.dice_sides == 21 + def test_handle_options_show_wordlist_dirs(self): + # we can request to show the wordlist dirs + options = handle_options([]) + assert options.show_wordlist_dirs is False + options = handle_options(['--show-wordlist-dirs']) + assert options.show_wordlist_dirs is True + def test_handle_options_considers_configfile(self, home_dir): # defaults from a local configfile are respected config_file = home_dir / ".diceware.ini" @@ -288,10 +295,8 @@ def test_main_help(self, argv_handler, capsys): os.path.dirname(__file__), 'exp_help_output.txt') with open(expected_path, 'r') as fd: expected_output = fd.read() - wordlists_dir = get_wordlists_dir() expected_output = expected_output.replace("\n", "") out = expected_output.replace("\n", "") - out = out.replace(wordlists_dir, "") assert out == expected_output def test_main_version(self, argv_handler, capsys): @@ -371,3 +376,12 @@ def test_main_wordlist(self, argv_handler, capsys, wordlists_dir): main() out, err = capsys.readouterr() assert out == 'FooFooFooFooFooFoo\n' + + def test_main_can_show_wordlists(self, argv_handler, capsys, wordlists_dir): + # we can list the wordlist dirs + sys.argv = ['diceware', '--show-wordlist-dirs'] + with pytest.raises(SystemExit) as exc_info: + main() + assert exc_info.value.code == 0 + out, err = capsys.readouterr() + assert str(wordlists_dir) in out