diff --git a/awx/main/fields.py b/awx/main/fields.py index e4ec5ede2b..a1900294fa 100644 --- a/awx/main/fields.py +++ b/awx/main/fields.py @@ -637,6 +637,14 @@ class CredentialInputField(JSONSchemaField): 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 d023ef5e4b..9a534a8897 100644 --- a/awx/main/tests/functional/api/test_credential.py +++ b/awx/main/tests/functional/api/test_credential.py @@ -1153,6 +1153,22 @@ def test_cloud_credential_type_mutability(patch, organization, admin, credential 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 = {