Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
38fcc0d
test compliance with fips140-3
jUDASmILE Aug 25, 2026
a691a4d
bump golang to v1.26.7 and refresh base image (#23797)
stonezdj Aug 27, 2026
5952917
Bump trivy(0.74.0) and trivy adapter(0.39.0) (#23796)
stonezdj Aug 27, 2026
c143fe0
chore(deps): upgrade Go dependencies to fix CVEs (#23802)
stonezdj Aug 31, 2026
de1c751
(cherry-pick) fix: system_cve testcase failing at end of month (#23817)
stonezdj Sep 1, 2026
60e6a94
(cherry-pick): bump golang.org/x/crypto to v0.55.0 (#23825)
stonezdj Sep 4, 2026
a04aedc
chore(deps-dev): bump express and @types/express in /src/portal (#23739)
dependabot[bot] Sep 4, 2026
1f689e9
[cherry-pick]fix: correct grammar and typos in user-facing error mess…
wy65701436 Sep 6, 2026
9e3825c
[cherry pick] Remove hardcoded credentials and prevernt passwords in …
jUDASmILE Sep 7, 2026
5485a0f
(cherry-pick) make hostname comparisons case-insensitive (#23833)
stonezdj Sep 7, 2026
60db8bc
Bump up distribution version (#23811)
stonezdj Sep 8, 2026
92ee067
[cherry pick] improve HTTP client request logging and credential str……
jUDASmILE Sep 9, 2026
9f221b3
chore(deps): upgrade Go dependencies to fix CVEs (#23877)
stonezdj Sep 9, 2026
b8efce8
[cherry pick]refactor: sanitize connection details in logs and enhanc…
jUDASmILE Sep 10, 2026
ddaf08b
ci: Make go mod tidy work without generated swagger code so Dependabo…
Vad1mo Sep 10, 2026
d16d32c
Bump up distribution version (#23890)
stonezdj Sep 10, 2026
caac45e
(cherry-pick) make endpoint handling and validation case-insensitive …
stonezdj Sep 10, 2026
ded0848
chore(deps): bump github.com/go-asn1-ber/asn1-ber from 1.5.8-0.202504…
dependabot[bot] Sep 15, 2026
766a3fe
chore(deps): bump github.com/go-openapi/runtime from 0.32.2 to 0.33.2…
dependabot[bot] Sep 15, 2026
85ba316
chore(deps): bump marked from 18.0.9 to 18.0.12 in /src/portal in the…
dependabot[bot] Sep 15, 2026
1b2b056
chore(deps): bump github.com/aws/aws-sdk-go-v2/credentials from 1.19.…
dependabot[bot] Sep 15, 2026
6e27c63
chore(deps): bump golang.org/x/sync from 0.22.0 to 0.23.0 in /src (#2…
dependabot[bot] Sep 15, 2026
0328bcc
chore(deps): bump github.com/jackc/pgx/v5 from 5.10.0 to 5.11.0 in /s…
dependabot[bot] Sep 15, 2026
b70827e
chore(deps): bump the patch-updates group across 1 directory with 2 u…
dependabot[bot] Sep 16, 2026
89a8f18
chore(deps-dev): bump karma-chrome-launcher from 3.1.1 to 3.2.0 in /s…
dependabot[bot] Sep 16, 2026
59636bb
chore(deps): bump the kubernetes group across 1 directory with 2 upda…
dependabot[bot] Sep 16, 2026
ad4d97b
chore(deps-dev): bump webpack from 5.107.2 to 5.110.3 in /src/portal/…
dependabot[bot] Sep 16, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .buildbaselog
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@
* Add date here... Add signature here...
- Add your reason here...

* Aug 27 2026 <stone.zhang@broadcom.com>
- Refresh base image

* Jul 01 2026 <stone.zhang@broadcom.com>
- Refresh base image

Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/CI.yml
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ jobs:
- name: Set up Go 1.26
uses: actions/setup-go@v5
with:
go-version: 1.26.4
go-version: 1.26.7
id: go
- uses: actions/checkout@v6
with:
Expand Down
16 changes: 14 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,20 @@ src/portal/cypress/screenshots
**/robotvars.py
src/core/conf/app.conf

src/server/v2.0/models/
src/server/v2.0/restapi/
# go-swagger output ("make gen_apis"). Everything is ignored except the
# zz_generated_placeholder.go files, which are committed so that the packages
# resolve before code generation has run. The patterns are written level by
# level because git cannot re-include a file whose parent directory is excluded.
src/server/v2.0/models/*
!src/server/v2.0/models/zz_generated_placeholder.go
src/server/v2.0/restapi/*
!src/server/v2.0/restapi/zz_generated_placeholder.go
!src/server/v2.0/restapi/operations/
src/server/v2.0/restapi/operations/*
!src/server/v2.0/restapi/operations/zz_generated_placeholder.go
!src/server/v2.0/restapi/operations/*/
src/server/v2.0/restapi/operations/*/*
!src/server/v2.0/restapi/operations/*/zz_generated_placeholder.go
.editorconfig

harborclient/
Expand Down
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,7 @@ Harbor backend is written in [Go](http://golang.org/). If you don't have a Harbo
| 2.12 | 1.23.2 |
| 2.13 | 1.23.8 |
| 2.14 | 1.24.6 |
| 2.15 | 1.26.4 |
| 2.15 | 1.26.7 |


Ensure your GOPATH and PATH have been configured in accordance with the Go environment instructions.
Expand Down
44 changes: 38 additions & 6 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -125,12 +125,12 @@ PREPARE_VERSION_NAME=versions

#versions
REGISTRYVERSION=v2.8.3-patch-redis
TRIVYVERSION=v0.72.0
TRIVYADAPTERVERSION=v0.38.0
TRIVYVERSION=v0.74.0
TRIVYADAPTERVERSION=v0.39.0
NODEBUILDIMAGE=node:22.22.3

# version of registry for pulling the source code
REGISTRY_SRC_TAG=v2.8.3-harbor.1
REGISTRY_SRC_TAG=v2.8.3-harbor.2-rc.5
# source of upstream distribution code
DISTRIBUTION_SRC=https://github.com/goharbor/distribution.git

Expand Down Expand Up @@ -165,7 +165,7 @@ GOINSTALL=$(GOCMD) install
GOTEST=$(GOCMD) test
GODEP=$(GOTEST) -i
GOFMT=gofmt -w
GOBUILDIMAGE=golang:1.26.4
GOBUILDIMAGE=golang:1.26.7
GOBUILDPATHINCONTAINER=/harbor

# go build
Expand Down Expand Up @@ -332,9 +332,28 @@ SWAGGER_IMAGE_BUILD_CMD=${DOCKERBUILD} -f ${TOOLSPATH}/swagger/Dockerfile --buil
# $3 the name of the application
define swagger_generate_server
@echo "generate all the files for API from $(1)"
@rm -rf $(2)/{models,restapi}
@# Drop the previously generated files but keep the committed
@# zz_generated_placeholder.go of each package (see .gitignore).
@find $(2)/models $(2)/restapi -type f ! -name 'zz_generated_placeholder.go' -delete
@mkdir -p $(2)
@$(SWAGGER_GENERATE_SERVER) -f $(1) -A $(3) --target $(2)
@missing=; orphaned=; \
for d in $$(find $(2)/models $(2)/restapi -type d); do \
if [ ! -f "$$d/zz_generated_placeholder.go" ]; then \
missing="$$missing $$d"; \
elif [ -z "$$(find $$d -maxdepth 1 -name '*.go' ! -name 'zz_generated_placeholder.go')" ]; then \
orphaned="$$orphaned $$d"; \
fi; \
done; \
if [ -n "$$missing" ]; then \
echo "error: generated package without a committed placeholder:$$missing"; \
echo "copy a zz_generated_placeholder.go into it, fix the package clause and commit it"; \
fi; \
if [ -n "$$orphaned" ]; then \
echo "error: placeholder for a package go-swagger no longer generates:$$orphaned"; \
echo "remove the orphaned placeholder"; \
fi; \
if [ -n "$$missing$$orphaned" ]; then exit 1; fi
endef

gen_apis:
Expand Down Expand Up @@ -490,7 +509,20 @@ package_offline: check_buildinstaller update_prepare_version compile build
@rm -rf $(HARBORPKG)
@echo "Done."

go_check: gen_apis mocks_check misspell commentfmt lint
go_check: tidy_check gen_apis mocks_check misspell commentfmt lint

# Dependabot runs "go mod tidy" on a plain checkout, so it must be a no-op there.
# This runs before gen_apis, on a tree that still has no generated code, so that a
# change to the import set of the generated code cannot silently stale the blank
# imports in src/server/v2.0/restapi/zz_generated_placeholder.go.
tidy_check:
@echo checking go mod tidy on a checkout without generated code...
@cd ./src/; if ! go mod tidy -diff; then \
echo "go mod tidy is not a no-op here, so Dependabot cannot update any Go module."; \
echo "if the go-swagger import set changed, update the blank imports in"; \
echo "src/server/v2.0/restapi/zz_generated_placeholder.go to match."; \
exit 1; \
fi

commentfmt:
@echo checking comment format...
Expand Down
14 changes: 14 additions & 0 deletions src/.golangci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,20 @@ linters:
- revive
text: "var-naming: avoid package names that conflict with Go standard library package names"
path: "(common/http/|lib/errors/|lib/http/)"
# The packages under server/v2.0/restapi/operations are named after the
# swagger tags in api/v2.0/swagger.yaml, some of which contain underscores.
# The generated files are skipped as generated code, the committed
# zz_generated_placeholder.go files are not.
- linters:
- revive
text: "var-naming: don't use an underscore in package name"
path: "server/v2\\.0/restapi/operations/.*/zz_generated_placeholder\\.go$"
# Same placeholders: "scanner" and "user" are swagger tag names that
# happen to match standard library package names.
- linters:
- revive
text: "var-naming: avoid package names that conflict with Go standard library package names"
path: "server/v2\\.0/restapi/operations/.*/zz_generated_placeholder\\.go$"
paths:
- third_party$
- builtin$
Expand Down
2 changes: 1 addition & 1 deletion src/common/dao/testutils.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ func PrepareTestForPostgresSQL() {
},
}

log.Infof("POSTGRES_HOST: %s, POSTGRES_USR: %s, POSTGRES_PORT: %d, POSTGRES_PWD: %s\n", dbHost, dbUser, dbPort, dbPassword)
log.Infof("POSTGRES_HOST: %s, POSTGRES_USR: %s, POSTGRES_PORT: %d\n", dbHost, dbUser, dbPort)
o = initDatabaseForTest(database)
}

Expand Down
5 changes: 2 additions & 3 deletions src/common/http/tls.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,14 +66,13 @@ func NewServerTLSConfig() *tls.Config {
PreferServerCipherSuites: true,
CurvePreferences: []tls.CurveID{
tls.CurveP256,
tls.X25519,
tls.CurveP384,
tls.CurveP521,
},
MinVersion: tls.VersionTLS12,
CipherSuites: []uint16{
tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,
tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,
tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
},
Expand Down
2 changes: 1 addition & 1 deletion src/common/security/secret/context.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ func (s *SecurityContext) IsAuthenticated() bool {
}
valid := s.store.IsValid(s.secret)
if !valid {
log.Debugf("invalid secret: %s", s.secret)
log.Debug("invalid secret")
}

return valid
Expand Down
24 changes: 20 additions & 4 deletions src/common/utils/encrypt.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ package utils
import (
"crypto/aes"
"crypto/cipher"
"crypto/fips140"
"crypto/pbkdf2"
"crypto/rand"
"crypto/sha1" // nolint:gosec // G505: blocklisted import kept for legacy PBKDF2-SHA1 password verification only
Expand Down Expand Up @@ -61,7 +62,13 @@ const (
// PBKDF2-HMAC-SHA256 with a high iteration count instead (see
// pkg/user/manager.go). Please don't use SHA1 to hash any new secrets.
var HashAlg = map[string]func() hash.Hash{
SHA1: sha1.New, // nolint:gosec // G401/G505: weak hash kept only for legacy PBKDF2 password verification
SHA1: func() hash.Hash {
var h hash.Hash
fips140.WithoutEnforcement(func() {
h = sha1.New()
})
return h
}, // nolint:gosec // G401/G505: weak hash kept only for legacy PBKDF2 password verification
SHA256: sha256.New,
PBKDF2SHA256: sha256.New,
}
Expand All @@ -88,7 +95,10 @@ func pbkdf2Params(version string) (func() hash.Hash, int) {
// Encrypt encrypts the content with salt
func Encrypt(content string, salt string, encryptAlg string) string {
alg, iterations := pbkdf2Params(encryptAlg)
key, _ := pbkdf2.Key(alg, content, []byte(salt), iterations, 16)
var key []byte
fips140.WithoutEnforcement(func() {
key, _ = pbkdf2.Key(alg, content, []byte(salt), iterations, 16)
})
return fmt.Sprintf("%x", key)
}

Expand All @@ -114,7 +124,10 @@ func ReversibleEncrypt(str, key string) (string, error) {
return "", err
}

cfb := cipher.NewCFBEncrypter(block, iv)
var cfb cipher.Stream
fips140.WithoutEnforcement(func() {
cfb = cipher.NewCFBEncrypter(block, iv)
})
cfb.XORKeyStream(cipherText[aes.BlockSize:], []byte(str))
encrypted := EncryptHeaderV1 + base64.StdEncoding.EncodeToString(cipherText)
return encrypted, nil
Expand Down Expand Up @@ -154,7 +167,10 @@ func decryptAES(str, key string) (string, error) {

iv := cipherText[:aes.BlockSize]
cipherText = cipherText[aes.BlockSize:]
cfb := cipher.NewCFBDecrypter(block, iv)
var cfb cipher.Stream
fips140.WithoutEnforcement(func() {
cfb = cipher.NewCFBDecrypter(block, iv)
})
cfb.XORKeyStream(cipherText, cipherText)
return string(cipherText), nil
}
2 changes: 1 addition & 1 deletion src/common/utils/test/database.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ func InitDatabaseFromEnv() {
},
}

log.Infof("POSTGRES_HOST: %s, POSTGRES_USR: %s, POSTGRES_PORT: %d, POSTGRES_PWD: %s\n", dbHost, dbUser, dbPort, dbPassword)
log.Infof("POSTGRES_HOST: %s, POSTGRES_USR: %s, POSTGRES_PORT: %d\n", dbHost, dbUser, dbPort)

if err := dao.InitDatabase(database); err != nil {
log.Fatalf("failed to init database : %v", err)
Expand Down
43 changes: 38 additions & 5 deletions src/common/utils/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,26 @@ func ParseEndpoint(endpoint string) (*url.URL, error) {
endpoint = "http://" + endpoint
}

return url.ParseRequestURI(endpoint)
u, err := url.ParseRequestURI(endpoint)
if err == nil && u != nil {
u.Host = strings.ToLower(u.Host)
}
return u, err
}

// EqualURL checks whether two URLs are equal, with case-insensitive host matching per RFC 1035.
func EqualURL(rawURL1, rawURL2 string) bool {
if rawURL1 == rawURL2 {
return true
}
u1, err1 := url.Parse(rawURL1)
u2, err2 := url.Parse(rawURL2)
if err1 != nil || err2 != nil {
return false
}
u1.Host = strings.ToLower(u1.Host)
u2.Host = strings.ToLower(u2.Host)
return u1.String() == u2.String()
}

// ParseRepository splits a repository into two parts: project and rest
Expand All @@ -66,19 +85,33 @@ func ParseRepository(repository string) (project, rest string) {
return
}

// GenerateRandomStringWithLen generates a random string with length
func GenerateRandomStringWithLen(length int) string {
// GenerateRandomStringWithLenAndError generates a random string with length or returns an error if entropy cannot be read
func GenerateRandomStringWithLenAndError(length int) (string, error) {
const chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
l := len(chars)
result := make([]byte, length)
_, err := rand.Read(result)
if err != nil {
log.Warningf("Error reading random bytes: %v", err)
return "", fmt.Errorf("failed to read random bytes: %w", err)
}
for i := range length {
result[i] = chars[int(result[i])%l]
}
return string(result)
return string(result), nil
}

// GenerateRandomStringOrError generates a random string with 32 byte length or returns an error if entropy cannot be read
func GenerateRandomStringOrError() (string, error) {
return GenerateRandomStringWithLenAndError(32)
}

// GenerateRandomStringWithLen generates a random string with length
func GenerateRandomStringWithLen(length int) string {
str, err := GenerateRandomStringWithLenAndError(length)
if err != nil {
log.Warningf("Error reading random bytes: %v", err)
}
return str
}

// GenerateRandomString generate a random string with 32 byte length
Expand Down
43 changes: 43 additions & 0 deletions src/common/utils/utils_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@ func TestParseEndpoint(t *testing.T) {
{"ftp://example.com", true, ""},
{"http://example.com", false, "http://example.com"},
{"https://example.com", false, "https://example.com"},
{"http://EXAMPLE.COM", false, "http://example.com"},
{"https://EXAMPLE.COM:8080/Path", false, "https://example.com:8080/Path"},
{" MIXED.case.com/path/ ", false, "http://mixed.case.com/path"},
{"http://example!@#!?//#", true, ""},
}

Expand All @@ -51,6 +54,31 @@ func TestParseEndpoint(t *testing.T) {
}
}

func TestEqualURL(t *testing.T) {
cases := []struct {
url1 string
url2 string
expected bool
}{
{"http://example.com:8080/v2", "http://example.com:8080/v2", true},
{"http://EXAMPLE.COM:8080/v2", "http://example.com:8080/v2", true},
{"http://example.com:8080/V2", "http://example.com:8080/v2", false},
{"https://core:8080", "https://CORE:8080", true},
{"http://example.com", "https://example.com", false},
{"http://example.com:8080", "http://example.com:8081", false},
{"http://example.com/foo", "http://example.com/bar", false},
{"://invalid-url-1", "http://example.com", false},
{"http://example.com", "://invalid-url-2", false},
{"http://example.com/A%", "http://example.com/a%", false},
}

for _, c := range cases {
t.Run(c.url1+"_vs_"+c.url2, func(t *testing.T) {
assert.Equal(t, c.expected, EqualURL(c.url1, c.url2))
})
}
}

func TestParseRepository(t *testing.T) {
repository := "library/ubuntu"
project, rest := ParseRepository(repository)
Expand Down Expand Up @@ -155,13 +183,28 @@ func TestGenerateRandomString(t *testing.T) {
}
}

func TestGenerateRandomStringOrError(t *testing.T) {
str, err := GenerateRandomStringOrError()
assert.Nil(t, err)
assert.Equal(t, 32, len(str))
str2, err := GenerateRandomStringOrError()
assert.Nil(t, err)
assert.NotEqual(t, str, str2)
}

func TestGenerateRandomStringWithLen(t *testing.T) {
str := GenerateRandomStringWithLen(16)
if len(str) != 16 {
t.Errorf("Failed to generate ramdom string with fixed length.")
}
}

func TestGenerateRandomStringWithLenAndError(t *testing.T) {
str, err := GenerateRandomStringWithLenAndError(16)
assert.Nil(t, err)
assert.Equal(t, 16, len(str))
}

func TestTestTCPConn(t *testing.T) {
server := httptest.NewServer(nil)
defer server.Close()
Expand Down
2 changes: 1 addition & 1 deletion src/controller/artifact/model_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ func TestUnmarshalJSONWithACCUnknownType(t *testing.T) {
var artifact []Artifact
err := json.Unmarshal(data, &artifact)
assert.NotNil(t, err)
assert.Contains(t, err.Error(), "accessory type not support")
assert.Contains(t, err.Error(), `unsupported accessory type ""`)
}

func TestUnmarshalJSONWithoutACC(t *testing.T) {
Expand Down
Loading
Loading