Marco Ricci commited on 2024-08-31 21:24:29
Zeige 2 geänderte Dateien mit 155 Einfügungen und 1 Löschungen.
Further tests are necessary to achieve 100% coverage, but these concern the detection of format errors, so the test files are rather hard to generate.
... | ... |
@@ -440,6 +440,51 @@ VAULT_STOREROOM_CONFIG_DATA = { |
440 | 440 |
}, |
441 | 441 |
} |
442 | 442 |
|
443 |
+_VAULT_STOREROOM_BROKEN_DIR_CONFIG_ZIPPED_JAVASCRIPT_SOURCE = """ |
|
444 |
+// Executed in the top-level directory of the vault project code, in Node.js. |
|
445 |
+const storeroom = require('storeroom') |
|
446 |
+const Store = require('./lib/store.js') |
|
447 |
+let store = new Store(storeroom.createFileAdapter('./broken-dir', 'vault key')) |
|
448 |
+await store._storeroom.put('/services/array/', ['entry1','entry2']) |
|
449 |
+// The resulting "broken-dir" was then zipped manually. |
|
450 |
+""" |
|
451 |
+VAULT_STOREROOM_BROKEN_DIR_CONFIG_ZIPPED = b""" |
|
452 |
+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 |
|
486 |
+""" |
|
487 |
+ |
|
443 | 488 |
CANNOT_LOAD_CRYPTOGRAPHY = ( |
444 | 489 |
b'Cannot load the required Python module "cryptography".' |
445 | 490 |
) |
... | ... |
@@ -11,7 +11,7 @@ import click.testing |
11 | 11 |
import pytest |
12 | 12 |
|
13 | 13 |
import tests |
14 |
-from derivepassphrase.exporter import cli |
|
14 |
+from derivepassphrase.exporter import cli, storeroom |
|
15 | 15 |
|
16 | 16 |
cryptography = pytest.importorskip('cryptography', minversion='38.0') |
17 | 17 |
|
... | ... |
@@ -194,3 +194,112 @@ class TestCLI: |
194 | 194 |
assert result.stderr_bytes |
195 | 195 |
assert b'Invalid vault config: ' in result.stderr_bytes |
196 | 196 |
assert tests.CANNOT_LOAD_CRYPTOGRAPHY not in result.stderr_bytes |
197 |
+ |
|
198 |
+ |
|
199 |
+class TestStoreroom: |
|
200 |
+ @pytest.mark.parametrize( |
|
201 |
+ ['path', 'key'], |
|
202 |
+ [ |
|
203 |
+ ('.vault', tests.VAULT_MASTER_KEY), |
|
204 |
+ ('.vault', None), |
|
205 |
+ (None, tests.VAULT_MASTER_KEY), |
|
206 |
+ (None, None), |
|
207 |
+ ], |
|
208 |
+ ) |
|
209 |
+ def test_200_export_data_path_and_keys_type( |
|
210 |
+ self, |
|
211 |
+ monkeypatch: pytest.MonkeyPatch, |
|
212 |
+ path: str | None, |
|
213 |
+ key: str | None, |
|
214 |
+ ) -> None: |
|
215 |
+ runner = click.testing.CliRunner(mix_stderr=False) |
|
216 |
+ with tests.isolated_vault_exporter_config( |
|
217 |
+ monkeypatch=monkeypatch, |
|
218 |
+ runner=runner, |
|
219 |
+ vault_config=tests.VAULT_STOREROOM_CONFIG_ZIPPED, |
|
220 |
+ vault_key=tests.VAULT_MASTER_KEY, |
|
221 |
+ ): |
|
222 |
+ assert ( |
|
223 |
+ storeroom.export_storeroom_data(path, key) |
|
224 |
+ == tests.VAULT_STOREROOM_CONFIG_DATA |
|
225 |
+ ) |
|
226 |
+ |
|
227 |
+ def test_400_decrypt_bucket_item_unknown_version(self) -> None: |
|
228 |
+ bucket_item = ( |
|
229 |
+ b'\xff' + bytes(storeroom.ENCRYPTED_KEYPAIR_SIZE) + bytes(3) |
|
230 |
+ ) |
|
231 |
+ master_keys: storeroom.MasterKeys = { |
|
232 |
+ 'encryption_key': bytes(storeroom.KEY_SIZE), |
|
233 |
+ 'signing_key': bytes(storeroom.KEY_SIZE), |
|
234 |
+ 'hashing_key': bytes(storeroom.KEY_SIZE), |
|
235 |
+ } |
|
236 |
+ with pytest.raises(RuntimeError, match='Cannot handle version 255'): |
|
237 |
+ storeroom.decrypt_bucket_item(bucket_item, master_keys) |
|
238 |
+ |
|
239 |
+ @pytest.mark.parametrize('config', ['xxx', 'null', '{"version": 255}']) |
|
240 |
+ def test_401_decrypt_bucket_file_bad_json_or_version( |
|
241 |
+ self, |
|
242 |
+ monkeypatch: pytest.MonkeyPatch, |
|
243 |
+ config: str, |
|
244 |
+ ) -> None: |
|
245 |
+ runner = click.testing.CliRunner(mix_stderr=False) |
|
246 |
+ master_keys: storeroom.MasterKeys = { |
|
247 |
+ 'encryption_key': bytes(storeroom.KEY_SIZE), |
|
248 |
+ 'signing_key': bytes(storeroom.KEY_SIZE), |
|
249 |
+ 'hashing_key': bytes(storeroom.KEY_SIZE), |
|
250 |
+ } |
|
251 |
+ with ( |
|
252 |
+ tests.isolated_vault_exporter_config( |
|
253 |
+ monkeypatch=monkeypatch, |
|
254 |
+ runner=runner, |
|
255 |
+ vault_config=tests.VAULT_STOREROOM_CONFIG_ZIPPED, |
|
256 |
+ ), |
|
257 |
+ ): |
|
258 |
+ with open('.vault/20', 'w', encoding='UTF-8') as outfile: |
|
259 |
+ print(config, file=outfile) |
|
260 |
+ with pytest.raises(RuntimeError, match='Invalid bucket file: '): |
|
261 |
+ list(storeroom.decrypt_bucket_file('.vault/20', master_keys)) |
|
262 |
+ |
|
263 |
+ @pytest.mark.parametrize( |
|
264 |
+ ['data', 'err_msg'], |
|
265 |
+ [ |
|
266 |
+ ('{"version": 255}', 'bad or unsupported keys version header'), |
|
267 |
+ ('{"version": 1}\nAAAA\nAAAA', 'trailing data; cannot make sense'), |
|
268 |
+ ('{"version": 1}\nAAAA', 'cannot handle version 0 encrypted keys'), |
|
269 |
+ ], |
|
270 |
+ ) |
|
271 |
+ def test_402_export_storeroom_data_bad_master_keys_file( |
|
272 |
+ self, |
|
273 |
+ monkeypatch: pytest.MonkeyPatch, |
|
274 |
+ data: str, |
|
275 |
+ err_msg: str, |
|
276 |
+ ) -> None: |
|
277 |
+ runner = click.testing.CliRunner(mix_stderr=False) |
|
278 |
+ with ( |
|
279 |
+ tests.isolated_vault_exporter_config( |
|
280 |
+ monkeypatch=monkeypatch, |
|
281 |
+ runner=runner, |
|
282 |
+ vault_config=tests.VAULT_STOREROOM_CONFIG_ZIPPED, |
|
283 |
+ vault_key=tests.VAULT_MASTER_KEY, |
|
284 |
+ ), |
|
285 |
+ ): |
|
286 |
+ with open('.vault/.keys', 'w', encoding='UTF-8') as outfile: |
|
287 |
+ print(data, file=outfile) |
|
288 |
+ with pytest.raises(RuntimeError, match=err_msg): |
|
289 |
+ storeroom.export_storeroom_data() |
|
290 |
+ |
|
291 |
+ def test_403_export_storeroom_data_bad_directory_listing( |
|
292 |
+ self, |
|
293 |
+ monkeypatch: pytest.MonkeyPatch, |
|
294 |
+ ) -> None: |
|
295 |
+ runner = click.testing.CliRunner(mix_stderr=False) |
|
296 |
+ with ( |
|
297 |
+ tests.isolated_vault_exporter_config( |
|
298 |
+ monkeypatch=monkeypatch, |
|
299 |
+ runner=runner, |
|
300 |
+ vault_config=tests.VAULT_STOREROOM_BROKEN_DIR_CONFIG_ZIPPED, |
|
301 |
+ vault_key=tests.VAULT_MASTER_KEY, |
|
302 |
+ ), |
|
303 |
+ pytest.raises(RuntimeError, match='Object key mismatch'), |
|
304 |
+ ): |
|
305 |
+ storeroom.export_storeroom_data() |
|
197 | 306 |