From 5ff6c3c35fb4beb18416f8a77ada2f45cb27e544 Mon Sep 17 00:00:00 2001 From: HugoPBrito Date: Fri, 27 Feb 2026 14:02:46 +0100 Subject: [PATCH] fix(api): reject cloudflare api key in token field - Add serializer validation for Cloudflare api_token values - Reject 32-character hexadecimal values in token credentials - Add serializer tests for valid token and api key-shaped token --- api/src/backend/api/tests/test_serializers.py | 19 ++++++++++++++++++- api/src/backend/api/v1/serializers.py | 10 ++++++++++ 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/api/src/backend/api/tests/test_serializers.py b/api/src/backend/api/tests/test_serializers.py index 5810a97b63..613114a92d 100644 --- a/api/src/backend/api/tests/test_serializers.py +++ b/api/src/backend/api/tests/test_serializers.py @@ -2,7 +2,7 @@ import pytest from rest_framework.exceptions import ValidationError from api.v1.serializer_utils.integrations import S3ConfigSerializer -from api.v1.serializers import ImageProviderSecret +from api.v1.serializers import CloudflareTokenProviderSecret, ImageProviderSecret class TestS3ConfigSerializer: @@ -133,3 +133,20 @@ class TestImageProviderSecret: serializer = ImageProviderSecret(data={"registry_password": "pass"}) assert not serializer.is_valid() assert "non_field_errors" in serializer.errors + + +class TestCloudflareProviderSecret: + """Test cases for Cloudflare provider credential formats.""" + + def test_valid_api_token(self): + serializer = CloudflareTokenProviderSecret( + data={"api_token": "Sn3lZJTBX6kkg7OdcBUAxOO963GEIyGQqnFTOFYY"} + ) + assert serializer.is_valid(), serializer.errors + + def test_invalid_api_token_with_api_key_format(self): + serializer = CloudflareTokenProviderSecret( + data={"api_token": "144c9defac04969c7bfad8efaa8ea194"} + ) + assert not serializer.is_valid() + assert "api_token" in serializer.errors diff --git a/api/src/backend/api/v1/serializers.py b/api/src/backend/api/v1/serializers.py index 7c4bb41ca5..7ffdb97f15 100644 --- a/api/src/backend/api/v1/serializers.py +++ b/api/src/backend/api/v1/serializers.py @@ -1,5 +1,6 @@ import base64 import json +import re from datetime import datetime, timedelta, timezone from django.conf import settings @@ -1704,6 +1705,15 @@ class OracleCloudProviderSecret(serializers.Serializer): class CloudflareTokenProviderSecret(serializers.Serializer): api_token = serializers.CharField() + def validate_api_token(self, value: str) -> str: + # Cloudflare Global API Key is 32 hex chars; reject it in token field. + if re.fullmatch(r"[a-fA-F0-9]{32}", (value or "").strip()): + raise serializers.ValidationError( + "This value matches Cloudflare API Key format. " + "Use 'api_key' and 'api_email' instead." + ) + return value + class Meta: resource_name = "provider-secrets"