Skip to content

Raise DockerException for invalid ports in parse_host()#3421

Open
semx wants to merge 1 commit into
docker:mainfrom
semx:fix-parse-host-invalid-port
Open

Raise DockerException for invalid ports in parse_host()#3421
semx wants to merge 1 commit into
docker:mainfrom
semx:fix-parse-host-invalid-port

Conversation

@semx

@semx semx commented Jul 13, 2026

Copy link
Copy Markdown

Problem

parse_host() mishandles invalid ports in the tcp/ssh branch.

1. A raw ValueError escapes instead of DockerException.
The port is read via parsed_url.port, and urllib's .port property raises ValueError for an out-of-range or non-numeric port. That error propagates unwrapped, even though the rest of parse_host() — and the invalid_hosts test contract in tests/unit/utils_test.py — guarantees invalid input raises DockerException. A user with a malformed DOCKER_HOST gets an opaque ValueError that except DockerException won't catch.

>>> from docker.utils import parse_host
>>> parse_host('tcp://host:99999')
ValueError: Port out of range 0-65535
>>> parse_host('tcp://host:notaport')
ValueError: Port could not be cast to integer value as 'notaport'

2. ssh://host:0 silently produces a malformed result.
parsed_url.port or 0 collapses an explicit :0 into the "no port given" case. For ssh the code then rebuilds netloc = f'{parsed_url.netloc}:{port}', but parsed_url.netloc still ends in :0, so :22 is appended to it:

>>> parse_host('ssh://host:0')
'ssh://host:0:22'

tcp://host:0 is already rejected with DockerException, so the two protocols were inconsistent.

Fix

Wrap the parsed_url.port access, distinguish a missing port (None) from an explicitly invalid one, and reject port 0 for both protocols:

try:
    port = parsed_url.port
except ValueError as e:
    raise errors.DockerException(f'Invalid bind address format: {addr}') from e
if port is None:
    if proto != 'ssh':
        raise errors.DockerException(f'Invalid bind address format: port is required: {addr}')
    port = 22
    netloc = f'{parsed_url.netloc}:{port}'
elif port <= 0:
    raise errors.DockerException(f'Invalid bind address format: port is required: {addr}')

After the change:

tcp://host:99999     -> DockerException: Invalid bind address format
tcp://host:notaport  -> DockerException: Invalid bind address format
ssh://host:0         -> DockerException: Invalid bind address format: port is required

Valid hosts are unaffected (ssh://hostssh://host:22, tcp://host:2375http://host:2375, empty host → 127.0.0.1, IPv6, etc.).

Tests

Added tcp://host:0, ssh://host:0, tcp://host:99999 and tcp://host:notaport to the invalid_hosts list in tests/unit/utils_test.py. The full utils_test.py suite (73 tests) passes.

parse_host() read the port via parsed_url.port, whose urllib property
raises ValueError for out-of-range or non-numeric ports. That error
escaped unwrapped, so a bad DOCKER_HOST surfaced as a raw ValueError
instead of the DockerException the rest of parse_host() (and the
invalid_hosts test contract) guarantees.

Additionally, 'parsed_url.port or 0' collapsed an explicit ':0' into
the no-port case. For ssh that appended ':22' to a netloc that already
ended in ':0', producing a malformed 'ssh://host:0:22'; tcp://host:0
was already rejected, so the two protocols were inconsistent.

Wrap the port access, distinguish a missing port (None) from an
explicitly invalid one, and reject port 0 for both protocols.

Signed-off-by: Sergey Sannikov <sergey@sannikov.dev>
@semx semx force-pushed the fix-parse-host-invalid-port branch from 7945fee to 2b67132 Compare July 13, 2026 15:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant