[BUG] flytekit: _dnsify can return node IDs over 63 characters or ending in '-', violating the DNS_LABEL contract
Flyte & Flytekit version
flytekit: master @ d69b3fb (also present in 1.16.x)
Python: 3.12
Describe the bug
flytekit.core.utils._dnsify is the only place flytekit enforces the Kubernetes DNS_LABEL contract. Its own docstring states the result:
must only consist of alphanumeric (lower-case a-z, and 0-9) and not exceed 63 characters. It's permitted to have '-' character as long as it's not in the first or last positions.
It violates both halves of that contract.
1. The 63-character cap is applied to the input, not the output.
The hash-and-truncate step runs before the character conversion:
if len(value) >= MAX: # MAX = 63
h = _sha224(value.encode("utf-8")).hexdigest()[:HASH_LEN]
value = "{}-{}".format(h, value[-(MAX - HASH_LEN - 1):])
for ch in value:
...But the conversion grows the string: every upper-case character gets a - inserted before it. A 62-character camelCase name passes the input check and comes out at 70 characters.
There is a len(res) < 62 guard on the two places a - is appended, but it only suppresses the separator — the character itself is still appended unconditionally, so res keeps growing well past 63. The guard also silently corrupts the tail of the name: ...SweepStage becomes ...sweepstage, with the separator dropped.
2. Consecutive separators can leave a trailing -.
Each of _, -, . appends a - without checking whether the previous character is already a -, and only a single trailing - is stripped at the end. Two adjacent separators at the end of a name therefore produce a label ending in -, which is not a valid DNS_LABEL.
Why it matters: _dnsify is applied to every node ID in flytekit/tools/translator.py, to node_name overrides in flytekit/core/node.py, and to eager execution names in flytekit/core/worker_queue.py. Nothing downstream re-validates length or shape, so an over-long or trailing-- identifier is serialized into the workflow spec and registered as-is. This is the same class of problem as #7607.
Expected behavior
_dnsify returns a valid DNS_LABEL — at most 63 characters, lower-case alphanumerics and -, never leading or trailing with - — for every input.
Additional context to reproduce
from flytekit import task, workflow
from flytekit.core.utils import _dnsify
@task
def t(x: int) -> int:
return x
NAME = "TrainImageClassifierOnLargeDatasetWithHyperparameterSweepStage" # 62 chars
@workflow
def wf(x: int) -> int:
return t(x=x).with_overrides(node_name=NAME)
node_id = wf.nodes[0].id
print(len(node_id), node_id)
# 70 train-image-classifier-on-large-dataset-with-hyperparameter-sweepstage
# ^^^^^^^^^^^ separator dropped
# Length, directly:
print(len(_dnsify("A" * 70))) # 88
print(len(_dnsify("aB" * 40))) # 80
print(len(_dnsify("MyTaskName" * 10))) # 75
# Trailing '-':
print(repr(_dnsify("test.."))) # 'test-' <- invalid DNS_LABEL
print(repr(_dnsify("a.."))) # 'a-' <- invalid DNS_LABELA fuzz sweep over 300k random inputs drawn from [A-Za-z0-9_-.$ ] produces 431 outputs that are not valid DNS_LABELs (over-length or trailing -) on current master, and 0 after the fix.
Screenshots
No response
Are you sure this issue hasn't been raised already?
- Yes
Have you read the Code of Conduct?
- Yes
Source: flyteorg/flyte