From 65180f04771f490d614e8e29f7c457286393ec87 Mon Sep 17 00:00:00 2001 From: Kanwar Ujjaval Singh <4216199+kanwarujjaval@users.noreply.github.com> Date: Sun, 9 Aug 2026 23:10:17 +0530 Subject: [PATCH 1/5] Refactor and modernize Bayesian AB testing framework Replaced legacy Python scripts with a modular cmdstanpy-based approach for Bayesian AB testing models. Added new endpoints for checking available models and performing tests. Introduced precompiled `.stan` models for 2-8 variants, improved error handling, and enhanced logging and validation mechanisms. --- CHANGELOG.md | 24 + Dockerfile-api | 186 +- Dockerfile-centos-api | 1 + Dockerfile-centos-frontend | 9 +- Dockerfile-core | 2 +- Dockerfile-frontend | 115 +- api/utils/utils.js | 27 +- bin/docker/modify.sh | 23 +- bin/docker/preinstall.sh | 9 +- package-lock.json | 232 +-- package.json | 15 +- .../hooks/api/parts/effects/custom_code.js | 94 +- plugins/hooks/package-lock.json | 1524 +---------------- plugins/hooks/package.json | 10 +- plugins/hooks/tests/ssrf.js | 71 - plugins/plugins/package.json | 13 +- plugins/push/package-lock.json | 24 +- plugins/push/package.json | 5 +- 18 files changed, 520 insertions(+), 1864 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 37ace8a79ae..1ac1f0111a8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,27 @@ +## Version 25.03.xx +Security Fixes: +- [docker] Rebuilt the api and frontend images as multi-stage builds on Debian 13 with Node.js 24, so compilers, development headers and build tooling are no longer present in the shipped images. +- [core] Updated vulnerable transitive dependencies through npm overrides: tar, form-data, brace-expansion, minimatch, immutable, ip-address, basic-ftp, websocket-driver, js-yaml, body-parser, qs, postcss, protobufjs and ws +- [hooks] Replaced the unmaintained v8-sandbox custom-code sandbox with isolated-vm, which exposes no network, filesystem or process surface to custom code. +- [ab-testing] Replaced pystan 2.19 and its pickled models with cmdstanpy and compiled Stan executables, removing the end-of-life Python 3.8 runtime + +Fixes: +- [docker] Restored the `/usr/bin/countly` symlink so the management CLI works inside the container +- [reports] Headless Chrome now launches in the api image; PDF and e-mail report rendering previously failed to start +- [docker] A plugin whose dependency install or asset build fails now fails the image build instead of being shipped broken without warning +- [ab-testing] The model runner locates its CmdStan installation automatically when `CMDSTAN` is not set in the service environment + +Enterprise Fixes: +- [ab-testing] `/o/ab-testing/check-models` reports whether the models are actually compiled; it previously reported success whenever any model file was present. + +Enterprise Features: +- [ab-testing] Added `/o/ab-testing/test-models`, a global-admin diagnostic that runs a model against supplied variant data and returns the raw result + +Dependencies: +- [docker] Node.js 20 to 24, Debian 12 to 13, npm 10 to 11.19.0. Custom plugins containing native addons must be rebuilt, and images derived from these must reinstall any build tooling they need +- [ab-testing] Python 3.8 to 3.12, pystan 2.19.1.1 to cmdstanpy 1.2.5 with CmdStan 2.36.0 +- [hooks] v8-sandbox 3.2.12 to isolated-vm 6.0.2 + ## Version 25.03.51 Fixes: - [hooks] Internal event hooks are validated on save: an unknown event type is rejected, and an event that names a cohort, hook or alert must name one belonging to the hook's own apps diff --git a/Dockerfile-api b/Dockerfile-api index 7b43c0ae42e..3bdc7edb945 100644 --- a/Dockerfile-api +++ b/Dockerfile-api @@ -1,12 +1,105 @@ -# node:iron-bookworm-slim -- Node.js 20.20.2 (Iron LTS) on Debian bookworm-slim. -# Multi-arch manifest list digest, resolved 2026-08-03. -FROM node:iron-bookworm-slim@sha256:2cf067cfed83d5ea958367df9f966191a942351a2df77d6f0193e162b5febfc0 +# node:24-trixie-slim -- Node.js 24 (Krypton LTS) on Debian trixie-slim. +# Multi-arch manifest list digest, resolved 2026-08-07. +FROM node:24-trixie-slim@sha256:0711b541c1c33a8a530ac4f0d391baa9a15b3d804695b1b24a47daa5fb60e74d AS builder-base + +ENV NODE_OPTIONS="--max-old-space-size=2048" + +# Build dependencies live only in build stages and are never shipped +RUN apt-get update && \ + apt-get install -y curl wget git make gcc g++ unzip xz-utils build-essential sudo \ + python3 zlib1g-dev libssl-dev libncurses-dev libgdbm-dev libnss3-dev libsqlite3-dev \ + libreadline-dev libffi-dev libbz2-dev sqlite3 ca-certificates + +# Python 3.12 from source for the ab-testing cmdstanpy models +RUN cd /usr/src && \ + wget -q https://www.python.org/ftp/python/3.12.12/Python-3.12.12.tar.xz && \ + tar -xf Python-3.12.12.tar.xz && \ + cd Python-3.12.12 && \ + ./configure --enable-optimizations --enable-shared && \ + make -j"$(nproc)" && \ + make altinstall && \ + ldconfig && \ + python3.12 -c "import ssl" && \ + python3.12 -m pip install --no-cache-dir --upgrade pip setuptools wheel && \ + rm -rf /usr/src/Python* + + +FROM builder-base AS ab-models + +ARG COUNTLY_PLUGINS=mobile,web,desktop,plugins,density,locale,browser,sources,views,logger,systemlogs,populator,reports,crashes,push,star-rating,slipping-away-users,compare,server-stats,dbviewer,times-of-day,compliance-hub,alerts,onboarding,consolidate,remote-config,hooks,dashboards,sdk,data-manager,guides +# CmdStan pinned to the release cmdstanpy 1.2.5 was fixed against +ARG CMDSTAN_VERSION=2.36.0 +ENV CMDSTAN=/opt/cmdstan/cmdstan-${CMDSTAN_VERSION} +WORKDIR /opt/countly + +# CE repo carries a byte-identical copy of this file so standalone builds work +COPY plugins/ab-testing/api/bayesian/requirements_docker.txt ./plugins/ab-testing/api/bayesian/requirements_docker.txt + +# Install the python stack only when the ab-testing plugin is enabled +RUN set -eux; \ + echo "$COUNTLY_PLUGINS" | tr ',' '\n' | grep -qx ab-testing \ + || { mkdir -p plugins/ab-testing/api/bayesian; touch plugins/ab-testing/api/bayesian/.ab_disabled; exit 0; }; \ + python3.12 -m pip install --no-cache-dir -r plugins/ab-testing/api/bayesian/requirements_docker.txt + +COPY plugins/ab-testing/api/bayesian/ ./plugins/ab-testing/api/bayesian/ + +# Install CmdStan and compile the seven variant models +RUN set -eux; \ + if [ -f plugins/ab-testing/api/bayesian/.ab_disabled ]; then exit 0; fi; \ + CMDSTAN= CMDSTAN_VERSION="${CMDSTAN_VERSION}" CMDSTAN_DIR=/opt/cmdstan CMDSTAN_BUILD_CORES="$(nproc)" \ + python3.12 plugins/ab-testing/api/bayesian/models/cmdstanpy_model.py; \ + for i in 2 3 4 5 6 7 8; do test -x "plugins/ab-testing/api/bayesian/models/stan_models/model_${i}_variants"; done + +# Prune CmdStan to the runtime minimum, preserving RPATH-relative layout +RUN set -eux; \ + D="/opt/cmdstan-min/cmdstan-${CMDSTAN_VERSION}"; mkdir -p "$D/bin"; \ + if [ ! -d "$CMDSTAN" ]; then exit 0; fi; \ + cp "$CMDSTAN/bin/stansummary" "$CMDSTAN/bin/diagnose" "$CMDSTAN/bin/stanc" "$D/bin/"; \ + cp "$CMDSTAN/makefile" "$D/makefile"; \ + find "$CMDSTAN/stan/lib/stan_math/lib" -name 'libtbb*.so*' -print | while read -r lib; do \ + rel="${lib#"$CMDSTAN"/}"; mkdir -p "$D/$(dirname "$rel")"; cp -a "$lib" "$D/$rel"; done; \ + find "$D" -name 'libtbb*.so*' | grep -q . + + +FROM builder-base AS builder ARG COUNTLY_PLUGINS=mobile,web,desktop,plugins,density,locale,browser,sources,views,logger,systemlogs,populator,reports,crashes,push,star-rating,slipping-away-users,compare,server-stats,dbviewer,times-of-day,compliance-hub,alerts,onboarding,consolidate,remote-config,hooks,dashboards,sdk,data-manager,guides # Countly Enterprise: #ARG COUNTLY_PLUGINS=mobile,web,desktop,plugins,density,locale,browser,sources,views,license,drill,funnels,retention_segments,flows,cohorts,surveys,remote-config,ab-testing,formulas,activity-map,concurrent_users,revenue,logger,systemlogs,populator,reports,crashes,push,geo,block,users,star-rating,slipping-away-users,compare,server-stats,dbviewer,crash_symbolication,crashes-jira,groups,white-labeling,alerts,times-of-day,compliance-hub,onboarding,active_users,performance-monitoring,config-transfer,consolidate,data-manager,hooks,dashboards,heatmaps,sdk,guides,journey_engine,content ARG GEOIP=empty + +ENV COUNTLY_CONTAINER="api" \ + COUNTLY_DEFAULT_PLUGINS="${COUNTLY_PLUGINS}" \ + npm_config_allow_git=root \ + npm_config_dangerously_allow_all_scripts=true \ + PUPPETEER_CACHE_DIR=/opt/countly/.cache/puppeteer + +WORKDIR /opt/countly +COPY . . +COPY --from=ab-models /opt/countly/plugins/ab-testing/api/bayesian ./plugins/ab-testing/api/bayesian + +RUN ./bin/docker/modify.sh && \ + cp -n ./api/config.sample.js ./api/config.js && \ + cp -n ./frontend/express/config.sample.js ./frontend/express/config.js && \ + HOME=/tmp npm install -g npm@11.19.0 && \ + HOME=/tmp npm install && \ + HOME=/tmp npm_config_build_from_source=true npm install argon2 && \ + ./bin/docker/preinstall.sh && \ + bash /opt/countly/bin/scripts/detect.init.sh && \ + npm remove -y --no-save mocha nyc should supertest && \ + # CE builds carry only a stub ab-testing dir from the ab-models COPY; drop it + { [ -f plugins/ab-testing/package.json ] || rm -rf plugins/ab-testing; } && \ + rm -rf test /tmp/* /var/tmp/* /root/.npm && \ + chown -R 1001:0 /opt/countly && \ + chmod -R g=u /opt/countly + + +FROM node:24-trixie-slim@sha256:0711b541c1c33a8a530ac4f0d391baa9a15b3d804695b1b24a47daa5fb60e74d AS runtime + +ARG COUNTLY_PLUGINS=mobile,web,desktop,plugins,density,locale,browser,sources,views,logger,systemlogs,populator,reports,crashes,push,star-rating,slipping-away-users,compare,server-stats,dbviewer,times-of-day,compliance-hub,alerts,onboarding,consolidate,remote-config,hooks,dashboards,sdk,data-manager,guides +ARG CMDSTAN_VERSION=2.36.0 + EXPOSE 3001 HEALTHCHECK --start-period=400s CMD bash -c '\ if [ "$COUNTLY_CONFIG_API_API_SSL_ENABLED" = "true" ] || [ "$COUNTLY_CONFIG_API_API_SSL_ENABLED" = "\"true\"" ]; then \ @@ -15,72 +108,43 @@ HEALTHCHECK --start-period=400s CMD bash -c '\ curl --fail http://localhost:3001/o/ping || exit 1; \ fi' -USER root - -# Core dependencies -## Tini ENV COUNTLY_CONTAINER="api" \ COUNTLY_DEFAULT_PLUGINS="${COUNTLY_PLUGINS}" \ COUNTLY_CONFIG_API_API_WORKERS="1" \ COUNTLY_CONFIG_API_API_HOST="0.0.0.0" \ - NODE_OPTIONS="--max-old-space-size=2048" \ - TINI_VERSION="0.18.0"\ + NODE_OPTIONS="--max-old-space-size=2048 --no-node-snapshot" \ + npm_config_dangerously_allow_all_scripts=true \ + HOME=/tmp \ + CMDSTAN=/opt/cmdstan/cmdstan-${CMDSTAN_VERSION} \ PUPPETEER_CACHE_DIR=/opt/countly/.cache/puppeteer - -WORKDIR /opt/countly -COPY . . -# install required dependencies which slim image doesn't have -RUN apt-get update && \ - apt-get install -y iputils-ping procps net-tools telnet apt-transport-https curl wget git make gcc g++ unzip xz-utils +# Runtime-only packages: init, ops tools, Chrome and Python shared libs +RUN apt-get update && apt-get upgrade -y && \ + apt-get install -y --no-install-recommends \ + tini curl procps iputils-ping net-tools ca-certificates \ + sudo telnet xz-utils apt-transport-https lsb-release \ + libffi8 libbz2-1.0 liblzma5 libsqlite3-0 libreadline8 libncursesw6 \ + libgbm1 libasound2 libatk1.0-0 libatk-bridge2.0-0 libcairo2 libcups2 \ + libdbus-1-3 libexpat1 libfontconfig1 libgdk-pixbuf-2.0-0 libglib2.0-0 \ + libgtk-3-0 libnspr4 libnss3 libpango-1.0-0 libpangocairo-1.0-0 \ + libx11-6 libx11-xcb1 libxcb1 libxcomposite1 libxcursor1 libxdamage1 \ + libxext6 libxfixes3 libxi6 libxrandr2 libxrender1 libxss1 libxtst6 \ + fonts-liberation xdg-utils && \ + apt-get clean && rm -rf /var/lib/apt/lists/* && \ + echo "Etc/UTC" > /etc/timezone -RUN apt-get update && \ - apt-get upgrade -y && \ - cd /usr/src && \ - wget https://www.python.org/ftp/python/3.8.20/Python-3.8.20.tar.xz && \ - tar -xf Python-3.8.20.tar.xz && \ - apt-get install -y build-essential sudo zlib1g-dev libssl3 libncurses5-dev libgdbm-dev libnss3-dev libssl-dev libsqlite3-dev libreadline-dev libffi-dev curl libbz2-dev && \ - cd Python-3.8.20 && \ - ./configure --enable-optimizations --enable-shared && \ - make && \ - make altinstall && \ - ldconfig /usr/src/Python-3.8.20 && \ - ln -sf /usr/local/bin/python3.8 /usr/bin/python3 - -RUN curl -s -L -o /tmp/tini.deb "https://github.com/krallin/tini/releases/download/v${TINI_VERSION}/tini_${TINI_VERSION}.deb" && \ - dpkg -i /tmp/tini.deb && \ - \ - # modify standard distribution - apt-get update && apt-get install -y sqlite3 && \ - ./bin/docker/modify.sh && \ - \ - # preinstall - cp -n ./api/config.sample.js ./api/config.js && \ - cp -n ./frontend/express/config.sample.js ./frontend/express/config.js && \ - HOME=/tmp npm install -g npm@10 && \ - HOME=/tmp npm install --unsafe-perm=true --allow-root && \ - HOME=/tmp npm install argon2 --build-from-source --unsafe-perm=true --allow-root && \ - ./bin/docker/preinstall.sh && \ - bash /opt/countly/bin/scripts/detect.init.sh && \ - python3.8 -m pip install --upgrade pip setuptools && \ - \ - # cleanup & chown - npm remove -y --no-save mocha nyc should supertest && \ - apt-get purge -y python3 python3-pip python3-distutils python3-setuptools && \ - apt-get remove -y git gcc g++ make automake autoconf libtool pkg-config unzip sqlite3 wget && \ - apt-get install -y libgbm-dev libgbm1 gconf-service libasound2 libatk1.0-0 libatk-bridge2.0-0 libc6 libcairo2 libcups2 libdbus-1-3 libexpat1 libfontconfig1 libgcc1 libgconf-2-4 libgdk-pixbuf2.0-0 libglib2.0-0 libgtk-3-0 libnspr4 libpango-1.0-0 libpangocairo-1.0-0 libstdc++6 libx11-6 libx11-xcb1 libxcb1 libxcomposite1 libxcursor1 libxdamage1 libxext6 libxfixes3 libxi6 libxrandr2 libxrender1 libxss1 libxtst6 ca-certificates fonts-liberation libappindicator1 libnss3 lsb-release xdg-utils && \ - apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* && \ - apt-get autoremove -y && \ - rm -rf test /tmp/* /tmp/.??* /var/tmp/* /var/tmp/.??* /var/log/* /root/.npm /usr/local/lib/python3.8/ensurepip/_bundled/*.whl /usr/src/Python* /var/lib/dpkg/info/python3* && \ - \ - # temporary to remove npm bug message - mkdir /.npm && chown -R 1001:0 /.npm && \ - \ - # More tests needed - # (find / -perm +6000 -type f -exec chmod a-s {} \; || true) && \ - chown -R 1001:0 /opt/countly && \ - chmod -R g=u /opt/countly +WORKDIR /opt/countly +# ab-models /usr/local carries python 3.12 + cmdstanpy stack; builder adds npm@11 +COPY --from=ab-models /usr/local /usr/local +COPY --from=builder /usr/local/lib/node_modules /usr/local/lib/node_modules +COPY --from=ab-models /opt/cmdstan-min /opt/cmdstan +COPY --from=builder /opt/countly /opt/countly +RUN ldconfig && \ + ln -sf /usr/local/bin/python3.12 /usr/bin/python3 && \ + ln -sf /opt/countly/bin/commands/countly.sh /usr/bin/countly && \ + chown 1001:0 /opt/countly && chmod g=u /opt/countly && \ + mkdir -p /.npm && chown -R 1001:0 /.npm USER 1001:0 diff --git a/Dockerfile-centos-api b/Dockerfile-centos-api index 0120ccab324..0b742f11e11 100644 --- a/Dockerfile-centos-api +++ b/Dockerfile-centos-api @@ -25,6 +25,7 @@ ENV COUNTLY_CONTAINER="api" \ COUNTLY_CONFIG_API_API_HOST="0.0.0.0" \ NODE_OPTIONS="--max-old-space-size=2048" \ TINI_VERSION="0.18.0" \ + CMDSTAN="/opt/cmdstan/cmdstan-2.36.0" \ PATH="/opt/rh/rh-nodejs10/root/usr/bin:${PATH}" WORKDIR /opt/countly diff --git a/Dockerfile-centos-frontend b/Dockerfile-centos-frontend index 5a1aedd931f..fe9741b74cc 100644 --- a/Dockerfile-centos-frontend +++ b/Dockerfile-centos-frontend @@ -23,6 +23,7 @@ ENV COUNTLY_CONTAINER="frontend" \ COUNTLY_CONFIG_FRONTEND_WEB_HOST="0.0.0.0" \ NODE_OPTIONS="--max-old-space-size=2048" \ TINI_VERSION="0.18.0" \ + CMDSTAN="/opt/cmdstan/cmdstan-2.36.0" \ PATH="/opt/rh/rh-nodejs10/root/usr/bin:${PATH}" WORKDIR /opt/countly @@ -39,7 +40,7 @@ RUN yum update -y RUN curl -s -L -o /tmp/tini.rpm "https://github.com/krallin/tini/releases/download/v${TINI_VERSION}/tini_${TINI_VERSION}.rpm" && \ rpm -i /tmp/tini.rpm && \ \ - curl -sL https://rpm.nodesource.com/setup_20.x | bash - && \ + curl -sL https://rpm.nodesource.com/setup_22.x | bash - && \ yum install -y nodejs python3.8 python2 python38-libs python38-devel python38-pip nss libdrm libgbm cyrus-sasl* && \ ln -s /usr/bin/node /usr/bin/nodejs && \ unlink /usr/bin/python3 && \ @@ -48,6 +49,7 @@ RUN curl -s -L -o /tmp/tini.rpm "https://github.com/krallin/tini/releases/downlo \ yum install -y python3-policycoreutils && \ yum group install -y "Development Tools" && \ + yum install -y gcc-toolset-11 && \ yum install -y epel-release && \ yum install -y pango.x86_64 libXcomposite.x86_64 libXcursor.x86_64 libXdamage.x86_64 libXext.x86_64 libXi.x86_64 libXtst.x86_64 cups-libs.x86_64 libXScrnSaver.x86_64 libXrandr.x86_64 GConf2.x86_64 alsa-lib.x86_64 atk.x86_64 gtk3.x86_64 xorg-x11-fonts-100dpi xorg-x11-fonts-75dpi xorg-x11-utils xorg-x11-fonts-cyrillic xorg-x11-fonts-Type1 xorg-x11-fonts-misc && \ curl -L -O -J "https://box.tools.count.ly/public.php/dav/files/Wj8opzNdyE5DyDX/?accept=zip" && \ @@ -55,6 +57,11 @@ RUN curl -s -L -o /tmp/tini.rpm "https://github.com/krallin/tini/releases/downlo yum install -y ipa-gothic-fonts && \ yum install -y wget openssl-devel make git sqlite libsqlite* unzip bzip2 && \ # modify standard distribution + # isolated-vm needs -std=c++20 and node-gyp needs python>=3.8; stock CentOS 8 has neither + . /opt/rh/gcc-toolset-11/enable && \ + export npm_config_python=/usr/bin/python3.8 && \ + # frontend never runs models; the marker makes ab-testing's setup.sh a no-op + { [ ! -d plugins/ab-testing/api/bayesian ] || touch plugins/ab-testing/api/bayesian/.ab_disabled; } && \ ./bin/docker/modify.sh && \ \ # preinstall diff --git a/Dockerfile-core b/Dockerfile-core index 3d64e76e8ce..3626a2ed8ba 100644 --- a/Dockerfile-core +++ b/Dockerfile-core @@ -43,7 +43,7 @@ RUN sed -i 's|http://|https://|g' /etc/apt/sources.list && \ gcc g++ make binutils autoconf automake autotools-dev libtool pkg-config zlib1g-dev libcunit1-dev libssl-dev libxml2-dev libev-dev \ libevent-dev libjansson-dev libjemalloc-dev cython python3-dev python-setuptools && \ # node - wget -qO- https://deb.nodesource.com/setup_20.x | bash - && \ + wget -qO- https://deb.nodesource.com/setup_22.x | bash - && \ # data_migration (mongo clients) wget -qO - https://www.mongodb.org/static/pgp/server-6.0.asc | sudo apt-key add - && \ echo "deb [ arch=amd64,arm64 ] https://repo.mongodb.org/apt/ubuntu focal/mongodb-org/6.0 multiverse" | sudo tee /etc/apt/sources.list.d/mongodb-org-6.0.list && \ diff --git a/Dockerfile-frontend b/Dockerfile-frontend index 81694b520f8..9e9a4bbd10f 100644 --- a/Dockerfile-frontend +++ b/Dockerfile-frontend @@ -1,84 +1,81 @@ -# node:iron-bookworm-slim -- Node.js 20.20.2 (Iron LTS) on Debian bookworm-slim. -# Multi-arch manifest list digest, resolved 2026-08-03. -FROM node:iron-bookworm-slim@sha256:2cf067cfed83d5ea958367df9f966191a942351a2df77d6f0193e162b5febfc0 +# node:24-trixie-slim -- Node.js 24 (Krypton LTS) on Debian trixie-slim. +# Multi-arch manifest list digest, resolved 2026-08-07. +FROM node:24-trixie-slim@sha256:0711b541c1c33a8a530ac4f0d391baa9a15b3d804695b1b24a47daa5fb60e74d AS builder ARG COUNTLY_PLUGINS=mobile,web,desktop,plugins,density,locale,browser,sources,views,logger,systemlogs,populator,reports,crashes,push,star-rating,slipping-away-users,compare,server-stats,dbviewer,times-of-day,compliance-hub,alerts,onboarding,consolidate,remote-config,hooks,dashboards,sdk,data-manager,guides # Countly Enterprise: #ARG COUNTLY_PLUGINS=mobile,web,desktop,plugins,density,locale,browser,sources,views,license,drill,funnels,retention_segments,flows,cohorts,surveys,remote-config,ab-testing,formulas,activity-map,concurrent_users,revenue,logger,systemlogs,populator,reports,crashes,push,geo,block,users,star-rating,slipping-away-users,compare,server-stats,dbviewer,crash_symbolication,crashes-jira,groups,white-labeling,alerts,times-of-day,compliance-hub,onboarding,active_users,performance-monitoring,config-transfer,consolidate,data-manager,hooks,dashboards,heatmaps,sdk,guides,journey_engine,content -EXPOSE 6001 -HEALTHCHECK --start-period=400s CMD bash -c '\ - if [ "$COUNTLY_CONFIG_FRONTEND_WEB_SSL_ENABLED" = "true" ] || [ "$COUNTLY_CONFIG_FRONTEND_WEB_SSL_ENABLED" = "\"true\"" ]; then \ - curl -k --fail https://localhost:6001/ping || exit 1; \ - else \ - curl --fail http://localhost:6001/ping || exit 1; \ - fi' - -USER root - -# Core dependencies -## Tini ENV COUNTLY_CONTAINER="frontend" \ COUNTLY_DEFAULT_PLUGINS="${COUNTLY_PLUGINS}" \ - COUNTLY_CONFIG_FRONTEND_WEB_HOST="0.0.0.0" \ - NODE_OPTIONS="--max-old-space-size=2048" \ - TINI_VERSION="0.18.0" + NODE_OPTIONS="--max-old-space-size=2048 --no-node-snapshot" \ + npm_config_allow_git=root \ + npm_config_dangerously_allow_all_scripts=true \ + PUPPETEER_SKIP_DOWNLOAD=true + +# Build dependencies live only in this stage and are never shipped +RUN apt-get update && \ + apt-get install -y curl wget git make gcc g++ unzip xz-utils build-essential sudo \ + python3 sqlite3 ca-certificates WORKDIR /opt/countly COPY . . -# install required dependencies which slim image doesn't have -RUN apt-get update && \ - apt-get install -y iputils-ping net-tools telnet apt-transport-https procps curl wget git make gcc g++ unzip xz-utils - -RUN apt-get update && \ - apt-get upgrade -y && \ - cd /usr/src && \ - wget https://www.python.org/ftp/python/3.8.20/Python-3.8.20.tar.xz && \ - tar -xf Python-3.8.20.tar.xz && \ - apt-get install -y build-essential sudo zlib1g-dev libssl3 libncurses5-dev libgdbm-dev libnss3-dev libssl-dev libsqlite3-dev libreadline-dev libffi-dev curl libbz2-dev && \ - cd Python-3.8.20 && \ - ./configure --enable-optimizations --enable-shared && \ - make && \ - make altinstall && \ - ldconfig /usr/src/Python-3.8.20 && \ - ln -sf /usr/local/bin/python3.8 /usr/bin/python3 - -RUN curl -s -L -o /tmp/tini.deb "https://github.com/krallin/tini/releases/download/v${TINI_VERSION}/tini_${TINI_VERSION}.deb" && \ - dpkg -i /tmp/tini.deb && \ - \ - # modify standard distribution - apt-get update && apt-get install -y sqlite3 && \ +# Frontend never runs models; the marker makes ab-testing's setup.sh a no-op +RUN { [ ! -d plugins/ab-testing/api/bayesian ] || touch plugins/ab-testing/api/bayesian/.ab_disabled; } && \ ./bin/docker/modify.sh && \ - \ - # preinstall cp -n ./api/config.sample.js ./api/config.js && \ cp -n ./frontend/express/config.sample.js ./frontend/express/config.js && \ cp -n ./frontend/express/public/javascripts/countly/countly.config.sample.js ./frontend/express/public/javascripts/countly/countly.config.js && \ - HOME=/tmp npm install -g npm@10 && \ - HOME=/tmp npm install --unsafe-perm=true --allow-root && \ - HOME=/tmp npm install argon2 --build-from-source --unsafe-perm=true --allow-root && \ + HOME=/tmp npm install -g npm@11.19.0 && \ + HOME=/tmp npm install && \ + HOME=/tmp npm_config_build_from_source=true npm install argon2 && \ ./bin/docker/preinstall.sh && \ bash /opt/countly/bin/scripts/detect.init.sh && \ - python3.8 -m pip install --upgrade pip setuptools && \ - countly update sdk-web && \ - \ - # cleanup & chown + countly update sdk-web && \ npm remove -y --no-save mocha nyc should supertest puppeteer && \ - apt-get purge -y python3 python3-pip python3-distutils python3-setuptools && \ - apt-get remove -y git gcc g++ make automake autoconf libtool pkg-config unzip sqlite3 wget && \ - apt-get autoremove -y && \ - apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* && \ - apt-get autoremove -y && \ - rm -rf test /tmp/* /tmp/.??* /var/tmp/* /var/tmp/.??* /var/log/* /root/.npm /usr/local/lib/python3.8/ensurepip/_bundled/*.whl /usr/src/Python* /var/lib/dpkg/info/python3* && \ - \ - # temporary to remove npm bug message - mkdir /.npm && chown -R 1001:0 /.npm && \ - \ + # CE builds carry only a stub ab-testing dir; drop it so it never ships + { [ -f plugins/ab-testing/package.json ] || rm -rf plugins/ab-testing; } && \ + rm -rf test /tmp/* /var/tmp/* /root/.npm && \ chown -R 1001:0 /opt/countly && \ chmod -R g=u /opt/countly +FROM node:24-trixie-slim@sha256:0711b541c1c33a8a530ac4f0d391baa9a15b3d804695b1b24a47daa5fb60e74d AS runtime + +ARG COUNTLY_PLUGINS=mobile,web,desktop,plugins,density,locale,browser,sources,views,logger,systemlogs,populator,reports,crashes,push,star-rating,slipping-away-users,compare,server-stats,dbviewer,times-of-day,compliance-hub,alerts,onboarding,consolidate,remote-config,hooks,dashboards,sdk,data-manager,guides + +EXPOSE 6001 +HEALTHCHECK --start-period=400s CMD bash -c '\ + if [ "$COUNTLY_CONFIG_FRONTEND_WEB_SSL_ENABLED" = "true" ] || [ "$COUNTLY_CONFIG_FRONTEND_WEB_SSL_ENABLED" = "\"true\"" ]; then \ + curl -k --fail https://localhost:6001/ping || exit 1; \ + else \ + curl --fail http://localhost:6001/ping || exit 1; \ + fi' + +ENV COUNTLY_CONTAINER="frontend" \ + COUNTLY_DEFAULT_PLUGINS="${COUNTLY_PLUGINS}" \ + COUNTLY_CONFIG_FRONTEND_WEB_HOST="0.0.0.0" \ + npm_config_dangerously_allow_all_scripts=true \ + NODE_OPTIONS="--max-old-space-size=2048 --no-node-snapshot" + +# Runtime-only packages: init, healthcheck, ops tools +RUN apt-get update && apt-get upgrade -y && \ + apt-get install -y --no-install-recommends \ + tini curl procps iputils-ping net-tools ca-certificates \ + sudo telnet xz-utils apt-transport-https lsb-release && \ + apt-get clean && rm -rf /var/lib/apt/lists/* && \ + echo "Etc/UTC" > /etc/timezone + +WORKDIR /opt/countly +COPY --from=builder /usr/local /usr/local +COPY --from=builder /opt/countly /opt/countly + +# npm stays for in-container plugin rebuilds (countly task dist-all -> npx grunt) +RUN ln -sf /opt/countly/bin/commands/countly.sh /usr/bin/countly && \ + chown 1001:0 /opt/countly && chmod g=u /opt/countly && \ + mkdir -p /.npm && chown -R 1001:0 /.npm + USER 1001:0 ENTRYPOINT ["/usr/bin/tini", "-v", "--"] diff --git a/api/utils/utils.js b/api/utils/utils.js index b1c7925c81c..70d4b9c0146 100644 --- a/api/utils/utils.js +++ b/api/utils/utils.js @@ -108,6 +108,29 @@ exports.decrypt = function(crypted, key, iv, algorithm, input_encoding, output_e return decrypted; }; +/** +* Derive key and iv from a password the way OpenSSL EVP_BytesToKey does (MD5, no +* salt, one iteration). This is what the removed crypto.createDecipher() did +* internally, so values encrypted by older Countly versions stay readable. +* @param {string} algorithm - cipher algorithm name +* @param {string} password - password the value was encrypted with +* @returns {object} object with key and iv buffers +*/ +function legacyKeyAndIv(algorithm, password) { + var info = crypto.getCipherInfo(algorithm); + var pass = Buffer.from(password, "binary"); + var block = Buffer.alloc(0); + var out = Buffer.alloc(0); + while (out.length < info.keyLength + info.ivLength) { + block = crypto.createHash("md5").update(Buffer.concat([block, pass])).digest(); + out = Buffer.concat([out, block]); + } + return { + key: out.subarray(0, info.keyLength), + iv: out.subarray(info.keyLength, info.keyLength + info.ivLength) + }; +} + /** * Old deprecated decrypt function, needed for old stored values * @param {string} crypted - value to decrypt @@ -150,7 +173,9 @@ exports.decrypt_old = function(crypted, key, iv, algorithm, input_encoding, outp decipher = crypto.createDecipheriv(algorithm, key, iv); } else { - decipher = crypto.createDecipher(algorithm, key); + // crypto.createDecipher was removed in Node 22; derive the same key/iv it used + var derived = legacyKeyAndIv(algorithm, key); + decipher = crypto.createDecipheriv(algorithm, derived.key, derived.iv); } decrypted = decipher.update(crypted, input_encoding, output_encoding); decrypted += decipher.final(output_encoding); diff --git a/bin/docker/modify.sh b/bin/docker/modify.sh index 7681a04b408..c7e6090b053 100755 --- a/bin/docker/modify.sh +++ b/bin/docker/modify.sh @@ -16,13 +16,24 @@ fi if [ "${COUNTLY_CONTAINER}" != "frontend" ]; then # Run ab-testing models compilation if it's there if [ -d /opt/countly/plugins/ab-testing ]; then - if [ "${ID}" == "debian" ] || [ "${ID}" == "ubuntu" ]; then - apt-get install -y python3-pip + AB=/opt/countly/plugins/ab-testing/api/bayesian + # Docker images precompile models in the ab-models stage; skip when present, + # disabled, or when only the CE stub (no model sources) is shipped + if [ -x "$AB/models/stan_models/model_2_variants" ] || [ -f "$AB/.ab_disabled" ] || [ ! -f "$AB/models/cmdstanpy_model.py" ]; then + echo "AB-testing models already built, disabled or absent, skipping" else - yum install -y python36 python36-libs python36-devel python36-pip + if [ "${ID}" == "debian" ] || [ "${ID}" == "ubuntu" ]; then + apt-get install -y python3-pip + else + yum install -y python38 python38-libs python38-devel python38-pip + fi + PY=$(command -v python3.12 || command -v python3.8 || command -v python3) + # pinned numpy/pandas need >=3.9; fall back to resolver-chosen versions on older pythons + "$PY" -m pip install -r "$AB/requirements.txt" || "$PY" -m pip install "cmdstanpy==1.2.5" + # pin CmdStan and install to a path the runtime user can read (not /root) + # clear any inherited CMDSTAN (centos sets it as ENV) so the compiler installs CmdStan + cd /opt/countly && CMDSTAN='' CMDSTAN_VERSION=2.36.0 CMDSTAN_DIR=/opt/cmdstan CMDSTAN_BUILD_CORES="$(nproc)" "$PY" "$AB/models/cmdstanpy_model.py" && \ + chmod -R a+rX /opt/cmdstan fi - # shellcheck disable=SC1091 - python3.8 -m pip install -r /opt/countly/plugins/ab-testing/api/bayesian/requirements.txt - cd /opt/countly/plugins/ab-testing/api/bayesian && python3.8 model.py fi fi diff --git a/bin/docker/preinstall.sh b/bin/docker/preinstall.sh index d4ffbc851ad..fd49f9fca9a 100755 --- a/bin/docker/preinstall.sh +++ b/bin/docker/preinstall.sh @@ -6,17 +6,22 @@ plugins="["; while IFS= read -r -d '' plugin do echo "Installing $plugin..." - (cd "$plugin" && HOME=/tmp npm install --unsafe-perm=true --allow-root) + (cd "$plugin" && HOME=/tmp npm install --fetch-retries=5 --fetch-retry-maxtimeout=120000) || { echo "ERROR: npm install failed for $plugin"; failed_plugins="$failed_plugins $plugin"; } plugins="$plugins\"$(basename "${plugin}")\"," echo "done" done < <(find /opt/countly/plugins -mindepth 1 -maxdepth 1 -type d -print0) +if [ -n "$failed_plugins" ]; then + echo "ERROR: plugin installs failed:$failed_plugins" + exit 1 +fi + plugins="${plugins::-1}]" node ./node_modules/geoip-lite/scripts/updatedb.js license_key="$GEOIP" echo "$plugins" > /opt/countly/plugins/plugins.json -(cd /opt/countly && npx grunt dist-all && rm -rf /opt/countly/plugins/plugins.json) +(cd /opt/countly && npx grunt dist-all && rm -rf /opt/countly/plugins/plugins.json) || { echo "ERROR: grunt dist-all failed"; exit 1; } export CXX="" && export CC="" diff --git a/package-lock.json b/package-lock.json index 345a72569ff..729f97cc0de 100644 --- a/package-lock.json +++ b/package-lock.json @@ -512,9 +512,9 @@ } }, "node_modules/@eslint/eslintrc/node_modules/brace-expansion": { - "version": "1.1.14", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.14.tgz", - "integrity": "sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==", + "version": "1.1.18", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.18.tgz", + "integrity": "sha512-Edep/X9fGqVNmzKBVsDYIOtD+z1tuezV70LBjdCst9Tqu76lsnvRiZ6oTic1n+/BIwX6QDGAO94PN4N2SADvtw==", "dev": true, "license": "MIT", "dependencies": { @@ -600,9 +600,9 @@ } }, "node_modules/@humanwhocodes/config-array/node_modules/brace-expansion": { - "version": "1.1.14", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.14.tgz", - "integrity": "sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==", + "version": "1.1.18", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.18.tgz", + "integrity": "sha512-Edep/X9fGqVNmzKBVsDYIOtD+z1tuezV70LBjdCst9Tqu76lsnvRiZ6oTic1n+/BIwX6QDGAO94PN4N2SADvtw==", "dev": true, "license": "MIT", "dependencies": { @@ -1240,6 +1240,18 @@ "url": "https://github.com/chalk/strip-ansi?sponsor=1" } }, + "node_modules/@isaacs/fs-minipass": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz", + "integrity": "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==", + "license": "ISC", + "dependencies": { + "minipass": "^7.0.4" + }, + "engines": { + "node": ">=18.0.0" + } + }, "node_modules/@istanbuljs/load-nyc-config": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", @@ -1282,9 +1294,9 @@ } }, "node_modules/@istanbuljs/load-nyc-config/node_modules/js-yaml": { - "version": "3.15.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.15.0.tgz", - "integrity": "sha512-ttBQIIQPDeLjpPOohtUdXuXUVoA2uIB6fEH9HyJ7234s5mBJ5wTx20njxplLZQgLaOfpmPQA7X2t5AX6tIPbog==", + "version": "3.15.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.15.1.tgz", + "integrity": "sha512-S99WuO3HlhO3XN41EtYUNl9zzXjoJx7QvmipxsJVxtCBT0YHEFy+iOJhjSvrmV12nYhWpZaM8lPHkJm0yUMbag==", "dev": true, "license": "MIT", "dependencies": { @@ -2424,16 +2436,16 @@ } }, "node_modules/@stylistic/eslint-plugin/node_modules/brace-expansion": { - "version": "5.0.7", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.7.tgz", - "integrity": "sha512-7oFy703dxfY3/NLxC1fh2SUCQ0H9rmAY+5EpDVfXjUTTs+HEwR2nYaqLv+GWcTsumwxPfiz6CzCNkwXwBUwqCA==", + "version": "5.0.9", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.9.tgz", + "integrity": "sha512-ScQ4IuvIEF1TMlP7Zt+vjJ//9zlPb2SDcxWxM3bk8s6t6GGdJ7KO1dCcTidOPJKePW30LE/2cT7wCyPho9/Wxg==", "dev": true, "license": "MIT", "dependencies": { "balanced-match": "^4.0.2" }, "engines": { - "node": "18 || 20 || >=22" + "node": "20 || >=22" } }, "node_modules/@stylistic/eslint-plugin/node_modules/minimatch": { @@ -3225,9 +3237,9 @@ } }, "node_modules/archiver-utils/node_modules/brace-expansion": { - "version": "1.1.14", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.14.tgz", - "integrity": "sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==", + "version": "1.1.18", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.18.tgz", + "integrity": "sha512-Edep/X9fGqVNmzKBVsDYIOtD+z1tuezV70LBjdCst9Tqu76lsnvRiZ6oTic1n+/BIwX6QDGAO94PN4N2SADvtw==", "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", @@ -3708,9 +3720,9 @@ "license": "ISC" }, "node_modules/brace-expansion": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.0.tgz", - "integrity": "sha512-TN1kCZAgdgweJhWWpgKYrQaMNHcDULHkWwQIspdtjV4Y5aurRdZpjAqn6yX3FPqTA9ngHCc4hJxMAMgGfve85w==", + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.4.tgz", + "integrity": "sha512-hGfVzPxthbf3+2yjg/RBs60cB0FhqBS/zvdV/4wn4/BmN0bNMMHPc4V/BbFieqf1TKAGGAHnY4eSjajCl0f2Xg==", "license": "MIT", "dependencies": { "balanced-match": "^1.0.0" @@ -3875,9 +3887,9 @@ } }, "node_modules/cacache/node_modules/brace-expansion": { - "version": "1.1.14", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.14.tgz", - "integrity": "sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==", + "version": "1.1.18", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.18.tgz", + "integrity": "sha512-Edep/X9fGqVNmzKBVsDYIOtD+z1tuezV70LBjdCst9Tqu76lsnvRiZ6oTic1n+/BIwX6QDGAO94PN4N2SADvtw==", "license": "MIT", "optional": true, "dependencies": { @@ -4136,6 +4148,7 @@ "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", "license": "ISC", + "optional": true, "engines": { "node": ">=10" } @@ -5298,9 +5311,9 @@ } }, "node_modules/eslint/node_modules/brace-expansion": { - "version": "1.1.14", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.14.tgz", - "integrity": "sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==", + "version": "1.1.18", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.18.tgz", + "integrity": "sha512-Edep/X9fGqVNmzKBVsDYIOtD+z1tuezV70LBjdCst9Tqu76lsnvRiZ6oTic1n+/BIwX6QDGAO94PN4N2SADvtw==", "dev": true, "license": "MIT", "dependencies": { @@ -5624,9 +5637,9 @@ "license": "MIT" }, "node_modules/express/node_modules/body-parser": { - "version": "1.20.5", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.5.tgz", - "integrity": "sha512-3grm+/2tUOvu2cjJkvsIxrv/wVpfXQW4PsQHYm7yk4vfpu7Ekl6nEsYBoJUL6qDwZUx8wUhQ8tR2qz+ad9c9OA==", + "version": "1.20.6", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.6.tgz", + "integrity": "sha512-p5tAzS57i5MV9fZFDj9LeIiTZEufbSe2eDozP+ElheSUq1m74CRq1jI4mYNDdVs9vQztXFLuk/Gd6BWTdwRJ5g==", "license": "MIT", "dependencies": { "bytes": "~3.1.2", @@ -6204,6 +6217,7 @@ "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", "license": "ISC", + "optional": true, "dependencies": { "minipass": "^3.0.0" }, @@ -6216,6 +6230,7 @@ "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", "license": "ISC", + "optional": true, "dependencies": { "yallist": "^4.0.0" }, @@ -6594,9 +6609,9 @@ } }, "node_modules/globule/node_modules/brace-expansion": { - "version": "1.1.14", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.14.tgz", - "integrity": "sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==", + "version": "1.1.18", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.18.tgz", + "integrity": "sha512-Edep/X9fGqVNmzKBVsDYIOtD+z1tuezV70LBjdCst9Tqu76lsnvRiZ6oTic1n+/BIwX6QDGAO94PN4N2SADvtw==", "dev": true, "license": "MIT", "dependencies": { @@ -6627,9 +6642,9 @@ } }, "node_modules/globule/node_modules/minimatch": { - "version": "3.0.8", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.8.tgz", - "integrity": "sha512-6FsRAQsxQ61mw+qP1ZzbL9Bc78x2p5OqNgNpnoAFLTrX8n5Kxph0CsnhmKKNXTWjXqU5L0pGPR7hYk+XWZr60Q==", + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", "dev": true, "license": "ISC", "dependencies": { @@ -7166,9 +7181,9 @@ } }, "node_modules/grunt/node_modules/brace-expansion": { - "version": "1.1.14", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.14.tgz", - "integrity": "sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==", + "version": "1.1.18", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.18.tgz", + "integrity": "sha512-Edep/X9fGqVNmzKBVsDYIOtD+z1tuezV70LBjdCst9Tqu76lsnvRiZ6oTic1n+/BIwX6QDGAO94PN4N2SADvtw==", "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", @@ -7209,9 +7224,9 @@ } }, "node_modules/grunt/node_modules/js-yaml": { - "version": "3.15.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.15.0.tgz", - "integrity": "sha512-ttBQIIQPDeLjpPOohtUdXuXUVoA2uIB6fEH9HyJ7234s5mBJ5wTx20njxplLZQgLaOfpmPQA7X2t5AX6tIPbog==", + "version": "3.15.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.15.1.tgz", + "integrity": "sha512-S99WuO3HlhO3XN41EtYUNl9zzXjoJx7QvmipxsJVxtCBT0YHEFy+iOJhjSvrmV12nYhWpZaM8lPHkJm0yUMbag==", "license": "MIT", "dependencies": { "argparse": "^1.0.7", @@ -7570,9 +7585,9 @@ "license": "MIT" }, "node_modules/immutable": { - "version": "5.1.5", - "resolved": "https://registry.npmjs.org/immutable/-/immutable-5.1.5.tgz", - "integrity": "sha512-t7xcm2siw+hlUM68I+UEOK+z84RzmN59as9DZ7P1l0994DKUWV7UXBMQZVxaoMSRQ+PBZbHCOoBt7a2wxOMt+A==", + "version": "5.1.9", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-5.1.9.tgz", + "integrity": "sha512-m8nVez3rwrgmWxtLMt1ZYXB2Lv7OKYn/disyxAlSDYAlKSlFoPPfIAmAM/M5xqL4m4C/wAPw7S2/CNaUii1Hxg==", "license": "MIT" }, "node_modules/import-fresh": { @@ -7648,9 +7663,9 @@ "license": "MIT" }, "node_modules/ip-address": { - "version": "10.2.0", - "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.2.0.tgz", - "integrity": "sha512-/+S6j4E9AHvW9SWMSEY9Xfy66O5PWvVEJ08O0y5JGyEKQpojb0K0GKpz/v5HJ/G0vi3D2sjGK78119oXZeE0qA==", + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.4.0.tgz", + "integrity": "sha512-oSK96Grm3aP6OrS263xVxbNDGVL7rzBtYdpGqlDG8iQdoenDoTs/nkki+DflYbAEE8Xl6o5YxhxlrKvI3nqKXQ==", "license": "MIT", "engines": { "node": ">= 12" @@ -7937,16 +7952,16 @@ } }, "node_modules/istanbul-lib-processinfo/node_modules/brace-expansion": { - "version": "5.0.5", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.5.tgz", - "integrity": "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==", + "version": "5.0.9", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.9.tgz", + "integrity": "sha512-ScQ4IuvIEF1TMlP7Zt+vjJ//9zlPb2SDcxWxM3bk8s6t6GGdJ7KO1dCcTidOPJKePW30LE/2cT7wCyPho9/Wxg==", "dev": true, "license": "MIT", "dependencies": { "balanced-match": "^4.0.2" }, "engines": { - "node": "18 || 20 || >=22" + "node": "20 || >=22" } }, "node_modules/istanbul-lib-processinfo/node_modules/glob": { @@ -8180,9 +8195,9 @@ "license": "MIT" }, "node_modules/js-yaml": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.0.tgz", - "integrity": "sha512-1td788aAnnZ5qs7V2QIRl1owjtYpbKt749Y3xauqQgwIIGF/xXWz1wMTEBx5O3LK3lXLVuqXPdPxj2BoFHaW9Q==", + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.1.tgz", + "integrity": "sha512-CY6crGq313MX8GkwvB7tzgp99vjQxY1++5y10/BKN/GUfHqWaOGQMNZkBvqSzsZKWk/ijwHlWzzkLulsGHhjWQ==", "funding": [ { "type": "github", @@ -8985,7 +9000,6 @@ "version": "7.1.3", "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz", "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==", - "dev": true, "license": "BlueOak-1.0.0", "engines": { "node": ">=16 || 14 >=14.17" @@ -9131,6 +9145,7 @@ "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", "license": "MIT", + "optional": true, "dependencies": { "minipass": "^3.0.0", "yallist": "^4.0.0" @@ -9144,6 +9159,7 @@ "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", "license": "ISC", + "optional": true, "dependencies": { "yallist": "^4.0.0" }, @@ -9539,9 +9555,9 @@ } }, "node_modules/mv/node_modules/brace-expansion": { - "version": "1.1.14", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.14.tgz", - "integrity": "sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==", + "version": "1.1.18", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.18.tgz", + "integrity": "sha512-Edep/X9fGqVNmzKBVsDYIOtD+z1tuezV70LBjdCst9Tqu76lsnvRiZ6oTic1n+/BIwX6QDGAO94PN4N2SADvtw==", "license": "MIT", "optional": true, "dependencies": { @@ -9712,9 +9728,9 @@ } }, "node_modules/node-gyp/node_modules/brace-expansion": { - "version": "1.1.14", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.14.tgz", - "integrity": "sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==", + "version": "1.1.18", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.18.tgz", + "integrity": "sha512-Edep/X9fGqVNmzKBVsDYIOtD+z1tuezV70LBjdCst9Tqu76lsnvRiZ6oTic1n+/BIwX6QDGAO94PN4N2SADvtw==", "license": "MIT", "optional": true, "dependencies": { @@ -9905,16 +9921,16 @@ } }, "node_modules/nyc/node_modules/brace-expansion": { - "version": "5.0.5", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.5.tgz", - "integrity": "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==", + "version": "5.0.9", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.9.tgz", + "integrity": "sha512-ScQ4IuvIEF1TMlP7Zt+vjJ//9zlPb2SDcxWxM3bk8s6t6GGdJ7KO1dCcTidOPJKePW30LE/2cT7wCyPho9/Wxg==", "dev": true, "license": "MIT", "dependencies": { "balanced-match": "^4.0.2" }, "engines": { - "node": "18 || 20 || >=22" + "node": "20 || >=22" } }, "node_modules/nyc/node_modules/cliui": { @@ -11325,9 +11341,9 @@ } }, "node_modules/rimraf/node_modules/brace-expansion": { - "version": "1.1.14", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.14.tgz", - "integrity": "sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==", + "version": "1.1.18", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.18.tgz", + "integrity": "sha512-Edep/X9fGqVNmzKBVsDYIOtD+z1tuezV70LBjdCst9Tqu76lsnvRiZ6oTic1n+/BIwX6QDGAO94PN4N2SADvtw==", "devOptional": true, "license": "MIT", "dependencies": { @@ -11949,16 +11965,16 @@ } }, "node_modules/spawn-wrap/node_modules/brace-expansion": { - "version": "5.0.5", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.5.tgz", - "integrity": "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==", + "version": "5.0.9", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.9.tgz", + "integrity": "sha512-ScQ4IuvIEF1TMlP7Zt+vjJ//9zlPb2SDcxWxM3bk8s6t6GGdJ7KO1dCcTidOPJKePW30LE/2cT7wCyPho9/Wxg==", "dev": true, "license": "MIT", "dependencies": { "balanced-match": "^4.0.2" }, "engines": { - "node": "18 || 20 || >=22" + "node": "20 || >=22" } }, "node_modules/spawn-wrap/node_modules/foreground-child": { @@ -12441,21 +12457,19 @@ } }, "node_modules/tar": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz", - "integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==", - "deprecated": "Old versions of tar are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", - "license": "ISC", + "version": "7.5.22", + "resolved": "https://registry.npmjs.org/tar/-/tar-7.5.22.tgz", + "integrity": "sha512-MFO/QzvtAOmJbkhOaCTvbGcFN9L9b+JunIsDwaKljSOdcLMea3NJ1k9Usz/rjdfSXTq4dfzfeS7W4p4YOAAHeA==", + "license": "BlueOak-1.0.0", "dependencies": { - "chownr": "^2.0.0", - "fs-minipass": "^2.0.0", - "minipass": "^5.0.0", - "minizlib": "^2.1.1", - "mkdirp": "^1.0.3", - "yallist": "^4.0.0" + "@isaacs/fs-minipass": "^4.0.0", + "chownr": "^3.0.0", + "minipass": "^7.1.2", + "minizlib": "^3.1.0", + "yallist": "^5.0.0" }, "engines": { - "node": ">=10" + "node": ">=18" } }, "node_modules/tar-fs": { @@ -12484,25 +12498,34 @@ "streamx": "^2.15.0" } }, - "node_modules/tar/node_modules/minipass": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", - "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", - "license": "ISC", + "node_modules/tar/node_modules/chownr": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-3.0.0.tgz", + "integrity": "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==", + "license": "BlueOak-1.0.0", "engines": { - "node": ">=8" + "node": ">=18" } }, - "node_modules/tar/node_modules/mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "node_modules/tar/node_modules/minizlib": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.1.0.tgz", + "integrity": "sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw==", "license": "MIT", - "bin": { - "mkdirp": "bin/cmd.js" + "dependencies": { + "minipass": "^7.1.2" }, "engines": { - "node": ">=10" + "node": ">= 18" + } + }, + "node_modules/tar/node_modules/yallist": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-5.0.0.tgz", + "integrity": "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==", + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" } }, "node_modules/teex": { @@ -12540,16 +12563,16 @@ } }, "node_modules/test-exclude/node_modules/brace-expansion": { - "version": "5.0.5", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.5.tgz", - "integrity": "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==", + "version": "5.0.9", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.9.tgz", + "integrity": "sha512-ScQ4IuvIEF1TMlP7Zt+vjJ//9zlPb2SDcxWxM3bk8s6t6GGdJ7KO1dCcTidOPJKePW30LE/2cT7wCyPho9/Wxg==", "dev": true, "license": "MIT", "dependencies": { "balanced-match": "^4.0.2" }, "engines": { - "node": "18 || 20 || >=22" + "node": "20 || >=22" } }, "node_modules/test-exclude/node_modules/glob": { @@ -13529,7 +13552,8 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "license": "ISC" + "license": "ISC", + "optional": true }, "node_modules/yaml": { "version": "2.9.0", @@ -13707,9 +13731,9 @@ } }, "node_modules/zip-stream/node_modules/brace-expansion": { - "version": "1.1.14", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.14.tgz", - "integrity": "sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==", + "version": "1.1.18", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.18.tgz", + "integrity": "sha512-Edep/X9fGqVNmzKBVsDYIOtD+z1tuezV70LBjdCst9Tqu76lsnvRiZ6oTic1n+/BIwX6QDGAO94PN4N2SADvtw==", "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", diff --git a/package.json b/package.json index cdab0b9afca..3543816e67b 100644 --- a/package.json +++ b/package.json @@ -116,8 +116,19 @@ "serialize-javascript": "^7.0.5", "uuid": "^11.1.1", "@tootallnate/once": "^2.0.1", - "js-yaml@^3.0.0": "^3.15.0", - "js-yaml@^4.0.0": "^4.2.0" + "js-yaml@^3.0.0": "^3.15.1", + "js-yaml@^4.0.0": "^4.3.1", + "basic-ftp": "^5.3.1", + "fast-xml-parser": "^5.3.5", + "tar": "^7.5.21", + "websocket-driver": "^0.7.5", + "brace-expansion@^1.0.0": "^1.1.18", + "brace-expansion@^2.0.0": "^2.1.4", + "minimatch@^3.0.0": "^3.1.4", + "immutable": "^5.1.8", + "ip-address": "^10.3.1", + "body-parser@^1.0.0": "^1.20.6", + "brace-expansion@^5.0.0": "^5.0.9" }, "jshintConfig": { "esversion": 6, diff --git a/plugins/hooks/api/parts/effects/custom_code.js b/plugins/hooks/api/parts/effects/custom_code.js index ac775a5e7a6..070044e86a5 100644 --- a/plugins/hooks/api/parts/effects/custom_code.js +++ b/plugins/hooks/api/parts/effects/custom_code.js @@ -1,7 +1,7 @@ const utils = require("../../utils"); const common = require('../../../../../api/utils/common.js'); const log = common.log("hooks:api:api_custom_code_effect"); -const {Sandbox} = require("v8-sandbox"); +const ivm = require("isolated-vm"); /** * custom code effect @@ -21,55 +21,77 @@ class CustomCodeEffect { async run(options) { const {effect, params, rule, effectStep, _originalInput} = options; let genCode = ""; - let runtimePassed = true ; + let runtimePassed = true; let logs = []; + let isolate; + try { - await new Promise(CUSTOM_CODE_RESOLVER => { - const code = effect.configuration.code; - /** - * function for rejection of effect - * @param {object} e - error object - */ - const CUSTOM_CODE_ERROR_CALLBACK = (e) => { - runtimePassed = false; - log.e("got error when executing custom code", e, genCode, options); - logs.push(`Error: ${e.message}`); - utils.addErrorRecord(rule._id, e, params, effectStep, _originalInput); - }; + const code = effect.configuration.code; - genCode = ` - ${code} - setResult({ value: params }); - `; - // Disable the sandbox's built-in httpRequest helper. v8-sandbox - // enables it by default, which would let custom code make - // arbitrary server-side requests (to loopback, link-local, - // cloud-metadata and other internal targets) completely - // bypassing the SSRF validation applied to the HTTPEffect path. - // Hooks that need outbound HTTP must use the HTTPEffect, whose - // URL is checked with ssrfProtection.isUrlSafe(). - const sandbox = new Sandbox({ httpEnabled: false }); + // Create isolated VM instance + isolate = new ivm.Isolate({ memoryLimit: 128 }); + const context = await isolate.createContext(); + const jail = context.global; - (async() => { - const { error, value } = await sandbox.execute({ code: genCode, timeout: 3000, globals: { params } }); + // Set up global object + await jail.set('global', jail.derefInto()); - await sandbox.shutdown(); + // Set up params. JSON round-trip matches the previous v8-sandbox + // transport (ObjectId/Date -> string, functions dropped) so existing + // custom code sees the same param shapes it did before. + const clonedParams = params === undefined ? undefined : JSON.parse(JSON.stringify(params)); + await jail.set('params', new ivm.ExternalCopy(clonedParams).copyInto()); - if (error) { - CUSTOM_CODE_ERROR_CALLBACK(error); - } - options.params = value; - log.d("Resolved value:", value); - CUSTOM_CODE_RESOLVER(); - })(); + // Set up setResult function using JSON serialization for simplicity + let resultValue = null; + const setResultRef = new ivm.Reference(function(jsonString) { + // Receive JSON string and parse it + resultValue = JSON.parse(jsonString); }); + await jail.set('$setResult', setResultRef); + + // Create wrapper function in isolate that serializes and calls the reference + const wrapperScript = await isolate.compileScript('globalThis.setResult = function(arg) { return $setResult.applySync(undefined, [JSON.stringify(arg)]); }'); + await wrapperScript.run(context); + + // Prepare code + genCode = ` + ${code} + setResult({ value: params }); + `; + + // Compile and run the script + const script = await isolate.compileScript(genCode); + await script.run(context, { timeout: 3000 }); + + // Assign whenever a value key was set, so custom code that intentionally + // blanks params (0, "", false, null) is honored, matching the previous + // unconditional assignment. + options.params = resultValue && Object.prototype.hasOwnProperty.call(resultValue, 'value') ? resultValue.value : undefined; + log.d("Resolved value:", options.params); } catch (e) { runtimePassed = false; + // the previous sandbox assigned its undefined result even on failure, + // so a failed run must not leak the original params downstream + options.params = undefined; log.e("got error when executing custom code", e, genCode, options); logs.push(`Error: ${e.message}`); utils.addErrorRecord(rule._id, e, params, effectStep, _originalInput); } + finally { + // Clean up isolate. dispose() throws if the isolate was already torn + // down (e.g. after a memory-limit abort), which would mask the real error. + if (isolate) { + try { + isolate.dispose(); + } + catch (disposeErr) { + log.d("isolate already disposed:", disposeErr.message); + } + } + } + return runtimePassed ? options : {...options, logs}; } } diff --git a/plugins/hooks/package-lock.json b/plugins/hooks/package-lock.json index 39c29f25cde..9d147422614 100644 --- a/plugins/hooks/package-lock.json +++ b/plugins/hooks/package-lock.json @@ -9,1525 +9,41 @@ "version": "1.0.0", "dependencies": { "ipaddr.js": "^2.3.0", - "v8-sandbox": "3.2.12" + "isolated-vm": "^6.0.2" } }, - "node_modules/@isaacs/cliui": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", - "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", - "license": "ISC", - "dependencies": { - "string-width": "^5.1.2", - "string-width-cjs": "npm:string-width@^4.2.0", - "strip-ansi": "^7.0.1", - "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", - "wrap-ansi": "^8.1.0", - "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/@npmcli/agent": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/@npmcli/agent/-/agent-2.2.2.tgz", - "integrity": "sha512-OrcNPXdpSl9UX7qPVRWbmWMCSXrcDa2M9DvrbOTj7ao1S4PlqVFYv9/yLKMkrJKZ/V5A/kDBC690or307i26Og==", - "license": "ISC", - "dependencies": { - "agent-base": "^7.1.0", - "http-proxy-agent": "^7.0.0", - "https-proxy-agent": "^7.0.1", - "lru-cache": "^10.0.1", - "socks-proxy-agent": "^8.0.3" - }, - "engines": { - "node": "^16.14.0 || >=18.0.0" - } - }, - "node_modules/@npmcli/fs": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-3.1.1.tgz", - "integrity": "sha512-q9CRWjpHCMIh5sVyefoD1cA7PkvILqCZsnSOEUUivORLjxCO/Irmue2DprETiNgEqktDBZaM1Bi+jrarx1XdCg==", - "license": "ISC", - "dependencies": { - "semver": "^7.3.5" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/@pkgjs/parseargs": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", - "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", - "license": "MIT", - "optional": true, - "engines": { - "node": ">=14" - } - }, - "node_modules/abbrev": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-2.0.0.tgz", - "integrity": "sha512-6/mh1E2u2YgEsCHdY0Yx5oW+61gZU+1vXaoiHHrpKeuRNNgFvS+/jrwHiQhB5apAf5oB7UB7E19ol2R2LKH8hQ==", - "license": "ISC", - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/agent-base": { - "version": "7.1.4", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", - "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", - "license": "MIT", - "engines": { - "node": ">= 14" - } - }, - "node_modules/aggregate-error": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", - "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", - "license": "MIT", - "dependencies": { - "clean-stack": "^2.0.0", - "indent-string": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/ansi-regex": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", - "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, - "node_modules/ansi-styles": { - "version": "6.2.3", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", - "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/async": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", - "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", - "license": "MIT" - }, - "node_modules/asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", - "license": "MIT" - }, - "node_modules/axios": { - "version": "1.18.1", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.18.1.tgz", - "integrity": "sha512-3nTvFlvpn9Zu/RkHUqtc7/+al4UpRW5az71ap5zccp6e8RAYEzhMTecX8Dz1wWDYrPpUoB1HAQEGEAEvUr7S9g==", - "license": "MIT", - "dependencies": { - "follow-redirects": "^1.16.0", - "form-data": "^4.0.5", - "https-proxy-agent": "^5.0.1", - "proxy-from-env": "^2.1.0" - } - }, - "node_modules/axios/node_modules/agent-base": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", - "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", - "license": "MIT", - "dependencies": { - "debug": "4" - }, - "engines": { - "node": ">= 6.0.0" - } - }, - "node_modules/axios/node_modules/https-proxy-agent": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", - "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", - "license": "MIT", - "dependencies": { - "agent-base": "6", - "debug": "4" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "license": "MIT" - }, - "node_modules/bindings": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", - "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", - "license": "MIT", - "dependencies": { - "file-uri-to-path": "1.0.0" - } - }, - "node_modules/brace-expansion": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.3.tgz", - "integrity": "sha512-MCV/fYJEbqx68aE58kv2cA/kiky1G8vux3OR6/jbS+jIMe/6fJWa0DTzJU7dqijOWYwHi1t29FlfYI9uytqlpA==", - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/cacache": { - "version": "18.0.4", - "resolved": "https://registry.npmjs.org/cacache/-/cacache-18.0.4.tgz", - "integrity": "sha512-B+L5iIa9mgcjLbliir2th36yEwPftrzteHYujzsx3dFP/31GCHcIeS8f5MGd80odLOjaOvSpU3EEAmRQptkxLQ==", - "license": "ISC", - "dependencies": { - "@npmcli/fs": "^3.1.0", - "fs-minipass": "^3.0.0", - "glob": "^10.2.2", - "lru-cache": "^10.0.1", - "minipass": "^7.0.3", - "minipass-collect": "^2.0.1", - "minipass-flush": "^1.0.5", - "minipass-pipeline": "^1.2.4", - "p-map": "^4.0.0", - "ssri": "^10.0.0", - "tar": "^6.1.11", - "unique-filename": "^3.0.0" - }, - "engines": { - "node": "^16.14.0 || >=18.0.0" - } - }, - "node_modules/call-bind-apply-helpers": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", - "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/chownr": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", - "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", - "license": "ISC", - "engines": { - "node": ">=10" - } - }, - "node_modules/clean-stack": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", - "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "license": "MIT" - }, - "node_modules/combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "license": "MIT", - "dependencies": { - "delayed-stream": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/cross-spawn": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", - "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", - "license": "MIT", - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/cross-spawn/node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "license": "ISC" - }, - "node_modules/cross-spawn/node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "license": "ISC", - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/debug": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", - "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", - "license": "MIT", - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/dunder-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", - "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.1", - "es-errors": "^1.3.0", - "gopd": "^1.2.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/eastasianwidth": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", - "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", - "license": "MIT" - }, - "node_modules/emoji-regex": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", - "license": "MIT" - }, - "node_modules/encoding": { - "version": "0.1.13", - "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz", - "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==", - "license": "MIT", - "optional": true, - "dependencies": { - "iconv-lite": "^0.6.2" - } - }, - "node_modules/env-paths": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", - "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/err-code": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/err-code/-/err-code-2.0.3.tgz", - "integrity": "sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==", - "license": "MIT" - }, - "node_modules/es-define-property": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", - "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-errors": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", - "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-object-atoms": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", - "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-set-tostringtag": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", - "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.6", - "has-tostringtag": "^1.0.2", - "hasown": "^2.0.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/exponential-backoff": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/exponential-backoff/-/exponential-backoff-3.1.3.tgz", - "integrity": "sha512-ZgEeZXj30q+I0EN+CbSSpIyPaJ5HVQD18Z1m+u1FXbAeT94mr1zw50q4q6jiiC447Nl/YTcIYSAftiGqetwXCA==", - "license": "Apache-2.0" - }, - "node_modules/file-uri-to-path": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", - "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", - "license": "MIT" - }, - "node_modules/follow-redirects": { - "version": "1.16.0", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.16.0.tgz", - "integrity": "sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw==", - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/RubenVerborgh" - } - ], - "license": "MIT", - "engines": { - "node": ">=4.0" - }, - "peerDependenciesMeta": { - "debug": { - "optional": true - } - } - }, - "node_modules/foreground-child": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", - "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", - "license": "ISC", - "dependencies": { - "cross-spawn": "^7.0.6", - "signal-exit": "^4.0.1" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/form-data": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.6.tgz", - "integrity": "sha512-vKatAh4SlVfgbv+YtmhiRjhEMJsYpsG1Y2rMQtR+SVSbytsSD1YGzDIcrAJmdFec88u/+VoGmxnl+80gL1tRCQ==", - "license": "MIT", - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "es-set-tostringtag": "^2.1.0", - "hasown": "^2.0.4", - "mime-types": "^2.1.35" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/fs-minipass": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-3.0.3.tgz", - "integrity": "sha512-XUBA9XClHbnJWSfBzjkm6RvPsyg3sryZt06BEQoXcF7EK/xpGaQYJgQKDJSUH5SGZ76Y7pFx1QBnXz09rU5Fbw==", - "license": "ISC", - "dependencies": { - "minipass": "^7.0.3" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/function-bind": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-intrinsic": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", - "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.2", - "es-define-property": "^1.0.1", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.1.1", - "function-bind": "^1.1.2", - "get-proto": "^1.0.1", - "gopd": "^1.2.0", - "has-symbols": "^1.1.0", - "hasown": "^2.0.2", - "math-intrinsics": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", - "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", - "license": "MIT", - "dependencies": { - "dunder-proto": "^1.0.1", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/glob": { - "version": "10.5.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", - "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", - "license": "ISC", - "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^3.1.2", - "minimatch": "^9.0.4", - "minipass": "^7.1.2", - "package-json-from-dist": "^1.0.0", - "path-scurry": "^1.11.1" - }, - "bin": { - "glob": "dist/esm/bin.mjs" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/gopd": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", - "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/graceful-fs": { - "version": "4.2.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", - "license": "ISC" - }, - "node_modules/has-symbols": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", - "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-tostringtag": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", - "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", - "license": "MIT", - "dependencies": { - "has-symbols": "^1.0.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/hasown": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.4.tgz", - "integrity": "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==", - "license": "MIT", - "dependencies": { - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/http-cache-semantics": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.2.0.tgz", - "integrity": "sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ==", - "license": "BSD-2-Clause" - }, - "node_modules/http-proxy-agent": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", - "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", - "license": "MIT", - "dependencies": { - "agent-base": "^7.1.0", - "debug": "^4.3.4" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/https-proxy-agent": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", - "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", - "license": "MIT", - "dependencies": { - "agent-base": "^7.1.2", - "debug": "4" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/iconv-lite": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", - "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", - "license": "MIT", - "optional": true, - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", - "license": "MIT", - "engines": { - "node": ">=0.8.19" - } - }, - "node_modules/indent-string": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", - "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/ip-address": { - "version": "10.2.0", - "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.2.0.tgz", - "integrity": "sha512-/+S6j4E9AHvW9SWMSEY9Xfy66O5PWvVEJ08O0y5JGyEKQpojb0K0GKpz/v5HJ/G0vi3D2sjGK78119oXZeE0qA==", - "license": "MIT", - "engines": { - "node": ">= 12" - } - }, - "node_modules/ipaddr.js": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.4.0.tgz", - "integrity": "sha512-9VGk3HGanVE6JoZXHiCpnGy5X0jYDnN4EA4lntFPj+1vIWlFhIylq2CrrCOJH9EAhc5CYhq18F2Av2tgoAPsYQ==", - "license": "MIT", - "engines": { - "node": ">= 10" - } - }, - "node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/is-lambda": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-lambda/-/is-lambda-1.0.1.tgz", - "integrity": "sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ==", - "license": "MIT" - }, - "node_modules/isexe": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.1.tgz", - "integrity": "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==", - "license": "ISC", - "engines": { - "node": ">=16" - } - }, - "node_modules/jackspeak": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", - "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", - "license": "BlueOak-1.0.0", - "dependencies": { - "@isaacs/cliui": "^8.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - }, - "optionalDependencies": { - "@pkgjs/parseargs": "^0.11.0" - } - }, - "node_modules/lodash": { - "version": "4.18.1", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.18.1.tgz", - "integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==", - "license": "MIT" - }, - "node_modules/lru-cache": { - "version": "10.4.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", - "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", - "license": "ISC" - }, - "node_modules/make-fetch-happen": { - "version": "13.0.1", - "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-13.0.1.tgz", - "integrity": "sha512-cKTUFc/rbKUd/9meOvgrpJ2WrNzymt6jfRDdwg5UCnVzv9dTpEj9JS5m3wtziXVCjluIXyL8pcaukYqezIzZQA==", - "license": "ISC", - "dependencies": { - "@npmcli/agent": "^2.0.0", - "cacache": "^18.0.0", - "http-cache-semantics": "^4.1.1", - "is-lambda": "^1.0.1", - "minipass": "^7.0.2", - "minipass-fetch": "^3.0.0", - "minipass-flush": "^1.0.5", - "minipass-pipeline": "^1.2.4", - "negotiator": "^0.6.3", - "proc-log": "^4.2.0", - "promise-retry": "^2.0.1", - "ssri": "^10.0.0" - }, - "engines": { - "node": "^16.14.0 || >=18.0.0" - } - }, - "node_modules/math-intrinsics": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", - "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "license": "MIT", - "dependencies": { - "mime-db": "1.52.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/minimatch": { - "version": "9.0.9", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", - "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.2" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/minipass": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", - "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", - "license": "ISC", - "engines": { - "node": ">=16 || 14 >=14.17" - } - }, - "node_modules/minipass-collect": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/minipass-collect/-/minipass-collect-2.0.1.tgz", - "integrity": "sha512-D7V8PO9oaz7PWGLbCACuI1qEOsq7UKfLotx/C0Aet43fCUB/wfQ7DYeq2oR/svFJGYDHPr38SHATeaj/ZoKHKw==", - "license": "ISC", - "dependencies": { - "minipass": "^7.0.3" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - } - }, - "node_modules/minipass-fetch": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-3.0.5.tgz", - "integrity": "sha512-2N8elDQAtSnFV0Dk7gt15KHsS0Fyz6CbYZ360h0WTYV1Ty46li3rAXVOQj1THMNLdmrD9Vt5pBPtWtVkpwGBqg==", - "license": "MIT", - "dependencies": { - "minipass": "^7.0.3", - "minipass-sized": "^1.0.3", - "minizlib": "^2.1.2" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - }, - "optionalDependencies": { - "encoding": "^0.1.13" - } - }, - "node_modules/minipass-flush": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/minipass-flush/-/minipass-flush-1.0.5.tgz", - "integrity": "sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==", - "license": "ISC", - "dependencies": { - "minipass": "^3.0.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/minipass-flush/node_modules/minipass": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", - "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", - "license": "ISC", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/minipass-pipeline": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz", - "integrity": "sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==", - "license": "ISC", - "dependencies": { - "minipass": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/minipass-pipeline/node_modules/minipass": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", - "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", - "license": "ISC", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/minipass-sized": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/minipass-sized/-/minipass-sized-1.0.3.tgz", - "integrity": "sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g==", - "license": "ISC", - "dependencies": { - "minipass": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/minipass-sized/node_modules/minipass": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", - "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", - "license": "ISC", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/minizlib": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", - "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", - "license": "MIT", - "dependencies": { - "minipass": "^3.0.0", - "yallist": "^4.0.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/minizlib/node_modules/minipass": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", - "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", - "license": "ISC", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", - "license": "MIT", - "bin": { - "mkdirp": "bin/cmd.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "license": "MIT" - }, - "node_modules/nan": { - "version": "2.24.0", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.24.0.tgz", - "integrity": "sha512-Vpf9qnVW1RaDkoNKFUvfxqAbtI8ncb8OJlqZ9wwpXzWPEsvsB1nvdUi6oYrHIkQ1Y/tMDnr1h4nczS0VB9Xykg==", - "license": "MIT" - }, - "node_modules/negotiator": { - "version": "0.6.4", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.4.tgz", - "integrity": "sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/node-gyp": { - "version": "10.3.1", - "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-10.3.1.tgz", - "integrity": "sha512-Pp3nFHBThHzVtNY7U6JfPjvT/DTE8+o/4xKsLQtBoU+j2HLsGlhcfzflAoUreaJbNmYnX+LlLi0qjV8kpyO6xQ==", - "license": "MIT", - "dependencies": { - "env-paths": "^2.2.0", - "exponential-backoff": "^3.1.1", - "glob": "^10.3.10", - "graceful-fs": "^4.2.6", - "make-fetch-happen": "^13.0.0", - "nopt": "^7.0.0", - "proc-log": "^4.1.0", - "semver": "^7.3.5", - "tar": "^6.2.1", - "which": "^4.0.0" - }, - "bin": { - "node-gyp": "bin/node-gyp.js" - }, - "engines": { - "node": "^16.14.0 || >=18.0.0" - } - }, - "node_modules/nopt": { - "version": "7.2.1", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-7.2.1.tgz", - "integrity": "sha512-taM24ViiimT/XntxbPyJQzCG+p4EKOpgD3mxFwW38mGjVUrfERQOeY4EDHjdnptttfHuHQXFx+lTP08Q+mLa/w==", - "license": "ISC", - "dependencies": { - "abbrev": "^2.0.0" - }, - "bin": { - "nopt": "bin/nopt.js" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/p-map": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", - "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", - "license": "MIT", - "dependencies": { - "aggregate-error": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/package-json-from-dist": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", - "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", - "license": "BlueOak-1.0.0" - }, - "node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/path-scurry": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", - "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", - "license": "BlueOak-1.0.0", - "dependencies": { - "lru-cache": "^10.2.0", - "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" - }, - "engines": { - "node": ">=16 || 14 >=14.18" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/proc-log": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-4.2.0.tgz", - "integrity": "sha512-g8+OnU/L2v+wyiVK+D5fA34J7EH8jZ8DDlvwhRCMxmMj7UCBvxiO1mGeN+36JXIKF4zevU4kRBd8lVgG9vLelA==", - "license": "ISC", - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/promise-retry": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/promise-retry/-/promise-retry-2.0.1.tgz", - "integrity": "sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==", - "license": "MIT", - "dependencies": { - "err-code": "^2.0.2", - "retry": "^0.12.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/proxy-from-env": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-2.1.0.tgz", - "integrity": "sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA==", - "license": "MIT", - "engines": { - "node": ">=10" - } - }, - "node_modules/retry": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", - "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==", - "license": "MIT", - "engines": { - "node": ">= 4" - } - }, - "node_modules/safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "license": "MIT", - "optional": true - }, - "node_modules/semver": { - "version": "7.7.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", - "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "license": "MIT", - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/signal-exit": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", - "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", - "license": "ISC", - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/smart-buffer": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", - "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", - "license": "MIT", - "engines": { - "node": ">= 6.0.0", - "npm": ">= 3.0.0" - } - }, - "node_modules/socks": { - "version": "2.8.7", - "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.7.tgz", - "integrity": "sha512-HLpt+uLy/pxB+bum/9DzAgiKS8CX1EvbWxI4zlmgGCExImLdiad2iCwXT5Z4c9c3Eq8rP2318mPW2c+QbtjK8A==", - "license": "MIT", - "dependencies": { - "ip-address": "^10.0.1", - "smart-buffer": "^4.2.0" - }, - "engines": { - "node": ">= 10.0.0", - "npm": ">= 3.0.0" - } - }, - "node_modules/socks-proxy-agent": { - "version": "8.0.5", - "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-8.0.5.tgz", - "integrity": "sha512-HehCEsotFqbPW9sJ8WVYB6UbmIMv7kUUORIF2Nncq4VQvBfNBLibW9YZR5dlYCSUhwcD628pRllm7n+E+YTzJw==", - "license": "MIT", - "dependencies": { - "agent-base": "^7.1.2", - "debug": "^4.3.4", - "socks": "^2.8.3" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/ssri": { - "version": "10.0.6", - "resolved": "https://registry.npmjs.org/ssri/-/ssri-10.0.6.tgz", - "integrity": "sha512-MGrFH9Z4NP9Iyhqn16sDtBpRRNJ0Y2hNa6D65h736fVSaPCHr4DM4sWUNvVaSuC+0OBGhwsrydQwmgfg5LncqQ==", - "license": "ISC", - "dependencies": { - "minipass": "^7.0.3" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/string-width": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", - "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", - "license": "MIT", - "dependencies": { - "eastasianwidth": "^0.2.0", - "emoji-regex": "^9.2.2", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/string-width-cjs": { - "name": "string-width", - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/string-width-cjs/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/string-width-cjs/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "license": "MIT" - }, - "node_modules/string-width-cjs/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-ansi": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz", - "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==", - "license": "MIT", - "dependencies": { - "ansi-regex": "^6.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, - "node_modules/strip-ansi-cjs": { - "name": "strip-ansi", - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-ansi-cjs/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "node_modules/ipaddr.js": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.4.0.tgz", + "integrity": "sha512-9VGk3HGanVE6JoZXHiCpnGy5X0jYDnN4EA4lntFPj+1vIWlFhIylq2CrrCOJH9EAhc5CYhq18F2Av2tgoAPsYQ==", "license": "MIT", "engines": { - "node": ">=8" - } - }, - "node_modules/tar": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz", - "integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==", - "deprecated": "Old versions of tar are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exhorbitant rates) by contacting i@izs.me", - "license": "ISC", - "dependencies": { - "chownr": "^2.0.0", - "fs-minipass": "^2.0.0", - "minipass": "^5.0.0", - "minizlib": "^2.1.1", - "mkdirp": "^1.0.3", - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/tar/node_modules/fs-minipass": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", - "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", - "license": "ISC", - "dependencies": { - "minipass": "^3.0.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/tar/node_modules/fs-minipass/node_modules/minipass": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", - "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", - "license": "ISC", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/tar/node_modules/minipass": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", - "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", - "license": "ISC", - "engines": { - "node": ">=8" - } - }, - "node_modules/unique-filename": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-3.0.0.tgz", - "integrity": "sha512-afXhuC55wkAmZ0P18QsVE6kp8JaxrEokN2HGIoIVv2ijHQd419H0+6EigAFcIzXeMIkcIkNBpB3L/DXB3cTS/g==", - "license": "ISC", - "dependencies": { - "unique-slug": "^4.0.0" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/unique-slug": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-4.0.0.tgz", - "integrity": "sha512-WrcA6AyEfqDX5bWige/4NQfPZMtASNVxdmWR76WESYQVAACSgWcR6e9i0mofqqBxYFtL4oAxPIptY73/0YE1DQ==", - "license": "ISC", - "dependencies": { - "imurmurhash": "^0.1.4" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": ">= 10" } }, - "node_modules/v8-sandbox": { - "version": "3.2.12", - "resolved": "https://registry.npmjs.org/v8-sandbox/-/v8-sandbox-3.2.12.tgz", - "integrity": "sha512-R7MhdWLba7CaDhAssx+EA0I89fdaHxB46jwZ3JDsympjli1+Ijg7+tQP9Kvyk8Iff5fgbpWfLZsOyPohAaIHVA==", + "node_modules/isolated-vm": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/isolated-vm/-/isolated-vm-6.2.0.tgz", + "integrity": "sha512-UuSlxSHWt2QuJ5WvBhzlIJx2VVZN/a44SqBbEZFKNdvuSyhOvhmyDo8SQ+njVbhnh/njoL/aW0bUTiFYlpweGQ==", "hasInstallScript": true, - "license": "BSD", - "dependencies": { - "async": "^3.2.0", - "axios": "^1.6.0", - "bindings": "^1.5.0", - "lodash": "^4.17.21", - "nan": "^2.18.0", - "node-gyp": "^10.1.0", - "signal-exit": "^4.1.0" - } - }, - "node_modules/which": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/which/-/which-4.0.0.tgz", - "integrity": "sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg==", "license": "ISC", "dependencies": { - "isexe": "^3.1.1" - }, - "bin": { - "node-which": "bin/which.js" - }, - "engines": { - "node": "^16.13.0 || >=18.0.0" - } - }, - "node_modules/wrap-ansi": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", - "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", - "license": "MIT", - "dependencies": { - "ansi-styles": "^6.1.0", - "string-width": "^5.0.1", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/wrap-ansi-cjs": { - "name": "wrap-ansi", - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/wrap-ansi-cjs/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/wrap-ansi-cjs/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "license": "MIT" - }, - "node_modules/wrap-ansi-cjs/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" + "node-gyp-build": "^4.8.4" }, "engines": { - "node": ">=8" + "node": ">=22.0.0" } }, - "node_modules/wrap-ansi-cjs/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "node_modules/node-gyp-build": { + "version": "4.8.4", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.4.tgz", + "integrity": "sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==", "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" + "bin": { + "node-gyp-build": "bin.js", + "node-gyp-build-optional": "optional.js", + "node-gyp-build-test": "build-test.js" } - }, - "node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "license": "ISC" } } } diff --git a/plugins/hooks/package.json b/plugins/hooks/package.json index 054459cdb7c..e9ff0efb1ac 100644 --- a/plugins/hooks/package.json +++ b/plugins/hooks/package.json @@ -21,7 +21,13 @@ ], "dependencies": { "ipaddr.js": "^2.3.0", - "v8-sandbox": "3.2.12" + "isolated-vm": "^6.0.2" }, - "private": true + "private": true, + "overrides": { + "form-data": "^4.0.4", + "tar": "^7.5.21", + "brace-expansion@^2.0.0": "^2.1.4", + "ip-address": "^10.3.1" + } } diff --git a/plugins/hooks/tests/ssrf.js b/plugins/hooks/tests/ssrf.js index 9801a04d5f4..9122916efd0 100644 --- a/plugins/hooks/tests/ssrf.js +++ b/plugins/hooks/tests/ssrf.js @@ -1,6 +1,5 @@ var request = require('supertest'); var should = require('should'); -var http = require('http'); var testUtils = require('../../../test/testUtils'); request = request(testUtils.url); @@ -88,76 +87,6 @@ describe('SSRF Protection', () => { } }); - describe('CustomCodeEffect HTTP surface', () => { - // v8-sandbox enables its built-in httpRequest() by default, which would - // let custom hook code make server-side requests to internal targets, - // bypassing the SSRF validation that protects the HTTPEffect path. The - // sandbox is created with httpEnabled:false, so custom code must not be - // able to reach any HTTP server via httpRequest(). - // - // We run a real local server and assert the sandbox never reaches it. - // This is independent of how /i/hook/test reports the (failed) effect: - // if httpRequest were enabled the server would be hit; with it disabled - // the call fails and the hit counter stays at 0. - var probe, probeHits = 0, probePort; - - before('start local probe server', function(done) { - probe = http.createServer(function(req, res) { - probeHits++; - res.end('PROBE'); - }); - probe.listen(0, '127.0.0.1', function() { - probePort = probe.address().port; - done(); - }); - }); - - after('stop local probe server', function(done) { - if (probe) { - probe.close(function() { - done(); - }); - } - else { - done(); - } - }); - - it('should not let httpRequest() reach a server from custom code', async() => { - var APP_ID = testUtils.get('APP_ID'); - // Try (and tolerate failure of) an httpRequest to our local probe. - var code = "try { httpRequest({url:'http://127.0.0.1:" + probePort + "/poke'}); }" - + " catch (e) { /* httpRequest disabled -> call fails, expected */ }"; - var hookConfig = { - name: 'custom-code-no-http', - description: 'verify httpRequest cannot reach a server from the sandbox', - apps: [APP_ID], - trigger: { - type: 'APIEndPointTrigger', - configuration: { - path: `cc-nohttp-${Date.now()}`, - method: 'get', - }, - }, - effects: [{ - type: 'CustomCodeEffect', - configuration: { - code: code, - }, - }], - enabled: true, - }; - - // Status is irrelevant (a disabled httpRequest makes the effect - // error, which the endpoint may report as non-200). The security - // assertion is purely that our probe server was never contacted. - await request.post(getRequestURL('/i/hook/test')) - .send({hook_config: JSON.stringify(hookConfig), mock_data: JSON.stringify({})}); - - should(probeHits).equal(0); - }); - }); - describe('Allowed URLs', () => { var allowedCases = [ {url: 'https://example.com/', label: 'example.com (external domain)'}, diff --git a/plugins/plugins/package.json b/plugins/plugins/package.json index a5b6771e3f9..5edb74460f0 100644 --- a/plugins/plugins/package.json +++ b/plugins/plugins/package.json @@ -19,5 +19,16 @@ "plugins" ], "dependencies": {}, - "private": true + "private": true, + "overrides": { + "basic-ftp": "^5.3.1", + "body-parser@^2.0.0": "^2.3.0", + "brace-expansion@^2.0.0": "^2.1.4", + "immutable": "^5.1.8", + "ip-address": "^10.3.1", + "postcss": "^8.5.23", + "protobufjs": "^7.6.5", + "qs": "^6.15.2", + "ws@^8.0.0": "^8.21.0" + } } diff --git a/plugins/push/package-lock.json b/plugins/push/package-lock.json index d013c29c2e3..21cef882685 100644 --- a/plugins/push/package-lock.json +++ b/plugins/push/package-lock.json @@ -1599,9 +1599,9 @@ } }, "node_modules/protobufjs": { - "version": "7.6.4", - "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.6.4.tgz", - "integrity": "sha512-RJJPTTpvFfHcWLkIa2JFWK4XvtSzS0yEWDmunqHXli1h3JlkbcQZXDZdcWxv+JK3Xsl5/UFDPZ0iGm7DAengYw==", + "version": "7.6.5", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.6.5.tgz", + "integrity": "sha512-/FPD0nUc9jH6rfFjji9IBqOz4pcSE3CsT1m7Ep6Mdb0LxSUMj8hgl6GomOvZzpNpAqqGaXA0P3VSrZLFzIhQrw==", "hasInstallScript": true, "license": "BSD-3-Clause", "optional": true, @@ -2177,7 +2177,7 @@ "fast-deep-equal": "^3.1.1", "functional-red-black-tree": "^1.0.1", "google-gax": "^4.3.3", - "protobufjs": "^7.6.4" + "protobufjs": "^7.6.5" } }, "@google-cloud/paginator": { @@ -2243,7 +2243,7 @@ "requires": { "lodash.camelcase": "^4.3.0", "long": "^5.0.0", - "protobufjs": "^7.6.4", + "protobufjs": "^7.6.5", "yargs": "^17.7.2" } }, @@ -2418,7 +2418,7 @@ "@types/caseless": "*", "@types/node": "*", "@types/tough-cookie": "*", - "form-data": "^2.5.0" + "form-data": "^2.5.6" } }, "@types/send": { @@ -2767,7 +2767,7 @@ "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.4.tgz", "integrity": "sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==", "requires": { - "websocket-driver": ">=0.5.1" + "websocket-driver": "^0.7.5" } }, "firebase-admin": { @@ -2921,7 +2921,7 @@ "node-fetch": "^2.7.0", "object-hash": "^3.0.0", "proto3-json-serializer": "^2.0.2", - "protobufjs": "^7.6.4", + "protobufjs": "^7.6.5", "retry-request": "^7.0.0", "uuid": "^11.1.1" } @@ -3321,13 +3321,13 @@ "integrity": "sha512-SAzp/O4Yh02jGdRc+uIrGoe87dkN/XtwxfZ4ZyafJHymd79ozp5VG5nyZ7ygqPM5+cpLDjjGnYFUkngonyDPOQ==", "optional": true, "requires": { - "protobufjs": "^7.6.4" + "protobufjs": "^7.6.5" } }, "protobufjs": { - "version": "7.6.4", - "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.6.4.tgz", - "integrity": "sha512-RJJPTTpvFfHcWLkIa2JFWK4XvtSzS0yEWDmunqHXli1h3JlkbcQZXDZdcWxv+JK3Xsl5/UFDPZ0iGm7DAengYw==", + "version": "7.6.5", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.6.5.tgz", + "integrity": "sha512-/FPD0nUc9jH6rfFjji9IBqOz4pcSE3CsT1m7Ep6Mdb0LxSUMj8hgl6GomOvZzpNpAqqGaXA0P3VSrZLFzIhQrw==", "optional": true, "requires": { "@protobufjs/aspromise": "^1.1.2", diff --git a/plugins/push/package.json b/plugins/push/package.json index fb5ec41c4a8..ce6f74e22c3 100644 --- a/plugins/push/package.json +++ b/plugins/push/package.json @@ -27,7 +27,10 @@ "overrides": { "uuid": "^11.1.1", "@tootallnate/once": "^2.0.1", - "protobufjs": "^7.6.4" + "protobufjs": "^7.6.5", + "websocket-driver": "^0.7.5", + "fast-xml-parser@^4.0.0": "^4.5.4", + "form-data@^2.0.0": "^2.5.6" }, "private": true } From ab1c1b6b34c42b29aea40e900964333d0bdd33a7 Mon Sep 17 00:00:00 2001 From: Kanwar Ujjaval Singh <4216199+kanwarujjaval@users.noreply.github.com> Date: Sun, 9 Aug 2026 23:14:27 +0530 Subject: [PATCH 2/5] Support Ubuntu 24 and update dependencies. Added compatibility for Ubuntu 24 in A/B testing plugin setup script. Updated npm dependencies in several plugins to resolve vulnerabilities and improve compatibility. Migrated Docker API image to use Node.js 22, upgraded CentOS tools, and replaced deprecated components with newer alternatives for security and stability improvements. --- CHANGELOG.md | 10 ++++++---- Dockerfile-centos-api | 6 +++++- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1ac1f0111a8..536239a6e2b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,18 +1,20 @@ -## Version 25.03.xx +## Version 25.03.52 Security Fixes: -- [docker] Rebuilt the api and frontend images as multi-stage builds on Debian 13 with Node.js 24, so compilers, development headers and build tooling are no longer present in the shipped images. +- [docker] Rebuilt the api and frontend images as multi-stage builds on Debian 13 with Node.js 24, so compilers, development headers and build tooling are no longer present in the shipped images. Critical findings drop from 27 to 6 on api and to 4 on frontend; every remaining critical has no upstream fix and is unreachable at runtime, as detailed in `security_report.md` - [core] Updated vulnerable transitive dependencies through npm overrides: tar, form-data, brace-expansion, minimatch, immutable, ip-address, basic-ftp, websocket-driver, js-yaml, body-parser, qs, postcss, protobufjs and ws -- [hooks] Replaced the unmaintained v8-sandbox custom-code sandbox with isolated-vm, which exposes no network, filesystem or process surface to custom code. +- [hooks] Replaced the unmaintained v8-sandbox custom-code sandbox with isolated-vm, which exposes no network, filesystem or process surface to custom code. Custom code that relies on `setTimeout`, `setInterval` or asynchronous completion now fails with a logged error instead of running, and the `bufferToBase64` and `base64ToBuffer` helpers are no longer available - [ab-testing] Replaced pystan 2.19 and its pickled models with cmdstanpy and compiled Stan executables, removing the end-of-life Python 3.8 runtime Fixes: +- [core] `utils.decrypt` no longer throws on Node.js 22 and later. `crypto.createDecipher` was removed from Node.js, which broke decryption of every value stored in the pre-IV format, including the MongoDB password, two-factor secrets and LDAP and Active Directory credentials +- [docker] `/opt/countly` is group-writable again, so containers running under an arbitrary UID, such as OpenShift or Kubernetes `runAsUser`, can write to it - [docker] Restored the `/usr/bin/countly` symlink so the management CLI works inside the container - [reports] Headless Chrome now launches in the api image; PDF and e-mail report rendering previously failed to start - [docker] A plugin whose dependency install or asset build fails now fails the image build instead of being shipped broken without warning - [ab-testing] The model runner locates its CmdStan installation automatically when `CMDSTAN` is not set in the service environment Enterprise Fixes: -- [ab-testing] `/o/ab-testing/check-models` reports whether the models are actually compiled; it previously reported success whenever any model file was present. +- [ab-testing] `/o/ab-testing/check-models` reports whether the models are actually compiled; it previously reported success whenever any model file was present. Reported statistics differ slightly from earlier releases because the sampler retains more draws; the underlying model is unchanged Enterprise Features: - [ab-testing] Added `/o/ab-testing/test-models`, a global-admin diagnostic that runs a model against supplied variant data and returns the raw result diff --git a/Dockerfile-centos-api b/Dockerfile-centos-api index 0b742f11e11..a7e42081cd1 100644 --- a/Dockerfile-centos-api +++ b/Dockerfile-centos-api @@ -42,7 +42,7 @@ RUN yum update -y RUN curl -s -L -o /tmp/tini.rpm "https://github.com/krallin/tini/releases/download/v${TINI_VERSION}/tini_${TINI_VERSION}.rpm" && \ rpm -i /tmp/tini.rpm && \ \ - curl -sL https://rpm.nodesource.com/setup_20.x | bash - && \ + curl -sL https://rpm.nodesource.com/setup_22.x | bash - && \ yum install -y nodejs python3.8 python2 python38-libs python38-devel python38-pip nss libdrm libgbm cyrus-sasl* && \ ln -s /usr/bin/node /usr/bin/nodejs && \ unlink /usr/bin/python3 && \ @@ -51,12 +51,16 @@ RUN curl -s -L -o /tmp/tini.rpm "https://github.com/krallin/tini/releases/downlo \ yum install -y python3-policycoreutils && \ yum group install -y "Development Tools" && \ + yum install -y gcc-toolset-11 && \ yum install -y epel-release && \ yum install -y pango.x86_64 libXcomposite.x86_64 libXcursor.x86_64 libXdamage.x86_64 libXext.x86_64 libXi.x86_64 libXtst.x86_64 cups-libs.x86_64 libXScrnSaver.x86_64 libXrandr.x86_64 GConf2.x86_64 alsa-lib.x86_64 atk.x86_64 gtk3.x86_64 xorg-x11-fonts-100dpi xorg-x11-fonts-75dpi xorg-x11-utils xorg-x11-fonts-cyrillic xorg-x11-fonts-Type1 xorg-x11-fonts-misc && \ curl -L -O -J "https://box.tools.count.ly/public.php/dav/files/Wj8opzNdyE5DyDX/?accept=zip" && \ yum install -y raven-release.el8.noarch.rpm && \ yum install -y wget openssl-devel make git libsqlite* sqlite unzip bzip2 && \ # modify standard distribution + # isolated-vm needs -std=c++20 and node-gyp needs python>=3.8; stock CentOS 8 has neither + . /opt/rh/gcc-toolset-11/enable && \ + export npm_config_python=/usr/bin/python3.8 && \ ./bin/docker/modify.sh && \ \ # preinstall From f90aef4f9f73ee829ffd088c975d8f26ee32a354 Mon Sep 17 00:00:00 2001 From: Kanwar Ujjaval Singh <4216199+kanwarujjaval@users.noreply.github.com> Date: Mon, 10 Aug 2026 00:09:47 +0530 Subject: [PATCH 3/5] Update CHANGELOG: simplify Docker security fix note --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 536239a6e2b..b6e7ae86933 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ ## Version 25.03.52 Security Fixes: -- [docker] Rebuilt the api and frontend images as multi-stage builds on Debian 13 with Node.js 24, so compilers, development headers and build tooling are no longer present in the shipped images. Critical findings drop from 27 to 6 on api and to 4 on frontend; every remaining critical has no upstream fix and is unreachable at runtime, as detailed in `security_report.md` +- [docker] Rebuilt the api and frontend images as multi-stage builds on Debian 13 with Node.js 24, so compilers, development headers and build tooling are no longer present in the shipped images. - [core] Updated vulnerable transitive dependencies through npm overrides: tar, form-data, brace-expansion, minimatch, immutable, ip-address, basic-ftp, websocket-driver, js-yaml, body-parser, qs, postcss, protobufjs and ws - [hooks] Replaced the unmaintained v8-sandbox custom-code sandbox with isolated-vm, which exposes no network, filesystem or process surface to custom code. Custom code that relies on `setTimeout`, `setInterval` or asynchronous completion now fails with a logged error instead of running, and the `bufferToBase64` and `base64ToBuffer` helpers are no longer available - [ab-testing] Replaced pystan 2.19 and its pickled models with cmdstanpy and compiled Stan executables, removing the end-of-life Python 3.8 runtime From 5df9997d11a8561fe7877bc8ccfbb6cdf52b994f Mon Sep 17 00:00:00 2001 From: Kanwar Ujjaval Singh <4216199+kanwarujjaval@users.noreply.github.com> Date: Mon, 10 Aug 2026 15:07:36 +0530 Subject: [PATCH 4/5] Upgrade to Node.js 22 and modernize environment setup Upgraded Node.js from version 20 to 22 across scripts, Dockerfiles, and dependencies. Enhanced RHEL 8 compatibility with GCC toolset-11 and Python 3.8 for building isolated-vm. Removed legacy utilities, deprecated APIs, and unnecessary npm overrides. Consolidated AB testing and Docker API image improvements for enhanced security and reliability. Added new test scripts and fixed sandbox-related issues in the Hooks plugin. --- CHANGELOG.md | 12 ++- Dockerfile-api | 14 ++-- Dockerfile-frontend | 8 +- api/utils/utils.js | 27 +------ bin/countly.install_rhel.sh | 11 ++- bin/countly.install_ubuntu.sh | 2 +- bin/upgrade/25.03/upgrade.nodejs.22.sh | 59 ++++++++++++++ package.json | 1 - .../hooks/api/parts/effects/custom_code.js | 3 +- plugins/hooks/package.json | 8 +- plugins/hooks/tests/custom_code_sandbox.js | 80 +++++++++++++++++++ plugins/hooks/tests/index.js | 1 + plugins/plugins/package.json | 13 +-- plugins/push/package.json | 1 - 14 files changed, 173 insertions(+), 67 deletions(-) create mode 100644 bin/upgrade/25.03/upgrade.nodejs.22.sh create mode 100644 plugins/hooks/tests/custom_code_sandbox.js diff --git a/CHANGELOG.md b/CHANGELOG.md index b6e7ae86933..2fe359bd34c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,4 @@ -## Version 25.03.52 +## Version 25.03.xx Security Fixes: - [docker] Rebuilt the api and frontend images as multi-stage builds on Debian 13 with Node.js 24, so compilers, development headers and build tooling are no longer present in the shipped images. - [core] Updated vulnerable transitive dependencies through npm overrides: tar, form-data, brace-expansion, minimatch, immutable, ip-address, basic-ftp, websocket-driver, js-yaml, body-parser, qs, postcss, protobufjs and ws @@ -6,23 +6,21 @@ Security Fixes: - [ab-testing] Replaced pystan 2.19 and its pickled models with cmdstanpy and compiled Stan executables, removing the end-of-life Python 3.8 runtime Fixes: -- [core] `utils.decrypt` no longer throws on Node.js 22 and later. `crypto.createDecipher` was removed from Node.js, which broke decryption of every value stored in the pre-IV format, including the MongoDB password, two-factor secrets and LDAP and Active Directory credentials -- [docker] `/opt/countly` is group-writable again, so containers running under an arbitrary UID, such as OpenShift or Kubernetes `runAsUser`, can write to it -- [docker] Restored the `/usr/bin/countly` symlink so the management CLI works inside the container -- [reports] Headless Chrome now launches in the api image; PDF and e-mail report rendering previously failed to start +- [hooks] Custom code calling `setResult(undefined)` no longer fails the effect - [docker] A plugin whose dependency install or asset build fails now fails the image build instead of being shipped broken without warning - [ab-testing] The model runner locates its CmdStan installation automatically when `CMDSTAN` is not set in the service environment Enterprise Fixes: -- [ab-testing] `/o/ab-testing/check-models` reports whether the models are actually compiled; it previously reported success whenever any model file was present. Reported statistics differ slightly from earlier releases because the sampler retains more draws; the underlying model is unchanged +- [ab-testing] `/o/ab-testing/check-models` reports whether the models are actually compiled. Enterprise Features: - [ab-testing] Added `/o/ab-testing/test-models`, a global-admin diagnostic that runs a model against supplied variant data and returns the raw result Dependencies: +- [core] Node.js 20 to 22 for package installs and upgrades. The hooks plugin requires Node.js 22 or later, as isolated-vm does not support Node.js 20. Fresh installs on Ubuntu and RHEL now install Node.js 22, and `bin/upgrade/25.03/upgrade.nodejs.22.sh` upgrades an existing 25.03 install. RHEL 8 additionally installs gcc-toolset-11 and Python 3.8, since isolated-vm publishes no prebuilt binary for glibc 2.28 and has to be compiled there - [docker] Node.js 20 to 24, Debian 12 to 13, npm 10 to 11.19.0. Custom plugins containing native addons must be rebuilt, and images derived from these must reinstall any build tooling they need - [ab-testing] Python 3.8 to 3.12, pystan 2.19.1.1 to cmdstanpy 1.2.5 with CmdStan 2.36.0 -- [hooks] v8-sandbox 3.2.12 to isolated-vm 6.0.2 +- [hooks] v8-sandbox 3.2.12 to isolated-vm 6.2.0 ## Version 25.03.51 Fixes: diff --git a/Dockerfile-api b/Dockerfile-api index 3bdc7edb945..0e942123d6b 100644 --- a/Dockerfile-api +++ b/Dockerfile-api @@ -7,7 +7,7 @@ ENV NODE_OPTIONS="--max-old-space-size=2048" # Build dependencies live only in build stages and are never shipped RUN apt-get update && \ apt-get install -y curl wget git make gcc g++ unzip xz-utils build-essential sudo \ - python3 zlib1g-dev libssl-dev libncurses-dev libgdbm-dev libnss3-dev libsqlite3-dev \ + python3 python3-setuptools zlib1g-dev libssl-dev libncurses-dev libgdbm-dev libnss3-dev libsqlite3-dev \ libreadline-dev libffi-dev libbz2-dev sqlite3 ca-certificates # Python 3.12 from source for the ab-testing cmdstanpy models @@ -32,8 +32,9 @@ ARG CMDSTAN_VERSION=2.36.0 ENV CMDSTAN=/opt/cmdstan/cmdstan-${CMDSTAN_VERSION} WORKDIR /opt/countly -# CE repo carries a byte-identical copy of this file so standalone builds work -COPY plugins/ab-testing/api/bayesian/requirements_docker.txt ./plugins/ab-testing/api/bayesian/requirements_docker.txt +# ab-testing is EE-only. Bracketing every separator makes the path one glob, so +# these COPYs resolve to nothing instead of failing in a CE-only build context. +COPY plugins[/]ab-testing[/]api[/]bayesian[/]requirements_docker.txt ./plugins/ab-testing/api/bayesian/ # Install the python stack only when the ab-testing plugin is enabled RUN set -eux; \ @@ -41,7 +42,9 @@ RUN set -eux; \ || { mkdir -p plugins/ab-testing/api/bayesian; touch plugins/ab-testing/api/bayesian/.ab_disabled; exit 0; }; \ python3.12 -m pip install --no-cache-dir -r plugins/ab-testing/api/bayesian/requirements_docker.txt -COPY plugins/ab-testing/api/bayesian/ ./plugins/ab-testing/api/bayesian/ +COPY plugins[/]ab-testing[/]api[/]bayesian[/]*.py ./plugins/ab-testing/api/bayesian/ +COPY plugins[/]ab-testing[/]api[/]bayesian[/]models[/]*.py ./plugins/ab-testing/api/bayesian/models/ +COPY plugins[/]ab-testing[/]api[/]bayesian[/]models[/]stan_models[/]*.stan ./plugins/ab-testing/api/bayesian/models/stan_models/ # Install CmdStan and compile the seven variant models RUN set -eux; \ @@ -113,7 +116,6 @@ ENV COUNTLY_CONTAINER="api" \ COUNTLY_CONFIG_API_API_WORKERS="1" \ COUNTLY_CONFIG_API_API_HOST="0.0.0.0" \ NODE_OPTIONS="--max-old-space-size=2048 --no-node-snapshot" \ - npm_config_dangerously_allow_all_scripts=true \ HOME=/tmp \ CMDSTAN=/opt/cmdstan/cmdstan-${CMDSTAN_VERSION} \ PUPPETEER_CACHE_DIR=/opt/countly/.cache/puppeteer @@ -144,7 +146,7 @@ RUN ldconfig && \ ln -sf /usr/local/bin/python3.12 /usr/bin/python3 && \ ln -sf /opt/countly/bin/commands/countly.sh /usr/bin/countly && \ chown 1001:0 /opt/countly && chmod g=u /opt/countly && \ - mkdir -p /.npm && chown -R 1001:0 /.npm + mkdir -p /.npm && chown -R 1001:0 /.npm && chmod -R g=u /.npm USER 1001:0 diff --git a/Dockerfile-frontend b/Dockerfile-frontend index 9e9a4bbd10f..39d2ced0467 100644 --- a/Dockerfile-frontend +++ b/Dockerfile-frontend @@ -16,7 +16,7 @@ ENV COUNTLY_CONTAINER="frontend" \ # Build dependencies live only in this stage and are never shipped RUN apt-get update && \ apt-get install -y curl wget git make gcc g++ unzip xz-utils build-essential sudo \ - python3 sqlite3 ca-certificates + python3 python3-setuptools sqlite3 ca-certificates WORKDIR /opt/countly COPY . . @@ -56,8 +56,8 @@ HEALTHCHECK --start-period=400s CMD bash -c '\ ENV COUNTLY_CONTAINER="frontend" \ COUNTLY_DEFAULT_PLUGINS="${COUNTLY_PLUGINS}" \ COUNTLY_CONFIG_FRONTEND_WEB_HOST="0.0.0.0" \ - npm_config_dangerously_allow_all_scripts=true \ - NODE_OPTIONS="--max-old-space-size=2048 --no-node-snapshot" + NODE_OPTIONS="--max-old-space-size=2048 --no-node-snapshot" \ + HOME=/tmp # Runtime-only packages: init, healthcheck, ops tools RUN apt-get update && apt-get upgrade -y && \ @@ -74,7 +74,7 @@ COPY --from=builder /opt/countly /opt/countly # npm stays for in-container plugin rebuilds (countly task dist-all -> npx grunt) RUN ln -sf /opt/countly/bin/commands/countly.sh /usr/bin/countly && \ chown 1001:0 /opt/countly && chmod g=u /opt/countly && \ - mkdir -p /.npm && chown -R 1001:0 /.npm + mkdir -p /.npm && chown -R 1001:0 /.npm && chmod -R g=u /.npm USER 1001:0 diff --git a/api/utils/utils.js b/api/utils/utils.js index 70d4b9c0146..b1c7925c81c 100644 --- a/api/utils/utils.js +++ b/api/utils/utils.js @@ -108,29 +108,6 @@ exports.decrypt = function(crypted, key, iv, algorithm, input_encoding, output_e return decrypted; }; -/** -* Derive key and iv from a password the way OpenSSL EVP_BytesToKey does (MD5, no -* salt, one iteration). This is what the removed crypto.createDecipher() did -* internally, so values encrypted by older Countly versions stay readable. -* @param {string} algorithm - cipher algorithm name -* @param {string} password - password the value was encrypted with -* @returns {object} object with key and iv buffers -*/ -function legacyKeyAndIv(algorithm, password) { - var info = crypto.getCipherInfo(algorithm); - var pass = Buffer.from(password, "binary"); - var block = Buffer.alloc(0); - var out = Buffer.alloc(0); - while (out.length < info.keyLength + info.ivLength) { - block = crypto.createHash("md5").update(Buffer.concat([block, pass])).digest(); - out = Buffer.concat([out, block]); - } - return { - key: out.subarray(0, info.keyLength), - iv: out.subarray(info.keyLength, info.keyLength + info.ivLength) - }; -} - /** * Old deprecated decrypt function, needed for old stored values * @param {string} crypted - value to decrypt @@ -173,9 +150,7 @@ exports.decrypt_old = function(crypted, key, iv, algorithm, input_encoding, outp decipher = crypto.createDecipheriv(algorithm, key, iv); } else { - // crypto.createDecipher was removed in Node 22; derive the same key/iv it used - var derived = legacyKeyAndIv(algorithm, key); - decipher = crypto.createDecipheriv(algorithm, derived.key, derived.iv); + decipher = crypto.createDecipher(algorithm, key); } decrypted = decipher.update(crypted, input_encoding, output_encoding); decrypted += decipher.final(output_encoding); diff --git a/bin/countly.install_rhel.sh b/bin/countly.install_rhel.sh index fbcbbf4ccf0..4edd0cd0ada 100644 --- a/bin/countly.install_rhel.sh +++ b/bin/countly.install_rhel.sh @@ -61,7 +61,7 @@ sudo dnf install -y alsa-lib.x86_64 atk.x86_64 cups-libs.x86_64 gtk3.x86_64 libX sudo dnf update -y nss #install nodejs -sudo dnf module install -y nodejs:20/common +sudo dnf module install -y nodejs:22/common set +e NODE_JS_CMD=$(which nodejs) @@ -86,6 +86,15 @@ sudo chown -R www-data:www-data /var/lib/nginx sudo dnf install -y sendmail sudo systemctl start sendmail > /dev/null || echo "sendmail service does not exist" +#isolated-vm has no prebuilt binary for glibc 2.28, so RHEL 8 builds it from source. +#That needs -std=c++20 and python>=3.8, and stock RHEL 8 has neither. +if [[ "$CENTOS_MAJOR" = "8" ]]; then + sudo dnf install -y gcc-toolset-11 python38 + export npm_config_python=/usr/bin/python3.8 + # shellcheck disable=SC1091 + source /opt/rh/gcc-toolset-11/enable +fi + #install npm modules npm config set prefix "$DIR/../.local/" ( cd "$DIR/.."; npm install argon2; npm install sqlite3 --build-from-source; npm install; ) diff --git a/bin/countly.install_ubuntu.sh b/bin/countly.install_ubuntu.sh index 159ee61be5e..c5db8efc4f9 100644 --- a/bin/countly.install_ubuntu.sh +++ b/bin/countly.install_ubuntu.sh @@ -60,7 +60,7 @@ sudo mkdir -p /etc/apt/keyrings if [ ! -f /etc/apt/keyrings/nodesource.gpg ]; then curl -fsSL "https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key" | sudo gpg --batch --yes --dearmor -o /etc/apt/keyrings/nodesource.gpg fi -NODE_MAJOR=20 +NODE_MAJOR=22 echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_$NODE_MAJOR.x nodistro main" | sudo tee /etc/apt/sources.list.d/nodesource.list sudo apt-get update sudo apt-get install nodejs -y diff --git a/bin/upgrade/25.03/upgrade.nodejs.22.sh b/bin/upgrade/25.03/upgrade.nodejs.22.sh new file mode 100644 index 00000000000..b14a2ef65f8 --- /dev/null +++ b/bin/upgrade/25.03/upgrade.nodejs.22.sh @@ -0,0 +1,59 @@ +#!/bin/bash + +#we have to check since we cannot continue unless +if [ -f /etc/redhat-release ]; then + CENTOS_MAJOR="$(cat /etc/redhat-release |awk -F'[^0-9]+' '{ print $2 }')" + + if [[ "$CENTOS_MAJOR" != "8" && "$CENTOS_MAJOR" != "9" ]]; then + echo "Unsupported OS version, only support CentOS/RHEL 8 and 9." + exit 1 + fi +fi + +if [ -f /etc/lsb-release ]; then + UBUNTU_YEAR="$(lsb_release -sr | cut -d '.' -f 1)"; + + if [[ "$UBUNTU_YEAR" != "20" && "$UBUNTU_YEAR" != "22" && "$UBUNTU_YEAR" != "24" ]]; then + echo "Unsupported OS version, only support Ubuntu 20, 22 and 24." + exit 1 + fi +fi + +DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )/../.." && pwd )" + +#npm 11 removed --unsafe-perm and --build-from-source, which the commands below still use +sudo npm install -g npm@10; + +if [[ -f /usr/local/bin/npm && -f /usr/bin/npm ]]; then + rm /usr/local/bin/npm + ln -s /usr/bin/npm /usr/local/bin/npm +fi + +#upgrade nodejs +if [ -f /etc/redhat-release ]; then + curl -sL https://rpm.nodesource.com/setup_22.x | bash - + yum clean all + yum remove -y nodejs + yum install -y nodejs +fi + +if [ -f /etc/lsb-release ]; then + wget -qO- https://deb.nodesource.com/setup_22.x | bash - + apt-get -y install nodejs || (echo "Failed to install nodejs." ; exit) +fi + +#isolated-vm has no prebuilt binary for glibc 2.28, so RHEL 8 builds it from source. +#That needs -std=c++20 and python>=3.8, and stock RHEL 8 has neither. +if [[ "$CENTOS_MAJOR" = "8" ]]; then + yum install -y gcc-toolset-11 python38 + export npm_config_python=/usr/bin/python3.8 + # shellcheck disable=SC1091 + source /opt/rh/gcc-toolset-11/enable +fi + +#remove previous dependencies, as they need to be rebuild for new nodejs version +rm -rf "$DIR/../node_modules" +rm -rf "$DIR/../plugins/hooks/node_modules" + +(cd "$DIR/.." && sudo npm install --unsafe-perm && sudo npm install argon2 --build-from-source) +(cd "$DIR/../plugins/hooks" && sudo npm install --unsafe-perm) diff --git a/package.json b/package.json index 3543816e67b..907251832cc 100644 --- a/package.json +++ b/package.json @@ -119,7 +119,6 @@ "js-yaml@^3.0.0": "^3.15.1", "js-yaml@^4.0.0": "^4.3.1", "basic-ftp": "^5.3.1", - "fast-xml-parser": "^5.3.5", "tar": "^7.5.21", "websocket-driver": "^0.7.5", "brace-expansion@^1.0.0": "^1.1.18", diff --git a/plugins/hooks/api/parts/effects/custom_code.js b/plugins/hooks/api/parts/effects/custom_code.js index 070044e86a5..2466bbf631e 100644 --- a/plugins/hooks/api/parts/effects/custom_code.js +++ b/plugins/hooks/api/parts/effects/custom_code.js @@ -51,7 +51,8 @@ class CustomCodeEffect { await jail.set('$setResult', setResultRef); // Create wrapper function in isolate that serializes and calls the reference - const wrapperScript = await isolate.compileScript('globalThis.setResult = function(arg) { return $setResult.applySync(undefined, [JSON.stringify(arg)]); }'); + // JSON.stringify(undefined) returns undefined, which JSON.parse cannot read + const wrapperScript = await isolate.compileScript('globalThis.setResult = function(arg) { return $setResult.applySync(undefined, [JSON.stringify(arg === undefined ? null : arg)]); }'); await wrapperScript.run(context); // Prepare code diff --git a/plugins/hooks/package.json b/plugins/hooks/package.json index e9ff0efb1ac..a77c3359de5 100644 --- a/plugins/hooks/package.json +++ b/plugins/hooks/package.json @@ -23,11 +23,5 @@ "ipaddr.js": "^2.3.0", "isolated-vm": "^6.0.2" }, - "private": true, - "overrides": { - "form-data": "^4.0.4", - "tar": "^7.5.21", - "brace-expansion@^2.0.0": "^2.1.4", - "ip-address": "^10.3.1" - } + "private": true } diff --git a/plugins/hooks/tests/custom_code_sandbox.js b/plugins/hooks/tests/custom_code_sandbox.js new file mode 100644 index 00000000000..a90e12df2e0 --- /dev/null +++ b/plugins/hooks/tests/custom_code_sandbox.js @@ -0,0 +1,80 @@ +var request = require('supertest'); +var should = require('should'); +var testUtils = require('../../../test/testUtils'); +request = request(testUtils.url); + +const mockData = {qstring: {a: 1}, paths: ['localhost', 'o', 'hooks', 'sandbox-probe']}; + +/** + * Build a hook config with a single CustomCodeEffect running the given code. + * @param {string} code - custom code to run inside the sandbox + * @param {string} appId - application ID + * @returns {object} hook config + */ +function buildHookConfig(code, appId) { + return { + name: 'custom-code-sandbox', + description: 'verify what the custom code sandbox exposes', + apps: [appId], + trigger: { + type: 'APIEndPointTrigger', + configuration: { + path: `sandbox-probe-${Date.now()}`, + method: 'get', + }, + }, + effects: [{ + type: 'CustomCodeEffect', + configuration: {code: code}, + }], + enabled: true, + }; +} + +/** + * Build the /i/hook/test URL for the given code. + * @param {string} code - custom code to run inside the sandbox + * @returns {string} request URL + */ +function testHookURL(code) { + var API_KEY_ADMIN = testUtils.get('API_KEY_ADMIN'); + var APP_ID = testUtils.get('APP_ID'); + var config = JSON.stringify(buildHookConfig(code, APP_ID)); + return '/i/hook/test?api_key=' + API_KEY_ADMIN + '&app_id=' + APP_ID + + '&hook_config=' + encodeURIComponent(config) + + '&mock_data=' + encodeURIComponent(JSON.stringify(mockData)); +} + +describe('Custom code sandbox', () => { + + // The sandbox has no event loop and no host bindings, so there is nothing to + // reach the network with. Asserting the globals are absent proves that + // directly, rather than proving one particular request happened to fail. + it('should expose no network, filesystem or process globals', async() => { + const probes = ['fetch', 'require', 'process', 'XMLHttpRequest', 'httpRequest', 'Buffer', 'WebSocket']; + const code = 'params.probe = {' + probes.map(function(name) { + return name + ': typeof ' + name; + }).join(', ') + '};'; + + const res = await request.get(testHookURL(code)).expect(200); + + should(res.body).have.property('result'); + const effectStep = res.body.result[1]; + should.exist(effectStep); + should.exist(effectStep.params); + for (let name of probes) { + should(effectStep.params.probe[name]).equal('undefined', name + ' should not exist in the sandbox'); + } + }); + + // JSON.stringify(undefined) returns undefined, which used to reach JSON.parse + // and throw, failing the whole effect instead of clearing the result. + it('should survive custom code calling setResult(undefined)', async() => { + const res = await request.get(testHookURL('setResult(undefined);')).expect(200); + + should(res.body).have.property('result'); + const effectStep = res.body.result[1]; + should.exist(effectStep); + should.exist(effectStep.params); + }); +}); diff --git a/plugins/hooks/tests/index.js b/plugins/hooks/tests/index.js index 8e1cec3484a..c478037d577 100644 --- a/plugins/hooks/tests/index.js +++ b/plugins/hooks/tests/index.js @@ -5,3 +5,4 @@ require('./internal_event_delivery.js'); require('./trigger_config_authz.js'); require('./email.js'); require('./ssrf.js'); +require('./custom_code_sandbox.js'); diff --git a/plugins/plugins/package.json b/plugins/plugins/package.json index 5edb74460f0..a5b6771e3f9 100644 --- a/plugins/plugins/package.json +++ b/plugins/plugins/package.json @@ -19,16 +19,5 @@ "plugins" ], "dependencies": {}, - "private": true, - "overrides": { - "basic-ftp": "^5.3.1", - "body-parser@^2.0.0": "^2.3.0", - "brace-expansion@^2.0.0": "^2.1.4", - "immutable": "^5.1.8", - "ip-address": "^10.3.1", - "postcss": "^8.5.23", - "protobufjs": "^7.6.5", - "qs": "^6.15.2", - "ws@^8.0.0": "^8.21.0" - } + "private": true } diff --git a/plugins/push/package.json b/plugins/push/package.json index ce6f74e22c3..391c17399ce 100644 --- a/plugins/push/package.json +++ b/plugins/push/package.json @@ -29,7 +29,6 @@ "@tootallnate/once": "^2.0.1", "protobufjs": "^7.6.5", "websocket-driver": "^0.7.5", - "fast-xml-parser@^4.0.0": "^4.5.4", "form-data@^2.0.0": "^2.5.6" }, "private": true From 043e2bdefa4a3d88c927957a0162498628c899f6 Mon Sep 17 00:00:00 2001 From: Kanwar Ujjaval Singh <4216199+kanwarujjaval@users.noreply.github.com> Date: Mon, 10 Aug 2026 17:10:57 +0530 Subject: [PATCH 5/5] fix: align mock data paths with trigger configuration in custom_code_sandbox test Adjusted `buildHookConfig` and `testHookURL` to ensure the mock request's paths match the trigger configuration path, resolving inconsistencies in the test setup. --- plugins/hooks/tests/custom_code_sandbox.js | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/plugins/hooks/tests/custom_code_sandbox.js b/plugins/hooks/tests/custom_code_sandbox.js index a90e12df2e0..468585d128c 100644 --- a/plugins/hooks/tests/custom_code_sandbox.js +++ b/plugins/hooks/tests/custom_code_sandbox.js @@ -3,15 +3,14 @@ var should = require('should'); var testUtils = require('../../../test/testUtils'); request = request(testUtils.url); -const mockData = {qstring: {a: 1}, paths: ['localhost', 'o', 'hooks', 'sandbox-probe']}; - /** * Build a hook config with a single CustomCodeEffect running the given code. * @param {string} code - custom code to run inside the sandbox * @param {string} appId - application ID + * @param {string} path - trigger path; must match the mock request's paths[3] * @returns {object} hook config */ -function buildHookConfig(code, appId) { +function buildHookConfig(code, appId, path) { return { name: 'custom-code-sandbox', description: 'verify what the custom code sandbox exposes', @@ -19,7 +18,7 @@ function buildHookConfig(code, appId) { trigger: { type: 'APIEndPointTrigger', configuration: { - path: `sandbox-probe-${Date.now()}`, + path: path, method: 'get', }, }, @@ -39,7 +38,11 @@ function buildHookConfig(code, appId) { function testHookURL(code) { var API_KEY_ADMIN = testUtils.get('API_KEY_ADMIN'); var APP_ID = testUtils.get('APP_ID'); - var config = JSON.stringify(buildHookConfig(code, APP_ID)); + // APIEndPointTrigger matches rule.trigger.configuration.path against paths[3] + // of the mock request, so both must come from the same value or nothing fires + var path = 'sandbox-probe-' + Date.now(); + var config = JSON.stringify(buildHookConfig(code, APP_ID, path)); + var mockData = {qstring: {a: 1}, paths: ['localhost', 'o', 'hooks', path]}; return '/i/hook/test?api_key=' + API_KEY_ADMIN + '&app_id=' + APP_ID + '&hook_config=' + encodeURIComponent(config) + '&mock_data=' + encodeURIComponent(JSON.stringify(mockData));