Compare commits

...

431 Commits
v2.2 ... master

Author SHA1 Message Date
Jaromil
ff692999de enable btrfs light zstd:1 compression by default at mount
this comes free and is handled gracefully by btrfs according to
pre-compression heuristics, making it an ideal filesystem choice for
tomb volumes that have compressable contents.

A `compress-force=zstd:1` custom option would deactivate the heuristic
test and compress everything.
2024-09-08 04:31:33 +02:00
Jaromil
4783456814 local definition of variables in mount function 2024-09-08 04:31:33 +02:00
Narrat
653609a4b9 slam_tomb: simplify and rename to _kill_processes
In general umount_tomb and slam_tomb shared a lot of similar code.
Main difference being, that the latter additionally searched for
processes and would still call umount_tomb if the processes could
be killed.
umount_tomb would then again search with the provided name for the
relevant tomb in list_tomb_mounts, which should be obsolete at this
point.
Therefore the decision to reduce slam_tomb in functionality. It would
only work on a supplied tombname and tombmount, look if there are
processes and is called from within umount_tomb.
(Theoretical tombname could be removed)
Calling tomb with slam or close sets a flag, which will decide if
that part in umount_tomb will be executed.
2024-09-01 22:30:17 +02:00
Narrat
284fb4a3cd slam_tomb(): don't parse process output and rework
In #504 list_processes() got reworked in a way to avoid parsing process
output as this had interesting side-effects.
Back then I mentioned the same behaviour existing in slam_tomb() which
should probably be changed too. This PR addresses that.
Firstly it will use list_processes() from within slam_tomb(), as this is
in principal overlapping functionality. For this list_processes() needed
to be adjusted. It now has a return value which can indicate if there
were processes.
Secondly the order of execution was changed in slam_tomb(). Before it
would process one process and work through the signals until this
process was killed. Now it will take a signal and issue a kill for all
processes found.
2024-09-01 22:30:17 +02:00
Jaromil
0ef195dff0 small updates to readme 2024-08-31 22:46:39 +02:00
Jaromil
48c08c0086 fix: resize on btrfs formatted volumes
new minimum increase for resize is 120MiB

increase resize delta on all test to be above new minimum

skip resize test for btrfs mixedmode (always fails)
2024-08-31 22:46:39 +02:00
Jaromil
29098f356c correct error message typo in resize (and in all translations) 2024-08-31 22:46:39 +02:00
Jaromil
447817de6c test btrfs tomb resize 2024-08-31 22:46:39 +02:00
Jaromil
b7fa057e48 elevate minimum size permitted for btrfs filesystem 2024-08-31 22:46:39 +02:00
Jaromil
963a0cc321 test btrfs tomb 2024-08-31 22:46:39 +02:00
Jaromil
117bd9bd6e improve readability of code in some complex branching points
avoid usage of if...elif...elif...else in some points, substituted
with while true; do ... done loops and break statements on success.
2024-08-31 22:46:39 +02:00
Jaromil
c1b5e1b310 remove sphinx from tests and docs
leftover strings are in translations, maybe take them off later
2024-08-31 22:46:39 +02:00
Jaromil
afe0390d93 remove unused and old libsphinx support
steff seems to be moving towards new implementations and this was
never reported as used by anyone
2024-08-31 22:46:39 +02:00
Jaromil
32eab3beec kdf iterations need only to be specified when forging a key
the key header saves the key iteration set when forging
2024-08-31 22:46:39 +02:00
Jaromil
11a5776456 add argon2 kdf test 2024-08-31 22:46:39 +02:00
Jaromil
ef1541f7a2 enable tests on ubuntu 24, and add doas and argon2 to CI
also remove python2 from latest ubuntu as no more found

doas test is enabled only for latest
2024-08-31 22:46:39 +02:00
Narrat
73950fe3d8 tests: add outside bind mounts to 75_hooks
it may happen, that someone bind mounts manually or via an immutable setup the tomb mountdir somewhere else.
Tomb should be able to discover such mounts and close them if the tomb itself is closed.
2024-08-31 19:07:09 +02:00
Narrat
41b899e4e1 slam_tomb: adjust for changes in list_tomb_mounts
As the argument for list_tomb_mounts uses the input directly, it needs to be uniform.
Therefore one must make sure that extraneous character like parentheses are removed from the variable.
And those are in place in tombname for slam_tomb().
2024-08-31 19:07:09 +02:00
Narrat
33f7878a22 rework handling of bind mounts
Instead of only looking for bind mounts from within a tomb due to bind-hooks, also consider bind mounts that happenfrom the outside (example: open a tomb and manually issue a mount --bind /media/tomb some/other/location).
Such a mount wouldn't be filtered before (only looking for an additional [/path/] added to TARGET.
Instead look for every mount that is related to the respective /dev/mapper/ entry of a tomb and also close or list them.
This helps to avoid to loop again against mounted tombs inside the main loop which loops over mounted tombs.
2024-08-31 19:07:09 +02:00
Narrat
42e233d2b0 list_tomb_binds: simplify function
similar to list_tomb_mounts, rework the findmnt usage to usage of the
actual tomb mapper device.
Simplifies the awk usage and just only one argument needed for the
mapper function.
2024-08-31 19:07:09 +02:00
Narrat
6df1cdeab9 list_tomb_mounts: simplify the function
previously it had dedicated cases for listing all tombs and a singular
one, which duplicated code.
The function got reworked, that it uses a different approach for
findmnt. Instead of filtering the general result, it now uses --source
on the tomb specific crypsetup mapper. Those are searched via general
globbing of the devices in /dev/mapper. This allows to combine the
previous separate cases.
Additionally remove the usage of _sudo for findmnt, as it is not
necessary.
2024-08-31 19:07:09 +02:00
Narrat
0b25ba6d68 umount_tomb: avoid double execution of list_tomb_binds()
Especially directly after each other.
2024-08-31 19:07:09 +02:00
Narrat
89283a06b7 Avoid manual handling of loop devices
Cryptsetup is since 1.3.0 capable of setting up a loop device if the
device argument is a file.
This has the additional benefit that those loop devices will get the
AUTOCLEAR flag (available with Linux 2.6.25). This means those loop
devices will be closed as soon they're unused (on luksClose).
2024-08-31 19:07:09 +02:00
Narrat
c83068c03a Man-Page: remove part about gpg-agent
No plans on making this necessary and information about setting it up may not be the right place for this man-page.
2024-08-31 15:50:50 +02:00
Narrat
be533b3995 Man: merge section on Password Input
Somehow this section existed two times in the man page with similar information.
Enhance it with notes regarding wayland and adjust the recommened pinentry programs.
GTK2 is long time EOL and actively being fased out by distributions, which makes pinentry-gtk2 obsolete.
pinentry-tty will work on every headless system or from a textual interface. pinentry-curses may end up starting if the respective ncurses is available.
2024-08-31 15:50:50 +02:00
Narrat
75aafc0c8c
CI optimization (#531)
* portable was moved into extras and is unmaintained
* the CI tests for the portable rewrite are archived
2024-08-05 15:32:54 +02:00
Ricky Tigg
45c4616110 Translated using Weblate (Finnish)
Currently translated at 79.0% (276 of 349 strings)

Translation: Tomb/tomb
Translate-URL: https://hosted.weblate.org/projects/tomb/tomb/fi/
2024-07-30 06:21:01 +02:00
gallegonovato
7016515ce6 Translated using Weblate (Spanish)
Currently translated at 71.3% (249 of 349 strings)

Translation: Tomb/tomb
Translate-URL: https://hosted.weblate.org/projects/tomb/tomb/es/
2024-07-30 06:21:01 +02:00
Ricky Tigg
97c61dc513 Translated using Weblate (Finnish)
Currently translated at 61.0% (213 of 349 strings)

Translation: Tomb/tomb
Translate-URL: https://hosted.weblate.org/projects/tomb/tomb/fi/
2024-07-30 06:21:01 +02:00
gallegonovato
4523823c48 Translated using Weblate (Spanish)
Currently translated at 70.7% (247 of 349 strings)

Translation: Tomb/tomb
Translate-URL: https://hosted.weblate.org/projects/tomb/tomb/es/
2024-07-30 06:21:01 +02:00
Narrat
cb997eec2c extras/tomber: restructure
move contents one folder up. Instead of extras/tomber/tomber it now resides in extras/tomber.
Move and rename extras/PYTHON.md into extras/tomber/README.md, as it is the README of the tool.
2024-07-30 06:19:10 +02:00
Dyne.org foundation
857895a750 Added translation using Weblate (Finnish) 2024-07-22 12:13:24 +02:00
Gianluca Montecchi
891fa80c7a Translated using Weblate (Italian)
Currently translated at 96.5% (277 of 287 strings)

Translation: Tomb/tomb
Translate-URL: https://hosted.weblate.org/projects/tomb/tomb/it/
2024-07-22 12:13:24 +02:00
Gianluca Montecchi
431f1c6647 Translated using Weblate (Italian)
Currently translated at 95.8% (278 of 290 strings)

Translation: Tomb/tomb
Translate-URL: https://hosted.weblate.org/projects/tomb/tomb/it/
2024-07-22 12:13:24 +02:00
Jaromil
2b75962e54 updated for release
version bump
2024-07-12 12:37:28 +02:00
Jaromil
bae8af351b improve support of non-sudo setup, by default support doas
aldo better support when launched from root
2024-07-12 11:51:53 +02:00
Jaromil
2082198b36 remove unnecessary flag from maphash sha256 2024-07-12 11:51:53 +02:00
Jaromil
552dc82fe3 add alpine deps
typo in makefile
2024-07-12 11:51:53 +02:00
Jaromil
7ebcfc767e add support for pinentry-tty
fallback to pinentry-tty is supported, correct message working now
doesn't distinguishes between pinentry versions
2024-07-12 11:51:53 +02:00
Jaromil
c72432d640 fix cloakify to work easily and update documentation
a few changes needed to be adjusted and manpage updated, now we also
ship cloakify ciphers into extras, a link is left to original repo.
2024-07-12 11:51:53 +02:00
Jaromil
4a277c97f2 set default iterations to 3 in case of argon2 kdf
fix: #214
2024-07-12 11:51:53 +02:00
Jaromil
735d540fe7 feature recoll in place of swish-e to search a tomb's contents
Swish is outdated and recoll works much better as a frontend to
xapian, so we adopt it to provide local search over file contents in a
tomb. This update also makes plocate optional to search over file
names, not mandatory. Recoll works also in GUI using its -c
commandline option followed by the indexed tomb's path.
2024-07-12 11:51:53 +02:00
Jaromil
b715917b31
deploy new website via hugo action on github pages 2024-07-11 10:40:12 +02:00
Jaromil
0d06c994cf fix tests to run also when swap present 2024-05-12 22:09:46 +02:00
Jaromil
6c383ffd64 move unfinished portable veracrypt version in extras 2024-05-12 21:47:57 +02:00
nerun
ddb2de6072 Updated copyright year in 'generate_translatable_strings' script. 2024-05-06 15:06:56 +02:00
nerun
3398b7bf89 Updated extras/translations/README. 2024-05-06 15:06:56 +02:00
nerun
abe5704658 Updated all language files to Tomb v2.10 (.po/.pot). 2024-05-06 15:06:56 +02:00
nerun
d0b84d78a0 Fixed tomb listing failure message: localization friendly. 2024-05-06 15:06:56 +02:00
Melroy van den Berg
91adbbe183 Update copyright in translation source 2024-01-26 00:38:37 +01:00
Melroy van den Berg
766cd27c1b Update copyright lines 2024-01-26 00:38:13 +01:00
Narrat
dee2b0f8c4 list_processes: use lsof to list processes
Parsing the output from lsof had possibilities for race conditions.
Either due to short lived processes or issuing "tomb ps" from a terminal which cwd is from inside the tomb.
This would spit out available users on the system.
To avoid this use the lsof output directly.

In the future formatting could be reintroduced via commands like
"lsof +D "$tombmount" -F Lc" or "lsof +D "$tombmount" -F Lc0".

This fixes #503
2024-01-26 00:37:46 +01:00
nerun
13eeef7c6c manpage: replaced 'mlocate' to 'mlocate/plocate', because of commit 59d7331. 2024-01-26 00:30:58 +01:00
nerun
194d60fe9a manpage: better formatting of .EX/.EE macros 2024-01-26 00:30:58 +01:00
nerun
7f91cc917d manpage: fixed typo in "bind-hooks" 2024-01-26 00:30:58 +01:00
nerun
09d981f0fc manpage: 80 columns line break in PASSWORD INPUT. 2024-01-26 00:30:58 +01:00
vladislav doster
e1afecb832 fix: remove duplicate help in subcommands_opts 2024-01-26 00:28:50 +01:00
Melroy van den Berg
e97c088a26 Update copyright in README 2024-01-24 08:42:08 +01:00
Melroy van den Berg
9385ddee43 Update download link 2024-01-24 08:42:08 +01:00
27792f4421 Fix umount issue when path have spaces in directory or file name 2024-01-12 15:09:47 +01:00
8be3163022 Fix spaces in bind-hooks paths 2024-01-12 15:09:47 +01:00
0rtz
21da75adab Use cat instead of _cat
_cat is not defined/autoloaded anywhere, so use plain unix cat utility
to avoid getting "command not found" error
2023-12-16 15:52:34 +01:00
bittin1ddc447d824349b2
a0b633fb77 Translated using Weblate (Swedish)
Currently translated at 100.0% (290 of 290 strings)

Translation: Tomb/tomb
Translate-URL: https://hosted.weblate.org/projects/tomb/tomb/sv/
2023-12-07 02:33:37 +01:00
Sven Geuer
f9ec17d126 Fix some table mimicking lines to honor the recent line width 2023-09-30 22:55:28 +02:00
Sven Geuer
dcdf4cb3bb Fix operators to compare numerically, not lexicographically
The operator < compares two strings lexicographically resulting in that a 100MB tomb is considered smaller than 47MB or 18MB.

Closes #489
2023-09-30 22:54:35 +02:00
Jaromil
0c8d4cf477 short README, info moved to homepage 2023-09-20 16:52:12 +02:00
Jaromil
51452e4e6a documentation update for release
long due, was ready in november 2022 and basically left unchanged at
that stage, except the deprecation of veracrypt in the experimental
portable branch, which is not included in the stable release.
2023-09-18 16:04:19 +02:00
nerun
58035cf14a Added README to extras/translations. 2023-08-24 21:21:18 +02:00
nerun
06ea734878 Added Brazilian Portuguese (pt_BR) translation (for Tomb 2.10). Also
created tomb.pot template for version 2.10.
2023-08-24 21:21:18 +02:00
nerun
347462cb0c Updated Brazilian Portuguese translation (pt_BR). Also updated Makefile
to include this translation.
2023-08-24 21:21:18 +02:00
nerun
cd9fa46964 Translations: updated 'es', 'fr', 'it' and 'ru.po' to line up help message
(PR #481 and Jaromil request in PR #483).
2023-08-24 21:21:18 +02:00
nerun
d8989e1955 extras/kdf-keys: fixed hexencode warning caused by wrong type. 2023-08-24 21:11:42 +02:00
nerun
59d7331cb0 Fixed issue with mlocate. Now tomb accepts also plocate for search/index. 2023-06-27 22:47:10 +02:00
Dyne.org foundation
4d72a2180a Added translation using Weblate (Portuguese (Brazil)) 2023-06-27 16:53:44 +02:00
nerun
7249afe98e tomb help: "engrave" lined up (added +5 spaces). 2023-06-27 07:30:31 +02:00
nerun
0eed3cf321 doc: updated tomb_manpage.pdf with new version. 2023-06-27 07:30:31 +02:00
nerun
e316ef22cc manpage: fixed wrong information about --kdfmem. 2023-06-26 03:09:54 +02:00
nerun
82e5342334 extras/gtomb: inserted a "wait" statement in the dig, forge, lock and open functions;
spelling standardization (canceled to cancelled);
simplified the call to MONMORT icon inside zenity functions;
zenity warning function changes;
fixed missing dots.
2023-06-25 19:37:27 +02:00
nerun
4e3221e937 extras/gtomb: replaced soft with hard tabs to maintain code standard. 2023-06-25 19:37:27 +02:00
Narrat
364a457fbc Remove unused check on python2 2023-06-24 04:35:48 +02:00
Alex
571cdeafbf Translated using Weblate (French)
Currently translated at 98.6% (286 of 290 strings)

Translation: Tomb/tomb
Translate-URL: https://hosted.weblate.org/projects/tomb/tomb/fr/
2023-03-21 16:59:45 +01:00
Maxime Leroy
b9cf58054a Translated using Weblate (French)
Currently translated at 96.2% (279 of 290 strings)

Translation: Tomb/tomb
Translate-URL: https://hosted.weblate.org/projects/tomb/tomb/fr/
2023-03-21 16:59:45 +01:00
Selene ToyKeeper
646d2c33fd Use $SUDO_ASKPASS if defined. Allows non-terminal sudo password prompts.
The sudo program checks this env var and uses it, but only if --askpass
was given at the command line, or if it thinks there is no terminal.
But the terminal detection is unreliable, so give it --askpass if there
is an askpass program defined in the environment.

To try it, simply "export SUDO_ASKPASS=/usr/bin/ssh-askpass" before
running tomb.

For me personally, this makes it possible to have a hotkey to run
"pass" and "pass tomb" related commands.  Without this patch, invoking
via hotkey causes my window manager to lock up while waiting for a
password on the VT where Xorg was started... and since it's locked up,
I can't change to the VT to enter the data it's waiting for.  So I have
to log in via ssh from another host to recover it.

So, instead of locking up... now it can use a GUI askpass program.
2023-03-21 16:58:18 +01:00
nerun
713cfd3071 extras/gtomb: SWAPOFF defaults to false. 2023-03-21 16:54:57 +01:00
nerun
5eaa92e614 extras/gtomb again: sudo no longer stored in variable. 2023-03-21 16:54:57 +01:00
nerun
d65a3f1586 extras/gtomb severe update. 2023-03-21 16:54:57 +01:00
Jaromil
b5eae55c8b portable: remove strtosize and use MiB 2022-12-21 10:08:33 +01:00
Jaromil
0130fcf86d update docs 2022-11-21 09:44:21 +01:00
Jaromil
30d2e92a77 sponsor buttons 2022-11-16 15:25:42 +01:00
Jaromil
0d004f312d improve build status badges on readme 2022-11-16 15:25:42 +01:00
Jaromil
b14be5022e fix readme build status links 2022-11-16 15:25:42 +01:00
Jaromil
ed841b9f32 update CI scripts 2022-11-16 15:25:42 +01:00
Jaromil
6e8ef0a29a documentation update for a new release 2022-11-16 15:25:42 +01:00
Jaromil
5772d39e19 updated portable readme with links to milestones and more info 2022-11-14 10:40:15 +01:00
Jaromil
1ae5845c61 portable tomb tests in ci 2022-11-14 10:40:15 +01:00
Jaromil
fce89023a5 try to use native ubuntu matrix on github ci
remove doas tests

removed comparison from bind-hook test
2022-11-14 10:40:15 +01:00
Jaromil
be911b1e16 first basic implementation of portable tomb
this new "flavor" of tomb uses veracrypt for mounted volumes and
POSIX sh only for its scripting, is a work in progress and still
lacks full functionality, but provides a proof-of-concept to be
developed further if needs arise.
2022-11-13 22:00:41 +01:00
Chris Vogel
12684e6740 cmd lock: extend parameters to --filesystem
The --filesystem option can be used  to  specify
an  alternative  filesystem used to format the tomb, in place of the default "ext4".

Beside "btrfs" now the following parameters to --filesystem are supported:

"ext3"    using operating system defaults
"ext4"    using operating system defaults
"btrfs"   for tombs >= 47MB using operating system defaults
"btrfsmixedmode"    for tombs >=18MB btrfs mixed mode (see mkfs.btrfs(8))
"ext3maxinodes"     ext3 with a maximum of inodes (for many small files)
"ext4maxinodes"     ext4 with a maximum of inodes (for many small files)

These changes help use scenarios in which there is a great number of small files
and/or directories in a small filesystem, like e.g. the pass-tomb extension to pass.
2022-10-28 16:19:28 +02:00
Jaromil
42390b78c0 add a notice and link to tomb3 in readme 2022-10-24 15:28:05 +02:00
Valentin Heidelberger
3639b2e0ec fix typo Succesfully -> Successfully 2022-10-20 12:25:40 +02:00
Chris Vogel
8502bdc722 Adjusted messages returned about zramswap
As explained to me here https://github.com/dyne/Tomb/pull/447#discussion_r999777746
it would be bad to change existing messages. Reverted to the already existing messages
and added new messages about zramswap.
2022-10-20 12:22:23 +02:00
Chris Vogel
e463a6e600 fixed patch error
Something went wrong when I tried to move my changes from my installation
into the git. Now the tests complete successfully.
2022-10-20 12:22:23 +02:00
Chris Vogel
6af298e15f recognize zram as swap
Check if unencrypted swap is zram. If it is zram check whether a writeback to
disk is configured.

Unencrypted zramswap not written to disk is accepted.

ToDo (as for other unencrypted swap): check if the writeback happens on an
already encrypted disk/partition.
2022-10-20 12:22:23 +02:00
Jaromil
1655fd5a99 add tomb/.cleanexit lock to check on clean umount 2022-10-11 08:26:37 +02:00
Jens Rischbieth
8782dab9ed Update Dockerfile
Building the image fails as pinentry doesn't exist in the repository, only pinentry-curses. Image building works w/ only pinentry-curses.
2022-07-20 22:43:01 +02:00
AHOHNMYC
3b44a6be3a Translated using Weblate (Russian)
Currently translated at 99.3% (286 of 288 strings)

Translation: Tomb/tomb
Translate-URL: https://hosted.weblate.org/projects/tomb/tomb/ru/
2022-05-26 01:27:24 +02:00
Francisco Serrador
7a1b699cea Translated using Weblate (Spanish)
Currently translated at 100.0% (288 of 288 strings)

Translation: Tomb/tomb
Translate-URL: https://hosted.weblate.org/projects/tomb/tomb/es/
2022-05-26 01:27:24 +02:00
luzhen
85af9459aa Translated using Weblate (French)
Currently translated at 74.6% (215 of 288 strings)

Translation: Tomb/tomb
Translate-URL: https://hosted.weblate.org/projects/tomb/tomb/fr/
2022-05-26 01:27:24 +02:00
Jaromil
f4e2ae2f97 add win11 compatibility notice to readme 2022-04-15 11:07:40 +02:00
Jaromil
62806769af disable broken gpg recipient tests 2022-04-13 22:53:36 +02:00
Jaromil
8be3e7d2a0 gpg remove check for secret key 2022-04-13 22:53:36 +02:00
Jaromil
97343cf590 fix detection of gpg recipient id validity 2022-04-13 22:53:36 +02:00
Jaromil
60034b0b55 update test docker to devuan chimaera
install pinentry curses and gpg

update github action checkout v3
2022-04-13 22:53:36 +02:00
Narrat
b7822afaf0
lo_mount: check for loop support needs privilege escalation (#437)
If there is no free loop device, the call of loopsetup -f will create one and return it. For this it needs privilege escalation.
It doesn't need those, if there is already an used device, but that cannot be guaranteed.

Closes #436
2022-04-07 15:20:38 +02:00
Jaromil
6955719f04 fix shellcheck linter 2022-03-03 10:27:35 +01:00
Jaromil
8ceeca8769
KDF support for argon2 memory intensive algorithm (#432)
* KDF support for argon2 memory intensive algorithm

following many requests, here is support for argon2 KDF to be switched
on using --kdftype argon2 (--kdf iterations --kdfmem memory)

effective memory required is 2^memory KiB, defaults to 18 (262 MiB)
number of iterations are still specified as --kdf argument

requires the argon2 reference C implementation from P-H-C
also requires tomb-kdb-pbkdf2-gensalt in extras/kdf-keys

example usage:
tomb forge -k argon.key --kdf 10 --kdftype argon2

* manual updates for argon2
2022-02-20 22:05:01 +01:00
Jaromil
03c93ef976
Sudo loopback improve (#435)
* small improvements to loopback setup and --sudo

* support reading hostname from file

also tolerate not finding the hostname (fill localhost)

address #428

* cleanup and support sup,sud,pkexec
2022-02-20 21:57:05 +01:00
Jaromil
5a5eb6ddcf
wrap all references to $tombmount string into quotes (#434)
this may fix whitespace issues referred by #433 and previously related
to bind mounts as of #222
2022-02-20 21:10:47 +01:00
Jaromil
9323c1caf8 reduce noise of superuser password requests
messages downgraded to verbose mode (fix #431)

also updated dates and version
2022-01-31 04:44:10 +01:00
Weblate (bot)
f7046c5941
Translations update from Weblate (#401)
* Translated using Weblate (French)

Currently translated at 74.6% (215 of 288 strings)

Translation: Tomb/tomb
Translate-URL: https://hosted.weblate.org/projects/tomb/tomb/fr/

* Added translation using Weblate (Chinese (Simplified))

* Translated using Weblate (French)

Currently translated at 76.0% (219 of 288 strings)

Translation: Tomb/tomb
Translate-URL: https://hosted.weblate.org/projects/tomb/tomb/fr/

* Translated using Weblate (French)

Currently translated at 76.3% (220 of 288 strings)

Translation: Tomb/tomb
Translate-URL: https://hosted.weblate.org/projects/tomb/tomb/fr/

* Translated using Weblate (French)

Currently translated at 78.4% (226 of 288 strings)

Translation: Tomb/tomb
Translate-URL: https://hosted.weblate.org/projects/tomb/tomb/fr/

* Translated using Weblate (French)

Currently translated at 95.4% (275 of 288 strings)

Translation: Tomb/tomb
Translate-URL: https://hosted.weblate.org/projects/tomb/tomb/fr/

Co-authored-by: luzhen <luzhen@uniontech.com>
Co-authored-by: Dyne.org foundation <translate@dyne.org>
Co-authored-by: Maxime Leroy <lisacintosh@gmail.com>
2022-01-30 16:21:18 +01:00
Artur Malimonov
b6ffe1a2f1
GitHub Actions CI (#430)
Add Github Actions CI config
2022-01-08 01:26:51 +01:00
Jaromil
84ef4bef4b remove pkexec autodetection 2021-10-21 11:49:54 +02:00
Jaromil
e0ba8c5f4d wider support for privilege escalation tools
now supporting also pkexec (polkit daemon), suckless' sup and
sud.dyne.org

pkexec is autodetected when polkit is running

manpage documents the --sudo flag which overrides any autodetection
2021-10-20 16:27:27 +02:00
Jaromil
90eec3d830 adopt external utility cat instead of zsh builtin
fix #426
2021-10-13 15:46:34 +02:00
Damien Ready
930b414889 Correct some typos 2021-10-01 18:32:44 +02:00
Jaromil
61a9d1a420 updates to dockerfile 2021-08-10 10:57:43 +02:00
timvisee
d2d35bc8db Quiet cryptsetup when opening a Tomb with -q provided 2021-07-21 09:06:22 +02:00
timvisee
585af6a61b Quiet fsck when opening a Tomb with -q provided 2021-07-21 09:06:22 +02:00
Jaromil
2a744fe89d quick install instructions for qt tray
fix #413
2021-06-24 07:42:29 +02:00
Jaromil
b235f16ce4 correct shell sequence to open without tomb script
fix #372 thanks to @grcancelliere
2021-06-24 07:36:50 +02:00
heat-wave
ce521ed2e2 Validate user-supplied sudo alternative (in name only) 2021-04-15 12:27:26 +02:00
heat-wave
087ecd25a2 Restrict access to doas.conf 2021-04-15 12:27:26 +02:00
heat-wave
24a89b680d Disable sphinx tests to test loop devices limit hypothesis 2021-04-15 12:27:26 +02:00
heat-wave
3860487a0b Fix typos in doas config and --sudo opt definition 2021-04-15 12:27:26 +02:00
heat-wave
61386ca646 Support for sudo alternatives such as doas 2021-04-15 12:27:26 +02:00
Denis Roio
ae21619d04
Merge pull request #408 from heat-wave/fix/sphinx-test-configs
Fix configs and dockerfile to enable sphinx in tests
2021-01-31 00:10:22 +01:00
Jaromil
7f2e22c517 fix read-only opening of tombs using -o ro
skip touch, chown and some minor operations when read-only
2021-01-25 18:26:49 +01:00
Jaromil
fb3ffcec03 manpage mention of fallocate(1) for faster dig 2021-01-25 15:14:31 +01:00
heat-wave
815b8f4218 Fix configs and dockerfile to enable sphinx in tests 2021-01-23 19:16:29 +00:00
Jaromil
f35ad11e3f updated documentation for release 2021-01-04 22:00:29 +01:00
Denis Roio
c0d1a7584d
Merge pull request #406 from mcrapet/dig_sudo
dig/forge unecessary sudo
2021-01-04 10:51:40 +01:00
Matthieu Crapet
c5701793fb minor typo/formatting fixes
Signed-off-by: Matthieu Crapet <mcrapet@gmail.com>
2021-01-02 10:22:06 +01:00
Matthieu Crapet
02812f4c06 tomb forge: useless sudo and chown
Depending script invokation, behavior is not exactly similar.
Assuming that if SUDO_USER is set, the _sudo invokation can be dropped (EUID=0).
In the other case, user has created file, owner is already good, don't call chown.

Preparation:
$ tomb dig foo.tomb -s 10

Method 1:
$ sudo tomb forge foo.tomb.key -v

Method 2:
$ tomb forge foo.tomb.key -v
... ask user password to gain superuser privileges
...
Sorry, user <username> is not allowed to execute '/bin/chown <uid>:<gid> foo.tomb.key' as root on <hostname>.

Signed-off-by: Matthieu Crapet <mcrapet@gmail.com>
2021-01-02 10:20:26 +01:00
Matthieu Crapet
99f10bf215 tomb dig: useless sudo and chown
Depending script invokation, behavior is not exactly similar.
Assuming that if SUDO_USER is set, the _sudo invokation can be dropped (EUID=0).
In the other case, user has created file, owner is already good, don't call chown.

Method 1:
$ sudo tomb dig foo.tomb -s 10 -v

Method 2:
$ tomb dig foo.tomb -s 10 -v
... ask user password to gain superuser privileges
...
Sorry, user <username> is not allowed to execute '/bin/chown <uid>:<gid> foo.tomb' as root on <hostname>.

Signed-off-by: Matthieu Crapet <mcrapet@gmail.com>
2021-01-02 10:12:55 +01:00
Matthieu Crapet
859a5c7783 TMPPREFIX is not supposed to be a directory
http://zsh.sourceforge.net/Doc/Release/Files.html
TMPPREFIX defaults to /tmp/zsh (for zsh shell)

Note: --tmp command line switch is not documented?

Signed-off-by: Matthieu Crapet <mcrapet@gmail.com>
2021-01-02 10:09:51 +01:00
Matthieu Crapet
312915b4b3 fix potential wrong _USER value
"id -u" gives then uid not a the name.
https://man7.org/linux/man-pages/man1/id.1.html

Signed-off-by: Matthieu Crapet <mcrapet@gmail.com>
2021-01-02 10:09:51 +01:00
Jaromil
d227695778 add support for tombs formatted with the btrfs filesystem
basic functionality working for open, close and resize

still needs test coverage and some minor checks
2020-12-29 13:50:04 +01:00
Jaromil
8d5a85658f explicit return codes for all operations 2020-12-29 11:52:23 +01:00
Jaromil
0ac5a34c20 close luks mapper and end with an error on lock format failures 2020-12-29 10:56:20 +01:00
Jaromil
d8360688b3 fix wrong comparison of $pass_asked left by last commits
fix #404
2020-12-29 10:52:16 +01:00
Jaromil
7a81ad032d add zsh to version output 2020-12-29 08:58:14 +01:00
Denis Roio
9f30f7da89
Merge pull request #403 from catleeball/cflags
Read CFLAGS in kdf-keys makefile
2020-12-29 08:07:09 +01:00
🎶🎷🐛 Lee Ball
6d87b7e355 Read CFLAGS in kdf-keys makefile
Added $(CFLAGS) in the kdf-keys makefile to allow users to specify
additional build flags.
2020-12-23 22:03:49 -08:00
Jaromil
b6fff10c2a add file among dependencies in the INSTALL guide
fix #396
2020-12-16 12:52:17 +01:00
Jaromil
b0de6e07b2 adopt everywhere -z test to check when variables are empty
check works both for empty ("") and non-existing vars and is a fix
for regression #398 to work on older Zsh versions. It is normalized
through all tomb's code.
2020-12-15 18:22:38 +01:00
Jaromil
5199bef4a6 documentation for release 2020-11-27 18:40:17 +01:00
Jaromil
45b144d213 print messages by default on stderr 2020-11-25 14:47:54 +01:00
Jaromil
c13f38266e fix tomb list output 2020-11-25 14:47:43 +01:00
Jaromil
285f3c3a07 docker tomb wrapper included in extras 2020-11-24 19:48:07 +01:00
Jaromil
0a968b80b7 fix losetup permission
leftover bug in #391
2020-11-23 21:56:25 +01:00
Denis Roio
694390bd4a
Merge pull request #394 from Narrat/fix/393
Fix typo calling pinentry_assuan_getpass
2020-11-23 11:14:15 +01:00
Narrat
da590fb50e Fix typo calling pinentry_assuan_getpass
Affected the use of Tomb without DISPLAY and pinentry-curses.
Fixes #393
2020-11-21 22:33:30 +01:00
Jaromil
c9f3b07cd8 documentation updates for release 2020-11-17 13:58:43 +01:00
Denis Roio
f9d9d4bc8b
Merge pull request #391 from dyne/loopmount_refactor
refactor of state tracking for loop mounting
2020-11-17 10:21:43 +01:00
Jaromil
c3a354cc0f fixes and cleanups 2020-11-17 08:19:36 +01:00
Jaromil
3fb248bde8 refactor of state tracking for loop mounting
simplified function calls for tracking of loop mount by using global
variables whose scope is limited to execution, most computation is now
included in the `is_valid_tomb` function.
2020-11-16 23:35:03 +01:00
Jaromil
940563d02c resize now checks for correct password before operating
fixes bug mentioned in issue #333 that made tomb append space to a
tomb file before checking for correct password, leading to file
corruption in case the wrong password is inserted 3 times.
2020-11-16 13:28:37 +01:00
Denis Roio
91debdbf58
Merge pull request #390 from dyne/pinentry_display_updates
updated pinentry invokation to include wayland
2020-11-16 13:27:04 +01:00
Denis Roio
c7b1f00370
Merge pull request #389 from dyne/check_in_use
improve the check if a tomb file is in use
2020-11-16 13:26:42 +01:00
Jaromil
bc94559ac4 updated pinentry invokation to include wayland
also changes to priority order of invokation and some code cleanups and
indentations. Invokation order is now:

- WAYLAND? pinentry-gnome3
- X11?
	1. pinentry-x11 (distro specific wrapper)
	2. pinentry-gtk2 (legacy, removable)
	3. pinentry-gnome3
	4. pinentry-qt5
	5. pinentry-qt4
- NO DISPLAY? pinentry-curses
2020-11-16 10:24:37 +01:00
Denis Roio
b20ef50563
Merge pull request #382 from weblate/weblate-tomb-tomb
Translations update from Weblate
2020-11-16 10:00:52 +01:00
Jaromil
72da5b481e add .loop extension to /dev/mapper volume to identify its nature 2020-11-16 09:56:38 +01:00
Jaromil
9be5dff823 use realpath to always use absolute paths of tomb files in maphash 2020-11-16 09:54:37 +01:00
Jaromil
59d3810665 improve the check if a tomb file is in use
Change the mapper path using a hash of the tomb file path,
making it unique and reproducible to check if tomb is in use.
Check happens inside the new render_mapper() function which is
executed right after the key file opening.
2020-11-16 09:54:37 +01:00
mv87
d135730386 Translated using Weblate (German)
Currently translated at 100.0% (290 of 290 strings)

Translation: Tomb/tomb
Translate-URL: https://hosted.weblate.org/projects/tomb/tomb/de/
2020-11-14 11:28:54 +01:00
Jeannette L
010674f30b Translated using Weblate (French)
Currently translated at 73.7% (214 of 290 strings)

Translation: Tomb/tomb
Translate-URL: https://hosted.weblate.org/projects/tomb/tomb/fr/
2020-11-14 11:28:54 +01:00
Milo Ivir
193cd42bbd Translated using Weblate (German)
Currently translated at 95.5% (277 of 290 strings)

Translation: Tomb/tomb
Translate-URL: https://hosted.weblate.org/projects/tomb/tomb/de/
2020-11-14 11:28:54 +01:00
Mattias Münster
a1f2364eb8 Translated using Weblate (Swedish)
Currently translated at 95.2% (276 of 290 strings)

Translation: Tomb/tomb
Translate-URL: https://hosted.weblate.org/projects/tomb/tomb/sv/
2020-11-14 11:28:54 +01:00
Jaromil
763dbdb356 document known bug for password with DISPLAY and pinentry-curses
detailed in issue #385
2020-11-13 23:54:41 +01:00
Jaromil
5b80abd96d deactivate test for libsphinx
seems to break in libsphinx build when calling `pkgconf`
2020-11-13 23:36:29 +01:00
Jaromil
136ba6e053 honor custom settings for GNUPGHOME
fix #371
2020-11-13 23:16:36 +01:00
Denis Roio
9ddfdc426f
Merge pull request #386 from aaronjanse/security-pinentry-curses
Use _verbose for pinentry-curses in ask_password
2020-11-11 14:41:09 +01:00
Denis Roio
298eedfedf
Merge pull request #384 from giacomoferretti/patch-1
Fix table syntax Markdown
2020-11-11 14:40:53 +01:00
Aaron Janse
15c894dfb4 use _verbose for pinentry-curses in ask_password 2020-11-09 16:43:06 -08:00
Giacomo Ferretti
d3995b82db
Fix table syntax 2020-10-06 16:26:34 +02:00
Jaromil
fb154bbb2f fix test docker for beowulf stable 2020-06-22 10:48:49 +02:00
Denis Roio
f14ba758ee
Merge pull request #378 from quickcougar/pinentry-override
Allow pinentry to fail, so long as the password comes through.
2020-06-22 10:05:31 +02:00
Denis Roio
85f02f746e
Merge pull request #376 from quickcougar/debug-messages
Write all debug messages to stderr to avoid polluting stdout.
2020-06-22 10:01:10 +02:00
Mark Mykkanen
2f29e6709c Allow pinentry to fail, so long as the password comes through. 2020-03-23 17:07:46 -05:00
Mark Mykkanen
7cead041ac Write all debug messages to stderr to avoid polluting stdout. 2020-03-22 14:02:12 -05:00
Jaromil
4ec85ea3ca
Merge pull request #373 from dcommisso/fix_manpage
Fix manpage
2020-02-05 19:56:14 +01:00
Domenico Commisso
7da034c146 Fix Firefox example in linux manpage to avoid Firefox error at startup 2020-02-04 15:27:25 +01:00
Domenico Commisso
ac13adaa28 Update exec-hooks name in linux manpage 2020-02-04 15:07:08 +01:00
Jaromil
e9fd1a19e1 documentation fix for default xts cipher string 2019-11-18 10:17:38 +01:00
Jaromil
377e335e93 remove warning on open by fixing order of chown
fix #369 (completes #324)
2019-11-18 10:15:12 +01:00
Jaromil
bec53aeb72 documentation update for new release 2019-10-11 22:06:55 +02:00
Jaromil
2d8a72ed95
Merge pull request #367 from edoffagne/doc
Fixed documentation
2019-10-03 09:37:36 +02:00
Erik Doffagne
7e82beaca6 Fixed documentation 2019-10-03 06:31:45 +02:00
Jaromil
15d279605b fix getent parsing when fields are emtpy
fix #365
2019-09-14 12:06:13 +02:00
Jaromil
ca708b4d46 Documentation to build gtk-tray
fix #351
2019-08-22 17:43:36 +02:00
Jaromil
d2cd41e1e7
Merge pull request #362 from dyne/getent-everywhere
use getent with wrappers every /etc/passwd query
2019-08-22 17:20:11 +02:00
Jaromil
382070481f use getent with wrappers every /etc/passwd query 2019-08-22 17:07:12 +02:00
Jaromil
01f5412fc5
Merge pull request #361 from dyne/normalize-conditionals
rewrite some conditionals for less ambiguity
2019-08-22 15:38:54 +02:00
Jaromil
1574723502 rewrite some conditionals for less ambiguity
this restyles some code introduced by the sphinx feature and tries to fix parse
errors reported in issue #357
2019-08-22 14:37:47 +02:00
Jaromil
71a7241f9c remove unneeded umount on forge_key failure 2019-08-22 11:49:43 +02:00
Jaromil
94d8ad497f Fix inconsistent cleanup on forge_key failure
Address concern raised in #360
2019-08-22 11:47:58 +02:00
Jaromil
187a627022 Fix tomb-kdb-pbkdf2 helper for multiplatform build
Avoid EOF being missed when reading in the password. Replace undefined
char-to-int comparison with a int-to-int one. Fix #360
2019-08-22 11:47:58 +02:00
Jaromil
6f2ce59d29
Merge pull request #356 from dyne/gettext_optional
make gettext optional in _sudo()
2019-07-08 07:19:36 +02:00
Jaromil
0b9080e0ca make gettext optional in _sudo()
address #355
2019-07-05 08:10:00 +02:00
Jaromil
d83e39f1ec fix spelling errors in manpage 2019-07-05 08:04:06 +02:00
Jaromil
1a93481480
Merge pull request #353 from roddhjav/master
Ensure the gpg key is trusted.
2019-06-28 09:12:28 +02:00
Alexandre Pujol
6b4bd69dbc
Ensure GPG key is trusted. See #340 2019-06-27 19:58:58 +01:00
Alexandre Pujol
0e9fe51b50
Add untrusted key in the test keyring and convert to v2 keyring. 2019-06-27 19:54:46 +01:00
Jaromil
06039a9e47 fix warnings on first tomb open
fix issue #324
2019-06-26 08:56:05 +02:00
Jaromil
e45c005c88 documentation updates for release
known bugs, changelog and manual for sphinx
2019-05-30 18:48:30 +02:00
Jaromil
0af46fe3c1 move dockerfile for tests into extras/tests 2019-05-22 10:59:36 +02:00
Jaromil
bd3e3c7056
Merge pull request #350 from dyne/urandom-switch
switch default random source to /dev/urandom
2019-05-22 10:13:07 +02:00
Jaromil
ace80c87db switch default random source to /dev/urandom
there is no need to default to a blocking source of random,
since /dev/random doesn't improves the quality of randomness in Linux
2019-05-22 09:55:02 +02:00
Jaromil
2b8eec6ba5 improve tomb umount checks avoiding grep call
consolidate use of `list_tomb_mounts` also inside umount_tomb
this should fix #315
2019-05-22 01:03:00 +02:00
Jaromil
7b06be5d43 before opening check if mountpoint is already used by a tomb
a simple check introduced on `tomb open` to list all tombs mounted and
control if any mountpoint is already in use, in case refuse opening.

fix #326
2019-05-22 00:30:48 +02:00
Jaromil
95f2f68654 Revert temp file mechanism for sphinx
for some reason to be investigated further using tomb's temp
file mechanism breaks the test suite of sphinx support

This reverts commit a6d252c949.
2019-05-18 09:51:55 +02:00
Jaromil
b34584b61c added new contributor to credits 2019-05-18 09:51:12 +02:00
Jaromil
91e607efb5 improve gpg key listing command 2019-05-18 09:50:57 +02:00
Jaromil
ec31d2d280 improved gpg recipient output warning
dropped the dependency from grep, head, cut, sed (only using awk)
added human readeable GPG ID besides fingerprint on recipient check
2019-05-05 11:29:25 +02:00
Jaromil
eec64f4f65
Merge pull request #348 from AlexisDanizan/master
Bug fix #328
2019-05-05 10:39:16 +02:00
Jaromil
a6d252c949 correct use of tomb's internal temp file mechanism for sphinx 2019-05-05 10:21:38 +02:00
Jaromil
5ce9960207
Merge pull request #344 from heat-wave/feature/pitchforked-sphinx-integration
Pitchforked sphinx integration
2019-05-05 09:58:39 +02:00
alexisdanizan
f1f23c417b Bug fix #328 2019-05-01 22:43:04 +02:00
heat-wave
b054a83ee5 Pitchforked sphinx integration for remote password storage 2019-04-30 16:05:03 +01:00
Jaromil
2dfb744a34
Merge pull request #347 from stevesbrain/master
Update suggested command with missing flag
2019-04-18 16:01:46 +02:00
Steve Divskinsy
522ad3abad
Update suggested command with missing flag
`tomb lock` suggested command was missing "-k" flag
2019-04-18 20:54:30 +09:30
Jaromil
a0c74985ca documentation updates on the new cloakify feature
also updated the list of code contributors
2019-02-22 09:50:04 +01:00
Jaromil
59408b373f
Merge pull request #342 from heat-wave/feature/cloakify-integration
Cloakify integration
2019-02-22 09:40:46 +01:00
heat-wave
f2eb1fd242 Expanded steganographic functionality with cloakify integration 2019-02-21 09:28:21 +00:00
Jaromil
9299f3eecf mention need of using trusted gpg keys in manpage
documentation to address issue #340
2019-02-20 20:37:28 +01:00
Jaromil
12767764fb
Merge pull request #336 from jcrd/usage
Improve usage message
2018-12-06 10:45:39 +01:00
James Reed
46286047f6
Align usage message lines (fix #335)
Print newlines only when necessary

Correct coma to comma in usage message
2018-11-26 10:48:19 -07:00
Jaromil
1b535f920b
Merge pull request #331 from jcrd/msg
Overhaul message printing
2018-11-25 15:12:36 +08:00
James Reed
477ab20443
Overhaul message printing 2018-11-24 15:48:25 -07:00
Jaromil
a14a39ae12
Merge pull request #332 from jcrd/kdf-header
Check for KDF header in is_valid_key
2018-11-04 16:01:02 +01:00
James Reed
1ba3c55241
Check for KDF header in is_valid_key 2018-11-03 15:15:48 -06:00
Jaromil
629841c0e7
Merge pull request #320 from roddhjav/stat
Track access/modification time of sensitive files
2018-09-24 18:09:10 +02:00
Alexandre Pujol
2f3826d88d
Fix modification time restoration. 2018-09-24 12:58:51 +01:00
Alexandre Pujol
aaa4637ed0
Track access/modification time of sensitive files
Collects the stats of tomb keys and tomb files then restore them when
Tomb exits. Can be extended to any file opened by Tomb. See #266
2018-09-23 22:21:05 +01:00
Jaromil
cdd3c5804a updated documentation on KDF whitespace bug
fix #307
2018-09-23 12:05:02 +02:00
Jaromil
e8919af867
Merge pull request #308 from AitorATuin/kdf_input
Fixes a problem reading passwords when using the kdf wrapper
2018-09-23 11:04:10 +01:00
Jaromil
27fd026cd5 documentation updates
also fix #319
2018-09-23 11:41:08 +02:00
Jaromil
a27f15aada
Merge pull request #318 from roddhjav/master
Add test coverage for GPG subkeys support.
2018-08-20 13:03:50 +02:00
Alexandre Pujol
c293aa7261
Add regression test for tomb 2.4 2018-08-19 23:08:48 +01:00
Alexandre Pujol
24fee7a076
Add test coverage for GPG subkeys support. See #317 2018-08-19 22:53:21 +01:00
Jaromil
7c8067fef3
Merge pull request #317 from bjonnh/patch-1
Add support for multiple GPG subkeys
2018-08-19 23:25:03 +02:00
bjonnh
18b7541a98
subkey_id for display must be out of _fingerprint
I did a typo, the ($gpg_id) should have been out of the _fingerprint call.
2018-08-18 17:41:37 -05:00
bjonnh
b49a36a07b
Add support for multiple keys especially subkeys
Correcting issue #316
2018-08-17 14:03:33 -05:00
Jaromil
8d698ad46a
Merge pull request #309 from Ganondolf/gtk-tray-path
Fix mounted volume default path
2018-02-18 11:10:22 +01:00
Ganondolf
f290904f71
Fix mounted volume default path 2018-02-17 16:23:26 +01:00
Jaromil
68a9589925 set ownership after dig and forge 2018-02-11 21:41:58 +01:00
ATuinDev
3cb8ebefd3
Don't impose any limitation in password size
Remove the hardcoded buffer with dynamic memory to get password input
(uses now calloc / realloc)
2018-02-04 20:53:35 +01:00
ATuinDev
35cf8572bf
Rename variable j to something more descriptive
Remove `+ 1` in conditions, easier to understand using the comparators
2018-02-03 23:24:20 +01:00
ATuinDev
a66224d549
Run extras/kdf-keys tests in travis 2018-02-03 21:23:07 +01:00
ATuinDev
510c8f6430
Add check for password lens 2018-02-03 21:05:56 +01:00
ATuinDev
5c419b3117
Add explicit comment before reading the password 2018-02-03 19:20:01 +01:00
ATuinDev
257e5ee99a
whitespaces are now part of the password
NULL character can also be part of the password. There was a failing
test that was checking exactly that (now all the tests pass).
2018-02-03 19:16:20 +01:00
ATuinDev
bce58cae3e
Add test testing that whitespaces are used by kdf wrapper
Adds a new test that assures that spaces are part of the passwords
2018-02-03 19:12:05 +01:00
Jaromil
3440a32839
Merge pull request #305 from dyne/assuan_deduplication
put assuan commands to pinentry into a single function
2018-02-02 16:47:28 +01:00
Jaromil
f5ceddc0b7 put assuan commands to pinentry into a single function 2018-01-29 09:47:42 +01:00
Jaromil
f4913e0744
Merge pull request #303 from sargo-devel/master
Added pinentry-qt5 support
2018-01-29 09:38:47 +01:00
SargoDevel
66ade86441 Added pinentry-qt5 support 2018-01-28 21:49:21 +01:00
Jaromil
d0805084a2 added doc to avoid logging of invokations in syslog
see issue #302 and thanks to @stevesbrain
2018-01-23 09:38:52 +01:00
Jaromil
f42a4c2a0b
Merge pull request #301 from Narrat/small_things
Small things
2018-01-09 10:42:40 +01:00
Narrat
08ca0a8eef list_gnupg_ciphers: Remove check on gpg
This function is called after _ensure_dependencies(), which would bail out if gpg is not found
2018-01-07 21:56:19 +01:00
Narrat
5e3b0dec84 list_gnupg_ciphers: be language agnostic
The old awk implementation always worked on lines beginning with 'Ciphers:' until it found 'Hash:'.
This fails for locales where a respective gnupg2.mo entry exists (Example: Ciphers in german is translated as Verschlü.:).
This is replaced by pointing awk on a specific line, which is for gpg1 and gpg2 the same. Work is done until awk stumbles up on a line which marks a new section (marked by keyword and :)

This closes #299
2018-01-07 21:40:32 +01:00
Narrat
e15c58dfd7 list_gnupg_ciphers: Pipe everything into /dev/null
Firstly the printed binary path is in the wrong place. Reading the text, one assumes Ciphers coming next.
Secondly it doesn't make sense to check there for a missing gnupg installation. Before calling list_gnupg_ciphers(), there is a direct call for gpg --version. If that fails the whole text is scrambled and no error reported

Dropping the output from which allows to remove the space from printing the ciphers. The text is correctly aligned now
2018-01-07 21:03:21 +01:00
Narrat
61fdab85be Show only version of pinentry
pinentry --version invocation includes License information.
As the same applies for gpg, and the information is not displayed there, we should the same with pinentry.
And tomb doesn't deal with the gpg sourcecode in any way.

This closes #300
2018-01-07 20:03:04 +01:00
Jaromil
74689ea484 tagging the release 2018-01-03 20:34:18 +01:00
Jaromil
7951645db5 switch code indentation to hard tabs
also update code guidelines.
2018-01-03 20:27:14 +01:00
Jaromil
70abf31bab documentation updates for a new release 2018-01-03 19:53:35 +01:00
Jaromil
ae78659efb
Merge pull request #297 from dyne/restore-chown
restored change of ownership on tomb's contents
2018-01-03 19:42:52 +01:00
Jaromil
ab3044c6f5 restored change of ownership on tomb's contents
this reverts commit 843b7fdfc4
and refers to various issues, among them #268

on the long term its easy to realise how this is a usability feature for most
users, so we just provide a new '-p' flag to preserve ownership on open.
2018-01-03 18:34:50 +01:00
Jaromil
214ec8ecbd
Merge pull request #298 from roddhjav/fix-version-check
Fixes: version check & tests
2018-01-03 16:36:16 +01:00
Alexandre Pujol
cec0c01b3e
Test: remove useless cleanup 2018-01-03 14:50:00 +00:00
Alexandre Pujol
c7dc379c0d
Tests: support non-standard zsh location. See #283 2018-01-03 14:49:08 +00:00
Alexandre Pujol
b20daeea6f
Fix: use is-at-least function to check program version 2018-01-03 14:46:23 +00:00
Jaromil
bc963cd1ae fix for correct execution of exec-hooks
this was broken in the latest release and fixes #271
2018-01-03 14:37:15 +01:00
Jaromil
3b1759b2b3 fix for hidden files without an extension as tombs
fix #147 introducing an extra check on TOMBNAME that, if returned
empty by the first transormation that removes the last .extension,
then is filled with the full TOMBFILE name without any transformation
2018-01-03 13:43:05 +01:00
Jaromil
c8616787f7 added a comment in manpage about resize fail and restore
documentation according to #269
2018-01-03 11:14:21 +01:00
Jaromil
31a78de23f removed known_bugs entry according to #276
This was confusing. Later found time to verify facts. We use 512 bytes
long keys (4096 bits) so AES-256 was always used in XTS mode.
2018-01-03 11:09:33 +01:00
Jaromil
60b72ad91f documentation and version updates
findmnt version shown
2018-01-03 10:44:27 +01:00
Jaromil
cf93551efa
Merge pull request #296 from dyne/use-findmnt
Replace 'mount -l' with 'findmnt'
2018-01-03 10:38:48 +01:00
Jaromil
9b1d1891cc
Merge pull request #290 from parazyd/use-shred-instead-wipe-patch
use shred instead of wipe
2018-01-03 10:03:50 +01:00
Jaromil
b72d67618b restore square parens detection in list_tomb_mounts() 2018-01-02 15:35:32 +01:00
Jaromil
3f107f9d31 small fix for usage of findmnt --raw flag 2018-01-02 14:04:53 +01:00
Jaromil
3593721967 drop zsh/regex module in favor of =~
As debated in issue #282 Zsh introduced a bug in v5.3.1 which briefly affected
our mechanism for closing tombs. The bug is fixed, but while investigating the
issue @aude realised there can be a better way to apply this regex for the
detection of mounted volumes on distro dependent /run/media/$USER paths.

This change drops usage of the regex optional module in Zsh to use the built-in
=~ comparison and improves the match using round parenthesis. It may fix the
close command on some distributions.
2018-01-02 14:04:53 +01:00
Victor Calvert
09ff889c1d Replace mount with findmnt
this fixes a mount-related functionality (finding the volume label) in new
versions of util-linux, that since v2.30 does not list anymore volume labels
with its mount -l command. Since findmnt needs sudo to list labels, this also
introduces the need for sudo in more commands: is_valid_tomb(), list, index and
search. The issue was examined in PR #283 and this is a rebase of it.
2018-01-02 13:39:35 +01:00
Jaromil
742b097192
Merge pull request #294 from dyne/regex-match
drop zsh/regex module in favor of =~
2018-01-02 13:23:23 +01:00
Jaromil
a7190eb2e2 drop zsh/regex module in favor of =~
As debated in issue #282 Zsh introduced a bug in v5.3.1 which briefly affected
our mechanism for closing tombs. The bug is fixed, but while investigating the
issue @aude realised there can be a better way to apply this regex for the
detection of mounted volumes on distro dependent /run/media/$USER paths.

This change drops usage of the regex optional module in Zsh to use the built-in
=~ comparison and improves the match using round parenthesis. It may fix the
close command on some distributions.
2018-01-02 12:35:11 +01:00
Jaromil
e083ce7a23
Merge pull request #291 from dyne/slam-refactor
Slam refactor
2017-12-14 17:48:47 +01:00
Jaromil
c537676e92 wrap the cats 2017-12-12 11:29:53 +01:00
Jaromil
f6457090af documentation of the new 'ps' command 2017-12-11 17:55:02 +01:00
Jaromil
121a64bc80 improvements to the new ps and slam implementations 2017-12-11 17:55:02 +01:00
Jaromil
8e33be0472 new slam and list_processes 2017-12-11 17:55:02 +01:00
Jaromil
85ac179cc8 cleanup of umount_tomb and removed slam from inside 2017-12-11 17:55:02 +01:00
Jaromil
21347f3657 square parens detection in list_mounted_tombs 2017-12-11 17:53:38 +01:00
Jaromil
6182a56fb6 fix bug in message rendering: stop overriding $i if existing
this small bug caused rewriting the $i variable (often used as
iterator in loops) whenever a log message was produced. Fix also
includes an acceleration of log printing by substituting seq with
native zsh notation.
2017-12-11 17:53:17 +01:00
parazyd
c8be1c22dd
use shred instead of wipe 2017-12-07 11:36:59 +01:00
Jaromil
6696f0c758 Merge pull request #285 from avindra/patch-2
changelog: fix minor typo
2017-10-19 09:59:04 +02:00
Avindra Goolcharan
fa1049e8f0 changelog: fix minor typo
just missing a space here
2017-10-18 02:02:37 -04:00
Jaromil
aba4f4c3b8 Merge pull request #275 from gador/master
Double check after umounting bind directories
2017-08-08 09:55:09 +02:00
Florian Brandes
8d2cb2d852
Double check after umounting bind directories
Checking if a tomb is still present in the
output of mount after umounting binded directories.
Addresses issue #265
2017-07-23 10:28:06 +02:00
Jaromil
64daf70229 remove 'lint' target from travis CI builds
the shellcheck developer unilaterally decided removal of zsh support
https://github.com/koalaman/shellcheck/issues/298 so far ignoring all
requests to add support, so we won't run our shellcheck tests on
travis. using Devuan Jessie's 0.3.5 with our ignores still works, so
will keep testing it locally.
2017-07-21 11:45:40 +02:00
Ivan J
6926583d79 Merge pull request #270 from mesbahamin/grammatical-corrections
Fix 3 grammatical errors.
2017-06-24 21:43:26 +02:00
Amin Mesbah
04ec0189a6 Fix 3 grammatical errors. 2017-06-24 12:24:50 -07:00
Jaromil
fb5eee002b Merge pull request #267 from dyne/exec-hooks
Exec hooks
2017-06-13 09:30:14 +02:00
Jaromil
127a8ed7ee exit code check on close
also removed pre-open and post-close as they don't really make sense
since all hooks are contained inside the Tomb. The post-close may be
implemented using a temp file, if a use case turns up for it.
2017-06-06 16:30:48 +02:00
Jaromil
056d0174f4 refactoring of exec-hooks
Renamed file from "post-hooks" to more appropriate "exec-hooks".
Implemented and documented a more consistent call system made of 4
different stages: pre-open, post-open, pre-close, post-close.
Addresses issue #265
2017-06-06 12:45:29 +02:00
Jaromil
5f71b486df Merge pull request #261 from Narrat/missing_lsof_stuff_2
Missing lsof stuff
2017-05-14 09:47:28 +02:00
Narrat
67004392fa Add testcase for slam operation
Thanks to @roddhjav for the intel and the test itself.

Closes #258
2017-05-12 20:24:49 +02:00
Narrat
c658d33187 --version includes lsof as optional dep 2017-05-10 21:50:48 +02:00
Jaromil
9fa55d1165 Merge pull request #260 from roddhjav/sharness
Replace the test suite by the sharness test suite
2017-05-10 14:58:49 +02:00
Alexandre Pujol
99855746b3
Travis: install KDF dependencies & use apt addons 2017-05-04 20:57:14 +01:00
Alexandre Pujol
593747d619
Add bind hooks tests and minors test setup changes 2017-05-03 19:51:36 +01:00
Alexandre Pujol
4688820b68
Tests: support for /media and /usr/media 2017-05-01 23:26:17 +01:00
Alexandre Pujol
92a670efd8
Add integrity tests 2017-05-01 23:02:49 +01:00
Alexandre Pujol
045c55436f
Regression tests: add old tomb version, support for zsh 5.3 2017-05-01 22:48:28 +01:00
Alexandre Pujol
8aaa8e1725
Add setkey tests 2017-05-01 21:22:36 +01:00
Alexandre Pujol
408977a351
Add steganography tests 2017-05-01 21:22:20 +01:00
Alexandre Pujol
a609b855c7
Add tests for tomb resize, passwd and engrave 2017-05-01 21:21:25 +01:00
Alexandre Pujol
36f5ed8729
Add regression tests 2017-05-01 21:19:48 +01:00
Alexandre Pujol
eb57899162
Add KDF related tests 2017-05-01 21:19:19 +01:00
Alexandre Pujol
166bbdc18c
Add main tests for GPG key support 2017-05-01 21:18:59 +01:00
Alexandre Pujol
e99a0350fb
Add base tests 2017-05-01 21:11:28 +01:00
Alexandre Pujol
bdebb6af32
Add tests setup:
- Tomb & auxiliary programs config
- GPG config
- Temporary test files config
- Helper function definitions
2017-05-01 21:08:08 +01:00
Alexandre Pujol
e558ad9f13
Update files for Sharness support
- extras/test/Makefile
- extras/test/.gitignore

Add test aggregate script
2017-05-01 21:06:02 +01:00
Alexandre Pujol
1a2fd5901f
Add sharness v1.0.0 - https://github.com/chriscool/sharness 2017-05-01 21:01:16 +01:00
Jaromil
c858def9c4 minor typo in authors 2017-04-16 17:25:34 +02:00
Jaromil
18067a1c2e version bump 2017-04-16 17:18:16 +02:00
Jaromil
7241eb0fd1 updated README with recent help screen 2017-04-16 13:06:33 +02:00
Jaromil
f9dc9ed5a7 documentation updates 2017-04-16 12:49:04 +02:00
Jaromil
5de9cb32b9 manual updated with actual gpg id usage information 2017-04-16 12:15:21 +02:00
Jaromil
66aa7fdac7 minor corrections to new gpg related message formatting 2017-04-16 12:07:41 +02:00
Jaromil
3e0dd1e111 add debug message of loop device in umount_tomb 2017-04-16 12:00:47 +02:00
Jaromil
3ffcc74e5b Merge pull request #256 from roddhjav/gpg-options
Options for GnuPG Key - 2
2017-04-16 11:55:26 +02:00
Alexandre Pujol
1050d43c59
Support for gpg encrypted tomb key loaded from stdin. See #255 2017-04-05 14:52:47 +01:00
Alexandre Pujol
c303513be4
Fix issues with GPG default key.
- Remove --no-options gpg option when using GPG key.
- Improve gpg default key tests

To use the default key, ~/.gnupg/gpg.conf needs:
  default-key <keyid>
  default-recipient-self
Or
  default-recipient <keyid>

Otherwise the first key in the keyring is used.
2017-04-03 13:24:25 +01:00
Alexandre Pujol
d720e4b2ce
Remove --shared flag when sharing a tomb key. See #252 2017-03-24 20:08:33 +00:00
Jaromil
6c2077fee1 make documentation less opinionated about the u/random issue
fix #253
2017-03-22 16:40:21 +01:00
Jaromil
ba9c0481cc Merge pull request #250 from roddhjav/gpg-support
Options for GnuPG Key
2017-03-20 21:03:03 +01:00
Alexandre Pujol
bb77de0815
Fix compatibility with GnuPG 2.2.19
GnuPG 2.2.19 added a warning when no command was given. Some invocations
do not specify a command, added --decrypt in this these cases.
2017-03-20 19:14:47 +00:00
Alexandre Pujol
29a177aa05
Fix issue #251 2017-03-20 19:07:04 +00:00
Jaromil
69f52bee25 information about compatible software 2017-03-18 15:24:01 +01:00
Alexandre Pujol
c793e0b132
Add support for non hidden and hidden recipient
Use -r to provide non-hidden recipient,
Use -R to provide hidden recipient.
2017-03-03 21:19:04 +00:00
Alexandre Pujol
6352a1d417
Add GPG default key support for key encryption
If the option -r is not set, use the gpg default key to encrypt
a tomb key
2017-03-03 20:36:50 +00:00
Jaromil
0644ebe951 updated manual to reflect final stage of gpg asymmetric encryption 2017-02-21 15:39:54 +01:00
Jaromil
b0815b514b small improvement to ISO compliancy documentation 2017-02-20 22:13:43 +01:00
Jaromil
3f06bce8eb failed bind-hooks (missing target) no more abort the mount operation
a warning is printed and the mounting goes forward without the hooks
2017-02-20 22:13:43 +01:00
Jaromil
e37982d114 Merge pull request #244 from roddhjav/gnupg-key-support
GnuPG Key Support
2017-02-20 22:09:48 +01:00
Jaromil
bea7fe3f7c Merge branch 'master' into gnupg-key-support 2017-02-20 20:47:06 +01:00
Jaromil
1f022d10f1 Merge pull request #248 from mesbahamin/open_read_only
Open non-writable Tomb files with "read only " mount option
2017-02-18 20:30:33 +01:00
Jaromil
2bc7e43198 Merge pull request #245 from Arusekk/master
Totally fixed spaces handling in tomb and tomber
2017-02-14 10:58:35 +01:00
Amin Mesbah
70334f58fb Skip writable check when mounting with "ro" option.
When opening a tomb file with "ro" passed through the -o option, the
writability check in is-valid-tomb() is skipped. This allows tomb files
to be opened without write permission.

test-open-read-only() now succeeds.
2017-02-12 17:44:55 -08:00
Amin Mesbah
f4f8c4e024 Add failing test for opening read-only tomb.
Adds a test function called test-open-read-only(). The test prepares a
tomb file, removes the "write" permissions from it, and then attempts to
open it with "read-only" mount options (`-o ro,noatime,nodev`).

The test currently fails as expected.
2017-02-12 16:39:39 -08:00
Arusekk
f4cdc1a0c5 Fixed spaces handling in Tomb 2017-02-10 21:05:04 +01:00
Alexandre Pujol
4a7019715f Use --hidden-recipient by default instead of --recipient.
Due to the hidden-recipient, GPG will try all the available keys. User
can speed up this process providing the recipent using the -r
option. Therefore, 'tomb open' optionaly support the -r option.
2017-02-09 20:59:10 +00:00
Alexandre Pujol
c63fcf2730 Fix is_valid_recipients private key detection 2017-02-09 19:18:02 +00:00
Alexandre Pujol
528140738a Add -g/--gpgkey option to tell tomb to use GPG key to encrypt a tomb key
Option -r is now only used to provide the recipient
Option -R removed, the new recipient can be given by the -r option.
2017-02-09 18:57:34 +00:00
Jaromil
8832471170 Merge pull request #246 from Narrat/bug/slam
lsof should be correctly detected now
2017-02-09 02:23:10 +01:00
Narrat
e69795fe71 lsof should be correctly detected now
LSOF would be set everytime otherwise
2017-02-07 03:30:25 +01:00
Jaromil
5b7f875f3d Merge pull request #243 from Narrat/bug/slam
Use of lsof to fix slam for specific mountpoint
2017-02-06 11:52:08 +01:00
Narrat
b2ee2114cf Make lsof an optional dep
tomb doesn't need lsof for anything else, and can work regulary without it.
So make it an optional feature, which allows to slam a tomb if lsof is installed

Updates additionally the man page and generates a new pdf from it
2017-02-05 20:03:29 +01:00
Alexandre Pujol
bfe5bb9707 Update the man page with GPG key support 2017-02-03 23:57:52 +00:00
Alexandre Pujol
f27130053d Add new options description in tomb -h 2017-02-03 23:57:52 +00:00
Alexandre Pujol
6cfffef137 Update function comments & description with GPG recipient support. 2017-02-03 23:57:52 +00:00
Alexandre Pujol
dfc593f9d6 Add support for GPG key in the tomb outputs. 2017-02-03 23:57:52 +00:00
Alexandre Pujol
e8384ec7ac Allow opening a tomb without giving a valid recipient.
The -r option always requires an arguments. However GPG does not need
any recipient when decrypting a key. In order to be able to open a tomb
without writing (the long) recipient, the user can use the -f option to
short-cut the valid recipient checking. A dummy recipient is still required.
2017-02-03 23:57:52 +00:00
Alexandre Pujol
6f89dbd2fe Add '--shared' in order to activate sharing support.
Sharing feature is a very sensitive action, the user needs to trust the
GPG public key it is going to share its tomb. This is why this feature
needs to be explicitly activated using in more the flag --shared
on the key encryption commands.
2017-02-03 23:57:52 +00:00
Alexandre Pujol
15164f5578 Add sharing support for tomb key.
A tomb key can be encrypted with more than one recipient. Therefore, a
tomb can be shared between different user. The multiple recipients are
given using the -r (or/and -R) option and must be separated by ','.

Multiple recipients can be given for the commands: forge, setket and passwd
2017-02-03 23:57:52 +00:00
Alexandre Pujol
53b7460274 Add tomb setkey support for GPG key 2017-02-03 23:57:52 +00:00
Alexandre Pujol
a200448de2 Add tomb resize support for GPG key 2017-02-03 23:57:52 +00:00
Alexandre Pujol
47ddeebbc4 Add support to change the GPG key used to encrypt a tomb key. (tomb passwd) 2017-02-03 23:57:52 +00:00
Alexandre Pujol
5a35ab9668 Improve key encryption/decryption using GPG key.
Decryption/Encryption works without these improvment, however, there
are needed in order to have clean key (without empty line).

Moreover, tests showed not doing cause troubles when changing the GPG key
used to encrypt a tomb key.
2017-02-03 23:57:52 +00:00
Alexandre Pujol
8f8dc0a0d4 Improve exhumation of key when opening a tomb 2017-02-03 23:57:52 +00:00
Alexandre Pujol
b23e9aa028 Add --tomb-pwd support for GPG key on steganography functions 2017-02-03 23:57:52 +00:00
Alexandre Pujol
e2fe8e508e Add unit tests for steganography feature using GPG key 2017-02-03 23:57:52 +00:00
Alexandre Pujol
d1b016b3c1 Add GPG recipient support for steganography function (bury and exhume)
The tomb policy is to use the same password to encrypt
the key and to bury it. However, steganography cannot be
done with GPG key. Therefore, we check the user can
decrypt the tomb with its GPG key and we ask for a
steganography password. Having different method is a
technical requirement and should enhance security.
2017-02-03 23:57:52 +00:00
Alexandre Pujol
2d516cbaed Check if the GPG key given is a valid.
Add the function 'is_valid_recipients'
A key is valid if both public and private keys are present
in the GPG database
2017-02-03 23:57:52 +00:00
Alexandre Pujol
902860fd9f Add GPG recipient support when generating a new tomb key 2017-02-03 23:57:52 +00:00
Alexandre Pujol
db7109da4a Add tests for GPG recipient support in tomb 2017-02-03 23:57:52 +00:00
Alexandre Pujol
f72534790a Fix test suite error in the return code: GLOBAL_RESULT were always true. 2017-02-03 23:57:52 +00:00
Alexandre Pujol
e78af47c56 Add a GPG database in 'extras/test/gnupg' for test suite purpose
The GPG Key are unencrypted. Do not use them for an other purpose
than a test suite.
2017-02-03 23:57:52 +00:00
Alexandre Pujol
6c0d89cab1 Use -r option to shortcut interactive tomb password popup 2017-02-03 23:57:52 +00:00
Alexandre Pujol
9ee0a1550e Add -r option to enable GPG key integration.
The flag -r (for recipient like in GPG itself) takes a mandatory
argument, the GPG key ID.
2017-02-03 23:57:52 +00:00
Narrat
537bb6aaeb Use of lsof to fix slam for specific mountpoint
Apparantly fuser didn't report back, if the tomb was mounted in a subdir of /run (whereas /run itself is often a tmpfs mount).
With no list of process ids those couldn't be killed, so slamming the tomb failed.
lsof is capable to report back the sought information.

Fixes #220

Additionally fixing the debug output, where a hardcoded mountpoint was used
2017-02-03 17:46:16 +01:00
Jaromil
1f852908ae improved readme, section on compliancy 2017-02-01 09:19:09 +01:00
Jaromil
9110ccd9d1 really use key-size 512 on luksFormat 2017-01-29 21:54:46 +01:00
Jaromil
7a98ee8ba6 change forged key lenght to 512 bits
Addresses issue #238: as 512 bit key length triggers use of AES256.
Apparently so far tombs used AES128 due to key length 256.
Change passes all tests and has no regression implications.
2017-01-21 23:50:57 +01:00
Jaromil
4439a6a327 minor fixes to regression tests 2017-01-21 23:50:57 +01:00
Daniel Rodriguez
e7e21243db Automatically remove conflicting quotes on pot generation 2017-01-21 18:48:09 +01:00
Daniel Rodriguez
26e549292f Remove extra char quotes in translation files 2017-01-21 18:38:07 +01:00
Jaromil
5e8db49701 Merge pull request #239 from reiven/master
Update tomber in extras for v2.2+
2017-01-11 14:36:40 +01:00
Federico Reiven
a808d4aef8 Update tomber in extras for v2.2+ 2017-01-10 12:11:32 -03:00
Daniel Rodriguez
42ae73d727 Sync translations with POEditor 2017-01-03 12:00:29 +01:00
Jaromil
ed37b4e1fa integrated latest changes in changelog 2017-01-02 12:54:55 +01:00
Jaromil
fa145074f8 documentation updates and reorganisation
Added two new sections to the manpage: deniability and password.
Small actualisation of the install instructions.
2017-01-02 12:02:23 +01:00
Jaromil
843b7fdfc4 remove change of ownership when mounting tombs
The chmod/chown launched on the mounted volume is not really effective
for security, plus the UID is not correctly guessed when tomb is
launched using sudo. It is now up to the user to correctly set
ownership and permission on mounted volumes. There is also one less
check on the ownership of the tomb file which was failing with a
warning in the same case.
2017-01-02 11:04:08 +01:00
Jaromil
5996beab0e small fixes to run clean tests 2017-01-02 07:03:54 +01:00
Jaromil
cb699189e7 small linting fixes 2017-01-02 06:13:52 +01:00
Jaromil
0fa4a07f8c make lint check on travis using shellcheck 2017-01-02 06:04:58 +01:00
Jaromil
18743c82a5 code linting
small cleanup using shellcheck, also available as 'make lint'
2017-01-02 06:03:29 +01:00
Jaromil
6f4cfd626c prefer ascii single-quotes to utf8 2017-01-02 06:02:50 +01:00
Jaromil
d41347fe22 documentation updates for release 2016-12-29 19:20:48 +01:00
Jaromil
88f5a926f0 updated extras/gtomb to latest by parazyd 2016-12-29 17:29:15 +01:00
Jaromil
7b72f07f96 switch shebang to use /usr/bin/env
this is a more generic approach to shebang which supports interpreters
when installed anywhere in the current path.
2016-12-29 13:49:03 +01:00
Jaromil
14cba81f6e fix is_valid_tomb check for already mounted tombs
also added some more verbosity on debug
2016-12-26 20:40:23 +01:00
Jaromil
db976a5210 improve wrapping of key generation
gen_key now avoids adding a final newline to file (addressing #226)
and provides more debugging information from the gpg  process.
2016-12-26 20:19:01 +01:00
Jaromil
e59518befa included regression tests against old Tomb versions 2016-12-26 20:03:14 +01:00
Jaromil
f5375c61fe improvement over previous gpg_decrypt fix
now also avoiding the use of `read` shell built-in
2016-12-26 19:04:54 +01:00
Jaromil
df75c39a58 new parsing for gpg_decrypt function
this new parser works with all ZSh versions and brings overall
improvement by eliminating the invocation of exernal binary `grep`
over the secret data.
2016-12-26 12:12:34 +01:00
Jaromil
4b1afb4fab documentation on direct use of images as keys
as mentioned in #225 now the manual mentions using jpeg images
directly as arguments to -k on open commands.
2016-12-22 20:46:40 +01:00
Jaromil
b9f555b5fe temporary advice for zsh 5.3 users 2016-12-19 09:40:10 +01:00
Jaromil
844a886da1 fix sudo execution (patch by robertmx in #223)
tested also in #228, this stops overwriting the $USERNAME
variable which is not really useful (it was used in the previous
privilege escalation model)
2016-11-18 19:00:47 +01:00
Jaromil
7e88c5d07b travis fix to force apt to overwrite new conf files 2016-11-18 14:27:04 +01:00
Jaromil
99bb7fd067 fix travis for non-interactive apt 2016-11-18 14:21:57 +01:00
Jaromil
101b89f0be use head directly without cat in post-hooks
less is more...
2016-11-18 13:56:44 +01:00
Jaromil
fa44f46eba better documentation for kdf
also correctly use _failure on fatal error using --kdf
2016-11-18 13:56:44 +01:00
D.J.R
c502ef3d92 Merge pull request #230 from mandeep/change_swap_success_message
Changed message when encrypted swap found to something more informative
2016-10-17 22:00:46 +02:00
mandeepbhutani
50719fb06f Changed message when encrypted swap found to something more informative
Changed message to detail all swap partitions
2016-10-16 11:40:26 -05:00
Jaromil
c80ebd6d6e travis CI: removed kdf tests
kdf computation seems to not work on travis, it times out
2016-06-13 10:34:31 +02:00
Jaromil
6855127c15 added extras to CI tests (kdf etc.) 2016-06-12 17:28:09 +02:00
Jaromil
feb35205c8 setup continuous integration tests on Travis 2016-06-12 17:18:21 +02:00
Jaromil
ff57aa3214 experimental port to android - works with limited functionality 2016-01-24 23:07:19 +01:00
Jaromil
8f0b2943ce documentation improvements for kdf and dm-crypt cipher choice 2016-01-09 10:16:42 +01:00
Jaromil
a08cb6e0de forgot to bump the version into the script 2015-12-31 13:33:36 +01:00
161 changed files with 29606 additions and 7102 deletions

4
.github/FUNDING.yml vendored Normal file
View File

@ -0,0 +1,4 @@
github: dyne
patreon: dyneorg
open_collective: dyne

76
.github/workflows/hugo.yml vendored Normal file
View File

@ -0,0 +1,76 @@
# Sample workflow for building and deploying a Hugo site to GitHub Pages
name: Deploy Hugo site to Pages
on:
# Runs on pushes targeting the default branch
push:
branches: ["website"]
# Allows you to run this workflow manually from the Actions tab
workflow_dispatch:
# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages
permissions:
contents: read
pages: write
id-token: write
# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued.
# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete.
concurrency:
group: "pages"
cancel-in-progress: false
# Default to bash
defaults:
run:
shell: bash
jobs:
# Build job
build:
runs-on: ubuntu-latest
env:
HUGO_VERSION: 0.124.1
steps:
- name: Install Hugo CLI
run: |
wget -O ${{ runner.temp }}/hugo.deb https://github.com/gohugoio/hugo/releases/download/v${HUGO_VERSION}/hugo_extended_${HUGO_VERSION}_linux-amd64.deb \
&& sudo dpkg -i ${{ runner.temp }}/hugo.deb
- name: Install Dart Sass
run: sudo snap install dart-sass
- name: Checkout
uses: actions/checkout@v4
with:
submodules: recursive
- name: Setup Pages
id: pages
uses: actions/configure-pages@v5
- name: Install Node.js dependencies
run: "[[ -f package-lock.json || -f npm-shrinkwrap.json ]] && npm ci || true"
- name: Build with Hugo
env:
# For maximum backward compatibility with Hugo modules
HUGO_ENVIRONMENT: production
HUGO_ENV: production
run: |
hugo \
--minify \
--source docs \
## breaks URL building ## --baseURL "${{ steps.pages.outputs.base_url }}/"
- name: Upload artifact
uses: actions/upload-pages-artifact@v3
with:
path: ./docs/public
# Deployment job
deploy:
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
runs-on: ubuntu-latest
needs: build
steps:
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v4

56
.github/workflows/linux.yml vendored Normal file
View File

@ -0,0 +1,56 @@
name:
💀 Linux Tomb
on:
push:
paths-ignore:
- 'doc/**'
- 'extras/portable/**'
- '*.md'
branches:
- master
pull_request:
paths-ignore:
- 'doc/**'
- 'extras/portable/**'
- '*.md'
branches:
- master
concurrency:
group: ${{ github.workflow }}-${{ github.ref_name }}
cancel-in-progress: true
jobs:
build-matrix:
strategy:
matrix:
os: [ubuntu-22.04, ubuntu-20.04, ubuntu-24.04]
runs-on: ${{ matrix.os }}
steps:
- name: Install tomb dependencies
run: |
sudo apt-get update -y -q
sudo apt-get install -y -q zsh cryptsetup gpg gawk libgcrypt20-dev steghide qrencode python3-pip python3-dev libssl-dev make gcc sudo gettext bsdmainutils file pinentry-curses xxd libsodium23 libsodium-dev argon2
- name: Install python2 on ubuntu 20
if: matrix.os == 'ubuntu-20.04'
run: sudo apt-get install -y -q python2
- name: Install python2 on ubuntu 22
if: matrix.os == 'ubuntu-22.04'
run: sudo apt-get install -y -q python2
- name: Install doas where found
if: matrix.os == 'ubuntu-24.04'
run: |
sudo apt-get install -y -q opendoas
echo "permit nopass root" | sudo tee /etc/doas.conf
- uses: actions/checkout@v3
- name: Build the pbkdf2 extras
run: |
make --directory=extras/kdf-keys
sudo make --directory=extras/kdf-keys install
- name: Run pbkdf2 tests
run: sudo make -C extras/kdf-keys test
- name: Disable swap
run: sudo swapoff -a
- name: Run main tests
run: sudo make test

13
.travis.yml Normal file
View File

@ -0,0 +1,13 @@
sudo: required
language: c
services:
- docker
before_script:
- cp -v extras/test/Dockerfile .
- docker build -t dyne/tomb .
- docker run -it --privileged dyne/tomb /bin/bash -c "oracle & make test"
script:
- docker run -it --privileged dyne/tomb /bin/bash -c "make -C extras/kdf-keys test"

View File

@ -1,32 +1,43 @@
Cryptsetup is written by Christophe Saout and Clemens Fruhwirth. Cryptsetup was originally written in 2004 by Jana Saout
Tomb is written and directed by Denis Roio <jaromil@dyne.org> LUKS extensions are written in 2006 by Clemens Fruhwirth
Tomb is written and maintained since 2007 by [Denis "Jaromil" Roio](https://jaromil.dyne.org)
Tomb includes code and advices by Anathema, Boyska, Hellekin O. Wolf, Tomb includes code and advices by Anathema, Boyska, Hellekin O. Wolf,
GDrooid, Parazyd. Daniel Rodriguez, Parazyd, Alexandre Pujol, AitorATuin, Narrat, Artur
Malimonov and Chris Vogel.
The minimal Zenity GUI is being written by Parazyd The 'gtomb' GUI based on Zenity is written by Parazyd and Daniel Dias Rodrigues.
The Qt5 desktop tray GUI is written by Gianluca Montecchi. The Qt5 desktop tray GUI is written by Gianluca Montecchi.
Python Tomb wrappers are written by Reiven and Boyska. Python Tomb wrappers are contributed by Reiven and Boyska.
The Docker Tomb wrapper is contributed by Greg Tczap and Jens Rischbieth.
Artwork is contributed by Jordi aka Mon Mort and Logan VanCuren. Artwork is contributed by Jordi aka Mon Mort and Logan VanCuren.
Gettext internationalization and Spanish translation is contributed by Gettext internationalization and Spanish translation is contributed by
GDrooid, French translation by Hellekin, Russian translation by fsLeg, Daniel Rodriguez and Francisco Serrador. French translation by
German translation by x3nu, Italian translation by Massimiliano Hellekin and Roy Lockhart, Russian translation by fsLeg and AHOHNMYC,
Augello and Swedish translation by PLJ / Kosovoper. German translation by Jerry Polfer, Italian translation by
Massimiliano Augello and Swedish translation by PLJ / Kosovoper,
general fixes contributed by Daniel Dias Rodrigues.
Testing, reviews and documentation contributed by Dreamer, Vlax, Testing, reviews and documentation contributed by Dreamer, Vlax,
Shining the Translucent, Mancausoft, Asbesto Molesto, Nignux, TheJH, Shining the Translucent, Mancausoft, Asbesto Molesto, Nignux, TheJH,
The Grugq, Reiven, GDrooid, Alphazo, Brian May, fsLeg, JoelMon, The Grugq, Reiven, GDrooid, Alphazo, Brian May, fsLeg, Narrat, Jerry
Narrat, x3nu, Jim Turner, Maxime Arthaud and the Linux Action Show! Polfer, Jim Turner, Maxime Arthaud, RobertMX, mhogomchungu Mandeep
Bhutani, Emil Lundberg, Joel Montes de Oca, Armin Mesbah, Arusekk,
Stephan Schindel, Asbjørn Apeland, Victor Calvert, bjonnh, SargoDevel,
AitorATuin, Alexis Danizan, Sven Geuer, Greg Tczap, Aaron Janse, Mark
Mykkanen, Alexis Danizan, Steve Litt, James R, Matthieu Crapet, Selene
ToyKeeper, Valentin Heidelberg and... the Linux Action Show!
Tomb includes an implementation of the "Password-Based Key Derivation Tomb includes an implementation of the "Password-Based Key Derivation
Function v2" based on GCrypt and written by Anthony Thyssen. Function v2" based on GCrypt and written by Anthony Thyssen, with
fixes contributed by AitorATuin.
Tomb developers can be contacted via GitHub issues on Some of the Tomb developers can be contacted via [GitHub discussions](https://github.com/dyne/Tomb/discussions)
https://www.github.com/dyne/Tomb or over IRC https://irc.dyne.org or over Telegram via the [Dyne.org Chat Channel](https://t.me/dyne_chat).
channel **#dyne** (or directly over port 9999 with SSL)

View File

@ -1,5 +1,141 @@
# Tomb ChangeLog # Tomb ChangeLog
## 2.11
### July 2024
Search engine is upgraded to use recoll, based on xapian backend:
offers a GUI and is more up to date, replaces swish-e. Various fixes
include a better default of 3 rounds for Argon2 KDF, improved support
for cloakify which is now shipped in tomb/extras, new support for
pinentry-tty useful for some headless systems and terminals over
serial port, improved usage on machines without sudo and support
bind-hooks on folders containing spaces in their names. Also the
translations to some other languages were improved. All documentation
was revisited and reorganized, manpage corrected in some parts and a
new homepage is up at https://dyne.org/tomb
## 2.10
### Sep 2023
This release adds optional support for Argon2 KDF brute-force
protection and introduces support for doas as an alternative to sudo
for privilege escalation. It also improves support for BTRFS formatted
Tombs, adds zram detection as swap memory, updates documentation and
translations and cleans up the script code.
## 2.9
### Jan 2021
This release fixes all bugs introduced by the unfortunate 2.8 release
series in 2020 as well introduces support for BTRFS formatted
Tombs. The fixes are for password insertion to work on all desktops,
as well the fix to a regression when using old Zsh versions. The new
feature is activated by the '--filesystem' flag on 'lock' commands.
It only supports BTRFS as internal filesystem of a Tomb instead of the
default EXT4; resizing works as well to create and send or receive
subvolumes and snapshots inside a Tomb. There are also some cleanups,
small error handling improvements and no more need for suid actions by
'forge' and 'dig' commands.
## 2.8.1
### Nov 2020
This is a minor bugfix release. It fixes two bugs introduced by the
previous release: the release of loopback devices and a typo affecting
password insertion in text-only mode. It also provides a cosmetic fix
for the output of 'tomb list' that now displays correct sizes. At
last, the docker wrapper has been included in extras/ to be shipped in
Tomb. The span of CVE-2020-28638 has been assessed with more precision
and KNOWN_BUGS updated accordingly.
## 2.8
### Nov 2020
This new release updates the documentation, improves usability and
fixes two bugs. A bug has been found (CVE-2020-28638) to corrupt
passwords entered using pinentry-curses on desktops using a X11
DISPLAY, the documentation in KNOWN_BUGS outlines how to fix
regressions. Another bug has been fixed to prevent mounting tombs that
are already opened, a situation leading to potential data loss.
Changes mentioned lead to a small internal refactoring and cleanup,
leading to a change in the way volumes appear in /dev/mapper. Along
the usability improvements are the support of GNUPGHOME environment
variable to support non-standard GnuPG home locations as well updated
translations and the fact that debug messages are now written to
stderr, making it easier to parse stdout.
## 2.7
### Oct 2019
Fixed getent parsing of passwd and notation of conditionals
normalised. A few other minor fixes and documentation improvements.
## 2.6
### May 2019
This release adds new features and provides an important fix for usage
of Tomb with cryptsetup 2.1 and future versions; it also fixes a
whitespace bug in KDF passwords, all fixes are documented in
KNOWN_BUGS. A notable new feature is the libsphinx integration for
password-authenticated key agreement (PAKE). Another feature is the
integration of cloakify to support new cloak/uncloak commands that
hide keys inside long text files. Also support for gpg sub-keys has
been added and overall gpg asymmetric key protection is improved.
## 2.5
### January 2018
This is mostly a bugfix release, including two internal
refactorings. An important change is the re-introduction (since v2.3)
of ownership change of all files inside tombs, to facilitate single
user usage, which is now default and can be prevented using the '-p'
flag on 'open' commands. The first refactoring concerns the test
units, now using the 'sharness' framework. The other refactoring
concerns 'post-hooks' now renamed to 'exec-hooks' and launched on
'open' and 'close' commands with a defined set of arguments. Another
internal change concerns the use of 'findmnt' instead of parsing the
output of 'mount -l', which grants compatibility with more recent
versions of util-linux. A fix was made to the 'slam' command for a
better process detection and the introduction of a new 'ps' command to
just list processes using tombs. Another fix was made to support tomb
hidden filenames (starting with a dot) without any extension. Some
more minor fixes were made to messaging and translations, plus all the
documentation is updated.
## 2.4
### April 2017
This release introduces a major new feature with support for
asymmetric encryption of Tomb keys using public/private GPG key
pairs. It is now possible to protect a Tomb key using a GPG key (which
can also be password-less for automations) as well encrypt a Tomb key
for multiple recipients (list of GPG ids). Other improvements include:
a fix to the 'slam' command with better detection of running programs
using 'lsof' (new optional dependency); a fix to 'forge' key creation
to really use 512 bits long keys to really trigger usage of AES256;
correct support for opening tombs in read-only mode; update of the
Tomber python wrapper in extras. Documentation has been updated.
## 2.3
### January 2017
Fix to bug occurring when using ZSh version 5.3 or higher. Fix to
inclusion of final newline in keys generated with 2.2, only affecting
third-party software. Removed chmod/chown of tombs when open. Enhanced
continuous integration script with regression tests with usage of old
stable versions of Tomb and shellcheck linting. Improved parser and
post-hooks to avoid usage of external binaries (grep and cat) also
improving security when decrypting keys. Fix for clean execution via
sudo nopasswd. Updated extras/gtomb to latest stable version. Various
documentation updates about kdf, using images as keys, deniability and
gpg-agent usage. New experimental port to Android platforms in extras.
## 2.2 ## 2.2
### December 2015 ### December 2015

View File

@ -5,6 +5,7 @@
Tomb needs a few programs to be installed on a system in order to work: Tomb needs a few programs to be installed on a system in order to work:
* zsh * zsh
* file
* sudo * sudo
* gnupg * gnupg
* cryptsetup * cryptsetup
@ -20,12 +21,12 @@ To install Tomb simply download the source distribution (the tar.gz file)
from https://files.dyne.org/tomb and decompress it. From a terminal: from https://files.dyne.org/tomb and decompress it. From a terminal:
cd Downloads cd Downloads
tar xvfz Tomb-2.0.1.tar.gz (correct with actual file name) tar xvfz Tomb-2.4.tar.gz (correct with actual file name)
Then enter its directory and run 'make install' as root, this will install Then enter its directory and run 'make install' as root, this will install
Tomb into /usr/local: Tomb into /usr/local:
cd Tomb-2.0.1 (correct with actual directory name) cd Tomb-2.4 (correct with actual directory name)
sudo make install sudo make install
After installation one can read the commandline help or read the manual: After installation one can read the commandline help or read the manual:
@ -50,7 +51,7 @@ The key can also be hidden in an image, to be used as key later
tomb bury -k secrets.tomb.key nosferatu.jpg (hide the key in a jpeg image) tomb bury -k secrets.tomb.key nosferatu.jpg (hide the key in a jpeg image)
tomb open -k nosferatu.jpg secrets.tomb (use the jpeg image to open the tomb) tomb open -k nosferatu.jpg secrets.tomb (use the jpeg image to open the tomb)
Or backupped to a QRCode that can be printed on paper and hidden in Or backed up to a QRCode that can be printed on paper and hidden in
books. QRCodes can be scanned with any mobile application, resulting books. QRCodes can be scanned with any mobile application, resulting
into a block of text that can be used with `-k` just as a normal key. into a block of text that can be used with `-k` just as a normal key.
@ -60,47 +61,19 @@ There are some more things that tomb can do for you, make sure you
have a look at the manpage and at the commandline help to find out have a look at the manpage and at the commandline help to find out
more. more.
## Basic usage notes # Optional tools
Here we collect notes on common issues users may or may not experience
and the commonly working solutions found.
### Pinentry issues
If pinentry has problems dealing with the password because of language
or tty settings on your system, try running `gpg-agent` by launching it
from the session initialization (~/.xsession or ~/.xinitrc) with this
command:
```
eval $(gpg-agent --daemon --write-env-file "${HOME}/.gpg-agent-info")
```
### Deleting history
To improve deniability one has to avoid that tomb commands are
recorded in the shell history. In order to do so the
`HISTIGNORESPACE=1` environment setting of Zsh comes handy. Anywhere
in the `.zshrc` put:
```
export HISTIGNORESPACE=1
alias tomb=' tomb'
```
# Advanced usage
## Install optional tools
Tomb can use some optional tools to extend its functionalities: Tomb can use some optional tools to extend its functionalities:
executable | function executable | function
---------- | --------------------------------------------------- ---------- | ---------------------------------------------------
lsof | slam a tomb (close even if open programs)
dcfldd | show progress while digging tombs and keys dcfldd | show progress while digging tombs and keys
steghide | bury and exhume keys inside images steghide | bury and exhume keys inside images
resizefs | extend the size of existing tomb volumes resizefs | extend the size of existing tomb volumes
qrencode | engrave keys into printable qrcode sheets qrencode | engrave keys into printable qrcode sheets
mlocate | fast search of file names inside tombs plocate | fast search of file names inside tombs
swish++ | fast search of file contents inside tombs recoll | fast search of file contents inside tombs
unoconv | fast search of contents in PDF and DOC files unoconv | fast search of contents in PDF and DOC files
lesspipe | fast search of contents in compressed archives lesspipe | fast search of contents in compressed archives
haveged | fast entropy generation for key forging haveged | fast entropy generation for key forging
@ -110,12 +83,12 @@ the packages provided by each distribution.
Once any of the above is installed Tomb will find the tool automatically. Once any of the above is installed Tomb will find the tool automatically.
## Install Tomb Extras # Extras
Tomb comes with a bunch of extra tools that contribute to enhance its Tomb comes with a bunch of extra tools that contribute to enhance its
functionality or integrate it into particular system environments. functionality or integrate it into particular system environments.
### extras/gtk-tray ## extras/gtk-tray
The Gtk tray adds a nifty tomb skull into the desktop toolbar: one can The Gtk tray adds a nifty tomb skull into the desktop toolbar: one can
use it to close, slam and explore the open tomb represented by it. use it to close, slam and explore the open tomb represented by it.
@ -130,15 +103,15 @@ To have it change directory `extras/gtk-tray` then
3. run `sudo make install` (default PREFIX is `/usr/local`) 3. run `sudo make install` (default PREFIX is `/usr/local`)
4. start `tomb-gtk-tray tombname` after the tomb is open 4. start `tomb-gtk-tray tombname` after the tomb is open
Of cource one can include the launch of tomb-gtk-tray scripts. Of course, one can include the launch of tomb-gtk-tray scripts.
### extras/qt-tray ## extras/qt-tray
The QT tray adds a tomb tray in a QT desktop toolbar. It requires at The QT tray adds a tomb tray in a QT desktop toolbar. It requires at
least QT libraries of version 5.4 or above. least QT libraries of version 5.4 or above.
Build with 'qmake' and then 'make'. Build with 'qmake' and then 'make'.
### extras/kdf-keys ## extras/kdf-keys
The KDF wrapper programs allows one to use KDF rounds on passwords in The KDF wrapper programs allows one to use KDF rounds on passwords in
order to obstruct dictionary based and similar brute-forcing attacks. order to obstruct dictionary based and similar brute-forcing attacks.
@ -165,7 +138,7 @@ Please note that it doesn't makes much sense to use KDF keys and
steganography, since the latter will invalidate the brute-forcing steganography, since the latter will invalidate the brute-forcing
protection. For details on the issue see [KNOWN_BUGS.md](KNOWN_BUGS). protection. For details on the issue see [KNOWN_BUGS.md](KNOWN_BUGS).
### extras/translations/ ## extras/translations/
There are translations available for Tomb and they are installed by There are translations available for Tomb and they are installed by
default. If you wish to update them manually navigate to extras/po default. If you wish to update them manually navigate to extras/po
@ -174,7 +147,7 @@ and run 'make install' as root:
cd extras/translations cd extras/translations
sudo make install sudo make install
### extras/gtomb/ ## extras/gtomb/
This is a minimalistic graphical user interface scripted in ZSh This is a minimalistic graphical user interface scripted in ZSh
depending from Zenity to display dialog boxes. It covers all basic depending from Zenity to display dialog boxes. It covers all basic
@ -204,6 +177,14 @@ other people logged on the same system can easily log your passwords
while such commands are executing. while such commands are executing.
We only recommend using the pinentry to input your passwords. We only recommend using the pinentry to input your passwords.
At the time of writing another free software graphical application
supports opening and closing Tombs via a plugin installed by
default: [zuluCrypt](https://mhogomchungu.github.io/zuluCrypt/). One
needs to activate the Tomb plugin included in the zuluCrypt source to
be able to create, open and close tombs. Beware zuluCrypt may miss
advanced Tomb functionalities that are only available from the
command-line.
## Python ## Python
![](extras/images/python_for_tomb.png) ![](extras/images/python_for_tomb.png)
@ -212,14 +193,6 @@ A Python wrapper is under development and already usable, but it
introduces some vulnerabilities mentioned above. Find it in introduces some vulnerabilities mentioned above. Find it in
`extras/tomber`. For more information see [PYTHON](extras/PYTHON.md). `extras/tomber`. For more information see [PYTHON](extras/PYTHON.md).
## Graphical applications
So far the only graphical application supporting Tomb volumes is
[ZuluCrypt](https://github.com/mhogomchungu/zuluCrypt). One needs to
activate the Tomb plugin included in its source and will be able to
create, open and close tombs. It might still miss advanced Tomb
functionalities that are only available from the command-line.
## Let us know! ## Let us know!
If you plan to develop any kind of wrapper for Tomb you are welcome to If you plan to develop any kind of wrapper for Tomb you are welcome to

View File

@ -1,3 +1,42 @@
# Password bug in X11 when using pinentry-curses
## Issue with Tomb version 2.6 and 2.7
This bug affects systems with a running X11 DISPLAY, but where only
pinentry-ncurses is installed. It wrongly reads the input password: no
matter what string is chosen, the password becomes:
tomb [W] Detected DISPLAY, but only pinentry-curses is found.
Following the fix in Tomb 2.8 affected users will need to use the line
above as password to open their tomb and should change their key with
a new password using 'tomb passwd'.
# Cryptsetup change of default to luks2
## Issue opening tombs with cryptsetup >2.0
Tomb uses the cryptsetup LUKS volume header default to type luks1
which has been for long the default in cryptsetup. But starting from
cryptsetup v2.1 a new default has been introduced (luks2) and the
--type option added to specify the old luks1.
Using Tomb version 2.6 (and future releases) the problem opening tombs
using recent GNU/Linux distributions is fixed.
# Whitespace in KDF passwords
## Issue affecting passwords used with PBKDF2 keys (<2.6)
Up until and including Tomb's version 2.5 the PBKDF2 wrapper for keys
in Tomb has a bug affecting passwords that contain whitespace. Since
the passwords are trimmed at the first whitespace, this makes them
weaker, while fortunately the KDF transformation still applies.
This issue is fixed in Tomb version 2.6: all users adopting KDF keys
that have passwords containing whitespace should change them,
knowing that their "old password" is trimmed until the whitespace.
Users adopting GPG keys or plain (without KDF wrapper) can ignore
this bug.
# Vulnerability to password bruteforcing # Vulnerability to password bruteforcing
## Issue affecting keys used in steganography ## Issue affecting keys used in steganography
@ -23,6 +62,18 @@
a good practice to change it using the `setkey` command on a secure a good practice to change it using the `setkey` command on a secure
machine, possibly while off-line or in single user mode. machine, possibly while off-line or in single user mode.
# Ending newline in tomb keys
## 2.2
When used to forge new keys, Tomb version 2.2 incorrectly added a new
line ('\n', 0x0A) character at the end of each key's secret sequence
before encoding it with GnuPG. This does not affect Tomb regression
and compatibility with other Tomb versions as this final newline is
ignored in any case, but third party software may have
problems. Those writing a software that supports opening Tomb files
should always ignore the final newline when present in the secret
material obtained after decoding the key with the password.
# Versioning and stdin key # Versioning and stdin key
## 1.5 ## 1.5

View File

@ -2,6 +2,14 @@ PROG = tomb
PREFIX ?= /usr/local PREFIX ?= /usr/local
MANDIR ?= ${PREFIX}/share/man MANDIR ?= ${PREFIX}/share/man
deps:
@if [ -r /etc/debian_version ]; then \
apt-get install -qy zsh cryptsetup file gnupg pinentry-curses; fi
@if [ -r /etc/fedora-release ]; then \
yum install -y zsh cryptsetup file gnupg pinentry-curses; fi
@if [ -r /etc/alpine-release ]; then \
apk add zsh cryptsetup file gpg pinentry-tty e2fsprogs findmnt; fi
all: all:
@echo @echo
@echo "Tomb is a script and does not need compilation, it can be simply executed." @echo "Tomb is a script and does not need compilation, it can be simply executed."
@ -9,7 +17,7 @@ all:
@echo "To install it in /usr/local together with its manpage use 'make install'." @echo "To install it in /usr/local together with its manpage use 'make install'."
@echo @echo
@echo "To run Tomb one needs to have some tools installed on the system:" @echo "To run Tomb one needs to have some tools installed on the system:"
@echo "Sudo, cryptsetup, pinentry and gnupg. Also wipe is recommended." @echo "Sudo, cryptsetup, pinentry and gnupg."
@echo @echo
install: install:
@ -24,3 +32,6 @@ install:
test: test:
make -C extras/test make -C extras/test
lint:
shellcheck -s bash -e SC1058,SC1073,SC1072,SC1009 tomb

228
README.md
View File

@ -1,232 +1,58 @@
..... .. # Tomb: The Linux Crypto Undertaker
.H8888888h. ~-. . uW8"
888888888888x `> u. .. . : `t888
X~ `?888888hx~ ...ue888b .888: x888 x888. 8888 .
' x8.^"*88*" 888R Y888r ~`8888~'888X`?888f` 9888.z88N
`-:- X8888x 888R I888> X888 888X '888> 9888 888E
488888> 888R I888> X888 888X '888> 9888 888E
.. `"88* 888R I888> X888 888X '888> 9888 888E
x88888nX" . u8888cJ888 X888 888X '888> 9888 888E
!"*8888888n.. : "*888*P" "*88%""*88" '888!` .8888 888"
' "*88888888* 'Y" `~ " `"` `%888*%"
^"***"` "`
*A minimalistic commandline tool to manage encrypted volumes* aka **The Crypto Undertaker** [![Build Status](https://github.com/dyne/tomb/actions/workflows/linux.yml/badge.svg)](https://github.com/dyne/Tomb/actions)
<!-- [![Build Status](https://github.com/dyne/tomb/actions/workflows/portable.yml/badge.svg)](https://github.com/dyne/Tomb/actions) -->
[![software by Dyne.org](https://www.dyne.org/wp-content/uploads/2015/12/software_by_dyne.png)](http://www.dyne.org) Minimalistic command line tool based on Linux dm-crypt and LUKS, trusted by hackers since 2007.
Updates on website: https://www.dyne.org/software/tomb You can keep your volumes secure and easily manageable with simple commands.
Get the stable .tar.gz signed release for production use!
Download it from https://files.dyne.org/tomb
![tomb's logo](https://github.com/dyne/Tomb/blob/master/extras/images/monmort.png) ![tomb's logo](https://github.com/dyne/Tomb/blob/master/extras/images/monmort.png)
# What is Tomb, the crypto undertaker? Create a new 120MiB `secret.tomb` folder and lock it with a new `secret.tomb.key` file.
Tomb aims to be a free and open source system for easy encryption and
backup of personal files, written in code that is easy to review and
links shared GNU/Linux components.
At present, Tomb consists of a simple shell script (Zsh) using
standard filesystem tools (GNU) and the cryptographic API of the Linux
kernel (cryptsetup and LUKS). Tomb can also produce machine parsable
output to facilitate its use inside graphical applications.
# How does it work?
To create a Tomb, do:
``` ```
$ tomb dig -s 100 secret.tomb $ tomb dig -s 120 secret.tomb
$ tomb forge secret.tomb.key $ tomb forge -k secret.tomb.key
$ tomb lock secret.tomb -k secret.tomb.key $ tomb lock -k secret.tomb.key secret.tomb
``` ```
To open it, do To open it, do
``` ```
$ tomb open secret.tomb -k secret.tomb.key $ tomb open -k secret.tomb.key secret.tomb
``` ```
and after you are done And after you are done
``` ```
$ tomb close $ tomb close
``` ```
or if you are in a hurry Or, if you are in a hurry, kill all processes with open files inside your tomb and close it.
``` ```
$ tomb slam all $ tomb slam
``` ```
## 📖 [Get started on dyne.org/tomb](https://dyne.org/tomb)
For the instructions on how to get started using Tomb, see [INSTALL](INSTALL.md). <a href="https://dyne.org/tomb"><img src="https://files.dyne.org/software_by_dyne.png" width="30%"></a>
``` More information in `man tomb` and on [dyne.org/docs/tomb](https://dyne.org/docs/tomb).
Syntax: tomb [options] command [arguments]
Commands: ### 💾 [Download from files.dyne.org/tomb](https://files.dyne.org/tomb/)
// Creation: Use only stable and signed releases in production!
dig create a new empty TOMB file of size -s in MB
forge create a new KEY file and set its password
lock installs a lock on a TOMB to use it with KEY
// Operations on tombs: Tomb's development is community-based!
open open an existing TOMB (-k specify KEY file)
index update the search indexes of tombs
search looks for filenames matching text patterns
list list of open TOMBs and information on them
close close a specific TOMB (or 'all')
slam slam a TOMB killing all programs using it
resize resize a TOMB to a new size -s (can only grow)
// Operations on keys: ## 🤏🏽 How can you help
passwd change the password of a KEY (needs old pass)
setkey change the KEY locking a TOMB (needs old key and pass)
// Backup on paper: Donations are very welcome on [dyne.org/donate](https://www.dyne.org/donate)
engrave makes a QR code of a KEY to be saved on paper
// Steganography: Translations are also welcome: see our simple [translation guide](https://github.com/dyne/Tomb/blob/master/extras/translations/README.md)
bury hide a KEY inside a JPEG image (for use with -k)
exhume extract a KEY from a JPEG image (prints to stout)
Options: Tomb's code is short and readable: don't be afraid to inspect it! If you plan to submit a PR, please remember that this is a minimalist tool, and the code should be short and readable. Also, first, read our small intro to [Tomb's coding style](doc/HACKING.txt).
-s size of the tomb file when creating/resizing one (in MB) We have a [space for issues](https://github.com/dyne/Tomb/issues) open for detailed bug reports. Always include the Tomb version being used when filing a case, please.
-k path to the key to be used ('-k -' to read from stdin)
-n don't process the hooks found in tomb
-o mount options used to open (default: rw,noatime,nodev)
-f force operation (i.e. even if swap is active)
--kdf generate passwords armored against dictionary attacks
-h print this help There is also a [space for discussion](https://github.com/dyne/Tomb/discussions) of new features, desiderata and whatnot on github.
-v print version, license and list of available ciphers
-q run quietly without printing informations
-D print debugging information at runtime
```
# What is this for, exactly?
This tool can be used to dig .tomb files (LUKS volumes), forge keys
protected by a password (GnuPG symmetric encryption) and use the keys
to lock the tombs. Tombs are like single files whose contents are
inaccessible in the absence of the key they were locked with and its
password.
Once open, the tombs are just like normal folders and can contain
different files, plus they offer advanced functionalities like bind
and execution hooks and fast search, or they can be slammed close even
if busy. Keys can be stored on separate media like USB sticks, NFC, or
bluetooth devices to make the transport of data safer: one always
needs both the tomb and the key, plus its password, to access it.
The tomb script takes care of several details to improve user's
behaviour and the security of tombs in everyday usage: secures the
typing of passwords from keyloggers, facilitates hiding keys inside
images, indexes and search a tomb's contents, lists open tombs and
selectively closes them, warns the user about free space and last time
usage, etc.
# How secure is this?
Death is the only sure thing in life. That said, Tomb is a pretty
secure tool especially because it is kept minimal, its source is
always open to review (even when installed) and its code is easy to
read with a bit of shell script knowledge.
All encryption tools being used in Tomb are included as default in
many GNU/Linux operating systems and therefore are regularly peer
reviewed: we don't add anything else to them really, just a layer of
usability.
The file [KNOWN_BUGS.md](KNOWN_BUGS.md) contains some notes on known
vulnerabilities and threat model analysis.
In absence or malfunction of the Tomb script it is always possible to
access the contents of a Tomb only using a dm-crypt enabled Linux
kernel, cryptsetup, GnuPG and any shell interpreter issuing the
following commands as root:
```
lo=$(losetup -f)
losetup -f secret.tomb
pass="$(gpg -d secret.key)"
echo -n -e "$pass" | cryptsetup --key-file - luksOpen $lo secret
mount /dev/mapper/secret /mnt
```
One can change the last argument `/mnt` to where the Tomb has to be
mounted and made accessible. To close the tomb then use:
```
umount /mnt
cryptsetup luksClose /dev/mapper/secret
```
# Stage of development
Tomb is an evolution of the 'mknest' tool developed for the
[dyne:bolic](http://www.dynebolic.org) 100% Free GNU/Linux
distribution in 2001: its 'nesting' mechanism allowed the liveCD users
to encrypt and make persistent home directories. Since then the same
shell routines kept being maintained and used for dyne:bolic until
2007, when they were ported to work on more GNU/Linux distributions.
As of today, Tomb is a very stable tool also used in mission critical
situations by a number of activists in dangerous zones. It has been
reviewed by forensics analysts and it can be considered to be safe for
military grade use where the integrity of information stored depends
on the user's behaviour and the strength of a standard AES-256 (XTS
plain) encryption algorithm.
# Use stable releases in production!
Anyone planning to use Tomb to store and access secrets should not use
the latest development version in Git, but use instead the .tar.gz
release on https://files.dyne.org/tomb . The stable version will
always ensure backward compatibility with older tombs: we make sure it
creates sane tombs and keys by running various tests before releasing
it. The development version in Git might introduce sudden bugs and is
not guaranteed to produce backward- or forward-compatible tombs and keys.
The development version in Git should be used to report bugs, test new
features and develop patches.
So be warned: do not use the latest Git version in production
environments, but use a stable release versioned and packed as
tarball on https://files.dyne.org/tomb
# How can you help
Donations are very welcome, please go to https://www.dyne.org/donate
Translations are also needed: they can be contributed via this website
https://poeditor.com/join/project/b276xMGAmB
or simply sending the .po file. Start from `extras/po/tomb.pot`.
The code is pretty short and readable: start looking around and the
materials found in `doc/` which are good pointers at security measures
to be further implemented.
For the bleeding edge visit https://github.com/dyne/Tomb
If you plan to commit code into Tomb, please keep in mind this is a
minimalist tool and its code should be readable. Guidelines on the
coding style are illustrated in [doc/HACKING.txt](doc/HACKING.txt).
Tomb's developers can be contacted using the issues on GitHub or over
IRC on https://irc.dyne.org channel **#dyne** (or direct port 9999 SSL)
# Licensing # Licensing
Tomb is Copyright (C) 2007-2016 by the Dyne.org Foundation Tomb is Copyright (C) 2007-2024 by the Dyne.org Foundation and maintained by [Jaromil](https://github.com/jaromil). The [AUTHORS](AUTHORS.md) file contains more information on all the developers involved. The license is GNU Public License v3.
More information on all the developers involved is found in the ## [More info on dyne.org/tomb](https://dyne.org/tomb)
[AUTHORS](AUTHORS.md) file.
This source code is free software; you can redistribute it and/or
modify it under the terms of the GNU Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This source code is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. Please refer
to the GNU Public License for more details.
You should have received a copy of the GNU Public License along with
this source code; if not, write to: Free Software Foundation, Inc.,
675 Mass Ave, Cambridge, MA 02139, USA.

View File

@ -4,11 +4,7 @@ Style guidelines
Indentation Indentation
----------- -----------
Code must be indented using four spaces. Code must be indented using hard tabs.
In vim, this can be accomplished using
set shiftwidth=4
set expandtab
Naming Naming
------ ------

View File

@ -1,13 +1,11 @@
Overview Overview
========= =========
What is a key?
What's a key? It is a gpg symmetrically encrypted, ascii-armored file.
It basicly is a gpg simmetrically encrypted, ascii-armored file. The encryption key is a function (see below, on KDF section) of your tomb
It's encryption key is a function (see below, on KDF section) of your tomb
passphrase. passphrase.
Layout Layout
====== ======

View File

@ -31,11 +31,11 @@ Linux hard disk encryption settings
and LRW for standardisation. EME along with it's cousin CMC seems to and LRW for standardisation. EME along with it's cousin CMC seems to
provide the best security level, but imposes additional encryption provide the best security level, but imposes additional encryption
steps. Plumb-IV is discussed only for reference, because it has the steps. Plumb-IV is discussed only for reference, because it has the
same performance penalty as CMC, but in constrast suffers from same performance penalty as CMC, but in contrast suffers from
weaknesses of CBC encryption. weaknesses of CBC encryption.
As convention, this document will use the term "blocks", when it As convention, this document will use the term "blocks", when it
referes to a single block of plain or cipher text (usually 16 byte), refers to a single block of plain or cipher text (usually 16 byte),
and will use the term "sectors", when it refers to a 512-byte wide hard and will use the term "sectors", when it refers to a 512-byte wide hard
disk block. disk block.
@ -171,8 +171,8 @@ Content leaks
cipher blocks. But how does this number grow in n? Obviously cipher blocks. But how does this number grow in n? Obviously
exponentially. Plotting a few a decimal powers shows that the chance exponentially. Plotting a few a decimal powers shows that the chance
for finding at least on identical cipher pair flips to 1 around n = for finding at least on identical cipher pair flips to 1 around n =
10^20 (n = 10^40 for a 256-bit cipher). This inflexion point is reached 10^20 (n = 10^40 for a 256-bit cipher). This inflection point is reached
for a 146 million TB storage (or a hundered thousand trillion trillions for a 146 million TB storage (or a hundred thousand trillion trillions
TB storage for a 256-bit cipher). TB storage for a 256-bit cipher).
^1The blocks with available preceding cipher blocks is 62/1KB for all ^1The blocks with available preceding cipher blocks is 62/1KB for all

View File

@ -9,8 +9,14 @@ Roadmap notes:
* Release 3.0 * Release 3.0
*** [#A] integrate the zenroom for custom crypto functions
https://decodeproject.github.io/lua-zenroom
*** [#A] study cryptsetup 2.0 and integrate it
In particular kernel keystore functionalities
*** [#A] support BtrFS and snapshots *** [#A] support BtrFS and snapshots
*** [#A] system to split passwords in parts (ssss)
*** [#B] modular encryption system support *** [#B] modular encryption system support
to go beyond dm-crypt/cryptsetup to go beyond dm-crypt/cryptsetup
@ -21,13 +27,21 @@ Roadmap notes:
*** [#B] udev rules to avoid usb automount of keyplug in gnome *** [#B] udev rules to avoid usb automount of keyplug in gnome
*** [#B] sign and verify tomb script integrity *** [#B] sign and verify tomb script integrity
*** [#B] make a graphical tomb undertaker (gnome-druid in glade?)
*** [#B] analyse and show tomb entropy using libdisorder *** [#B] analyse and show tomb entropy using libdisorder
*** [#B] use inotify on tomb *** [#B] use inotify on tomb
inotify can also count when was the last time tomb was used and inotify can also count when was the last time tomb was used and
unmount it automatically after a timeout, see how much free space unmount it automatically after a timeout, see how much free space
is left and warn when the space is almost finished is left and warn when the space is almost finished
*** DONE [#A] system to split passwords in parts
CLOSED: [2018-01-03 Wed 19:48]
solved with secrets.dyne.org
*** DONE [#B] make a graphical tomb undertaker (gnome-druid in glade?)
CLOSED: [2018-01-03 Wed 19:49]
solved by gtomb and qtomb
** Notes from #CybRes ** Notes from #CybRes

View File

@ -1,4 +1,4 @@
.TH tomb 1 "November 26, 2014" "tomb" .TH tomb 1 "Jun 25, 2023" "tomb"
.SH NAME .SH NAME
Tomb \- the Crypto Undertaker Tomb \- the Crypto Undertaker
@ -30,23 +30,27 @@ harddisk and its key file on a USB stick.
.IP "dig" .IP "dig"
Generates a file that can be used as a tomb and will occupy as much Generates a file that can be used as a tomb and will occupy as much
space as its desired initial size, the unlocked \fI.tomb\fR file can space as its desired initial size, the unlocked \fI.tomb\fR file can
then be locked using a \fIkey\fR. It takes a mandatory \fI-s\fR option which is then be locked using a \fIkey\fR. It takes a mandatory \fI-s\fR option
the size in megabytes (MiB). Tombs are digged using which is the size in megabytes (MiB). Tombs are digged using random
low-quality random data (/dev/urandom). data gathered from a non-blocking source (/dev/urandom). For very
large tombs this may take up too much time and entropy, then it is
possible to use \fIfallocate(1)\fR being aware it does not pre-fill
with random data, decreasing the tomb's security.
.B .B
.IP "forge" .IP "forge"
Creates a new \fIkey\fR and prompts the user for a \fIpassword\fR to Creates a new \fIkey\fR and prompts the user for a \fIpassword\fR to protect
protect its usage. This operation requires high quality random data its usage using symmetric encryption. This operation uses random data from a
(/dev/random) which can take quite some time to be gathered on a non-blocking source (/dev/urandom) and it may take long only in some cases; to
server: it works better on a desktop where the mouse can be moved switch using a blocking source the \fI--use-random\fR flag can be used. The
around for entropy. The default cipher to protect the key is AES256, a \fI-g\fR option switches on the use of a GPG key instead of a password
custom one can be specified using the \fI-o\fR option, for a list of (asymmetric encryption), then the \fI-r\fR option indicates the recipient key;
supported ciphers use \fI-v\fR. For additional protection against more recipient GPG ids can be indicated (comma separated). The default cipher
dictionary attacks on keys, the (experimental) \fI--kdf\fR option can to protect the key is AES256, a custom one can be specified using the \fI-o\fR
be used when forging a key, making sure that the \fItomb-kdb-pbkdf2\fR option, for a list of supported ciphers use \fI-v\fR. For additional protection
binaries in \fIextras/kdf\fR were compiled and installed on the against dictionary attacks on keys, the \fI--kdf\fR option can be used when
system. forging a key, making sure that the binaries in \fIextras/kdf\fR were compiled
and installed on the system.
.B .B
.IP "lock" .IP "lock"
@ -54,20 +58,48 @@ Initializes and locks an empty tomb (made with \fIdig\fR) using a key
(made with \fIforge\fR), making it ready for usage. After this (made with \fIforge\fR), making it ready for usage. After this
operation, the tomb can only be opened in possession of the key and operation, the tomb can only be opened in possession of the key and
knowing its password. As in any other command requiring a key, the knowing its password. As in any other command requiring a key, the
option \fI-k\fR should be used to specify a key file. The \fI-o\fR option \fI-k\fR should be used to specify a key file; in case of
encryption to GPG recipients the \fI-g\fR flag should be used followed
by \fI-r\fR and the recipient's secret GPG key id. The \fI-o\fR
option can be used to specify the cipher specification: default is option can be used to specify the cipher specification: default is
"aes-xts-plain64:sha256", old versions of Tomb used "aes-cbc-essiv:sha256". "aes-xts-plain64", old versions of Tomb used "aes-cbc-essiv:sha256".
This operation requires root privileges to loopback mount, format the tomb (using If you are looking for something exotic, also try
LUKS and Ext4), then set the key in its first LUKS slot. "serpent-xts-plain64". More options may be found in cryptsetup(8) and
Linux documentation. The \fI--filesystem\fR option can be used to
specify an alternative filesystem used to format the tomb,
in place of the default "ext4". This operation requires root
privileges to loopback mount, format the tomb (using LUKS and mkfs),
then set the key in its first LUKS slot.
.RS
Supported filesystems for \fI--filesystem\fR:
.PD 0
.IP "ext3" 15
using operating system defaults
.IP "ext4"
using operating system defaults
.IP "btrfs"
for tombs >= 47MB using operating system defaults
.IP "btrfsmixedmode"
for tombs >=18MB btrfs mixed mode (see mkfs.btrfs(8))
.IP "ext3maxinodes"
ext3 with a maximum of inodes (for many small files)
.IP "ext4maxinodes"
ext4 with a maximum of inodes (for many small files)
.PD
.RE
.B .B
.IP "open" .IP "open"
Opens an existing \fI.tomb\fR (first argument) using a key (\fI-k\fR), Opens an existing \fItomb file\fR (first argument) using a key
if a second argument is given it will indicate the \fImountpoint\fR (\fI-k\fR) which can also be hidden inside a \fIjpeg image\fR (see
where the tomb should be made accessible, else the tomb is mounted in \fIbury\fR/\fIexhume\fR) or a long text file
a directory inside /media (if not available it uses /run/media/$USER). (see\fIcloak\fR/\fIuncloak\fR). If a second argument is given it will
The option \fI-o\fR can be used to pass mount(8) options indicate the \fImountpoint\fR where the tomb should be made
(default: rw,noatime,nodev). accessible, else the tomb is mounted in a directory inside /media (if
not available it uses /run/media/$USER). The option \fI-o\fR can be
used to pass mount(8) options (default: rw,noatime,nodev). The
\fI-g\fR option is needed when using GPG encryption to recipients.
.B .B
.IP "list" .IP "list"
@ -78,23 +110,33 @@ returns an error if it's not found. If the option
\fI--get-mountpoint\fR is used then print a simple list of currently \fI--get-mountpoint\fR is used then print a simple list of currently
open tomb mountpoint paths. open tomb mountpoint paths.
.B
.IP "ps"
List all the processes found running inside the tombs that are open,
printing out their PIDs and owners. This is useful to have an overview
of programs that are keeping the tombs busy and would eventually be
killed by the \fIslam\fR command. The lsof(8) utility is used
internally to enumerate processes running in one or all tombs.
.B .B
.IP "index" .IP "index"
Creates or updates the search indexes of all tombs currently open: Creates or updates the search indexes of all tombs currently open:
enables use of the \fIsearch\fR command using simple word patterns on enables use of the \fIsearch\fR command using simple word patterns on
file names. Indexes are created using mlocate's updatedb(8) and file names. Indexes are created using plocate's updatedb(8) and
swish-e(1) if they are found on the system. Indexes allow to search recoll(1) if they are found on the system. Indexes allow one to search
very fast for filenames and contents inside a tomb, they are stored very fast for filenames and contents inside a tomb, they are stored
inside it and are not accessible if the Tomb is closed. To avoid inside it and are not accessible if the Tomb is closed. To avoid
indexing a specific tomb simply touch a \fI.noindex\fR file in it. indexing a specific tomb simply touch a \fI.noindex\fR file in it.
Useful tools to have: poppler-utils, aspell, xdg-utils, plocate.
.B .B
.IP "search" .IP "search"
Takes any string as argument and searches for them through all tombs Takes any string as argument and searches for them through all tombs
currently open and previously indexed using the \fIindex\fR command. currently open and previously indexed using the \fIindex\fR command.
The search matches filenames if mlocate is installed and then also The search matches filenames if plocate is installed and then also
file contents if swish++ is present on the system, results are listed file contents if recoll is installed, all results are listed on the
on the console. console.
One can also run recoll's GUI using \fIrecoll -c /media/tomb\fR
.B .B
.IP "close" .IP "close"
@ -108,36 +150,43 @@ the tomb is in use by running processes (to force close, see
.IP "slam" .IP "slam"
Closes a tomb like the command \fIclose\fR does, but it doesn't fail Closes a tomb like the command \fIclose\fR does, but it doesn't fail
even if the tomb is in use by other application processes: it looks even if the tomb is in use by other application processes: it looks
for and violently kills \-9 each of them. This command may for and closes each of them (in order: TERM, HUP, KILL). This command may
provoke unsaved data loss, but assists users to face surprise provoke unsaved data loss, but assists users to face surprise
situations. situations. It requires \fIlsof\fR else it falls back to \fIclose\fR.
.B .B
.IP "passwd" .IP "passwd"
Changes the password protecting a key file specified using Changes the password protecting a key file specified using
\fI-k\fR. The user will need to know the key's current password, then \fI-k\fR. With keys encrypted for GPG recipients use \fI-g\fR followed
its content will be decoded and reencoded using the new one. This by \fI-r\fR to indicate the new recipient key, or a comma separated
action can't be forced if the current password is not known. If the list.. The user will need to know the key's current password, or
key file is broken (missing headers) this function also attempts its possess at least one of the current recipients GPG secret keys,
recovery. because the key contents will be decoded and reencoded using the new
passwords or keys. If the key file is broken (missing headers) this
function also attempts its recovery.
.B .B
.IP "setkey" .IP "setkey"
Changes the key file that locks a tomb, substituting the old one with Changes the key file that locks a tomb, substituting the old one with
a new one. Both the old and the new key files are needed for this a new one. Both the old and the new key files are needed for this
operation and their passwords must be known. The new key must be operation and their passwords or GPG recipient(s) secret keys must be
specified using the \fI-k\fR option, the first argument should be the old available. The new key must be specified using the \fI-k\fR option,
key and the second and last argument the tomb file. the first argument should be the old key and the second and last
argument the tomb file. Use the \fI-g\fR option to unlock the tomb
with a GPG key, the \fI-r\fR to indicate the recipient or a comma
separated list for more than one recipient.
.B .B
.IP "resize" .IP "resize"
Increase the size of a tomb file to the amount specified by the Increase the size of a tomb file to the amount specified by the
\fI-s\fR option, which is the new size in megabytes (MiB). Full access to the tomb using \fI-s\fR option, which is the new size in megabytes (MiB). Full access
a key (\fI-k\fR) and its password is required. Tombs can only grow and to the tomb using a key (\fI-k\fR) and its password is required. Tombs
can never be made smaller. This command makes use of the cryptsetup can only grow and can never be made smaller. This command makes use of
resize feature and the resize2fs command: its much more practical than the cryptsetup(8) resize feature and the resize2fs command: its much
creating a new tomb and moving everything into it. more practical than creating a new tomb and moving everything into
it. There is no data-loss if a failure occurs during resize: the
command can be re-launched and the resize operation will complete.
.B .B
.IP "engrave" .IP "engrave"
@ -155,7 +204,9 @@ Hides a tomb key (\fI-k\fR) inside a \fIjpeg image\fR (first argument)
using \fIsteganography\fR: the image will change in a way that cannot using \fIsteganography\fR: the image will change in a way that cannot
be noticed by human eye and hardly detected by data analysis. This be noticed by human eye and hardly detected by data analysis. This
option is useful to backup tomb keys in unsuspected places; it depends option is useful to backup tomb keys in unsuspected places; it depends
from the availability of \fIsteghide\fR. from the availability of \fIsteghide\fR. Use the \fI-g\fR flag and
\fI-r\fR option followed by recipient id to use GPG asymmetric
encryption.
.B .B
.IP "exhume" .IP "exhume"
@ -166,27 +217,52 @@ containing a key. If the right key password is given, the key will be
exhumed. If the password is not known, it is very hard to verify if a exhumed. If the password is not known, it is very hard to verify if a
key is buried in any image or not. key is buried in any image or not.
.B
.IP "cloak"
Cloaks a tomb key (\fI-k\fR) disguising it as a text file using a
cipher from \fIextras/cloak/ciphers\fR (second argument) using
\fIcloakify\fR. This option is useful to backup tomb keys in
unsuspected places; it needs \fIextras/cloak\fR installed and
\fIpython3\fR.
.B
.IP "uncloak"
Recovers a tomb key from a cloaked text file. Uncloak requires a text
file (first argument), a cipher file (second argument) and optionally
an output file (third argument). If the first two parameters are
correct then the output will be a valid tomb key file restored from
cloak.
.SH OPTIONS .SH OPTIONS
.B .B
.B .B
.IP "-k \fI<keyfile>\fR" .IP "-k \fI<keyfile>\fR"
For all operations requiring a key, this option specifies the location For all operations requiring a key, this option specifies the location
of the key file to use. Arguments can also be \fIjpeg image\fR files of the key file to use. Arguments can also be \fIjpeg image\fR files
where keys have been hidden using the \fIbury\fR command, or text where keys have been hidden using the \fIbury\fR or \fIcloak\fR
files retrieved from \fIengraved\fR QR codes. If the \fIkeyfile\fR commands, or text files retrieved from \fIengraved\fR QR codes. If the
argument is "-" (dash), Tomb will read the key from stdin (blocking). \fIkeyfile\fR argument is "-" (dash), Tomb will read the key from
stdin (blocking).
.B .B
.IP "-n" .IP "-n"
Skip processing of post-hooks and bind-hooks if found inside the tomb. Skip processing of exec-hooks and bind-hooks if found inside the tomb.
See the \fIHOOKS\fR section in this manual for more information. See the \fIHOOKS\fR section in this manual for more information.
.B .B
.IP "-p"
When opening a tomb, preserves the ownership of all files and
directories contained in it. Normally the \fIopen\fR command changes
the ownership of a tomb's contents to the UID and GID of the user who
has successfully opened it: it is a usability feature in case a tomb is
used by a single user across different systems. This flag deactivates
this behaviour.
.B
.IP "-o" .IP "-o"
Manually specify mount options to be used when opening a tomb instead Manually specify mount options to be used when opening a tomb instead
of the default \fIrw,noatime,nodev\fR, i.e. to mount a tomb read-only of the default \fIrw,noatime,nodev\fR, i.e. to mount a tomb read-only
(ro) to prevent any modification of its data. Can also be used to (ro) to prevent any modification of its data. Can also be used to
change the symmetric encryption algorithm for keys during \fIforge\fR change the symmetric encryption algorithm for keys during \fIforge\fR
operations (default \fIAES256\fR) or the LUKS encryption method during operations (default \fIAES256\fR) or the LUKS encryption method during
\fIlock\fR operations (default \fIaes-xts-plain64:sha256\fR). \fIlock\fR operations (default \fIaes-xts-plain64\fR).
.B .B
.IP "-f" .IP "-f"
Force flag, currently used to override swap checks, might be Force flag, currently used to override swap checks, might be
@ -197,12 +273,40 @@ what you are doing if you force an operation.
When digging or resizing a tomb, this option must be used to specify When digging or resizing a tomb, this option must be used to specify
the \fIsize\fR of the new file to be created. Units are megabytes (MiB). the \fIsize\fR of the new file to be created. Units are megabytes (MiB).
.B .B
.IP "-g"
Tell tomb to use a asymmetric GnuPG key encryption instead of a
symmetric passphrase to protect a tomb key. This option can be
followed by \fI-r\fR when the command needs to specify recipient(s).
.B
.IP "-r \fI<gpg_id>[,<gpg_id2>]\fR"
Provide a new set of recipient(s) to encrypt a tomb key. \fIgpg_ids\fR
can be one or more GPG key ID, comma separated. All GPG keys must be
trusted keys in GPG.
.B
.IP "--kdf \fI<itertime>\fR" .IP "--kdf \fI<itertime>\fR"
Activate the KDF feature against dictionary attacks when creating a Activate the KDF feature against dictionary attacks when creating a key: forces
key: forces a delay of \fI<itertime>\fR seconds every time this key is used. a delay of \fI<itertime>\fR times every time this key is used. The actual time
You should keep in mind that the actual iteration count is calculated based on to wait depends on the CPU speed (default) or the RAM size (argon2) of the
the performance of the computer where you forge the key. computer where the key is used. Using 5 or 10 is a sane amount for modern
The argument must be an integer, so you cannot say \fI--kdf 0.3\fR for 300ms. computers, the value is multiplied by 1 million.
.B
.IP "--kdftype \fIargon2 | pbkdf2\fR"
Adopt the \fIargon2\fR algorithm for KDF, stressing the RAM capacity rather
than the CPU speed of the computer decrypting the tomb. Requires the
\fIargon2\fR binary by P-H-C to be installed, as packaged by most distros.
Default is \fIpbkdf2\fR.
.B
.IP "--kdfmem \fI<memory>\fR"
In case of \fIargon2\fR KDF algorithm, this value specifies the size of RAM
used: it consists of a number which is the elevated power of two in kilobytes.
Default is 18 which is 250 MiB (2^18 = 262,144 kilobytes).
.B
.IP "--sudo \fI<executable>\fR"
Select a different tool than sudo for privilege escalation.
Alternatives supported so far are: pkexec, doas, sup, sud. For any
alternative to work the executable must be included in the current
PATH.
.B .B
.IP "-h" .IP "-h"
Display a help text and quit. Display a help text and quit.
@ -227,9 +331,10 @@ Enable using dev-mode arguments, i.e. to pass passwords from
commandline options. This is mostly used needed for execution by commandline options. This is mostly used needed for execution by
wrappers and testing suite. wrappers and testing suite.
.B .B
.IP "--use-urandom" .IP "--use-random"
Use an inferior quality random source to improve the speed of key Use a blocking random source. Tomb uses by default /dev/urandom since
generation at the cost of security (needed for the testing suite). the non-blocking source of Linux kernel doesn't degrades the quality
of random.
.B .B
.IP "--tomb-pwd <string>" .IP "--tomb-pwd <string>"
Use string as password when needed on tomb. Use string as password when needed on tomb.
@ -251,17 +356,19 @@ Switch to this TTY terminal when dropping privileges.
Hooks are special files that can be placed inside the tomb and trigger Hooks are special files that can be placed inside the tomb and trigger
actions when it is opened and closed; there are two kinds of such actions when it is opened and closed; there are two kinds of such
files: \fIbind-hooks\fR and \fIpost-hooks\fR can be placed in the files: \fIbind-hooks\fR and \fIexec-hooks\fR can be placed in the
base root of the tomb. base root of the tomb.
.B .B
.IP "bind-hooks" .IP "bind-hooks"
This hook file consists of a simple two column list of files or This hook file consists of a simple text file named \fIbind-hooks\fR
directories inside the tomb to be made directly accessible inside the containing a two column list of paths to files or directories inside
current user's home directory. Tomb will use the "mount \-o bind" the tomb. The files and directories will be made directly
command to bind locations inside the tomb to locations found in $HOME accessible by the tomb \fIopen\fR command inside the current user's
so in the first column are indicated paths relative to the tomb and in home directory. Tomb uses internally the "mount \-o bind" command to
the second column are indicated paths relative to $HOME contents, for bind locations inside the tomb to locations found in $HOME. In the
first column are indicated paths relative to the tomb and in the
second column are indicated paths relative to $HOME contents, for
example: example:
.EX .EX
mail mail mail mail
@ -271,20 +378,21 @@ example:
.EE .EE
.B .B
.IP "post-hooks" .IP "exec-hooks"
This hook file gets executed as user by tomb right after opening it; This hook file gets executed as user by tomb with the first argument
it should be a regular shell script, starting with a shebang. Tomb determining the step of execution (\fIopen\fR or \fIclose\fR) and the second
executes this hook as user (dropping root privileges) and giving it being the full path to the mountpoint. The \fIexec-hooks\fR file should be
two arguments: "$1" is "open" or "close" depending from the tomb executable (ELF or shell script) and present inside the Tomb. Tomb
command given, "$2" is the full path to the mountpoint where the tomb executes this hook as user and adds the name, loopback device and
is open. dev-mapper device paths as additional arguments for the \fIclose\fR
command.
.SH PRIVILEGE ESCALATION .SH PRIVILEGE ESCALATION
The tomb commandline tool needs to acquire super user rights to The tomb commandline tool needs to acquire super user rights to
execute most of its operations: to do so it uses sudo(8), while execute most of its operations: so it uses sudo(8) or other configured
pinentry(1) is adopted to collect passwords from the user. Tomb tools, while pinentry(1) is adopted to collect passwords from the
executes as super user only when required. user. Tomb executes as super user only when required.
To be made available on multi user systems, the superuser execution of To be made available on multi user systems, the superuser execution of
the tomb script can be authorized for users without jeopardizing the the tomb script can be authorized for users without jeopardizing the
@ -294,14 +402,23 @@ whole system's security: just add such a line to \fI/etc/sudoers\fR:
username ALL=NOPASSWD: /usr/local/bin/tomb username ALL=NOPASSWD: /usr/local/bin/tomb
.EE .EE
To avoid that tomb execution is logged by \fIsyslog\fR also add:
.EX
Cmnd_Alias TOMB = /usr/local/bin/tomb
Defaults!TOMB !syslog
.EE
.SH PASSWORD INPUT
Password input is handled by the pinentry program: it can be text Password input is handled by the pinentry program: it can be text
based or graphical and is usually configured with a symlink. When based or graphical and is usually configured with a symlink. When
using Tomb in X11 it is better to use a graphical pinentry-gtk2 or using Tomb in a graphical environment (X11 or Wayland) it is better
pinentry-qt because it helps preventing keylogging by other X to use either pinentry-gtk2 (deprecated), pinentry-gnome or
clients. When using it from a remote ssh connection it might be pinentry-qt because it helps preventing keylogging by other clients.
necessary to force use of pinentry-curses for instance by unsetting When using it from a remote ssh connection it might be necessary to
the DISPLAY environment var. force use of pinentry-tty for instance by unsetting the DISPLAY (X11)
or WAYLAND_DISPLAY (Wayland) environment var.
.SH SWAP .SH SWAP
@ -324,6 +441,38 @@ If you don't need swap, execute \fI swapoff -a\fR. If you really need
it, you could make an encrypted swap partition. Tomb doesn't detect if it, you could make an encrypted swap partition. Tomb doesn't detect if
your swap is encrypted, and will complain anyway. your swap is encrypted, and will complain anyway.
.SH DENIABILITY
The possibility to have an encrypted volume which is invisible and
cannot be detected is called "deniability". The cryptographic layer of
the device mapper in Linux (dm-crypt) does not implement
deniability. Tomb is just a wrapper on top of that and it doesn't add
cryptographic deniability. However a certain way of using tomb can
facilitate a weak sort of deniability outside of the scenario of
seized devices and forensic analysis of files and blocks on disc.
For instance to eliminate any trace of tomb usage from the shell
history ZSh users can activate the "HISTIGNORESPACE" feature and
prefix all invocations of tomb with a blank space, including two lines
in ".zshrc":
.EX
export HISTIGNORESPACE=1
alias tomb=' tomb'
.EE
.SH SHARE A TOMB
A tomb key can be encrypted with more than one recipient. Therefore, a
tomb can be shared between different users. The recipients are given
using the \fI-r\fR (or/and \fI-R\fR) option and if multiple each GPG
key ID must be separated by a comma (\fI,\fR). Sharing a tomb is a
very sensitive action and the user needs to trust that all the GPG
public keys used are kept safe. If one of them its stolen or lost, it
will be always possible to use it to access the tomb key unless all
its copies are destroyed. The \fI-r\fR option can be used in the tomb
commands: \fIopen\fR, \fIforge\fR \fIsetkey\fR, \fIpasswd\fR,
\fIbury\fR, \fIexhume\fR and \fIresize\fR.
.SH EXAMPLES .SH EXAMPLES
.IP \(bu .IP \(bu
@ -373,13 +522,14 @@ keeping all its profile data inside it:
.EX .EX
tomb open FOX.tomb -k FOX.tomb.key tomb open FOX.tomb -k FOX.tomb.key
cat <<EOF > /media/FOX.tomb/post-hooks cat <<EOF > /media/FOX.tomb/exec-hooks
#!/bin/sh #!/bin/sh
if [ "$1" = "open" ]; then if [ "$1" = "open" ]; then
firefox -no-remote -profile "$2"/firefox-pro & firefox -no-remote -profile "$2"/firefox-pro &
fi fi
EOF EOF
chmod +x /media/FOX.tomb/post-hooks chmod +x /media/FOX.tomb/exec-hooks
mkdir /media/FOX.tomb/firefox-pro
.EE .EE
.IP \(bu .IP \(bu
@ -390,7 +540,7 @@ Script a tomb to archive Pictures using Shotwell, launching it on open:
cat <<EOF > /media/Pictures.tomb/bind-hooks cat <<EOF > /media/Pictures.tomb/bind-hooks
Pictures Pictures Pictures Pictures
EOF EOF
cat <<EOF > /media/Pictures.tomb/post-hooks cat <<EOF > /media/Pictures.tomb/exec-hooks
#!/bin/sh #!/bin/sh
if [ "$1" = "open" ]; then if [ "$1" = "open" ]; then
which shotwell > /dev/null which shotwell > /dev/null
@ -399,7 +549,7 @@ if [ "$1" = "open" ]; then
fi fi
fi fi
EOF EOF
chmod +x /media/Pictures.tomb/post-hooks chmod +x /media/Pictures.tomb/exec-hooks
.EE .EE
.SH BUGS .SH BUGS
@ -407,30 +557,12 @@ Please report bugs on the Github issue tracker at
.UR https://github.com/dyne/Tomb/issues .UR https://github.com/dyne/Tomb/issues
.UE .UE
One can also try to get in touch with developers via the #dyne chat channel on \fIhttps://irc.dyne.org\fR. One can also try to get in touch with developers via the #dyne chat
channel on \fIhttps://irc.dyne.org\fR.
.SH AUTHORS
Tomb is designed, written and maintained by Denis Roio aka Jaromil.
Tomb includes code by Anathema, Boyska, Hellekin O. Wolf and GDrooid.
Tomb's artwork is contributed by Jordi aka Mon Mort and Logan VanCuren.
Gettext internationalization and Spanish translation is contributed by
GDrooid, French translation by Hellekin, Russian translation by fsLeg,
German translation by x3nu.
Testing, reviews and documentation are contributed by Dreamer, Shining
the Translucent, Mancausoft, Asbesto Molesto, Nignux, Vlax, The Grugq,
Reiven, GDrooid, Alphazo, Brian May, TheJH, fsLeg, JoelMon and the
Linux Action Show!
Cryptsetup was developed by Christophe Saout and Clemens Fruhwirth.
.SH COPYING .SH COPYING
This manual is Copyright (c) 2011-2015 by Denis Roio <\fIjaromil@dyne.org\fR> This manual is Copyright (c) 2011-2021 by Denis Roio <\fIjaromil@dyne.org\fR>
This manual includes contributions by Boyska and Hellekin O. Wolf. This manual includes contributions by Boyska and Hellekin O. Wolf.
@ -451,15 +583,13 @@ documentation is available for download from its website on
.B .B
.IP cryptsetup(8) .IP cryptsetup(8)
.B
.IP pinentry(1)
.B
.IP gpg-agent(1)
GnuPG website: GnuPG website: https://www.gnupg.org
.br
https://www.gnupg.org
DM-Crypt website: DM-Crypt website: https://gitlab.com/cryptsetup/cryptsetup/wikis/DMCrypt
.br
https://gitlab.com/cryptsetup/cryptsetup/wikis/DMCrypt
LUKS website: LUKS website: https://gitlab.com/cryptsetup/cryptsetup/wikis/home
.br
https://gitlab.com/cryptsetup/cryptsetup/wikis/home

Binary file not shown.

18
extras/android/README.txt Normal file
View File

@ -0,0 +1,18 @@
This is an experimental port of Tomb to Android platform
REQUIRES ROOT - see on-line docs on how to root Android.
This includes a static binary of cryptsetup to interact with the
dm-crypt Linux API for filesystem cryptography (ELF for ARMv6)
Tombs can be created and opened correctly
Only aes-cbc-essiv:sha254 crypto algo is supported.
At the moment tombs cannot be closed.
One has to reboot the device for that.
Other functions have not been tested.
More than ever, this is provided WITHOUT ANY WARRANTY

2760
extras/android/tomb Executable file

File diff suppressed because it is too large Load Diff

5
extras/cloak/Makefile Normal file
View File

@ -0,0 +1,5 @@
PREFIX ?= /usr/local
install:
install -Dm755 cloakify.py ${DESTDIR}${PREFIX}/bin/cloakify
install -Dm755 decloakify.py ${DESTDIR}${PREFIX}/bin/decloakify

3
extras/cloak/README.md Normal file
View File

@ -0,0 +1,3 @@
# Cloakify steganography into text files
Original repo: https://github.com/asrabon/Cloakify-3

View File

@ -0,0 +1,66 @@
Allophrynidae
Pipidae
Dicroglossidae
Myobatrachidae
Mavortium
Indotyphlidae
Elongatus
Croceater
Xanthoptica
Telmatobiidae
Arthroleptidae
Phrynobatrachidae
Hynobiidae
Rivularis
Torosa
Ranidae
Leptodactylidae
Dendrobatidae
Alsodidae
Eleutherodactylidae
Bufonidae
Craugastoridae
Ambystomatidae
Hylodidae
Croceum
Sierrae
Ambystoma
Micrixalidae
Nyctibatrachidae
Taricha
Typhlonectidae
Attenuatus
Brachycephalidae
Dicamptodon
Platycephalus
Ichthyophiidae
Centrolenidae
Plethodontidae
Oregonensis
Hyperoliidae
Batrachoseps
Hylidae
Rhacophoridae
Diabolicus
Ensatina
Gavilanensis
Nigriventris
Californiense
Rhyacotritonidae
Salamandridae
Altasierrae
Plethodon
Megophryidae
Sigillatum
Variegatus
Tenebrosus
Microhylidae
Pacificus
Odontobatrachidae
Ceratobatrachidae
Odontophrynidae
Strabomantidae
Mantellidae
Hydromantes
Siphonopidae
Pyxicephalidae

View File

@ -0,0 +1,66 @@
Nondedju
St. Paul Double
Lesage Dubbel
La Namuroise
Saint-Monon Ambrée
Belle-Vue Kriek Classique
Floris Framboise
Corsendonk Triple 11.11.11
Keizer Karel Robijn Rood
Waase Wolf
Horst bier
Morpheus Tripel
Mageleno
Gordon Finest Copper
St. Benoit Blonde
Waterloo Tripel 7 Blond
Totentrekker
Molse Tripel
Geuze Mariage Parfait
Quest
Limerick
Buffalo Bitter
Mongozo Palmnut
La Waterlootoise
Serafijn Tripel
Lindemans Apple
Sint-Gummarus Tripel
Steendonk
Chimay Wit
Podge Oak Aged Stout
Liefmans Frambozenbier
Louwaege Faro
Ypres
Hapkin
Affligem 950 Cuvee
Caulier Brune
Hoppe
Joseph
Florilège de Rose
Saison de Dottignies
Shark Pants
Pikkeling Tripel
Den Twaalf
Antiek Blond
Belgoo Luppoo
Cambrinus
Adelardus
La Rulles Blonde
Kapel van Viven Blond
Montagnarde Altitude 6
Ramée Triple Blond
Abbay d'Aulne Triple Blonde
't Smisje Calva Reserva
De Koninck Winter
Holger
Zonderik 100
Elliot Brew
Abbay d'Aulne Val de Sambre
Sur-les-Bois Blonde
Schaarbeekse Oude Kriek 3 Fonteinen
Lustem
Rodenbach
Vossen met de Meynen Blond
Xenophons Wine
Westhoek XX
Brussels Fruit Beer "Red Fruit"

View File

@ -0,0 +1,107 @@
honey
jelly
lollipop
spumoni
milkshake
shortcake
souffle
flower
fondant
crunch
pineapple
marionberry
lime
pudding
sugar
caramel
granita
zest
brittle
liquer
bun
toffee
ginger
custard
cookie
sucker
pistachio
meringue
eggs
peach
buttermilk
turnover
biscuits
turtle
puffs
doughnut
apricot
nutmeg
gingerbread
cherry
truffle
turnovers
licorice
mousse
muffins
raspberry
sorbet
streusel
candy
torte
syrup
terrine
curd
hazelnut
brownie
strawberries
blueberry
coconut
butterscotch
cookies
huckleberry
icing
walnut
pie
snickerdoodles
cannoli
marzipan
cake
compote
bonbon
glaze
flan
cane
foster
sherbet
ganache
cream
buttercream
jam
cobbler
tirimisu
creme
cupcake
cinnamon
mint
vanilla
éclair
taffy
orange
almond
rhubarb
pastry
brulee
lemon
cheesecake
chocolate
donut
sundae
peach pie
shortbread
frosting
parfaits
blackberry
popsicle
confection
crepe
macaroon

View File

@ -0,0 +1,100 @@
سلحفاة
كاب كيك
ترين
معجنات
مرزبانية حلوى لوز وسكر
اللبن المخفوق
مخيض اللبن
كعكة فواكه
القاناش
مصاصة
ايكلير
زهرة
أقراص سكرية
دوران
بروليه
عرق السوس
المصاصة
بقلمي
مشمش
نفث
الإسكافي
تبرع
تيريميسو
كلس
تلذذ
.توت
لوز
كعكة الغريبة
فراولة
كرز
جنية سمراء صغيرة
تحولات
خبز الزنجبيل
سطح أملس
فستق
تورتى
كومبوت
نعناع
نبات
شوكولاتة
قصب
كعكة
كريم
كريب
مرنغ
نفيخة
الكعك
البون بون
كاسترد
أناناس
الكرمل
يكر
عسل
الزبد
شراب مركز
السكر
شربات
خوخ
جوزة الطيب
الموسية
سكريات الطوفي حلوى
راوند
بندق
تخثر
فطيرة الجبن
سبوموني
الحاضنة
زنجبيل
فطيرة
كوكي
البسكويت
بسكويت
جرانيتا
صقيع
البرتقالي
جوزة الهند
بودنغ
حلوى
الكمأة
توت العُليق
معكرون نوع حلوي
كانولي
فطيرة الخوخ
سحق
هش
قرفة
ليمون
هلام
جوز
الفانيليا
بارفيتس
مثلجات
إبله
بلاك بيري
[ستريوسل]
حلوى من سكر أسمر وزبدة
الكعك المحلى بالسكر
مربى
بيض
تثليج

View File

@ -0,0 +1,94 @@
蛋糕
甜甜圈
草莓
焦糖
糖浆
越橘
酥皮
蛋白杏仁饼干
榛子
乳蛋糕
果仁蛋糕
皮匠
香草
培育
周转
糖粉奶油细末
杯形饼
棒糖
软糖
酸橙
棒冰
核桃
覆盆子
包子
果盘
樱桃
乳酪蛋糕
紧缩
糖果
柠檬
松露
失误
奶油曲奇
巧克力
奶糖
桃子馅饼
甘草
凝乳
杂音
太妃糖
热情
圣代
冻糕
布朗尼
格兰尼塔
果酱
果冻
布丁
鸡蛋
黑莓
开心果
奶昔
肉桂
甘蔗
薄荷
糕点
杏仁
肉豆蔻
冰糕
松饼
果子露
菠萝
大黄
蓝莓
沙锅
结霜
邦邦
蜜饯
吸盘
姜饼
香炸奶酪卷
奶油
伽纳彻
曲奇饼
椰子
果馅饼
馅饼
摩丝
积冰
泡芙
蜂蜜
饼干

View File

@ -0,0 +1,84 @@
केक
बादाम का मीठा हलुआ
टुकड़े
कुचले हुए फल
जायफल
कलाकंद
कुकी
कप केक
दालचीनी
क्रेप
कारोबार
चीज़केक
खुबानी
बटरस्कॉच
सिरप
टाफ़ी
जाम
जिंजरब्रेड
नारंगी
कुकीज़
ठंडा करना
ब्राउनी
पाई
शहद
क्रीम
एक प्रकार का फल
बिस्कुट
आड़ू
चीनी
अदरक
मिठाई
टोफ़ी
नद्यपान
नाज़ुक
चेरी
चूसने की मिठाई
वनीला
दही
दिलचस्पी
गन्ना
पिस्ता
चॉकलेट
मोची
गनाचे
अंडे
शीशे का आवरण
कचौड़ी
पेस्ट्री
छाछ
जेली
नारियल
बन
चूना
कश
की कमी
फ़्लान
कारमेल
नींबू
शर्बत
आड़ु पाई
मिल्कशेक
फूल
डोनट
स्ट्रॉबेरीज
पालक
अखरोट
ब्लूबेरी
पुदीना
मलाई
मानसिक शांति
हेज़लनट
ब्लैकबेरी
बादाम
रसभरी
कैंडी
मूस
टर्नओवर
बॉन बॉन
कछुआ
चूसने वाला
कस्टर्ड
कुकुरमुत्ता
पुडिंग
अनानास

View File

@ -0,0 +1,92 @@
آناناس
تخم مرغ
بستنی چوبی
حجم معاملات
بستنی و مغز گردو
نان شیرینی مرکب از شکر و زرده تخم مرغ و بادام
فندق
زنجبیل
ژله
گردو
دونات
پینه دوز
لاک پشت
مستی
زغال اخته
پسته
سرخ
طفل شیرخوار
گل
میلک شیک
شیرینی زنجفیلی
شکننده
جوز
نارگیل
پودینگ
نارنجی
ابدوغ
کلوچه
کمپوت
بحران
شکلات
هلو
شربت
کرپ
کیک تخم مرغ و شکر و مغز گردو
تمشک
لیمو شیرین
عسل
گردش
زردالو
کیک میوه
پای
کیک
بستنی میوه
تافی
کرم رنگ
کوکی
اب نبات چوبی
نوعی کیک میوه دار
توت سیاه
ریواس
فاستر
وانیل
نبات
بیسکویت
قارچ خوراکی دنبلان
لیکور
شیرینی
شکر و تخم مرغ
بستنی میوه و مغز بادام و میوه جات ایتالیایی
یکجور دوربین عکاسی
شیرین بیان
کماج
فوندانت
نیشکر
کرم
پاف
قند
نعناع
موس
گرانیت
دارچین
اهک
پوشش
گراوند
بادام
فیالبداهه
پای هلو
شیرینی خامهدار و بستنی دار
کیک پنیر
نوعی کیک کوچک
توت فرنگی
رنگ زرد
مربا
سوفله
کاستارد
درخت زغال اخته
کشک
گیلاس
مزه
نان روغنی
لعاب

View File

@ -0,0 +1,90 @@
присоска
праздничный торт
малина
трюфель
лесной орех
леденец
кремовый
песочное печенье
ананас
мускатный орех
лайм
печенье
сироп
абрикос
лимон
кондитерские изделия
миндальное печенье
эклер
конфета
имбирный пряник
чизкейк
ваниль
крем
сапожник
флан
цветок
мусс
творог
шоколад
брюле
хруст
кексы
черепаха
карамель
имбирь
обороты
иней
желе
пирог
яйца
домовой
фисташковый
парфе
варенье
сахар
кекс
грецкий орех
глазурь
мята
обледенение
затяжек
булочка
эскимо
конфеты
тростник
ликер
вишня
кокос
суфле
персиковый пирог
корица
помадка
молочный коктейль
пудинг
черника
ежевика
ревень
меренга
ириски
персик
ломкий
пломбир
ириска
пончик
миндальный
блюдо из дичи
марципан
лакрица
способствовать
заварной крем
мед
компот
пахта
цедра
оборот
канноли
оранжевый
шербет
клубника
креп

View File

@ -0,0 +1,104 @@
boon
meelksheke-a
brooneee-a
mooffffeens
coosterd
bleckberry
ceennemun
tureemisoo
teffffy
cubbler
guneche-a
fundunt
pooddeeng
perffeeets
shurtbreed
meceruun
syroop
flooer
meent
suooffffle-a
cuukeee-a
merzeepun
bootterscutch
creme-a
soondee-a
streoosel
creem
ceremel
cherry
sooger
shurtceke-a
hoockleberry
hezelnoot
geenger
oorunge-a
tuffffee-a
muoosse-a
cunnulee
sherbet
jelly
cheeseceke-a
chuculete-a
cune-a
coopceke-a
cuukeees
surbet
crepe-a
iceeng
duooghnoot
pestry
toortle-a
mereengooe-a
gruneeta
leeme-a
epreecut
sneeckerduudles
toornufers
beescooits
gleze-a
rhooberb
huney
coord
lulleepup
bunbun
dunoot
ceke-a
breettle-a
croonch
leeqooer
frusteeng
pupseecle-a
spoomunee
cundy
pooffffs
soocker
blooeberry
toornufer
nootmeg
broolee-a
lemun
cunffecshun
terreene-a
respberry
elmund
turte-a
velnoot
mereeunberry
fuster
peee-a
peech
trooffffle-a
cumpute-a
leecurice-a
cucunoot
peeneepple-a
jem
geengerbreed
iggs
flun
zest
peestechiu
strevberreees
écleur
funeella

View File

@ -0,0 +1,86 @@
นมผสมไอศกรีม
ไข่
แห้ว
เปลือกน้ำฅาล
ครีม
ผลไม้ชนิดหนึ่ง
ผลไม้แช่อิ่ม
ขนมเมอร์แรง
ฉาบ
ความเอร็ดอร่อย
คุกกี้
โดนัท
ลูกจันทน์เทศ
นมเปรี้ยว
พาย
ขนมชนิดร่วน
ไอติม
ราสเบอร์รี่
ทอฟฟี่
เฮเซลนัท
ขนมปิ้ง
อ้อย
ทำเหรียญ
น้ำผึ้ง
มะพร้าว
กาละแม
อุปถัมภ์
บัตเตอร์
อาหารตีให้เป็นฟอง
ขนมปังขิง
เชอร์รี่
พุดดิ้ง
ผักชนิดหนึ่ง
มวย
ผลประกอบการ
สีส้ม
ต้นมันฮ่อ
ลูกอม
อัลมอนด์
ขนมแมคะรูน
สตรอเบอร์รี่
เครื่องปรุงอาหารมาร์ซิปัน
มะนาว
ขนมปังกรอบ
ลูกกวาด
คัพเค้ก
ฅกประหม่า
ผี
อบเชย
ขนมชนิดหนึ่ง
น้ำเชื่อม
เต่า
แอปริคอท
ขนมเค้กเนยแข็ง
พาย ลูกพีช
เครื่องดื่มเชอร์เบ็ท
วุ้น
เค้ก
กระทืบ
ช็อคโกแลต
มูสส์
แพรย่น
ผลไม้ดำนำ้เงินลูเล็ก ๆ
ขิง
คัสตาร์
ไอศกรีมใส่ผลไม้
แยม
เครื่องดูด
มัฟฟิน
ขนมที่ติดกับปลายไม้
ดอกไม้
น้ำตาล
หลากสี
บลูเบอร์รี่
พีช
วานิลลา
เชอร์เบท
คุ้กกี้
พิสตาเชีย
เคลือบน้ำแข็ง
เปราะ
พัฟ
พายผลไม้
พืชชะเอ็ม
สับปะรด
บัตเตอร์ครีม

View File

@ -0,0 +1,70 @@
😀
😴
😜
😝
😩
😃
😛
😕
😏
👊
🙉
😿
😅
😁
😭
🐱
😸
🙌
😓
🙈
🐨
😽
🐵
😮
😈
😆
😑
😶
🐔
😂
😪
😥
🙆
😲
🙇
😾
😹
🙍
😗
😳
😼
😧
🐸
🙀
🖖
😌
😟
🙅
😬
🙊
🐼
😄
😣
🐻
😻
😱
😯
😷
😊
🐂
🐰
😵
😰
🙏
🙎
😺
🙋
😫
😡
😙

View File

@ -0,0 +1,65 @@
4
v
q
m
0
B
j
u
E
e
g
J
w
y
Y
Q
D
H
t
x
i
3
6
f
O
C
9
N
a
T
Z
F
A
5
G
M
h
s
o
7
l
U
k
R
L
X
n
r
W
d
P
K
S
I
c
=
+
/
8
V
2
1
p
b
z

View File

@ -0,0 +1,199 @@
18°21'N 64°56'W
05°50'N 55°10'W
00°15'S 78°35'W
12°06'N 86°20'W
13°50'S 171°50'W
14°36'N 61°02'W
59°55'N 10°45'E
43°52'N 18°26'E
50°30'N 30°28'E
37°31'N 126°58'E
29°18'S 27°30'E
06°55'N 158°09'E
51°40'S 59°51'W
37°58'N 23°46'E
01°59'S 30°04'E
25°10'S 57°30'W
04°00'N 73°28'E
35°10'N 33°25'E
05°35'N 00°06'W
22°35'S 17°04'E
03°16'S 29°18'E
14°40'N 121°03'E
02°02'N 45°25'E
52°23'N 04°54'E
09°24'S 147°08'E
18°40'N 72°20'W
46°04'N 14°33'E
12°00'S 77°00'W
35°54'N 14°31'E
27°31'N 89°45'E
23°08'N 82°22'W
15°31'N 32°35'E
17°20'N 61°48'W
21°05'N 105°55'E
09°00'N 79°25'W
33°40'N 73°10'E
49°26'N 02°33'W
14°00'S 33°48'E
59°22'N 24°48'E
53°00'S 74°00'E
24°45'S 25°57'E
18°28'N 66°07'W
15°28'S 28°16'E
53°52'N 27°30'E
42°01'N 21°26'E
25°58'S 32°32'E
09°05'N 07°32'E
11°40'S 43°16'E
00°25'N 09°26'E
22°17'S 166°30'E
25°05'N 77°20'W
19°20'N 99°10'W
36°30'S 60°00'W
45°50'N 15°58'E
09°02'N 38°42'E
31°57'N 35°52'E
17°32'S 149°34'W
12°10'N 14°59'E
14°16'S 170°43'W
35°15'S 149°08'E
01°17'S 36°48'E
06°23'N 02°42'E
12°15'N 01°30'W
41°19'S 174°46'E
04°23'N 18°35'E
46°46'N 56°12'W
24°28'N 54°22'E
18°00'N 76°50'W
40°25'N 03°45'W
14°05'N 87°14'W
15°02'N 23°34'W
13°28'N 16°40'W
23°43'N 90°26'E
42°45'N 23°20'E
09°55'N 84°02'W
12°05'N 69°00'W
49°37'N 06°09'E
48°12'N 16°22'E
41°43'N 44°50'E
40°10'N 44°31'E
04°34'N 74°00'W
41°20'N 69°10'E
13°10'N 61°10'W
41°54'N 12°29'E
41°18'N 19°49'E
39°55'N 116°20'E
44°27'N 26°10'E
46°57'N 07°28'E
08°31'S 179°13'E
24°41'N 46°42'E
50°51'N 04°21'E
35°44'N 51°30'E
12°34'N 07°55'W
04°52'N 115°00'E
40°29'N 49°56'E
27°45'N 85°20'E
21°10'S 174°00'W
43°55'N 12°30'E
42°31'N 01°32'E
18°30'N 69°59'W
33°20'N 44°30'E
05°05'N 52°18'W
04°20'S 15°15'E
53°21'N 06°15'W
00°10'N 06°39'E
17°17'N 62°43'W
54°38'N 25°19'E
32°49'N 13°07'E
47°02'N 28°50'E
30°01'N 31°14'E
14°34'N 17°29'W
34°28'N 69°11'E
15°47'S 47°55'W
13°45'N 100°35'E
08°50'S 13°15'E
20°10'S 57°30'E
00°20'N 32°30'E
16°45'N 96°20'E
64°10'N 51°35'W
12°48'S 45°14'E
11°08'N 42°20'E
64°10'N 21°57'W
55°45'N 37°35'E
28°37'N 77°13'E
03°09'N 101°41'E
60°15'N 25°03'E
03°45'N 08°50'E
15°12'N 145°45'E
15°19'N 38°55'E
09°29'N 13°49'W
56°53'N 24°08'E
29°30'N 48°00'E
17°43'S 1°02'E
16°20'S 68°10'W
08°30'N 13°17'W
31°47'N 35°12'E
45°20'S 168°43'E
48°10'N 17°07'E
06°49'N 05°17'W
22°12'N 113°33'E
06°18'N 10°47'W
25°44'S 28°12'E
38°33'N 68°48'E
48°50'N 02°20'E
10°30'N 66°55'W
18°27'N 64°37'W
36°50'N 10°11'E
17°45'S 168°18'E
11°33'N 104°55'E
08°29'S 125°34'E
52°13'N 21°00'E
51°36'N 00°05'W
33°30'N 36°18'E
62°05'N 06°56'W
26°10'N 50°30'E
15°20'N 61°24'W
42°54'N 74°46'E
39°91'N 77°02'W
06°50'N 58°12'W
01°30'N 173°00'E
39°57'N 32°54'E
44°50'N 20°37'E
16°00'N 61°44'W
47°29'N 19°05'E
17°58'N 102°36'E
18°55'S 47°31'E
06°09'N 01°20'E
11°45'N 15°45'W
33°53'N 35°31'E
13°40'N 89°10'W
34°50'S 56°11'W
17°18'N 88°30'W
06°09'S 106°49'E
47°08'N 09°31'E
51°10'N 71°30'E
45°27'N 75°42'W
52°30'N 13°25'E
13°27'N 02°06'E
38°00'N 57°50'E
50°05'N 14°22'E
14°02'N 60°58'W
14°40'N 90°22'W
03°50'N 11°35'E
13°05'N 59°30'W
33°24'S 70°40'W
07°20'N 134°28'E
36°42'N 03°08'E
38°42'N 09°10'W
12°32'N 70°02'W
18°06'S 178°30'E
06°08'S 35°45'E
59°20'N 18°03'E
09°27'S 159°57'E
04°09'S 15°12'E
23°37'N 58°36'E
25°15'N 51°35'E
39°09'N 125°30'E
19°20'N 81°24'W
26°18'S 31°06'E

View File

@ -0,0 +1,82 @@
Carley State Park N 44 º 06.717 W 092 º 10.390
Scenic State Park N 47 º 42.700 W 093 º 34.167
Lake Bronson State Park N 48 º 43.472 W 096 º 37.545
Myre-Big Island State Park N 43 º 38.226 W 093 º 18.541
Interstate State Park N 45 º 23.518 W 092 º 40.199
Tettegouche State Park N 47 º 21.592 W 091 º 16.939
Garden Island State Recreation Area N 49 º 10.537 W 094 º 50.031
Nerstrand Big Woods State Park N 44 º 20.533 W 093 º 06.339
Bear Head Lake State Park N 47o 47.787 W 092o 03.858
Cascade River State Park. N 47 º 42.660 W 090 º 30.643
Mille Lacs Kathio State Park N 46 º 08.132 W 093 º 43.520
Schoolcraft State Park N 47 º 13.390 W 093 º 48.252
Frontenac State Park N 44 º 31.428 W 092 º 20.467
Temperance River State Park N 47 º 33.241 W 090 º 52.498
Cuyuna Country State Recreation Area N 46 º 28.724 W 093 º 58.598
Browns Creek State Trail N 45 º 04.531 W 092 º 49.776
Minnesota Valley State Trail N 44 º 46.901 W 093 º 35.350
Lake Louise State Park N 43 º 31.739 W 092 º 31.452
Itasca State Park N 47 º 11.488 W 095 º 10.199
Split Rock Creek State Park N 43 º 53.664 W 096 º 21.941
Kilen Woods State Park N 43 º 43.858 W 095 º 04.101
Zippel Bay State Park N 48 º 50.891 W 094 º 50.859
Split Rock Lighthouse State Park N 47 º 11.912 W 091 º 22.479
George H. Crosby Manitou State Park N 47 º 28.732 W 091 º 06.703
Glendalough State Park N 46 º 19.233 W 095 º 40.287
Gateway State Trail N 45 º 03.522 W 092 º 55.627
Sakatah Lake State Park N 44 º 13.504 W 093 º 31.181
Hill Annex Mine State Park N 47 º 19.585 W 093 º 16.697
Root River State Trail N 43 º 46.298 W 091 º 38.153
Lake Bemidji State Park N 47 º 32.079 W 094 º 49.590
La Salle Lake State Recreation Area N 47 º 20.697 W 095 º 09.507
Franz Jevne State Park N 48 º 38.526 W 094 º 03.773
Minnesota Valley State Recreation Area N 44 º 39.144 W 093 º 42.849
Sibley State Park N 45 º 18.865 W 095 º 02.355
Blazing Star State Trail N 43 º 38.226 W 093 º 18.541
Judge C.R. Magney State Park N 47 º 49.082 W 090 º 03.173
Iron Range Off-Highway Vehicle State Recreation Area N 47 º 28.721 W 092 º 26.319
Upper Sioux Agency State Park N 44o 44.203 W 095 º 27.571
Blue Mounds State Park N 43 º 41.474 W 096 º 11.807
Luce Line State Trail N 44 º 59.163 W 093 º 29.651
Fort Snelling State Park N 44o 53.349 W 093o 11.014
Fort Ridgely State Park N 44 º 27.193 W 094 º 43.607
Hayes Lake State Park N 48 º 38.257 W 095 º 32.739
Charles A. Lindbergh State Park N 45 º 57.548 W 094 º 23.337
Lake Maria State Park N 45 º 19.070 W 093 º 56.139
Gooseberry Falls State Park N 47 º 08.463 W 091 º 28.207
Savanna Portage State Park N 46 º 49.716 W 093 º 09.031
Grand Portage State Park N 48 º 00.200 W 089 º 35.657
Greenleaf Lake State Recreation Area N 45 º 00.536 W 094 º 28.491
Forestville/Mystery Cave State Park N 43 º 38.425 W 092 º 13.110
Big Stone Lake State Park N 45 º 23.126 W 096 º 32.099
Lake Vermilion-Soudan Underground Mine State Park N 47 º 49.197 W 092 º 14.320
Red River State Recreation Area N 47 º 55.743 W 097 º 01.723
Great River Bluffs State Park N 46 º 51.919 W 096 º 28.031
Crow Wing State Park N 46 º 16.389 W 094 º 19.972
Beaver Creek Valley State Park N 43 º 38.571 W 091 º 34.872
Shooting Star State Trail N 43 º 31.547 W 092 º 30.986
Glacial Lakes State Park N 45 º 32.438 W 095 º 31.430
Flandrau State Park N 44 º 17.596 W 094 º 28.065
Lac Qui Parle State Park N 45 º 02.620 W 095 º 53.025
Wild River State Park N 45 º 32.139 W 092 º 43.947
Lake Shetek State Park N 44 º 06.248 W 095 º 41.103
Maplewood State Park N 46 º 32.361 W 095 º 59.151
Old Mill State Park N 48 º 21.691 W 096 º 34.011
Banning State Park N 46 º 09.869 W 092 º 50.373
Jay Cooke State Park N 46 º 39.271 W 092 º 22.270
Camden State Park N 44 º 20.787 W 095 º 55.735
Afton State Park N 44 º 51.336 W 092 º 46.484
Minneopa State Park N 44 º 08.879 W 094 º 05.540
Rice Lake State Park N 44 º 05.576 W 093 º 03.849
McCarthy Beach State Park N 47 º 40.200 W 093 º 01.830
Whitewater State Park N 44 º 03.206 W 092 º 02.703
Monson Lake State Park N 45 º 19.164 W 095 º 16.493
Father Hennepin State Park N 46 º 08.614 W 093 º 28.948
Lake Carlos State Park N 46 º 00.052 W 095 º 20.073
Moose Lake State Park N 46 º 26.109 W 092 º 43.949
John A. Latsch State Park N 44 º 10.310 W 091 º 50.319
St. Croix State Park N 45 º 57.048 W 092 º 34.198
Big Bog State Recreation Area N 48 º 10.203 W 094 º 30.901
Garden Island State Recreation Area Alternate N 48 º 52.073 W 094 º 50.896
William OBrien State Park N 45 º 13.373 W 092 º 46.047
Buffalo River State Park N 46 º 51.919 W 096 º 28.031

View File

@ -0,0 +1,113 @@
033698705E420DD71634D5C8A861DE7E
0566F25FC73E4ACC826C84FF521EDAF7
05D5668D5CF9A65FAABE2186C0B19001
0991756FBCC2D438CDDD07E7BA996C4F
0A12376A0B0A65C39299D94EB02FE799
0A26C74B6012B5BBBF715B9327C00C44
0BF2D9B8F392C853EE41F15006A38839
0F5F49305B40F326674AD23AED0554BC
0F82D00A63498CCE8982D5608B759F9E
10515EC6CF372232E995602BB7A70046
1080418C171BB86F202DA3EB234C74A1
108B7B6D86873256A1958916035872E9
10B43971A8295F3720F38FBCDD9D6AC6
137609CBF5410CFDC487FC2118FB16AE
13D1261EFBCEAE62EC6B65F3257769A4
15BB21591BE151CAADCC264D3602E522
1ACC555ADC3E731133EEAA80154BB886
1B4E227142C24B81C9EAA61122B12CB1
1C63129AE9DB9C60C3E8AA94D3E00495
1D1E60D5E0B698FF150DCAB24DF184D5
1F7A1132D93837D2A83E4CA8FABA9941
2345F10BB948C5665EF91F6773B3E455
236B922AEA10AF085C5206E4D5DBCC85
26B580B0AEDE3B3E8B58BC761E09577C
26E059C26592E4A078CCB31AFEDF213C
26FEE1830A3552695A975E509B289F0D
2902A7FD778812E75F953779B67BE83C
2AAE667514F164221A71E9CEB734E31A
2B92AD5136C0C171DF9B02A24D3D9C82
332D114301906A70E56EDB1325B192E3
3362E5D0E467010F8FFAFE6E079B3FCC
35E656E1FCF2FC8FBCAC23EA148E7BD0
3B287E2F82D9B57BD0801638A21B6EB6
40068DCA5800AD8BC5E1EA749B5A968F
447CC066DE9703C5AEE3FDD3AF0AD5E0
45C311F94E075EF1F15EFBA7B5084A10
498A8A3364A43A8E9D880E441043E33F
4AAEA6397CFA277E4433FA325FA396F7
4DC642D0E0881B63072691C194F99B7E
51951570A66679F7B5C21A83E3385D13
5263DC84FFBA8FA607754CACCE3E65FD
57AC36ADC5258C1B8048A7D7BD966A73
5836CAD35828419B754B684A9DA147A0
596BFF793A503D75B68B760A9BF70AA3
5E5B18D4681B24F342F89A19DA2D4699
604E708741886E3D922D26CE1A7B2835
618206DD0078A5740373C161D7570989
64B9469496B3012D4E19F626AC068233
64F4AE7B5A81F0EB65316410581047C0
6676E7D0995EBD8DBD136869A9358D14
69BDBA2238286ED674D2DBD66319B629
69F3EEC589EFF77854287AF84E0258FE
6FC4B922446021AD57578E4E281D7965
722EF044BF1D7A293440B68A6E7B6721
744CE343D3FAF9FBFA81116688714F3B
791701316B125F94CC61F8C2D9FF9C58
7989D7CFAF00CAB7D0B6A58324EB63E9
7A2D09AF8F40467DDDAF0F946960ADE3
7AD7E9A563E9F3CA7175DE3668C85654
7BCD03390834DFF914740E27D826C2DA
7E5FAF44E6D49A0000135A9D5D59EF76
7EDEB90A6ED6E0141E9F28FACA7F3AF6
8350ADB55BAA13F09BFB53B0D51CAF2D
859C7547DF3CA77B07D4ECD3B0F42F57
865BDF0F2727792BCD77C7BE5B36C6CA
8745BA34F93696B1165C36A52FAC0FDF
8904646023577F9746BADE425D317ABE
8911BFAF73444B67B000B3D3EDB55E6D
8A548185B6A6A873D9243072B13D04AB
8B0EE5A501CEF4A5699FD3B2D4549E8F
8BD2DB1BE8AABBC2CE9AE757D0302707
939E652393234AE78B511092C5B3A0B6
93FE91EB27123C4927105753C4DB4B7D
9536637DF923B3AAD6DDE0A80D7CD7E4
9684C7747527BFA45DE2D0E733058D9F
970796E057AD9284F16AF55E19BDCE7F
9A0711EC9F302EDE9C07731208553E69
9FEA03AA1B4293B8566B1B5AEAE12D8F
A304C99480E027B2D2AE5A9D10021335
A33B929706F3280EC6D46BE67E569915
A402080405F59AAD7E3C66B2339B1311
A4DC1273708D83FCF39355AFA587E2EC
A5484EEFDBC11797A293DA331B31C62B
A59D3A8442AD87289D6891238CD9C7DD
A8A4C199766A66EA54079F19F819EE8D
A8EA4070D693DB509DECA6676DE675E9
AA13ABDB56EA5FD37B133F96EB3E3B6A
AB6A89F36224022B8A954316C55137DC
AEBA8FB7353C9C4F9B27F015FB9DC5AD
B13A1C9325803C9258F868EF97F04F8D
B32FD22E4BFAD91974A40D08EBC7D8B1
B55549D6177C833E55DFA04F6A3650DC
B79313B4FE11BF40EA80915AD676BE5E
B80370360E1E0D5F7BA9709B12A01DEA
BAE0168A5446D0B00D13B5CEC4D6F905
BBA3BAFE548581C53B933B7DCCE620AB
C03AA55846E82AEEADF1879065BAF7ED
C44A471BD78CC6C2FEA32B9FE028D30A
C5B68394786DF93CF7AD7366D3D5807C
CD00B006870EABFBB56135FFAF2AC444
CD8032768CFBD94F5C5876BA56EB78CD
D27E2ABDB7A8F23407B803E04EB5AD90
DB99B1E9EE0E4BB159F9256820214634
E0F79190404F264B77DEA125D5FC8304
E12B91EF812074FFD54663280739B825
F0B2231D462E970D954AA661122D1034
F55893FDF4409E1C6C2574C8D3005A6E
F5E852552DEB0FF9A3899938E77CCCDE
F89684B8DD9E68E3261E617D772ECF0E
FA767CDB99CE06491731B69DDEEFBD69
FCC96DF2D80B43831F5926BE58F63574
FDB8250C494E39F3820B7C81DA1418A7
FE4CEEB01D43A6C29D8F4FE93313C6C1

View File

@ -0,0 +1,77 @@
198.78.201.252
209.200.154.225
69.171.224.11
144.198.29.112
69.65.13.216
208.87.33.151
209.31.22.39
69.63.181.15
69.63.187.17
65.55.72.135
208.94.146.80
74.125.157.99
69.63.181.12
178.162.238.136
64.208.126.67
72.247.244.88
69.10.25.46
178.17.165.74
69.63.176.13
97.107.137.164
131.253.13.32
98.124.248.77
199.59.148.10
195.191.207.40
194.71.107.15
31.7.57.13
69.63.187.18
212.58.241.131
208.80.152.201
80.94.76.5
93.158.65.211
89.238.130.247
173.231.140.219
199.7.177.218
216.239.113.172
95.211.149.7
174.121.194.34
173.0.84.3
74.125.224.72
207.97.227.239
95.211.143.200
91.220.176.248
199.59.149.230
174.140.154.20
74.125.65.91
65.39.178.43
62.149.24.66
74.125.224.181
69.63.181.11
199.47.217.179
72.233.56.138
97.107.132.144
84.22.170.149
62.149.24.67
69.174.244.50
109.163.226.240
23.21.142.179
216.52.208.187
69.63.187.19
98.139.183.24
76.74.254.126
205.196.120.13
98.137.149.56
65.55.175.254
199.9.249.21
69.63.184.142
216.52.242.86
184.173.141.231
208.223.219.206
194.71.107.50
67.201.54.151
64.191.203.30
174.140.154.32
72.21.211.176
67.21.232.223
23.23.130.59
69.171.234.21

View File

@ -0,0 +1,76 @@
Bellsprout
Zubat
Doduo
Rhyhorn
Dratini
Snorlax
Krabby
Onix
Ponyta
Paras
Lapras
Drowzee
Zapdos
Diglett
Sandshrew
Ekans
Lickitung
Tangela
Scyther
Oddish
Geodude
Slowpoke
Voltorb
Magmar
Pidgey
Caterpie
Nidoran
Poliwag
Shellder
Koffing
Seel
Mew
Growlithe
Machop
Vulpix
Porygon
Mankey
Hitmonchan
Farfetchd
Kabuto
Tauros
Venonat
Articuno
Clefairy
Psyduck
Jynx
Squirtle
Horsea
Jigglypuff
Mewtwo
Pinsir
Hitmonlee
Goldeen
Kangaskhan
Moltres
Cubone
Magnemite
Staryu
Weedle
Charmander
Pikachu
Omanyte
Tentacool
Spearow
Grimer
Gastly
Abra
Magikarp
Rattata
Aerodactyl
Bulbasaur
Electabuzz
Meowth
Ditto
Chansey
Exeggcute

View File

@ -0,0 +1,67 @@
https://bit.ly/1bu2Ruu
https://bit.ly/1ijxdRx
https://bit.ly/1FQbMVt
https://bit.ly/2nQmPXd
https://bit.ly/2oq4DYI
https://bit.ly/2Kb9fsx
https://bit.ly/1f9lqAP
https://bit.ly/IqT6zt
https://bit.ly/2HjNryg
https://bit.ly/2efiE68
https://bit.ly/2geoqTg
https://bit.ly/2xZrYlX
https://bit.ly/29wawO1
https://bit.ly/2IvdUsg
https://bit.ly/2Guggqi
https://bit.ly/1p9Gf4X
https://bit.ly/2jQX9JX
https://bit.ly/2KT5CbZ
https://bit.ly/2gkkzU2
https://bit.ly/2ItVvMx
https://bit.ly/2I9RflR
https://bit.ly/2FFZyE3
https://bit.ly/2uGlEgu
https://bit.ly/2jP0U2S
https://bit.ly/2IvQJOs
https://bit.ly/2lqrm1G
https://bit.ly/R0z0jZ
https://bit.ly/1yA76u9
https://bit.ly/1zTtQZr
https://bit.ly/2Iwbcmh
https://bit.ly/2I92hnl
https://bit.ly/2IcYpC2
https://bit.ly/2I5fm56
https://bit.ly/2KcvsXq
https://bit.ly/2KRVKPD
https://bit.ly/2jN3VjX
https://bit.ly/2KS8mqi
https://bit.ly/1QasgsE
https://bit.ly/2ryu1eN
https://bit.ly/2ff2sPq
https://bit.ly/2I5eIVr
https://bit.ly/2G472wA
https://bit.ly/2rxYokQ
https://bit.ly/2eO3vYp
https://bit.ly/2jLbl7w
https://bit.ly/2rxVPQq
https://bit.ly/1UisbYb
https://bit.ly/2ry3luH
https://bit.ly/2G3vWNb
https://bit.ly/1iegU5b
https://bit.ly/2G3wnHj
https://bit.ly/2I6dYPX
https://bit.ly/2rxWXn8
https://bit.ly/2wxIYCQ
https://bit.ly/2jO8lY8
https://bit.ly/2jQbuXg
https://bit.ly/29yCC7s
https://bit.ly/2KOGFyi
https://bit.ly/2KTRv6q
https://bit.ly/2IvLePW
https://bit.ly/2jP6ylA
https://bit.ly/2KcD3VQ
https://bit.ly/1NistpZ
https://bit.ly/1Qz4NnI
https://bit.ly/2Ib66bT
https://bit.ly/2Bj7825
https://bit.ly/2rzRuLR

View File

@ -0,0 +1,68 @@
Niseko, Japan
Jackson Hole, Wyoming
Squaw Valley, California
Kirkwood, California
Alpine Meadows, California
Grand Targhee Resort, Wyoming
Red Mountain Resort, British Columbia
Big Bear, California
Alagna, Italy
Grand Targhee, Wyoming
La Hoya, Chile
Mountain High, California
Meribel, France
Kitzbühel, Austria
Breckenridge, Colorado
Heavenly, California & Nevada
Taos, New Mexico
Canyons, Utah
Murren, Switzerland
Snowbasin, Utah
Incline Village, Nevada
Winter Park, Colorado
Shiga Kogen, Japan
Park City, Utah
Smugglers Notch, Vermont
Mt. Schweitzer, Idaho
Bernese Oberland, Switzerland
Sierra-at-Tahoe, California
Aspen-Snowmass, Colorado
Riksgransen, Sweden
Telluride, Colorado
Solitude, Utah
Mammoth Mountain, California
Wolf Creek Ski Area
Stowe, Vermont
Craigieburn, New Zealand
Big Sky, Montana
Crested Butte, Colorado
Jay Peak, Vermont
Tremblant, Quebec
Verbier, Switzerland
Cortina dAmpezzo, Italy
Mount Snow, Vermont
Vail, Colorado
Keystone, Colorado
Sun Valley, Idaho
Timberline, Mt Hood Oregon
Silverton, Colorado
Wanaka, New Zealand
Brighton, Utah
St. Anton, Austria
Mt. Rose, Nevada
Okemo Mountain Resort, Vermont
Copper Mountain, Colorado
Northstar, California
Beaver Creek, Colorado
Whistler-Blackcomb, British Columbia
Sundance, Utah
Nozawa Onsen, Japan
Deer Valley, Utah
Whiteface Mountain, New York
Purgatory, Colorado
Mt. Bachelor, Oregon
Killington, Vermont
Snowbird, Utah
Revelstoke, British Columbia
Treble Cone, New Zealand
Alta, Utah

View File

@ -0,0 +1,147 @@
Ishka
Nog
Charles Tucker
William Riker
Dolim
Lon Suder
Brunt
Hogan
J. M. Colt
Winn Adami
Leonardo da Vinci
Jake Sisko
Azan
Amanda Grayson
Weyoun
Ayala
Nyota Uhura
Tuvok
Saavik
Lwaxana Troi
Gowron
José Tyler
Miles O'Brien
Kashimuro Nozawa
Soval
William Ross
Shakaar Edon
Kathryn Janeway
Jonathan Archer
Keiko O'Brien
Kimara Cretak
Julian Bashir
Dukat
Spock
Alexander Rozhenko
Seska
Evek
Sonya Gomez
Tora Ziyal
Damar
Chakotay
Mezoti
Hugh of Borg
Sela
Thy'lek Shran
Leonard McCoy
Michael Rostov
Jennifer Sisko
Janice Rand
Daniels
Degra
Beverly Crusher
Kasidy Yates
Reginald Barclay
The Doctor
Kes
Jal Culluh
Rom
Mallora
Elim Garak
Silik
Neelix
Michael Jonas
Phlox
The Borg Queen
Benjamin Sisko
Kurn
Hoshi Sato
Mot
K'Ehleyr
Guinan
Erika Hernandez
B'Etor
Leeta
Harry Kim
James T. Kirk
Joseph Sisko
Tal Celes
The Traveler
Samantha Wildman
Rebi
Morn
Lursa
Luther Sloan
Female Changeling
Susan Nicoletti
Naomi Wildman
Mr. Homn
Katherine Pulaski
Phillip Boyce
Ezri Dax
Christopher Pike
Carol Marcus
Mora Pol
Kira Nerys
Vash
T'Pol
Hikaru Sulu
Jean-Luc Picard
Bareil Antos
Wesley Crusher
Number One
Geordi La Forge
Montgomery Scott
Lore
Garrison
Jannar
Ro Laren
Zek
Icheb
Tomalak
Vorik
Elizabeth Cutler
Maxwell Forrest
Maihar'du
Vic Fontaine
Owen Paris
Michael Eddington
Malcolm Reed
Li Nalas
Pavel Chekov
Travis Mayweather
B'Elanna Torres
Worf
Tom Paris
Sarah Sisko
Jadzia Dax
Mila
Data
Q
Seven of Nine
Christine Chapel
Alyssa Ogawa
Joseph Carey
Molly O'Brien
Sarek
Martok
J. Hayes
Kor
Enabran Tain
Robin Lefler
Deanna Troi
Quark
Chell
Tasha Yar
Opaka Sulan
Odo

View File

@ -0,0 +1,77 @@
Status code: 60
Status code: 612
Status code: 299
Status code: 725
Status code: 974
Status code: 472
Status code: 182
Status code: 256
Status code: 203
Status code: 619
Status code: 915
Status code: 746
Status code: 77
Status code: 857
Status code: 865
Status code: 488
Status code: 115
Status code: 159
Status code: 782
Status code: 500
Status code: 962
Status code: 333
Status code: 938
Status code: 395
Status code: 320
Status code: 821
Status code: 894
Status code: 898
Status code: 287
Status code: 871
Status code: 870
Status code: 575
Status code: 867
Status code: 216
Status code: 116
Status code: 937
Status code: 662
Status code: 593
Status code: 423
Status code: 748
Status code: 647
Status code: 62
Status code: 817
Status code: 452
Status code: 212
Status code: 946
Status code: 471
Status code: 401
Status code: 527
Status code: 935
Status code: 131
Status code: 119
Status code: 868
Status code: 776
Status code: 762
Status code: 706
Status code: 268
Status code: 331
Status code: 106
Status code: 787
Status code: 89
Status code: 134
Status code: 681
Status code: 793
Status code: 971
Status code: 355
Status code: 28
Status code: 31
Status code: 559
Status code: 715
Status code: 501
Status code: 385
Status code: 197
Status code: 709
Status code: 344
Status code: 205
Status code: 892

View File

@ -0,0 +1,71 @@
office.com
imgur.com
microsoft.com
outbrain.com
amazon.com
reddit.com
aol.com
netflix.com
bankofamerica.com
force.com
wordpress.com
bing.com
foxnews.com
wellsfargo.com
google.com
blogspot.com
cnet.com
homedepot.com
instagram.com
live.com
etsy.com
stackoverflow.com
imdb.com
github.com
facebook.com
huffingtonpost.com
craiglist.org
walmart.com
diply.com
quora.com
wikia.com
pinterest.com
groupon.com
bestbuy.com
pandora.com
tripadvisor.com
msn.com
amazonaws.com
indeed.com
tumblr.com
apple.com
xfinity.com
comcast.net
capitalone.com
citi.com
ebay.com
go.com
espn.go.com
yelp.com
zillow.com
vice.com
salesforce.com
washingtonpost.com
chase.com
forbes.com
buzzfeed.com
cnn.com
microsoftonline.com
twitter.com
americanexpress.com
paypal.com
target.com
wikipedia.com
nytimes.com
dropbox.com
weather.com
youtube.com
usps.com
bbc.com
yahoo.com
linkedin.com

View File

@ -0,0 +1,100 @@
Balos Beach, Greece
Oludeniz Beach, Turkey
Hanalei Bay, Hawaii, United States
Ffryes Beach, Antigua
Wineglass Bay, Tasmania
Sunrise Beach, Koh Lipe, Thailand
Coffee Bay, Wild Coast, South Africa
Hot Water Beach, Coromandel Peninsula, New Zealand
Bandon, Oregon, United States
Little Corn beaches, Nicaragua
Lover's Beach, Baja California Sur, Mexico
Pulau Derawan, Indonesia
Akajima, Okinawa, Japan
Trunk Bay, St. John, U.S. Virgin Islands
Navagio Beach, Greece
Flamenco Beach, Puerto Rico
Plage de Piémanson, France
Natadola Beach, Fiji
Cayo Paraiso, Dominican Republic
Unawatuna, Sri Lanka
Los Roques, Venezuela
Champagne Beach, Vanuatu
Kaiteriteri Beach, Nelson, New Zealand
Matira Beach, Bora Bora, Tahiti
Anse de Grande Saline, St. Barths
Puka Beach, Boracay, Philippines
Laughing Bird Caye, Belize
El Nido, Palawan, Philippines
Anse Source d'Argent, La Digue, Seychelles
Beidaihe, China
Arashi Beach, Aruba
An Bang Beach, Hoi An, Vietnam
Juara Beach, Tioman Island, Malaysia
D-Day beaches, Normandy, France
Rarotonga, Cook Islands
West Bay Beach, Roatan, Honduras
Cavendish Beach, Prince Edward Island, Canada
Ifaty Beach, Madagascar
Grande Anse Beach, La Digue Island, Seychelles
Diani Beach, Kenya
Dominical Beach, Costa Rica
Bahia Solano, Colombia
Patnem Beach, Goa, India
Tanjung Rhu, Langkawi, Malaysia
Essaouira, Morocco
Tulum, Mexico
Belle Mare, Mauritius
Long Beach, Phu Quoc, Vietnam
Egremni Beach, Greece
Bondi Beach, Sydney, Australia
Isshiki Beach, Hayama, Japan
Paradise Beach, Rab, Croatia
Grace Bay, Providenciales, Turks & Caicos
Abaka Bay, Haiti
Warwick Long Bay, Bermuda
Phra Nang Beach, Railay, Thailand
Karekare, West Auckland, New Zealand
Gardner Bay, Espanola Island, Ecuador
Meads Bay, Anguilla
Panama City Beach, Florida, United States
Boulders Beach, Cape Town, South Africa
La Concha, Spain
Rabbit Beach, Lampedusa, Italy
Byron Bay, Australia
Cape Maclear, Malawi
Southwestern Beach, Koh Rong, Cambodia
Canggu Beach, Bali, Indonesia
Capo Sant'Andrea, Elba, Italy
Cabbage Beach, Paradise Island, Bahamas
Margaret River Beach, Australia
Crane Beach, Barbados
Radhanagar Beach, Andaman Islands, India
Jeffreys Bay, South Africa
Luskentyre Beach, Scotland
Maya Bay, Ko Phi Phi, Thailand
Long Bay, Saint-Martin
Portstewart Strand, Northern Ireland
Vilanculos Beach, Mozambique
Las Salinas, Ibiza, Spain
Grand Anse, Grenada
Sun Island Beach, Maldives
Whitehaven Beach, Queensland, Australia
Placenia Beach, Belize
Pigeon Point, Tobago, Trinidad and Tobago
Punalu'u, Hawaii, United States
The Baths, Virgin Gorda, British Virgin Islands
Porto da Barra, Salvador, Brazil
Nungwi, Zanzibar, Tanzania
Skagen Beach, Denmark
Palaui Island, Cagayan Valley, Philippines
Negril Beach, Jamaica
Falassarna Beach, Crete, Greece
Nihiwatu Beach, Sumba, Indonesia
Playa Paraiso, Cayo Largo, Cuba
Bottom Bay, Barbados
Na'ama Bay, Sharm el Sheikh, Egypt
Haad Rin, Ko Pha Ngan, Thailand
Pulau Perhentian Kecil, Malaysia
Praia do Sancho, Fernando de Noronha, Brazil
Venice Beach, California, United States

View File

@ -0,0 +1,100 @@
Celta Vigo Spain
Rangers Scotland
Rosario Central Argentina
Roma Italy
Santos FC Brazil
Dinamo Zagreb Croatia
Málaga Spain
Liverpool FC England
FC Porto Portugal
Sevilla Spain
Al-Ahli Saudi Arabia
Feyenoord Netherlands
Saint-Étienne France
Corinthians Brazil
Borussia Mönchengladbach Germany
Sport Recife Brazil
Everton FC England
Lyon France
Sassuolo Italy
Schalke 04 Germany
Internacional Brazil
Ajax Netherlands
Valencia Spain
Lille France
Villarreal Spain
Real Sociedad Spain
Levadia Tallinn Estonia
Leicester City England
Nice France
Flora Tallinn Estonia
Ludogorets Razgrad Bulgaria
Atlético Madrid Spain
BATE Borisov Belarus
Club Brugge Belgium
Köln Germany
Arsenal England
APOEL Nicosia Cyprus
Anderlecht Belgium
São Paulo Brazil
Salzburg Austria
Dynamo Kyiv Ukraine
América Mexico
Maribor Slovenia
PSV Eindhoven Netherlands
Barcelona Spain
Monaco France
West Ham United England
Wolfsburg Germany
SSC Napoli Italy
Guangzhou Evergrande China PR
Paris Saint Germain France
Crvena Zvezda Serbia
Fenerbahçe Turkey
Boca Juniors Argentina
FC Sheriff Moldova
CSKA Moskva Russia
Manchester United England
AZ Alkmaar Netherlands
Lazio Italy
Inter Milan Italy
Zenit St. Petersburg Russia
Benfica Portugal
Basel Switzerland
Tottenham Hotspur England
Bayer Leverkusen Germany
Estudiantes Argentina
Sporting Portugal
Augsburg Germany
Swansea City England
Genoa Italy
Celtic Scotland
Racing Club Argentina
Olympiakos Greece
Juventus Italy
Grêmio Brazil
Mainz 05 Germany
Sparta Prague Czech Republic
Nomme JK Kalju Estonia
The New Saints Wales
Fiorentina Italy
Borussia Dortmund Germany
Dnipro Dnipropetrovsk Ukraine
Beşiktaş Turkey
Olympique Marseille France
Atlético Mineiro Brazil
Manchester City England
Athletic Bilbao Spain
Viktoria Plzeň Czech Republic
FC Krasnodar Russia
AC Milan Italy
Southampton England
Chelsea England
San Lorenzo Argentina
Independiente Argentina
Atlético Nacional Colombia
Lanús Argentina
Real Madrid Spain
Cruzeiro Brazil
Bayern München Germany
Shakhtar Donetsk Ukraine

111
extras/cloak/cloakify.py Normal file
View File

@ -0,0 +1,111 @@
#!/usr/bin/env python3
#
# Filename: cloakify.py
#
# Version: 1.1.0
#
# Author: Joe Gervais (TryCatchHCF)
#
# Summary: Exfiltration toolset (see decloakify.py) that transforms any filetype (binaries,
# archives, images, etc.) into lists of words / phrases / Unicode to ease exfiltration of
# data across monitored networks, hiding the data in plain sight. Also facilitates social
# engineering attacks against human analysts and their workflows. Bonus Feature: Defeats
# signature-based malware detection tools (cloak your other tools during an engagement).
#
# Used by cloakifyFactory.py, can be used as a standalone script as well (example below).
#
# Description: Base64-encodes the given payload and translates the output using a list
# of words/phrases/Unicode provided in the cipher. This is NOT a secure encryption tool,
# the output is vulnerable to frequency analysis attacks. Use the Noise Generator scripts
# to add entropy to your cloaked file. You should encrypt the file before cloaking if
# secrecy is needed.
#
# Prepackaged ciphers include: lists of desserts in English, Arabic, Thai, Russian,
# Hindi, Chinese, Persian, and Muppet (Swedish Chef); PokemonGo creatures; Top 100 IP
# Addresses; Top Websites; GeoCoords of World Capitols; MD5 Password Hashes; An Emoji
# cipher; Star Trek characters; Geocaching Locations; Amphibians (Scientific Names);
# evadeAV cipher (simple cipher that minimizes size of the resulting obfuscated data).
#
# To create your own cipher:
#
# - Generate a list of at least 66 unique words (Unicode-16 accepted)
# - Remove all duplicate entries and blank lines
# - Randomize the list (see 'randomizeCipherExample.txt' in Cloakify directory)
# - Provide the file as the cipher argument to the script.
# - ProTip: Place your cipher in the "ciphers/" directory and cloakifyFactory
# will pick it up automatically as a new cipher
#
# Example:
#
# $ ./cloakify.py payload.txt ciphers/desserts > exfiltrate.txt
#
import argparse
import base64
import os
import random
import sys
array64 = list("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789/+=")
def Cloakify(payloadPath:str, cipherPath:str, outputPath:str="", password:str=None):
"""Payload file's binary contents will be read and converted into base64.
Cipher file will be read into a list that will be used for the payload's obfuscation.
If an output path is defined the obfuscated content will be written to that otherwise,
it will print it out to the console.
Args:
payloadPath (str): Path to the file that will be encoded
cipherPath (str): Path to the file used as the base64 cipher
outputPath (str): Path to write out the obfuscated payload
"""
try:
with open(payloadPath, 'rb') as payloadFile:
payloadRaw = payloadFile.read()
payloadB64 = base64.encodebytes(payloadRaw)
payloadB64 = payloadB64.decode("ascii").replace("\n", "")
except Exception as e:
print("Error reading payload file {}: {}".format(payloadPath, e))
payloadOrdering = None
if password:
random.seed(password)
# Get a list of each line number in the cloaked file
payloadOrdering = [i for i in range(len(payloadB64))]
# Shuffle the order of the lines
random.shuffle(payloadOrdering)
try:
with open(cipherPath, encoding="utf-8") as file:
cipherArray = file.readlines()
except Exception as e:
print("Error reading cipher file {}: {}".format(cipherPath, e))
if outputPath:
try:
with open(outputPath, "w+", encoding="utf-8") as outFile:
if payloadOrdering:
# Iterate through the randomized line order and write each line to the file
for randomLoc in payloadOrdering:
outFile.write(cipherArray[array64.index(payloadB64[randomLoc])])
else:
for char in payloadB64:
outFile.write(cipherArray[array64.index(char)])
except Exception as e:
print("Error writing to output file {}: {}".format(outputPath, e))
else:
for char in payloadB64:
print(cipherArray[array64.index(char)].strip())
if __name__ == "__main__":
parser = argparse.ArgumentParser(description='Transform file into obfuscated text file.')
parser.add_argument('-i', "--input", type=str, help='Payload File Path', required=True)
parser.add_argument('-c', "--cipher", type=str, help='Cipher File Path', required=True)
parser.add_argument('-o', "--output", type=str, help='Output File Path', default="")
parser.add_argument('-p', "--password", type=str, help='Password', default=None)
args = parser.parse_args()
Cloakify(args.input, args.cipher, args.output, args.password)

View File

@ -0,0 +1,84 @@
#!/usr/bin/env python3
#
# Filename: decloakify.py
#
# Author: Joe Gervais (TryCatchHCF)
#
# Summary: Exfiltration toolset (see cloakify.py) that transforms data into lists
# of words / phrases / Unicode to ease exfiltration of data across monitored networks,
# essentially hiding the data in plain sight, and facilitate social engineering attacks
# against human analysts and their workflows. Bonus Feature: Defeats signature-based
# malware detection tools (cloak your other tools).
#
# Used by cloakifyFactory.py, can be used as a standalone script as well (example below).
#
# Description: Decodes the output of cloakify.py into its underlying Base64 format,
# then does Base64 decoding to unpack the cloaked payload file. Requires the use of the
# same cipher that was used to cloak the file prior to exfitration, of course.
#
# Prepackaged ciphers include: lists of desserts in English, Arabic, Thai, Russian,
# Hindi, Chinese, Persian, and Muppet (Swedish Chef); Top 100 IP Addresses; GeoCoords of
# World Capitols; MD5 Password Hashes; An Emoji cipher; Star Trek characters; Geocaching
# Locations; Amphibians (Scientific Names); and evadeAV cipher, a simple cipher that
# minimizes the size of the resulting obfuscated data.
#
# Example:
#
# $ ./decloakify.py cloakedPayload.txt ciphers/desserts.ciph
import argparse
import base64
import random
import sys
array64 = list("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789/+=")
def Decloakify(cloakedPath:str, cipherPath:str, outputPath:str="", password:str=""):
"""Cipher file will be read into a list that will be used for the payload's deobfuscation.
Cloaked file's contents will be read in line by line mapping the line to a base64 character.
If an output path is defined the base64 contents will be decoded and written to the output
file otherwise it will be written to the console.
Args:
cloakedPath (str): Path to the file that is encoded
cipherPath (str): Path to the file used as the base64 cipher
outputPath (str): Path to write out the decoded
"""
with open(cipherPath, encoding="utf-8") as file:
arrayCipher = file.readlines()
clear64 = ""
with open(cloakedPath, encoding="utf-8") as file:
if password:
random.seed(password)
lines = file.readlines()
# Get a list of each line number in the cloaked file
decodeOrdering = [i for i in range(len(lines))]
# Shuffle the order of the lines to what they were during encoding
random.shuffle(decodeOrdering)
# Map the index of the original payload to the index in the cloaked file
decodeOrdering = {k: v for v, k in enumerate(decodeOrdering)}
# Iterate through the proper line order and reconstruct the unshuffled base64 payload
for i in range(len(lines)):
clear64 += array64[arrayCipher.index(lines[decodeOrdering[i]])]
else:
for line in file:
clear64 += array64[arrayCipher.index(line)]
payload = base64.b64decode(clear64)
if outputPath:
with open(outputPath, "wb") as outFile:
outFile.write(payload)
else:
print(payload)
if __name__ == "__main__":
parser = argparse.ArgumentParser(description='Transform file into unobfuscated text file.')
parser.add_argument('-i', "--input", type=str, help='Cloaked File Path', required=True)
parser.add_argument('-c', "--cipher", type=str, help='Cipher File Path', required=True)
parser.add_argument('-o', "--output", type=str, help='Output File Path', default="")
parser.add_argument('-p', "--password", type=str, help='Password', default=None)
args = parser.parse_args()
Decloakify(args.input, args.cipher, args.output, args.password)

33
extras/docker/Dockerfile Normal file
View File

@ -0,0 +1,33 @@
##
# gregtzar/tomb
#
# This creates an Ubuntu derived base image and installs the tomb library
# along with it's dependencies.
FROM dyne/devuan:chimaera
ARG DEBIAN_FRONTEND=noninteractive
ARG TOMB_VERSION=2.9
# Install dependencies
RUN apt-get update -y && \
apt-get install -y -q --no-install-recommends\
make \
sudo \
curl \
rsync \
zsh \
gnupg \
cryptsetup \
pinentry-curses \
file xxd \
steghide \
plocate \
recoll
# Build and install Tomb from remote repo
RUN curl https://files.dyne.org/tomb/releases/Tomb-$TOMB_VERSION.tar.gz -o /tmp/Tomb-$TOMB_VERSION.tar.gz && \
cd /tmp && \
tar -zxvf /tmp/Tomb-$TOMB_VERSION.tar.gz && \
cd /tmp/Tomb-$TOMB_VERSION && \
make install

21
extras/docker/LICENSE Normal file
View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2019 Greg Tzar
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

166
extras/docker/README.md Normal file
View File

@ -0,0 +1,166 @@
# docker-tomb
A [docker](https://www.docker.com) wrapper for the the Linux-based [Tomb encryption library](https://www.dyne.org/software/tomb) aimed at enabling functional cross platform support for the [Tomb library](https://github.com/dyne/Tomb) and container format.
**This is a work in progress and while functional it is not yet fully stable or secure.**
# Requirements
This strategy should work on MacOS, Windows, Linux, and any host OS where Docker is available. The only requirement is that you must have Docker running on your machine. My recommendation for first time users is to install [Docker Desktop](https://www.docker.com/products/docker-desktop).
# Container Setup
You can build the container by running the following command from the root directory of this repo:
```bash
docker build -t tomb .
```
Although building it yourself is preferred, you could also pull the container from my [dockerhub repo](https://hub.docker.com/r/gregtzar/tomb).
You can confirm the setup by first opening an interactive bash prompt in the new container and from there confirm the tomb version and exit the container.
```bash
docker run -it --rm tomb /bin/bash
tomb -v
exit
```
### Security Notes
You will see by examining the `Dockerfile` that I am using Docker's official Ubuntu Bionic container as the base, and that the latest Tomb library is downloaded directly from the [official tomb filestore](https://files.dyne.org/tomb). The `TOMB_VERSION` arg near the top of the `Dockerfile` will allow you to configure which Tomb version you want.
Be aware that because of the way the filestore is currently organized, the curl path will break unless the version is set to the most current. I am filing a request with them to address this.
Optionally if you wanted to store the tomb code in the repo, for example inside of a `src` folder, then you could install it alternatively like this:
```
ADD src/Tomb-$TOMB_VERSION.tar.gz /tmp/
RUN cd /tmp/Tomb-$TOMB_VERSION && \
make install
```
# Container Usage
The thing which allows this container strategy to be really functional is the use of Docker's [bind mounts](https://docs.docker.com/storage/bind-mounts/) to give the dockerized tomb instance the ability to read an encrypted/closed tomb volume **from** the host and to then mount the decrypted/open tomb volume **back** to the host where it can be accessed as normal.
## Usage Examples
Once you understand the concept of docker bind mounts you will be able to run tomb commands from the host in any way you need. Here are a few examples to get you started.
### Create a Tomb Volume
First launch an interactive bash prompt in a temporary instance of the container, and bind-mount a working directory from the host machine where we can output the new tomb volume.
This will mount the `/tmp/tomb-gen` directy on the host to the location of `/tomb-gen` inside the docker container instance. **Note**: The `/tmp/tomb-gen` directory must already exist on the host.
```bash
docker run -it --rm --privileged \
--mount type=bind,source=/tmp/tomb-gen,target=/tomb-gen \
tomb /bin/bash
```
Now from the interative prompt inside the docker continer instance creating a new tomb volume is textbook. **Note**: If you get an error about swap partitions during the `forge` command, this is a host issue and not a container issue. You need to disable swapping on the host if you want to fix it, or use `-f` to forge ahead anyways.
```bash
cd /tomb-gen
tomb dig -s 100 secret.tomb
tomb forge secret.tomb.key
tomb lock secret.tomb -k secret.tomb.key
```
Now the host `/tmp/tomb-gen` directory contains a new `secret.tomb` volume and `secret.tomb.key` key. Use `exit` to close the tomb container and the new files will remain on the host.
### Direct Mount a Tomb Volume
This example will use the volume and key we created in the previous example but can be modified to suite your needs. First, launch another interactive bash prompt in a temporary instance of the container.
This will mount the host `/tmp/tomb-gen` directory as well as a new `/tmp/tomb-mount` directory where the container can mount the open tomb volume back to so the host can access it.
```bash
docker run -it --rm --privileged \
--mount type=bind,source=/tmp/tomb-gen,target=/tomb-gen \
--mount type=bind,source=/tmp/tomb-mount,target=/tomb-mount,bind-propagation=shared \
tomb /bin/bash
```
Now from the interative prompt inside the docker continer instance we can open the tomb volume as usual, referencing the tomb volume, key, and mount destinations bind-mounted to the host:
```bash
tomb open /tomb-gen/secret.tomb /tomb-mount -k /tomb-gen/secret.tomb.key
```
The contents of the tomb volume are now accessible via `/tmp/tomb-mount` on the host. Be sure to successfully close the tomb volume before you exit the docker container:
```bash
tomb close
```
### Unpack and Repack a Tomb Volume
This workflow was created as a workaround for the MacOS lack of support for bind propagation which prevents us from directly mounting a tomb volume back to the host OS. The workaround is to use an additional bash script layer to copy and synchronize the tomb volume contents between a bind mounted host directory and the mounted tomb volume accesible only from inside the docker container.
The `tomb.sh` script is desinged to run from the host and requires the following ENV VARS to be available to it:
* `TOMB_DOCKER_IMAGE`: The name of the docker image on the host. In the examples we named it `tomb`.
* `TOMB_VOLUME`: The full path of the tomb volume file on the host. In the example it is `/tmp/tomb-gen/secret.tomb`.
* `TOMB_VOLUME_KEY`: The full path of the tomb volume key file on the host. In the examples it is `/tmp/tomb-gen/secret.tomb.key`.
* `TOMB_OUTPUT_DIR`: The full path of the directory where we will unpack the tomb volume contents to on the host. In the examples it is `/tmp/tomb-out`.
#### tomb.sh unpack
This command will launch a new docker container, open the tomb volume internally, and copy it's contents to the host `TOMB_OUTPUT_DIR` directory which it expects to be empty. It will then close the tomb volume and exit the docker container.
```bash
export TOMB_DOCKER_IMAGE=tomb
export TOMB_VOLUME=/tmp/tomb-gen/secret.tomb
export TOMB_VOLUME_KEY=/tmp/tomb-gen/secret.tomb.key
export TOMB_OUTPUT_DIR=/tmp/tomb-out
./tomb.sh unpack
```
If needed, the `-f` flag can be forwarded through to tomb by passing it to the `unpack` command.
```bash
./tomb.sh unpack -f
```
#### tomb.sh repack
This command will launch a new docker container, open the tomb volume internally, and copy the current contents of the host `TOMB_OUTPUT_DIR` directory back into the open tomb volume. Copying uses `rsync` with `--delete` any files which were removed from `TOMB_OUTPUT_DIR` will also be removed from the open tomb volume contents. It will then close the tomb volume, delete the contents of `TOMB_OUTPUT_DIR`, and exit the docker container. This way all changes are persisted back to the tomb volume. Each step is performed sequentially and an error will cause the entire sequence to bail, so for example if we are unable to successfully close the tomb volume the the contents of `TOMB_OUTPUT_DIR` will not be deleted.
```bash
export TOMB_DOCKER_IMAGE=tomb
export TOMB_VOLUME=/tmp/tomb-gen/secret.tomb
export TOMB_VOLUME_KEY=/tmp/tomb-gen/secret.tomb.key
export TOMB_OUTPUT_DIR=/tmp/tomb-out
./tomb.sh repack
```
If needed, the `-f` flag can be forwarded through to tomb by passing it to the `repack` command.
```bash
./tomb.sh repack -f
```
# Known Issues and Workarounds
## Swap partitions
The user may get a security error from Tomb regarding the swap partitions and is prompted to either disable them or force an override. While the swap error is reported by tomb, it actually pertains to the state of the host OS and not the container OS. So the recommended `swapoff -a` command will obviously work on a Linux host but won't work on a MacOS or Windows host. In these later cases it is up to the user to disable swapping (either temporarily or permanently) on their host os should they choose to, or use the `-f` flag with tomb.
## Privileged access containers
The docker container must be launched in privileged mode, otherwise the tomb library cannot mount loopback devices which it depends. Launching a docker container in privileged mode removes some of the container security sandboxing and grants anything in the container access to parts of your host system. Until docker can support loopback mounting for unprivileged users this may be the only option.
## Bind propagation in MacOS
MacOS does not supported bind propagation for bind mounts, which means we cannot set the `bind-propagation=shared` option on the `--mount` flag for the docker instance. So even though we can mount the tomb volume back to the bind-mounted host directory, the mount will not recurse and the directory will appear empty from the hosts point of view. I have attempted to overcome this a variety of ways including FUSE `bindfs` and symlinks and cannot find a way to expose the mounted tomb volume directory back to the host of MacOS. As a workaround I created the `tomb unpack` and `tomb repack` commands which use `rsync` to recursively copy the contents between the tomb volume mounted inside the docker container and the bind-mounted directory from the host.
## Loop device mount ghost
If the docker container instance is shut down before the tomb volume is successfully closed then the the mounted volume and it's contents may remain attached and available from the host. Obviously any changes made to the contents at this point will not be persisted back to the tomb volume. The ghosted device mount and it's contents will disappear the host system is restarted or the node is manually cleaned up (which I don't know how to do properly), and you will be able to mount the real tomb volume again.
## Filesystem permissions
Since the docker container needs to run as root, anything it creates will have root permissions and may not be accessible from the host user without additional privileges.

121
extras/docker/tomb.sh Executable file
View File

@ -0,0 +1,121 @@
#!/bin/bash
set -e
function log_info(){
local msg=$1
printf "INFO: ${msg}\n"
}
function log_error(){
local msg=$1
printf "ERROR: ${msg}\n"
}
# Check & Validate Required ENV VARS
##
# TOMB_DOCKER_IMAGE
if [ -z "$TOMB_DOCKER_IMAGE" ]; then
log_error "ENV VAR: TOMB_DOCKER_IMAGE is required!"
exit 1
fi
if [ -z "$(docker images -q $TOMB_DOCKER_IMAGE)" ]; then
log_error "TOMB_DOCKER_IMAGE not found: $TOMB_DOCKER_IMAGE"
exit 1
fi
##
# TOMB_VOLUME
if [ -z "$TOMB_VOLUME" ]; then
log_error "ENV VAR: TOMB_VOLUME is required!"
exit 1
fi
if [ ! -f "$TOMB_VOLUME" ]; then
log_error "TOMB_VOLUME not found: $TOMB_VOLUME"
exit 1
fi
##
# TOMB_VOLUME_KEY
if [ -z "$TOMB_VOLUME_KEY" ]; then
log_error "ENV VAR: TOMB_VOLUME_KEY is required!"
exit 1
fi
if [ ! -f "$TOMB_VOLUME_KEY" ]; then
log_error "TOMB_VOLUME_KEY not found: $TOMB_VOLUME_KEY"
exit 1
fi
##
# TOMB_OUTPUT_DIR
if [ -z "$TOMB_OUTPUT_DIR" ]; then
log_error "ENV VAR: TOMB_OUTPUT_DIR is required!"
exit 1
fi
if [ ! -d "$TOMB_OUTPUT_DIR" ]; then
log_error "TOMB_OUTPUT_DIR not found: $TOMB_OUTPUT_DIR"
exit 1
fi
# Internal Vars
I_TOMB_VOLUME=/tomb/volume
I_TOMB_VOLUME_KEY=/tomb/volume_key
I_TOMB_OUTPUT_DIR=/tomb/output_dir # Important: This must NOT end in a slash!
I_TOMB_MOUNT_DIR=/tomb/mount_dir # Important: This must NOT end in a slash!
# Parse command
cmd=$1
if [ -z "$cmd" ]; then
log_error "A valid command is required! (eg: unpack, repack)"
exit 1
fi
# Parse flags (right now there is only one possible flag so I'm being lazy about parsing it)
flags=$2
force_opt=""
if [ "$flags" = "-f" ]; then
force_opt=$flags
fi
if [ $cmd = unpack ]; then
if [ ! -z "$(ls -A $TOMB_OUTPUT_DIR)" ]; then
log_error "Cannot unpack: TOMB_OUTPUT_DIR is not empty! $TOMB_OUTPUT_DIR"
exit 1
fi
docker run -it --rm --privileged \
--mount type=bind,source=${TOMB_VOLUME},target=${I_TOMB_VOLUME} \
--mount type=bind,source=${TOMB_VOLUME_KEY},target=${I_TOMB_VOLUME_KEY} \
--mount type=bind,source=${TOMB_OUTPUT_DIR},target=${I_TOMB_OUTPUT_DIR} \
tomb /bin/bash -c "set -e; tomb open ${I_TOMB_VOLUME} ${I_TOMB_MOUNT_DIR} -k ${I_TOMB_VOLUME_KEY} ${force_opt}; rsync -azh --delete ${I_TOMB_MOUNT_DIR}/ ${I_TOMB_OUTPUT_DIR}; tomb close"
elif [ $cmd = repack ]; then
if [ -z "$(ls -A $TOMB_OUTPUT_DIR)" ]; then
log_error "Cannot repack: TOMB_OUTPUT_DIR is empty! $TOMB_OUTPUT_DIR"
exit 1
fi
docker run -it --rm --privileged \
--mount type=bind,source=${TOMB_VOLUME},target=${I_TOMB_VOLUME} \
--mount type=bind,source=${TOMB_VOLUME_KEY},target=${I_TOMB_VOLUME_KEY} \
--mount type=bind,source=${TOMB_OUTPUT_DIR},target=${I_TOMB_OUTPUT_DIR} \
tomb /bin/bash -c "set -e; tomb open ${I_TOMB_VOLUME} ${I_TOMB_MOUNT_DIR} -k ${I_TOMB_VOLUME_KEY} ${force_opt}; rsync -azh --delete ${I_TOMB_OUTPUT_DIR}/ ${I_TOMB_MOUNT_DIR}; tomb close; rm -rf ${I_TOMB_OUTPUT_DIR}/..?* ${I_TOMB_OUTPUT_DIR}/* ${I_TOMB_OUTPUT_DIR}/.[!.]*"
elif [ $cmd = drop ]; then
if [ -z "$(ls -A $TOMB_OUTPUT_DIR)" ]; then
log_error "Cannot drop: TOMB_OUTPUT_DIR is empty! $TOMB_OUTPUT_DIR"
exit 1
fi
docker run -it --rm --privileged \
--mount type=bind,source=${TOMB_OUTPUT_DIR},target=${I_TOMB_OUTPUT_DIR} \
tomb /bin/bash -c "set -e; rm -rf ${I_TOMB_OUTPUT_DIR}/..?* ${I_TOMB_OUTPUT_DIR}/* ${I_TOMB_OUTPUT_DIR}/.[!.]*"
else
log_error "Invalid command: $cmd"
fi

View File

@ -4,6 +4,23 @@
If you like to see our nifty little skull on the upper right corner of If you like to see our nifty little skull on the upper right corner of
your desktop, then compile and install this little auxiliary program. your desktop, then compile and install this little auxiliary program.
## Build
Make sure that Gcc, GNU Make, pkg-config, Gtk 2.0 and libnotify
development packages are installed: in Debian and Ubuntu the packages
are:
- gcc
- make
- pkg-config
- libgtk2.0-dev
- libnotify-dev
Then launch the `make` command which will build the `tomb-gtk-tray`
executable one can copy to the path (for instance `~/bin`).
## Usage
Use by launching `tomb-gtk-tray` followed by the name of your tomb as Use by launching `tomb-gtk-tray` followed by the name of your tomb as
reported by `tomb list`. For instance if your tomb is `secrets.tomb`: reported by `tomb list`. For instance if your tomb is `secrets.tomb`:

View File

@ -77,7 +77,7 @@ int main(int argc, char **argv) {
} }
snprintf(filename,255, "%s", argv[1]); snprintf(filename,255, "%s", argv[1]);
snprintf(mountpoint,255, "/media/%s.tomb", argv[1]); snprintf(mountpoint,255, "/media/%s", argv[1]);
// libnotify // libnotify
notify_init("Tomb"); notify_init("Tomb");

View File

@ -1,11 +1,12 @@
gtomb - A GUI wrapper for Tomb, the crypto undertaker gtomb - A GUI wrapper for Tomb, the crypto undertaker
Copyright (C) 2015 Parazyd <parazyd AT dyne DOT org> Copyright (C) 2015-2016 Parazyd <parazyd@dyne.org>
This program is free software: you can redistribute it and/or modify This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or the Free Software Foundation, either version 3 of the License, or
(at your option) any later version. (at your option) any later version.
This program is distributed in the hope that it will be useful, This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the

View File

@ -11,7 +11,7 @@ list and the script will run it for you. Easy-peasy.
### Random notes ### Random notes
* If you type in your sudo password once correctly, in the next 5 (or whatever your sudoers timeout is) minutes, you can type in the wrong password as well. * If you type in your sudo password once correctly, in the next 5 (or whatever your sudoers timeout is) minutes, you can type in the wrong password as well.
* The function for catching cancellation sometimes fails because of bad ps syntax. No idea why yet. * The function for catching cancellation sometimes fails because of bad ps syntax. (Possibly fixed, needs more testing)
## Dependencies ## Dependencies
* [tomb](https://github.com/dyne/Tomb) (also get tomb's dependencies) * [tomb](https://github.com/dyne/Tomb) (also get tomb's dependencies)
@ -19,8 +19,8 @@ list and the script will run it for you. Easy-peasy.
## TODO ## TODO
* Complete error checking * Complete error checking
* Figure out why ps fails sometimes
* and more stuff * and more stuff
## What you need to do ## What you need to do
* Be patient or help with coding :) * Be patient or help with coding :)
* Request features

File diff suppressed because it is too large Load Diff

11
extras/install_cloakify.sh Executable file
View File

@ -0,0 +1,11 @@
#!/bin/sh
set -ex
wget https://github.com/TryCatchHCF/Cloakify/archive/v1.0.3.tar.gz -O /tmp/cloakify.tar.gz
mkdir -p extras/cloakify
tar -xvf /tmp/cloakify.tar.gz --strip-components=1 -C $PWD/extras/cloakify
echo "#!/bin/sh
python2 $PWD/extras/cloakify/cloakify.py $@" > /usr/bin/cloakify
echo "#!/bin/sh
python2 $PWD/extras/cloakify/decloakify.py $@" > /usr/bin/decloakify
chmod +x /usr/bin/cloakify
chmod +x /usr/bin/decloakify

View File

@ -2,10 +2,14 @@
PREFIX ?= /usr/local PREFIX ?= /usr/local
all: all:
$(CC) -O2 -o tomb-kdb-pbkdf2 pbkdf2.c -lgcrypt $(CC) -O2 $(CFLAGS) -o tomb-kdb-pbkdf2 pbkdf2.c -lgcrypt
$(CC) -O2 -o tomb-kdb-pbkdf2-getiter benchmark.c -lgcrypt $(CC) -O2 $(CFLAGS) -o tomb-kdb-pbkdf2-getiter benchmark.c -lgcrypt
$(CC) -O2 -o tomb-kdb-pbkdf2-gensalt gen_salt.c -lgcrypt $(CC) -O2 $(CFLAGS) -o tomb-kdb-pbkdf2-gensalt gen_salt.c -lgcrypt
$(CC) -O2 -o tomb-kdb-hexencode hexencode.c $(CC) -O2 $(CFLAGS) -o tomb-kdb-hexencode hexencode.c
test:
@echo "Running Tomb-kdb tests"
./test.sh
clean: clean:
rm -f tomb-kdb-pbkdf2 tomb-kdb-pbkdf2-getiter tomb-kdb-pbkdf2-gensalt tomb-kdb-hexencode rm -f tomb-kdb-pbkdf2 tomb-kdb-pbkdf2-getiter tomb-kdb-pbkdf2-gensalt tomb-kdb-hexencode

View File

@ -41,7 +41,7 @@ int main(int argc, char *argv[]) {
} else { } else {
while( (read_bytes=fread(buf, sizeof(char), 2, stdin)) != 0) { while( (read_bytes=fread(buf, sizeof(char), 2, stdin)) != 0) {
if(read_bytes == 1) buf[1]='\0'; if(read_bytes == 1) buf[1]='\0';
sscanf(buf, "%x", &c); sscanf(buf, "%s", &c);
printf("%c", c); printf("%c", c);
} }
return 0; return 0;

View File

@ -28,6 +28,7 @@
************* *************
** **
** Anthony Thyssen 4 November 2009 A.Thyssen@griffith.edu.au ** Anthony Thyssen 4 November 2009 A.Thyssen@griffith.edu.au
** AitorATuin 3 February 2018 (whitespace password fix in Tomb)
** **
** Based on a test program "pkcs5.c" found on ** Based on a test program "pkcs5.c" found on
** http://www.mail-archive.com/openssl-users@openssl.org ** http://www.mail-archive.com/openssl-users@openssl.org
@ -43,6 +44,9 @@
#include <gcrypt.h> #include <gcrypt.h>
/* block size for password buffer */
#define BLOCK_SIZE 40
/* TODO: move print_hex and hex_to_binary to utils.h, with separate compiling */ /* TODO: move print_hex and hex_to_binary to utils.h, with separate compiling */
void print_hex(unsigned char *buf, int len) void print_hex(unsigned char *buf, int len)
{ {
@ -73,15 +77,37 @@ int hex_to_binary(unsigned char *buf, char *hex)
return count; return count;
} }
void cleanup(char *result, int result_len, char *pass, char *salt, int salt_len) {
int i;
//clear and free everything
if (result) {
for(i=0; i<result_len;i++)
result[i]=0;
free(result);
}
if (pass) {
for(i=0; i<strlen(pass); i++) //blank
pass[i]=0;
free(pass);
}
if (salt) {
for(i=0; i<salt_len; i++) //blank
salt[i]=0;
free(salt);
}
}
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
char *pass = NULL; char *pass = NULL;
unsigned char *salt; unsigned char *salt = NULL;
int salt_len; // salt length in bytes int salt_len; // salt length in bytes
int ic=0; // iterative count int ic=0; // iterative count
int result_len; int result_len;
unsigned char *result; // result (binary - 32+16 chars) unsigned char *result = NULL; // result (binary - 32+16 chars)
int i; int i, pw_len = 0;
int buff_len = BLOCK_SIZE;
if ( argc != 4 ) { if ( argc != 4 ) {
fprintf(stderr, "usage: %s salt count len <passwd >binary_key_iv\n", argv[0]); fprintf(stderr, "usage: %s salt count len <passwd >binary_key_iv\n", argv[0]);
@ -89,7 +115,8 @@ int main(int argc, char *argv[])
} }
//TODO: move to base64decode //TODO: move to base64decode
salt=calloc(strlen(argv[1])/2+3, sizeof(char)); salt_len = strlen(argv[1])/2+3;
salt = calloc(salt_len, sizeof(char));
salt_len=hex_to_binary(salt, argv[1]); salt_len=hex_to_binary(salt, argv[1]);
if( salt_len <= 0 ) { if( salt_len <= 0 ) {
fprintf(stderr, "Error: %s is not a valid salt (it must be a hexadecimal string)\n", argv[1]); fprintf(stderr, "Error: %s is not a valid salt (it must be a hexadecimal string)\n", argv[1]);
@ -105,14 +132,41 @@ int main(int argc, char *argv[])
exit(1); exit(1);
} }
fscanf(stdin, "%ms", &pass); /* Read password char by char.
if ( pass[strlen(pass)-1] == '\n' ) *
pass[strlen(pass)-1] = '\0'; * Doing in this way we make sure that blanks (even null bytes) end up
* in the password.
*
* passwords containing just a bunch of spaces are valid
*/
pass = calloc(buff_len, sizeof(char));
int c = getchar();
while (c != EOF) {
if (pw_len == buff_len) {
buff_len *= 2;
pass = realloc(pass, buff_len);
if (!pass) {
fprintf(stderr, "Error allocating memory\n");
cleanup(result, result_len, pass, salt, salt_len);
exit(3);
}
}
pass[pw_len] = (char)c;
pw_len++;
c = getchar();
}
if (pw_len <= 1) {
fprintf(stderr, "Error: password is empty\n");
cleanup(result, result_len, pass, salt, salt_len);
exit(1);
}
pass[pw_len-1] = '\0';
// PBKDF 2 // PBKDF 2
result = calloc(result_len, sizeof(unsigned char*)); result = calloc(result_len, sizeof(unsigned char*));
if (!gcry_check_version ("1.5.0")) { if (!gcry_check_version ("1.5.0")) {
fputs ("libgcrypt version mismatch\n", stderr); fputs ("libgcrypt version mismatch\n", stderr);
cleanup(result, result_len, pass, salt, salt_len);
exit (2); exit (2);
} }
/* Allocate a pool of 16k secure memory. This make the secure memory /* Allocate a pool of 16k secure memory. This make the secure memory
@ -124,19 +178,10 @@ int main(int argc, char *argv[])
/* Tell Libgcrypt that initialization has completed. */ /* Tell Libgcrypt that initialization has completed. */
gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0); gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0);
gcry_kdf_derive( pass, strlen(pass), GCRY_KDF_PBKDF2, GCRY_MD_SHA1, salt, salt_len, ic, result_len, result); gcry_kdf_derive(pass, pw_len-1, GCRY_KDF_PBKDF2, GCRY_MD_SHA1, salt, salt_len, ic, result_len, result);
print_hex(result, result_len); // Key + IV (as hex string) print_hex(result, result_len); // Key + IV (as hex string)
//clear and free everything cleanup(result, result_len, pass, salt, salt_len);
for(i=0; i<result_len;i++)
result[i]=0;
free(result);
for(i=0; i<strlen(pass); i++) //blank
pass[i]=0;
free(pass);
for(i=0; i<strlen(argv[1])/2+3; i++) //blank
salt[i]=0;
free(salt);
return(0); return(0);
} }

48
extras/kdf-keys/test.sh Normal file → Executable file
View File

@ -1,6 +1,8 @@
#!/usr/bin/env zsh #!/usr/bin/env zsh
error=0 error=0
check_kdf() {
while read line; do while read line; do
pass=`cut -f1 <<<$line` pass=`cut -f1 <<<$line`
salt=`cut -f2 <<<$line` salt=`cut -f2 <<<$line`
@ -9,13 +11,55 @@ while read line; do
expected=`cut -f5 <<<$line` expected=`cut -f5 <<<$line`
hexsalt=`cut -f6 <<<$line` hexsalt=`cut -f6 <<<$line`
#TODO: check! #TODO: check!
derived=`./pbkdf2 $hexsalt $iter $keylen <<<$pass` derived=`./tomb-kdb-pbkdf2 $hexsalt $iter $keylen <<<$pass`
if [[ $derived != $expected ]]; then if [[ $derived != $expected ]]; then
echo ./pbkdf2 $hexsalt $iter $keylen "<<<$pass"
echo "Expected $expected, got $derived" >&2 echo "Expected $expected, got $derived" >&2
error=$((error + 1)) error=$((error + 1))
fi fi
done < test.txt done < test.txt
}
check_white_spaces() {
hexsalt="73616c74"
iter=4096
keylen=20
typeset -a results
passwords=('one two three' 'one two' 'one')
for pwd in $passwords; do
results+=`./tomb-kdb-pbkdf2 $hexsalt $iter $keylen <<<$pwd`
done
for ((i=1;i<=3;i++)); do
d1=$results[$i]
i1=$passwords[$i]
for ((j=(($i+1));j<=3;j++)); do
d2=$results[$j]
i2=$passwords[$j]
if [[ $d1 == $d2 ]]; then
echo "Inputs \"$i1\" and \"$i2\" produce the same output $d1" >&2
error=$((error + 1))
fi
done
done
}
check_password_len() {
hexsalt="73616c74"
iter=4096
keylen=20
./tomb-kdb-pbkdf2 $hexsalt $iter $keylen 2>/dev/null <<<"" && {
echo "Empty passwords are accepted"
error=$((error + 1))
}
bigpassword=`perl -e 'print "a"x3000'`
./tomb-kdb-pbkdf2 $hexsalt $iter $keylen &>/dev/null <<<"$bigpassword" || {
echo "Error when using long password"
error=$((error + 1))
}
}
check_kdf
check_white_spaces
check_password_len
if [[ $error == 1 ]]; then if [[ $error == 1 ]]; then
exit $error exit $error

View File

@ -0,0 +1,46 @@
.PHONY: test
system := $(shell uname -s)
# if [ ${system} == "FreeBSD" ]; then sudo kldload fusefs; fi
TOMB ?= test.tomb
#########
## BUILD
#########
test: create-open-close check-random-data
########
## TEST
########
create-open-close:
TOMB=${TOMB} ./test/bats/bin/bats ./test/create_open_close.bats
chmod a+r ${TOMB}
# arg: TOMB='path to tomb containing random.data'
# assumes $TOMB.hash is the pre-calculated hash on creation
check-random-data:
TOMB=${TOMB} ./test/bats/bin/bats test/check-random-data.bats
########
## LINT
########
shellcheck:
shellcheck -s sh tomb -e SC2006,SC2059,SC2034
########
## DEPS
########
download-deps-ubuntu22:
curl https://files.dyne.org/tomb3/third-party/veracrypt-ubuntu22-amd64 -o /usr/local/bin/veracrypt && chmod +x /usr/local/bin/veracrypt
curl https://files.dyne.org/zenroom/nightly/zenroom-linux-amd64 -o /usr/local/bin/zenroom && chmod +x /usr/local/bin/zenroom
download-deps-ubuntu20:
curl https://files.dyne.org/tomb3/third-party/veracrypt-ubuntu20-amd64 -o /usr/local/bin/veracrypt && chmod +x /usr/local/bin/veracrypt
curl https://files.dyne.org/zenroom/nightly/zenroom-linux-amd64 -o /usr/local/bin/zenroom && chmod +x /usr/local/bin/zenroom
download-deps-freebsd13:
curl https://files.dyne.org/tomb3/third-party/veracrypt-freebsd13-amd64 -o /usr/local/bin/veracrypt && chmod +x /usr/local/bin/veracrypt
curl https://files.dyne.org/tomb3/third-party/zenroom-freebsd13-amd64 -o /usr/local/bin/zenroom && chmod +x /usr/local/bin/zenroom
#curl https://files.dyne.org/zenroom/nightly/zenroom-freebsd13-amd64 -o /usr/local/bin/zenroom && chmod +x /usr/local/bin/zenroom

69
extras/portable/README.md Normal file
View File

@ -0,0 +1,69 @@
# Portable Tomb :: the crypto undertaker runs everywhere
[![continuous integration tests badge](https://github.com/dyne/tomb/actions/workflows/portable.yml/badge.svg)](https://github.com/dyne/Tomb/actions) test coverage status for portability
## ⚠️ WORK IN PROGRESS 🛠️
This is the portable version of [Tomb](https://github.com/dyne/tomb)
[![software by Dyne.org](https://files.dyne.org/software_by_dyne.png)](http://www.dyne.org)
# Purpose
Portable tomb achieves direct **interoperable access to tomb volumes** between:
- GNU base Linux (Ubuntu)
- Busybox based Linux (Alpine)
- MS/Windows using WSL2 (Ubuntu)
- FreeBSD (WIP using libluksde)
- ~~Apple/OSX using [MacFUSE](https://osxfuse.github.io/)~~
After some extensive testing during 2022, __adoption of Veracrypt in portable Tomb has been dropped__ because of unreliability and bad performance.
Portable tomb stays as an experimental branch that aims to reduce dependencies and in particular uses only the **POSIX sh interpreter**.
# Status
Portable tomb development is in progress and tracked via issues and the [portable milestone](https://github.com/dyne/Tomb/milestone/9).
## Features
The following features will be implemented where possible:
- mount bind (Linux only)
- ps / slam
- resize (pending investigation)
- index & search ([recoll based](https://github.com/dyne/Tomb/issues/211))
- bury / exhume
## Dependencies
- FreeBSD: `fusefs-libs3 fusefs-lkl e2fsprogs util-linux libluksde`
- Linux: `fuse3 util-linux`
- crossplatform [Veracrypt binaries](https://files.dyne.org/tomb3/third-party) console-only
## Note on Veracrypt
The way upstream developers distribute Veracrypt is far from meeting our minimalist needs, but the console-only binary once installed has a few library dependencies and is all what we need.
I setup [my own build](https://github.com/jaromil/veracrypt) and provide binary builds of Veracrypt v1.25.9 for download on Tomb's file repository for testing purposes.
# Disclaimer
Tomb is Copyright (C) 2007-2023 by the Dyne.org Foundation and
developed by [Jaromil](https://github.com/jaromil).
This source code is free software; you can redistribute it and/or
modify it under the terms of the GNU Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This source code is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. Please refer
to the GNU Public License for more details.
You should have received a copy of the GNU Public License along with
this source code; if not, write to: Free Software Foundation, Inc.,
675 Mass Ave, Cambridge, MA 02139, USA.

View File

@ -0,0 +1,97 @@
name: ☠️ Portable tomb
on:
push:
paths:
- 'extras/portable/**'
- '.github/workflows/portable.yml'
branches:
- master
pull_request:
paths:
- 'extras/portable/**'
- '.github/workflows/portable.yml'
branches:
- master
concurrency:
group: ${{ github.workflow }}-${{ github.ref_name }}
cancel-in-progress: true
jobs:
ubuntu-20:
name: 🐧 test Ubuntu-20
runs-on: ubuntu-20.04
strategy:
fail-fast: false
steps:
- uses: actions/checkout@v3
- name: Install portable tomb dependencies
run: |
sudo apt-get install -q -y make openssl wget fuse3 util-linux
sudo make -C portable download-deps-ubuntu20
- name: Run portable tomb test
run: sudo make -C extras/portable create-open-close
- uses: actions/upload-artifact@v3
with:
name: ubuntu-test.tomb
path: extras/portable/test.tomb*
retention-days: 1
freebsd:
runs-on: macos-12
name: 😈 test FreeBSD-13
needs: [ubuntu-20]
steps:
- uses: actions/checkout@v2
- name: Download portable tomb volume built on Ubuntu
uses: actions/download-artifact@v3
with:
name: ubuntu-test.tomb
path: extras/portable
- name: Test in FreeBSD
id: test
uses: vmactions/freebsd-vm@v0
with:
usesh: true
prepare: |
pkg install -y sudo gmake bash fusefs-libs3 fusefs-lkl e2fsprogs wget
wget https://files.dyne.org/tomb3/third-party/veracrypt-freebsd13-amd64
mv veracrypt-freebsd13-amd64 /usr/bin/veracrypt
wget https://files.dyne.org/tomb3/third-party/zenroom-freebsd13-amd64
mv zenroom-freebsd13-amd64 /usr/bin/zenroom
chmod a+x /usr/bin/veracrypt /usr/bin/zenroom
kldload fusefs
run: |
sudo gmake -C extras/portable
sudo gmake -C portable check-random-data
- uses: actions/upload-artifact@v3
with:
name: freebsd-test.tomb
path: extras/portable/test.tomb*
retention-days: 1
ubuntu-last-cleanup:
name: 🎯 final Ubuntu tests and cleanup
runs-on: ubuntu-20.04
needs: [freebsd]
steps:
- uses: actions/checkout@v3
- name: Install compiler and dependencies
run: |
sudo apt-get install -q -y make openssl wget fuse3 util-linux
sudo make -C extras/portable download-deps-ubuntu20
- name: Download tomb built on FreeBSD
uses: actions/download-artifact@v3
with:
name: freebsd-test.tomb
path: extras/portable
- name: Check integrity FreeBSD->Ubuntu
run: sudo make -C extras/portable check-random-data
- name: delete FreeBSD tomb artifacts
uses: geekyeggo/delete-artifact@v2
with:
name: freebsd-test.tomb
- name: delete Ubuntu tomb artifacts
uses: geekyeggo/delete-artifact@v2
with:
name: ubuntu-test.tomb

View File

@ -0,0 +1,59 @@
#!/usr/bin/env bash
set -euo pipefail
if command -v greadlink >/dev/null; then
bats_readlinkf() {
greadlink -f "$1"
}
else
bats_readlinkf() {
readlink -f "$1"
}
fi
fallback_to_readlinkf_posix() {
bats_readlinkf() {
[ "${1:-}" ] || return 1
max_symlinks=40
CDPATH='' # to avoid changing to an unexpected directory
target=$1
[ -e "${target%/}" ] || target=${1%"${1##*[!/]}"} # trim trailing slashes
[ -d "${target:-/}" ] && target="$target/"
cd -P . 2>/dev/null || return 1
while [ "$max_symlinks" -ge 0 ] && max_symlinks=$((max_symlinks - 1)); do
if [ ! "$target" = "${target%/*}" ]; then
case $target in
/*) cd -P "${target%/*}/" 2>/dev/null || break ;;
*) cd -P "./${target%/*}" 2>/dev/null || break ;;
esac
target=${target##*/}
fi
if [ ! -L "$target" ]; then
target="${PWD%/}${target:+/}${target}"
printf '%s\n' "${target:-/}"
return 0
fi
# `ls -dl` format: "%s %u %s %s %u %s %s -> %s\n",
# <file mode>, <number of links>, <owner name>, <group name>,
# <size>, <date and time>, <pathname of link>, <contents of link>
# https://pubs.opengroup.org/onlinepubs/9699919799/utilities/ls.html
link=$(ls -dl -- "$target" 2>/dev/null) || break
target=${link#*" $target -> "}
done
return 1
}
}
if ! BATS_PATH=$(bats_readlinkf "${BASH_SOURCE[0]}" 2>/dev/null); then
fallback_to_readlinkf_posix
BATS_PATH=$(bats_readlinkf "${BASH_SOURCE[0]}")
fi
export BATS_ROOT=${BATS_PATH%/*/*}
export -f bats_readlinkf
exec env BATS_ROOT="$BATS_ROOT" "$BATS_ROOT/libexec/bats-core/bats" "$@"

View File

@ -0,0 +1,98 @@
#!/usr/bin/env bash
bats_prefix_lines_for_tap_output() {
while IFS= read -r line; do
printf '# %s\n' "$line" || break # avoid feedback loop when errors are redirected into BATS_OUT (see #353)
done
if [[ -n "$line" ]]; then
printf '# %s\n' "$line"
fi
}
function bats_replace_filename() {
local line
while read -r line; do
printf "%s\n" "${line//$BATS_TEST_SOURCE/$BATS_TEST_FILENAME}"
done
if [[ -n "$line" ]]; then
printf "%s\n" "${line//$BATS_TEST_SOURCE/$BATS_TEST_FILENAME}"
fi
}
bats_quote_code() { # <var> <code>
printf -v "$1" -- "%s%s%s" "$BATS_BEGIN_CODE_QUOTE" "$2" "$BATS_END_CODE_QUOTE"
}
bats_check_valid_version() {
if [[ ! $1 =~ [0-9]+.[0-9]+.[0-9]+ ]]; then
printf "ERROR: version '%s' must be of format <major>.<minor>.<patch>!\n" "$1" >&2
exit 1
fi
}
# compares two versions. Return 0 when version1 < version2
bats_version_lt() { # <version1> <version2>
bats_check_valid_version "$1"
bats_check_valid_version "$2"
local -a version1_parts version2_parts
IFS=. read -ra version1_parts <<< "$1"
IFS=. read -ra version2_parts <<< "$2"
for i in {0..2}; do
if (( version1_parts[i] < version2_parts[i] )); then
return 0
elif (( version1_parts[i] > version2_parts[i] )); then
return 1
fi
done
# if we made it this far, they are equal -> also not less then
return 2 # use other failing return code to distinguish equal from gt
}
# ensure a minimum version of bats is running or exit with failure
bats_require_minimum_version() { # <required version>
local required_minimum_version=$1
if bats_version_lt "$BATS_VERSION" "$required_minimum_version"; then
printf "BATS_VERSION=%s does not meet required minimum %s\n" "$BATS_VERSION" "$required_minimum_version"
exit 1
fi
if bats_version_lt "$BATS_GUARANTEED_MINIMUM_VERSION" "$required_minimum_version"; then
BATS_GUARANTEED_MINIMUM_VERSION="$required_minimum_version"
fi
}
bats_binary_search() { # <search-value> <array-name>
if [[ $# -ne 2 ]]; then
printf "ERROR: bats_binary_search requires exactly 2 arguments: <search value> <array name>\n" >&2
return 2
fi
local -r search_value=$1 array_name=$2
# we'd like to test if array is set but we cannot distinguish unset from empty arrays, so we need to skip that
local start=0 mid end mid_value
# start is inclusive, end is exclusive ...
eval "end=\${#${array_name}[@]}"
# so start == end means empty search space
while (( start < end )); do
mid=$(( (start + end) / 2 ))
eval "mid_value=\${${array_name}[$mid]}"
if [[ "$mid_value" == "$search_value" ]]; then
return 0
elif [[ "$mid_value" < "$search_value" ]]; then
# This branch excludes equality -> +1 to skip the mid element.
# This +1 also avoids endless recursion on odd sized search ranges.
start=$((mid + 1))
else
end=$mid
fi
done
# did not find it -> its not there
return 1
}

View File

@ -0,0 +1,116 @@
#!/usr/bin/env bash
# reads (extended) bats tap streams from stdin and calls callback functions for each line
# bats_tap_stream_plan <number of tests> -> when the test plan is encountered
# bats_tap_stream_begin <test index> <test name> -> when a new test is begun WARNING: extended only
# bats_tap_stream_ok [--duration <milliseconds] <test index> <test name> -> when a test was successful
# bats_tap_stream_not_ok [--duration <milliseconds>] <test index> <test name> -> when a test has failed
# bats_tap_stream_skipped <test index> <test name> <skip reason> -> when a test was skipped
# bats_tap_stream_comment <comment text without leading '# '> <scope> -> when a comment line was encountered,
# scope tells the last encountered of plan, begin, ok, not_ok, skipped, suite
# bats_tap_stream_suite <file name> -> when a new file is begun WARNING: extended only
# bats_tap_stream_unknown <full line> <scope> -> when a line is encountered that does not match the previous entries,
# scope @see bats_tap_stream_comment
# forwards all input as is, when there is no TAP test plan header
function bats_parse_internal_extended_tap() {
local header_pattern='[0-9]+\.\.[0-9]+'
IFS= read -r header
if [[ "$header" =~ $header_pattern ]]; then
bats_tap_stream_plan "${header:3}"
else
# If the first line isn't a TAP plan, print it and pass the rest through
printf '%s\n' "$header"
exec cat
fi
ok_line_regexpr="ok ([0-9]+) (.*)"
skip_line_regexpr="ok ([0-9]+) (.*) # skip( (.*))?$"
not_ok_line_regexpr="not ok ([0-9]+) (.*)"
timing_expr="in ([0-9]+)ms$"
local test_name begin_index ok_index not_ok_index index scope
begin_index=0
index=0
scope=plan
while IFS= read -r line; do
case "$line" in
'begin '*) # this might only be called in extended tap output
((++begin_index))
scope=begin
test_name="${line#* "$begin_index" }"
bats_tap_stream_begin "$begin_index" "$test_name"
;;
'ok '*)
((++index))
if [[ "$line" =~ $ok_line_regexpr ]]; then
ok_index="${BASH_REMATCH[1]}"
test_name="${BASH_REMATCH[2]}"
if [[ "$line" =~ $skip_line_regexpr ]]; then
scope=skipped
test_name="${BASH_REMATCH[2]}" # cut off name before "# skip"
local skip_reason="${BASH_REMATCH[4]}"
bats_tap_stream_skipped "$ok_index" "$test_name" "$skip_reason"
else
scope=ok
if [[ "$line" =~ $timing_expr ]]; then
bats_tap_stream_ok --duration "${BASH_REMATCH[1]}" "$ok_index" "$test_name"
else
bats_tap_stream_ok "$ok_index" "$test_name"
fi
fi
else
printf "ERROR: could not match ok line: %s" "$line" >&2
exit 1
fi
;;
'not ok '*)
((++index))
scope=not_ok
if [[ "$line" =~ $not_ok_line_regexpr ]]; then
not_ok_index="${BASH_REMATCH[1]}"
test_name="${BASH_REMATCH[2]}"
if [[ "$line" =~ $timing_expr ]]; then
bats_tap_stream_not_ok --duration "${BASH_REMATCH[1]}" "$not_ok_index" "$test_name"
else
bats_tap_stream_not_ok "$not_ok_index" "$test_name"
fi
else
printf "ERROR: could not match not ok line: %s" "$line" >&2
exit 1
fi
;;
'# '*)
bats_tap_stream_comment "${line:2}" "$scope"
;;
'#')
bats_tap_stream_comment "" "$scope"
;;
'suite '*)
scope=suite
# pass on the
bats_tap_stream_suite "${line:6}"
;;
*)
bats_tap_stream_unknown "$line" "$scope"
;;
esac
done
}
normalize_base_path() { # <target variable> <base path>
# the relative path root to use for reporting filenames
# this is mainly intended for suite mode, where this will be the suite root folder
local base_path="$2"
# use the containing directory when --base-path is a file
if [[ ! -d "$base_path" ]]; then
base_path="$(dirname "$base_path")"
fi
# get the absolute path
base_path="$(cd "$base_path" && pwd)"
# ensure the path ends with / to strip that later on
if [[ "${base_path}" != *"/" ]]; then
base_path="$base_path/"
fi
printf -v "$1" "%s" "$base_path"
}

View File

@ -0,0 +1,22 @@
#!/usr/bin/env bash
BATS_TMPNAME="$BATS_RUN_TMPDIR/bats.$$"
BATS_PARENT_TMPNAME="$BATS_RUN_TMPDIR/bats.$PPID"
# shellcheck disable=SC2034
BATS_OUT="${BATS_TMPNAME}.out" # used in bats-exec-file
bats_preprocess_source() {
# export to make it visible to bats_evaluate_preprocessed_source
# since the latter runs in bats-exec-test's bash while this runs in bats-exec-file's
export BATS_TEST_SOURCE="${BATS_TMPNAME}.src"
bats-preprocess "$BATS_TEST_FILENAME" >"$BATS_TEST_SOURCE"
}
bats_evaluate_preprocessed_source() {
if [[ -z "${BATS_TEST_SOURCE:-}" ]]; then
BATS_TEST_SOURCE="${BATS_PARENT_TMPNAME}.src"
fi
# Dynamically loaded user files provided outside of Bats.
# shellcheck disable=SC1090
source "$BATS_TEST_SOURCE"
}

View File

@ -0,0 +1,107 @@
#!/usr/bin/env bash
# setup the semaphore environment for the loading file
bats_semaphore_setup() {
export -f bats_semaphore_get_free_slot_count
export -f bats_semaphore_acquire_while_locked
export BATS_SEMAPHORE_DIR="$BATS_RUN_TMPDIR/semaphores"
if command -v flock >/dev/null; then
bats_run_under_lock() {
flock "$BATS_SEMAPHORE_DIR" "$@"
}
elif command -v shlock >/dev/null; then
bats_run_under_lock() {
local lockfile="$BATS_SEMAPHORE_DIR/shlock.lock"
while ! shlock -p $$ -f "$lockfile"; do
sleep 1
done
# we got the lock now, execute the command
"$@"
local status=$?
# free the lock
rm -f "$lockfile"
return $status
}
else
printf "ERROR: flock/shlock is required for parallelization within files!\n" >&2
exit 1
fi
}
# $1 - output directory for stdout/stderr
# $@ - command to run
# run the given command in a semaphore
# block when there is no free slot for the semaphore
# when there is a free slot, run the command in background
# gather the output of the command in files in the given directory
bats_semaphore_run() {
local output_dir=$1
shift
local semaphore_slot
semaphore_slot=$(bats_semaphore_acquire_slot)
bats_semaphore_release_wrapper "$output_dir" "$semaphore_slot" "$@" &
printf "%d\n" "$!"
}
# $1 - output directory for stdout/stderr
# $@ - command to run
# this wraps the actual function call to install some traps on exiting
bats_semaphore_release_wrapper() {
local output_dir="$1"
local semaphore_name="$2"
shift 2 # all other parameters will be use for the command to execute
# shellcheck disable=SC2064 # we want to expand the semaphore_name right now!
trap "status=$?; bats_semaphore_release_slot '$semaphore_name'; exit $status" EXIT
mkdir -p "$output_dir"
"$@" 2>"$output_dir/stderr" >"$output_dir/stdout"
local status=$?
# bash bug: the exit trap is not called for the background process
bats_semaphore_release_slot "$semaphore_name"
trap - EXIT # avoid calling release twice
return $status
}
bats_semaphore_acquire_while_locked() {
if [[ $(bats_semaphore_get_free_slot_count) -gt 0 ]]; then
local slot=0
while [[ -e "$BATS_SEMAPHORE_DIR/slot-$slot" ]]; do
(( ++slot ))
done
if [[ $slot -lt $BATS_SEMAPHORE_NUMBER_OF_SLOTS ]]; then
touch "$BATS_SEMAPHORE_DIR/slot-$slot" && printf "%d\n" "$slot" && return 0
fi
fi
return 1
}
# block until a semaphore slot becomes free
# prints the number of the slot that it received
bats_semaphore_acquire_slot() {
mkdir -p "$BATS_SEMAPHORE_DIR"
# wait for a slot to become free
# TODO: avoid busy waiting by using signals -> this opens op prioritizing possibilities as well
while true; do
# don't lock for reading, we are fine with spuriously getting no free slot
if [[ $(bats_semaphore_get_free_slot_count) -gt 0 ]]; then
bats_run_under_lock bash -c bats_semaphore_acquire_while_locked && break
fi
sleep 1
done
}
bats_semaphore_release_slot() {
# we don't need to lock this, since only our process owns this file
# and freeing a semaphore cannot lead to conflicts with others
rm "$BATS_SEMAPHORE_DIR/slot-$1" # this will fail if we had not acquired a semaphore!
}
bats_semaphore_get_free_slot_count() {
# find might error out without returning something useful when a file is deleted,
# while the directory is traversed -> only continue when there was no error
until used_slots=$(find "$BATS_SEMAPHORE_DIR" -name 'slot-*' 2>/dev/null | wc -l); do :; done
echo $(( BATS_SEMAPHORE_NUMBER_OF_SLOTS - used_slots ))
}

View File

@ -0,0 +1,357 @@
#!/usr/bin/env bash
BATS_TEST_DIRNAME="${BATS_TEST_FILENAME%/*}"
BATS_TEST_NAMES=()
# shellcheck source=lib/bats-core/warnings.bash
source "$BATS_ROOT/lib/bats-core/warnings.bash"
# find_in_bats_lib_path echoes the first recognized load path to
# a library in BATS_LIB_PATH or relative to BATS_TEST_DIRNAME.
#
# Libraries relative to BATS_TEST_DIRNAME take precedence over
# BATS_LIB_PATH.
#
# Library load paths are recognized using find_library_load_path.
#
# If no library is found find_in_bats_lib_path returns 1.
find_in_bats_lib_path() { # <return-var> <library-name>
local return_var="${1:?}"
local library_name="${2:?}"
local -a bats_lib_paths
IFS=: read -ra bats_lib_paths <<< "$BATS_LIB_PATH"
for path in "${bats_lib_paths[@]}"; do
if [[ -f "$path/$library_name" ]]; then
printf -v "$return_var" "%s" "$path/$library_name"
# A library load path was found, return
return 0
elif [[ -f "$path/$library_name/load.bash" ]]; then
printf -v "$return_var" "%s" "$path/$library_name/load.bash"
# A library load path was found, return
return 0
fi
done
return 1
}
# bats_internal_load expects an absolute path that is a library load path.
#
# If the library load path points to a file (a library loader) it is
# sourced.
#
# If it points to a directory all files ending in .bash inside of the
# directory are sourced.
#
# If the sourcing of the library loader or of a file in a library
# directory fails bats_internal_load prints an error message and returns 1.
#
# If the passed library load path is not absolute or is not a valid file
# or directory bats_internal_load prints an error message and returns 1.
bats_internal_load() {
local library_load_path="${1:?}"
if [[ "${library_load_path:0:1}" != / ]]; then
printf "Passed library load path is not an absolute path: %s\n" "$library_load_path" >&2
return 1
fi
# library_load_path is a library loader
if [[ -f "$library_load_path" ]]; then
# shellcheck disable=SC1090
if ! source "$library_load_path"; then
printf "Error while sourcing library loader at '%s'\n" "$library_load_path" >&2
return 1
fi
return 0
fi
printf "Passed library load path is neither a library loader nor library directory: %s\n" "$library_load_path" >&2
return 1
}
# bats_load_safe accepts an argument called 'slug' and attempts to find and
# source a library based on the slug.
#
# A slug can be an absolute path, a library name or a relative path.
#
# If the slug is an absolute path bats_load_safe attempts to find the library
# load path using find_library_load_path.
# What is considered a library load path is documented in the
# documentation for find_library_load_path.
#
# If the slug is not an absolute path it is considered a library name or
# relative path. bats_load_safe attempts to find the library load path using
# find_in_bats_lib_path.
#
# If bats_load_safe can find a library load path it is passed to bats_internal_load.
# If bats_internal_load fails bats_load_safe returns 1.
#
# If no library load path can be found bats_load_safe prints an error message
# and returns 1.
bats_load_safe() {
local slug="${1:?}"
if [[ ${slug:0:1} != / ]]; then # relative paths are relative to BATS_TEST_DIRNAME
slug="$BATS_TEST_DIRNAME/$slug"
fi
if [[ -f "$slug.bash" ]]; then
bats_internal_load "$slug.bash"
return $?
elif [[ -f "$slug" ]]; then
bats_internal_load "$slug"
return $?
fi
# loading from PATH (retained for backwards compatibility)
if [[ ! -f "$1" ]] && type -P "$1" >/dev/null; then
# shellcheck disable=SC1090
source "$1"
return $?
fi
# No library load path can be found
printf "bats_load_safe: Could not find '%s'[.bash]\n" "$slug" >&2
return 1
}
bats_require_lib_path() {
if [[ -z "${BATS_LIB_PATH:-}" ]]; then
printf "%s: requires BATS_LIB_PATH to be set!\n" "${FUNCNAME[1]}" >&2
exit 1
fi
}
bats_load_library_safe() { # <slug>
local slug="${1:?}" library_path
bats_require_lib_path
# Check for library load paths in BATS_TEST_DIRNAME and BATS_LIB_PATH
if [[ ${slug:0:1} != / ]]; then
find_in_bats_lib_path library_path "$slug"
if [[ -z "$library_path" ]]; then
printf "Could not find library '%s' relative to test file or in BATS_LIB_PATH\n" "$slug" >&2
return 1
fi
else
# absolute paths are taken as is
library_path="$slug"
if [[ ! -f "$library_path" ]]; then
printf "Could not find library on absolute path '%s'\n" "$library_path" >&2
return 1
fi
fi
bats_internal_load "$library_path"
return $?
}
# immediately exit on error, use bats_load_library_safe to catch and handle errors
bats_load_library() { # <slug>
bats_require_lib_path
if ! bats_load_library_safe "$@"; then
exit 1
fi
}
# load acts like bats_load_safe but exits the shell instead of returning 1.
load() {
if ! bats_load_safe "$@"; then
echo "${FUNCNAME[0]} $LINENO" >&3
exit 1
fi
}
bats_redirect_stderr_into_file() {
"$@" 2>>"$bats_run_separate_stderr_file" # use >> to see collisions' content
}
bats_merge_stdout_and_stderr() {
"$@" 2>&1
}
# write separate lines from <input-var> into <output-array>
bats_separate_lines() { # <output-array> <input-var>
local output_array_name="$1"
local input_var_name="$2"
if [[ $keep_empty_lines ]]; then
local bats_separate_lines_lines=()
if [[ -n "${!input_var_name}" ]]; then # avoid getting an empty line for empty input
while IFS= read -r line; do
bats_separate_lines_lines+=("$line")
done <<<"${!input_var_name}"
fi
eval "${output_array_name}=(\"\${bats_separate_lines_lines[@]}\")"
else
# shellcheck disable=SC2034,SC2206
IFS=$'\n' read -d '' -r -a "$output_array_name" <<<"${!input_var_name}" || true # don't fail due to EOF
fi
}
run() { # [!|-N] [--keep-empty-lines] [--separate-stderr] [--] <command to run...>
# This has to be restored on exit from this function to avoid leaking our trap INT into surrounding code.
# Non zero exits won't restore under the assumption that they will fail the test before it can be aborted,
# which allows us to avoid duplicating the restore code on every exit path
trap bats_interrupt_trap_in_run INT
local expected_rc=
local keep_empty_lines=
local output_case=merged
local has_flags=
# parse options starting with -
while [[ $# -gt 0 ]] && [[ $1 == -* || $1 == '!' ]]; do
has_flags=1
case "$1" in
'!')
expected_rc=-1
;;
-[0-9]*)
expected_rc=${1#-}
if [[ $expected_rc =~ [^0-9] ]]; then
printf "Usage error: run: '-NNN' requires numeric NNN (got: %s)\n" "$expected_rc" >&2
return 1
elif [[ $expected_rc -gt 255 ]]; then
printf "Usage error: run: '-NNN': NNN must be <= 255 (got: %d)\n" "$expected_rc" >&2
return 1
fi
;;
--keep-empty-lines)
keep_empty_lines=1
;;
--separate-stderr)
output_case="separate"
;;
--)
shift # eat the -- before breaking away
break
;;
*)
printf "Usage error: unknown flag '%s'" "$1" >&2
return 1
;;
esac
shift
done
if [[ -n $has_flags ]]; then
bats_warn_minimum_guaranteed_version "Using flags on \`run\`" 1.5.0
fi
local pre_command=
case "$output_case" in
merged) # redirects stderr into stdout and fills only $output/$lines
pre_command=bats_merge_stdout_and_stderr
;;
separate) # splits stderr into own file and fills $stderr/$stderr_lines too
local bats_run_separate_stderr_file
bats_run_separate_stderr_file="$(mktemp "${BATS_TEST_TMPDIR}/separate-stderr-XXXXXX")"
pre_command=bats_redirect_stderr_into_file
;;
esac
local origFlags="$-"
set +eET
local origIFS="$IFS"
if [[ $keep_empty_lines ]]; then
# 'output', 'status', 'lines' are global variables available to tests.
# preserve trailing newlines by appending . and removing it later
# shellcheck disable=SC2034
output="$($pre_command "$@"; status=$?; printf .; exit $status)" && status=0 || status=$?
output="${output%.}"
else
# 'output', 'status', 'lines' are global variables available to tests.
# shellcheck disable=SC2034
output="$($pre_command "$@")" && status=0 || status=$?
fi
bats_separate_lines lines output
if [[ "$output_case" == separate ]]; then
# shellcheck disable=SC2034
read -d '' -r stderr < "$bats_run_separate_stderr_file"
bats_separate_lines stderr_lines stderr
fi
# shellcheck disable=SC2034
BATS_RUN_COMMAND="${*}"
IFS="$origIFS"
set "-$origFlags"
if [[ ${BATS_VERBOSE_RUN:-} ]]; then
printf "%s\n" "$output"
fi
if [[ -n "$expected_rc" ]]; then
if [[ "$expected_rc" = "-1" ]]; then
if [[ "$status" -eq 0 ]]; then
BATS_ERROR_SUFFIX=", expected nonzero exit code!"
return 1
fi
elif [ "$status" -ne "$expected_rc" ]; then
# shellcheck disable=SC2034
BATS_ERROR_SUFFIX=", expected exit code $expected_rc, got $status"
return 1
fi
elif [[ "$status" -eq 127 ]]; then # "command not found"
bats_generate_warning 1 "$BATS_RUN_COMMAND"
fi
# don't leak our trap into surrounding code
trap bats_interrupt_trap INT
}
setup() {
return 0
}
teardown() {
return 0
}
skip() {
# if this is a skip in teardown ...
if [[ -n "${BATS_TEARDOWN_STARTED-}" ]]; then
# ... we want to skip the rest of teardown.
# communicate to bats_exit_trap that the teardown was completed without error
# shellcheck disable=SC2034
BATS_TEARDOWN_COMPLETED=1
# if we are already in the exit trap (e.g. due to previous skip) ...
if [[ "$BATS_TEARDOWN_STARTED" == as-exit-trap ]]; then
# ... we need to do the rest of the tear_down_trap that would otherwise be skipped after the next call to exit
bats_exit_trap
# and then do the exit (at the end of this function)
fi
# if we aren't in exit trap, the normal exit handling should suffice
else
# ... this is either skip in test or skip in setup.
# Following variables are used in bats-exec-test which sources this file
# shellcheck disable=SC2034
BATS_TEST_SKIPPED="${1:-1}"
# shellcheck disable=SC2034
BATS_TEST_COMPLETED=1
fi
exit 0
}
bats_test_begin() {
BATS_TEST_DESCRIPTION="$1"
if [[ -n "$BATS_EXTENDED_SYNTAX" ]]; then
printf 'begin %d %s\n' "$BATS_SUITE_TEST_NUMBER" "${BATS_TEST_NAME_PREFIX:-}$BATS_TEST_DESCRIPTION" >&3
fi
setup
}
bats_test_function() {
local test_name="$1"
BATS_TEST_NAMES+=("$test_name")
}
# decides whether a failed test should be run again
bats_should_retry_test() {
# test try number starts at 1
# 0 retries means run only first try
(( BATS_TEST_TRY_NUMBER <= BATS_TEST_RETRIES ))
}

View File

@ -0,0 +1,386 @@
#!/usr/bin/env bash
# shellcheck source=lib/bats-core/common.bash
source "$BATS_ROOT/lib/bats-core/common.bash"
bats_capture_stack_trace() {
local test_file
local funcname
local i
BATS_DEBUG_LAST_STACK_TRACE=()
for ((i = 2; i != ${#FUNCNAME[@]}; ++i)); do
# Use BATS_TEST_SOURCE if necessary to work around Bash < 4.4 bug whereby
# calling an exported function erases the test file's BASH_SOURCE entry.
test_file="${BASH_SOURCE[$i]:-$BATS_TEST_SOURCE}"
funcname="${FUNCNAME[$i]}"
BATS_DEBUG_LAST_STACK_TRACE+=("${BASH_LINENO[$((i-1))]} $funcname $test_file")
case "$funcname" in
"$BATS_TEST_NAME" | setup | teardown | setup_file | teardown_file | setup_suite | teardown_suite)
break
;;
esac
if [[ "${BASH_SOURCE[$i + 1]:-}" == *"bats-exec-file" ]] && [[ "$funcname" == 'source' ]]; then
break
fi
done
}
bats_get_failure_stack_trace() {
local stack_trace_var
# See bats_debug_trap for details.
if [[ -n "${BATS_DEBUG_LAST_STACK_TRACE_IS_VALID:-}" ]]; then
stack_trace_var=BATS_DEBUG_LAST_STACK_TRACE
else
stack_trace_var=BATS_DEBUG_LASTLAST_STACK_TRACE
fi
# shellcheck disable=SC2016
eval "$(printf \
'%s=(${%s[@]+"${%s[@]}"})' \
"${1}" \
"${stack_trace_var}" \
"${stack_trace_var}")"
}
bats_print_stack_trace() {
local frame
local index=1
local count="${#@}"
local filename
local lineno
for frame in "$@"; do
bats_frame_filename "$frame" 'filename'
bats_trim_filename "$filename" 'filename'
bats_frame_lineno "$frame" 'lineno'
printf '%s' "${BATS_STACK_TRACE_PREFIX-# }"
if [[ $index -eq 1 ]]; then
printf '('
else
printf ' '
fi
local fn
bats_frame_function "$frame" 'fn'
if [[ "$fn" != "$BATS_TEST_NAME" ]] &&
# don't print "from function `source'"",
# when failing in free code during `source $test_file` from bats-exec-file
! [[ "$fn" == 'source' && $index -eq $count ]]; then
local quoted_fn
bats_quote_code quoted_fn "$fn"
printf "from function %s " "$quoted_fn"
fi
if [[ $index -eq $count ]]; then
printf 'in test file %s, line %d)\n' "$filename" "$lineno"
else
printf 'in file %s, line %d,\n' "$filename" "$lineno"
fi
((++index))
done
}
bats_print_failed_command() {
local stack_trace=("${@}")
if [[ ${#stack_trace[@]} -eq 0 ]]; then
return
fi
local frame="${stack_trace[${#stack_trace[@]} - 1]}"
local filename
local lineno
local failed_line
local failed_command
bats_frame_filename "$frame" 'filename'
bats_frame_lineno "$frame" 'lineno'
bats_extract_line "$filename" "$lineno" 'failed_line'
bats_strip_string "$failed_line" 'failed_command'
local quoted_failed_command
bats_quote_code quoted_failed_command "$failed_command"
printf '# %s ' "${quoted_failed_command}"
if [[ "$BATS_ERROR_STATUS" -eq 1 ]]; then
printf 'failed%s\n' "$BATS_ERROR_SUFFIX"
else
printf 'failed with status %d%s\n' "$BATS_ERROR_STATUS" "$BATS_ERROR_SUFFIX"
fi
}
bats_frame_lineno() {
printf -v "$2" '%s' "${1%% *}"
}
bats_frame_function() {
local __bff_function="${1#* }"
printf -v "$2" '%s' "${__bff_function%% *}"
}
bats_frame_filename() {
local __bff_filename="${1#* }"
__bff_filename="${__bff_filename#* }"
if [[ "$__bff_filename" == "$BATS_TEST_SOURCE" ]]; then
__bff_filename="$BATS_TEST_FILENAME"
fi
printf -v "$2" '%s' "$__bff_filename"
}
bats_extract_line() {
local __bats_extract_line_line
local __bats_extract_line_index=0
while IFS= read -r __bats_extract_line_line; do
if [[ "$((++__bats_extract_line_index))" -eq "$2" ]]; then
printf -v "$3" '%s' "${__bats_extract_line_line%$'\r'}"
break
fi
done <"$1"
}
bats_strip_string() {
[[ "$1" =~ ^[[:space:]]*(.*)[[:space:]]*$ ]]
printf -v "$2" '%s' "${BASH_REMATCH[1]}"
}
bats_trim_filename() {
printf -v "$2" '%s' "${1#"$BATS_CWD"/}"
}
# normalize a windows path from e.g. C:/directory to /c/directory
# The path must point to an existing/accessable directory, not a file!
bats_normalize_windows_dir_path() { # <output-var> <path>
local output_var="$1" path="$2"
if [[ "$output_var" != NORMALIZED_INPUT ]]; then
local NORMALIZED_INPUT
fi
if [[ $path == ?:* ]]; then
NORMALIZED_INPUT="$(cd "$path" || exit 1; pwd)"
else
NORMALIZED_INPUT="$path"
fi
printf -v "$output_var" "%s" "$NORMALIZED_INPUT"
}
bats_emit_trace() {
if [[ $BATS_TRACE_LEVEL -gt 0 ]]; then
local line=${BASH_LINENO[1]}
# shellcheck disable=SC2016
if [[ $BASH_COMMAND != '"$BATS_TEST_NAME" >> "$BATS_OUT" 2>&1 4>&1' && $BASH_COMMAND != "bats_test_begin "* ]] && # don't emit these internal calls
[[ $BASH_COMMAND != "$BATS_LAST_BASH_COMMAND" || $line != "$BATS_LAST_BASH_LINENO" ]] &&
# avoid printing a function twice (at call site and at definition site)
[[ $BASH_COMMAND != "$BATS_LAST_BASH_COMMAND" || ${BASH_LINENO[2]} != "$BATS_LAST_BASH_LINENO" || ${BASH_SOURCE[3]} != "$BATS_LAST_BASH_SOURCE" ]]; then
local file="${BASH_SOURCE[2]}" # index 2: skip over bats_emit_trace and bats_debug_trap
if [[ $file == "${BATS_TEST_SOURCE}" ]]; then
file="$BATS_TEST_FILENAME"
fi
local padding='$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$'
if (( BATS_LAST_STACK_DEPTH != ${#BASH_LINENO[@]} )); then
printf '%s [%s:%d]\n' "${padding::${#BASH_LINENO[@]}-4}" "${file##*/}" "$line" >&4
fi
printf '%s %s\n' "${padding::${#BASH_LINENO[@]}-4}" "$BASH_COMMAND" >&4
BATS_LAST_BASH_COMMAND="$BASH_COMMAND"
BATS_LAST_BASH_LINENO="$line"
BATS_LAST_BASH_SOURCE="${BASH_SOURCE[2]}"
BATS_LAST_STACK_DEPTH="${#BASH_LINENO[@]}"
fi
fi
}
# bats_debug_trap tracks the last line of code executed within a test. This is
# necessary because $BASH_LINENO is often incorrect inside of ERR and EXIT
# trap handlers.
#
# Below are tables describing different command failure scenarios and the
# reliability of $BASH_LINENO within different the executed DEBUG, ERR, and EXIT
# trap handlers. Naturally, the behaviors change between versions of Bash.
#
# Table rows should be read left to right. For example, on bash version
# 4.0.44(2)-release, if a test executes `false` (or any other failing external
# command), bash will do the following in order:
# 1. Call the DEBUG trap handler (bats_debug_trap) with $BASH_LINENO referring
# to the source line containing the `false` command, then
# 2. Call the DEBUG trap handler again, but with an incorrect $BASH_LINENO, then
# 3. Call the ERR trap handler, but with a (possibly-different) incorrect
# $BASH_LINENO, then
# 4. Call the DEBUG trap handler again, but with $BASH_LINENO set to 1, then
# 5. Call the EXIT trap handler, with $BASH_LINENO set to 1.
#
# bash version 4.4.20(1)-release
# command | first DEBUG | second DEBUG | ERR | third DEBUG | EXIT
# -------------+-------------+--------------+---------+-------------+--------
# false | OK | OK | OK | BAD[1] | BAD[1]
# [[ 1 = 2 ]] | OK | BAD[2] | BAD[2] | BAD[1] | BAD[1]
# (( 1 = 2 )) | OK | BAD[2] | BAD[2] | BAD[1] | BAD[1]
# ! true | OK | --- | BAD[4] | --- | BAD[1]
# $var_dne | OK | --- | --- | BAD[1] | BAD[1]
# source /dne | OK | --- | --- | BAD[1] | BAD[1]
#
# bash version 4.0.44(2)-release
# command | first DEBUG | second DEBUG | ERR | third DEBUG | EXIT
# -------------+-------------+--------------+---------+-------------+--------
# false | OK | BAD[3] | BAD[3] | BAD[1] | BAD[1]
# [[ 1 = 2 ]] | OK | --- | BAD[3] | --- | BAD[1]
# (( 1 = 2 )) | OK | --- | BAD[3] | --- | BAD[1]
# ! true | OK | --- | BAD[3] | --- | BAD[1]
# $var_dne | OK | --- | --- | BAD[1] | BAD[1]
# source /dne | OK | --- | --- | BAD[1] | BAD[1]
#
# [1] The reported line number is always 1.
# [2] The reported source location is that of the beginning of the function
# calling the command.
# [3] The reported line is that of the last command executed in the DEBUG trap
# handler.
# [4] The reported source location is that of the call to the function calling
# the command.
bats_debug_trap() {
# on windows we sometimes get a mix of paths (when install via nmp install -g)
# which have C:/... or /c/... comparing them is going to be problematic.
# We need to normalize them to a common format!
local NORMALIZED_INPUT
bats_normalize_windows_dir_path NORMALIZED_INPUT "${1%/*}"
local file_excluded='' path
for path in "${BATS_DEBUG_EXCLUDE_PATHS[@]}"; do
if [[ "$NORMALIZED_INPUT" == "$path"* ]]; then
file_excluded=1
break
fi
done
# don't update the trace within library functions or we get backtraces from inside traps
# also don't record new stack traces while handling interruptions, to avoid overriding the interrupted command
if [[ -z "$file_excluded" && "${BATS_INTERRUPTED-NOTSET}" == NOTSET ]]; then
BATS_DEBUG_LASTLAST_STACK_TRACE=(
${BATS_DEBUG_LAST_STACK_TRACE[@]+"${BATS_DEBUG_LAST_STACK_TRACE[@]}"}
)
BATS_DEBUG_LAST_LINENO=(${BASH_LINENO[@]+"${BASH_LINENO[@]}"})
BATS_DEBUG_LAST_SOURCE=(${BASH_SOURCE[@]+"${BASH_SOURCE[@]}"})
bats_capture_stack_trace
bats_emit_trace
fi
}
# For some versions of Bash, the `ERR` trap may not always fire for every
# command failure, but the `EXIT` trap will. Also, some command failures may not
# set `$?` properly. See #72 and #81 for details.
#
# For this reason, we call `bats_check_status_from_trap` at the very beginning
# of `bats_teardown_trap` and check the value of `$BATS_TEST_COMPLETED` before
# taking other actions. We also adjust the exit status value if needed.
#
# See `bats_exit_trap` for an additional EXIT error handling case when `$?`
# isn't set properly during `teardown()` errors.
bats_check_status_from_trap() {
local status="$?"
if [[ -z "${BATS_TEST_COMPLETED:-}" ]]; then
BATS_ERROR_STATUS="${BATS_ERROR_STATUS:-$status}"
if [[ "$BATS_ERROR_STATUS" -eq 0 ]]; then
BATS_ERROR_STATUS=1
fi
trap - DEBUG
fi
}
bats_add_debug_exclude_path() { # <path>
if [[ -z "$1" ]]; then # don't exclude everything
printf "bats_add_debug_exclude_path: Exclude path must not be empty!\n" >&2
return 1
fi
if [[ "$OSTYPE" == cygwin || "$OSTYPE" == msys ]]; then
local normalized_dir
bats_normalize_windows_dir_path normalized_dir "$1"
BATS_DEBUG_EXCLUDE_PATHS+=("$normalized_dir")
else
BATS_DEBUG_EXCLUDE_PATHS+=("$1")
fi
}
bats_setup_tracing() {
# Variables for capturing accurate stack traces. See bats_debug_trap for
# details.
#
# BATS_DEBUG_LAST_LINENO, BATS_DEBUG_LAST_SOURCE, and
# BATS_DEBUG_LAST_STACK_TRACE hold data from the most recent call to
# bats_debug_trap.
#
# BATS_DEBUG_LASTLAST_STACK_TRACE holds data from two bats_debug_trap calls
# ago.
#
# BATS_DEBUG_LAST_STACK_TRACE_IS_VALID indicates that
# BATS_DEBUG_LAST_STACK_TRACE contains the stack trace of the test's error. If
# unset, BATS_DEBUG_LAST_STACK_TRACE is unreliable and
# BATS_DEBUG_LASTLAST_STACK_TRACE should be used instead.
BATS_DEBUG_LASTLAST_STACK_TRACE=()
BATS_DEBUG_LAST_LINENO=()
BATS_DEBUG_LAST_SOURCE=()
BATS_DEBUG_LAST_STACK_TRACE=()
BATS_DEBUG_LAST_STACK_TRACE_IS_VALID=
BATS_ERROR_SUFFIX=
BATS_DEBUG_EXCLUDE_PATHS=()
# exclude some paths by default
bats_add_debug_exclude_path "$BATS_ROOT/lib/"
bats_add_debug_exclude_path "$BATS_ROOT/libexec/"
exec 4<&1 # used for tracing
if [[ "${BATS_TRACE_LEVEL:-0}" -gt 0 ]]; then
# avoid undefined variable errors
BATS_LAST_BASH_COMMAND=
BATS_LAST_BASH_LINENO=
BATS_LAST_BASH_SOURCE=
BATS_LAST_STACK_DEPTH=
# try to exclude helper libraries if found, this is only relevant for tracing
while read -r path; do
bats_add_debug_exclude_path "$path"
done < <(find "$PWD" -type d -name bats-assert -o -name bats-support)
fi
local exclude_paths path
# exclude user defined libraries
IFS=':' read -r exclude_paths <<< "${BATS_DEBUG_EXCLUDE_PATHS:-}"
for path in "${exclude_paths[@]}"; do
if [[ -n "$path" ]]; then
bats_add_debug_exclude_path "$path"
fi
done
# turn on traps after setting excludes to avoid tracing the exclude setup
trap 'bats_debug_trap "$BASH_SOURCE"' DEBUG
trap 'bats_error_trap' ERR
}
bats_error_trap() {
bats_check_status_from_trap
# If necessary, undo the most recent stack trace captured by bats_debug_trap.
# See bats_debug_trap for details.
if [[ "${BASH_LINENO[*]}" = "${BATS_DEBUG_LAST_LINENO[*]:-}"
&& "${BASH_SOURCE[*]}" = "${BATS_DEBUG_LAST_SOURCE[*]:-}"
&& -z "$BATS_DEBUG_LAST_STACK_TRACE_IS_VALID" ]]; then
BATS_DEBUG_LAST_STACK_TRACE=(
${BATS_DEBUG_LASTLAST_STACK_TRACE[@]+"${BATS_DEBUG_LASTLAST_STACK_TRACE[@]}"}
)
fi
BATS_DEBUG_LAST_STACK_TRACE_IS_VALID=1
}
bats_interrupt_trap() {
# mark the interruption, to handle during exit
BATS_INTERRUPTED=true
BATS_ERROR_STATUS=130
# debug trap fires before interrupt trap but gets wrong linenumber (line 1)
# -> use last stack trace
exit $BATS_ERROR_STATUS
}
# this is used inside run()
bats_interrupt_trap_in_run() {
# mark the interruption, to handle during exit
BATS_INTERRUPTED=true
BATS_ERROR_STATUS=130
BATS_DEBUG_LAST_STACK_TRACE_IS_VALID=true
exit $BATS_ERROR_STATUS
}

View File

@ -0,0 +1,37 @@
#!/usr/bin/env bash
bats_test_count_validator() {
trap '' INT # continue forwarding
header_pattern='[0-9]+\.\.[0-9]+'
IFS= read -r header
# repeat the header
printf "%s\n" "$header"
# if we detect a TAP plan
if [[ "$header" =~ $header_pattern ]]; then
# extract the number of tests ...
local expected_number_of_tests="${header:3}"
# ... count the actual number of [not ] oks...
local actual_number_of_tests=0
while IFS= read -r line; do
# forward line
printf "%s\n" "$line"
case "$line" in
'ok '*)
(( ++actual_number_of_tests ))
;;
'not ok'*)
(( ++actual_number_of_tests ))
;;
esac
done
# ... and error if they are not the same
if [[ "${actual_number_of_tests}" != "${expected_number_of_tests}" ]]; then
printf '# bats warning: Executed %s instead of expected %s tests\n' "$actual_number_of_tests" "$expected_number_of_tests"
return 1
fi
else
# forward output unchanged
cat
fi
}

View File

@ -0,0 +1,35 @@
#!/usr/bin/env bash
# shellcheck source=lib/bats-core/tracing.bash
source "$BATS_ROOT/lib/bats-core/tracing.bash"
BATS_WARNING_SHORT_DESCS=(
# to start with 1
'PADDING'
# see issue #578 for context
"\`run\`'s command \`%s\` exited with code 127, indicating 'Command not found'. Use run's return code checks, e.g. \`run -127\`, to fix this message."
"%s requires at least BATS_VERSION=%s. Use \`bats_require_minimum_version %s\` to fix this message."
)
# generate a warning report for the parent call's call site
bats_generate_warning() { # <warning number> [<printf args for warning string>...]
local warning_number="$1" padding="00"
shift
if [[ $warning_number =~ [0-9]+ ]] && ((warning_number < ${#BATS_WARNING_SHORT_DESCS[@]} )); then
{
printf "BW%s: ${BATS_WARNING_SHORT_DESCS[$warning_number]}\n" "${padding:${#warning_number}}${warning_number}" "$@"
bats_capture_stack_trace
BATS_STACK_TRACE_PREFIX=' ' bats_print_stack_trace "${BATS_DEBUG_LAST_STACK_TRACE[@]}"
} >> "$BATS_WARNING_FILE" 2>&3
else
printf "Invalid Bats warning number '%s'. It must be an integer between 1 and %d." "$warning_number" "$((${#BATS_WARNING_SHORT_DESCS[@]} - 1))" >&2
exit 1
fi
}
# generate a warning if the BATS_GUARANTEED_MINIMUM_VERSION is not high enough
bats_warn_minimum_guaranteed_version() { # <feature> <minimum required version>
if bats_version_lt "$BATS_GUARANTEED_MINIMUM_VERSION" "$2"; then
bats_generate_warning 2 "$1" "$2" "$2"
fi
}

View File

@ -0,0 +1,467 @@
#!/usr/bin/env bash
set -e
export BATS_VERSION='1.7.0'
VALID_FORMATTERS="pretty, junit, tap, tap13"
version() {
printf 'Bats %s\n' "$BATS_VERSION"
}
abort() {
local print_usage=1
if [[ ${1:-} == --no-print-usage ]]; then
print_usage=
shift
fi
printf 'Error: %s\n' "$1" >&2
if [[ -n $print_usage ]]; then
usage >&2
fi
exit 1
}
usage() {
local cmd="${0##*/}"
local line
cat <<HELP_TEXT_HEADER
Usage: ${cmd} [OPTIONS] <tests>
${cmd} [-h | -v]
HELP_TEXT_HEADER
cat <<'HELP_TEXT_BODY'
<tests> is the path to a Bats test file, or the path to a directory
containing Bats test files (ending with ".bats")
-c, --count Count test cases without running any tests
--code-quote-style <style>
A two character string of code quote delimiters
or 'custom' which requires setting $BATS_BEGIN_CODE_QUOTE and
$BATS_END_CODE_QUOTE. Can also be set via $BATS_CODE_QUOTE_STYLE
-f, --filter <regex> Only run tests that match the regular expression
--filter-status <status> Only run tests with the given status in the last completed (no CTRL+C/SIGINT) run.
Valid <status> values are:
failed - runs tests that failed or were not present in the last run
missed - runs tests that were not present in the last run
-F, --formatter <type> Switch between formatters: pretty (default),
tap (default w/o term), tap13, junit, /<absolute path to formatter>
--gather-test-outputs-in <directory>
Gather the output of failing *and* passing tests
as files in directory (if existing, must be empty)
-h, --help Display this help message
-j, --jobs <jobs> Number of parallel jobs (requires GNU parallel)
--no-tempdir-cleanup Preserve test output temporary directory
--no-parallelize-across-files
Serialize test file execution instead of running
them in parallel (requires --jobs >1)
--no-parallelize-within-files
Serialize test execution within files instead of
running them in parallel (requires --jobs >1)
--report-formatter <type> Switch between reporters (same options as --formatter)
-o, --output <dir> Directory to write report files (must exist)
-p, --pretty Shorthand for "--formatter pretty"
--print-output-on-failure Automatically print the value of `$output` on failed tests
-r, --recursive Include tests in subdirectories
--show-output-of-passing-tests
Print output of passing tests
-t, --tap Shorthand for "--formatter tap"
-T, --timing Add timing information to tests
-x, --trace Print test commands as they are executed (like `set -x`)
--verbose-run Make `run` print `$output` by default
-v, --version Display the version number
For more information, see https://github.com/bats-core/bats-core
HELP_TEXT_BODY
}
expand_path() {
local path="${1%/}"
local dirname="${path%/*}"
local result="$2"
if [[ "$dirname" == "$path" ]]; then
dirname="$PWD"
else
cd "$dirname"
dirname="$PWD"
cd "$OLDPWD"
fi
printf -v "$result" '%s/%s' "$dirname" "${path##*/}"
}
BATS_LIBEXEC="$(cd "$(dirname "$(bats_readlinkf "${BASH_SOURCE[0]}")")"; pwd)"
export BATS_LIBEXEC
export BATS_CWD="$PWD"
export BATS_TEST_FILTER=
export PATH="$BATS_LIBEXEC:$PATH"
export BATS_ROOT_PID=$$
export BATS_TMPDIR="${TMPDIR:-/tmp}"
BATS_TMPDIR=${BATS_TMPDIR%/} # chop off trailing / to avoid duplication
export BATS_RUN_TMPDIR=
export BATS_GUARANTEED_MINIMUM_VERSION=0.0.0
if [[ ! -d "${BATS_TMPDIR}" ]];then
printf "Error: BATS_TMPDIR (%s) does not exist or is not a directory" "${BATS_TMPDIR}" >&2
exit 1
elif [[ ! -w "${BATS_TMPDIR}" ]];then
printf "Error: BATS_TMPDIR (%s) is not writable" "${BATS_TMPDIR}" >&2
exit 1
fi
arguments=()
# Unpack single-character options bundled together, e.g. -cr, -pr.
for arg in "$@"; do
if [[ "$arg" =~ ^-[^-]. ]]; then
index=1
while option="${arg:$((index++)):1}"; do
if [[ -z "$option" ]]; then
break
fi
arguments+=("-$option")
done
else
arguments+=("$arg")
fi
shift
done
set -- "${arguments[@]}"
arguments=()
unset flags recursive formatter_flags
flags=('--dummy-flag') # add a dummy flag to prevent unset variable errors on empty array expansion in old bash versions
formatter_flags=('--dummy-flag') # add a dummy flag to prevent unset variable errors on empty array expansion in old bash versions
formatter='tap'
report_formatter=''
recursive=
setup_suite_file=''
export BATS_TEMPDIR_CLEANUP=1
output=
if [[ -z "${CI:-}" && -t 0 && -t 1 ]] && command -v tput >/dev/null; then
formatter='pretty'
fi
while [[ "$#" -ne 0 ]]; do
case "$1" in
-h | --help)
version
usage
exit 0
;;
-v | --version)
version
exit 0
;;
-c | --count)
flags+=('-c')
;;
-f | --filter)
shift
flags+=('-f' "$1")
;;
-F | --formatter)
shift
# allow cat formatter to see extended output but don't advertise to users
if [[ $1 =~ ^(pretty|junit|tap|tap13|cat|/.*)$ ]]; then
formatter="$1"
else
printf "Unknown formatter '%s', valid options are %s\n" "$1" "${VALID_FORMATTERS}"
exit 1
fi
;;
--report-formatter)
shift
if [[ $1 =~ ^(pretty|junit|tap|tap13)$ ]]; then
report_formatter="$1"
else
printf "Unknown report formatter '%s', valid options are %s\n" "$1" "${VALID_FORMATTERS}"
exit 1
fi
;;
-o | --output)
shift
output="$1"
;;
-p | --pretty)
formatter='pretty'
;;
-j | --jobs)
shift
flags+=('-j' "$1")
;;
-r | --recursive)
recursive=1
;;
-t | --tap)
formatter='tap'
;;
-T | --timing)
flags+=('-T')
formatter_flags+=('-T')
;;
# this flag is now a no-op, as it is the parallel default
--parallel-preserve-environment)
;;
--no-parallelize-across-files)
flags+=("--no-parallelize-across-files")
;;
--no-parallelize-within-files)
flags+=("--no-parallelize-within-files")
;;
--no-tempdir-cleanup)
BATS_TEMPDIR_CLEANUP=''
;;
--tempdir) # for internal test consumption only!
BATS_RUN_TMPDIR="$2"
shift
;;
-x | --trace)
flags+=(--trace)
;;
--print-output-on-failure)
flags+=(--print-output-on-failure)
;;
--show-output-of-passing-tests)
flags+=(--show-output-of-passing-tests)
;;
--verbose-run)
flags+=(--verbose-run)
;;
--gather-test-outputs-in)
shift
output_dir="$1"
if [ -d "$output_dir" ]; then
if ! find "$output_dir" -mindepth 1 -exec false {} + 2>/dev/null; then
abort --no-print-usage "Directory '$output_dir' must be empty for --gather-test-outputs-in"
fi
elif ! mkdir "$output_dir" 2>/dev/null; then
abort --no-print-usage "Could not create '$output_dir' for --gather-test-outputs-in"
fi
flags+=(--gather-test-outputs-in "$output_dir")
;;
--setup-suite-file)
shift
setup_suite_file="$1"
;;
--code-quote-style)
shift
BATS_CODE_QUOTE_STYLE="$1"
;;
--filter-status)
shift
flags+=('--filter-status' "$1")
;;
-*)
abort "Bad command line option '$1'"
;;
*)
arguments+=("$1")
;;
esac
shift
done
if [[ -n "${BATS_RUN_TMPDIR:-}" ]];then
if [[ -d "$BATS_RUN_TMPDIR" ]]; then
printf "Error: BATS_RUN_TMPDIR (%s) already exists\n" "$BATS_RUN_TMPDIR" >&2
printf "Reusing old run directories can lead to unexpected results ... aborting!\n" >&2
exit 1
elif ! mkdir -p "$BATS_RUN_TMPDIR" ;then
printf "Error: Failed to create BATS_RUN_TMPDIR (%s)\n" "$BATS_RUN_TMPDIR" >&2
exit 1
fi
elif ! BATS_RUN_TMPDIR=$(mktemp -d "${BATS_TMPDIR}/bats-run-XXXXXX");then
printf "Error: Failed to create BATS_RUN_TMPDIR (%s) with mktemp\n" "${BATS_TMPDIR}/bats-run-XXXXXX" >&2
exit 1
fi
export BATS_WARNING_FILE="${BATS_RUN_TMPDIR}/warnings.log"
bats_exit_trap() {
if [[ -s "$BATS_WARNING_FILE" ]]; then
local pre_cat='' post_cat=''
if [[ $formatter == pretty ]]; then
pre_cat=$'\x1B[31m'
post_cat=$'\x1B[0m'
fi
printf "\nThe following warnings were encountered during tests:\n%s" "$pre_cat"
cat "$BATS_WARNING_FILE"
printf "%s" "$post_cat"
fi >&2
if [[ -n "$BATS_TEMPDIR_CLEANUP" ]]; then
rm -rf "$BATS_RUN_TMPDIR"
else
printf "BATS_RUN_TMPDIR: %s\n" "$BATS_RUN_TMPDIR" >&2
fi
}
trap bats_exit_trap EXIT
if [[ "$formatter" != "tap" ]]; then
flags+=('-x')
fi
if [[ -n "$report_formatter" && "$report_formatter" != "tap" ]]; then
flags+=('-x')
fi
if [[ "$formatter" == "junit" ]]; then
flags+=('-T')
formatter_flags+=('--base-path' "${arguments[0]}")
fi
if [[ "$report_formatter" == "junit" ]]; then
flags+=('-T')
report_formatter_flags+=('--base-path' "${arguments[0]}")
fi
if [[ "$formatter" == "pretty" ]]; then
formatter_flags+=('--base-path' "${arguments[0]}")
fi
# if we don't need to filter extended syntax, use the faster formatter
if [[ "$formatter" == tap && -z "$report_formatter" ]]; then
formatter="cat"
fi
bats_check_formatter() { # <formatter-path>
local -r formatter="$1"
if [[ ! -f "$formatter" ]]; then
printf "ERROR: Formatter '%s' is not readable!\n" "$formatter"
exit 1
elif [[ ! -x "$formatter" ]]; then
printf "ERROR: Formatter '%s' is not executable!\n" "$formatter"
exit 1
fi
}
if [[ $formatter == /* ]]; then # absolute paths are direct references to formatters
bats_check_formatter "$formatter"
interpolated_formatter="$formatter"
else
interpolated_formatter="bats-format-${formatter}"
fi
if [[ "${#arguments[@]}" -eq 0 ]]; then
abort 'Must specify at least one <test>'
fi
if [[ -n "$report_formatter" ]]; then
# default to the current directory for output
if [[ -z "$output" ]]; then
output=.
fi
# only set BATS_REPORT_FILENAME if none was given
if [[ -z "${BATS_REPORT_FILENAME:-}" ]]; then
case "$report_formatter" in
tap|tap13)
BATS_REPORT_FILE_NAME="report.tap"
;;
junit)
BATS_REPORT_FILE_NAME="report.xml"
;;
*)
BATS_REPORT_FILE_NAME="report.log"
;;
esac
fi
fi
if [[ $report_formatter == /* ]]; then # absolute paths are direct references to formatters
bats_check_formatter "$report_formatter"
interpolated_report_formatter="${report_formatter}"
else
interpolated_report_formatter="bats-format-${report_formatter}"
fi
if [[ "${BATS_CODE_QUOTE_STYLE-BATS_CODE_QUOTE_STYLE_UNSET}" == BATS_CODE_QUOTE_STYLE_UNSET ]]; then
BATS_CODE_QUOTE_STYLE="\`'"
fi
case "${BATS_CODE_QUOTE_STYLE}" in
??)
BATS_BEGIN_CODE_QUOTE="${BATS_CODE_QUOTE_STYLE::1}"
BATS_END_CODE_QUOTE="${BATS_CODE_QUOTE_STYLE:1:1}"
export BATS_BEGIN_CODE_QUOTE BATS_END_CODE_QUOTE
;;
custom)
if [[ ${BATS_BEGIN_CODE_QUOTE-BATS_BEGIN_CODE_QUOTE_UNSET} == BATS_BEGIN_CODE_QUOTE_UNSET
|| ${BATS_END_CODE_QUOTE-BATS_BEGIN_CODE_QUOTE_UNSET} == BATS_BEGIN_CODE_QUOTE_UNSET ]]; then
printf "ERROR: BATS_CODE_QUOTE_STYLE=custom requires BATS_BEGIN_CODE_QUOTE and BATS_END_CODE_QUOTE to be set\n" >&2
exit 1
fi
;;
*)
printf "ERROR: Unknown BATS_CODE_QUOTE_STYLE: %s\n" "$BATS_CODE_QUOTE_STYLE" >&2
exit 1
;;
esac
if [[ -n "$output" ]]; then
if [[ ! -w "${output}" ]]; then
abort "Output path ${output} is not writeable"
fi
export BATS_REPORT_OUTPUT_PATH="$output"
fi
if [[ -n "$setup_suite_file" && ! -f "$setup_suite_file" ]]; then
abort "--setup-suite-file $setup_suite_file does not exist!"
fi
filenames=()
for filename in "${arguments[@]}"; do
expand_path "$filename" 'filename'
if [[ -z "$setup_suite_file" ]]; then
if [[ -d "$filename" ]]; then
dirname="$filename"
else
dirname="${filename%/*}"
fi
potential_setup_suite_file="$dirname/setup_suite.bash"
if [[ -e "$potential_setup_suite_file" ]]; then
setup_suite_file="$potential_setup_suite_file"
fi
fi
if [[ -d "$filename" ]]; then
shopt -s nullglob
if [[ "$recursive" -eq 1 ]]; then
while IFS= read -r -d $'\0' file; do
filenames+=("$file")
done < <(find -L "$filename" -type f -name "*.${BATS_FILE_EXTENSION:-bats}" -print0 | sort -z)
else
for suite_filename in "$filename"/*."${BATS_FILE_EXTENSION:-bats}"; do
filenames+=("$suite_filename")
done
fi
shopt -u nullglob
else
filenames+=("$filename")
fi
done
if [[ -n "$setup_suite_file" ]]; then
flags+=("--setup-suite-file" "$setup_suite_file")
fi
# shellcheck source=lib/bats-core/validator.bash
source "$BATS_ROOT/lib/bats-core/validator.bash"
trap 'BATS_INTERRUPTED=true' INT # let the lower levels handle the interruption
set -o pipefail execfail
if [[ -n "$report_formatter" ]]; then
exec bats-exec-suite "${flags[@]}" "${filenames[@]}" | \
tee >("$interpolated_report_formatter" "${report_formatter_flags[@]}" >"${BATS_REPORT_OUTPUT_PATH}/${BATS_REPORT_FILE_NAME}") | \
bats_test_count_validator | \
"$interpolated_formatter" "${formatter_flags[@]}"
else
exec bats-exec-suite "${flags[@]}" "${filenames[@]}" | \
bats_test_count_validator | \
"$interpolated_formatter" "${formatter_flags[@]}"
fi

View File

@ -0,0 +1,338 @@
#!/usr/bin/env bash
set -eET
flags=('--dummy-flag')
num_jobs=${BATS_NUMBER_OF_PARALLEL_JOBS:-1}
extended_syntax=''
BATS_TRACE_LEVEL="${BATS_TRACE_LEVEL:-0}"
declare -r BATS_RETRY_RETURN_CODE=126
export BATS_TEST_RETRIES=0 # no retries by default
while [[ "$#" -ne 0 ]]; do
case "$1" in
-j)
shift
num_jobs="$1"
;;
-T)
flags+=('-T')
;;
-x)
flags+=('-x')
extended_syntax=1
;;
--no-parallelize-within-files)
# use singular to allow for users to override in file
BATS_NO_PARALLELIZE_WITHIN_FILE=1
;;
--dummy-flag)
;;
--trace)
flags+=('--trace')
;;
--print-output-on-failure)
flags+=(--print-output-on-failure)
;;
--show-output-of-passing-tests)
flags+=(--show-output-of-passing-tests)
;;
--verbose-run)
flags+=(--verbose-run)
;;
--gather-test-outputs-in)
shift
flags+=(--gather-test-outputs-in "$1")
;;
*)
break
;;
esac
shift
done
filename="$1"
TESTS_FILE="$2"
if [[ ! -f "$filename" ]]; then
printf 'Testfile "%s" not found\n' "$filename" >&2
exit 1
fi
export BATS_TEST_FILENAME="$filename"
# shellcheck source=lib/bats-core/preprocessing.bash
# shellcheck disable=SC2153
source "$BATS_ROOT/lib/bats-core/preprocessing.bash"
bats_run_setup_file() {
# shellcheck source=lib/bats-core/tracing.bash
# shellcheck disable=SC2153
source "$BATS_ROOT/lib/bats-core/tracing.bash"
# shellcheck source=lib/bats-core/test_functions.bash
# shellcheck disable=SC2153
source "$BATS_ROOT/lib/bats-core/test_functions.bash"
exec 3<&1
# these are defined only to avoid errors when referencing undefined variables down the line
# shellcheck disable=2034
BATS_TEST_NAME= # used in tracing.bash
# shellcheck disable=2034
BATS_TEST_COMPLETED= # used in tracing.bash
BATS_SOURCE_FILE_COMPLETED=
BATS_SETUP_FILE_COMPLETED=
BATS_TEARDOWN_FILE_COMPLETED=
# shellcheck disable=2034
BATS_ERROR_STATUS= # used in tracing.bash
touch "$BATS_OUT"
bats_setup_tracing
trap 'bats_file_teardown_trap' EXIT
local status=0
# get the setup_file/teardown_file functions for this file (if it has them)
# shellcheck disable=SC1090
source "$BATS_TEST_SOURCE" >>"$BATS_OUT" 2>&1
BATS_SOURCE_FILE_COMPLETED=1
setup_file >>"$BATS_OUT" 2>&1
BATS_SETUP_FILE_COMPLETED=1
}
bats_run_teardown_file() {
# avoid running the therdown trap due to errors in teardown_file
trap 'bats_file_exit_trap' EXIT
# rely on bats_error_trap to catch failures
teardown_file >>"$BATS_OUT" 2>&1
BATS_TEARDOWN_FILE_COMPLETED=1
}
bats_file_teardown_trap() {
bats_run_teardown_file
bats_file_exit_trap
}
# shellcheck source=lib/bats-core/common.bash
source "$BATS_ROOT/lib/bats-core/common.bash"
bats_file_exit_trap() {
trap - ERR EXIT
local failure_reason
local -i failure_test_index=$(( BATS_FILE_FIRST_TEST_NUMBER_IN_SUITE + 1 ))
if [[ -z "$BATS_SETUP_FILE_COMPLETED" || -z "$BATS_TEARDOWN_FILE_COMPLETED" ]]; then
if [[ -z "$BATS_SETUP_FILE_COMPLETED" ]]; then
failure_reason='setup_file'
elif [[ -z "$BATS_TEARDOWN_FILE_COMPLETED" ]]; then
failure_reason='teardown_file'
failure_test_index=$(( BATS_FILE_FIRST_TEST_NUMBER_IN_SUITE + ${#tests_to_run[@]} + 1 ))
elif [[ -z "$BATS_SOURCE_FILE_COMPLETED" ]]; then
failure_reason='source'
else
failure_reason='unknown internal'
fi
printf "not ok %d %s\n" "$failure_test_index" "$failure_reason failed" >&3
local stack_trace
bats_get_failure_stack_trace stack_trace
bats_print_stack_trace "${stack_trace[@]}" >&3
bats_print_failed_command "${stack_trace[@]}" >&3
bats_prefix_lines_for_tap_output < "$BATS_OUT" | bats_replace_filename >&3
rm -rf "$BATS_OUT"
bats_exec_file_status=1
fi
exit $bats_exec_file_status
}
function setup_file() {
return 0
}
function teardown_file() {
return 0
}
bats_forward_output_of_parallel_test() {
local test_number_in_suite=$1
local status=0
wait "$(cat "$output_folder/$test_number_in_suite/pid")" || status=1
cat "$output_folder/$test_number_in_suite/stdout"
cat "$output_folder/$test_number_in_suite/stderr" >&2
return $status
}
bats_is_next_parallel_test_finished() {
local PID
# get the pid of the next potentially finished test
PID=$(cat "$output_folder/$(( test_number_in_suite_of_last_finished_test + 1 ))/pid")
# try to send a signal to this process
# if it fails, the process exited,
# if it succeeds, the process is still running
if kill -0 "$PID" 2>/dev/null; then
return 1
fi
}
# prints output from all tests in the order they were started
# $1 == "blocking": wait for a test to finish before printing
# != "blocking": abort printing, when a test has not finished
bats_forward_output_for_parallel_tests() {
local status=0
# was the next test already started?
while (( test_number_in_suite_of_last_finished_test + 1 <= test_number_in_suite )); do
# if we are okay with waiting or if the test has already been finished
if [[ "$1" == "blocking" ]] || bats_is_next_parallel_test_finished ; then
(( ++test_number_in_suite_of_last_finished_test ))
bats_forward_output_of_parallel_test "$test_number_in_suite_of_last_finished_test" || status=$?
else
# non-blocking and the process has not finished -> abort the printing
break
fi
done
return $status
}
bats_run_test_with_retries() { # <args>
local status=0
local should_try_again=1 try_number
for ((try_number=1; should_try_again; ++try_number)); do
if "$BATS_LIBEXEC/bats-exec-test" "$@" "$try_number"; then
should_try_again=0
else
status=$?
if ((status == BATS_RETRY_RETURN_CODE)); then
should_try_again=1
else
should_try_again=0
bats_exec_file_status=$status
fi
fi
done
return $status
}
bats_run_tests_in_parallel() {
local output_folder="$BATS_RUN_TMPDIR/parallel_output"
local status=0
mkdir -p "$output_folder"
# shellcheck source=lib/bats-core/semaphore.bash
source "$BATS_ROOT/lib/bats-core/semaphore.bash"
bats_semaphore_setup
# the test_number_in_file is not yet incremented -> one before the next test to run
local test_number_in_suite_of_last_finished_test="$BATS_FILE_FIRST_TEST_NUMBER_IN_SUITE" # stores which test was printed last
local test_number_in_file=0 test_number_in_suite=$BATS_FILE_FIRST_TEST_NUMBER_IN_SUITE
for test_name in "${tests_to_run[@]}"; do
# Only handle non-empty lines
if [[ $test_name ]]; then
((++test_number_in_suite))
((++test_number_in_file))
mkdir -p "$output_folder/$test_number_in_suite"
bats_semaphore_run "$output_folder/$test_number_in_suite" \
bats_run_test_with_retries "${flags[@]}" "$filename" "$test_name" "$test_number_in_suite" "$test_number_in_file" \
> "$output_folder/$test_number_in_suite/pid"
fi
# print results early to get interactive feedback
bats_forward_output_for_parallel_tests non-blocking || status=1 # ignore if we did not finish yet
done
bats_forward_output_for_parallel_tests blocking || status=1
return $status
}
bats_read_tests_list_file() {
local line_number=0
tests_to_run=()
# the global test number must be visible to traps -> not local
local test_number_in_suite=''
while read -r test_line; do
# check if the line begins with filename
# filename might contain some hard to parse characters,
# use simple string operations to work around that issue
if [[ "$filename" == "${test_line::${#filename}}" ]]; then
# get the rest of the line without the separator \t
test_name=${test_line:$((1 + ${#filename} ))}
tests_to_run+=("$test_name")
# save the first test's number for later iteration
# this assumes that tests for a file are stored consecutive in the file!
if [[ -z "$test_number_in_suite" ]]; then
test_number_in_suite=$line_number
fi
fi
((++line_number))
done <"$TESTS_FILE"
BATS_FILE_FIRST_TEST_NUMBER_IN_SUITE="$test_number_in_suite"
declare -ri BATS_FILE_FIRST_TEST_NUMBER_IN_SUITE # mark readonly (cannot merge assignment, because value would be lost)
}
bats_run_tests() {
bats_exec_file_status=0
if [[ "$num_jobs" != 1 && "${BATS_NO_PARALLELIZE_WITHIN_FILE-False}" == False ]]; then
export BATS_SEMAPHORE_NUMBER_OF_SLOTS="$num_jobs"
bats_run_tests_in_parallel "$BATS_RUN_TMPDIR/parallel_output" || bats_exec_file_status=1
else
local test_number_in_suite=$BATS_FILE_FIRST_TEST_NUMBER_IN_SUITE \
test_number_in_file=0
for test_name in "${tests_to_run[@]}"; do
if [[ "${BATS_INTERRUPTED-NOTSET}" != NOTSET ]]; then
bats_exec_file_status=130 # bash's code for SIGINT exits
break
fi
# Only handle non-empty lines
if [[ $test_name ]]; then
((++test_number_in_suite))
((++test_number_in_file))
bats_run_test_with_retries "${flags[@]}" "$filename" "$test_name" \
"$test_number_in_suite" "$test_number_in_file" || bats_exec_file_status=$?
fi
done
fi
}
bats_create_file_tempdirs() {
local bats_files_tmpdir="${BATS_RUN_TMPDIR}/file"
if ! mkdir -p "$bats_files_tmpdir"; then
printf 'Failed to create %s\n' "$bats_files_tmpdir" >&2
exit 1
fi
BATS_FILE_TMPDIR="$bats_files_tmpdir/${BATS_FILE_FIRST_TEST_NUMBER_IN_SUITE?}"
if ! mkdir "$BATS_FILE_TMPDIR"; then
printf 'Failed to create BATS_FILE_TMPDIR=%s\n' "$BATS_FILE_TMPDIR" >&2
exit 1
fi
ln -s "$BATS_TEST_FILENAME" "$BATS_FILE_TMPDIR-$(basename "$BATS_TEST_FILENAME").source_file"
export BATS_FILE_TMPDIR
}
trap 'BATS_INTERRUPTED=true' INT
if [[ -n "$extended_syntax" ]]; then
printf "suite %s\n" "$filename"
fi
BATS_FILE_FIRST_TEST_NUMBER_IN_SUITE=0 # predeclare as Bash 3.2 does not support declare -g
bats_read_tests_list_file
# don't run potentially expensive setup/teardown_file
# when there are no tests to run
if [[ ${#tests_to_run[@]} -eq 0 ]]; then
exit 0
fi
# requires the test list to be read but not empty
bats_create_file_tempdirs
bats_preprocess_source "$filename"
trap bats_interrupt_trap INT
bats_run_setup_file
# during tests, we don't want to get backtraces from this level
# just wait for the test to be interrupted and display their trace
trap 'BATS_INTERRUPTED=true' INT
bats_run_tests
trap bats_interrupt_trap INT
bats_run_teardown_file
exit $bats_exec_file_status

View File

@ -0,0 +1,359 @@
#!/usr/bin/env bash
set -e
count_only_flag=''
filter=''
num_jobs=${BATS_NUMBER_OF_PARALLEL_JOBS:-1}
bats_no_parallelize_across_files=${BATS_NO_PARALLELIZE_ACROSS_FILES-}
bats_no_parallelize_within_files=
filter_status=''
flags=('--dummy-flag') # add a dummy flag to prevent unset variable errors on empty array expansion in old bash versions
setup_suite_file=''
BATS_TRACE_LEVEL="${BATS_TRACE_LEVEL:-0}"
abort() {
printf 'Error: %s\n' "$1" >&2
exit 1
}
while [[ "$#" -ne 0 ]]; do
case "$1" in
-c)
count_only_flag=1
;;
-f)
shift
filter="$1"
;;
-j)
shift
num_jobs="$1"
flags+=('-j' "$num_jobs")
;;
-T)
flags+=('-T')
;;
-x)
flags+=('-x')
;;
--no-parallelize-across-files)
bats_no_parallelize_across_files=1
;;
--no-parallelize-within-files)
bats_no_parallelize_within_files=1
flags+=("--no-parallelize-within-files")
;;
--filter-status)
shift
filter_status="$1"
;;
--dummy-flag)
;;
--trace)
flags+=('--trace')
(( ++BATS_TRACE_LEVEL )) # avoid returning 0
;;
--print-output-on-failure)
flags+=(--print-output-on-failure)
;;
--show-output-of-passing-tests)
flags+=(--show-output-of-passing-tests)
;;
--verbose-run)
flags+=(--verbose-run)
;;
--gather-test-outputs-in)
shift
flags+=(--gather-test-outputs-in "$1")
;;
--setup-suite-file)
shift
setup_suite_file="$1"
;;
*)
break
;;
esac
shift
done
if [[ "$num_jobs" != 1 ]]; then
if ! type -p parallel >/dev/null && [[ -z "$bats_no_parallelize_across_files" ]]; then
abort "Cannot execute \"${num_jobs}\" jobs without GNU parallel"
exit 1
fi
# shellcheck source=lib/bats-core/semaphore.bash
source "${BATS_ROOT}/lib/bats-core/semaphore.bash"
bats_semaphore_setup
fi
# create a file that contains all (filtered) tests to run from all files
TESTS_LIST_FILE="${BATS_RUN_TMPDIR}/test_list_file.txt"
bats_gather_tests() {
all_tests=()
for filename in "$@"; do
if [[ ! -f "$filename" ]]; then
abort "Test file \"${filename}\" does not exist"
fi
test_names=()
test_dupes=()
while read -r line; do
if [[ ! "$line" =~ ^bats_test_function\ ]]; then
continue
fi
line="${line%$'\r'}"
line="${line#* }"
test_line=$(printf "%s\t%s" "$filename" "$line")
all_tests+=("$test_line")
printf "%s\n" "$test_line" >>"$TESTS_LIST_FILE"
# avoid unbound variable errors on empty array expansion with old bash versions
if [[ ${#test_names[@]} -gt 0 && " ${test_names[*]} " == *" $line "* ]]; then
test_dupes+=("$line")
continue
fi
test_names+=("$line")
done < <(BATS_TEST_FILTER="$filter" bats-preprocess "$filename")
if [[ "${#test_dupes[@]}" -ne 0 ]]; then
abort "Duplicate test name(s) in file \"${filename}\": ${test_dupes[*]}"
fi
done
test_count="${#all_tests[@]}"
}
TEST_ROOT=${1-}
TEST_ROOT=${TEST_ROOT%/*}
BATS_RUN_LOGS_DIRECTORY="$TEST_ROOT/.bats/run-logs"
if [[ ! -d "$BATS_RUN_LOGS_DIRECTORY" ]]; then
if [[ -n "$filter_status" ]]; then
printf "Error: --filter-status needs '%s/' to save failed tests. Please create this folder, add it to .gitignore and try again.\n" "$BATS_RUN_LOGS_DIRECTORY"
exit 1
else
BATS_RUN_LOGS_DIRECTORY=
fi
# discard via sink instead of having a conditional later
export BATS_RUNLOG_FILE='/dev/null'
else
# use UTC (-u) to avoid problems with TZ changes
BATS_RUNLOG_DATE=$(date -u '+%Y-%m-%d %H:%M:%S UTC')
export BATS_RUNLOG_FILE="$BATS_RUN_LOGS_DIRECTORY/${BATS_RUNLOG_DATE}.log"
fi
bats_gather_tests "$@"
if [[ -n "$filter_status" ]]; then
# shellcheck source=lib/bats-core/common.bash
source "$BATS_ROOT/lib/bats-core/common.bash"
case "$filter_status" in
failed)
bats_filter_test_by_status() { # <line>
! bats_binary_search "$1" "passed_tests"
}
;;
passed)
bats_filter_test_by_status() {
! bats_binary_search "$1" "failed_tests"
}
;;
missed)
bats_filter_test_by_status() {
! bats_binary_search "$1" "failed_tests" && ! bats_binary_search "$1" "passed_tests"
}
;;
*)
printf "Error: Unknown value '%s' for --filter-status. Valid values are 'failed' and 'missed'.\n" "$filter_status">&2
exit 1
;;
esac
if IFS='' read -d $'\n' -r BATS_PREVIOUS_RUNLOG_FILE < <(ls -1r "$BATS_RUN_LOGS_DIRECTORY"); then
BATS_PREVIOUS_RUNLOG_FILE="$BATS_RUN_LOGS_DIRECTORY/$BATS_PREVIOUS_RUNLOG_FILE"
if [[ $BATS_PREVIOUS_RUNLOG_FILE == "$BATS_RUNLOG_FILE" ]]; then
count=$(find "$BATS_RUN_LOGS_DIRECTORY" -name "$BATS_RUNLOG_DATE*" | wc -l)
BATS_RUNLOG_FILE="$BATS_RUN_LOGS_DIRECTORY/${BATS_RUNLOG_DATE}-$count.log"
fi
failed_tests=()
passed_tests=()
# store tests that were already filtered out in the last run for the same filter reason
last_filtered_tests=()
i=0
while read -rd $'\n' line; do
((++i))
case "$line" in
"passed "*)
passed_tests+=("${line#passed }")
;;
"failed "*)
failed_tests+=("${line#failed }")
;;
"status-filtered $filter_status"*) # pick up tests that were filtered in the last round for the same status
last_filtered_tests+=("${line#status-filtered "$filter_status" }")
;;
"status-filtered "*) # ignore other status-filtered lines
;;
"#"*) # allow for comments
;;
*)
printf "Error: %s:%d: Invalid format: %s\n" "$BATS_PREVIOUS_RUNLOG_FILE" "$i" "$line" >&2
exit 1
;;
esac
done < <(sort "$BATS_PREVIOUS_RUNLOG_FILE")
filtered_tests=()
for line in "${all_tests[@]}"; do
if bats_filter_test_by_status "$line" && ! bats_binary_search "$line" last_filtered_tests; then
printf "%s\n" "$line"
filtered_tests+=("$line")
else
printf "status-filtered %s %s\n" "$filter_status" "$line" >> "$BATS_RUNLOG_FILE"
fi
done > "$TESTS_LIST_FILE"
# save filtered tests to exclude them again in next round
for test_line in "${last_filtered_tests[@]}"; do
printf "status-filtered %s %s\n" "$filter_status" "$test_line"
done >> "$BATS_RUNLOG_FILE"
test_count="${#filtered_tests[@]}"
if [[ ${#failed_tests[@]} -eq 0 && ${#filtered_tests[@]} -eq 0 ]]; then
printf "There where no failed tests in the last recorded run.\n" >&2
fi
else
printf "No recording of previous runs found. Running all tests!\n" >&2
fi
fi
if [[ -n "$count_only_flag" ]]; then
printf '%d\n' "${test_count}"
exit
fi
if [[ -n "$bats_no_parallelize_across_files" ]] && [[ ! "$num_jobs" -gt 1 ]]; then
abort "The flag --no-parallelize-across-files requires at least --jobs 2"
exit 1
fi
if [[ -n "$bats_no_parallelize_within_files" ]] && [[ ! "$num_jobs" -gt 1 ]]; then
abort "The flag --no-parallelize-across-files requires at least --jobs 2"
exit 1
fi
# only abort on the lowest levels
trap 'BATS_INTERRUPTED=true' INT
bats_exec_suite_status=0
printf '1..%d\n' "${test_count}"
# No point on continuing if there's no tests.
if [[ "${test_count}" == 0 ]]; then
exit
fi
export BATS_SUITE_TMPDIR="${BATS_RUN_TMPDIR}/suite"
if ! mkdir "$BATS_SUITE_TMPDIR"; then
printf '%s\n' "Failed to create BATS_SUITE_TMPDIR" >&2
exit 1
fi
# Deduplicate filenames (without reordering) to avoid running duplicate tests n by n times.
# (see https://github.com/bats-core/bats-core/issues/329)
# If a file was specified multiple times, we already got it repeatedly in our TESTS_LIST_FILE.
# Thus, it suffices to bats-exec-file it once to run all repeated tests on it.
IFS=$'\n' read -d '' -r -a BATS_UNIQUE_TEST_FILENAMES < <(printf "%s\n" "$@"| nl | sort -k 2 | uniq -f 1 | sort -n | cut -f 2-) || true
# shellcheck source=lib/bats-core/tracing.bash
source "$BATS_ROOT/lib/bats-core/tracing.bash"
bats_setup_tracing
trap bats_suite_exit_trap EXIT
bats_suite_exit_trap() {
if [[ -z "${BATS_SETUP_SUITE_COMPLETED}" || -z "${BATS_TEARDOWN_SUITE_COMPLETED}" ]]; then
if [[ -z "${BATS_SETUP_SUITE_COMPLETED}" ]]; then
printf "not ok 1 setup_suite\n"
elif [[ -z "${BATS_TEARDOWN_SUITE_COMPLETED}" ]]; then
printf "not ok %d teardown_suite\n" $((test_count+1))
fi
local stack_trace
bats_get_failure_stack_trace stack_trace
bats_print_stack_trace "${stack_trace[@]}"
bats_print_failed_command "${stack_trace[@]}"
bats_exec_suite_status=1
fi
if [[ ${BATS_INTERRUPTED-NOTSET} != NOTSET ]]; then
printf "\n# Received SIGINT, aborting ...\n\n"
fi
if [[ -d "$BATS_RUN_LOGS_DIRECTORY" && -n "${BATS_INTERRUPTED:-}" ]]; then
# aborting a test run with CTRL+C does not save the runlog file
rm "$BATS_RUNLOG_FILE"
fi
exit "$bats_exec_suite_status"
}
bats_run_teardown_suite() {
# avoid being called twice, in case this is not called through bats_teardown_suite_trap
# but from the end of file
trap bats_suite_exit_trap EXIT
set -eET
BATS_TEARDOWN_SUITE_COMPLETED=
teardown_suite 2>&1
BATS_TEARDOWN_SUITE_COMPLETED=1
set +ET
}
bats_teardown_suite_trap() {
bats_run_teardown_suite
bats_suite_exit_trap
}
setup_suite() {
:
}
teardown_suite() {
:
}
trap bats_teardown_suite_trap EXIT
if [[ -n "$setup_suite_file" ]]; then
setup_suite() {
printf "%s does not define \`setup_suite()\`\n" "$setup_suite_file" >&2
exit 1
}
# shellcheck disable=SC1090
source "$setup_suite_file"
fi
set -eET
BATS_SETUP_SUITE_COMPLETED=
setup_suite 2>&1
BATS_SETUP_SUITE_COMPLETED=1
set +ET
if [[ "$num_jobs" -gt 1 ]] && [[ -z "$bats_no_parallelize_across_files" ]]; then
# run files in parallel to get the maximum pool of parallel tasks
# shellcheck disable=SC2086,SC2068
# we need to handle the quoting of ${flags[@]} ourselves,
# because parallel can only quote it as one
parallel --keep-order --jobs "$num_jobs" bats-exec-file "$(printf "%q " "${flags[@]}")" "{}" "$TESTS_LIST_FILE" ::: "${BATS_UNIQUE_TEST_FILENAMES[@]}" 2>&1 || bats_exec_suite_status=1
else
for filename in "${BATS_UNIQUE_TEST_FILENAMES[@]}"; do
if [[ "${BATS_INTERRUPTED-NOTSET}" != NOTSET ]]; then
bats_exec_suite_status=130 # bash's code for SIGINT exits
break
fi
bats-exec-file "${flags[@]}" "$filename" "${TESTS_LIST_FILE}" || bats_exec_suite_status=1
done
fi
set -eET
bats_run_teardown_suite
exit "$bats_exec_suite_status"

View File

@ -0,0 +1,231 @@
#!/usr/bin/env bash
set -eET
# Variables used in other scripts.
BATS_ENABLE_TIMING=''
BATS_EXTENDED_SYNTAX=''
BATS_TRACE_LEVEL="${BATS_TRACE_LEVEL:-0}"
BATS_PRINT_OUTPUT_ON_FAILURE="${BATS_PRINT_OUTPUT_ON_FAILURE:-}"
BATS_SHOW_OUTPUT_OF_SUCCEEDING_TESTS="${BATS_SHOW_OUTPUT_OF_SUCCEEDING_TESTS:-}"
BATS_VERBOSE_RUN="${BATS_VERBOSE_RUN:-}"
BATS_GATHER_TEST_OUTPUTS_IN="${BATS_GATHER_TEST_OUTPUTS_IN:-}"
BATS_TEST_NAME_PREFIX="${BATS_TEST_NAME_PREFIX:-}"
while [[ "$#" -ne 0 ]]; do
case "$1" in
-T)
BATS_ENABLE_TIMING='-T'
;;
-x)
# shellcheck disable=SC2034
BATS_EXTENDED_SYNTAX='-x'
;;
--dummy-flag)
;;
--trace)
(( ++BATS_TRACE_LEVEL )) # avoid returning 0
;;
--print-output-on-failure)
BATS_PRINT_OUTPUT_ON_FAILURE=1
;;
--show-output-of-passing-tests)
BATS_SHOW_OUTPUT_OF_SUCCEEDING_TESTS=1
;;
--verbose-run)
BATS_VERBOSE_RUN=1
;;
--gather-test-outputs-in)
shift
BATS_GATHER_TEST_OUTPUTS_IN="$1"
;;
*)
break
;;
esac
shift
done
export BATS_TEST_FILENAME="$1"
export BATS_TEST_NAME="$2"
export BATS_SUITE_TEST_NUMBER="$3"
export BATS_TEST_NUMBER="$4"
BATS_TEST_TRY_NUMBER="$5"
if [[ -z "$BATS_TEST_FILENAME" ]]; then
printf 'usage: bats-exec-test <filename>\n' >&2
exit 1
elif [[ ! -f "$BATS_TEST_FILENAME" ]]; then
printf 'bats: %s does not exist\n' "$BATS_TEST_FILENAME" >&2
exit 1
fi
bats_create_test_tmpdirs() {
local tests_tmpdir="${BATS_RUN_TMPDIR}/test"
if ! mkdir -p "$tests_tmpdir"; then
printf 'Failed to create: %s\n' "$tests_tmpdir" >&2
exit 1
fi
BATS_TEST_TMPDIR="$tests_tmpdir/$BATS_SUITE_TEST_NUMBER"
if ! mkdir "$BATS_TEST_TMPDIR"; then
printf 'Failed to create BATS_TEST_TMPDIR%d: %s\n' "$BATS_TEST_TRY_NUMBER" "$BATS_TEST_TMPDIR" >&2
exit 1
fi
printf "%s\n" "$BATS_TEST_NAME" > "$BATS_TEST_TMPDIR.name"
export BATS_TEST_TMPDIR
}
# load the test helper functions like `load` or `run` that are needed to run a (preprocessed) .bats file without bash errors
# shellcheck source=lib/bats-core/test_functions.bash disable=SC2153
source "$BATS_ROOT/lib/bats-core/test_functions.bash"
# shellcheck source=lib/bats-core/tracing.bash disable=SC2153
source "$BATS_ROOT/lib/bats-core/tracing.bash"
bats_teardown_trap() {
bats_check_status_from_trap
local bats_teardown_trap_status=0
# mark the start of this function to distinguish where skip is called
# parameter 1 will signify the reason why this function was called
# this is used to identify when this is called as exit trap function
BATS_TEARDOWN_STARTED=${1:-1}
teardown >>"$BATS_OUT" 2>&1 || bats_teardown_trap_status="$?"
if [[ $bats_teardown_trap_status -eq 0 ]]; then
BATS_TEARDOWN_COMPLETED=1
elif [[ -n "$BATS_TEST_COMPLETED" ]]; then
BATS_DEBUG_LAST_STACK_TRACE_IS_VALID=1
BATS_ERROR_STATUS="$bats_teardown_trap_status"
fi
bats_exit_trap
}
# shellcheck source=lib/bats-core/common.bash
source "$BATS_ROOT/lib/bats-core/common.bash"
bats_exit_trap() {
local status
local skipped=''
trap - ERR EXIT
if [[ -n "$BATS_TEST_SKIPPED" ]]; then
skipped=' # skip'
if [[ "$BATS_TEST_SKIPPED" != '1' ]]; then
skipped+=" $BATS_TEST_SKIPPED"
fi
fi
BATS_TEST_TIME=''
if [[ -z "${skipped}" && -n "$BATS_ENABLE_TIMING" ]]; then
BATS_TEST_TIME=" in "$(( $(get_mills_since_epoch) - BATS_TEST_START_TIME ))"ms"
fi
local print_bats_out="${BATS_SHOW_OUTPUT_OF_SUCCEEDING_TESTS}"
local should_retry=''
if [[ -z "$BATS_TEST_COMPLETED" || -z "$BATS_TEARDOWN_COMPLETED" || "${BATS_INTERRUPTED-NOTSET}" != NOTSET ]]; then
if [[ "$BATS_ERROR_STATUS" -eq 0 ]]; then
# For some versions of bash, `$?` may not be set properly for some error
# conditions before triggering the EXIT trap directly (see #72 and #81).
# Thanks to the `BATS_TEARDOWN_COMPLETED` signal, this will pinpoint such
# errors if they happen during `teardown()` when `bats_perform_test` calls
# `bats_teardown_trap` directly after the test itself passes.
#
# If instead the test fails, and the `teardown()` error happens while
# `bats_teardown_trap` runs as the EXIT trap, the test will fail with no
# output, since there's no way to reach the `bats_exit_trap` call.
BATS_ERROR_STATUS=1
fi
if bats_should_retry_test; then
should_retry=1
status=126 # signify retry
rm -r "$BATS_TEST_TMPDIR" # clean up for retry
else
printf 'not ok %d %s\n' "$BATS_SUITE_TEST_NUMBER" "${BATS_TEST_NAME_PREFIX:-}${BATS_TEST_DESCRIPTION}${BATS_TEST_TIME}" >&3
local stack_trace
bats_get_failure_stack_trace stack_trace
bats_print_stack_trace "${stack_trace[@]}" >&3
bats_print_failed_command "${stack_trace[@]}" >&3
if [[ $BATS_PRINT_OUTPUT_ON_FAILURE && -n "${output:-}" ]]; then
printf "Last output:\n%s\n" "$output" >> "$BATS_OUT"
fi
print_bats_out=1
status=1
local state=failed
fi
else
printf 'ok %d %s%s\n' "$BATS_SUITE_TEST_NUMBER" "${BATS_TEST_NAME_PREFIX:-}${BATS_TEST_DESCRIPTION}${BATS_TEST_TIME}" \
"$skipped" >&3
status=0
local state=passed
fi
if [[ -z "$should_retry" ]]; then
printf "%s %s\t%s\n" "$state" "$BATS_TEST_FILENAME" "$BATS_TEST_NAME" >> "$BATS_RUNLOG_FILE"
if [[ $print_bats_out ]]; then
bats_prefix_lines_for_tap_output < "$BATS_OUT" | bats_replace_filename >&3
fi
fi
if [[ $BATS_GATHER_TEST_OUTPUTS_IN ]]; then
local try_suffix=
if [[ -n "$should_retry" ]]; then
try_suffix="-try$BATS_TEST_TRY_NUMBER"
fi
cp "$BATS_OUT" "$BATS_GATHER_TEST_OUTPUTS_IN/$BATS_SUITE_TEST_NUMBER$try_suffix-$BATS_TEST_DESCRIPTION.log"
fi
rm -f "$BATS_OUT"
exit "$status"
}
get_mills_since_epoch() {
local ms_since_epoch
ms_since_epoch=$(date +%s%N)
if [[ "$ms_since_epoch" == *N || "${#ms_since_epoch}" -lt 19 ]]; then
ms_since_epoch=$(( $(date +%s) * 1000 ))
else
ms_since_epoch=$(( ms_since_epoch / 1000000 ))
fi
printf "%d\n" "$ms_since_epoch"
}
bats_perform_test() {
if ! declare -F "$BATS_TEST_NAME" &>/dev/null; then
local quoted_test_name
bats_quote_code quoted_test_name "$BATS_TEST_NAME"
printf "bats: unknown test name %s\n" "$quoted_test_name" >&2
exit 1
fi
BATS_TEST_COMPLETED=
BATS_TEST_SKIPPED=
BATS_TEARDOWN_COMPLETED=
BATS_ERROR_STATUS=
bats_setup_tracing
# mark this call as trap call
trap 'bats_teardown_trap as-exit-trap' EXIT
BATS_TEST_START_TIME=$(get_mills_since_epoch)
"$BATS_TEST_NAME" >>"$BATS_OUT" 2>&1 4>&1
BATS_TEST_COMPLETED=1
trap 'bats_exit_trap' EXIT
bats_teardown_trap "" # pass empty parameter to signify call outside trap
}
trap bats_interrupt_trap INT
# shellcheck source=lib/bats-core/preprocessing.bash
source "$BATS_ROOT/lib/bats-core/preprocessing.bash"
exec 3<&1
bats_create_test_tmpdirs
# Run the given test.
bats_evaluate_preprocessed_source
bats_perform_test

View File

@ -0,0 +1,6 @@
#!/usr/bin/env bash
set -e
trap '' INT
cat

View File

@ -0,0 +1,251 @@
#!/usr/bin/env bash
set -euo pipefail
# shellcheck source=lib/bats-core/formatter.bash
source "$BATS_ROOT/lib/bats-core/formatter.bash"
BASE_PATH=.
while [[ "$#" -ne 0 ]]; do
case "$1" in
--base-path)
shift
normalize_base_path BASE_PATH "$1"
;;
esac
shift
done
init_suite() {
suite_test_exec_time=0
# since we have to print the suite header before its contents but we don't know the contents before the header,
# we have to buffer the contents
_suite_buffer=""
test_result_state="" # declare for the first flush, when no test has been encountered
}
_buffer_log=
init_file() {
file_count=0
file_failures=0
file_skipped=0
file_exec_time=0
test_exec_time=0
_buffer=""
_buffer_log=""
_system_out_log=""
test_result_state="" # mark that no test has run in this file so far
}
host() {
local hostname="${HOST:-}"
[[ -z "$hostname" ]] && hostname="${HOSTNAME:-}"
[[ -z "$hostname" ]] && hostname="$(uname -n)"
[[ -z "$hostname" ]] && hostname="$(hostname -f)"
echo "$hostname"
}
# convert $1 (time in milliseconds) to seconds
milliseconds_to_seconds() {
# we cannot rely on having bc for this calculation
full_seconds=$(($1 / 1000))
remaining_milliseconds=$(($1 % 1000))
if [[ $remaining_milliseconds -eq 0 ]]; then
printf "%d" "$full_seconds"
else
printf "%d.%03d" "$full_seconds" "$remaining_milliseconds"
fi
}
suite_header() {
printf "<?xml version=\"1.0\" encoding=\"UTF-8\"?>
<testsuites time=\"%s\">\n" "$(milliseconds_to_seconds "${suite_test_exec_time}")"
}
file_header() {
timestamp=$(date -u +"%Y-%m-%dT%H:%M:%S")
printf "<testsuite name=\"%s\" tests=\"%s\" failures=\"%s\" errors=\"0\" skipped=\"%s\" time=\"%s\" timestamp=\"%s\" hostname=\"%s\">\n" \
"$(xml_escape "${class}")" "${file_count}" "${file_failures}" "${file_skipped}" "$(milliseconds_to_seconds "${file_exec_time}")" "${timestamp}" "$(host)"
}
file_footer() {
printf "</testsuite>\n"
}
suite_footer() {
printf "</testsuites>\n"
}
print_test_case() {
if [[ "$test_result_state" == ok && -z "$_system_out_log" && -z "$_buffer_log" ]]; then
# pass and no output can be shortened
printf " <testcase classname=\"%s\" name=\"%s\" time=\"%s\" />\n" "$(xml_escape "${class}")" "$(xml_escape "${name}")" "$(milliseconds_to_seconds "${test_exec_time}")"
else
printf " <testcase classname=\"%s\" name=\"%s\" time=\"%s\">\n" "$(xml_escape "${class}")" "$(xml_escape "${name}")" "$(milliseconds_to_seconds "${test_exec_time}")"
if [[ -n "$_system_out_log" ]]; then
printf " <system-out>%s</system-out>\n" "$(xml_escape "${_system_out_log}")"
fi
if [[ -n "$_buffer_log" || "$test_result_state" == not_ok ]]; then
printf " <failure type=\"failure\">%s</failure>\n" "$(xml_escape "${_buffer_log}")"
fi
if [[ "$test_result_state" == skipped ]]; then
printf " <skipped>%s</skipped>\n" "$(xml_escape "$test_skip_message")"
fi
printf " </testcase>\n"
fi
}
xml_escape() {
output=${1//&/&amp;}
output=${output//</&lt;}
output=${output//>/&gt;}
output=${output//'"'/&quot;}
output=${output//\'/&#39;}
local CONTROL_CHAR=$'\033'
output="${output//$CONTROL_CHAR/&#27;}"
printf "%s" "$output"
}
suite_buffer() {
local output
output="$("$@"; printf "x")" # use x marker to avoid losing trailing newlines
_suite_buffer="${_suite_buffer}${output%x}"
}
suite_flush() {
echo -n "${_suite_buffer}"
_suite_buffer=""
}
buffer() {
local output
output="$("$@"; printf "x")" # use x marker to avoid losing trailing newlines
_buffer="${_buffer}${output%x}"
}
flush() {
echo -n "${_buffer}"
_buffer=""
}
log() {
if [[ -n "$_buffer_log" ]]; then
_buffer_log="${_buffer_log}
$1"
else
_buffer_log="$1"
fi
}
flush_log() {
if [[ -n "$test_result_state" ]]; then
buffer print_test_case
fi
_buffer_log=""
_system_out_log=""
}
log_system_out() {
if [[ -n "$_system_out_log" ]]; then
_system_out_log="${_system_out_log}
$1"
else
_system_out_log="$1"
fi
}
finish_file() {
if [[ "${class-JUNIT_FORMATTER_NO_FILE_ENCOUNTERED}" != JUNIT_FORMATTER_NO_FILE_ENCOUNTERED ]]; then
file_header
printf "%s\n" "${_buffer}"
file_footer
fi
}
finish_suite() {
flush_log
suite_header
suite_flush
finish_file # must come after suite flush to not print the last file before the others
suite_footer
}
bats_tap_stream_plan() { # <number of tests>
:
}
init_suite
trap finish_suite EXIT
trap '' INT
bats_tap_stream_begin() { # <test index> <test name>
flush_log
# set after flushing to avoid overriding name of test
name="$2"
}
bats_tap_stream_ok() { # [--duration <milliseconds] <test index> <test name>
if [[ "$1" == "--duration" ]]; then
test_exec_time="${BASH_REMATCH[1]}"
else
test_exec_time=0
fi
((file_count += 1))
test_result_state='ok'
file_exec_time="$((file_exec_time + test_exec_time))"
suite_test_exec_time=$((suite_test_exec_time + test_exec_time))
}
bats_tap_stream_skipped() { # <test index> <test name> <skip reason>
((file_count += 1))
((file_skipped += 1))
test_result_state='skipped'
test_exec_time=0
test_skip_message="$3"
}
bats_tap_stream_not_ok() { # [--duration <milliseconds>] <test index> <test name>
((file_count += 1))
((file_failures += 1))
if [[ "$1" == "--duration" ]]; then
test_exec_time="${BASH_REMATCH[1]}"
else
test_exec_time=0
fi
test_result_state=not_ok
file_exec_time="$((file_exec_time + test_exec_time))"
suite_test_exec_time=$((suite_test_exec_time + test_exec_time))
}
bats_tap_stream_comment() { # <comment text without leading '# '> <scope>
local comment="$1" scope="$2"
case "$scope" in
begin)
# everything that happens between begin and [not] ok is FD3 output from the test
log_system_out "$comment"
;;
ok)
# non failed tests can produce FD3 output
log_system_out "$comment"
;;
*)
# everything else is considered error output
log "$1"
;;
esac
}
bats_tap_stream_suite() { # <file name>
flush_log
suite_buffer finish_file
init_file
class="${1/$BASE_PATH}"
}
bats_tap_stream_unknown() { # <full line>
:
}
bats_parse_internal_extended_tap

View File

@ -0,0 +1,328 @@
#!/usr/bin/env bash
set -e
# shellcheck source=lib/bats-core/formatter.bash
source "$BATS_ROOT/lib/bats-core/formatter.bash"
BASE_PATH=.
BATS_ENABLE_TIMING=
while [[ "$#" -ne 0 ]]; do
case "$1" in
-T)
BATS_ENABLE_TIMING="-T"
;;
--base-path)
shift
normalize_base_path BASE_PATH "$1"
;;
esac
shift
done
update_count_column_width() {
count_column_width=$((${#count} * 2 + 2))
if [[ -n "$BATS_ENABLE_TIMING" ]]; then
# additional space for ' in %s sec'
count_column_width=$((count_column_width + ${#SECONDS} + 8))
fi
# also update dependent value
update_count_column_left
}
update_screen_width() {
screen_width="$(tput cols)"
# also update dependent value
update_count_column_left
}
update_count_column_left() {
count_column_left=$((screen_width - count_column_width))
}
# avoid unset variables
count=0
screen_width=80
update_count_column_width
update_screen_width
test_result=
trap update_screen_width WINCH
begin() {
test_result= # reset to avoid carrying over result state from previous test
line_backoff_count=0
go_to_column 0
update_count_column_width
buffer_with_truncation $((count_column_left - 1)) ' %s' "$name"
clear_to_end_of_line
go_to_column $count_column_left
if [[ -n "$BATS_ENABLE_TIMING" ]]; then
buffer "%${#count}s/${count} in %s sec" "$index" "$SECONDS"
else
buffer "%${#count}s/${count}" "$index"
fi
go_to_column 1
}
finish_test() {
move_up $line_backoff_count
go_to_column 0
buffer "$@"
if [[ -n "$BATS_ENABLE_TIMING" ]]; then
set_color 2
buffer ' [%s]' "$TIMING"
fi
advance
move_down $(( line_backoff_count - 1 ))
}
pass() {
TIMING="${1:-}"
finish_test ' ✓ %s' "$name"
test_result=pass
}
skip() {
local reason="$1"
if [[ -n "$reason" ]]; then
reason=": $reason"
fi
BATS_ENABLE_TIMING='' finish_test ' - %s (skipped%s)' "$name" "$reason"
test_result=skip
}
fail() {
set_color 1 bold
TIMING="${1:-}"
finish_test ' ✗ %s' "$name"
test_result=fail
}
log() {
case ${test_result} in
pass)
clear_color
;;
fail)
set_color 1
;;
esac
buffer ' %s\n' "$1"
clear_color
}
summary() {
if [ "$failures" -eq 0 ] ; then
set_color 2 bold
else
set_color 1 bold
fi
buffer '\n%d test' "$count"
if [[ "$count" -ne 1 ]]; then
buffer 's'
fi
buffer ', %d failure' "$failures"
if [[ "$failures" -ne 1 ]]; then
buffer 's'
fi
if [[ "$skipped" -gt 0 ]]; then
buffer ', %d skipped' "$skipped"
fi
not_run=$((count - passed - failures - skipped))
if [[ "$not_run" -gt 0 ]]; then
buffer ', %d not run' "$not_run"
fi
if [[ -n "$BATS_ENABLE_TIMING" ]]; then
buffer " in $SECONDS seconds"
fi
buffer '\n'
clear_color
}
buffer_with_truncation() {
local width="$1"
shift
local string
# shellcheck disable=SC2059
printf -v 'string' -- "$@"
if [[ "${#string}" -gt "$width" ]]; then
buffer '%s...' "${string:0:$((width - 4))}"
else
buffer '%s' "$string"
fi
}
move_up() {
if [[ $1 -gt 0 ]]; then # avoid moving if we got 0
buffer '\x1B[%dA' "$1"
fi
}
move_down() {
if [[ $1 -gt 0 ]]; then # avoid moving if we got 0
buffer '\x1B[%dB' "$1"
fi
}
go_to_column() {
local column="$1"
buffer '\x1B[%dG' $((column + 1))
}
clear_to_end_of_line() {
buffer '\x1B[K'
}
advance() {
clear_to_end_of_line
buffer '\n'
clear_color
}
set_color() {
local color="$1"
local weight=22
if [[ "${2:-}" == 'bold' ]]; then
weight=1
fi
buffer '\x1B[%d;%dm' "$((30 + color))" "$weight"
}
clear_color() {
buffer '\x1B[0m'
}
_buffer=
buffer() {
local content
# shellcheck disable=SC2059
printf -v content -- "$@"
_buffer+="$content"
}
prefix_buffer_with() {
local old_buffer="$_buffer"
_buffer=''
"$@"
_buffer="$_buffer$old_buffer"
}
flush() {
printf '%s' "$_buffer"
_buffer=
}
finish() {
flush
printf '\n'
}
trap finish EXIT
trap '' INT
bats_tap_stream_plan() {
count="$1"
index=0
passed=0
failures=0
skipped=0
name=
update_count_column_width
}
bats_tap_stream_begin() {
index="$1"
name="$2"
begin
flush
}
bats_tap_stream_ok() {
local duration=
if [[ "$1" == "--duration" ]]; then
duration="$2"
shift 2
fi
index="$1"
name="$2"
((++passed))
pass "$duration"
}
bats_tap_stream_skipped() {
index="$1"
name="$2"
((++skipped))
skip "$3"
}
bats_tap_stream_not_ok() {
local duration=
if [[ "$1" == "--duration" ]]; then
duration="$2"
shift 2
fi
index="$1"
name="$2"
((++failures))
fail "$duration"
}
bats_tap_stream_comment() { # <comment> <scope>
local scope=$2
# count the lines we printed after the begin text,
if [[ $line_backoff_count -eq 0 && $scope == begin ]]; then
# if this is the first line after begin, go down one line
buffer "\n"
(( ++line_backoff_count )) # prefix-increment to avoid "error" due to returning 0
fi
(( ++line_backoff_count ))
(( line_backoff_count += ${#1} / screen_width)) # account for linebreaks due to length
log "$1"
}
bats_tap_stream_suite() {
#test_file="$1"
line_backoff_count=0
index=
# indicate filename for failures
local file_name="${1/$BASE_PATH}"
name="File $file_name"
set_color 4 bold
buffer "%s\n" "$file_name"
clear_color
}
line_backoff_count=0
bats_tap_stream_unknown() { # <full line> <scope>
local scope=$2
# count the lines we printed after the begin text, (or after suite, in case of syntax errors)
if [[ $line_backoff_count -eq 0 && ( $scope == begin || $scope == suite )]]; then
# if this is the first line after begin, go down one line
buffer "\n"
(( ++line_backoff_count )) # prefix-increment to avoid "error" due to returning 0
fi
(( ++line_backoff_count ))
(( line_backoff_count += ${#1} / screen_width)) # account for linebreaks due to length
buffer "%s\n" "$1"
flush
}
bats_parse_internal_extended_tap
summary

View File

@ -0,0 +1,52 @@
#!/usr/bin/env bash
set -e
trap '' INT
# shellcheck source=lib/bats-core/formatter.bash
source "$BATS_ROOT/lib/bats-core/formatter.bash"
bats_tap_stream_plan() {
printf "1..%d\n" "$1"
}
bats_tap_stream_begin() { #<test index> <test name>
:
}
bats_tap_stream_ok() { # [--duration <milliseconds] <test index> <test name>
if [[ "$1" == "--duration" ]]; then
printf "ok %d %s # in %d ms\n" "$3" "$4" "$2"
else
printf "ok %d %s\n" "$1" "$2"
fi
}
bats_tap_stream_not_ok() { # [--duration <milliseconds>] <test index> <test name>
if [[ "$1" == "--duration" ]]; then
printf "not ok %d %s # in %d ms\n" "$3" "$4" "$2"
else
printf "not ok %d %s\n" "$1" "$2"
fi
}
bats_tap_stream_skipped() { # <test index> <test name> <reason>
if [[ -n "$3" ]]; then
printf "ok %d %s # skip %s\n" "$1" "$2" "$3"
else
printf "ok %d %s # skip\n" "$1" "$2"
fi
}
bats_tap_stream_comment() { # <comment text without leading '# '>
printf "# %s\n" "$1"
}
bats_tap_stream_suite() { # <file name>
:
}
bats_tap_stream_unknown() { # <full line>
printf "%s\n" "$1"
}
bats_parse_internal_extended_tap

View File

@ -0,0 +1,91 @@
#!/usr/bin/env bash
set -e
while [[ "$#" -ne 0 ]]; do
case "$1" in
-T)
BATS_ENABLE_TIMING="-T"
;;
esac
shift
done
header_pattern='[0-9]+\.\.[0-9]+'
IFS= read -r header
if [[ "$header" =~ $header_pattern ]]; then
printf "TAP version 13\n"
printf "%s\n" "$header"
else
# If the first line isn't a TAP plan, print it and pass the rest through
printf '%s\n' "$header"
exec cat
fi
yaml_block_open=''
add_yaml_entry() {
if [[ -z "$yaml_block_open" ]]; then
printf " ---\n"
fi
printf " %s: %s\n" "$1" "$2"
yaml_block_open=1
}
close_previous_yaml_block() {
if [[ -n "$yaml_block_open" ]]; then
printf " ...\n"
yaml_block_open=''
fi
}
trap '' INT
number_of_printed_log_lines_for_this_test_so_far=0
while IFS= read -r line; do
case "$line" in
'begin '*) ;;
'ok '*)
close_previous_yaml_block
number_of_printed_log_lines_for_this_test_so_far=0
if [[ -n "${BATS_ENABLE_TIMING-}" ]]; then
timing_expr="(ok [0-9]+ .+) in ([0-9]+)ms$"
if [[ "$line" =~ $timing_expr ]]; then
printf "%s\n" "${BASH_REMATCH[1]}"
add_yaml_entry "duration_ms" "${BASH_REMATCH[2]}"
else
echo "Could not match output line to timing regex: $line" >&2
exit 1
fi
else
printf "%s\n" "${line}"
fi
;;
'not ok '*)
close_previous_yaml_block
number_of_printed_log_lines_for_this_test_so_far=0
timing_expr="(not ok [0-9]+ .+) in ([0-9])+ms$"
if [[ -n "${BATS_ENABLE_TIMING-}" ]]; then
if [[ "$line" =~ $timing_expr ]]; then
printf "%s\n" "${BASH_REMATCH[1]}"
add_yaml_entry "duration_ms" "${BASH_REMATCH[2]}"
else
echo "Could not match failure line to timing regex: $line" >&2
exit 1
fi
else
printf "%s\n" "${line}"
fi
;;
'# '*)
if [[ $number_of_printed_log_lines_for_this_test_so_far -eq 0 ]]; then
add_yaml_entry "message" "|" # use a multiline string for this entry
fi
((++number_of_printed_log_lines_for_this_test_so_far))
printf " %s\n" "${line:2}"
;;
'suite '*) ;;
esac
done
# close the final block if there was one
close_previous_yaml_block

View File

@ -0,0 +1,60 @@
#!/usr/bin/env bash
set -e
bats_encode_test_name() {
local name="$1"
local result='test_'
local hex_code
if [[ ! "$name" =~ [^[:alnum:]\ _-] ]]; then
name="${name//_/-5f}"
name="${name//-/-2d}"
name="${name// /_}"
result+="$name"
else
local length="${#name}"
local char i
for ((i = 0; i < length; i++)); do
char="${name:$i:1}"
if [[ "$char" == ' ' ]]; then
result+='_'
elif [[ "$char" =~ [[:alnum:]] ]]; then
result+="$char"
else
printf -v 'hex_code' -- '-%02x' \'"$char"
result+="$hex_code"
fi
done
fi
printf -v "$2" '%s' "$result"
}
BATS_TEST_PATTERN="^[[:blank:]]*@test[[:blank:]]+(.*[^[:blank:]])[[:blank:]]+\{(.*)\$"
BATS_TEST_PATTERN_COMMENT="[[:blank:]]*([^[:blank:]()]+)[[:blank:]]*\(?\)?[[:blank:]]+\{[[:blank:]]+#[[:blank:]]*@test[[:blank:]]*\$"
test_file="$1"
tests=()
{
while IFS= read -r line; do
line="${line//$'\r'/}"
if [[ "$line" =~ $BATS_TEST_PATTERN ]] || [[ "$line" =~ $BATS_TEST_PATTERN_COMMENT ]]; then
name="${BASH_REMATCH[1]#[\'\"]}"
name="${name%[\'\"]}"
body="${BASH_REMATCH[2]:-}"
bats_encode_test_name "$name" 'encoded_name'
printf '%s() { bats_test_begin "%s"; %s\n' "${encoded_name:?}" "$name" "$body" || :
if [[ -z "$BATS_TEST_FILTER" || "$name" =~ $BATS_TEST_FILTER ]]; then
tests+=("$encoded_name")
fi
else
printf '%s\n' "$line"
fi
done
} <<<"$(<"$test_file")"$'\n'
for test_name in "${tests[@]}"; do
printf 'bats_test_function %s\n' "$test_name"
done

View File

@ -0,0 +1,197 @@
# setup paths for BATS test units
setup() {
[ ! -f ${BATS_PARENT_TMPNAME}.skip ] || skip "skip remaining tests"
bats_require_minimum_version 1.5.0
R="$BATS_TEST_DIRNAME"
# R=`cd "$T"/.. && pwd`
TMP="$BATS_FILE_TMPDIR"
load test_helper/bats-support/load
load test_helper/bats-assert/load
load test_helper/bats-file/load
ZTMP="$BATS_FILE_TMPDIR"
if [ "$ZENROOM_EXECUTABLE" == "" ]; then
ZENROOM_EXECUTABLE="/usr/local/bin/zenroom"
fi
PATH="$PATH:/usr/local/bin:/usr/local/sbin"
MNT="/media/`basename $BATS_TEST_FILENAME`"
PIM=42
# max password stdin size to veracrypt is 128 bytes
PW="91cd69b2caab05cb64f8e1c8ae22a7c5a25564e7fc5116d0303dce1ffd26861ea0483c25bdb85adb216718c19815eb59ac14ec9783bfbbb57786ca7d9038c845"
# `openssl rand -hex 64`"
FS='ext4'
SIZE='20M'
[ "$TOMB" = "" ] && TOMB=test.tomb
# default on freebsd is 1001
user_uid=1000
user_gid=1000
system="`uname -s`"
f_create=""
f_format=""
f_map=""
f_mount=""
f_close=""
case "$system" in
FreeBSD)
f_create=portable_create
f_format=freebsd_format
f_map=portable_map
f_mount=freebsd_mount
f_close=freebsd_close
user_uid=1001
user_gid=1001
;;
Linux)
f_create=portable_create
f_format=linux_format
f_map=portable_map
f_mount=linux_mount
f_close=portable_close
user_uid=1000
user_gid=1000
;;
*)
>&2 echo "Unsupported system: $system"
exit 1
esac
# cd $ZTMP
}
teardown() {
[ -n "$BATS_TEST_COMPLETED" ] || touch ${BATS_PARENT_TMPNAME}.skip
>&3 echo
}
# usage: echo PASSWORD | portable_create file size pim
portable_create() {
local file="$1" # must not exist
local size="$2" # veracrypt format (accepts M or G)
local pim="$3" # any number
# >&2 echo "portable_create $file $size $pim"
veracrypt --non-interactive --text --stdin \
-m nokernelcrypto \
-c ${file} --volume-type normal \
--hash sha512 --encryption serpent-aes \
--filesystem none --size ${size} --pim ${pim} \
--random-source /dev/urandom -k ''
return $?
}
# usage: echo PASSWORD | portable_map file pim
portable_map() {
local file="$1"
local pim="$2"
# >&2 echo "portable_map $file $pim"
veracrypt --non-interactive --text --stdin \
--protect-hidden no -m nokernelcrypto \
-k '' --pim ${pim} --filesystem none \
${file}
local loop=`veracrypt -l "$file" | awk '{print $3}'`
return $?
}
portable_close() {
local file="$1"
veracrypt -d ${file}
return $?
}
linux_format() {
local file="$1"
# loop="`losetup -j ${vera}/volume | cut -d: -f1`"
local loop=`veracrypt -l "$FILE" | awk '{print $3}'`
# losetup -l
>&2 echo "veramap format: ${loop}"
sync
sudo mkfs.ext4 -E root_owner="${user_uid}:${user_gid}" "$loop"
return $?
}
# usage: _mount /tmp/.veracrypt_ mountpoint
linux_mount() {
local file="$1"
local mnt="$2"
# local loop="`losetup -j ${vera}/volume | cut -d: -f1`"
local loop=`veracrypt -l "$file" | awk '{print $3}'`
>&3 echo "fsck $loop"
fsck.ext4 -p -C0 "$loop"
sudo mount ${loop} "$mnt"
return $?
}
freebsd_format() {
file="$1"
local loop=`veracrypt -l "$FILE" | awk '{print $3}'`
mkfs.ext4 -E root_owner="${user_uid}:${user_gid}" "${loop}"
return $?
}
freebsd_mount() {
local file="$1"
local mnt="$2"
>&2 echo `veracrypt -l "$file"`
local loop=`veracrypt -l "$file" | awk '{print $3}'`
>&3 echo "fsck $loop"
fsck.ext4 -p -C0 "$loop"
lklfuse -o type=ext4 "${loop}" "$mnt"
return $?
}
freebsd_close() {
local file="$1"
local md=`veracrypt -l "$file" | awk '{print $3}'`
# umount "$mnt"
>&2 echo "md: $md"
local mnt=`pgrep -lf "lklfuse.*$md" | awk '{print $6}'`
>&2 echo "mnt: $mnt"
lkl=`pgrep -f "lklfuse.*$md" | awk '{print $1}'`
>&3 echo "kill $lkl (lklfuse on $md)"
# trying to deail with lklfuse bug
# https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=239831
renice -20 $lkl
kill $lkl
sync
sleep 1
kill -9 $lkl
# lkl should have really exited now
>&3 echo "veracrypt -d $file"
veracrypt -d "$file"
return $?
}
Z() {
tmptmp=0
if ! [ "$TMP" ]; then
TMP=`mktemp -d`; tmptmp=1
fi
script=$1
if [ "$script" == "-" ]; then
cat > $TMP/script_stdin
script=$TMP/script_stdin
else
if ! [ -r "$script" ]; then
script=$T/$script
fi
fi
if ! [ -r $script ]; then
>&2 echo "Error - script not found: $script"
return 1
fi
shift 1
if [ "${1##*.}" == "zen" ]; then
$ZENROOM_EXECUTABLE $@ -z $script
else
$ZENROOM_EXECUTABLE $@ $script
fi
if [ $tmptmp = 1 ]; then
rm -f $TMP/*
rmdir $TMP
fi
}

View File

@ -0,0 +1,26 @@
load bats_setup
@test "Find the tomb" {
assert_file_exists "$TOMB"
>&3 echo "Found: $TOMB"
}
@test "Open tomb with key" {
mkdir -p "$TOMB".mnt
./tomb open -k "$TOMB".key "$TOMB" "$TOMB".mnt
}
@test "Check integrity of random data" {
sha512sum "$TOMB".mnt/random.data | awk '{print $1}'
cat "$TOMB".hash
>&2 echo $newhash
>&2 echo $oldhash
assert_equal "$newhash" "$oldhash"
if [ -r "$TOMB".uname ]; then
>&3 cat "$TOMB".uname
fi
}
@test "Close tomb" {
./tomb close "$TOMB"
}

View File

@ -0,0 +1,79 @@
load bats_setup
# f_create=""
# f_format=""
# f_map=""
# f_mount=""
# f_close=""
@test "Create a new tomb" {
# >&3 echo $PW
>&3 echo $TOMB
echo -n "$PW" | $f_create "$TOMB" 20M ${PIM}
}
@test "Map the tomb" {
echo -n "$PW" | $f_map "$TOMB" ${PIM}
}
@test "Format the tomb" {
$f_format "/tmp/.veracrypt_aux_mnt1" || {
$f_close "$TOMB"
return false
}
}
@test "Mount the tomb" {
mkdir -p "$MNT"
$f_mount "$TOMB" "$MNT" || {
$f_close "$TOMB"
return false
}
}
@test "Create random.data inside the tomb" {
dd if=/dev/urandom of="$MNT"/random.data bs=1024 count=10000 || {
# sudo umount testmnt
$f_close "$TOMB"
return false
}
>&3 ls -l "$MNT"/random.data
sha512sum "$MNT"/random.data | awk '{print $1}' | >&3 tee "$TOMB".hash
uname -a > "$TOMB".uname
}
@test "Close the test.tomb" {
>&3 veracrypt -l
# sudo umount "$TMP"/testmnt
$f_close "$TOMB"
}
@test "Re-Map the test.tomb" {
echo -n "$PW" | $f_map "$TOMB" ${PIM}
}
@test "Re-Mount the test.tomb" {
mkdir -p "$MNT"
$f_mount "$TOMB" "$MNT" || {
$f_close "$TOMB"
return false
}
}
@test "Re-Check integrity of random data" {
newhash=`sha512sum "$MNT"/random.data | awk '{print $1}'`
oldhash=`cat "$TOMB".hash`
>&2 echo $newhash
>&2 echo $oldhash
assert_equal "$newhash" "$oldhash"
if [ -r "$TOMB".uname ]; then
>&3 cat "$TOMB".uname
fi
}
@test "Re-Close the test.tomb" {
>&3 veracrypt -l
$f_close "$TOMB"
}

View File

@ -0,0 +1,51 @@
load bats_setup
@test "Dig tomb" {
rm -f "$TOMB"
>&3 echo "$TOMB"
./tomb dig -s 20 "$TOMB"
}
@test "Forge key" {
rm -f "$TOMB".key
./tomb forge "$TOMB".key
}
@test "Lock tomb with key" {
./tomb lock -k "$TOMB".key "$TOMB"
}
@test "Open tomb with key" {
mkdir -p "$TOMB".mnt
./tomb open -k "$TOMB".key "$TOMB" "$TOMB".mnt
}
@test "Create random.data inside the tomb" {
dd if=/dev/urandom of="$TOMB".mnt/random.data bs=1024 count=10000
>&3 ls -l "$TOMB".mnt/random.data
sha512sum "$TOMB".mnt/random.data | awk '{print $1}' | >&3 tee "$TOMB".hash
uname -a > "$TOMB".uname
}
@test "Close tomb" {
./tomb close "$TOMB"
}
@test "Re-open tomb with key" {
./tomb open -k "$TOMB".key "$TOMB" "$TOMB".mnt
}
@test "Check integrity of random data" {
newhash=`sha512sum "$TOMB".mnt/random.data | awk '{print $1}'`
oldhash=`cat "$TOMB".hash`
>&2 echo $newhash
>&2 echo $oldhash
assert_equal "$newhash" "$oldhash"
if [ -r "$TOMB".uname ]; then
>&3 cat "$TOMB".uname
fi
}
@test "Close the tomb again" {
./tomb close "$TOMB"
}

View File

@ -0,0 +1,33 @@
# bats-assert - Common assertions for Bats
#
# Written in 2016 by Zoltan Tombol <zoltan dot tombol at gmail dot com>
#
# To the extent possible under law, the author(s) have dedicated all
# copyright and related and neighboring rights to this software to the
# public domain worldwide. This software is distributed without any
# warranty.
#
# You should have received a copy of the CC0 Public Domain Dedication
# along with this software. If not, see
# <http://creativecommons.org/publicdomain/zero/1.0/>.
#
# Assertions are functions that perform a test and output relevant
# information on failure to help debugging. They return 1 on failure
# and 0 otherwise.
#
# All output is formatted for readability using the functions of
# `output.bash' and sent to the standard error.
# shellcheck disable=1090
source "$(dirname "${BASH_SOURCE[0]}")/src/assert.bash"
source "$(dirname "${BASH_SOURCE[0]}")/src/refute.bash"
source "$(dirname "${BASH_SOURCE[0]}")/src/assert_equal.bash"
source "$(dirname "${BASH_SOURCE[0]}")/src/assert_not_equal.bash"
source "$(dirname "${BASH_SOURCE[0]}")/src/assert_success.bash"
source "$(dirname "${BASH_SOURCE[0]}")/src/assert_failure.bash"
source "$(dirname "${BASH_SOURCE[0]}")/src/assert_output.bash"
source "$(dirname "${BASH_SOURCE[0]}")/src/refute_output.bash"
source "$(dirname "${BASH_SOURCE[0]}")/src/assert_line.bash"
source "$(dirname "${BASH_SOURCE[0]}")/src/refute_line.bash"
source "$(dirname "${BASH_SOURCE[0]}")/src/assert_regex.bash"
source "$(dirname "${BASH_SOURCE[0]}")/src/refute_regex.bash"

View File

@ -0,0 +1,42 @@
# assert
# ======
#
# Summary: Fail if the given expression evaluates to false.
#
# Usage: assert <expression>
# Options:
# <expression> The expression to evaluate for truthiness.
# *__Note:__ The expression must be a simple command.
# [Compound commands](https://www.gnu.org/software/bash/manual/bash.html#Compound-Commands),
# such as `[[`, can be used only when executed with `bash -c`.*
#
# IO:
# STDERR - the failed expression, on failure
# Globals:
# none
# Returns:
# 0 - if expression evaluates to true
# 1 - otherwise
#
# ```bash
# @test 'assert()' {
# touch '/var/log/test.log'
# assert [ -e '/var/log/test.log' ]
# }
# ```
#
# On failure, the failed expression is displayed.
#
# ```
# -- assertion failed --
# expression : [ -e /var/log/test.log ]
# --
# ```
assert() {
if ! "$@"; then
batslib_print_kv_single 10 'expression' "$*" \
| batslib_decorate 'assertion failed' \
| fail
fi
}

View File

@ -0,0 +1,42 @@
# assert_equal
# ============
#
# Summary: Fail if the actual and expected values are not equal.
#
# Usage: assert_equal <actual> <expected>
#
# Options:
# <actual> The value being compared.
# <expected> The value to compare against.
#
# ```bash
# @test 'assert_equal()' {
# assert_equal 'have' 'want'
# }
# ```
#
# IO:
# STDERR - expected and actual values, on failure
# Globals:
# none
# Returns:
# 0 - if values equal
# 1 - otherwise
#
# On failure, the expected and actual values are displayed.
#
# ```
# -- values do not equal --
# expected : want
# actual : have
# --
# ```
assert_equal() {
if [[ $1 != "$2" ]]; then
batslib_print_kv_single_or_multi 8 \
'expected' "$2" \
'actual' "$1" \
| batslib_decorate 'values do not equal' \
| fail
fi
}

View File

@ -0,0 +1,78 @@
# assert_failure
# ==============
#
# Summary: Fail if `$status` is 0; or is not equal to the optionally provided status.
#
# Usage: assert_failure [<expected_status>]
#
# Options:
# <expected_status> The specific status code to check against.
# If not provided, simply asserts status is != 0.
#
# IO:
# STDERR - `$output`, on failure;
# - also, `$status` and `expected_status`, if provided
# Globals:
# status
# output
# Returns:
# 0 - if `$status' is 0,
# or if expected_status is provided but does not equal `$status'
# 1 - otherwise
#
# ```bash
# @test 'assert_failure() status only' {
# run echo 'Success!'
# assert_failure
# }
# ```
#
# On failure, `$output` is displayed.
#
# ```
# -- command succeeded, but it was expected to fail --
# output : Success!
# --
# ```
#
# ## Expected status
#
# When `expected_status` is provided, fail if `$status` does not equal the `expected_status`.
#
# ```bash
# @test 'assert_failure() with expected status' {
# run bash -c "echo 'Error!'; exit 1"
# assert_failure 2
# }
# ```
#
# On failure, both the expected and actual statuses, and `$output` are displayed.
#
# ```
# -- command failed as expected, but status differs --
# expected : 2
# actual : 1
# output : Error!
# --
# ```
assert_failure() {
: "${output?}"
: "${status?}"
(( $# > 0 )) && local -r expected="$1"
if (( status == 0 )); then
batslib_print_kv_single_or_multi 6 'output' "$output" \
| batslib_decorate 'command succeeded, but it was expected to fail' \
| fail
elif (( $# > 0 )) && (( status != expected )); then
{ local -ir width=8
batslib_print_kv_single "$width" \
'expected' "$expected" \
'actual' "$status"
batslib_print_kv_single_or_multi "$width" \
'output' "$output"
} \
| batslib_decorate 'command failed as expected, but status differs' \
| fail
fi
}

View File

@ -0,0 +1,248 @@
# assert_line
# ===========
#
# Summary: Fail if the expected line is not found in the output (default) or at a specific line number.
#
# Usage: assert_line [-n index] [-p | -e] [--] <expected>
#
# Options:
# -n, --index <idx> Match the <idx>th line
# -p, --partial Match if `expected` is a substring of `$output` or line <idx>
# -e, --regexp Treat `expected` as an extended regular expression
# <expected> The expected line string, substring, or regular expression
#
# IO:
# STDERR - details, on failure
# error message, on error
# Globals:
# output
# lines
# Returns:
# 0 - if matching line found
# 1 - otherwise
#
# Similarly to `assert_output`, this function verifies that a command or function produces the expected output.
# (It is the logical complement of `refute_line`.)
# It checks that the expected line appears in the output (default) or at a specific line number.
# Matching can be literal (default), partial or regular expression.
#
# *__Warning:__
# Due to a [bug in Bats][bats-93], empty lines are discarded from `${lines[@]}`,
# causing line indices to change and preventing testing for empty lines.*
#
# [bats-93]: https://github.com/sstephenson/bats/pull/93
#
# ## Looking for a line in the output
#
# By default, the entire output is searched for the expected line.
# The assertion fails if the expected line is not found in `${lines[@]}`.
#
# ```bash
# @test 'assert_line() looking for line' {
# run echo $'have-0\nhave-1\nhave-2'
# assert_line 'want'
# }
# ```
#
# On failure, the expected line and the output are displayed.
#
# ```
# -- output does not contain line --
# line : want
# output (3 lines):
# have-0
# have-1
# have-2
# --
# ```
#
# ## Matching a specific line
#
# When the `--index <idx>` option is used (`-n <idx>` for short), the expected line is matched only against the line identified by the given index.
# The assertion fails if the expected line does not equal `${lines[<idx>]}`.
#
# ```bash
# @test 'assert_line() specific line' {
# run echo $'have-0\nhave-1\nhave-2'
# assert_line --index 1 'want-1'
# }
# ```
#
# On failure, the index and the compared lines are displayed.
#
# ```
# -- line differs --
# index : 1
# expected : want-1
# actual : have-1
# --
# ```
#
# ## Partial matching
#
# Partial matching can be enabled with the `--partial` option (`-p` for short).
# When used, a match fails if the expected *substring* is not found in the matched line.
#
# ```bash
# @test 'assert_line() partial matching' {
# run echo $'have 1\nhave 2\nhave 3'
# assert_line --partial 'want'
# }
# ```
#
# On failure, the same details are displayed as for literal matching, except that the substring replaces the expected line.
#
# ```
# -- no output line contains substring --
# substring : want
# output (3 lines):
# have 1
# have 2
# have 3
# --
# ```
#
# ## Regular expression matching
#
# Regular expression matching can be enabled with the `--regexp` option (`-e` for short).
# When used, a match fails if the *extended regular expression* does not match the line being tested.
#
# *__Note__:
# As expected, the anchors `^` and `$` bind to the beginning and the end (respectively) of the matched line.*
#
# ```bash
# @test 'assert_line() regular expression matching' {
# run echo $'have-0\nhave-1\nhave-2'
# assert_line --index 1 --regexp '^want-[0-9]$'
# }
# ```
#
# On failure, the same details are displayed as for literal matching, except that the regular expression replaces the expected line.
#
# ```
# -- regular expression does not match line --
# index : 1
# regexp : ^want-[0-9]$
# line : have-1
# --
# ```
# FIXME(ztombol): Display `${lines[@]}' instead of `$output'!
assert_line() {
local -i is_match_line=0
local -i is_mode_partial=0
local -i is_mode_regexp=0
: "${lines?}"
# Handle options.
while (( $# > 0 )); do
case "$1" in
-n|--index)
if (( $# < 2 )) || ! [[ $2 =~ ^-?([0-9]|[1-9][0-9]+)$ ]]; then
echo "\`--index' requires an integer argument: \`$2'" \
| batslib_decorate 'ERROR: assert_line' \
| fail
return $?
fi
is_match_line=1
local -ri idx="$2"
shift 2
;;
-p|--partial) is_mode_partial=1; shift ;;
-e|--regexp) is_mode_regexp=1; shift ;;
--) shift; break ;;
*) break ;;
esac
done
if (( is_mode_partial )) && (( is_mode_regexp )); then
echo "\`--partial' and \`--regexp' are mutually exclusive" \
| batslib_decorate 'ERROR: assert_line' \
| fail
return $?
fi
# Arguments.
local -r expected="$1"
if (( is_mode_regexp == 1 )) && [[ '' =~ $expected ]] || (( $? == 2 )); then
echo "Invalid extended regular expression: \`$expected'" \
| batslib_decorate 'ERROR: assert_line' \
| fail
return $?
fi
# Matching.
if (( is_match_line )); then
# Specific line.
if (( is_mode_regexp )); then
if ! [[ ${lines[$idx]} =~ $expected ]]; then
batslib_print_kv_single 6 \
'index' "$idx" \
'regexp' "$expected" \
'line' "${lines[$idx]}" \
| batslib_decorate 'regular expression does not match line' \
| fail
fi
elif (( is_mode_partial )); then
if [[ ${lines[$idx]} != *"$expected"* ]]; then
batslib_print_kv_single 9 \
'index' "$idx" \
'substring' "$expected" \
'line' "${lines[$idx]}" \
| batslib_decorate 'line does not contain substring' \
| fail
fi
else
if [[ ${lines[$idx]} != "$expected" ]]; then
batslib_print_kv_single 8 \
'index' "$idx" \
'expected' "$expected" \
'actual' "${lines[$idx]}" \
| batslib_decorate 'line differs' \
| fail
fi
fi
else
# Contained in output.
if (( is_mode_regexp )); then
local -i idx
for (( idx = 0; idx < ${#lines[@]}; ++idx )); do
[[ ${lines[$idx]} =~ $expected ]] && return 0
done
{ local -ar single=( 'regexp' "$expected" )
local -ar may_be_multi=( 'output' "$output" )
local -ir width="$( batslib_get_max_single_line_key_width "${single[@]}" "${may_be_multi[@]}" )"
batslib_print_kv_single "$width" "${single[@]}"
batslib_print_kv_single_or_multi "$width" "${may_be_multi[@]}"
} \
| batslib_decorate 'no output line matches regular expression' \
| fail
elif (( is_mode_partial )); then
local -i idx
for (( idx = 0; idx < ${#lines[@]}; ++idx )); do
[[ ${lines[$idx]} == *"$expected"* ]] && return 0
done
{ local -ar single=( 'substring' "$expected" )
local -ar may_be_multi=( 'output' "$output" )
local -ir width="$( batslib_get_max_single_line_key_width "${single[@]}" "${may_be_multi[@]}" )"
batslib_print_kv_single "$width" "${single[@]}"
batslib_print_kv_single_or_multi "$width" "${may_be_multi[@]}"
} \
| batslib_decorate 'no output line contains substring' \
| fail
else
local -i idx
for (( idx = 0; idx < ${#lines[@]}; ++idx )); do
[[ ${lines[$idx]} == "$expected" ]] && return 0
done
{ local -ar single=( 'line' "$expected" )
local -ar may_be_multi=( 'output' "$output" )
local -ir width="$( batslib_get_max_single_line_key_width "${single[@]}" "${may_be_multi[@]}" )"
batslib_print_kv_single "$width" "${single[@]}"
batslib_print_kv_single_or_multi "$width" "${may_be_multi[@]}"
} \
| batslib_decorate 'output does not contain line' \
| fail
fi
fi
}

View File

@ -0,0 +1,42 @@
# assert_not_equal
# ============
#
# Summary: Fail if the actual and unexpected values are equal.
#
# Usage: assert_not_equal <actual> <unexpected>
#
# Options:
# <actual> The value being compared.
# <unexpected> The value to compare against.
#
# ```bash
# @test 'assert_not_equal()' {
# assert_not_equal 'foo' 'foo'
# }
# ```
#
# IO:
# STDERR - expected and actual values, on failure
# Globals:
# none
# Returns:
# 0 - if actual does not equal unexpected
# 1 - otherwise
#
# On failure, the unexpected and actual values are displayed.
#
# ```
# -- values should not be equal --
# unexpected : foo
# actual : foo
# --
# ```
assert_not_equal() {
if [[ "$1" == "$2" ]]; then
batslib_print_kv_single_or_multi 10 \
'unexpected' "$2" \
'actual' "$1" \
| batslib_decorate 'values should not be equal' \
| fail
fi
}

View File

@ -0,0 +1,197 @@
# assert_output
# =============
#
# Summary: Fail if `$output' does not match the expected output.
#
# Usage: assert_output [-p | -e] [- | [--] <expected>]
#
# Options:
# -p, --partial Match if `expected` is a substring of `$output`
# -e, --regexp Treat `expected` as an extended regular expression
# -, --stdin Read `expected` value from STDIN
# <expected> The expected value, substring or regular expression
#
# IO:
# STDIN - [=$1] expected output
# STDERR - details, on failure
# error message, on error
# Globals:
# output
# Returns:
# 0 - if output matches the expected value/partial/regexp
# 1 - otherwise
#
# This function verifies that a command or function produces the expected output.
# (It is the logical complement of `refute_output`.)
# Output matching can be literal (the default), partial or by regular expression.
# The expected output can be specified either by positional argument or read from STDIN by passing the `-`/`--stdin` flag.
#
# ## Literal matching
#
# By default, literal matching is performed.
# The assertion fails if `$output` does not equal the expected output.
#
# ```bash
# @test 'assert_output()' {
# run echo 'have'
# assert_output 'want'
# }
#
# @test 'assert_output() with pipe' {
# run echo 'hello'
# echo 'hello' | assert_output -
# }
#
# @test 'assert_output() with herestring' {
# run echo 'hello'
# assert_output - <<< hello
# }
# ```
#
# On failure, the expected and actual output are displayed.
#
# ```
# -- output differs --
# expected : want
# actual : have
# --
# ```
#
# ## Existence
#
# To assert that any output exists at all, omit the `expected` argument.
#
# ```bash
# @test 'assert_output()' {
# run echo 'have'
# assert_output
# }
# ```
#
# On failure, an error message is displayed.
#
# ```
# -- no output --
# expected non-empty output, but output was empty
# --
# ```
#
# ## Partial matching
#
# Partial matching can be enabled with the `--partial` option (`-p` for short).
# When used, the assertion fails if the expected _substring_ is not found in `$output`.
#
# ```bash
# @test 'assert_output() partial matching' {
# run echo 'ERROR: no such file or directory'
# assert_output --partial 'SUCCESS'
# }
# ```
#
# On failure, the substring and the output are displayed.
#
# ```
# -- output does not contain substring --
# substring : SUCCESS
# output : ERROR: no such file or directory
# --
# ```
#
# ## Regular expression matching
#
# Regular expression matching can be enabled with the `--regexp` option (`-e` for short).
# When used, the assertion fails if the *extended regular expression* does not match `$output`.
#
# *__Note__:
# The anchors `^` and `$` bind to the beginning and the end (respectively) of the entire output;
# not individual lines.*
#
# ```bash
# @test 'assert_output() regular expression matching' {
# run echo 'Foobar 0.1.0'
# assert_output --regexp '^Foobar v[0-9]+\.[0-9]+\.[0-9]$'
# }
# ```
#
# On failure, the regular expression and the output are displayed.
#
# ```
# -- regular expression does not match output --
# regexp : ^Foobar v[0-9]+\.[0-9]+\.[0-9]$
# output : Foobar 0.1.0
# --
# ```
assert_output() {
local -i is_mode_partial=0
local -i is_mode_regexp=0
local -i is_mode_nonempty=0
local -i use_stdin=0
: "${output?}"
# Handle options.
if (( $# == 0 )); then
is_mode_nonempty=1
fi
while (( $# > 0 )); do
case "$1" in
-p|--partial) is_mode_partial=1; shift ;;
-e|--regexp) is_mode_regexp=1; shift ;;
-|--stdin) use_stdin=1; shift ;;
--) shift; break ;;
*) break ;;
esac
done
if (( is_mode_partial )) && (( is_mode_regexp )); then
echo "\`--partial' and \`--regexp' are mutually exclusive" \
| batslib_decorate 'ERROR: assert_output' \
| fail
return $?
fi
# Arguments.
local expected
if (( use_stdin )); then
expected="$(cat -)"
else
expected="${1-}"
fi
# Matching.
if (( is_mode_nonempty )); then
if [ -z "$output" ]; then
echo 'expected non-empty output, but output was empty' \
| batslib_decorate 'no output' \
| fail
fi
elif (( is_mode_regexp )); then
if [[ '' =~ $expected ]] || (( $? == 2 )); then
echo "Invalid extended regular expression: \`$expected'" \
| batslib_decorate 'ERROR: assert_output' \
| fail
elif ! [[ $output =~ $expected ]]; then
batslib_print_kv_single_or_multi 6 \
'regexp' "$expected" \
'output' "$output" \
| batslib_decorate 'regular expression does not match output' \
| fail
fi
elif (( is_mode_partial )); then
if [[ $output != *"$expected"* ]]; then
batslib_print_kv_single_or_multi 9 \
'substring' "$expected" \
'output' "$output" \
| batslib_decorate 'output does not contain substring' \
| fail
fi
else
if [[ $output != "$expected" ]]; then
batslib_print_kv_single_or_multi 8 \
'expected' "$expected" \
'actual' "$output" \
| batslib_decorate 'output differs' \
| fail
fi
fi
}

View File

@ -0,0 +1,56 @@
# `assert_regex`
#
# This function is similar to `assert_equal` but uses pattern matching instead
# of equality, by wrapping `[[ value =~ pattern ]]`.
#
# Fail if the value (first parameter) does not match the pattern (second
# parameter).
#
# ```bash
# @test 'assert_regex()' {
# assert_regex 'what' 'x$'
# }
# ```
#
# On failure, the value and the pattern are displayed.
#
# ```
# -- values does not match regular expression --
# value : what
# pattern : x$
# --
# ```
#
# If the value is longer than one line then it is displayed in *multi-line*
# format.
#
# An error is displayed if the specified extended regular expression is invalid.
#
# For description of the matching behavior, refer to the documentation of the
# `=~` operator in the
# [Bash manual]: https://www.gnu.org/software/bash/manual/html_node/Conditional-Constructs.html.
# Note that the `BASH_REMATCH` array is available immediately after the
# assertion succeeds but is fragile, i.e. prone to being overwritten as a side
# effect of other actions.
assert_regex() {
local -r value="${1}"
local -r pattern="${2}"
if [[ '' =~ ${pattern} ]] || (( ${?} == 2 )); then
echo "Invalid extended regular expression: \`${pattern}'" \
| batslib_decorate 'ERROR: assert_regex' \
| fail
elif ! [[ "${value}" =~ ${pattern} ]]; then
if shopt -p nocasematch &>/dev/null; then
local case_sensitive=insensitive
else
local case_sensitive=sensitive
fi
batslib_print_kv_single_or_multi 8 \
'value' "${value}" \
'pattern' "${pattern}" \
'case' "${case_sensitive}" \
| batslib_decorate 'value does not match regular expression' \
| fail
fi
}

View File

@ -0,0 +1,44 @@
# assert_success
# ==============
#
# Summary: Fail if `$status` is not 0.
#
# Usage: assert_success
#
# IO:
# STDERR - `$status` and `$output`, on failure
# Globals:
# status
# output
# Returns:
# 0 - if `$status' is 0
# 1 - otherwise
#
# ```bash
# @test 'assert_success() status only' {
# run bash -c "echo 'Error!'; exit 1"
# assert_success
# }
# ```
#
# On failure, `$status` and `$output` are displayed.
#
# ```
# -- command failed --
# status : 1
# output : Error!
# --
# ```
assert_success() {
: "${output?}"
: "${status?}"
if (( status != 0 )); then
{ local -ir width=6
batslib_print_kv_single "$width" 'status' "$status"
batslib_print_kv_single_or_multi "$width" 'output' "$output"
} \
| batslib_decorate 'command failed' \
| fail
fi
}

View File

@ -0,0 +1,42 @@
# refute
# ======
#
# Summary: Fail if the given expression evaluates to true.
#
# Usage: refute <expression>
#
# Options:
# <expression> The expression to evaluate for falsiness.
# *__Note:__ The expression must be a simple command.
# [Compound commands](https://www.gnu.org/software/bash/manual/bash.html#Compound-Commands),
# such as `[[`, can be used only when executed with `bash -c`.*
#
# IO:
# STDERR - the successful expression, on failure
# Globals:
# none
# Returns:
# 0 - if expression evaluates to false
# 1 - otherwise
#
# ```bash
# @test 'refute()' {
# rm -f '/var/log/test.log'
# refute [ -e '/var/log/test.log' ]
# }
# ```
#
# On failure, the successful expression is displayed.
#
# ```
# -- assertion succeeded, but it was expected to fail --
# expression : [ -e /var/log/test.log ]
# --
# ```
refute() {
if "$@"; then
batslib_print_kv_single 10 'expression' "$*" \
| batslib_decorate 'assertion succeeded, but it was expected to fail' \
| fail
fi
}

View File

@ -0,0 +1,271 @@
# refute_line
# ===========
#
# Summary: Fail if the unexpected line is found in the output (default) or at a specific line number.
#
# Usage: refute_line [-n index] [-p | -e] [--] <unexpected>
#
# Options:
# -n, --index <idx> Match the <idx>th line
# -p, --partial Match if `unexpected` is a substring of `$output` or line <idx>
# -e, --regexp Treat `unexpected` as an extended regular expression
# <unexpected> The unexpected line string, substring, or regular expression.
#
# IO:
# STDERR - details, on failure
# error message, on error
# Globals:
# output
# lines
# Returns:
# 0 - if match not found
# 1 - otherwise
#
# Similarly to `refute_output`, this function verifies that a command or function does not produce the unexpected output.
# (It is the logical complement of `assert_line`.)
# It checks that the unexpected line does not appear in the output (default) or at a specific line number.
# Matching can be literal (default), partial or regular expression.
#
# *__Warning:__
# Due to a [bug in Bats][bats-93], empty lines are discarded from `${lines[@]}`,
# causing line indices to change and preventing testing for empty lines.*
#
# [bats-93]: https://github.com/sstephenson/bats/pull/93
#
# ## Looking for a line in the output
#
# By default, the entire output is searched for the unexpected line.
# The assertion fails if the unexpected line is found in `${lines[@]}`.
#
# ```bash
# @test 'refute_line() looking for line' {
# run echo $'have-0\nwant\nhave-2'
# refute_line 'want'
# }
# ```
#
# On failure, the unexpected line, the index of its first match and the output with the matching line highlighted are displayed.
#
# ```
# -- line should not be in output --
# line : want
# index : 1
# output (3 lines):
# have-0
# > want
# have-2
# --
# ```
#
# ## Matching a specific line
#
# When the `--index <idx>` option is used (`-n <idx>` for short), the unexpected line is matched only against the line identified by the given index.
# The assertion fails if the unexpected line equals `${lines[<idx>]}`.
#
# ```bash
# @test 'refute_line() specific line' {
# run echo $'have-0\nwant-1\nhave-2'
# refute_line --index 1 'want-1'
# }
# ```
#
# On failure, the index and the unexpected line are displayed.
#
# ```
# -- line should differ --
# index : 1
# line : want-1
# --
# ```
#
# ## Partial matching
#
# Partial matching can be enabled with the `--partial` option (`-p` for short).
# When used, a match fails if the unexpected *substring* is found in the matched line.
#
# ```bash
# @test 'refute_line() partial matching' {
# run echo $'have 1\nwant 2\nhave 3'
# refute_line --partial 'want'
# }
# ```
#
# On failure, in addition to the details of literal matching, the substring is also displayed.
# When used with `--index <idx>` the substring replaces the unexpected line.
#
# ```
# -- no line should contain substring --
# substring : want
# index : 1
# output (3 lines):
# have 1
# > want 2
# have 3
# --
# ```
#
# ## Regular expression matching
#
# Regular expression matching can be enabled with the `--regexp` option (`-e` for short).
# When used, a match fails if the *extended regular expression* matches the line being tested.
#
# *__Note__:
# As expected, the anchors `^` and `$` bind to the beginning and the end (respectively) of the matched line.*
#
# ```bash
# @test 'refute_line() regular expression matching' {
# run echo $'Foobar v0.1.0\nRelease date: 2015-11-29'
# refute_line --index 0 --regexp '^Foobar v[0-9]+\.[0-9]+\.[0-9]$'
# }
# ```
#
# On failure, in addition to the details of literal matching, the regular expression is also displayed.
# When used with `--index <idx>` the regular expression replaces the unexpected line.
#
# ```
# -- regular expression should not match line --
# index : 0
# regexp : ^Foobar v[0-9]+\.[0-9]+\.[0-9]$
# line : Foobar v0.1.0
# --
# ```
# FIXME(ztombol): Display `${lines[@]}' instead of `$output'!
refute_line() {
local -i is_match_line=0
local -i is_mode_partial=0
local -i is_mode_regexp=0
: "${lines?}"
# Handle options.
while (( $# > 0 )); do
case "$1" in
-n|--index)
if (( $# < 2 )) || ! [[ $2 =~ ^-?([0-9]|[1-9][0-9]+)$ ]]; then
echo "\`--index' requires an integer argument: \`$2'" \
| batslib_decorate 'ERROR: refute_line' \
| fail
return $?
fi
is_match_line=1
local -ri idx="$2"
shift 2
;;
-p|--partial) is_mode_partial=1; shift ;;
-e|--regexp) is_mode_regexp=1; shift ;;
--) shift; break ;;
*) break ;;
esac
done
if (( is_mode_partial )) && (( is_mode_regexp )); then
echo "\`--partial' and \`--regexp' are mutually exclusive" \
| batslib_decorate 'ERROR: refute_line' \
| fail
return $?
fi
# Arguments.
local -r unexpected="$1"
if (( is_mode_regexp == 1 )) && [[ '' =~ $unexpected ]] || (( $? == 2 )); then
echo "Invalid extended regular expression: \`$unexpected'" \
| batslib_decorate 'ERROR: refute_line' \
| fail
return $?
fi
# Matching.
if (( is_match_line )); then
# Specific line.
if (( is_mode_regexp )); then
if [[ ${lines[$idx]} =~ $unexpected ]]; then
batslib_print_kv_single 6 \
'index' "$idx" \
'regexp' "$unexpected" \
'line' "${lines[$idx]}" \
| batslib_decorate 'regular expression should not match line' \
| fail
fi
elif (( is_mode_partial )); then
if [[ ${lines[$idx]} == *"$unexpected"* ]]; then
batslib_print_kv_single 9 \
'index' "$idx" \
'substring' "$unexpected" \
'line' "${lines[$idx]}" \
| batslib_decorate 'line should not contain substring' \
| fail
fi
else
if [[ ${lines[$idx]} == "$unexpected" ]]; then
batslib_print_kv_single 5 \
'index' "$idx" \
'line' "${lines[$idx]}" \
| batslib_decorate 'line should differ' \
| fail
fi
fi
else
# Line contained in output.
if (( is_mode_regexp )); then
local -i idx
for (( idx = 0; idx < ${#lines[@]}; ++idx )); do
if [[ ${lines[$idx]} =~ $unexpected ]]; then
{ local -ar single=( 'regexp' "$unexpected" 'index' "$idx" )
local -a may_be_multi=( 'output' "$output" )
local -ir width="$( batslib_get_max_single_line_key_width "${single[@]}" "${may_be_multi[@]}" )"
batslib_print_kv_single "$width" "${single[@]}"
if batslib_is_single_line "${may_be_multi[1]}"; then
batslib_print_kv_single "$width" "${may_be_multi[@]}"
else
may_be_multi[1]="$( printf '%s' "${may_be_multi[1]}" | batslib_prefix | batslib_mark '>' "$idx" )"
batslib_print_kv_multi "${may_be_multi[@]}"
fi
} \
| batslib_decorate 'no line should match the regular expression' \
| fail
return $?
fi
done
elif (( is_mode_partial )); then
local -i idx
for (( idx = 0; idx < ${#lines[@]}; ++idx )); do
if [[ ${lines[$idx]} == *"$unexpected"* ]]; then
{ local -ar single=( 'substring' "$unexpected" 'index' "$idx" )
local -a may_be_multi=( 'output' "$output" )
local -ir width="$( batslib_get_max_single_line_key_width "${single[@]}" "${may_be_multi[@]}" )"
batslib_print_kv_single "$width" "${single[@]}"
if batslib_is_single_line "${may_be_multi[1]}"; then
batslib_print_kv_single "$width" "${may_be_multi[@]}"
else
may_be_multi[1]="$( printf '%s' "${may_be_multi[1]}" | batslib_prefix | batslib_mark '>' "$idx" )"
batslib_print_kv_multi "${may_be_multi[@]}"
fi
} \
| batslib_decorate 'no line should contain substring' \
| fail
return $?
fi
done
else
local -i idx
for (( idx = 0; idx < ${#lines[@]}; ++idx )); do
if [[ ${lines[$idx]} == "$unexpected" ]]; then
{ local -ar single=( 'line' "$unexpected" 'index' "$idx" )
local -a may_be_multi=( 'output' "$output" )
local -ir width="$( batslib_get_max_single_line_key_width "${single[@]}" "${may_be_multi[@]}" )"
batslib_print_kv_single "$width" "${single[@]}"
if batslib_is_single_line "${may_be_multi[1]}"; then
batslib_print_kv_single "$width" "${may_be_multi[@]}"
else
may_be_multi[1]="$( printf '%s' "${may_be_multi[1]}" | batslib_prefix | batslib_mark '>' "$idx" )"
batslib_print_kv_multi "${may_be_multi[@]}"
fi
} \
| batslib_decorate 'line should not be in output' \
| fail
return $?
fi
done
fi
fi
}

View File

@ -0,0 +1,199 @@
# refute_output
# =============
#
# Summary: Fail if `$output' matches the unexpected output.
#
# Usage: refute_output [-p | -e] [- | [--] <unexpected>]
#
# Options:
# -p, --partial Match if `unexpected` is a substring of `$output`
# -e, --regexp Treat `unexpected` as an extended regular expression
# -, --stdin Read `unexpected` value from STDIN
# <unexpected> The unexpected value, substring, or regular expression
#
# IO:
# STDIN - [=$1] unexpected output
# STDERR - details, on failure
# error message, on error
# Globals:
# output
# Returns:
# 0 - if output matches the unexpected value/partial/regexp
# 1 - otherwise
#
# This function verifies that a command or function does not produce the unexpected output.
# (It is the logical complement of `assert_output`.)
# Output matching can be literal (the default), partial or by regular expression.
# The unexpected output can be specified either by positional argument or read from STDIN by passing the `-`/`--stdin` flag.
#
# ## Literal matching
#
# By default, literal matching is performed.
# The assertion fails if `$output` equals the unexpected output.
#
# ```bash
# @test 'refute_output()' {
# run echo 'want'
# refute_output 'want'
# }
#
# @test 'refute_output() with pipe' {
# run echo 'hello'
# echo 'world' | refute_output -
# }
#
# @test 'refute_output() with herestring' {
# run echo 'hello'
# refute_output - <<< world
# }
# ```
#
# On failure, the output is displayed.
#
# ```
# -- output equals, but it was expected to differ --
# output : want
# --
# ```
#
# ## Existence
#
# To assert that there is no output at all, omit the matching argument.
#
# ```bash
# @test 'refute_output()' {
# run foo --silent
# refute_output
# }
# ```
#
# On failure, an error message is displayed.
#
# ```
# -- unexpected output --
# expected no output, but output was non-empty
# --
# ```
#
# ## Partial matching
#
# Partial matching can be enabled with the `--partial` option (`-p` for short).
# When used, the assertion fails if the unexpected _substring_ is found in `$output`.
#
# ```bash
# @test 'refute_output() partial matching' {
# run echo 'ERROR: no such file or directory'
# refute_output --partial 'ERROR'
# }
# ```
#
# On failure, the substring and the output are displayed.
#
# ```
# -- output should not contain substring --
# substring : ERROR
# output : ERROR: no such file or directory
# --
# ```
#
# ## Regular expression matching
#
# Regular expression matching can be enabled with the `--regexp` option (`-e` for short).
# When used, the assertion fails if the *extended regular expression* matches `$output`.
#
# *__Note__:
# The anchors `^` and `$` bind to the beginning and the end (respectively) of the entire output;
# not individual lines.*
#
# ```bash
# @test 'refute_output() regular expression matching' {
# run echo 'Foobar v0.1.0'
# refute_output --regexp '^Foobar v[0-9]+\.[0-9]+\.[0-9]$'
# }
# ```
#
# On failure, the regular expression and the output are displayed.
#
# ```
# -- regular expression should not match output --
# regexp : ^Foobar v[0-9]+\.[0-9]+\.[0-9]$
# output : Foobar v0.1.0
# --
# ```
refute_output() {
local -i is_mode_partial=0
local -i is_mode_regexp=0
local -i is_mode_empty=0
local -i use_stdin=0
: "${output?}"
# Handle options.
if (( $# == 0 )); then
is_mode_empty=1
fi
while (( $# > 0 )); do
case "$1" in
-p|--partial) is_mode_partial=1; shift ;;
-e|--regexp) is_mode_regexp=1; shift ;;
-|--stdin) use_stdin=1; shift ;;
--) shift; break ;;
*) break ;;
esac
done
if (( is_mode_partial )) && (( is_mode_regexp )); then
echo "\`--partial' and \`--regexp' are mutually exclusive" \
| batslib_decorate 'ERROR: refute_output' \
| fail
return $?
fi
# Arguments.
local unexpected
if (( use_stdin )); then
unexpected="$(cat -)"
else
unexpected="${1-}"
fi
if (( is_mode_regexp == 1 )) && [[ '' =~ $unexpected ]] || (( $? == 2 )); then
echo "Invalid extended regular expression: \`$unexpected'" \
| batslib_decorate 'ERROR: refute_output' \
| fail
return $?
fi
# Matching.
if (( is_mode_empty )); then
if [ -n "$output" ]; then
batslib_print_kv_single_or_multi 6 \
'output' "$output" \
| batslib_decorate 'output non-empty, but expected no output' \
| fail
fi
elif (( is_mode_regexp )); then
if [[ $output =~ $unexpected ]]; then
batslib_print_kv_single_or_multi 6 \
'regexp' "$unexpected" \
'output' "$output" \
| batslib_decorate 'regular expression should not match output' \
| fail
fi
elif (( is_mode_partial )); then
if [[ $output == *"$unexpected"* ]]; then
batslib_print_kv_single_or_multi 9 \
'substring' "$unexpected" \
'output' "$output" \
| batslib_decorate 'output should not contain substring' \
| fail
fi
else
if [[ $output == "$unexpected" ]]; then
batslib_print_kv_single_or_multi 6 \
'output' "$output" \
| batslib_decorate 'output equals, but it was expected to differ' \
| fail
fi
fi
}

View File

@ -0,0 +1,66 @@
# `refute_regex`
#
# This function is similar to `refute_equal` but uses pattern matching instead
# of equality, by wrapping `! [[ value =~ pattern ]]`.
#
# Fail if the value (first parameter) matches the pattern (second parameter).
#
# ```bash
# @test 'refute_regex()' {
# refute_regex 'WhatsApp' 'Threema'
# }
# ```
#
# On failure, the value, the pattern and the match are displayed.
#
# ```
# @test 'refute_regex()' {
# refute_regex 'WhatsApp' 'What.'
# }
#
# -- value matches regular expression --
# value : WhatsApp
# pattern : What.
# match : Whats
# case : sensitive
# --
# ```
#
# If the value or pattern is longer than one line then it is displayed in
# *multi-line* format.
#
# An error is displayed if the specified extended regular expression is invalid.
#
# For description of the matching behavior, refer to the documentation of the
# `=~` operator in the
# [Bash manual]: https://www.gnu.org/software/bash/manual/html_node/Conditional-Constructs.html.
#
# Note that the `BASH_REMATCH` array is available immediately after the
# assertion fails but is fragile, i.e. prone to being overwritten as a side
# effect of other actions like calling `run`. Thus, it's good practice to avoid
# using `BASH_REMATCH` in conjunction with `refute_regex()`. The valuable
# information the array contains is the matching part of the value which is
# printed in the failing test log, as mentioned above.
refute_regex() {
local -r value="${1}"
local -r pattern="${2}"
if [[ '' =~ ${pattern} ]] || (( ${?} == 2 )); then
echo "Invalid extended regular expression: \`${pattern}'" \
| batslib_decorate 'ERROR: refute_regex' \
| fail
elif [[ "${value}" =~ ${pattern} ]]; then
if shopt -p nocasematch &>/dev/null; then
local case_sensitive=insensitive
else
local case_sensitive=sensitive
fi
batslib_print_kv_single_or_multi 8 \
'value' "${value}" \
'pattern' "${pattern}" \
'match' "${BASH_REMATCH[0]}" \
'case' "${case_sensitive}" \
| batslib_decorate 'value matches regular expression' \
| fail
fi
}

View File

@ -0,0 +1,2 @@
source "$(dirname "${BASH_SOURCE[0]}")/src/file.bash"
source "$(dirname "${BASH_SOURCE[0]}")/src/temp.bash"

Some files were not shown because too many files have changed in this diff Show More