diff --git a/awx/main/fields.py b/awx/main/fields.py index e4ec5ede2b09..a1900294fa54 100644 --- a/awx/main/fields.py +++ b/awx/main/fields.py @@ -637,6 +637,14 @@ def validate(self, value, model_instance): else: decrypted_values[k] = v + # don't allow secrets with $encrypted$ on new object creation + if not model_instance.pk: + for field in model_instance.credential_type.secret_fields: + if value.get(field) == '$encrypted$': + raise serializers.ValidationError({ + self.name: [f'$encrypted$ is a reserved keyword, and cannot be used for {field}.'] + }) + super(JSONSchemaField, self).validate(decrypted_values, model_instance) errors = {} for error in Draft4Validator( diff --git a/awx/main/tests/functional/api/test_credential.py b/awx/main/tests/functional/api/test_credential.py index d023ef5e4b7f..9a534a88976b 100644 --- a/awx/main/tests/functional/api/test_credential.py +++ b/awx/main/tests/functional/api/test_credential.py @@ -1153,6 +1153,22 @@ def _change_credential_type(): assert response.status_code == 200 +@pytest.mark.django_db +@pytest.mark.parametrize('field', ['password', 'ssh_key_data']) +def test_secret_fields_cannot_be_special_encrypted_variable(post, organization, admin, credentialtype_ssh, field): + params = { + 'name': 'Best credential ever', + 'credential_type': credentialtype_ssh.id, + 'inputs': { + 'username': 'joe', + field: '$encrypted$', + }, + 'organization': organization.id, + } + response = post(reverse('api:credential_list'), params, admin, status=400) + assert str(response.data['inputs'][0]) == f'$encrypted$ is a reserved keyword, and cannot be used for {field}.' + + @pytest.mark.django_db def test_ssh_unlock_needed(put, organization, admin, credentialtype_ssh): params = {