1
0
mirror of https://github.com/goharbor/harbor.git synced 2026-10-23 10:08:57 +03:00

12917 Commits

Author SHA1 Message Date
stonezdj(Daojun Zhang)
533cfd7659 fix(replication): preserve artifact type for multi-tier accessories (#24200)
* fix(replication): preserve artifact type for multi-tier accessories

In getAccessoryArts, reassigning art when fetching accessory details
shadowed the parent artifact and caused sub-accessories (such as signatures
of an SBOM) to inherit Type "UNKNOWN" from the accessory artifact rather
than the subject artifact's type (e.g. "IMAGE"). Consequently, when a
replication policy configured a resource filter (e.g. "image"), the
sub-accessories were dropped by the artifact type filter.

Fix this by using a separate variable subArt to avoid shadowing art,
and propagate art.Type to subArt so nested accessories inherit the
root subject artifact's type.

Signed-off-by: stonezdj <stonezdj@gmail.com>

* test(replication): cover nested accessory type inheritance

Signed-off-by: stonezdj <stonezdj@gmail.com>

---------

Signed-off-by: stonezdj <stonezdj@gmail.com>
2026-10-23 03:03:30 +00:00
dependabot[bot]
d1a63a9161 chore(deps-dev): bump shell-quote from 1.10.0 to 1.12.0 in /src/portal (#24112)
Bumps [shell-quote](https://github.com/ljharb/shell-quote) from 1.10.0 to 1.12.0.
- [Changelog](https://github.com/ljharb/shell-quote/blob/main/CHANGELOG.md)
- [Commits](https://github.com/ljharb/shell-quote/compare/v1.10.0...v1.12.0)

---
updated-dependencies:
- dependency-name: shell-quote
  dependency-version: 1.12.0
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-10-22 16:59:09 +00:00
dependabot[bot]
65f4244575 chore(deps): bump hono from 4.13.8 to 4.13.13 in /src/portal (#24194)
Bumps [hono](https://github.com/honojs/hono) from 4.13.8 to 4.13.13.
- [Release notes](https://github.com/honojs/hono/releases)
- [Commits](https://github.com/honojs/hono/compare/v4.13.8...v4.13.13)

---
updated-dependencies:
- dependency-name: hono
  dependency-version: 4.13.13
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Orlix <7236111+OrlinVasilev@users.noreply.github.com>
Co-authored-by: Vadim Bauer <vb@container-registry.com>
2026-10-22 16:07:58 +00:00
dependabot[bot]
63d7c8d5ef chore(deps): bump proxy-addr from 2.0.7 to 2.0.8 in /src/portal (#24110)
Bumps [proxy-addr](https://github.com/jshttp/proxy-addr) from 2.0.7 to 2.0.8.
- [Release notes](https://github.com/jshttp/proxy-addr/releases)
- [Changelog](https://github.com/jshttp/proxy-addr/blob/master/HISTORY.md)
- [Commits](https://github.com/jshttp/proxy-addr/compare/v2.0.7...v2.0.8)

---
updated-dependencies:
- dependency-name: proxy-addr
  dependency-version: 2.0.8
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-10-22 13:34:14 +00:00
dependabot[bot]
0fa571cefb chore(deps): bump ip-address from 10.5.0 to 10.7.4 in /src/portal (#24181)
Bumps [ip-address](https://github.com/beaugunderson/ip-address) from 10.5.0 to 10.7.4.
- [Release notes](https://github.com/beaugunderson/ip-address/releases)
- [Commits](https://github.com/beaugunderson/ip-address/compare/v10.5.0...v10.7.4)

---
updated-dependencies:
- dependency-name: ip-address
  dependency-version: 10.7.4
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Orlix <7236111+OrlinVasilev@users.noreply.github.com>
2026-10-22 12:03:57 +00:00
dependabot[bot]
020b3e6c31 chore(deps): bump compression from 1.8.1 to 1.8.2 in /src/portal/app-swagger-ui (#24108)
chore(deps): bump compression in /src/portal/app-swagger-ui

Bumps [compression](https://github.com/expressjs/compression) from 1.8.1 to 1.8.2.
- [Release notes](https://github.com/expressjs/compression/releases)
- [Changelog](https://github.com/expressjs/compression/blob/master/HISTORY.md)
- [Commits](https://github.com/expressjs/compression/compare/v1.8.1...v1.8.2)

---
updated-dependencies:
- dependency-name: compression
  dependency-version: 1.8.2
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-10-22 11:16:14 +00:00
dependabot[bot]
9644fada24 chore(deps): bump shell-quote from 1.10.0 to 1.12.0 in /src/portal/app-swagger-ui (#24106)
chore(deps): bump shell-quote in /src/portal/app-swagger-ui

Bumps [shell-quote](https://github.com/ljharb/shell-quote) from 1.10.0 to 1.12.0.
- [Changelog](https://github.com/ljharb/shell-quote/blob/main/CHANGELOG.md)
- [Commits](https://github.com/ljharb/shell-quote/compare/v1.10.0...v1.12.0)

---
updated-dependencies:
- dependency-name: shell-quote
  dependency-version: 1.12.0
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-10-22 09:59:24 +00:00
Prasanth Baskar
f83b3ae751 chore: Upgrade Casbin To v3 (#24189)
Move the RBAC evaluator from github.com/casbin/casbin v1.9.1 to
github.com/casbin/casbin/v3 v3.11.0. v1 is unmaintained and pulls in the
archived Knetic/govaluate.

v3 returns errors from NewEnforcer, Enforce and policy loading; a
failure now denies the request and is logged. v3 pre-registers
keyMatch2 and never replaces it, so Harbor's matcher is registered as
harborKeyMatch2. v3 parses policy lines as CSV, so the adapter loads
rules as arrays to keep names with quotes or commas working.

Signed-off-by: Prasanth Baskar <prasanth@8gears.com>
2026-10-22 15:02:31 +08:00
Prasanth Baskar
4e07c69cbd chore(deps): bump up Go version to v1.27.3 (#24188)
Fixes GO-2026-6809: crypto/x509 now distrusts compromised intermediate
certificates. Affects go1.27.2, fixed in go1.27.3.

Signed-off-by: Prasanth Baskar <prasanth@8gears.com>
2026-10-21 14:28:50 +05:30
Wang Yan
c1e018787c bump the base version to v2.17.0 (#24187)
Signed-off-by: wang yan <yan-yw.wang@broadcom.com>
2026-10-21 14:46:16 +08:00
stonezdj(Daojun Zhang)
43dfe11b84 chore: bump Trivy, Trivy adapter, and Distribution versions (#24179)
Signed-off-by: stonezdj <stonezdj@gmail.com>
Co-authored-by: Prasanth Baskar <prasanth@8gears.com>
2026-10-20 10:23:18 +00:00
jUDASmILE
8414559ce3 Improve project metadata cache consistency (#24139)
* Improve project metadata cache consistency

Signed-off-by: jUDASmILE <judasmile@gmail.com>

* Regenerate cache iterator mock

Signed-off-by: jUDASmILE <judasmile@gmail.com>

* Match generated cache iterator mock ordering

Signed-off-by: jUDASmILE <judasmile@gmail.com>

* address comments

Signed-off-by: jUDASmILE <judasmile@gmail.com>

* fix ut issue

Signed-off-by: jUDASmILE <judasmile@gmail.com>

---------

Signed-off-by: jUDASmILE <judasmile@gmail.com>
2026-10-20 09:32:46 +00:00
Chlins Zhang
a360e527a9 fix(replication): make the image resource filter take effect again (#24176)
The resource filter stopped filtering artifacts by type when the chart
resource type was removed in #18191, so policies with the image filter
replicated charts and other artifacts as well.

Fixes #24047

Signed-off-by: chlins <chlins.zhang@gmail.com>
2026-10-20 06:46:22 +00:00
Youngju KIM
1e21dd147a Stamp version info into the bundled Trivy adapter binary (#24164)
The adapter is built without -ldflags, so its startup log and
/api/v1/metadata report version "dev", commit "none" and build date
"unknown". Pass the version, commit and date the same way the
upstream release build does.

Signed-off-by: YoungjuKim <fjvbn2003@gmail.com>
2026-10-20 14:00:27 +08:00
Qingyuan Wang
dc64f3b4cf portal: align robot account immutability label (#24171)
Use the established “Tag Immutability” label for the robot-account permission selector in all supported locales.

Fixes #24170

Prepared and tested with Codex assistance; no human review is claimed.

Signed-off-by: NUAAwqy <146524024+NUAAwqy@users.noreply.github.com>
2026-10-19 16:12:09 +00:00
stonezdj(Daojun Zhang)
9e79066f74 chore(deps): bump google.golang.org/grpc to v1.82.2 and golang/x/net to v0.60.0 (#24167)
* chore(deps): bump google.golang.org/grpc to v1.82.2

Signed-off-by: stonezdj <stonezdj@gmail.com>

* chore(deps): bump golang.org/x/net to v0.60.0

Signed-off-by: stonezdj <stonezdj@gmail.com>

---------

Signed-off-by: stonezdj <stonezdj@gmail.com>
2026-10-19 12:33:46 +08:00
stonezdj(Daojun Zhang)
2b982d21c0 test(robot): expect inherited signature for unsigned index child in r… (#24026)
test(robot): expect inherited signature for unsigned index child in replication event test

Commit c3ec2b133e introduced signature inheritance for OCI index children (#23540),
marking unsigned child artifacts under a signed index with the 'signed-inherited' status
in the Portal UI instead of 'color-red' (unsigned).

In 'Test Case - Replication Triggered By Events', image2 (busybox) under the signed
index was asserted with 'Should Not Be Signed', which looked for 'color-red' icon and
failed. Add 'Should Be Inherited Signed' keyword and update the test case to assert
the inherited signature state on image2 under the signed index.

Signed-off-by: stonezdj <stonezdj@gmail.com>
2026-10-16 08:52:27 +00:00
stonezdj(Daojun Zhang)
20f8af029b test(robot): update guest member radio button locator (#24154)
Update guest radio checkbox xpath locator in Project-Members_Elements.robot to match the current DOM structure.

Signed-off-by: stonezdj <stonezdj@gmail.com>
2026-10-16 07:56:40 +00:00
stonezdj(Daojun Zhang)
f1b4e3ccc6 Fix webhook egress allow all (#24126)
fix(webhook): default webhook egress policy level to allow_all

When the webhook egress policy is empty or unset, default to egressAllowAll
to preserve Harbor's default egress behavior and avoid regressions for internal
and test webhook targets.

Signed-off-by: stonezdj <stonezdj@gmail.com>
2026-10-16 07:06:05 +00:00
stonezdj(Daojun Zhang)
1edae4acdb feat(task): aggregate execution error messages and surface task failure details (#24028)
- Record and bubble up failure error messages (including tail of job logs) in Jobservice
- Store status_message on failed task and aggregate task errors into execution status_message
- Expose status_message in swagger ReplicationTask definition and handler
- Display status_message tooltip on replication tasks and execution status in UI portal

Signed-off-by: stonezdj <stonezdj@gmail.com>
2026-10-16 05:21:50 +00:00
stonezdj(Daojun Zhang)
502a51bdec feat(replication): separate event-triggered replication into dedicate… (#24138)
feat(replication): separate event-triggered replication into dedicated queue

Separate replication tasks generated by event-based executions into
a dedicated EVENT_REPLICATION queue in JobService with higher priority
to prevent head-of-line blocking by bulk manual or scheduled replication.

Signed-off-by: stonezdj <stonezdj@gmail.com>
Co-authored-by: Wang Yan <wangyan_0219@hotmail.com>
2026-10-15 09:55:45 +00:00
jUDASmILE
4cde2fb0e0 Align manual preheat execution with update permissions (#24141)
Signed-off-by: jUDASmILE <judasmile@gmail.com>
2026-10-15 09:07:47 +00:00
Nikita Kodkany
bcea998ed8 fix(jobservice): prevent CPU spin from unreachable cron expressions (#23925)
* fix(jobservice): prevent CPU spin from unreachable cron expressions

Cron expressions like '0 0 3 30 2 *' (Feb 30) parse successfully because
robfig/cron validates each field individually without cross-checking day-of-month
against month. schedule.Next() then returns time.Time{} since no matching date
exists within the five-year search window. This causes three failures:

1. ValidateCronString accepts the expression. Fix: call schedule.Next after Parse
   and reject zero results.
2. The scheduleNextJobs enqueuer loop spins indefinitely. Fix: add !t.IsZero()
   guard to the loop condition.
3. clearDirtyJobs misses negative-epoch entries. Fix: use math.MinInt64 as the
   lower bound instead of 0.

Fixes #23924

Signed-off-by: Nikita Kodkany <nikitakodkany@gmail.com>

* fix(jobservice): address review feedback on unreachable cron fix

Signed-off-by: Nikita Kodkany <nikitakodkany@gmail.com>

* fix(jobservice): address review feedback on unreachable cron fix

- Log and set lastEnqueueErr when policy is skipped as unreachable
- Add IsCronReachable check in jobservice Policy.Validate and controller
  submit validation so pre-upgrade schedules are rejected at restore
- Fix probe comment: century year gap, not weekday intersection
- Collapse cronIsReachable/IsCronReachable into one exported function
- Use consistent error message across all validation sites
- Fix clearDirtyJobs comment to describe legacy cleanup
- Improve test robustness: FailNow on timeout, policy-specific assertion
- Add Feb 29 leap day test case to pin reachable behavior

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: Nikita Kodkany <nikitakodkany@gmail.com>

* fix(jobservice): stop the enqueuer loop when Next returns zero mid-loop

The zero-time check only covered the first schedule.Next call. A reachable
spec can still return time.Time{} later: for "0 0 0 29 2 *", Next after
2096-02-29 finds nothing because 2104-02-29 is past robfig/cron's five-year
search window, and time.Time{}.Before(horizon) keeps the loop spinning.

The loop now lives in fireTimes with the IsZero guard in its condition. The
warning for a zero first Next now says there is no occurrence within the
search window, because in 2097-2099 that also applies to valid Feb 29 specs.

Signed-off-by: Vadim Bauer <vb@container-registry.com>

* fix(jobservice): use dedicated enqueuer in test to fix data race

  The test goroutine called scheduleNextJobs on the suite-level enqueuer
  while its background loop was running, racing on lastEnqueueErr.

Signed-off-by: Nikita Kodkany <nikitakodkany@gmail.com>

* style(jobservice): gofmt enqueuer_test.go

Signed-off-by: Vadim Bauer <vb@container-registry.com>

---------

Signed-off-by: Nikita Kodkany <nikitakodkany@gmail.com>
Signed-off-by: Vadim Bauer <vb@container-registry.com>
Co-authored-by: Nikita Kodkany <nkodkany@crusoe.ai>
Co-authored-by: Nikita Kodkany <nikitakodkany@gmail.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: Vadim Bauer <vb@container-registry.com>
2026-10-15 06:42:50 +00:00
Prasanth Baskar
5bdb8a678f docs: update the minor release support matrix (#24060)
List v2.15.x, v2.14.x and v2.13.x, the three most recent minor
releases, in line with the support policy stated in the same file.

Fixes #24059

Signed-off-by: Prasanth Baskar <prasanth@8gears.com>
2026-10-14 10:51:00 +00:00
jUDASmILE
7396332d5c Improve job status hook handling (#24137)
Signed-off-by: jUDASmILE <judasmile@gmail.com>
2026-10-14 10:38:41 +00:00
jUDASmILE
79c614d026 Improve global label listing behavior (#24140)
Signed-off-by: jUDASmILE <judasmile@gmail.com>
2026-10-14 17:44:24 +08:00
stonezdj(Daojun Zhang)
96387e3024 fix(registryctl): reject empty secrets to prevent authentication bypass (#23946)
Clean up empty secrets when creating a new secret handler in registryctl
and skip empty secrets during validation to prevent authentication bypass
when JOBSERVICE_SECRET is empty/unset.

Signed-off-by: stonezdj <stonezdj@gmail.com>
Co-authored-by: Pushkar Joglekar <pushkar.joglekar@broadcom.com>
2026-10-14 04:51:14 +00:00
stonezdj(Daojun Zhang)
35dd9e4337 fix(scanner): redact scanner URL in candidate list for non-admin users (#23947)
Redact the URL field in ListScannerCandidatesOfProject response to prevent
non-admin project members from listing system-global scanner URLs, while
retaining the scanner URL when the caller has system admin permissions.

Signed-off-by: stonezdj <stonezdj@gmail.com>
2026-10-13 11:43:36 +00:00
stonezdj(Daojun Zhang)
5c885152b9 fix(scan): scope scan stop lookup to caller artifact (#23948)
In basicController.Stop, the execution query previously used artifact.Digest
without scoping the lookup to the caller's artifact. Because artifact digests
are content-addressable and can be identical across different projects/tenants,
this allowed users to stop scans belonging to other projects, introducing a
cross-tenant scan-stop (availability) vulnerability.

This commit scopes the execution query specifically to the artifact's unique
database ID (`artifact.ID`), ensuring that only the scan of the caller's specific
artifact is stopped.

Signed-off-by: stonezdj <stonezdj@gmail.com>
2026-10-13 09:41:33 +00:00
Viktor Erpylev
23bfa9a110 fix(ui): keep multi-value filters when editing a P2P preheat policy (#24056)
Filters with several comma-separated values are stored wrapped in braces
(e.g. {a,b}). When opening such a policy for edit, the brace-stripping
branch returned from the forEach callback instead of assigning the value,
so the repository, tag and label fields stayed empty.

The fix was originally proposed in #23323, which was closed
as stale without review; this re-submits it with a regression test.

Fixes #23293

Signed-off-by: Viktor Erpylev <velmoga@gmail.com>
Co-authored-by: Psykii22 <189542486+Psykii22@users.noreply.github.com>
2026-10-12 10:04:29 +00:00
Max
205159bd91 Custom project roles — frontend UI (#23970)
feat(portal): custom project roles UI

Frontend for the custom project roles feature (backend: #23804). Splits the
UI out of the combined feature branch and adapts it to the final backend API.

- Roles management: list/create/edit custom roles with a permissions panel
  (left-side-nav/roles/*, shared/components/role-permissions-panel/*), wired
  into routing (base.module, harbor-shell nav, shared.const) behind the
  system-admin guard.
- Member add/edit (member, add-member, add-group): dynamic role selector that
  lists built-in and custom roles from the role API, with a built-in badge.
- Robot account editor: fetch creator's effective project permissions from
  /users/current/permissions (scope=/project/{id}, relative) instead of the
  removed /projects/{name}/permissions/effective endpoint, to filter grantable
  robot permissions; fail closed on empty permissions.
- Roles grid: compact fixed widths for ID and built-in columns, multi-row selection,
  ascending ID sorting by default, and proper navigation label.
- Role pickers: shared getAllRoles() helper across member and group dialogs with
  loading state and validation against unlisted roles.
- Role wizard: exact role name uniqueness check, input validation, and full-width modal.
- i18n: ROLE.* keys and MEMBER.BUILT_IN synced and translated across all supported locales.
- Tests: unit test coverage for role permissions panel, roles component, add-role wizard,
  and role pagination helper.

Signed-off-by: Max Grau Stenzel <max.grau.stenzel@gmail.com>
Signed-off-by: Prasanth Baskar <prasanth@8gears.com>
Signed-off-by: stonezdj <stonezdj@gmail.com>
Co-authored-by: Prasanth Baskar <prasanth@8gears.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-10-12 07:21:44 +00:00
stonezdj(Daojun Zhang)
2c94d4e213 bump: bump up Go version to v1.27.2 (#24119)
Update Go version from 1.26.4 to 1.27.2 across makefiles, configuration, CI workflows, and documentation.

Signed-off-by: stonezdj <stonezdj@gmail.com>
2026-10-12 06:28:24 +00:00
stonezdj(Daojun Zhang)
2c66bbe032 Bump trivy adapter to v0.39.1 and distribution to v2.8.3-harbor.2 (#24118)
- Bump trivy adapter version to v0.39.1 (ref: #24080)
- Bump distribution version to v2.8.3-harbor.2 (ref: #23991)


(cherry picked from commit 0c7f91025d)

Signed-off-by: stonezdj <stonezdj@gmail.com>
2026-10-10 11:13:20 +08:00
stonezdj(Daojun Zhang)
4d425b805f bump: bump up Go version to v1.26.9 (#24120)
Update Go version from 1.26.4 to 1.26.9 across makefiles, configuration, CI workflows, and documentation.

Signed-off-by: stonezdj <stonezdj@gmail.com>
2026-10-09 16:42:09 +08:00
Lewis Bertin
44d081c195 fix(i18n): translate remaining French UI strings (#24088)
Translate the strings of the French portal file that were still in
English, mostly from recent features (audit log event types, Dragonfly
preheat scope, OIDC logout, preferences).

Signed-off-by: Lewis Bertin <lewis@tactiqual.tech>
2026-10-08 10:39:19 +00:00
Wang Yan
c329590590 fix(registry): validate upload Location against the request origin, n… (#24113)
* fix(registry): validate upload Location against the request origin, not the configured endpoint

The GHSA-wjpm-hv5w-gxr6 fix compared registry-provided upload Location
headers against the configured registry endpoint (c.url). Adapters whose
authorizer rewrites the request host break under that rule: AWS ECR is
configured with https://api.ecr.<region>.amazonaws.com but every request
is rewritten to <account>.dkr.ecr.<region>.amazonaws.com, which is also
the host in the Location ECR returns. Every push replication to ECR
failed with 'resolves to a different origin'.

The correct invariant is 'the Location must be same-origin with the
server that issued it'. Validate the Location when it is received, using
the URL of the request that produced the response (after the authorizer
ran) as the origin. Root-relative Locations are still appended to the
endpoint so a path prefix is preserved. Cross-origin, network-path and
userinfo-bearing values are still rejected and the attacker host is
never contacted.

Fixes the ECR push replication regression introduced by #<GHSA-wjpm PR>.

Signed-off-by: wang yan <yan-yw.wang@broadcom.com>

* fix(registry): resolve upload Location against the final request URL after redirects

c.client.Do follows redirects, but req.URL still refers to the original
request. A same-origin 307 to another path followed by a path-relative
Location would resolve against the original path and send the next
upload to the wrong URL. Use resp.Request.URL as the origin when the
HTTP client followed redirects, but reject a redirect that changed the
origin so an arbitrary redirect target can never become a trusted,
credential-bearing upload destination.

Signed-off-by: wang yan <yan-yw.wang@broadcom.com>

* fix(registry): simplify upload Location comments

Signed-off-by: wang yan <yan-yw.wang@broadcom.com>

---------

Signed-off-by: wang yan <yan-yw.wang@broadcom.com>
2026-10-08 08:26:47 +00:00
Prasanth Baskar
72e54002bb chore(deps): forward-port the release-2.15.0 go module bumps to main (#24093)
Three dependabot bumps landed on release-2.15.0 but never reached main:

- github.com/jackc/pgx/v5 v5.10.0 -> v5.11.0 (#23902)
- github.com/aws/aws-sdk-go-v2/credentials v1.19.19 -> v1.20.3 (#23908)
- github.com/go-openapi/runtime v0.32.2 -> v0.33.2 (#23909)

The transitive sets follow from the module graph. main is behind
release-2.15.0 on the aws-sdk core, so the aws side pulls more here than
it did on the release branch. go-openapi/runtime 0.33 moves spec,
validate, analysis and jsonpointer to 1.0.0 and brings along strfmt,
loads, the swag modules, otel and testify.

Signed-off-by: Prasanth Baskar <prasanth@8gears.com>
2026-10-08 06:51:12 +00:00
Prasanth Baskar
8b67a48c4e chore(deps): bump trivy to 0.75.0 and the trivy adapter to 0.39.1-rc2 (#24092)
Signed-off-by: Prasanth Baskar <prasanth@8gears.com>
2026-10-08 06:09:13 +00:00
Wang Yan
ba53f0bb10 fix(contenttrust): allow legacy signer pull by default to avoid regre… (#24085)
fix(contenttrust): allow legacy signer pull by default to avoid regression

Default CONTENT_TRUST_LEGACY_SIGNER_PULL_ENABLED to true to prevent breaking
existing cosign and notation signing workflows on upgrade.

Signed-off-by: wang yan <yan-yw.wang@broadcom.com>
2026-10-08 04:34:08 +00:00
Vadim Bauer
f6f5bac8d1 feat(webhook): add webhook egress policy levels and allowlist (#24079)
The webhook SSRF guard was a single boolean. With it off (the default
since #24078) webhooks can reach loopback and cloud metadata; with it on,
every internal receiver breaks.

HARBOR_WEBHOOK_EGRESS_POLICY (harbor.yml webhook_egress.policy) selects a
level: allow_all, block_restricted (default) or public_only. Restricted
covers loopback, link-local, cloud metadata, unspecified, multicast and
NAT64/6to4/Teredo. HARBOR_WEBHOOK_EGRESS_ALLOWLIST takes IPs, CIDRs,
hostnames and *.domain suffixes that are allowed at every level.

When the policy is unset, the deprecated HARBOR_ALLOW_PRIVATE_NETWORK_ACCESS
keeps its 2.15 meaning: true is allow_all, false is public_only. Invalid
values fail closed, prepare rejects an unknown policy, and a blocked
target is logged with the setting that reopens it.

Signed-off-by: Vadim Bauer <vb@container-registry.com>
2026-10-08 07:15:15 +05:30
Wang Yan
9a37bcf55f fix(webhook): allow private network access by default to avoid regres… (#24078)
* fix(webhook): allow private network access by default to avoid regression

Default HARBOR_ALLOW_PRIVATE_NETWORK_ACCESS to true to prevent breaking
existing on-premise and Kubernetes deployments where webhooks target
internal services.

Remove the allow_private_network_access field from harbor.yml to keep
the YAML schema clean, managing this toggle solely via the environment
variable.

Signed-off-by: wang yan <yan-yw.wang@broadcom.com>

* resolve copilot review

Signed-off-by: wang yan <yan-yw.wang@broadcom.com>

* resolve the comments

Signed-off-by: wang yan <yan-yw.wang@broadcom.com>

---------

Signed-off-by: wang yan <yan-yw.wang@broadcom.com>
2026-10-07 20:04:17 +08:00
Vadim Bauer
b323c0c8ec Merge commit from fork
* fix(webhook): block SSRF to private-network targets and stop reflecting response bodies

Webhook and Slack notification jobs delivered to any operator-supplied URL with
no address classification, and folded a non-2xx response body into the
project-readable task log. A project admin could reach loopback, RFC1918,
link-local and cloud-metadata (169.254.169.254) targets and read internal
service responses back through the webhook task log (CWE-918).

Classify destinations against the IANA special-purpose registries and reject
non-public targets at (1) webhook policy validation and (2) a dial-time guard on
the notification HTTP clients; drop the response body from the task-log error;
refuse redirects. The HARBOR_ALLOW_PRIVATE_NETWORK_ACCESS opt-out (default off)
restores delivery to internal endpoints for operators who rely on it.

Signed-off-by: Vadim Bauer <vb@container-registry.com>

* test(webhook): cover SSRF target rejection, body-drop and redirect refusal

Signed-off-by: Vadim Bauer <vb@container-registry.com>

* test(webhook): resolve upstream's mixed-case target host in the stub resolver

Upstream #23889 added a create-policy test that posts a mixed-case target
address and expects 201. With the SSRF target check in place that hostname
now goes through the stub resolver, which only knew hooks.example.com, so
the test failed with 400. Teach the stub the second hostname; the upstream
test keeps asserting lowercasing, the SSRF tests keep asserting rejection.

Signed-off-by: Vadim Bauer <vb@container-registry.com>

* fix(webhook): honour the jobservice proxy settings in the SSRF-safe transport

The public-network guard cleared Transport.Proxy, so webhook and Slack
delivery ignored the HTTP_PROXY/HTTPS_PROXY/NO_PROXY settings that
prepare writes for jobservice. Deployments without a direct Internet
route lost every public webhook.

Keep the transport's proxy function. When it selects a proxy, validate
the target host (literal or all resolved addresses) as public before the
request is handed to the proxy, and let only dials to that proxy address
skip the dial-time private-address check. When no proxy applies
(NO_PROXY, loopback), the resolved-address dial guard is unchanged. A
direct request aimed at the proxy's own address is refused so it cannot
inherit the proxy exemption.

Signed-off-by: Vadim Bauer <vb@container-registry.com>

* chore: describe the fix without advisory identifiers

Signed-off-by: Vadim Bauer <vb@container-registry.com>
(cherry picked from commit 54cc3fca7a741ecb31293a767b998ceffec9d5b5)

* fix(webhook): drain notification response bodies before closing

Closing an unread response body forces the transport to drop the
keep-alive connection. Discard up to 64KiB of the body (never surfacing
it) so the pooled connection is reused after non-2xx responses.

Signed-off-by: Vadim Bauer <vb@container-registry.com>

* test(webhook): assert the dial guard on production clients and pin the env

Exercise the production notification clients against a loopback target
so dropping the public-network guard fails a test, cover connection
reuse after non-2xx responses, and pin HARBOR_ALLOW_PRIVATE_NETWORK_ACCESS
to false in tests that assert the default blocking behaviour.

Signed-off-by: Vadim Bauer <vb@container-registry.com>

* fix(prepare): keep network.allow_private_network_access across migrate

The migrate command re-renders harbor.yml from the 2.15.0 template, so a
key missing there is dropped. Render the network block, preserving an
operator-set value and defaulting to false.

Signed-off-by: Vadim Bauer <vb@container-registry.com>

---------

Signed-off-by: Vadim Bauer <vb@container-registry.com>
2026-10-06 14:04:35 +02:00
Vadim Bauer
f234c0cb68 Merge commit from fork
* fix(policy): stop User-Agent from bypassing content-trust and vulnerability policies

SkipPolicyChecking exempted a caller from the content-trust and
vulnerability-prevention pull policies when it held push and the request
User-Agent contained "cosign" or "notation". User-Agent is fully
attacker-controlled (CWE-807), so any push-capable principal (e.g. the
Developer role) bypassed both policies on a plain manifest pull by setting
one header.

Remove the User-Agent discriminator. Scanner pull keeps its bypass via the
non-forgeable scanner-pull RBAC action, and a genuinely signed artifact is
still recognised by its signature accessory. The pull-before-first-signature
bootstrap for content-trust is preserved as an opt-in, content-trust-only
exemption (CONTENT_TRUST_LEGACY_SIGNER_PULL_ENABLED=true); it never affects
the vulnerability-prevention policy.

Reported by sabuhimammadov0 (GHSA-wrw5-gmvj-gf23); independently reported on
the cncf-harbor-security list by kt (thread #61), Oleh Konko (thread #25) and
Eren Akdag (thread #68).

Signed-off-by: Vadim Bauer <vb@container-registry.com>

* test(policy): spoofed User-Agent no longer bypasses pull policies

Cover GHSA-wrw5-gmvj-gf23: a push-capable v2token caller with
User-Agent: cosign is now blocked by both the content-trust and the
vulnerability-prevention middleware, while scanner-pull and genuine signature
accessories still bypass. Add a test for the opt-in content-trust bootstrap.

Signed-off-by: Vadim Bauer <vb@container-registry.com>

* test(contenttrust): pin the legacy signer-pull hatch off in the spoofed User-Agent test

The test asserted the hatch-off behaviour but inherited the process
environment, so it failed whenever the flag was exported.

Signed-off-by: Vadim Bauer <vb@container-registry.com>

* test(vulnerable): enable the legacy signer-pull hatch in the spoofed User-Agent test

With the flag off the test could not detect the User-Agent bypass
being moved back into the shared policy check behind the flag.

Signed-off-by: Vadim Bauer <vb@container-registry.com>

* chore: describe the fix without advisory identifiers

Signed-off-by: Vadim Bauer <vb@container-registry.com>
(cherry picked from commit 49f78850621446dd113a3d2517fed7e23f3e565b)

* test(contenttrust): cover the legacy signer-pull hatch guards

Add negative controls for the opt-in signer-pull exemption: it stays off
when the variable is unset, and with it enabled a pull is still rejected
without push permission or without a signing-client User-Agent. Also
cover the notation User-Agent path.

Signed-off-by: Vadim Bauer <vb@container-registry.com>

---------

Signed-off-by: Vadim Bauer <vb@container-registry.com>
2026-10-06 14:03:35 +02:00
Vadim Bauer
0a9e68e6d5 Merge commit from fork
* test(pkg/registry): regression for chunk-upload Location SSRF (GHSA-wjpm-hv5w-gxr6)

Signed-off-by: Vadim Bauer <vb@container-registry.com>

* fix(registry): validate chunk-upload Location against the registry origin (GHSA-wjpm-hv5w-gxr6)

Signed-off-by: Vadim Bauer <vb@container-registry.com>

* fix(registry): keep the endpoint path prefix for root-relative upload Locations

Distribution with relativeurls returns /v2/... without the path prefix the endpoint is served under. Resolving it against the endpoint dropped that prefix, so pushes through a path-prefixed registry endpoint hit the wrong route. Append root-relative paths to the endpoint as before and keep the origin and userinfo checks.

Signed-off-by: Vadim Bauer <vb@container-registry.com>

* chore: describe the fix without advisory identifiers

Signed-off-by: Vadim Bauer <vb@container-registry.com>
(cherry picked from commit 6b4be4734a401c5b427667544534aea8bed53c1d)

---------

Signed-off-by: Vadim Bauer <vb@container-registry.com>
2026-10-06 14:02:35 +02:00
Vadim Bauer
c38a9eb5d8 Merge commit from fork
* test(common/http): regression for pagination Link SSRF (GHSA-xgp2-5vxg-rgh2)

Signed-off-by: Vadim Bauer <vb@container-registry.com>

* fix(common/http): pin pagination Link next URL to the request origin (GHSA-xgp2-5vxg-rgh2)

GetAndIteratePagination built the next-page URL by string-concatenating the
attacker-controlled Link rel="next" value onto scheme+host and re-parsing it, so
a value like "@evil.example/x" re-pointed the request at evil.example (host) with
the original host demoted to userinfo. Because Client.Do re-runs its auth modifiers
on every hop, the configured registry credentials were then sent to that host
(SSRF + credential disclosure).

Resolve the Link as an RFC 3986 reference against the current page URL, reject
userinfo, and require the resolved scheme/host/port to equal the origin. Relative
same-origin pagination keeps working; any cross-origin next target fails closed.

Signed-off-by: Vadim Bauer <vb@container-registry.com>

* test(common/http): require cross-origin pagination links to fail with an error

The regression test discarded the pagination error, so an implementation that silently stopped at a cross-origin Link and returned a truncated list would still pass. Assert that every vector returns an error.

Signed-off-by: Vadim Bauer <vb@container-registry.com>
(cherry picked from commit bfa9a1e750cceb9363a510d548f14694fdf6ae31)

* chore: describe the fix without advisory identifiers

Signed-off-by: Vadim Bauer <vb@container-registry.com>
(cherry picked from commit 00e1558ff2fd848ebd071d21a9a9b3e1895df761)

* test(common/http): cover userinfo rejection and absolute same-origin pagination links

The existing userinfo vector is rejected by url.Parse before the
userinfo check runs, so add resolveNextLink cases that only that check
can reject. Also exercise absolute same-origin next links, default port
equivalence and scheme/port mismatches.

Signed-off-by: Vadim Bauer <vb@container-registry.com>

* fix(registry): keep catalog and tag list pagination on the registry origin

Catalog and ListTags followed the Link rel="next" value from the remote
registry verbatim when absolute, and appended it to the endpoint otherwise,
so an absolute URL to another host or a value starting with "@" sent the
next request elsewhere. Authorizers that attach credentials regardless of
host (basic auth used by the JFrog adapter, the ACR token authorizer) then
disclosed them to that host.

Build the next URL exactly as before and reject it unless it keeps the
endpoint's scheme, host, effective port and userinfo. SameOrigin is
exported from common/http to share the origin check.

Signed-off-by: Vadim Bauer <vb@container-registry.com>

---------

Signed-off-by: Vadim Bauer <vb@container-registry.com>
2026-10-06 14:01:35 +02:00
Vadim Bauer
8d59dd5bba Merge commit from fork
* fix(security): fail closed when auth mode lookup fails (GHSA-3x7c-wf86-mjx6)

The security context middleware called config.AuthMode and, on error, logged a
warning and continued processing the request with no auth mode in the request
context. Determining the request's security context is an authorization decision
that depends on the configured auth mode, so a lookup failure must reject the
request rather than fall through to the handler (CWE-636).

Reject with 500 via lib_http.SendError before the security-context generators
run. Normal requests, where the lookup succeeds, are unaffected.

Signed-off-by: Vadim Bauer <vb@container-registry.com>

* test(security): assert middleware fails closed on auth mode lookup error (GHSA-3x7c-wf86-mjx6)

Signed-off-by: Vadim Bauer <vb@container-registry.com>

* chore: describe the fix without advisory identifiers

Signed-off-by: Vadim Bauer <vb@container-registry.com>
(cherry picked from commit 33304265ce4ad826c63e471fd65bcbf2aa589c86)

* test(security): restore default config manager after TestSecurity

Signed-off-by: Vadim Bauer <vb@container-registry.com>

---------

Signed-off-by: Vadim Bauer <vb@container-registry.com>
2026-10-06 14:00:35 +02:00
Vadim Bauer
d80de5b309 Merge commit from fork
* fix(authproxy): stop a local sysadmin flag leaking into an authproxy context (GHSA-xvh7-74g6-29xw)

In auth_mode=http_auth the /v2 path loads the Harbor-local user record by name
and overlays only GroupIDs and AdminRoleInAuth from the TokenReview result
(server/middleware/security/auth_proxy.go). The local SysAdminFlag survives, and
local.SecurityContext.IsSysAdmin returns SysAdminFlag || AdminRoleInAuth, so an
external identity that the authproxy admin policy did not grant admin obtains
sysadmin /v2 authorization purely by colliding with a local sysadmin name -- the
seeded admin account always exists.

Clear SysAdminFlag on the overlaid record so AdminRoleInAuth, derived from the
TokenReview username and groups against the configured admin set, is the only
admin signal in authproxy mode. An explicitly allowlisted authproxy admin is
unaffected: its access comes from AdminRoleInAuth, not a local flag.

Reported by cyberlanc3r (GHSA-xvh7-74g6-29xw); independently reported by
Mitchell Benjamin (thread #49, topic 119514428).

Signed-off-by: Vadim Bauer <vb@container-registry.com>

* test(authproxy): regression for sysadmin-flag collision (GHSA-xvh7-74g6-29xw)

Drive the real authProxy.Generate + local.SecurityContext.IsSysAdmin with a
mocked user manager returning a colliding sysadmin-flagged local record. Assert
the external identity is not sysadmin with an empty admin allowlist, and that an
explicitly allowlisted authproxy admin still is.

Signed-off-by: Vadim Bauer <vb@container-registry.com>

* chore: describe the fix without advisory identifiers

Signed-off-by: Vadim Bauer <vb@container-registry.com>
(cherry picked from commit df6273ae8d0dd15a0a1f2f84d5a9b3f5b018bdee)

* test(authproxy): return a fresh user per lookup in the collision test

The mocked GetByName returned one shared *User, and Generate mutates it,
so the allowlisted arm saw a record whose sysadmin flag the first arm had
already cleared and never exercised the collision. Return a new record
per call, as the DB-backed manager does, and assert the allowlisted arm
derives admin from AdminRoleInAuth with the local flag cleared.

Signed-off-by: Vadim Bauer <vb@container-registry.com>

---------

Signed-off-by: Vadim Bauer <vb@container-registry.com>
2026-10-06 13:59:35 +02:00
Vadim Bauer
d8a18f71b4 Merge commit from fork
* fix(immutable): evaluate all repository and tag selectors (GHSA-27f2-qg57-2gwg)

The immutable tag rule matcher only read ScopeSelectors["repository"][0] and
TagSelectors[0], so a rule carrying more than one repository or tag selector
protected only the first entry. Pushes and deletes to the repositories or tags
named by the remaining selectors bypassed the immutability control.

Evaluate every selector in each dimension and match when any of them matches,
mirroring the OR/alternation the portal expresses as a single {a,b} doublestar
pattern. Single-selector rules, which is all the portal ever creates, keep
identical behaviour.

Signed-off-by: Vadim Bauer <vb@container-registry.com>

* test(immutable): cover multi-selector immutable rule matching (GHSA-27f2-qg57-2gwg)

Signed-off-by: Vadim Bauer <vb@container-registry.com>

* fix(immutable): subtract exclusion selectors instead of OR-ing them

Several selectors in one rule dimension must behave like the portal's
single {a,b} pattern. OR-ing every selector made two repoExcludes or
excludes selectors select everything, so excluded repositories and
tags became immutable. Inclusion selectors stay alternatives, every
exclusion selector now has to select the candidate, and multi-tag
candidates are evaluated per tag so tags excluded by different
selectors do not cancel out.

Signed-off-by: Vadim Bauer <vb@container-registry.com>

* chore: describe the fix without advisory identifiers

Signed-off-by: Vadim Bauer <vb@container-registry.com>
(cherry picked from commit 27f4614d1ec7484eda1336bb2e01cf1b891326df)

* fix(immutable): ignore null selectors in rule matching

The rule API stores selector lists without validation, so a null entry
after the first selector was harmless while only the first one was read.
Evaluating every selector turned such entries into a nil dereference.
Drop null entries before matching.

Tests now purge leftover rules of their project before running, since
Match evaluates every rule of the project.

Signed-off-by: Vadim Bauer <vb@container-registry.com>

---------

Signed-off-by: Vadim Bauer <vb@container-registry.com>
2026-10-06 13:58:35 +02:00
Vadim Bauer
7894b309c3 Merge commit from fork
* fix(scan): verify TLS and bound body on scanner bearer token request (GHSA-884q-mjcg-86x4)

Signed-off-by: Vadim Bauer <vb@container-registry.com>

* test(scan): assert bearer token request verifies TLS and bounds body (GHSA-884q-mjcg-86x4)

Signed-off-by: Vadim Bauer <vb@container-registry.com>

* chore: describe the fix without advisory identifiers

Signed-off-by: Vadim Bauer <vb@container-registry.com>
(cherry picked from commit c9d2c8dc3997ef80fb1ea8a09936d01a13bab8e9)

* test(scan): assert certificate verification error type in bearer token TLS test

Match *tls.CertificateVerificationError via errors.As instead of a
substring of the error message, so the test does not depend on x509
error wording and cannot pass on an unrelated failure.

Signed-off-by: Vadim Bauer <vb@container-registry.com>

---------

Signed-off-by: Vadim Bauer <vb@container-registry.com>
2026-10-06 13:57:35 +02:00
Vadim Bauer
f5c89c7355 Merge commit from fork
* fix(scanner): stop access_cred ORM filter oracle (GHSA-vwvv-675v-p6rr)

The scanner_registration.access_cred column stores the literal Authorization
value Harbor presents to a scanner adapter. It lacked the filter:"false" ORM
tag, so the generic q= builder let a caller put it in a WHERE clause. Since the
API never returns the column, the row/X-Total-Count difference is a blind
boolean oracle that recovers the secret character by character (sibling of
CVE-2025-30086, matching the guarded reg.access_secret). Add filter:"false".

Signed-off-by: Vadim Bauer <vb@container-registry.com>

* test(scanner): regression for access_cred filter oracle (GHSA-vwvv-675v-p6rr)

Drives the real ListRegistrations/GetTotalOfRegistrations DAO path (the call
ListScannerCandidatesOfProject makes) with a non-matching access_cred filter and
asserts list/count are unchanged, i.e. the filter is dropped. Red without the
filter:"false" tag (oracle excludes the row), green with it. Positive control
keeps name filtering working.

Signed-off-by: Vadim Bauer <vb@container-registry.com>

* chore: describe the fix without advisory identifiers

Signed-off-by: Vadim Bauer <vb@container-registry.com>
(cherry picked from commit 9c160302ee3c56394957b263061adf374db4502f)

* docs(scanner): describe access_cred storage accurately

New writes are encrypted by the scanner manager; rows saved before that
remain plaintext because there is no data migration.

Signed-off-by: Vadim Bauer <vb@container-registry.com>

---------

Signed-off-by: Vadim Bauer <vb@container-registry.com>
2026-10-06 13:56:35 +02:00