Skip to content

Commit 56a2d80

Browse files
committed
Unify pulp_labels key validation to allow hyphens, spaces, and dots
The three places validating label keys (pulp_labels_validator, SetLabelSerializer/UnsetLabelSerializer, and LabelFilter) each allowed different characters, making it possible to create labels that couldn't be modified or filtered. Unify them all to accept alphanumerics, underscores, spaces, hyphens, and dots. closes #6456 Assisted-by: Claude-Opus-4.6 (Cursor)
1 parent 957343f commit 56a2d80

6 files changed

Lines changed: 39 additions & 14 deletions

File tree

CHANGES/6456.bugfix

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Unified `pulp_labels` key validation across create/update, `set_label`/`unset_label`, and label filters to consistently allow alphanumerics, underscores, spaces, hyphens, and dots.

pulpcore/app/serializers/base.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -564,16 +564,22 @@ class SetLabelSerializer(serializers.Serializer):
564564
Serializer for synchronously setting a label.
565565
"""
566566

567-
key = serializers.SlugField(required=True)
567+
key = serializers.CharField(required=True)
568568
value = serializers.CharField(required=True, allow_null=True, allow_blank=True)
569569

570+
def validate(self, data):
571+
from pulpcore.app.serializers.fields import pulp_labels_validator
572+
573+
pulp_labels_validator({data["key"]: data["value"]})
574+
return super().validate(data)
575+
570576

571577
class UnsetLabelSerializer(serializers.Serializer):
572578
"""
573579
Serializer for synchronously UNsetting a label.
574580
"""
575581

576-
key = serializers.SlugField(required=True)
582+
key = serializers.CharField(required=True)
577583
value = serializers.CharField(read_only=True)
578584

579585
def validate_key(self, value):

pulpcore/app/serializers/fields.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
from rest_framework.fields import empty
1212

1313
from pulpcore.app import models
14+
from pulpcore.constants import LABEL_KEY_REGEX
1415
from pulpcore.app.serializers import DetailIdentityField, IdentityField, RelatedField
1516
from pulpcore.app.util import reverse
1617

@@ -427,8 +428,13 @@ def pulp_labels_validator(value):
427428
value = json.loads(value)
428429

429430
for k, v in value.items():
430-
if not re.match(r"^[\w ]+$", k):
431-
raise serializers.ValidationError(_("Key '{}' contains non-alphanumerics.").format(k))
431+
if not re.match(LABEL_KEY_REGEX, k):
432+
raise serializers.ValidationError(
433+
_(
434+
"Key '{}' contains invalid characters. Only alphanumerics, underscores,"
435+
" spaces, hyphens, and dots are allowed."
436+
).format(k)
437+
)
432438
if v is not None and re.search(r"[,()]", v):
433439
raise serializers.ValidationError(
434440
_("Key '{}' contains value with comma or parenthesis.").format(k)

pulpcore/app/viewsets/custom_filters.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
from gettext import gettext as _
1010

1111
from django.conf import settings
12+
from pulpcore.constants import LABEL_KEY_CHARS
1213
from django.db.models import ObjectDoesNotExist
1314
from django_filters import BaseInFilter, CharFilter, Filter
1415
from drf_spectacular.types import OpenApiTypes
@@ -313,7 +314,7 @@ def filter(self, qs, value):
313314
return qs
314315

315316
for term in value.split(","):
316-
match = re.match(r"(!?[\w\s]+)(=|!=|~)?(.*)?", term)
317+
match = re.match(rf"(!?{LABEL_KEY_CHARS}+)(=|!=|~)?(.*)?", term)
317318
if not match:
318319
raise DRFValidationError(_("Invalid search term: '{}'.").format(term))
319320
key, op, val = match.groups()

pulpcore/constants.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,10 @@
129129
ORPHAN_PROTECTION_TIME_LOWER_BOUND = 0
130130
ORPHAN_PROTECTION_TIME_UPPER_BOUND = 4294967295 # (2^32)-1
131131

132+
# Valid characters for pulp_labels keys: alphanumerics, underscores, spaces, hyphens, and dots.
133+
LABEL_KEY_CHARS = r"[\w .\-]"
134+
LABEL_KEY_REGEX = rf"^{LABEL_KEY_CHARS}+$"
135+
132136
# VULNERABILITY REPORT CONSTANTS
133137
# OSV API URL
134138
OSV_QUERY_URL = "https://api.osv.dev/v1/query"

pulpcore/tests/unit/serializers/test_fields.py

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,25 +8,32 @@
88
@pytest.mark.parametrize(
99
"labels",
1010
[
11-
{"key": "value"},
12-
{"key": ""},
13-
{"key": None},
14-
{"key1": "value", "key2": None, "key3": ""},
11+
pytest.param({"key": "value"}, id="normal"),
12+
pytest.param({"key": ""}, id="empty-value"),
13+
pytest.param({"key": None}, id="none-value"),
14+
pytest.param({"key1": "value", "key2": None, "key3": ""}, id="multiple-keys"),
15+
pytest.param({"my-key": "value"}, id="dash-key"),
16+
pytest.param({"my.key": "value"}, id="dotted-key"),
17+
pytest.param({"my key": "value"}, id="spaced-key"),
18+
pytest.param({"my-dotted.key": "value"}, id="dotted-dash-key"),
19+
pytest.param({"spaced key-with.mixed_chars": "value"}, id="all-key"),
1520
],
1621
)
1722
def test_pulp_labels_validator_valid(labels):
18-
"""Valid label values including None should pass validation."""
23+
"""Valid label keys and values should pass validation."""
1924
result = pulp_labels_validator(labels)
2025
assert result == labels
2126

2227

2328
@pytest.mark.parametrize(
2429
"labels",
2530
[
26-
{"key": "val,ue"},
27-
{"key": "val(ue"},
28-
{"key": "val)ue"},
29-
{"bad!key": "value"},
31+
pytest.param({"key": "val,ue"}, id="comma-value"),
32+
pytest.param({"key": "val(ue"}, id="open-parenthesis-value"),
33+
pytest.param({"key": "val)ue"}, id="close-parenthesis-value"),
34+
pytest.param({"bad!key": "value"}, id="exclamation-key"),
35+
pytest.param({"bad:key": "value"}, id="colon-key"),
36+
pytest.param({"bad@key": "value"}, id="at-sign-key"),
3037
],
3138
)
3239
def test_pulp_labels_validator_invalid(labels):

0 commit comments

Comments
 (0)