From 7cef12463c8cc1a66107e473897fc37bca5bf36d Mon Sep 17 00:00:00 2001 From: ulferts Date: Fri, 24 Oct 2025 18:19:11 +0200 Subject: [PATCH 1/3] work around poor PgSQL query planning --- app/models/work_packages/scopes/allowed_to.rb | 26 ++++++++++++++----- 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/app/models/work_packages/scopes/allowed_to.rb b/app/models/work_packages/scopes/allowed_to.rb index a867f0a72b1..6ebaa0536ce 100644 --- a/app/models/work_packages/scopes/allowed_to.rb +++ b/app/models/work_packages/scopes/allowed_to.rb @@ -127,14 +127,28 @@ module WorkPackages::Scopes # ) # ``` # Postgresql however sometimes turns to a sequential scan with the query above. + # + # Index scans can still happen in the combination of the CTE with the check outside of the + # CTEs for the existence of any record. + # This is particularly likely in case AR.exists? is used which adds a LIMIT 1 + # to the query. In this case, there is a known shortcoming that PostgreSQL's query planner + # will make poor choices + # (https://www.postgresql.org/message-id/flat/CA%2BU5nMLbXfUT9cWDHJ3tpxjC3bTWqizBKqTwDgzebCB5bAGCgg%40mail.gmail.com). + # + # Once AR supports adding materialization hints (https://github.com/rails/rails/pull/54322), the inner + # `allowed` CTE can be abandoned as it is only used for being able to provide such a hint. allowed_by_projects_and_work_packages = Arel.sql(<<~SQL.squish) - SELECT id from work_packages - WHERE project_id in (SELECT id FROM member_projects) - AND NOT EXISTS ( - SELECT 1 FROM entity_member_projects_without_duplicates - WHERE entity_member_projects_without_duplicates.id = work_packages.project_id - AND entity_member_projects_without_duplicates.entity_id != work_packages.id + WITH allowed AS MATERIALIZED ( + SELECT id from work_packages + WHERE project_id in (SELECT id FROM member_projects) + AND NOT EXISTS ( + SELECT 1 FROM entity_member_projects_without_duplicates + WHERE entity_member_projects_without_duplicates.id = work_packages.project_id + AND entity_member_projects_without_duplicates.entity_id != work_packages.id + ) ) + + SELECT * from allowed SQL with(member_projects: Arel.sql(allowed_via_project_or_work_package_membership.to_sql), From c84f747fb8add461936450044f3a9d499e057dc0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oliver=20G=C3=BCnther?= Date: Mon, 27 Oct 2025 08:42:33 +0100 Subject: [PATCH 2/3] Introduce helper to look up asset --- .../homescreen/blocks/new_features.rb | 2 +- app/helpers/asset_helper.rb | 44 +++++++++++++++++++ 2 files changed, 45 insertions(+), 1 deletion(-) create mode 100644 app/helpers/asset_helper.rb diff --git a/app/components/homescreen/blocks/new_features.rb b/app/components/homescreen/blocks/new_features.rb index 8769ac42d63..e39868d6abd 100644 --- a/app/components/homescreen/blocks/new_features.rb +++ b/app/components/homescreen/blocks/new_features.rb @@ -65,7 +65,7 @@ module Homescreen end def has_image? - Rails.application.assets_manifest.assets.has_key?(feature_teaser_image) + helpers.has_rails_asset?(feature_teaser_image) end private diff --git a/app/helpers/asset_helper.rb b/app/helpers/asset_helper.rb new file mode 100644 index 00000000000..a764ca015e2 --- /dev/null +++ b/app/helpers/asset_helper.rb @@ -0,0 +1,44 @@ +# frozen_string_literal: true + +#-- copyright +# OpenProject is an open source project management software. +# Copyright (C) the OpenProject GmbH +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2013 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See COPYRIGHT and LICENSE files for more details. +#++ + +module AssetHelper + ## + # In development mode, assets are dynamically compiled + # while in production, they are precompiled. + # + # This results in different ways to check for the existence of an asset. + def has_rails_asset?(logical_path) + if Rails.configuration.assets.compile + Rails.application.assets.find_asset(logical_path).present? + else + Rails.application.assets_manifest.assets[logical_path].present? + end + end +end From 6bf4c0b67ca934c0a32721fe2451aa4159e2a697 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oliver=20G=C3=BCnther?= Date: Mon, 27 Oct 2025 10:36:46 +0100 Subject: [PATCH 3/3] Bump node to 22.21.0 Addresses CVE-2025-23166 --- .github/copilot-instructions.md | 4 ++-- docker/ci/Dockerfile | 2 +- docker/dev/frontend/Dockerfile | 2 +- docker/prod/Dockerfile | 2 +- docs/development/development-environment/linux/README.md | 8 ++++---- docs/development/development-environment/macos/README.md | 8 ++++---- .../installation/manual/README.md | 2 +- package-lock.json | 2 +- package.json | 2 +- 9 files changed, 16 insertions(+), 16 deletions(-) diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index 40430cc5bce..2b5b70d9550 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -7,7 +7,7 @@ - **Size**: Large monorepo (~840MB, ~1M+ lines of code) - **History**: Originally forked from Redmine over a decade ago, evolved significantly as an independent project - **Backend**: Ruby 3.4.5, Rails ~8.0.3 -- **Frontend**: Node.js 22.15.0, npm 10.1.0+, TypeScript +- **Frontend**: Node.js 22.21.0, npm 10.1.0+, TypeScript - **Database**: PostgreSQL (required) - **Architecture**: Server-rendered HTML with Hotwire (Turbo + Stimulus). Legacy Angular components exist and are being migrated to custom elements. Uses GitHub's Primer Design System via ViewComponent. - **Editions**: OpenProject comes in Community and Enterprise editions @@ -19,7 +19,7 @@ ### Ruby and Node Versions **ALWAYS verify versions before building:** - Ruby: `3.4.5` (see `.ruby-version`) -- Node: `^22.15.0` (see `package.json` engines) +- Node: `^22.21.0` (see `package.json` engines) - Bundler: Latest 2.x ### Development Environment Options diff --git a/docker/ci/Dockerfile b/docker/ci/Dockerfile index 35522fbccdf..425d3652ff2 100644 --- a/docker/ci/Dockerfile +++ b/docker/ci/Dockerfile @@ -2,7 +2,7 @@ ARG RUBY_VERSION FROM ruby:${RUBY_VERSION}-trixie -ENV NODE_VERSION="22.15.0" +ENV NODE_VERSION="22.21.0" ENV DEBIAN_FRONTEND=noninteractive ENV BUNDLE_WITHOUT="development:production:docker" diff --git a/docker/dev/frontend/Dockerfile b/docker/dev/frontend/Dockerfile index 5858dc69145..985cd8c2bdb 100644 --- a/docker/dev/frontend/Dockerfile +++ b/docker/dev/frontend/Dockerfile @@ -1,4 +1,4 @@ -FROM node:22.15.0 +FROM node:22.21.0 LABEL org.opencontainers.image.authors="operations@openproject.com" ARG DEV_UID=1000 diff --git a/docker/prod/Dockerfile b/docker/prod/Dockerfile index 71cccc8a4a9..3346958ea00 100755 --- a/docker/prod/Dockerfile +++ b/docker/prod/Dockerfile @@ -6,7 +6,7 @@ ARG BUILDKIT_SBOM_SCAN_STAGE=true FROM ruby:${RUBY_VERSION}-${DEBIAN_BASE} AS base LABEL maintainer="operations@openproject.com" -ARG NODE_VERSION="22.15.0" +ARG NODE_VERSION="22.21.0" ARG BIM_SUPPORT=true ENV USE_JEMALLOC=false ENV DEBIAN_FRONTEND=noninteractive diff --git a/docs/development/development-environment/linux/README.md b/docs/development/development-environment/linux/README.md index 3b5ca51cb2b..6c222b0c829 100644 --- a/docs/development/development-environment/linux/README.md +++ b/docs/development/development-environment/linux/README.md @@ -161,11 +161,11 @@ git clone https://github.com/nodenv/node-build.git $(nodenv root)/plugins/node-b You can find the latest LTS version here: [nodejs.org/en/download/](https://nodejs.org/en/download/) -At the time of writing this is v22.15.0 Install and activate it with: +At the time of writing this is v22.21.0 Install and activate it with: ```shell -nodenv install 22.15.0 -nodenv global 22.15.0 +nodenv install 22.21.0 +nodenv global 22.21.0 nodenv rehash ``` @@ -187,7 +187,7 @@ bundler --version Bundler version 2.7.2 node --version -v22.15.0 +v22.21.0 npm --version 10.5.0 diff --git a/docs/development/development-environment/macos/README.md b/docs/development/development-environment/macos/README.md index 96423603231..e1f2e45519c 100644 --- a/docs/development/development-environment/macos/README.md +++ b/docs/development/development-environment/macos/README.md @@ -115,11 +115,11 @@ nodenv init You can find the latest LTS version here: [nodejs.org/en/download](https://nodejs.org/en/download/) -At the time of writing this is v22.15.0. Install and activate it with: +At the time of writing this is v22.21.0. Install and activate it with: ```shell -nodenv install 22.15.0 -nodenv global 22.15.0 +nodenv install 22.21.0 +nodenv global 22.21.0 ``` #### Update NPM to the latest version @@ -140,7 +140,7 @@ $ bundler --version Bundler version 2.7.2 node --version -v22.15.0 +v22.21.0 npm --version 10.5.0 diff --git a/docs/installation-and-operations/installation/manual/README.md b/docs/installation-and-operations/installation/manual/README.md index 04f0d53c4f4..57eef1cc4e4 100644 --- a/docs/installation-and-operations/installation/manual/README.md +++ b/docs/installation-and-operations/installation/manual/README.md @@ -149,7 +149,7 @@ time to finish. To check our Node installation we run `node --version`. It should output something very similar to: ```text -v22.15.0 +v22.21.0 ``` ## Installation of OpenProject diff --git a/package-lock.json b/package-lock.json index 41c3d1013ec..d46a6752d61 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,7 +15,7 @@ "@redocly/openapi-cli": "^1.0.0-beta.80" }, "engines": { - "node": "^22.15.0", + "node": "^22.21.0", "npm": "^10.1.0" } }, diff --git a/package.json b/package.json index a89014d5f4b..0fa91d81f6e 100644 --- a/package.json +++ b/package.json @@ -10,7 +10,7 @@ }, "private": true, "engines": { - "node": "^22.15.0", + "node": "^22.21.0", "npm": "^10.1.0" }, "devDependencies": {