release: v1.4.3 - #9867
release: v1.4.3#9867sriramveeraghanta wants to merge 17 commits into
Conversation
…up endpoints (#9335) * fix: add rate limiting to email/password sign-in and sign-up endpoints All four password authentication views (app sign-in, app sign-up, space sign-in, space sign-up) extended django.views.View, so DRF's global AnonRateThrottle never ran and the endpoints accepted unlimited credential guesses with no friction (brute-force / credential stuffing, GHSA-349j). Add authentication_throttle_allows(request) at the top of each post() method — before any DB access — using the same AuthenticationThrottle already guarding the magic-code views. On rejection the view redirects with RATE_LIMIT_EXCEEDED, consistent with all other throttled auth endpoints. Default limit remains 10/minute, overridable via AUTHENTICATION_RATE_LIMIT. Co-authored-by: Plane AI <noreply@plane.so> * refactor: consolidate auth throttle into a decorator + add tests - Extract the repeated throttle-and-redirect block from the six redirect-flow auth views (email + magic, app + space) into a single throttle_auth_redirect decorator in rate_limit.py. Behaviour is unchanged: the throttle still runs before any DB access; brute-force traffic is rejected without a DB hit. - Add regression tests for the password sign-in/sign-up throttle on both app and space endpoints, mirroring the existing magic-code throttle tests. - Reset the shared AuthenticationThrottle bucket before every test in test_authentication.py. All auth endpoints share one per-IP throttle scope, so the newly-throttled password requests exhausted the budget mid-file and caused unrelated tests to trip RATE_LIMIT_EXCEEDED. --------- Co-authored-by: Plane AI <noreply@plane.so> Co-authored-by: sriramveeraghanta <veeraghanta.sriram@gmail.com> (cherry picked from commit e30605d)
* [WEB-8103] fix: stop leaking webhook HMAC secret_key on reads (GHSA-83rj) WebhookEndpoint list/retrieve/patch pass a fields= allowlist that excludes secret_key, but DynamicBaseSerializer.__init__ discards the caller allowlist (fields = self.expand). With WebhookSerializer using fields="__all__" and secret_key only in read_only_fields (read-only is still serialized), the HMAC signing secret leaked on every webhook read (GHSA-83rj-4282-x39v; admin-only). Rather than secret_key = CharField(write_only=True) — which would let a client inject their own secret on create/patch and break the intended one-time reveal — hide it by default and reveal only where intended: - WebhookSerializer.to_representation drops secret_key unless the show_secret_key context flag is set (secure by default). secret_key stays server-generated (default=generate_token) and non-writable. - POST create and WebhookSecretRegenerateEndpoint pass show_secret_key so the secret is still returned once for the caller to configure their receiver; list/retrieve/patch no longer emit it. Add contract regression tests (fail-before verified). Follow-up: the DynamicBaseSerializer.__init__ allowlist bug affects other serializers — tracked separately. Co-authored-by: Plane AI <noreply@plane.so> * [WEB-8103] test: address review — patch network boundary + pin to_representation Per review (@sriramveeraghanta): - Patch the network boundary (validate_url) instead of the whole private _validate_webhook_url method, so the domain/schema checks still run and the test survives a rename of the private method. - Add an assertion that an explicit fields=("secret_key",) request still hides the key, pinning to_representation as the enforcement point so a future DynamicBaseSerializer._filter_fields fix can't silently re-open the leak. Co-authored-by: Plane AI <noreply@plane.so> * [WEB-8103] docs: spell out both levels of the dead fields= allowlist The previous comment named only DynamicBaseSerializer.__init__ discarding the caller's fields=, which is half the root cause. _filter_fields never removes anything either: it builds `allowed` purely to attach expansion serializers for names not already on the serializer, then returns self.fields unfiltered (serializers/base.py:45-119). So the fields= kwargs in views/webhook/base.py are no-ops on two independent levels. Documented so a future `fields = fields or self.expand` fix isn't assumed to re-activate the allowlists for confidentiality — _filter_fields has to be made restrictive first. The show_secret_key context flag remains the sole enforcement point. Addresses @sriramveeraghanta's review on #9382. 6/6 contract tests pass. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * chore(security): drop advisory identifiers from code comments Explanations kept unchanged; only the IDs are removed. Co-authored-by: Plane AI <noreply@plane.so> --------- Co-authored-by: Plane AI <noreply@plane.so> Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> (cherry picked from commit 3052792)
* [WEB-8110] fix: sanitize page list order_by against an allowlist (GHSA-2v48) PageViewSet.get_queryset passed the raw order_by query param into .order_by(). In Django 4.2 .order_by() resolves field names at call time, so an unknown field (e.g. order_by=password) raises FieldError → 500 DoS, and a valid relation path (e.g. order_by=owned_by__password) enables ORM relational traversal (GHSA-2v48-qcjw-74ch). Add PAGE_ORDER_BY_ALLOWLIST to utils/order_queryset.py and wrap the param with the existing sanitize_order_by() before it reaches .order_by(), matching the issue/project/view/notification endpoints. Unknown or malformed values fall back to the safe -created_at default. Covers only the app project-pages residual; the 3 external-REST-API sites in the advisory are handled by PR #9348. EE Wiki counterpart: WEB-8111. Add contract regression tests (fail-before verified). Co-authored-by: Plane AI <noreply@plane.so> * [WEB-8110] fix: fold sanitized order_by into a single order_by() call (review) Address CodeRabbit + Copilot on #9387: the sanitized .order_by(user) was immediately overridden by a later .order_by("-is_favorite", "-created_at"), so the order_by param had no effect on the result (dead code) and cost an extra query-build step. Merge them into one .order_by("-is_favorite", <sanitized>, "id") — matching the EE project-pages viewset — so favourites stay pinned first, the allowlisted user ordering actually applies as the secondary sort, and id is a stable pagination tiebreak. The no-param default is unchanged (-created_at). Add a test asserting order_by=name / -name actually reorders the results. Co-authored-by: Plane AI <noreply@plane.so> * chore(security): drop advisory identifiers from code comments Explanations kept unchanged; only the IDs are removed. Co-authored-by: Plane AI <noreply@plane.so> --------- Co-authored-by: Plane AI <noreply@plane.so> (cherry picked from commit d25b99c)
…9442) * [WEB-8283] fix: bind Spaces board object IDs to the anchor's project The public Spaces board endpoints resolved the DeployBoard from the URL anchor but trusted the caller-supplied issue_id/comment_id/intake_id verbatim, without verifying the object belonged to that board's project/workspace. Any authenticated user could write comments, reactions and votes onto arbitrary issues cross-tenant, and read EXTERNAL comments from a different project in the same workspace. Bind every caller-supplied object id to the board's project + workspace before writing: - comment / issue-reaction / vote create: require the issue to exist in the board's project via Issue.issue_objects (excludes draft/archived/ triage), else 404. - comment-reaction create: require the comment to exist in the board's project with access="EXTERNAL", else 404. - intake create: require the URL intake_id to match the board's intake, else 400. - comment list read: scope the queryset to the board's project_id. Also add the missing is_votes_enabled gate on vote create for parity with comment/reaction create (pre-existing gap in the same method). Adds contract regression tests (fail-before verified): cross-tenant writes and the cross-project comment read now rejected, with positive controls confirming legitimate board writes/reads still succeed. Co-authored-by: Plane AI <noreply@plane.so> * [WEB-8283] test: address Copilot review — cross-workspace + votes-disabled coverage - Add cross-workspace write test (issue in a different workspace) to exercise the workspace_id binding, matching the advisory's cross-tenant impact (previously only same-workspace/different-project was covered). - Add a regression test for the new is_votes_enabled gate on vote create (votes-disabled board → 400), preventing the pre-existing gap from reappearing. - Clarify the section header comment: cross-tenant writes return 404 for issue/comment binding, 400 for the intake binding mismatch. Co-authored-by: Plane AI <noreply@plane.so> * [WEB-8283] refactor: extract board-scope guards into shared helpers Address CodeRabbit review: the identical "object belongs to the board's project+workspace" existence check was duplicated across four create() methods (in four different ViewSets). Extract two module-level helpers — _issue_in_board_scope and _comment_in_board_scope — so the check is a single source of truth and cannot drift between endpoints or be forgotten on a new one (the exact class of bug this PR fixes). Behavior-preserving; 14 contract tests still green. Co-authored-by: Plane AI <noreply@plane.so> * chore(security): drop advisory identifiers from code comments Explanations kept unchanged; only the IDs are removed. Co-authored-by: Plane AI <noreply@plane.so> * fix(security): use the deploy board's project_id in the reaction-create activity log CommentReactionPublicViewSet.create() logged the activity with str(self.kwargs.get("project_id", None)) — this route's URL only ever supplies anchor and comment_id, never project_id, so every comment reaction created on a public board logged project_id="None", silently corrupting the activity/audit trail. destroy() on the same viewset already resolves the correct project_id from the deploy board; create() now does the same. Co-authored-by: Plane AI <noreply@plane.so> * [WEB-8283] fix: apply board-scope guards to comment/reaction read, update and delete paths The board/issue/external-comment scoping added by this PR's create() methods was never applied to the list, update and delete paths built on the same models. A caller could read reactions on an INTERNAL (non-public) comment through the public reaction list, or reach a comment or reaction they authored through a board it doesn't actually belong to via partial_update() or destroy(), since those methods looked up objects by pk/actor only. Bind IssueCommentPublicViewSet.partial_update()/destroy() to the board's project, workspace, issue_id and EXTERNAL access; bind IssueReactionPublicViewSet.destroy() to the board's project (previously only workspace-scoped); and bind CommentReactionPublicViewSet.get_queryset()/ destroy() to EXTERNAL comments only. Add regression coverage for each gap, plus positive controls confirming legitimate reads/writes on the board's own objects still work. Co-authored-by: Plane AI <noreply@plane.so> --------- Co-authored-by: Plane AI <noreply@plane.so> (cherry picked from commit 5b5af0a)
…9466) * [WEB-8352] fix(security): scope SubIssuesEndpoint to the URL project (GHSA-gxhv-fw9x-2pg3) SubIssuesEndpoint is guarded only by ProjectEntityPermission, which verifies the caller belongs to the URL project_id but not that the path issue_id lives in that project. Both handlers then resolved issues without a project scope: - GET filtered sub-issues by parent_id + workspace__slug only, leaking the names/priorities/assignees/dates of another project's sub-issues (read IDOR). - POST loaded the parent by bare pk (no workspace/project scope) and filtered the moved sub-issues by workspace__slug only, letting any project member re-parent issues from other projects/workspaces (write IDOR). Scope the parent lookup and both sub-issue querysets to the URL project_id (and bind the parent to the workspace), returning 404 when the parent is not in the caller's project. Adds 5 contract tests (3 security, 2 positive controls); fail-before verified. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * [WEB-8352] fix: dispatch sub-issue activity only for project-scoped issues (CodeRabbit/Copilot #9466) The DB update + response were scoped to the URL project, but the activity loop still iterated the raw caller-supplied sub_issue_ids. A cross-project id (excluded from the re-parent) would still fire issue_activity.delay, whose task does an unscoped Issue.objects.get and bumps updated_at — touching a foreign issue and creating a bogus activity row. Dispatch from the project-scoped sub_issues (scoped_sub_issue_ids) instead. Strengthened the test to assert the foreign issue is absent from the response body (sub_issues / state_distribution) and that no activity is dispatched for it (mock). Fail-before verified. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * chore(security): drop advisory identifiers from code comments Explanations kept unchanged; only the IDs are removed. Co-authored-by: Plane AI <noreply@plane.so> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Co-authored-by: Plane AI <noreply@plane.so> (cherry picked from commit 3478d4f)
…ently dropping invalid ids (#9526) Work item create/update via the external API returned 200/201 even when assignees or labels didn't belong to the project, quietly filtering the invalid ids out with no error. Now raises the same kind of ValidationError already used for state/parent, matching the pattern of #9517. (cherry picked from commit aacfaac)
… removed Docker Hub org (#9829) MinIO removed the `minio` organisation from Docker Hub. Every deployment that pulls `minio/minio` or `minio/mc` now fails with: pull access denied for minio/minio, repository does not exist or may require 'docker login' The registry returns 401 (not 404) to anonymous clients, so the error reads like a credentials problem when the repository is simply gone. quay.io is MinIO's own second registry and still serves both images publicly (multi-arch: amd64, arm64, ppc64le). Repoint every default at quay.io, and pin an explicit RELEASE tag instead of `:latest` so a future upstream change to a floating tag cannot break deployments the same way: minio -> quay.io/minio/minio:RELEASE.2025-09-07T16-13-09Z mc -> quay.io/minio/mc:RELEASE.2025-08-13T08-35-41Z These are the digests `:latest` currently resolves to on quay. Note: the community MinIO build is effectively frozen upstream (last community push was 2025-09-07), so this unblocks deployments but does not change the longer-term need to steer self-hosted users toward external S3, with bundled MinIO as a dev/eval convenience. Claude-Session: https://claude.ai/code/session_01TB2aRDJ5BMx6m5c7jDzkqM Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> (cherry picked from commit f25814c)
…9443) * [WEB-8289] fix: scope draft-to-issue conversion to the draft owner (GHSA-vfqm-7rh7-84hq) WorkspaceDraftIssueViewSet.create_draft_to_issue resolved the draft with get_queryset().filter(pk=draft_id) — scoped only to the workspace, no created_by check — while the decorator admits any workspace ADMIN/MEMBER. Drafts are private to their creator, so any member could pass another user's draft_id to convert that draft into an issue, reassign (steal) its file assets, and delete the victim's draft. Scope the lookup to created_by=request.user (mirroring retrieve / partial_update) and 404 when no owned draft matches. The explicit null guard also removes a latent AttributeError on the following .project_id access when the draft is not found. Adds contract regression tests (fail-before verified): a non-owner member is rejected with 404 and the draft + its assets are left intact, with a positive control confirming the owner can still convert. Co-authored-by: Plane AI <noreply@plane.so> * [WEB-8289] test: make fixture ownership real (address Copilot review) Project and FileAsset fixtures passed created_by=create_user to objects.create(), but BaseModel.save() nulls created_by when there is no current request user (tests), so the ownership claim was silently a no-op. Instantiate + save(created_by_id=...) instead — same pattern already used for the draft — so the fixtures are accurate. Co-authored-by: Plane AI <noreply@plane.so> * chore(security): drop advisory identifiers from code comments Explanations kept unchanged; only the IDs are removed. Co-authored-by: Plane AI <noreply@plane.so> --------- Co-authored-by: Plane AI <noreply@plane.so> (cherry picked from commit a896115)
…invited project (#9866) `ProjectJoinEndpoint.post` looked up the accepting user's existing `ProjectMember` row by `(workspace_id, member)` and omitted `project_id`. Accepting an invitation to project Y therefore reactivated whatever membership row the user already had in the workspace -- typically a project they had been removed from -- while project Y itself never received a membership row at all. A companion no-op, `project_member.role = project_member.role`, left the stale row at its original role, so a user removed as ADMIN from another project came back as ADMIN even though the new invite was for a GUEST. Scope the lookup to `project_id` and apply `project_invite.role` on reactivation. The correct, project-scoped pattern already existed in `UserProjectInvitationsViewset.create` in the same file. Adds three regression tests covering: the invited project receives the membership, an unrelated membership is not reactivated, and a rejoin applies the invitation's role rather than the stale one. (cherry picked from commit bae037c)
…9734) * chore: upgrade pnpm to 11.10, turbo to 2.10.11, patch postcss-selector-parser - packageManager: pnpm@11.3.0 -> pnpm@11.10.0 - turbo catalog: 2.9.18 -> 2.10.11 (latest) - override postcss-selector-parser >=7.1.0 <7.1.3 to 7.1.3 (dependabot alert #345, DoS via uncontrolled AST recursion) * chore: remove comment from postcss-selector-parser override (cherry picked from commit 3717500)
Bumps the npm_and_yarn group with 1 update in the / directory: [sanitize-html](https://github.com/apostrophecms/apostrophe/tree/HEAD/packages/sanitize-html). Updates `sanitize-html` from 2.17.5 to 2.17.7 - [Changelog](https://github.com/apostrophecms/apostrophe/blob/main/packages/sanitize-html/CHANGELOG.md) - [Commits](https://github.com/apostrophecms/apostrophe/commits/HEAD/packages/sanitize-html) --- updated-dependencies: - dependency-name: sanitize-html dependency-version: 2.17.7 dependency-type: direct:production dependency-group: npm_and_yarn ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> (cherry picked from commit cb28b70)
(cherry picked from commit 97ea97e, adapted: web/admin keep nginx on this branch, so only the APK security-upgrade layer is applied there)
* fix(security): clear the 2026-09-10 nightly Trivy findings Two advisories published 2026-09-08 21:2x UTC reached Trivy's DB between the 09-09 nightly (green on every plane-* leg) and the 09-10 one, turning plane-live-community and plane-space-community red on unchanged images. - js-yaml 4.3.1 -> 4.3.2 (CVE-2026-84375). The exact pin was the patched version at the time and became the vulnerable one; the comment now says to bump rather than widen it, since a range drifts onto the 5.x break. - sharp ^0.35.3 -> ^0.35.4 (GHSA-rgj7-g3m4-5g8c, libheif GHSA-g89c-p67h-r497 and GHSA-2jg2-4ch7-h545). The third finding on plane-proxy/admin/frontend-community, CVE-2026-84445 in the Caddy binary's grpc-go, needs no change here: 2.11.4 is the newest Caddy published, and the nightly scan reads plane-ee's .trivyignore.yaml for every leg, where it is suppressed at **/bin/caddy on the reachability argument that Caddy runs no gRPC xDS server. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * chore: drop the added comment from pnpm-workspace.yaml Review feedback from @sriramveeraghanta. The diff is now two version changes and nothing else; the advisory ids and the reasoning stay in the PR description. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> (cherry picked from commit 2f895b8)
- vitest / @vitest/mocker: bump catalog to ^4.1.11 (path traversal / arbitrary file read via redirect mock) - qs: pin override to 6.16.0 (DoS via attacker-controlled isBuffer, array-limit bypass via bracket-key comma parsing) - morgan: pin override to 1.12.0 (log forging via unescaped Unicode line separators) - postcss-selector-parser: pin the 6.1.x line to 6.1.3 (DoS via uncontrolled AST recursion); existing override only covered 7.x - djangorestframework: 3.17.1 -> 3.17.2 (DATA_UPLOAD_MAX_MEMORY_SIZE bypass, AdminRenderer data disclosure) @tiptap/core is intentionally left on v2: the advisory is only patched in 3.30.4, a breaking major upgrade that needs a dedicated migration. (cherry picked from commit a0efb67)
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Advanced Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Copilot review overview
🟡 Changes recommended
Critical API intake validation and AIO image hardening issues remain unresolved.
Get a fresh assessment by requesting another Copilot review.
Review effort: Lite
Findings: 2
Open (6)
Issue intake updates omit required serializer project context · New Node library copy can overwrite upgraded Alpine packages · New Admin Dockerfile pins outdated Turbo version · New Live Dockerfile pins outdated Turbo version · New Space Dockerfile pins outdated Turbo version · New Web Dockerfile pins outdated Turbo version · New
What changed in this PR
Plane v1.4.3 is a patch release containing security fixes, dependency and container CVE updates, infrastructure changes, and targeted backend bug fixes.
Changes:
- Updated dependencies, package versions, lockfiles, Docker images, and MinIO sources.
- Added authentication throttling, access scoping, secret redaction, ordering safeguards, and validation.
- Added regression and contract test coverage.
| File | Summary |
|---|---|
pnpm-workspace.yaml |
Dependency catalog and security override updates. |
packages/utils/package.json |
Package version bump. |
packages/ui/package.json |
Package version bump. |
packages/typescript-config/package.json |
Package version bump. |
packages/types/package.json |
Package version bump. |
packages/tailwind-config/package.json |
Package version bump. |
packages/shared-state/package.json |
Package version bump. |
packages/services/package.json |
Package version bump. |
packages/propel/package.json |
Package version bump. |
packages/logger/package.json |
Package version bump. |
packages/i18n/package.json |
Package version bump. |
packages/hooks/package.json |
Package version bump. |
packages/editor/package.json |
Package version bump. |
packages/constants/package.json |
Package version bump. |
packages/codemods/package.json |
Package version bump. |
package.json |
Release version and pnpm update. |
docker-compose.yml |
MinIO image pinned to Quay. |
docker-compose-test.yml |
Test MinIO image pinned to Quay. |
docker-compose-local.yml |
Local MinIO image pinned to Quay. |
deployments/cli/community/docker-compose.yml |
Community MinIO image pinned to Quay. |
deployments/aio/community/supervisor.conf |
Removes the runtime npx dependency. |
deployments/aio/community/Dockerfile |
AIO image hardening; critical (1 vote) finding that copying /usr/lib after upgrades can replace fixed libraries. |
apps/web/package.json |
Application version bump. |
apps/web/Dockerfile.web |
Alpine hardening; moderate (2 votes) finding that the explicit Turbo pin remains at 2.9.18. |
apps/space/package.json |
Application version bump. |
apps/space/Dockerfile.space |
Runtime hardening; moderate (2 votes) finding that the explicit Turbo pin remains at 2.9.18. |
apps/proxy/Dockerfile.ce |
Caddy and Go dependency updates. |
apps/live/package.json |
Application version bump. |
apps/live/Dockerfile.live |
Runtime hardening; moderate (2 votes) finding that the explicit Turbo pin remains at 2.9.18. |
apps/api/tests/RUNNING_TESTS.md |
Test-stack documentation updates. |
apps/api/requirements/base.txt |
Django REST Framework security update. |
apps/api/plane/utils/order_queryset.py |
Page ordering allowlist. |
apps/api/plane/tests/unit/views/test_issue_link.py |
Conditional issue-link crawling tests. |
apps/api/plane/tests/unit/serializers/test_issue_serializer_api.py |
Assignee and label validation tests. |
apps/api/plane/tests/contract/app/test_webhook_secret_key_scope_app.py |
Webhook secret visibility tests. |
apps/api/plane/tests/contract/app/test_sub_issue_cross_project_scope_app.py |
Sub-issue project-scoping tests. |
apps/api/plane/tests/contract/app/test_spaces_board_object_scope_app.py |
Spaces board object-scoping tests. |
apps/api/plane/tests/contract/app/test_project_join_cross_project_activation.py |
Project invitation activation tests. |
apps/api/plane/tests/contract/app/test_page_order_by_allowlist_app.py |
Safe page-ordering tests. |
apps/api/plane/tests/contract/app/test_draft_to_issue_owner_scope_app.py |
Draft ownership enforcement tests. |
apps/api/plane/tests/contract/app/test_authentication.py |
Authentication throttling tests; moderate (1 vote) finding regarding clearing the entire shared cache. |
apps/api/plane/tests/contract/api/test_issue_assignee_label_validation.py |
Assignment validation tests; moderate (1 vote) finding that both Celery settings must be restored. |
apps/api/plane/space/views/issue.py |
Scopes public issue, comment, reaction, and vote access. |
apps/api/plane/space/views/intake.py |
Binds intake submissions to boards. |
apps/api/plane/authentication/views/space/magic.py |
Space magic-link throttling. |
apps/api/plane/authentication/views/space/email.py |
Space password throttling. |
apps/api/plane/authentication/views/app/magic.py |
App magic-link throttling. |
apps/api/plane/authentication/views/app/email.py |
App password throttling. |
apps/api/plane/authentication/rate_limit.py |
Redirect-flow throttle decorator. |
apps/api/plane/app/views/workspace/draft.py |
Restricts draft conversion to owners. |
apps/api/plane/app/views/webhook/base.py |
Enables controlled webhook secret disclosure. |
apps/api/plane/app/views/project/invite.py |
Scopes membership activation and invite roles. |
apps/api/plane/app/views/page/base.py |
Sanitizes page ordering. |
apps/api/plane/app/views/issue/sub_issue.py |
Scopes sub-issue reads and writes. |
apps/api/plane/app/views/issue/link.py |
Avoids unnecessary link recrawls. |
apps/api/plane/app/views/cycle/archive.py |
Handles cycles without end dates. |
apps/api/plane/app/serializers/webhook.py |
Hides webhook secrets by default. |
apps/api/plane/api/views/issue.py |
Avoids unnecessary API link recrawls. |
apps/api/plane/api/views/cycle.py |
Handles cycles without end dates. |
apps/api/plane/api/serializers/issue.py |
Rejects invalid assignee and label IDs; critical (1 vote) finding that missing project context breaks valid API intake updates. |
apps/api/package.json |
API version bump. |
apps/api/Dockerfile.api |
Python and Alpine package updates. |
apps/admin/package.json |
Application version bump. |
apps/admin/Dockerfile.admin |
Alpine hardening; moderate (2 votes) finding that the explicit Turbo pin remains at 2.9.18. |
Files not reviewed (1)
- pnpm-lock.yaml: Generated file
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| valid_assignee_ids = set( | ||
| ProjectMember.objects.filter( | ||
| project_id=self.context.get("project_id"), | ||
| is_active=True, | ||
| role__gte=15, | ||
| member_id__in=data["assignees"], | ||
| ).values_list("member_id", flat=True) |
| # bump APK_SECURITY_PATCH to bust buildx's cached layer (keyed on this command | ||
| # string), otherwise a rebuild silently re-ships the old packages. | ||
| ARG APK_SECURITY_PATCH=2026-09-07 | ||
| RUN echo "apk-security-patch ${APK_SECURITY_PATCH}" && apk update && apk upgrade --no-cache --available |
| ARG APK_SECURITY_PATCH=2026-09-07 | ||
| RUN echo "apk-security-patch ${APK_SECURITY_PATCH}" && apk update && apk upgrade --no-cache --available && rm -rf /var/cache/apk/* |
| ARG APK_SECURITY_PATCH=2026-09-07 | ||
| RUN echo "apk-security-patch ${APK_SECURITY_PATCH}" && apk update && apk upgrade --no-cache --available |
| ARG APK_SECURITY_PATCH=2026-09-07 | ||
| RUN echo "apk-security-patch ${APK_SECURITY_PATCH}" && apk update && apk upgrade --no-cache --available |
| ARG APK_SECURITY_PATCH=2026-09-07 | ||
| RUN echo "apk-security-patch ${APK_SECURITY_PATCH}" && apk update && apk upgrade --no-cache --available && rm -rf /var/cache/apk/* |


Summary
v1.4.3 patch release, cut from
master(v1.4.2). It contains cherry-picked security fixes, dependency and container CVE fixes, and a few low-risk bug fixes frompreview. The propel, i18n, refactor, perf and CI changes are left out.Security fixes
secret_keyon reads ([WEB-8103] fix: stop leaking webhook HMAC secret_key on reads #9382)order_byagainst an allowlist ([WEB-8110] fix: sanitize page list order_by against an allowlist #9387)Dependency and container CVE fixes
apk upgrade --availablelayers; Caddy/Go module bumps in the proxy; npm removed from the runtime images.pnpm install --lockfile-only) rather than taken from the textual merge.Infra
Bug fixes
Version
Testing
pnpm install --frozen-lockfile,pnpm check:types(28/28), and builds for web, space, admin and live all pass.test_projects_lite.py(429 from the API-key throttle) and depend on test order: the file passes on its own, andpreviewfails the same way. The throttle cache isn't reset between API-key contract test modules. This is a test-isolation problem, not a product regression. Follow-up onpreview.