Skip to content

Commit 3ec5a68

Browse files
fix(build): tag regex should allow ports (#3196)
Update the regex and add test cases. (There are some xfails here for cases that the regex is not currently handling. It's too strict for IPv6 domains at the moment.) Closes: #3195 Related: opencontainers/distribution-spec#498 Signed-off-by: Sven Kieske <kieske@osism.tech> Signed-off-by: Milas Bowman <milas.bowman@docker.com> Co-authored-by: Milas Bowman <milas.bowman@docker.com>
1 parent 6ceb082 commit 3ec5a68

File tree

2 files changed

+53
-5
lines changed

2 files changed

+53
-5
lines changed

docker/utils/build.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,9 @@
1010

1111
_SEP = re.compile('/|\\\\') if IS_WINDOWS_PLATFORM else re.compile('/')
1212
_TAG = re.compile(
13-
r"^[a-z0-9]+((\.|_|__|-+)[a-z0-9]+)*(\/[a-z0-9]+((\.|_|__|-+)[a-z0-9]+)*)*" \
14-
+ "(:[a-zA-Z0-9_][a-zA-Z0-9._-]{0,127})?$"
13+
r"^[a-z0-9]+((\.|_|__|-+)[a-z0-9]+)*"
14+
r"(?::[0-9]+)?(/[a-z0-9]+((\.|_|__|-+)[a-z0-9]+)*)*"
15+
r"(:[a-zA-Z0-9_][a-zA-Z0-9._-]{0,127})?$"
1516
)
1617

1718

tests/unit/utils_build_test.py

Lines changed: 50 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,10 @@
66
import tempfile
77
import unittest
88

9+
import pytest
910

1011
from docker.constants import IS_WINDOWS_PLATFORM
11-
from docker.utils import exclude_paths, tar
12-
13-
import pytest
12+
from docker.utils import exclude_paths, tar, match_tag
1413

1514
from ..helpers import make_tree
1615

@@ -489,3 +488,51 @@ def test_tar_directory_link(self):
489488
assert member in names
490489
assert 'a/c/b' in names
491490
assert 'a/c/b/utils.py' not in names
491+
492+
493+
# selected test cases from https://github.com/distribution/reference/blob/8507c7fcf0da9f570540c958ea7b972c30eeaeca/reference_test.go#L13-L328
494+
@pytest.mark.parametrize("tag,expected", [
495+
("test_com", True),
496+
("test.com:tag", True),
497+
# N.B. this implicitly means "docker.io/library/test.com:5000"
498+
# i.e. the `5000` is a tag, not a port here!
499+
("test.com:5000", True),
500+
("test.com/repo:tag", True),
501+
("test:5000/repo", True),
502+
("test:5000/repo:tag", True),
503+
("test:5000/repo", True),
504+
("", False),
505+
(":justtag", False),
506+
("Uppercase:tag", False),
507+
("test:5000/Uppercase/lowercase:tag", False),
508+
("lowercase:Uppercase", True),
509+
# length limits not enforced
510+
pytest.param("a/"*128 + "a:tag", False, marks=pytest.mark.xfail),
511+
("a/"*127 + "a:tag-puts-this-over-max", True),
512+
("aa/asdf$$^/aa", False),
513+
("sub-dom1.foo.com/bar/baz/quux", True),
514+
("sub-dom1.foo.com/bar/baz/quux:some-long-tag", True),
515+
("b.gcr.io/test.example.com/my-app:test.example.com", True),
516+
("xn--n3h.com/myimage:xn--n3h.com", True),
517+
("foo_bar.com:8080", True),
518+
("foo/foo_bar.com:8080", True),
519+
("192.168.1.1", True),
520+
("192.168.1.1:tag", True),
521+
("192.168.1.1:5000", True),
522+
("192.168.1.1/repo", True),
523+
("192.168.1.1:5000/repo", True),
524+
("192.168.1.1:5000/repo:5050", True),
525+
# regex does not properly handle ipv6
526+
pytest.param("[2001:db8::1]", False, marks=pytest.mark.xfail),
527+
("[2001:db8::1]:5000", False),
528+
pytest.param("[2001:db8::1]/repo", True, marks=pytest.mark.xfail),
529+
pytest.param("[2001:db8:1:2:3:4:5:6]/repo:tag", True, marks=pytest.mark.xfail),
530+
pytest.param("[2001:db8::1]:5000/repo", True, marks=pytest.mark.xfail),
531+
pytest.param("[2001:db8::1]:5000/repo:tag", True, marks=pytest.mark.xfail),
532+
pytest.param("[2001:db8::]:5000/repo", True, marks=pytest.mark.xfail),
533+
pytest.param("[::1]:5000/repo", True, marks=pytest.mark.xfail),
534+
("[fe80::1%eth0]:5000/repo", False),
535+
("[fe80::1%@invalidzone]:5000/repo", False),
536+
])
537+
def test_match_tag(tag: str, expected: bool):
538+
assert match_tag(tag) == expected

0 commit comments

Comments
 (0)