Skip to content

Commit 49cd3cb

Browse files
committed
fix(replication): Normalise CORE_URL comparison in isLocalHarbor (#873)
Signed-off-by: Prasanth Baskar <prasanth@8gears.com> (cherry picked from commit 51dda34) Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
1 parent dde5447 commit 49cd3cb

2 files changed

Lines changed: 82 additions & 1 deletion

File tree

src/pkg/reg/adapter/harbor/base/adapter.go

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ package base
1717
import (
1818
"fmt"
1919
"net/http"
20+
neturl "net/url"
2021
"os"
2122
"strconv"
2223
"strings"
@@ -317,8 +318,48 @@ type Project struct {
317318
RegistryID int64 `json:"registry_id"`
318319
}
319320

321+
// isLocalHarbor reports whether url points at this Harbor instance. Core and
322+
// jobservice each read their own CORE_URL, so the two may differ by an
323+
// explicit default port; compare normalized URLs instead of raw strings.
320324
func isLocalHarbor(url string) bool {
321-
return url == os.Getenv("CORE_URL")
325+
coreURL := os.Getenv("CORE_URL")
326+
if url == coreURL {
327+
return true
328+
}
329+
u, err := neturl.Parse(url)
330+
if err != nil || !bareURL(u) {
331+
return false
332+
}
333+
core, err := neturl.Parse(coreURL)
334+
if err != nil || !bareURL(core) {
335+
return false
336+
}
337+
return strings.EqualFold(u.Scheme, core.Scheme) &&
338+
strings.EqualFold(hostPort(u), hostPort(core)) &&
339+
strings.TrimSuffix(u.Path, "/") == strings.TrimSuffix(core.Path, "/")
340+
}
341+
342+
// bareURL reports whether u is a plain scheme://host[:port][/path] URL, the
343+
// only shape isLocalHarbor normalizes: userinfo, query, or fragment must
344+
// match exactly, and a schemeless string parses entirely into Path.
345+
func bareURL(u *neturl.URL) bool {
346+
return u.Scheme != "" && u.Host != "" &&
347+
u.User == nil && u.RawQuery == "" && u.Fragment == ""
348+
}
349+
350+
// hostPort returns host:port with the scheme's default port made explicit,
351+
// the inverse of the default-port strip in v2auth's match().
352+
func hostPort(u *neturl.URL) string {
353+
port := u.Port()
354+
if port == "" {
355+
switch strings.ToLower(u.Scheme) {
356+
case "http":
357+
port = "80"
358+
case "https":
359+
port = "443"
360+
}
361+
}
362+
return u.Hostname() + ":" + port
322363
}
323364

324365
// check whether the current process is running inside core

src/pkg/reg/adapter/harbor/base/adapter_test.go

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -315,3 +315,43 @@ func TestListProjects(t *testing.T) {
315315
require.Equal(t, "p1", projects[0].Name)
316316
require.Equal(t, "p2", projects[1].Name)
317317
}
318+
319+
func TestIsLocalHarbor(t *testing.T) {
320+
cases := []struct {
321+
name string
322+
coreURL string
323+
url string
324+
want bool
325+
}{
326+
{"identical", "http://harbor-core", "http://harbor-core", true},
327+
// The 2.15.8 field failure: an extraEnv pinned the default port on
328+
// core's CORE_URL only, and private replication answered 401.
329+
{"core pins the port through extraEnv, jobservice does not", "http://harbor-core", "http://harbor-core:80", true},
330+
{"jobservice pins the port through extraEnv, core does not", "http://harbor-core:80", "http://harbor-core", true},
331+
{"explicit https default port on the registry URL", "https://harbor-core", "https://harbor-core:443", true},
332+
{"explicit https default port on CORE_URL", "https://harbor-core:443", "https://harbor-core", true},
333+
{"trailing slash", "http://harbor-core", "http://harbor-core/", true},
334+
{"uppercase host", "http://harbor-core", "http://HARBOR-CORE", true},
335+
{"non-default port on both", "http://harbor-core:8080", "http://harbor-core:8080", true},
336+
{"different host", "http://harbor-core", "http://other-harbor", false},
337+
{"different non-default port", "http://harbor-core:8080", "http://harbor-core:8081", false},
338+
{"http default port against non-default", "http://harbor-core", "http://harbor-core:8080", false},
339+
{"different scheme", "http://harbor-core", "https://harbor-core", false},
340+
{"https default port is not http default port", "https://harbor-core", "http://harbor-core:443", false},
341+
{"different path", "http://harbor-core", "http://harbor-core/registry", false},
342+
{"unparseable registry URL", "http://harbor-core", "http://[::1", false},
343+
{"schemeless registry URL", "http://harbor-core", "harbor-core", false},
344+
{"empty CORE_URL against schemeless URL", "", "harbor-core", false},
345+
{"empty CORE_URL against absolute URL", "", "http://harbor-core", false},
346+
{"userinfo only matches exactly", "http://harbor-core", "http://user:pw@harbor-core", false},
347+
{"query only matches exactly", "http://harbor-core", "http://harbor-core?x=1", false},
348+
{"identical userinfo matches through the exact compare", "http://user:pw@harbor-core", "http://user:pw@harbor-core", true},
349+
}
350+
351+
for _, tc := range cases {
352+
t.Run(tc.name, func(t *testing.T) {
353+
t.Setenv("CORE_URL", tc.coreURL)
354+
assert.Equal(t, tc.want, isLocalHarbor(tc.url))
355+
})
356+
}
357+
}

0 commit comments

Comments
 (0)