Compare commits

...

162 Commits
v2.8 ... 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
134 changed files with 23451 additions and 6066 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

View File

@ -2,25 +2,28 @@ Cryptsetup was originally written in 2004 by Jana Saout
LUKS extensions are written in 2006 by Clemens Fruhwirth LUKS extensions are written in 2006 by Clemens Fruhwirth
Tomb is written and maintained since 2007 by Denis Roio <J@dyne.org> 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,
Daniel Rodriguez, Parazyd, Alexandre Pujol, AitorATuin, Narrat and Daniel Rodriguez, Parazyd, Alexandre Pujol, AitorATuin, Narrat, Artur
Artur Malimonov. Malimonov and Chris Vogel.
The 'gtomb' GUI based on Zenity is 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 contributed 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
Daniel Rodriguez. French translation by Hellekin and Roy Lockhart, Daniel Rodriguez and Francisco Serrador. French translation by
Russian translation by fsLeg, German translation by Jerry Polfer, Hellekin and Roy Lockhart, Russian translation by fsLeg and AHOHNMYC,
Italian translation by Massimiliano Augello and Swedish translation by German translation by Jerry Polfer, Italian translation by
PLJ / Kosovoper. 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,
@ -29,13 +32,12 @@ Polfer, Jim Turner, Maxime Arthaud, RobertMX, mhogomchungu Mandeep
Bhutani, Emil Lundberg, Joel Montes de Oca, Armin Mesbah, Arusekk, Bhutani, Emil Lundberg, Joel Montes de Oca, Armin Mesbah, Arusekk,
Stephan Schindel, Asbjørn Apeland, Victor Calvert, bjonnh, SargoDevel, Stephan Schindel, Asbjørn Apeland, Victor Calvert, bjonnh, SargoDevel,
AitorATuin, Alexis Danizan, Sven Geuer, Greg Tczap, Aaron Janse, Mark AitorATuin, Alexis Danizan, Sven Geuer, Greg Tczap, Aaron Janse, Mark
Mykkanen, Alexis Danizan, Steve Litt, James R, and... Mykkanen, Alexis Danizan, Steve Litt, James R, Matthieu Crapet, Selene
the Linux Action Show! 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, with Function v2" based on GCrypt and written by Anthony Thyssen, with
fixes contributed by AitorATuin. 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,55 @@
# 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 ## 2.8
### Nov 2020 ### Nov 2020

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
@ -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.
@ -71,8 +72,8 @@ executable | function
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

View File

@ -1,5 +1,5 @@
# Password bug in X11 when using pinentry-curses # Password bug in X11 when using pinentry-curses
## Issue with Tomb version 2.0 to 2.7 ## Issue with Tomb version 2.6 and 2.7
This bug affects systems with a running X11 DISPLAY, but where only This bug affects systems with a running X11 DISPLAY, but where only
pinentry-ncurses is installed. It wrongly reads the input password: no pinentry-ncurses is installed. It wrongly reads the input password: no
@ -22,16 +22,16 @@ cryptsetup v2.1 a new default has been introduced (luks2) and the
Using Tomb version 2.6 (and future releases) the problem opening tombs Using Tomb version 2.6 (and future releases) the problem opening tombs
using recent GNU/Linux distributions is fixed. using recent GNU/Linux distributions is fixed.
# Whitespaces in KDF passwords # Whitespace in KDF passwords
## Issue affecting passwords used with PBKDF2 keys (<2.6) ## Issue affecting passwords used with PBKDF2 keys (<2.6)
Up until and including Tomb's version 2.5 the PBKDF2 wrapper for keys Up until and including Tomb's version 2.5 the PBKDF2 wrapper for keys
in Tomb has a bug affecting passwords that contain whitespaces. Since in Tomb has a bug affecting passwords that contain whitespace. Since
the passwords are trimmed at the first whitespace, this makes them the passwords are trimmed at the first whitespace, this makes them
weaker, while fortunately the KDF transformation still applies. weaker, while fortunately the KDF transformation still applies.
This issue is fixed in Tomb version 2.6: all users adopting KDF keys This issue is fixed in Tomb version 2.6: all users adopting KDF keys
that have passwords containing whitespaces should change them, that have passwords containing whitespace should change them,
knowing that their "old password" is trimmed until the whitespace. knowing that their "old password" is trimmed until the whitespace.
Users adopting GPG keys or plain (without KDF wrapper) can ignore Users adopting GPG keys or plain (without KDF wrapper) can ignore

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."
@ -26,4 +34,4 @@ test:
make -C extras/test make -C extras/test
lint: lint:
shellcheck -s zsh -e SC1073,SC1027,SC1072,SC1083,SC1009 tomb shellcheck -s bash -e SC1058,SC1073,SC1072,SC1009 tomb

307
README.md
View File

@ -1,311 +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://files.dyne.org/software_by_dyne.png)](http://www.dyne.org) Minimalistic command line tool based on Linux dm-crypt and LUKS, trusted by hackers since 2007.
More information and 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
For the instructions on how to get started using Tomb, see [INSTALL](INSTALL.md).
![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)
[![Build Status](https://travis-ci.org/dyne/Tomb.svg?branch=master)](https://travis-ci.org/dyne/Tomb) Create a new 120MiB `secret.tomb` folder and lock it with a new `secret.tomb.key` file.
# What is Tomb, the crypto undertaker?
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 well reliable GNU/Linux components.
Tomb's ambition is to improve safety by way of:
- a minimalist design consisting in small and well readable code
- facilitation of good practices, i.e: key/storage physical separation
- adoption of a few standard and well tested implementations.
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)
``` <a href="https://dyne.org/tomb"><img src="https://files.dyne.org/software_by_dyne.png" width="30%"></a>
Syntax: tomb [options] command [arguments]
Commands: More information in `man tomb` and on [dyne.org/docs/tomb](https://dyne.org/docs/tomb).
// Creation: ### 💾 [Download from files.dyne.org/tomb](https://files.dyne.org/tomb/)
dig create a new empty TOMB file of size -s in MiB
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: Use only stable and signed releases in production!
open open an existing TOMB (-k KEY file or - for stdin)
index update the search indexes of tombs
search looks for filenames matching text patterns
list list of open TOMBs and information on them
ps list of running processes inside open TOMBs
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: Tomb's development is community-based!
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: ## 🤏🏽 How can you help
engrave makes a QR code of a KEY to be saved on paper
// Steganography: Donations are very welcome on [dyne.org/donate](https://www.dyne.org/donate)
bury hide a KEY inside a JPEG image (for use with -k)
exhume extract a KEY from a JPEG image (prints to stdout)
cloak transform a KEY into a TEXT using CIPHER (for use with -k)
uncloak extract a KEY from a TEXT file using CIPHER (prints to stdout)
Options: Translations are also welcome: see our simple [translation guide](https://github.com/dyne/Tomb/blob/master/extras/translations/README.md)
-s size of the tomb file when creating/resizing one (in MiB) 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).
-k path to the key to be used ('-k -' to read from stdin)
-n don't process the hooks found in tomb
-o options passed to commands: open, lock, forge (see man)
-f force operation (i.e. open even if swap is active)
-g use a GnuPG key to encrypt a tomb key
-r provide GnuPG recipients (separated by comma)
-R provide GnuPG hidden recipients (separated by comma)
--kdf forge keys armored against dictionary attacks
-h print this help 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.
-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? There is also a [space for discussion](https://github.com/dyne/Tomb/discussions) of new features, desiderata and whatnot on github.
This tool can be used to dig .tomb files (LUKS volumes), forge keys
protected by a password (GnuPG 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,
on-line SSH servers 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: protects the
typing of passwords from keyloggers, facilitates hiding keys inside
images, indexes and search a tomb's contents, mounts directories in
place, 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
unset pass
```
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 safe for
adoption where the integrity of information stored depends on the
user's behaviour and the strength of a standard AES-256 (XTS plain)
encryption algorithm (current default) or, at one's option, other
equivalent standards supported by the Linux kernel.
## Compatibility
Tomb can be used in conjunction with some other software applications,
some are developed by Dyne.org, but some also by third parties.
### Included extra applications
These auxiliary applications are found in the extras/ subdirectory of
distributed Tomb's sourcecode:
- [GTomb](extras/gtomb) is a graphical interface using zenity
- [gtk-tray](extras/gtk-tray) is a graphical tray icon for GTK panels
- [qt-tray](extras/qt-tray) is a graphical tray icon for QT panels
- [tomber](extras/tomber) is a wrapper to use Tomb in Python scripts
![skulls and pythons](https://github.com/dyne/Tomb/blob/master/extras/images/python_for_tomb.png)
### External applications
The following applications are not included in Tomb's distributed
sourcecode, but are known and tested to be compatible with Tomb:
- [Docker-tomb](https://github.com/gregtczap/docker-tomb) is a wrapper to use Tomb in any operating system supporting capable of running a Docker container with `--privileged` permissions.
- [Secrets](https://secrets.dyne.org) is a software that can be operated on-line and on-site to split a Tomb key in shares to be distributed to peers: some of them have to agree to combine back the shares in order to retrieve the key.
- [zuluCrypt](https://mhogomchungu.github.io/zuluCrypt/) is a graphical application to manage various types of encrypted volumes on GNU/Linux, among them also Tombs, written in C++.
- [Mausoleum](https://github.com/mandeep/Mausoleum) is a graphical interface to facilitate the creation and management of tombs, written in Python.
- [pass-tomb](https://github.com/roddhjav/pass-tomb) is a console based wrapper of the excellent password keeping program [pass](https://www.passwordstore.org) that helps to keep the whole tree of password encrypted inside a tomb. It is written in Bash.
If you are writing a project supporting Tomb volumes or wrapping Tomb, let us know!
## Compliancy
Tomb qualifies as sound for use on information rated as "top secret"
when used on an underlying stack of carefully reviewed hardware
(random number generator and other components) and software (Linux
kernel build, crypto modules, device manager, compiler used to built,
shell interpreter and packaged dependencies).
Tomb volumes are fully compliant with the FIPS 197 advanced encryption
standard published by NIST and with the following industry standards:
- Information technology -- Security techniques -- Encryption algorithms
- [ISO/IEC 18033-1:2015](http://www.iso.org/iso/home/store/catalogue_tc/catalogue_detail.htm?csnumber=54530) -- Part 1: General
- [ISO/IEC 18033-3:2010](http://www.iso.org/iso/home/store/catalogue_ics/catalogue_detail_ics.htm?csnumber=54531) -- Part 3: Block ciphers
Tomb implementation is known to address at least partially issues raised in:
- Information technology -- Security techniques -- Key management
- [ISO/IEC 11770-1:2010](http://www.iso.org/iso/home/store/catalogue_tc/catalogue_detail.htm?csnumber=53456) -- Part 1: Framework
- [ISO/IEC 11770-2:2008](http://www.iso.org/iso/home/store/catalogue_tc/catalogue_detail.htm?csnumber=46370) -- Part 2: Mechanisms using symmetric techniques
- [ISO/IEC 27005:2011](http://www.iso.org/iso/home/store/catalogue_tc/catalogue_detail.htm?csnumber=56742) Information technology -- Security techniques -- Information security risk management
- [ISO/IEC 24759:2014](http://www.iso.org/iso/home/store/catalogue_tc/catalogue_detail.htm?csnumber=59142) Information technology -- Security techniques -- Test requirements for cryptographic modules
Any help on further verification of compliancy is very welcome, as the
access to ISO/IEC document is limited due to its expensive nature.
# 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
![Day of the dead](https://github.com/dyne/Tomb/blob/master/extras/images/DayOfTheDead.jpg)
# How can you help
Donations are very welcome, please go to https://www.dyne.org/donate
Translations are also welcome: they can be contributed editing sending
the .po files in [extras/translations](extras/translations).
The code is pretty short and readable. There is also a collection of
specifications and design materials in the [doc](doc) directory.
To contribute code and reviews 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-2020 by the Dyne.org Foundation and 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.
maintained by Denis Roio <J@dyne.org>. More information on all
the developers involved is found in the [AUTHORS](AUTHORS.md) file.
This source code is free software; you can redistribute it and/or ## [More info on dyne.org/tomb](https://dyne.org/tomb)
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

@ -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

@ -1,4 +1,4 @@
.TH tomb 1 "May 22, 2019" "tomb" .TH tomb 1 "Jun 25, 2023" "tomb"
.SH NAME .SH NAME
Tomb \- the Crypto Undertaker Tomb \- the Crypto Undertaker
@ -32,23 +32,24 @@ 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 then be locked using a \fIkey\fR. It takes a mandatory \fI-s\fR option
which is the size in megabytes (MiB). Tombs are digged using random which is the size in megabytes (MiB). Tombs are digged using random
data gathered from a non-blocking source (/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 using symmetric encryption. This operation uses its usage using symmetric encryption. This operation uses random data from a
random data from a non-blocking source (/dev/urandom) and it may take non-blocking source (/dev/urandom) and it may take long only in some cases; to
long only in some cases; to switch using a blocking source the switch using a blocking source the \fI--use-random\fR flag can be used. The
\fI--use-random\fR flag can be used. The \fI-g\fR option switches on \fI-g\fR option switches on the use of a GPG key instead of a password
the use of a GPG key instead of a password (asymmetric encryption), (asymmetric encryption), then the \fI-r\fR option indicates the recipient key;
then the \fI-r\fR option indicates the recipient key; more recipient more recipient GPG ids can be indicated (comma separated). The default cipher
GPG ids can be indicated (comma separated). The default cipher to to protect the key is AES256, a custom one can be specified using the \fI-o\fR
protect the key is AES256, a custom one can be specified using the option, for a list of supported ciphers use \fI-v\fR. For additional protection
\fI-o\fR option, for a list of supported ciphers use \fI-v\fR. For against dictionary attacks on keys, the \fI--kdf\fR option can be used when
additional protection against dictionary attacks on keys, the forging a key, making sure that the binaries in \fIextras/kdf\fR were compiled
\fI--kdf\fR option can be used when forging a key, making sure that
the \fItomb-kdb-pbkdf2\fR binaries in \fIextras/kdf\fR were compiled
and installed on the system. and installed on the system.
.B .B
@ -61,12 +62,32 @@ 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 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 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", old versions of Tomb used "aes-xts-plain64", old versions of Tomb used "aes-cbc-essiv:sha256".
"aes-cbc-essiv:sha256". If you are looking for something exotic, also If you are looking for something exotic, also try
try "serpent-xts-plain64". More options may be found in cryptsetup(8) "serpent-xts-plain64". More options may be found in cryptsetup(8) and
and Linux documentation. This operation requires root privileges to Linux documentation. The \fI--filesystem\fR option can be used to
loopback mount, format the tomb (using LUKS and Ext4), then set the specify an alternative filesystem used to format the tomb,
key in its first LUKS slot. 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"
@ -101,19 +122,21 @@ internally to enumerate processes running in one or all tombs.
.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 one 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"
@ -196,22 +219,19 @@ key is buried in any image or not.
.B .B
.IP "cloak" .IP "cloak"
Hides a tomb key (\fI-k\fR) inside a \fIlong plain-text file\fR (first Cloaks a tomb key (\fI-k\fR) disguising it as a text file using a
argument) using \fIsteganography\fR: the text will change in a way cipher from \fIextras/cloak/ciphers\fR (second argument) using
that can hardly be noticed by human eye and hardly detected by data \fIcloakify\fR. This option is useful to backup tomb keys in
analysis. This option is useful to backup tomb keys in unsuspected unsuspected places; it needs \fIextras/cloak\fR installed and
places; it depends from the availability of \fIcloakify\fR and \fIpython3\fR.
consequently \fIpython2\fR. This function does not support asymmetric
encryption using the \fI-g\fR flag.
.B .B
.IP "uncloak" .IP "uncloak"
This command recovers from long plain-text files the keys that were Recovers a tomb key from a cloaked text file. Uncloak requires a text
previously hidden into them using \fIcloak\fR. Cloak requires a key file (first argument), a cipher file (second argument) and optionally
filename (\fI-k\fR) and a \fIplain-text\fR file (first argument) known an output file (third argument). If the first two parameters are
to be containing a key. If the right key password is given, the key correct then the output will be a valid tomb key file restored from
will be exhumed. If the password is not known, it is quite hard to cloak.
verify if a key is buried in a text or not.
.SH OPTIONS .SH OPTIONS
.B .B
@ -255,7 +275,8 @@ the \fIsize\fR of the new file to be created. Units are megabytes (MiB).
.B .B
.IP "-g" .IP "-g"
Tell tomb to use a asymmetric GnuPG key encryption instead of a 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). 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 .B
.IP "-r \fI<gpg_id>[,<gpg_id2>]\fR" .IP "-r \fI<gpg_id>[,<gpg_id2>]\fR"
Provide a new set of recipient(s) to encrypt a tomb key. \fIgpg_ids\fR Provide a new set of recipient(s) to encrypt a tomb key. \fIgpg_ids\fR
@ -263,22 +284,28 @@ can be one or more GPG key ID, comma separated. All GPG keys must be
trusted keys in GPG. trusted keys in GPG.
.B .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 times every time this key is a delay of \fI<itertime>\fR times every time this key is used. The actual time
used. The actual time to wait depends on the CPU speed of the to wait depends on the CPU speed (default) or the RAM size (argon2) of the
computer where the key is used. Using 5 or 10 is a sane amount for computer where the key is used. Using 5 or 10 is a sane amount for modern
modern computers, the value is multiplied by 1 million. computers, the value is multiplied by 1 million.
.B .B
.IP "--sphx-user \fI<username>\fR" .IP "--kdftype \fIargon2 | pbkdf2\fR"
Activate the SPHINX feature for password-authenticated key agreement. Adopt the \fIargon2\fR algorithm for KDF, stressing the RAM capacity rather
This option indicates the \fI<username>\fR used to retrieve the than the CPU speed of the computer decrypting the tomb. Requires the
password from a sphinx oracle key reachable via TCP/IP. \fIargon2\fR binary by P-H-C to be installed, as packaged by most distros.
Default is \fIpbkdf2\fR.
.IP "--sphx-host \fI<domain>\fR" .B
Activate the SPHINX feature for password-authenticated key agreement. .IP "--kdfmem \fI<memory>\fR"
This option indicates the \fI<domain>\fR used to retrieve the password In case of \fIargon2\fR KDF algorithm, this value specifies the size of RAM
from a sphinx oracle daemon reachable via TCP/IP. This is not the used: it consists of a number which is the elevated power of two in kilobytes.
network address of the daemon, which is configured in /etc/sphinx 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"
@ -336,7 +363,7 @@ base root of the tomb.
.IP "bind-hooks" .IP "bind-hooks"
This hook file consists of a simple text file named \fIbind-hooks\fR This hook file consists of a simple text file named \fIbind-hooks\fR
containing a two column list of paths to files or directories inside containing a two column list of paths to files or directories inside
the tomb. The files and directories will be be made directly the tomb. The files and directories will be made directly
accessible by the tomb \fIopen\fR command inside the current user's accessible by the tomb \fIopen\fR command inside the current user's
home directory. Tomb uses internally the "mount \-o bind" command to home directory. Tomb uses internally the "mount \-o bind" command to
bind locations inside the tomb to locations found in $HOME. In the bind locations inside the tomb to locations found in $HOME. In the
@ -344,10 +371,10 @@ first column are indicated paths relative to the tomb and in the
second column are indicated paths relative to $HOME contents, for second column are indicated paths relative to $HOME contents, for
example: example:
.EX .EX
mail mail mail mail
.gnupg .gnupg .gnupg .gnupg
.fmrc .fetchmailrc .fmrc .fetchmailrc
.mozilla .mozilla .mozilla .mozilla
.EE .EE
.B .B
@ -363,9 +390,9 @@ 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
@ -378,20 +405,20 @@ whole system's security: just add such a line to \fI/etc/sudoers\fR:
To avoid that tomb execution is logged by \fIsyslog\fR also add: To avoid that tomb execution is logged by \fIsyslog\fR also add:
.EX .EX
Cmnd_Alias TOMB = /usr/local/bin/tomb Cmnd_Alias TOMB = /usr/local/bin/tomb
Defaults!TOMB !syslog Defaults!TOMB !syslog
.EE .EE
.SH PASSWORD INPUT .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
@ -430,20 +457,10 @@ prefix all invocations of tomb with a blank space, including two lines
in ".zshrc": in ".zshrc":
.EX .EX
export HISTIGNORESPACE=1 export HISTIGNORESPACE=1
alias tomb=' tomb' alias tomb=' tomb'
.EE .EE
.SH PASSWORD INPUT
Tomb uses the external program "pinentry" to let users type the key password into a terminal or a graphical window. This program works in conjunction with "gpg-agent", a daemon running in background to facilitate secret key management with gpg. It is recommended one runs "gpg-agent" launching it from the X session initialization ("~/.xsession" or "~/.xinitrc" files) with this command:
.EX
eval $(gpg-agent --daemon --write-env-file "${HOME}/.gpg-agent-info")
.EE
In the future it may become mandatory to run gpg-agent when using tomb.
.SH SHARE A TOMB .SH SHARE A TOMB
A tomb key can be encrypted with more than one recipient. Therefore, a A tomb key can be encrypted with more than one recipient. Therefore, a
tomb can be shared between different users. The recipients are given tomb can be shared between different users. The recipients are given
@ -456,24 +473,6 @@ its copies are destroyed. The \fI-r\fR option can be used in the tomb
commands: \fIopen\fR, \fIforge\fR \fIsetkey\fR, \fIpasswd\fR, commands: \fIopen\fR, \fIforge\fR \fIsetkey\fR, \fIpasswd\fR,
\fIbury\fR, \fIexhume\fR and \fIresize\fR. \fIbury\fR, \fIexhume\fR and \fIresize\fR.
.SH SPHINX (PAKE)
Using the package libsphinx
.UR https://github.com/stef/libsphinx
.UE
and its python client/daemon implementation pwdsphinx
.UR https://github.com/stef/pwdsphinx
.UE
is possible to store and retrieve safely the password that locks the
tomb. Using this feature will make it impossible to retrieve the
password without the oracle sphinx server running and reachable. Each
key entry needs a username and a domain specified on creation and
a password that locks it.
SPHINX makes it impossible to maliciously retrieve the password
locking the tomb key without an attacker accessing both the
server, the sphinx password and the tomb key file.
.SH EXAMPLES .SH EXAMPLES
.IP \(bu .IP \(bu
@ -526,11 +525,11 @@ keeping all its profile data inside it:
cat <<EOF > /media/FOX.tomb/exec-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/exec-hooks chmod +x /media/FOX.tomb/exec-hooks
mkdir /media/FOX.tomb/firefox-pro mkdir /media/FOX.tomb/firefox-pro
.EE .EE
.IP \(bu .IP \(bu
@ -541,13 +540,13 @@ 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/exec-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
if [ "$?" = "0" ]; then if [ "$?" = "0" ]; then
shotwell -d "$2"/Pictures/.shotwell & shotwell -d "$2"/Pictures/.shotwell &
fi fi
fi fi
EOF EOF
chmod +x /media/Pictures.tomb/exec-hooks chmod +x /media/Pictures.tomb/exec-hooks
@ -563,7 +562,7 @@ channel on \fIhttps://irc.dyne.org\fR.
.SH COPYING .SH COPYING
This manual is Copyright (c) 2011-2019 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.

Binary file not shown.

View File

@ -1647,7 +1647,7 @@ change_tomb_key() {
_sudo cryptsetup luksClose "${mapper}" || _failure "Unexpected error in luksClose." _sudo cryptsetup luksClose "${mapper}" || _failure "Unexpected error in luksClose."
_success "Succesfully changed key for tomb: ::1 tomb file::" $TOMBFILE _success "Successfully changed key for tomb: ::1 tomb file::" $TOMBFILE
_message "The new key is: ::1 new key::" $TOMBKEYFILE _message "The new key is: ::1 new key::" $TOMBKEYFILE
return 0 return 0

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

@ -18,15 +18,18 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this source code. If not, see <http://www.gnu.org/licenses/>. # along with this source code. If not, see <http://www.gnu.org/licenses/>.
ver="0.8" # {{{ SETTINGS
TOMBPATH="/usr/local/bin/tomb" # Set this to your tomb executable's path ver="0.9.1"
KDFPATH="/usr/local/bin/" # Set this to the path of your KDF binaries (if you're using them) KDFPATH="/usr/libexec/tomb" # Path of your KDF binaries (if you're using them).
SWAPOFF="false" # Set to "true" to swapoff, or "false" to use -f (force) flag.
# The ones below should not need changing # The ones below should not need changing
TOMBPATH="$(which tomb)" # Tomb executable's path
HEXENC="$KDFPATH/tomb-kdb-hexencode" HEXENC="$KDFPATH/tomb-kdb-hexencode"
GENSALT="$KDFPATH/tomb-kdb-pbkdf2-gensalt" GENSALT="$KDFPATH/tomb-kdb-pbkdf2-gensalt"
GETITER="$KDFPATH/tomb-kdb-pbkdf2-getiter" GETITER="$KDFPATH/tomb-kdb-pbkdf2-getiter"
PBKDF="$KDFPATH/tomb-kdb-pbkdf2" PBKDF="$KDFPATH/tomb-kdb-pbkdf2"
_DD=/bin/dd _DD="$(which dd)"
# }}}
# {{{ monmort icon # {{{ monmort icon
MONMORT="/tmp/monmort.png" MONMORT="/tmp/monmort.png"
@ -41,337 +44,165 @@ b2RpZnkAMjAxMS0wMS0xMlQwOTozNDoyNCswMTowMNKiZVMAAAAASUVORK5CYII="
echo -e "$ICONB64" | base64 --decode > $MONMORT echo -e "$ICONB64" | base64 --decode > $MONMORT
# }}} # }}}
# {{{ sudo functions
function _sudo {
sudoassword=$(ask_password "Insert sudo password for user $USER")
echo -e "$sudoassword\n" | sudo -S -v
_sudowrong
}
function _sudowrong {
[[ $? = 0 ]] || {
sudoassword=$(ask_password "Wrong password. Insert sudo password for user $USER")
echo -e "$sudoassword\n" | sudo -S -v
_sudowrong
}
}
# }}}
# {{{ Zenity dialogs # {{{ Zenity dialogs
Icon="--window-icon="$MONMORT""
function _zenques { function _zenques {
zenity \ zenity \
--window-icon="$MONMORT" \ $Icon \
--question \ --question \
--text="$1" --text="$1"
} }
function _fsel { function _fsel {
zenity \ zenity \
--window-icon="$MONMORT" \ $Icon \
--file-selection \ --file-selection \
--title="$1" --title="$1"
} }
function _fsave { function _fsave {
zenity \ zenity \
--window-icon="$MONMORT" \ $Icon \
--file-selection \ --file-selection \
--save \ --save \
--title="$1" \ --title="$1" \
--filename="$2" --filename="$2"
} }
function _zenwarn { function _zenwarn {
zenity \ zenity \
--window-icon="$MONMORT" \ $Icon \
--warning \ --warning \
--title="$1" \ --title="Warning" \
--text="$2" --text="$1"
} }
function _info { function _info {
which notify-send > /dev/null which notify-send > /dev/null
if [[ $? == "0" ]]; then if [[ $? == "0" ]]; then
_zenotif $* _zenotif $*
else else
_zeninfo $* _zeninfo $*
fi fi
} }
function _zenotif { function _zenotif {
zenity \ zenity \
--window-icon="$MONMORT" \ $Icon \
--notification \ --notification \
--title="$1" \ --title="$1" \
--text="$2" --text="$2"
} }
function _zeninfo { function _zeninfo {
zenity \ zenity \
--window-icon="$MONMORT" \ $Icon \
--info \ --info \
--title="$1" \ --title="$1" \
--text="$2" --text="$2"
} }
function _zenerr { function _zenerr {
zenity \ zenity \
--window-icon="$MONMORT" \ $Icon \
--error \ --error \
--title="$1" \ --title="$1" \
--text="$2" --text="$2"
} }
function _zenprog { function _zenprog {
zenity \ zenity \
--window-icon="$MONMORT" \ $Icon \
--progress \ --progress \
--auto-close \ --auto-close \
--pulsate \ --pulsate \
--title="$1" \ --title="$1" \
--text="$2" --text="$2"
} }
function _zenprognc { function _zenprognc {
zenity \ zenity \
--window-icon="$MONMORT" \ $Icon \
--progress \ --progress \
--auto-close \ --auto-close \
--no-cancel \ --no-cancel \
--pulsate \ --pulsate \
--title="$1" \ --title="$1" \
--text="$2" --text="$2"
} }
function _zenentry { function _zenentry {
zenity \ zenity \
--window-icon="$MONMORT" \ $Icon \
--entry \ --entry \
--title="$1" \ --title="$1" \
--text="$2" \ --text="$2" \
--entry-text="$3" --entry-text="$3"
}
# }}}
# {{{ Some pinentry code shamelessly stolen from tomb
# Ask user for a password
# Wraps around the pinentry command, from the GnuPG project, as it
# provides better security and conveniently use the right toolkit.
ask_password() {
local description="$1"
local title="${2:-Enter tomb password.}"
local output
local password
local gtkrc
local theme
# Distributions have broken wrappers for pinentry: they do
# implement fallback, but they disrupt the output somehow. We are
# better off relying on less intermediaries, so we implement our
# own fallback mechanisms. Pinentry supported: curses, gtk-2, qt4
# and x11.
# make sure LANG is set, default to C
LANG=${LANG:-C}
_verbose "asking password with tty=$TTY lc-ctype=$LANG"
if [[ "$DISPLAY" = "" ]]; then
if _is_found "pinentry-curses"; then
_verbose "using pinentry-curses"
output=`cat <<EOF | pinentry-curses
OPTION ttyname=$TTY
OPTION lc-ctype=$LANG
SETTITLE $title
SETDESC $description
SETPROMPT Password:
GETPIN
EOF`
else
_failure "Cannot find pinentry-curses and no DISPLAY detected."
fi
else # a DISPLAY is found to be active
# customized gtk2 dialog with a skull (if extras are installed)
if _is_found "pinentry-gtk-2"; then
_verbose "using pinentry-gtk2"
gtkrc=""
theme=/share/themes/tomb/gtk-2.0-key/gtkrc
for i in /usr/local /usr; do
[[ -r $i/$theme ]] && {
gtkrc="$i/$theme"
break
}
done
[[ "$gtkrc" = "" ]] || {
gtkrc_old="$GTK2_RC_FILES"
export GTK2_RC_FILES="$gtkrc"
}
output=`cat <<EOF | pinentry-gtk-2
OPTION ttyname=$TTY
OPTION lc-ctype=$LANG
SETTITLE $title
SETDESC $description
SETPROMPT Password:
GETPIN
EOF`
[[ "$gtkrc" = "" ]] || export GTK2_RC_FILES="$gtkrc_old"
# TODO QT4 customization of dialog
elif _is_found "pinentry-qt4"; then
_verbose "using pinentry-qt4"
output=`cat <<EOF | pinentry-qt4
OPTION ttyname=$TTY
OPTION lc-ctype=$LANG
SETTITLE $title
SETDESC $description
SETPROMPT Password:
GETPIN
EOF`
# TODO X11 customization of dialog
elif _is_found "pinentry-x11"; then
_verbose "using pinentry-x11"
output=`cat <<EOF | pinentry-x11
OPTION ttyname=$TTY
OPTION lc-ctype=$LANG
SETTITLE $title
SETDESC $description
SETPROMPT Password:
GETPIN
EOF`
else
if _is_found "pinentry-curses"; then
_verbose "using pinentry-curses"
_warning "Detected DISPLAY, but only pinentry-curses is found."
output=`cat <<EOF | pinentry-curses
OPTION ttyname=$TTY
OPTION lc-ctype=$LANG
SETTITLE $title
SETDESC $description
SETPROMPT Password:
GETPIN
EOF`
else
_failure "Cannot find any pinentry: impossible to ask for password."
fi
fi
fi # end of DISPLAY block
# parse the pinentry output
for i in ${(f)output}; do
[[ "$i" =~ "^ERR.*" ]] && {
_warning "Pinentry error: ::1 error::" ${i[(w)3]}
print "canceled"
return 1 }
# here the password is found
[[ "$i" =~ "^D .*" ]] && password="${i##D }"
done
[[ "$password" = "" ]] && {
_warning "Empty password"
print "empty"
return 1 }
print "$password"
return 0
}
_is_found() {
# returns 0 if binary is found in path
[[ "$1" = "" ]] && return 1
command -v "$1" 1>/dev/null 2>/dev/null
return $?
}
function _warning no() {
option_is_set -q || _msg warning $@
return 1
}
function _verbose xxx() {
option_is_set -D && _msg verbose $@
return 0
}
function _failure die() {
typeset -i exitcode=${exitv:-1}
option_is_set -q || _msg failure $@
# be sure we forget the secrets we were told
exit $exitcode
} }
# }}} # }}}
# {{{ _clean - Clean function, removes sensitive stuff from memory # {{{ _clean - Clean function, removes sensitive stuff from memory
function _clean { function _clean {
unset $? unset $?
local rr="$RANDOM" local rr="$RANDOM"
while [[ ${#rr} -lt 500 ]]; do while [[ ${#rr} -lt 500 ]]; do
rr+="$RANDOM" rr+="$RANDOM"
done done
cmnd="$rr"; unset cmnd cmnd="$rr"; unset cmnd
tombname="$rr"; unset tombname tombname="$rr"; unset tombname
tombsize="$rr"; unset tombsize tombsize="$rr"; unset tombsize
keyfile="$rr"; unset keyfile keyfile="$rr"; unset keyfile
sudoassword="$rr"; unset sudoassword tombtmp="/tmp/tombtmp"
tombtmp="/tmp/tombtmp" if [ -f $tombtmp ]; then
if [ -f $tombtmp ]; then dd if=/dev/urandom of=$tombtmp bs=800 count=1
dd if=/dev/urandom of=$tombtmp bs=800 count=1 rm -f $tombtmp
rm -f $tombtmp fi
fi tombtmp="$rr"; unset tombtmp
tombtmp="$rr"; unset tombtmp newkey="$rr"; unset newkey
newkey="$rr"; unset newkey jpegfile="$rr"; unset jpegfile
jpegfile="$rr"; unset jpegfile
} }
# }}} # }}}
# {{{ _main - Main window # {{{ _main - Main window
function _main { function _main {
_clean _clean
cmnd=`zenity \ cmnd=`zenity \
--window-icon="$MONMORT" \ $Icon \
--title="gtomb" \ --title="gtomb" \
--width=640 \ --width=400 \
--height=420 \ --height=445 \
--list \ --list \
--hide-header \ --hide-header \
--text="gtomb v$ver\nThe GUI wrapper for Tomb, the crypto undertaker." \ --text="gtomb v$ver\nThe GUI wrapper for Tomb, the crypto undertaker." \
--separator=" & " \ --separator=" & " \
--column=Function \ --column=Function \
--column=Description \ --column=Description \
"dig" "Dig a new tomb of chosen size" \ "dig" "Dig a new tomb of chosen size" \
"forge" "Forge a new key used to lock tombs" \ "forge" "Forge a new key used to lock tombs" \
"lock" "Lock a non-locked tomb using an existing key" \ "lock" "Lock a non-locked tomb using an existing key" \
"open" "Open an existing tomb" \ "open" "Open an existing tomb" \
"index" "Index the contents of all tombs." \ "index" "Index the contents of all tombs." \
"search" "Search the content of indexed tombs." \ "search" "Search the content of indexed tombs." \
"list" "List all open tombs and information on them" \ "list" "List all open tombs and information on them" \
"close" "Close a specific tomb (or all)" \ "close" "Close a specific tomb (or all)" \
"slam" "Slam a tomb (or all) killing all programs using it" \ "slam" "Slam a tomb (or all) killing all programs using it" \
"resize" "Resize a tomb to a new size (can only grow)" \ "resize" "Resize a tomb to a new size (can only grow)" \
"passwd" "Change the passphrase of a key" \ "passwd" "Change the passphrase of a key" \
"setkey" "Change the key of an existing tomb" \ "setkey" "Change the key of an existing tomb" \
"engrave" "Generates a QR code of a key to be saved on paper" \ "engrave" "Generates a QR code of a key to be saved on paper" \
"bury" "Hide a key inside a JPEG image" \ "bury" "Hide a key inside a JPEG image" \
"exhume" "Extract a key from a JPEG image"` "exhume" "Extract a key from a JPEG image"`
eval "_$cmnd" if [[ "$?" = 1 && $SWAPOFF = "true" ]]; then
zenity --password --title="sudo swapon -a" | sudo swapon -a
unset $?
fi
eval "_$cmnd"
} }
# }}} # }}}
# {{{ dig - Dig a new tomb # {{{ dig - Dig a new tomb
function _dig { function _dig {
tombname=`_fsave "Choose where to dig your tomb" "secret.tomb"` tombname=`_fsave "Choose where to dig your tomb" "secret.tomb"`
res=$? res=$?
if [[ -f "$tombname" ]]; then if [[ -f "$tombname" ]]; then
_zenerr "Error" "This tomb already exists. I am not digging here." _zenerr "Error" "This tomb already exists. I am not digging here."
exec _main exec _main
elif [[ -z "$tombname" ]]; then elif [[ -z "$tombname" ]]; then
_info "gtomb" "Cancelled" _info "gtomb" "Cancelled"
exec _main exec _main
fi fi
[[ $res = 0 ]] || exec _main [[ $res = 0 ]] || exec _main
@ -387,7 +218,7 @@ function _dig {
exec _main exec _main
fi fi
[[ $res = 0 ]] || { _zenwarn "Warning" "Tomb digging canceled." ; exec _main } [[ $res = 0 ]] || { _zenwarn "Tomb digging cancelled." ; exec _main }
"$TOMBPATH" dig -s "$tombsize" "$tombname" | \ "$TOMBPATH" dig -s "$tombsize" "$tombname" | \
_zenprog "Digging new tomb" "Please wait while your tomb is being dug..." & _zenprog "Digging new tomb" "Please wait while your tomb is being dug..." &
@ -401,10 +232,12 @@ function _dig {
[[ -n "$PID_DD" && -z "$PID_ZEN" ]] && { [[ -n "$PID_DD" && -z "$PID_ZEN" ]] && {
kill -9 $PID_DD kill -9 $PID_DD
_zenwarn "Warning" "Tomb digging cancelled." _zenwarn "Tomb digging cancelled."
rm -f "$tombname" rm -f "$tombname"
exec _main exec _main
} }
wait
_info "Success" "Your tomb has been dug in $tombname" _info "Success" "Your tomb has been dug in $tombname"
exec _main exec _main
@ -413,42 +246,42 @@ function _dig {
# {{{ forge - Forge a new key # {{{ forge - Forge a new key
function _forge { function _forge {
keyfile=`_fsave "Choose where to forge your key" "secret.tomb.key"` keyfile=`_fsave "Choose where to forge your key" "secret.tomb.key"`
res=$? res=$?
if [[ -f $keyfile ]]; then if [[ -f $keyfile ]]; then
_zenerr "Error" "This key already exists. I am not overwriting." _zenerr "Error" "This key already exists. I am not overwriting."
exec _main exec _main
elif [[ -z $keyfile ]]; then elif [[ -z $keyfile ]]; then
_info "gtomb" "Canceled" _info "gtomb" "Cancelled"
exec _main exec _main
fi fi
kdf="" kdf=""
kdfiter="" kdfiter=""
if [[ -x $HEXENC ]] && [[ -x $GENSALT ]] && [[ -x $GETITER ]] && [[ -x $PBKDF ]]; then if [[ -x $GENSALT ]] && [[ -x $GETITER ]] && [[ -x $PBKDF ]]; then
_zenques "Do you want to use KDF? (Generates passwords armored against dictionary attacks)" _zenques "Do you want to use KDF? (Generates passwords armored against dictionary attacks)"
[[ $? == "0" ]] && { if [[ $? == "0" ]]; then
kdf="--kdf" kdf="--kdf"
kdfiter=`_zenentry "Iterations" "Enter the delay (itertime) in seconds for each time \n\ kdfiter=`_zenentry "Iterations" "Enter the delay (itertime) in seconds for each time \n\
this key is used:" "2"` this key is used:" "2"`
re='^[0-9]+$' re='^[0-9]+$'
if ! [[ $kdfiter =~ $re ]]; then if ! [[ $kdfiter =~ $re ]]; then
_zenerr "Error" "Please choose a valid number." _zenerr "Error" "Please choose a valid number."
exec _main exec _main
elif [[ -z $kdfiter ]]; then elif [[ -z $kdfiter ]]; then
_info "gtomb" "Canceled" _info "gtomb" "Cancelled"
exec _main exec _main
fi fi
} fi
else else
_zenotif "gtomb" "KDF binaries not found." _zenotif "gtomb" "KDF binaries not found."
fi fi
[[ $? = 0 ]] || exec _main [[ $? = 0 ]] || exec _main
"$TOMBPATH" forge "$keyfile" "$kdf" "$kdfiter" | \ "$TOMBPATH" forge "$keyfile" "$kdf" "$kdfiter" "$FLAG" | \
_zenprog "Forging key" "Please wait while your key is being forged...\n\ _zenprog "Forging key" "Please wait while your key is being forged...\n\
You can move your mouse around and use your computer to speed up the process." & You can move your mouse around and use your computer to speed up the process." &
@ -460,10 +293,12 @@ You can move your mouse around and use your computer to speed up the process." &
done done
[[ -n "$PID_DD" && -z "$PID_ZEN" ]] && { [[ -n "$PID_DD" && -z "$PID_ZEN" ]] && {
kill -9 $PID_DD kill -9 $PID_DD
_zenwarn "Warning" "Forging cancelled." _zenwarn "Forging cancelled."
rm -f $keyfile rm -f $keyfile
exec _main exec _main
} }
wait
_info "Success" "Your key is now forged in $keyfile" _info "Success" "Your key is now forged in $keyfile"
exec _main exec _main
@ -472,17 +307,19 @@ You can move your mouse around and use your computer to speed up the process." &
# {{{ lock - Lock a non-locked tomb # {{{ lock - Lock a non-locked tomb
function _lock { function _lock {
tombname=`_fsel "Select a tomb to lock"` tombname=`_fsel "Select a tomb to lock"`
[[ -n $tombname ]] || { _zenotif "gtomb" "Cancelled" ; exec _main } [[ -n $tombname ]] || { _zenotif "gtomb" "Cancelled" ; exec _main }
[[ $? = 0 ]] || exec _main [[ $? = 0 ]] || exec _main
keyfile=`_fsel "Choose the key for your tomb"` keyfile=`_fsel "Choose the key for your tomb"`
[[ -n $keyfile ]] || { _zenotif "gtomb" "Cancelled" ; exec _main } [[ -n $keyfile ]] || { _zenotif "gtomb" "Cancelled" ; exec _main }
[[ $? = 0 ]] || exec _main [[ $? = 0 ]] || exec _main
_sudo
"$TOMBPATH" lock "$tombname" -k "$keyfile" | \ "$TOMBPATH" lock "$tombname" -k "$keyfile" | \
_zenprognc "Locking your tomb" "Please wait while your tomb is being locked..." _zenprognc "Locking your tomb" "Please wait while your tomb is being locked..."
wait
_info "Success" "Your tomb is now locked." _info "Success" "Your tomb is now locked."
exec _main exec _main
} }
@ -490,14 +327,16 @@ function _lock {
# {{{ open - Open an existing tomb # {{{ open - Open an existing tomb
function _open { function _open {
tombname=`_fsel "Choose a tomb to open"` tombname=`_fsel "Choose a tomb to open"`
[[ $? = 0 ]] || exec _main [[ $? = 0 ]] || exec _main
keyfile=`_fsel "Choose the key for your tomb"` keyfile=`_fsel "Choose the key for your tomb"`
[[ $? = 0 ]] || exec _main [[ $? = 0 ]] || exec _main
_sudo "$TOMBPATH" open "$tombname" -k "$keyfile" "$FLAG"
"$TOMBPATH" open "$tombname" -k "$keyfile"
wait
_info "Success" "Your tomb is now open." _info "Success" "Your tomb is now open."
exec _main exec _main
} }
@ -505,33 +344,33 @@ function _open {
# {{{ list - list all open tombs, along with their mountpoint # {{{ list - list all open tombs, along with their mountpoint
function _list { function _list {
tombtmp="/tmp/tombtmp" tombtmp="/tmp/tombtmp"
"$TOMBPATH" list --get-mountpoint > $tombtmp "$TOMBPATH" list --get-mountpoint > $tombtmp
tombname=`cat $tombtmp | \ tombname=`cat $tombtmp | \
sed 's/.*\/\([^\/]*\)$/\1\n &/' | \ sed 's/.*\/\([^\/]*\)$/\1\n &/' | \
zenity \ zenity \
--title="Currently open tombs" \ --title="Currently open tombs" \
--window-icon="$MONMORT" \ $Icon \
--width=640 --height=380 --list \ --width=400 --height=380 --list \
--separator=" & " \ --separator=" & " \
--text="Here are your open tombs" \ --text="Here are your open tombs" \
--column=Tomb \ --column="Tomb" \
--column=Path ` --column="Path" `
tombname=`echo "$tombname" | cut -c1-16` tombname=`echo "$tombname" | cut -c1-16`
[[ $? = 0 ]] || exec _main [[ $? = 0 ]] || exec _main
listchoice=`zenity \ listchoice=`zenity \
--title="Choose action" \ --title="Choose action" \
--window-icon="$MONMORT" \ $Icon \
--width=640 --height=400 --list \ --width=400 --height=380 --list \
--separator=" & " \ --separator=" & " \
--text="What do you want to do with this tomb?" \ --text="What do you want to do with this tomb?" \
--column=Command \ --column="Command" \
--column=Description \ --column="Description" \
"disindex" "Disable indexing of this tomb." \ "disindex" "Disable indexing of this tomb." \
"enindex" "Enable indexing of this tomb." \ "enindex" "Enable indexing of this tomb." \
"close" "Close the selected tomb." \ "close" "Close the selected tomb." \
"slam" "Slam the selected tomb." \ "slam" "Slam the selected tomb." \
"binds" "Edit current bind-hooks." \ "binds" "Edit current bind-hooks." \
@ -541,13 +380,11 @@ function _list {
case $listchoice in case $listchoice in
close) close)
_sudo
"$TOMBPATH" close "$tombname" "$TOMBPATH" close "$tombname"
_zeninfo "Success" "Tomb closed successfully!" _zeninfo "Success" "Tomb closed successfully!"
exec _main exec _main
;; ;;
slam) slam)
_sudo
"$TOMBPATH" slam "$tombname" "$TOMBPATH" slam "$tombname"
_info "Success" "$tombname slammed successfully!" _info "Success" "$tombname slammed successfully!"
exec _main exec _main
@ -595,22 +432,21 @@ function _list {
# {{{ close - Close open tomb(s) # {{{ close - Close open tomb(s)
function _close { function _close {
tombtmp="/tmp/tombtmp" tombtmp="/tmp/tombtmp"
"$TOMBPATH" list --get-mountpoint > $tombtmp "$TOMBPATH" list --get-mountpoint > $tombtmp
echo "/all" >> $tombtmp echo "/all" >> $tombtmp
tombname=`cat $tombtmp | \ tombname=`cat $tombtmp | \
sed 's/.*\/\([^\/]*\)$/\1\n &/' | \ sed 's/.*\/\([^\/]*\)$/\1\n &/' | \
zenity \ zenity \
--title="Choose a tomb to close" \ --title="Choose a tomb to close" \
--window-icon="$MONMORT" \ $Icon \
--width=640 --height=380 --list \ --width=640 --height=380 --list \
--separator=" & " \ --separator=" & " \
--column=Tomb \ --column=Tomb \
--column=Path ` --column=Path `
[[ $? = 0 ]] || exec _main [[ $? = 0 ]] || exec _main
_sudo
tombname=`echo "$tombname" | cut -c1-16` tombname=`echo "$tombname" | cut -c1-16`
"$TOMBPATH" close "$tombname" "$TOMBPATH" close "$tombname"
_info "Success" "Closed successfully!" _info "Success" "Closed successfully!"
@ -620,22 +456,21 @@ function _close {
# {{{ slam - Slam open tombs # {{{ slam - Slam open tombs
function _slam { function _slam {
tombtmp="/tmp/tombtmp" tombtmp="/tmp/tombtmp"
"$TOMBPATH" list --get-mountpoint > $tombtmp "$TOMBPATH" list --get-mountpoint > $tombtmp
echo "/all" >> $tombtmp echo "/all" >> $tombtmp
tombname=`cat $tombtmp | \ tombname=`cat $tombtmp | \
sed 's/.*\/\([^\/]*\)$/\1\n &/' | \ sed 's/.*\/\([^\/]*\)$/\1\n &/' | \
zenity \ zenity \
--title="Choose a tomb to slam" \ --title="Choose a tomb to slam" \
--window-icon="$MONMORT" \ $Icon \
--width=640 --height=380 --list \ --width=640 --height=380 --list \
--separator=" & " \ --separator=" & " \
--column=Tomb \ --column=Tomb \
--column=Path ` --column=Path `
[[ $? = 0 ]] || exec _main [[ $? = 0 ]] || exec _main
_sudo
tombname=`echo "$tombname" | cut -c1-16` tombname=`echo "$tombname" | cut -c1-16`
"$TOMBPATH" slam "$tombname" "$TOMBPATH" slam "$tombname"
_info "Success" "Slammed successfully!" _info "Success" "Slammed successfully!"
@ -645,11 +480,11 @@ function _slam {
# {{{ resize - Resize an existing *closed* tomb # {{{ resize - Resize an existing *closed* tomb
function _resize { function _resize {
tombname=`_fsel "Choose a tomb to resize"` tombname=`_fsel "Choose a tomb to resize"`
res=$? res=$?
_zenques "Is your tomb closed?" _zenques "Is your tomb closed?"
[[ $? = 0 ]] || { _zenwarn "gtomb" "Please close the tomb before resizing." ; exec _main } [[ $? = 0 ]] || { _zenwarn "Please close the tomb before resizing." ; exec _main }
[[ $res = 0 ]] || exec _main [[ $res = 0 ]] || exec _main
@ -670,7 +505,6 @@ function _resize {
keyfile=`_fsel "Choose according keyfile"` keyfile=`_fsel "Choose according keyfile"`
[[ $? = 0 ]] || exec _main [[ $? = 0 ]] || exec _main
_sudo
"$TOMBPATH" resize "$tombname" -s "$tombsize" -k "$keyfile" | \ "$TOMBPATH" resize "$tombname" -s "$tombsize" -k "$keyfile" | \
_zenprognc "Resizing tomb." "Please wait while your tomb is being resized..." _zenprognc "Resizing tomb." "Please wait while your tomb is being resized..."
_info "Success" "Tomb resized successfully!" _info "Success" "Tomb resized successfully!"
@ -680,10 +514,10 @@ function _resize {
# {{{ passwd - Change existing key's passphrase # {{{ passwd - Change existing key's passphrase
function _passwd { function _passwd {
keyfile=`_fsel "Choose a keyfile"` keyfile=`_fsel "Choose a keyfile"`
[[ $? = 0 ]] || exec _main [[ $? = 0 ]] || exec _main
"$TOMBPATH" passwd -k "$keyfile" | \ "$TOMBPATH" passwd -k "$keyfile" "$FLAG" | \
_zenprognc "Changing passphrase" "Please wait while your key's passphrase is being changed..." _zenprognc "Changing passphrase" "Please wait while your key's passphrase is being changed..."
_info "Success" "$keyfile passphrase changed successfully!" _info "Success" "$keyfile passphrase changed successfully!"
@ -693,7 +527,7 @@ function _passwd {
# {{{ setkey - Change a tomb's keyfile # {{{ setkey - Change a tomb's keyfile
function _setkey { function _setkey {
tombname=`_fsel "Choose a tomb to change its keyfile"` tombname=`_fsel "Choose a tomb to change its keyfile"`
[[ $? = 0 ]] || exec _main [[ $? = 0 ]] || exec _main
keyfile=`_fsel "Chosse your tomb's old keyfile"` keyfile=`_fsel "Chosse your tomb's old keyfile"`
@ -702,19 +536,18 @@ function _setkey {
newkey=`_fsel "Choose your tomb's new keyfile"` newkey=`_fsel "Choose your tomb's new keyfile"`
[[ $? = 0 ]] || exec _main [[ $? = 0 ]] || exec _main
_sudo "$TOMBPATH" setkey -k "$newkey" "$keyfile" "$tombname" "$FLAG" | \
"$TOMBPATH" setkey -k "$newkey" "$keyfile" "$tombname" | \
_zenprognc "Changing key" "Please wait while your tomb's key is being changed..." _zenprognc "Changing key" "Please wait while your tomb's key is being changed..."
_info "Success" "$tombname keyfile successfully changed! Now using $newkey" _info "Success" "$tombname keyfile successfully changed! Now using $newkey"
exec _main exec _main
} }
# }}} # }}}
# {{{ engrave - generate QR code of a key # {{{ engrave - generate QR code of a key
function _engrave { function _engrave {
which qrencode || _zenwarn "Warning" "qrencode is not installed. Install it and try again" which qrencode || _zenwarn "qrencode is not installed. Install it and try again."
keyfile=`_fsel "Choose a keyfile to engrave"` keyfile=`_fsel "Choose a keyfile to engrave"`
[[ $? = 0 ]] || exec _main [[ $? = 0 ]] || exec _main
jpegfile=`_fsave "Choose where to save your keyfile (PNG format)"` jpegfile=`_fsave "Choose where to save your keyfile (PNG format)"`
@ -730,8 +563,8 @@ function _engrave {
# {{{ bury - hide a keyfile in a JPEG image # {{{ bury - hide a keyfile in a JPEG image
function _bury { function _bury {
which steghide || _zenwarn "Warning" "steghide is not installed. Install it and try again" which steghide || _zenwarn "steghide is not installed. Install it and try again."
keyfile=`_fsel "Choose keyfile"` keyfile=`_fsel "Choose keyfile"`
[[ $? = 0 ]] || exec _main [[ $? = 0 ]] || exec _main
jpegfile=`_fsel "Choose JPEG file"` jpegfile=`_fsel "Choose JPEG file"`
@ -745,8 +578,8 @@ function _bury {
# {{{ exhume - extract keyfile from JPEG # {{{ exhume - extract keyfile from JPEG
function _exhume { function _exhume {
which steghide || _zenwarn "Warning" "steghide is not installed. Install it and try again" which steghide || _zenwarn "steghide is not installed. Install it and try again."
jpegfile=`_fsel "Choose JPEG file"` jpegfile=`_fsel "Choose JPEG file"`
[[ $? = 0 ]] || exec _main [[ $? = 0 ]] || exec _main
keyfile=`_fsave "Choose where to extract your key"` keyfile=`_fsave "Choose where to extract your key"`
@ -760,18 +593,18 @@ function _exhume {
# {{{ index - index the contents of open tombs # {{{ index - index the contents of open tombs
function _index { function _index {
which locate || _zenwarn "Warning" "mlocate is not installed. Install it and try again" which locate || _zenwarn "mlocate is not installed. Install it and try again."
"$TOMBPATH" index | _zenprognc "Indexing" "Please wait while the open tombs are being indexed..." "$TOMBPATH" index | _zenprognc "Indexing" "Please wait while the open tombs are being indexed..."
_info "Success" "Tombs indexed!" _info "Success" "Tombs indexed!"
exec _main exec _main
} }
# }}} # }}}
# {{{ search - searches the contents of indexed tombs # {{{ search - searches the contents of indexed tombs
function _search { function _search {
strings="" strings=""
_searchstring _searchstring
exec _main exec _main
} }
function _searchstring { function _searchstring {
@ -799,7 +632,16 @@ function _searchstring {
function _ { _clean } # I like cleaning :) function _ { _clean } # I like cleaning :)
[[ -x $TOMBPATH ]] || { [[ -x $TOMBPATH ]] || {
_zenwarn "Warning" "Tomb binary is not executable or doesn't exist in the current path. Install it or edit the script to point to the correct path." _zenwarn "Tomb binary is not executable or doesn't exist in the current path. Install it or edit the script to point to the correct path."
exit 1 } exit 1 }
if [[ $SWAPOFF = "true" ]]; then
FLAG=""
zenity --password --title="sudo swapoff -a" | sudo swapoff -a
unset $?
else
FLAG="-f"
fi
_main _main

View File

@ -1,12 +0,0 @@
#!/bin/sh
set -ex
git clone https://github.com/stef/libsphinx
cd libsphinx
git submodule update --init --recursive --remote
cd src
sed -i 's|/usr/local|/usr|' makefile
make
sudo make install
ldconfig
pip3 install pwdsphinx
sudo mkdir -p /etc/sphinx

View File

@ -2,10 +2,10 @@
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: test:
@echo "Running Tomb-kdb tests" @echo "Running Tomb-kdb tests"

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

@ -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"

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,182 @@
#
# bats-file - Common filesystem assertions and helpers 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/>.
#
#
# temp.bash
# ---------
#
# Functions for handling temporary directories.
#
# Create a temporary directory for the current test in `BATS_TMPDIR`,
# and display its path on the standard output.
#
# The directory name is derived from the test's filename and number, and
# a random string for uniqueness.
#
# <test-filename>-<test-number>-<random-string>
#
# When `--prefix <prefix>' is specified, `<prefix>' is prepended to the
# directory name.
#
# <prefix><test-filename>-<test-number>-<random-string>
#
# Must be called from `setup', `@test' or `teardown'.
#
# Example:
#
# setup() {
# TEST_TEMP_DIR="$(temp_make --prefix 'myapp-')"
# }
#
# teardown() {
# temp_del "$TEST_TEMP_DIR"
# }
#
# Globals:
# BATS_TEST_NAME
# BATS_TEST_FILENAME
# BATS_TEST_NUMBER
# BATS_TMPDIR
# Arguments:
# none
# Options:
# -p, --prefix <prefix> - prefix the directory name with `<prefix>'
# Returns:
# 0 - on success
# 1 - otherwise
# Outputs:
# STDOUT - path of temporary directory
# STDERR - error messages
temp_make() {
# Check caller.
if ! ( batslib_is_caller --indirect 'setup' \
|| batslib_is_caller --indirect 'setup_file' \
|| batslib_is_caller --indirect "$BATS_TEST_NAME" \
|| batslib_is_caller --indirect 'teardown' \
|| batslib_is_caller --indirect 'teardown_file' )
then
echo "Must be called from \`setup', \`@test' or \`teardown'" \
| batslib_decorate 'ERROR: temp_make' \
| fail
return $?
fi
# Handle options.
local prefix=''
while (( $# > 0 )); do
case "$1" in
-p|--prefix)
if (( $# < 2 )); then
echo "\`--prefix' requires an argument" \
| batslib_decorate 'ERROR: temp_make' \
| fail
return $?
fi
prefix="$2"
shift 2
;;
--) shift; break ;;
*) break ;;
esac
done
# Create directory.
local template="$prefix"
template+="${BATS_TEST_FILENAME##*/}"
template+="-${BATS_TEST_NUMBER}"
template+='-XXXXXXXXXX'
local path
path="$(mktemp -d -- "${BATS_TMPDIR}/${template}" 2>&1)"
if (( $? )); then
echo "$path" \
| batslib_decorate 'ERROR: temp_make' \
| fail
return $?
fi
echo "$path"
}
# Delete a temporary directory, typically created with `temp_make', and
# its contents.
#
# Note: Actually, this function can be used to delete any file or
# directory. However, it is most useful in deleting temporary
# directories created with `temp_make', hence the naming.
#
# For development and debugging, deletion can be prevented using
# environment variables.
#
# When `BATSLIB_TEMP_PRESERVE' is set to 1, the function succeeds but
# the directory is not deleted.
#
# When `BATSLIB_TEMP_PRESERVE_ON_FAILURE' is set to 1 and `temp_del' is
# called, directly or indirectly, from `teardown', the function succeeds
# but the directory is not deleted if the test has failed.
#
# Example:
#
# setup() {
# TEST_TEMP_DIR="$(temp_make --prefix 'myapp-')"
# }
#
# teardown() {
# temp_del "$TEST_TEMP_DIR"
# }
#
# Globals:
# BATSLIB_TEMP_PRESERVE
# BATSLIB_TEMP_PRESERVE_ON_FAILURE
# BATS_TEST_COMPLETED
# Arguments:
# $1 - path of directory
# Returns:
# 0 - on success
# 1 - otherwise
# Outputs:
# STDERR - error messages
temp_del() {
local -r path="$1"
# Environment variables.
if [[ ${BATSLIB_TEMP_PRESERVE-} == '1' ]]; then
return 0
elif [[ ${BATSLIB_TEMP_PRESERVE_ON_FAILURE-} == '1' ]]; then
# Check caller.
if ! ( batslib_is_caller --indirect 'teardown' \
|| batslib_is_caller --indirect 'teardown_file' )
then
echo "Must be called from \`teardown' or \`teardown_file' when using \`BATSLIB_TEMP_PRESERVE_ON_FAILURE'" \
| batslib_decorate 'ERROR: temp_del' \
| fail
return $?
fi
(( ${BATS_TEST_COMPLETED:-0} != 1 )) && return 0
fi
# Delete directory.
local result
result="$(rm -r -- "$path" 2>&1 </dev/null)"
if (( $? )); then
echo "$result" \
| batslib_decorate 'ERROR: temp_del' \
| fail
return $?
fi
}

View File

@ -0,0 +1,3 @@
source "$(dirname "${BASH_SOURCE[0]}")/src/output.bash"
source "$(dirname "${BASH_SOURCE[0]}")/src/error.bash"
source "$(dirname "${BASH_SOURCE[0]}")/src/lang.bash"

View File

@ -0,0 +1,41 @@
#
# bats-support - Supporting library for Bats test helpers
#
# 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/>.
#
#
# error.bash
# ----------
#
# Functions implementing error reporting. Used by public helper
# functions or test suits directly.
#
# Fail and display a message. When no parameters are specified, the
# message is read from the standard input. Other functions use this to
# report failure.
#
# Globals:
# none
# Arguments:
# $@ - [=STDIN] message
# Returns:
# 1 - always
# Inputs:
# STDIN - [=$@] message
# Outputs:
# STDERR - message
fail() {
(( $# == 0 )) && batslib_err || batslib_err "$@"
return 1
}

View File

@ -0,0 +1,73 @@
#
# bats-util - Various auxiliary functions 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/>.
#
#
# lang.bash
# ---------
#
# Bash language and execution related functions. Used by public helper
# functions.
#
# Check whether the calling function was called from a given function.
#
# By default, direct invocation is checked. The function succeeds if the
# calling function was called directly from the given function. In other
# words, if the given function is the next element on the call stack.
#
# When `--indirect' is specified, indirect invocation is checked. The
# function succeeds if the calling function was called from the given
# function with any number of intermediate calls. In other words, if the
# given function can be found somewhere on the call stack.
#
# Direct invocation is a form of indirect invocation with zero
# intermediate calls.
#
# Globals:
# FUNCNAME
# Options:
# -i, --indirect - check indirect invocation
# Arguments:
# $1 - calling function's name
# Returns:
# 0 - current function was called from the given function
# 1 - otherwise
batslib_is_caller() {
local -i is_mode_direct=1
# Handle options.
while (( $# > 0 )); do
case "$1" in
-i|--indirect) is_mode_direct=0; shift ;;
--) shift; break ;;
*) break ;;
esac
done
# Arguments.
local -r func="$1"
# Check call stack.
if (( is_mode_direct )); then
[[ $func == "${FUNCNAME[2]}" ]] && return 0
else
local -i depth
for (( depth=2; depth<${#FUNCNAME[@]}; ++depth )); do
[[ $func == "${FUNCNAME[$depth]}" ]] && return 0
done
fi
return 1
}

View File

@ -0,0 +1,279 @@
#
# bats-support - Supporting library for Bats test helpers
#
# 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/>.
#
#
# output.bash
# -----------
#
# Private functions implementing output formatting. Used by public
# helper functions.
#
# Print a message to the standard error. When no parameters are
# specified, the message is read from the standard input.
#
# Globals:
# none
# Arguments:
# $@ - [=STDIN] message
# Returns:
# none
# Inputs:
# STDIN - [=$@] message
# Outputs:
# STDERR - message
batslib_err() {
{ if (( $# > 0 )); then
echo "$@"
else
cat -
fi
} >&2
}
# Count the number of lines in the given string.
#
# TODO(ztombol): Fix tests and remove this note after #93 is resolved!
# NOTE: Due to a bug in Bats, `batslib_count_lines "$output"' does not
# give the same result as `${#lines[@]}' when the output contains
# empty lines.
# See PR #93 (https://github.com/sstephenson/bats/pull/93).
#
# Globals:
# none
# Arguments:
# $1 - string
# Returns:
# none
# Outputs:
# STDOUT - number of lines
batslib_count_lines() {
local -i n_lines=0
local line
while IFS='' read -r line || [[ -n $line ]]; do
(( ++n_lines ))
done < <(printf '%s' "$1")
echo "$n_lines"
}
# Determine whether all strings are single-line.
#
# Globals:
# none
# Arguments:
# $@ - strings
# Returns:
# 0 - all strings are single-line
# 1 - otherwise
batslib_is_single_line() {
for string in "$@"; do
(( $(batslib_count_lines "$string") > 1 )) && return 1
done
return 0
}
# Determine the length of the longest key that has a single-line value.
#
# This function is useful in determining the correct width of the key
# column in two-column format when some keys may have multi-line values
# and thus should be excluded.
#
# Globals:
# none
# Arguments:
# $odd - key
# $even - value of the previous key
# Returns:
# none
# Outputs:
# STDOUT - length of longest key
batslib_get_max_single_line_key_width() {
local -i max_len=-1
while (( $# != 0 )); do
local -i key_len="${#1}"
batslib_is_single_line "$2" && (( key_len > max_len )) && max_len="$key_len"
shift 2
done
echo "$max_len"
}
# Print key-value pairs in two-column format.
#
# Keys are displayed in the first column, and their corresponding values
# in the second. To evenly line up values, the key column is fixed-width
# and its width is specified with the first parameter (possibly computed
# using `batslib_get_max_single_line_key_width').
#
# Globals:
# none
# Arguments:
# $1 - width of key column
# $even - key
# $odd - value of the previous key
# Returns:
# none
# Outputs:
# STDOUT - formatted key-value pairs
batslib_print_kv_single() {
local -ir col_width="$1"; shift
while (( $# != 0 )); do
printf '%-*s : %s\n' "$col_width" "$1" "$2"
shift 2
done
}
# Print key-value pairs in multi-line format.
#
# The key is displayed first with the number of lines of its
# corresponding value in parenthesis. Next, starting on the next line,
# the value is displayed. For better readability, it is recommended to
# indent values using `batslib_prefix'.
#
# Globals:
# none
# Arguments:
# $odd - key
# $even - value of the previous key
# Returns:
# none
# Outputs:
# STDOUT - formatted key-value pairs
batslib_print_kv_multi() {
while (( $# != 0 )); do
printf '%s (%d lines):\n' "$1" "$( batslib_count_lines "$2" )"
printf '%s\n' "$2"
shift 2
done
}
# Print all key-value pairs in either two-column or multi-line format
# depending on whether all values are single-line.
#
# If all values are single-line, print all pairs in two-column format
# with the specified key column width (identical to using
# `batslib_print_kv_single').
#
# Otherwise, print all pairs in multi-line format after indenting values
# with two spaces for readability (identical to using `batslib_prefix'
# and `batslib_print_kv_multi')
#
# Globals:
# none
# Arguments:
# $1 - width of key column (for two-column format)
# $even - key
# $odd - value of the previous key
# Returns:
# none
# Outputs:
# STDOUT - formatted key-value pairs
batslib_print_kv_single_or_multi() {
local -ir width="$1"; shift
local -a pairs=( "$@" )
local -a values=()
local -i i
for (( i=1; i < ${#pairs[@]}; i+=2 )); do
values+=( "${pairs[$i]}" )
done
if batslib_is_single_line "${values[@]}"; then
batslib_print_kv_single "$width" "${pairs[@]}"
else
local -i i
for (( i=1; i < ${#pairs[@]}; i+=2 )); do
pairs[$i]="$( batslib_prefix < <(printf '%s' "${pairs[$i]}") )"
done
batslib_print_kv_multi "${pairs[@]}"
fi
}
# Prefix each line read from the standard input with the given string.
#
# Globals:
# none
# Arguments:
# $1 - [= ] prefix string
# Returns:
# none
# Inputs:
# STDIN - lines
# Outputs:
# STDOUT - prefixed lines
batslib_prefix() {
local -r prefix="${1:- }"
local line
while IFS='' read -r line || [[ -n $line ]]; do
printf '%s%s\n' "$prefix" "$line"
done
}
# Mark select lines of the text read from the standard input by
# overwriting their beginning with the given string.
#
# Usually the input is indented by a few spaces using `batslib_prefix'
# first.
#
# Globals:
# none
# Arguments:
# $1 - marking string
# $@ - indices (zero-based) of lines to mark
# Returns:
# none
# Inputs:
# STDIN - lines
# Outputs:
# STDOUT - lines after marking
batslib_mark() {
local -r symbol="$1"; shift
# Sort line numbers.
set -- $( sort -nu <<< "$( printf '%d\n' "$@" )" )
local line
local -i idx=0
while IFS='' read -r line || [[ -n $line ]]; do
if (( ${1:--1} == idx )); then
printf '%s\n' "${symbol}${line:${#symbol}}"
shift
else
printf '%s\n' "$line"
fi
(( ++idx ))
done
}
# Enclose the input text in header and footer lines.
#
# The header contains the given string as title. The output is preceded
# and followed by an additional newline to make it stand out more.
#
# Globals:
# none
# Arguments:
# $1 - title
# Returns:
# none
# Inputs:
# STDIN - text
# Outputs:
# STDOUT - decorated text
batslib_decorate() {
echo
echo "-- $1 --"
cat -
echo '--'
echo
}

403
extras/portable/tomb Executable file
View File

@ -0,0 +1,403 @@
#!/bin/sh
#
# Tomb v3, the Crypto Undertaker portable version 3
#
# A commandline tool to easily operate encryption of secret data
#
# {{{ License
# Copyright (C) 2007-2022 Dyne.org Foundation
#
# Tomb is designed, written and maintained by Denis Roio <jaromil@dyne.org>
#
# Please refer to the AUTHORS file for more information.
#
# 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, , see <https://www.gnu.org/licenses/>.
# }}} - License
# {{{ Global variables
VERSION="3.1.0"
DATE="Nov/2022"
TOMBEXEC="$0"
TMPDIR="${TMP:-/tmp}"
TOMBTMPFILES=""
PATH="${PATH}:.:/usr/local/bin"
exitcode=0
# }}}
# {{{ Logging functions
_success() {
echo "[*] " "$1" "$2" "$3" "$4" 1>&2
}
_message() {
echo " . " "$1" "$2" "$3" "$4" 1>&2
}
_warning() {
echo "[W] " "$1" "$2" "$3" "$4" 1>&2
}
_verbose() {
echo "[D] " "$1" "$2" "$3" "$4" 1>&2
}
_error() {
echo "[!] " "$1" "$2" "$3" "$4" 1>&2
exitcode=1
}
_failure() {
echo "[!!] " "$1" "$2" "$3" "$4" 1>&2
exitcode=1
exit 1
}
# }}}
_success "Starting Tomb v$VERSION"
# {{{ Internal functions
_random_string() {
len=${1:-32}
[ "$1" != "" ] && {
echo "print(O.random($len):base58())" | zenroom 2>/dev/null
}
return $?
}
_tmp_create() {
[ -d "$TMPDIR" ] || {
# we create the tempdir with the sticky bit on
mkdir -m 1777 "$TMPDIR"
[ $? = 0 ] || {
_failure "Fatal error creating the temporary directory: %s" "$TMPDIR"
}
}
tfile="${TMPDIR}/`_random_string 64`" # Temporary file
umask 066
[ $? = 0 ] || {
_failure "Fatal error setting the permission umask for temporary files"
}
[ -r "$tfile" ] && {
_failure "Someone is messing up with us trying to hijack temporary files."
}
touch "$tfile"
[ $? = 0 ] || {
_failure "Fatal error creating a temporary file: %s" "$tfile"
}
_verbose "Created tempfile: ::1 temp file::" "$tfile"
TOMBTMP="$tfile"
TOMBTMPFILES="$TOMBTMPFILES:$tfile"
return 0
}
# }}}
# {{{ FreeBSD
freebsd_mount() {
file="$1"
mnt="$2"
_verbose `veracrypt -l "$file"`
loop=`veracrypt -l "$file" | awk '{print $3}'`
_verbose "fsck $loop"
fsck.ext4 -p -C0 "$loop"
lklfuse -o type=ext4 "${loop}" "$mnt"
return $?
}
freebsd_close() {
file="$1"
md=`veracrypt -l "$file" | awk '{print $3}'`
# umount "$mnt"
_verbose "md: $md"
mnt=`pgrep -lf "lklfuse.*$md" | awk '{print $6}'`
_verbose "mnt: $mnt"
lkl=`pgrep -f "lklfuse.*$md" | awk '{print $1}'`
_verbose "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
_verbose "veracrypt -d $file"
veracrypt --text --non-interactive -d "$file"
return $?
}
# }}}
# {{{ Linux
# usage: _mount /tmp/.veracrypt_ mountpoint
linux_mount() {
file="$1"
mnt="$2"
veralist=`veracrypt -l "$file" | awk '{print($2,":",$3,":",$4)}'`
[ "$veralist" = "" ] && {
_error "Cannot mount tomb not yet mapped " "$file"
return 1
}
loop=`echo $veralist | cut -d: -f2 | xargs`
[ "`echo $veralist | cut -d: -f3 | xargs`" != "-" ] && {
_error "Tomb already mounted " "$file on $loop"
return 1
}
_verbose "fsck $loop"
fsck.ext4 -p -C0 "$loop" 1>&2
_verbose "linux_mount $mnt"
mount "$loop" "$mnt"
return $?
}
# }}}
# {{{ POSIX portable
# usage: echo PASSWORD | posix_create file size pim
posix_create() {
file="$1" # must not exist
size="$2" # size in bytes
pim="$3" # any number
_verbose "posix_create $file $size $pim"
veracrypt --text --non-interactive --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 $?
}
posix_format() {
file="$1"
loop=`veracrypt -l "$file" | awk '{print $3}'`
_verbose "posix_format: ${loop}"
mkfs.ext4 -L "`basename $file`" "$loop" # -E root_owner="${user_uid}:${user_gid}" "$loop"
return $?
}
# usage: echo PASSWORD | posix_map file pim
posix_map() {
file="$1"
pim="$2"
_verbose "posix_map $file $pim"
veracrypt --text --non-interactive --stdin \
--protect-hidden no -m nokernelcrypto \
-k '' --pim "$pim" --filesystem none \
"$file"
return $?
}
posix_close() {
file="$1"
_verbose "posix_close $file"
veracrypt --text --non-interactive -d "$file"
return $?
}
# }}}
# {{{ Initialization
system="unknown"
create=""
format=""
map=""
mount=""
close=""
tomb_init() {
system="`uname -s`"
case "$system" in
FreeBSD)
cat <<EOF
create=posix_create
format=posix_format
map=posix_map
mount=freebsd_mount
close=freebsd_close
EOF
;;
Linux)
cat <<EOF
create=posix_create
format=posix_format
map=posix_map
mount=linux_mount
close=posix_close
EOF
;;
*)
_failure "Unsupported system: %s" "$system"
esac
echo "system=$system"
# _verbose "system detected: %s" $system
}
# }}}
# {{{ Main
[ "$1" = "source" ] && return 0
eval "`tomb_init`"
tombfile=""
tombsize=""
PIM=69
cmd="$1"
shift 1
case "$cmd" in
dig)
args=2
while [ $args -gt 0 ]; do
[ "$1" = '-s' ] && { tombsize="$2"; shift 2; }
[ "$1" != "" ] && { tombfile="$1"; shift 1; }
args=$(( $args - 1 ))
done
[ "$tombfile" = "" ] && _failure "Missing path to tomb"
[ "$tombsize" = "" ] && _failure "Size argument missing, use -s"
# TODO: check if size is integer
# [ "$tombsize" -ge 10 ] || _failure "Tombs can't be smaller than 10 mebibytes"
[ -e "$tombfile" ] && {
_error "A tomb exists already. I'm not digging here:"
ls -l "$tombfile"; exit 1
}
_message "Commanded to dig tomb " "$tombfile" " sized $tombsize MiB"
touch "$tombfile" || _failure "Error creating the tomb " "$tombfile"
# dd if=/dev/urandom bs=1048576 count=$tombsize of="$tombfile" || {
# _failure "Error creating the tomb " "$tombfile"
# exit 1
# }
bytesize="$(( $tombsize * 1048576 ))"
[ "$bytesize" = "" ] &&
_failure "Error reading tomb size " "$tombsize"
if [ "$system" = "Linux" ]; then
fallocate -x -l "$bytesize" "$tombfile" ||
_failure "Error creating the tomb " "$tombfile"
else
dd if=/dev/zero of="$tombfile" count="$tombsize" bs=1048576 ||
_failure "Error creating the tomb " "$tombfile of $tombsize MiB"
fi
;;
forge)
args=1
[ "$1" = "" ] && _failure "Missing argument in command " "forge"
while [ $args -gt 0 ]; do
case "$1" in
'-k') tombkey="$2"; shift 2 ;;
*) tombkey="$1"; shift 1 ;;
esac
args=$(( $args - 1 ))
done
[ "$tombkey" = "" ] && _failure "Missing path to tomb key"
[ -e "$tombkey" ] && {
_error "A tomb key exists already. I'm not forging here:"
ls -l "$tombkey"; exit 1
}
_message "Commanded to forge tomb key " "$tombkey"
touch "$tombkey" || _failure "Error creating the tomb key " "$tombkey"
cat <<EOF | zenroom -z | cut -d\" -f4 > "$tombkey"
rule check version 2.0.0
Given nothing
When I create the random object of '64' bytes
Then print the 'random object' as 'hex'
EOF
;;
lock)
args=2
while [ $args -gt 0 ]; do
case "$1" in
'-k') tombkey="$2"; shift 2 ;;
*) tombfile="$1"; shift 1 ;;
esac
args=$(( $args - 1 ))
done
case "$system" in
Linux)
bytesize=`stat "$tombfile" | awk '/Size:/ {print $2; exit}'`
;;
*)
bytesize=`stat -f '%z' "$tombfile"`
;;
esac
_message "Commanded to lock tomb " "$tombfile with key $tombkey"
[ "$tombfile" = "" ] && _failure "Missing path to tomb"
[ -r "$tombfile" ] || _failure "Tomb file not readable"
[ "$tombkey" = "" ] && _failure "Missing path to key"
[ -r "$tombkey" ] || _failure "Key file not readable"
"$create" "$tombfile" "$bytesize" "$PIM" < "$tombkey" || {
_failure "Error creating the tomb " "$tombfile"
exit 1
}
"$map" "$tombfile" "$PIM" < "$tombkey" ||
_failure "Error mapping the tomb " "$tombfile with key $tombkey"
"$format" "$tombfile" ||
_error "Error formatting tomb " "$tombfile"
"$close" "$tombfile"
[ $exitcode = 0 ] &&
_success "Success locking tomb " "$tombfile with key $tombkey"
;;
open)
args=3
while [ $args -gt 0 ]; do
case "$1" in
'-k') tombkey="$2"; shift 2 ;;
*) if [ "$tombfile" = "" ]; then
tombfile="$1"
else
tombmount="$1"
fi
shift 1 ;;
esac
args=$(( $args - 1 ))
done
_message "Commanded to open tomb " "$tombfile with key $tombkey on $tombmount"
[ "$tombfile" = "" ] && _failure "Missing path to tomb"
[ -r "$tombfile" ] || _failure "Tomb file not readable"
[ "$tombkey" = "" ] && _failure "Missing path to key"
[ -r "$tombkey" ] || _failure "Key file not readable"
[ "$tombmount" = "" ] && tombmount="`basename $tombfile`"
"$map" "$tombfile" "$PIM" < "$tombkey"
[ $? != 0 ] &&
_failure "Error mapping the tomb " "$tombfile with key $tombkey"
"$mount" "$tombfile" "$tombmount" || {
"$close" "$tombfile"
_failure "Error mounting the tomb " "$tombfile on $tombmount"
}
;;
close)
# args=1
if [ "$1" = "all" ]; then
"$close"
elif [ -e "$1" ]; then
"$close" "`realpath $1`" ||
_failure "Error closing " "$1"
elif [ "$1" = "" ]; then
_failure "Missing argument: tomb file or mountpoint"
else
_failure "Wrong argument: $1"
fi
;;
list)
veracrypt -l
;;
esac
# }}}
exit 0

View File

@ -3,3 +3,9 @@ Build with 'qmake' and then 'make'
Requires QT version 5.4 or above Requires QT version 5.4 or above
To install copy the binary in your PATH
then all contents of pixmaps to /usr/share/pixmaps
and all contents of i18n to /usr/share/i18n
The prefix /usr/local may be preferred if ever packaged.

View File

@ -11,19 +11,41 @@ test_expect_success 'Testing tomb creation: dig, forge and lock' '
tt_forge --tomb-pwd $DUMMYPASS && tt_forge --tomb-pwd $DUMMYPASS &&
print $DUMMYPASS \ print $DUMMYPASS \
| gpg --batch --passphrase-fd 0 --no-tty --no-options -d $tomb_key \ | gpg --batch --passphrase-fd 0 --no-tty --no-options -d $tomb_key \
| hexdump -C && | xxd &&
tt_lock --tomb-pwd $DUMMYPASS tt_lock --tomb-pwd $DUMMYPASS
' '
if test_have_prereq SPHINX ORACLE; then if test_have_prereq DOAS; then
test_export "sphinx_test" test_export "doas_test"
test_expect_success 'Testing tomb creation: dig, forge and lock (sphinx password handling)' ' test_expect_success 'Testing tomb creation: dig, forge and lock (using doas instead of sudo)' '
tt_dig -s 20 && tt_dig --sudo doas -s 20 &&
tt_forge --tomb-pwd $DUMMYPASS --sphx-user $DUMMYUSER --sphx-host $DUMMYHOST && tt_forge --sudo doas --tomb-pwd $DUMMYPASS &&
print $(echo $DUMMYPASS | sphinx get $DUMMYUSER $DUMMYHOST) \ print $DUMMYPASS \
| gpg --batch --passphrase-fd 0 --no-tty --no-options -d $tomb_key \
| xxd &&
tt_lock --sudo doas --tomb-pwd $DUMMYPASS
'
fi
if test_have_prereq BTRFS; then
test_export "btrfs"
test_expect_success 'Testing tomb creation using BTRFS filesystem' '
tt_dig -s 120 &&
tt_forge --tomb-pwd $DUMMYPASS &&
print $DUMMYPASS \
| gpg --batch --passphrase-fd 0 --no-tty --no-options -d $tomb_key \ | gpg --batch --passphrase-fd 0 --no-tty --no-options -d $tomb_key \
| hexdump -C && | xxd &&
tt_lock --tomb-pwd $DUMMYPASS --sphx-user $DUMMYUSER --sphx-host $DUMMYHOST tt_lock --tomb-pwd $DUMMYPASS --filesystem btrfs
'
test_export "btrfsmixed"
test_expect_success 'Testing tomb creation using BTRFS mixedmode filesystem' '
tt_dig -s 20 &&
tt_forge --tomb-pwd $DUMMYPASS &&
print $DUMMYPASS \
| gpg --batch --passphrase-fd 0 --no-tty --no-options -d $tomb_key \
| xxd &&
tt_lock --tomb-pwd $DUMMYPASS --filesystem btrfsmixedmode
' '
fi fi

View File

@ -44,13 +44,12 @@ if test_have_prereq LSOF; then
' '
fi fi
if test_have_prereq SPHINX ORACLE; then if test_have_prereq DOAS; then
test_export "sphinx_test" # Using already generated tomb test_export "doas_test" # Using already generated tomb
test_expect_success 'Testing open with good password (sphinx)' ' test_expect_success 'Testing open with good password (using doas instead of sudo)' '
tt_open --tomb-pwd $DUMMYPASS --sphx-user $DUMMYUSER --sphx-host $DUMMYHOST && tt_open --sudo doas --tomb-pwd $DUMMYPASS &&
tt_close tt_close --sudo doas
' '
fi fi
test_done test_done

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