@@ -4,6 +4,11 @@ module UriUtils
4
4
SSH_REGEX = %r{ \A (?:ssh://)? git@ .+? : .+? \. git \z }x
5
5
GIT_REGEX = %r{ \A git:// .+? : .+? \. git \z }x
6
6
DOCKER_INDEX_SERVER = 'docker.io' . freeze
7
+ DOCKER_PATH_REGEX = %r{\A [a-z0-9_\- \. \/ ]{2,255}\Z }
8
+ DOCKER_TAG_REGEX = /[a-zA-Z0-9_\- \. ]{1,128}/
9
+ DOCKER_DIGEST_REGEX = /sha256:[a-z0-9]{64}/
10
+ DOCKER_TAG_DIGEST_REGEX = Regexp . new ( "\\ A(#{ DOCKER_TAG_REGEX . source } |
11
+ (#{ DOCKER_TAG_REGEX . source } @#{ DOCKER_DIGEST_REGEX . source } ) | #{ DOCKER_DIGEST_REGEX . source } )\\ Z" , Regexp ::EXTENDED )
7
12
8
13
class InvalidDockerURI < StandardError ; end
9
14
@@ -62,13 +67,20 @@ def self.parse_docker_uri(docker_uri)
62
67
end
63
68
64
69
path = 'library/' + path if ( official_docker_registry ( name_parts [ 0 ] ) || missing_registry ( name_parts ) ) && path . exclude? ( '/' )
70
+ path , tag_digest = parse_docker_tag_digest_from_path ( path )
65
71
66
- path , tag = parse_docker_repository_tag ( path )
72
+ raise InvalidDockerURI . new "Invalid image name [#{ path } ]" unless DOCKER_PATH_REGEX =~ path
73
+ raise InvalidDockerURI . new "Invalid image tag [#{ tag_digest } ]" if tag_digest && DOCKER_TAG_DIGEST_REGEX !~ tag_digest
67
74
68
- raise InvalidDockerURI . new "Invalid image name [#{ path } ]" unless %r{\A [a-z0-9_\- \. \/ ]{2,255}\Z } =~ path
69
- raise InvalidDockerURI . new "Invalid image tag [#{ tag } ]" if tag && !( /\A (([a-zA-Z0-9_\- \. ]{1,128})|(([a-zA-Z0-9_\- \. ]{0,128})(@sha256:[a-z0-9]{64})))\Z / =~ tag )
75
+ # if only sha256 presented, we add hash value as fragment to the uri,
76
+ # since the ruby uri parser confuses because of second ':' in uri's path part.
77
+ if tag_digest && tag_digest . start_with? ( 'sha256:' )
78
+ _ , hash_value = tag_digest . split ( ':' )
79
+ path += '@sha256'
80
+ tag_digest = hash_value
81
+ end
70
82
71
- [ host , path , tag ]
83
+ [ host , path , tag_digest ]
72
84
end
73
85
74
86
private_class_method def self . official_docker_registry ( host )
@@ -81,11 +93,29 @@ def self.parse_docker_uri(docker_uri)
81
93
( host . exclude? ( '.' ) && host . exclude? ( ':' ) && host != 'localhost' )
82
94
end
83
95
84
- private_class_method def self . parse_docker_repository_tag ( path )
85
- path , tag = path . split ( /(?=@)|:/ , 2 )
96
+ private_class_method def self . parse_docker_tag_digest_from_path ( path )
97
+ # Split path into base path and digest if digest is present (after '@')
98
+ base_path , digest = path . split ( '@' , 2 )
99
+
100
+ if digest
101
+ # If digest is present and base_path contains a tag (':'), split it
102
+ if base_path . include? ( ':' )
103
+ base_path , tag = base_path . split ( ':' , 2 )
104
+ # Return path and combined tag@digest
105
+ return [ base_path , "#{ tag } @#{ digest } " ]
106
+ end
107
+
108
+ # Return path and digest if no tag present
109
+ return [ base_path , digest ]
110
+ end
111
+
112
+ # No digest present, check for tag
113
+ base_path , tag = base_path . split ( ':' , 2 )
86
114
87
- return [ path , tag ] unless tag && tag . include? ( '/' )
115
+ # If tag is present but looks like a path segment (contains '/'), treat as no tag
116
+ return [ base_path , 'latest' ] if tag &.include? ( '/' )
88
117
89
- [ path , 'latest' ]
118
+ # Return path and tag (or nil if no tag)
119
+ [ base_path , tag ]
90
120
end
91
121
end
0 commit comments