Marco Ricci commited on 2024-07-28 17:12:05
              Zeige 4 geänderte Dateien mit 71 Einfügungen und 9 Löschungen.
            
A control-flow error resulted in there being a code path where both `phrase` and `key` arguments were passed to the `derivepassphrase.Vault` constructor, which does not take a `key` argument. So, unconditionally discard the `key` argument from the dict of arguments to pass to the `derivepassphrase.Vault` constructor. This also allows streamlining the preceding phrase and key handling logic somewhat. For the tests, add further aliases for the second and third dummy keys, because the comprehensive regression test for this bug needs three different sources of key selection.
| ... | ... | 
                      @@ -1068,18 +1068,12 @@ def derivepassphrase(  | 
                  
| 1068 | 1068 | 
                        # derivepassphrase.Vault.phrase_from_key if a key is  | 
                    
| 1069 | 1069 | 
                        # given. Finally, if nothing is set, error out.  | 
                    
| 1070 | 1070 | 
                        if use_key or use_phrase:  | 
                    
| 1071 | 
                        - if use_key:  | 
                    |
| 1072 | 
                        - kwargs['phrase'] = key_to_phrase(key)  | 
                    |
| 1073 | 
                        - else:  | 
                    |
| 1074 | 
                        - kwargs['phrase'] = phrase  | 
                    |
| 1075 | 
                        -                    kwargs.pop('key', '')
                       | 
                    |
| 1071 | 
                        + kwargs['phrase'] = key_to_phrase(key) if use_key else phrase  | 
                    |
| 1076 | 1072 | 
                                     elif kwargs.get('phrase') and kwargs.get('key'):
                       | 
                    
| 1077 | 1073 | 
                                         if any('key' in m for m in settings.maps[:2]):
                       | 
                    
| 1078 | 
                        -                    kwargs['phrase'] = key_to_phrase(kwargs.pop('key'))
                       | 
                    |
| 1079 | 
                        - else:  | 
                    |
| 1080 | 
                        -                    kwargs.pop('key')
                       | 
                    |
| 1074 | 
                        + kwargs['phrase'] = key_to_phrase(kwargs['key'])  | 
                    |
| 1081 | 1075 | 
                                     elif kwargs.get('key'):
                       | 
                    
| 1082 | 
                        -                kwargs['phrase'] = key_to_phrase(kwargs.pop('key'))
                       | 
                    |
| 1076 | 
                        + kwargs['phrase'] = key_to_phrase(kwargs['key'])  | 
                    |
| 1083 | 1077 | 
                                     elif kwargs.get('phrase'):
                       | 
                    
| 1084 | 1078 | 
                        pass  | 
                    
| 1085 | 1079 | 
                        else:  | 
                    
| ... | ... | 
                      @@ -1088,6 +1082,7 @@ def derivepassphrase(  | 
                  
| 1088 | 1082 | 
                        'or in configuration'  | 
                    
| 1089 | 1083 | 
                        )  | 
                    
| 1090 | 1084 | 
                        raise click.UsageError(msg)  | 
                    
| 1085 | 
                        +            kwargs.pop('key', '')
                       | 
                    |
| 1091 | 1086 | 
                        vault = dpp.Vault(**kwargs)  | 
                    
| 1092 | 1087 | 
                        result = vault.generate(service)  | 
                    
| 1093 | 1088 | 
                                     click.echo(result.decode('ASCII'))
                       | 
                    
| ... | ... | 
                      @@ -327,6 +327,8 @@ DUMMY_KEY1 = SUPPORTED_KEYS['ed25519']['public_key_data']  | 
                  
| 327 | 327 | 
                         DUMMY_KEY1_B64 = base64.standard_b64encode(DUMMY_KEY1).decode('ASCII')
                       | 
                    
| 328 | 328 | 
                        DUMMY_KEY2 = SUPPORTED_KEYS['rsa']['public_key_data']  | 
                    
| 329 | 329 | 
                         DUMMY_KEY2_B64 = base64.standard_b64encode(DUMMY_KEY2).decode('ASCII')
                       | 
                    
| 330 | 
                        +DUMMY_KEY3 = SUPPORTED_KEYS['ed448']['public_key_data']  | 
                    |
| 331 | 
                        +DUMMY_KEY3_B64 = base64.standard_b64encode(DUMMY_KEY3).decode('ASCII')
                       | 
                    |
| 330 | 332 | 
                         DUMMY_CONFIG_SETTINGS = {
                       | 
                    
| 331 | 333 | 
                        'length': 10,  | 
                    
| 332 | 334 | 
                        'upper': 1,  | 
                    
| ... | ... | 
                      @@ -36,6 +36,9 @@ DUMMY_PHRASE_FROM_KEY1 = tests.DUMMY_PHRASE_FROM_KEY1  | 
                  
| 36 | 36 | 
                        DUMMY_KEY1 = tests.DUMMY_KEY1  | 
                    
| 37 | 37 | 
                        DUMMY_KEY1_B64 = tests.DUMMY_KEY1_B64  | 
                    
| 38 | 38 | 
                        DUMMY_KEY2 = tests.DUMMY_KEY2  | 
                    
| 39 | 
                        +DUMMY_KEY2_B64 = tests.DUMMY_KEY2_B64  | 
                    |
| 40 | 
                        +DUMMY_KEY3 = tests.DUMMY_KEY3  | 
                    |
| 41 | 
                        +DUMMY_KEY3_B64 = tests.DUMMY_KEY3_B64  | 
                    |
| 39 | 42 | 
                         | 
                    
| 40 | 43 | 
                         | 
                    
| 41 | 44 | 
                        class IncompatibleConfiguration(NamedTuple):  | 
                    
| ... | ... | 
                      @@ -358,6 +361,67 @@ class TestCLI:  | 
                  
| 358 | 361 | 
                        last_line.rstrip(b'\n') == DUMMY_RESULT_KEY1  | 
                    
| 359 | 362 | 
                        ), 'program generated unexpected result (wrong settings?)'  | 
                    
| 360 | 363 | 
                         | 
                    
| 364 | 
                        + @tests.skip_if_no_agent  | 
                    |
| 365 | 
                        + @pytest.mark.parametrize(  | 
                    |
| 366 | 
                        + 'config',  | 
                    |
| 367 | 
                        + [  | 
                    |
| 368 | 
                        + pytest.param(  | 
                    |
| 369 | 
                        +                {
                       | 
                    |
| 370 | 
                        +                    'global': {'key': DUMMY_KEY1_B64},
                       | 
                    |
| 371 | 
                        +                    'services': {DUMMY_SERVICE: {}},
                       | 
                    |
| 372 | 
                        + },  | 
                    |
| 373 | 
                        + id='global_config',  | 
                    |
| 374 | 
                        + ),  | 
                    |
| 375 | 
                        + pytest.param(  | 
                    |
| 376 | 
                        +                {'services': {DUMMY_SERVICE: {'key': DUMMY_KEY2_B64}}},
                       | 
                    |
| 377 | 
                        + id='service_config',  | 
                    |
| 378 | 
                        + ),  | 
                    |
| 379 | 
                        + pytest.param(  | 
                    |
| 380 | 
                        +                {
                       | 
                    |
| 381 | 
                        +                    'global': {'key': DUMMY_KEY1_B64},
                       | 
                    |
| 382 | 
                        +                    'services': {DUMMY_SERVICE: {'key': DUMMY_KEY2_B64}},
                       | 
                    |
| 383 | 
                        + },  | 
                    |
| 384 | 
                        + id='full_config',  | 
                    |
| 385 | 
                        + ),  | 
                    |
| 386 | 
                        + ],  | 
                    |
| 387 | 
                        + )  | 
                    |
| 388 | 
                        +    @pytest.mark.parametrize('key_index', [1, 2, 3], ids=lambda i: f'index{i}')
                       | 
                    |
| 389 | 
                        + def test_204c_key_override_on_command_line(  | 
                    |
| 390 | 
                        + self,  | 
                    |
| 391 | 
                        + monkeypatch: Any,  | 
                    |
| 392 | 
                        + config: dict[str, Any],  | 
                    |
| 393 | 
                        + key_index: int,  | 
                    |
| 394 | 
                        + ) -> None:  | 
                    |
| 395 | 
                        + def sign(  | 
                    |
| 396 | 
                        + _, key: bytes | bytearray, message: bytes | bytearray  | 
                    |
| 397 | 
                        + ) -> bytes:  | 
                    |
| 398 | 
                        + del message # Unused.  | 
                    |
| 399 | 
                        + for value in tests.SUPPORTED_KEYS.values():  | 
                    |
| 400 | 
                        + if value['public_key_data'] == key:  | 
                    |
| 401 | 
                        + assert value['expected_signature'] is not None  | 
                    |
| 402 | 
                        + return value['expected_signature']  | 
                    |
| 403 | 
                        + raise AssertionError  | 
                    |
| 404 | 
                        +  | 
                    |
| 405 | 
                        + monkeypatch.setattr(  | 
                    |
| 406 | 
                        + ssh_agent_client.SSHAgentClient, 'list_keys', tests.list_keys  | 
                    |
| 407 | 
                        + )  | 
                    |
| 408 | 
                        + monkeypatch.setattr(ssh_agent_client.SSHAgentClient, 'sign', sign)  | 
                    |
| 409 | 
                        + runner = click.testing.CliRunner(mix_stderr=False)  | 
                    |
| 410 | 
                        + with tests.isolated_config(  | 
                    |
| 411 | 
                        + monkeypatch=monkeypatch, runner=runner, config=config  | 
                    |
| 412 | 
                        + ):  | 
                    |
| 413 | 
                        + result = runner.invoke(  | 
                    |
| 414 | 
                        + cli.derivepassphrase,  | 
                    |
| 415 | 
                        + ['-k', DUMMY_SERVICE],  | 
                    |
| 416 | 
                        +                input=f'{key_index}\n'.encode('ASCII'),
                       | 
                    |
| 417 | 
                        + )  | 
                    |
| 418 | 
                        + assert result.stderr_bytes is not None  | 
                    |
| 419 | 
                        + assert (  | 
                    |
| 420 | 
                        + b'Error:' not in result.stderr_bytes  | 
                    |
| 421 | 
                        + ), 'program exited with failure'  | 
                    |
| 422 | 
                        + assert result.stdout_bytes, 'program output expected'  | 
                    |
| 423 | 
                        + assert result.exit_code == 0, 'program exited with failure'  | 
                    |
| 424 | 
                        +  | 
                    |
| 361 | 425 | 
                        def test_205_service_phrase_if_key_in_global_config(  | 
                    
| 362 | 426 | 
                        self,  | 
                    
| 363 | 427 | 
                        monkeypatch: Any,  |