Skip to content

[BUG] flytekit: _dnsify can return node IDs over 63 characters or ending in '-', violating the DNS_LABEL contract #7994

Description

@Daksha1611

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_LABEL

A 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

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions