From 30355a4deac0819d0050c1bd0989ef609e521be3 Mon Sep 17 00:00:00 2001 From: Kaniska Date: Fri, 10 Jul 2026 15:01:17 +0000 Subject: [PATCH 1/9] [universal] - Removing obsolete NVS to mitigate `follow-redirects` issue GHSA-r4q5-vmmm-2653 --- src/universal/.devcontainer/devcontainer.json | 2 - .../nvs/devcontainer-feature.json | 12 --- .../local-features/nvs/install.sh | 79 ------------------- .../setup-user/devcontainer-feature.json | 1 - src/universal/README.md | 2 +- src/universal/manifest.json | 3 +- src/universal/test-project/test.sh | 8 +- 7 files changed, 6 insertions(+), 101 deletions(-) delete mode 100644 src/universal/.devcontainer/local-features/nvs/devcontainer-feature.json delete mode 100644 src/universal/.devcontainer/local-features/nvs/install.sh diff --git a/src/universal/.devcontainer/devcontainer.json b/src/universal/.devcontainer/devcontainer.json index 2ebf06740f..c8b45688d2 100644 --- a/src/universal/.devcontainer/devcontainer.json +++ b/src/universal/.devcontainer/devcontainer.json @@ -21,7 +21,6 @@ "version": "24", "additionalVersions": "22" }, - "./local-features/nvs": "latest", "ghcr.io/devcontainers/features/python:1": { "version": "3.14.2", "additionalVersions": "3.13.8", @@ -84,7 +83,6 @@ "ghcr.io/devcontainers/features/dotnet", "ghcr.io/devcontainers/features/hugo", "ghcr.io/devcontainers/features/node", - "./local-features/nvs", "ghcr.io/devcontainers/features/conda", "./local-features/patch-conda", "ghcr.io/devcontainers/features/python", diff --git a/src/universal/.devcontainer/local-features/nvs/devcontainer-feature.json b/src/universal/.devcontainer/local-features/nvs/devcontainer-feature.json deleted file mode 100644 index 610e683ad2..0000000000 --- a/src/universal/.devcontainer/local-features/nvs/devcontainer-feature.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "id": "nvs", - "name": "Node Version Switcher", - "containerEnv": { - "NVS_DIR": "/usr/local/nvs", - "PATH": "${NVS_DIR}:${PATH}" - }, - "install": { - "app": "", - "file": "install.sh" - } -} \ No newline at end of file diff --git a/src/universal/.devcontainer/local-features/nvs/install.sh b/src/universal/.devcontainer/local-features/nvs/install.sh deleted file mode 100644 index fb12899a78..0000000000 --- a/src/universal/.devcontainer/local-features/nvs/install.sh +++ /dev/null @@ -1,79 +0,0 @@ -#!/usr/bin/env bash -#------------------------------------------------------------------------------------------------------------- -# Copyright (c) Microsoft Corporation. All rights reserved. -# Licensed under the MIT License. See https://go.microsoft.com/fwlink/?linkid=2090316 for license information. -#------------------------------------------------------------------------------------------------------------- - -USERNAME=${USERNAME:-"codespace"} -NVS_HOME=${NVS_HOME:-"/usr/local/nvs"} - -set -eux - -if [ "$(id -u)" -ne 0 ]; then - echo -e 'Script must be run as root. Use sudo, su, or add "USER root" to your Dockerfile before running this script.' - exit 1 -fi - -# Ensure that login shells get the correct path if the user updated the PATH using ENV. -rm -f /etc/profile.d/00-restore-env.sh -echo "export PATH=${PATH//$(sh -lc 'echo $PATH')/\$PATH}" > /etc/profile.d/00-restore-env.sh -chmod +x /etc/profile.d/00-restore-env.sh - -# Function to run apt-get if needed -apt_get_update_if_needed() -{ - if [ ! -d "/var/lib/apt/lists" ] || [ "$(ls /var/lib/apt/lists/ | wc -l)" = "0" ]; then - echo "Running apt-get update..." - apt-get update - else - echo "Skipping apt-get update." - fi -} - -# Checks if packages are installed and installs them if not -check_packages() { - if ! dpkg -s "$@" > /dev/null 2>&1; then - apt_get_update_if_needed - apt-get -y install --no-install-recommends "$@" - fi -} - -updaterc() { - echo "Updating /etc/bash.bashrc and /etc/zsh/zshrc..." - if [[ "$(cat /etc/bash.bashrc)" != *"$1"* ]]; then - echo -e "$1" >> /etc/bash.bashrc - fi - if [ -f "/etc/zsh/zshrc" ] && [[ "$(cat /etc/zsh/zshrc)" != *"$1"* ]]; then - echo -e "$1" >> /etc/zsh/zshrc - fi -} - -export DEBIAN_FRONTEND=noninteractive - -if ! cat /etc/group | grep -e "^nvs:" > /dev/null 2>&1; then - groupadd -r nvs -fi -usermod -a -G nvs "${USERNAME}" - -git config --global --add safe.directory ${NVS_HOME} -mkdir -p ${NVS_HOME} - -git clone -c advice.detachedHead=false --depth 1 https://github.com/jasongin/nvs ${NVS_HOME} 2>&1 -(cd ${NVS_HOME} && git remote get-url origin && echo $(git log -n 1 --pretty=format:%H -- .)) > ${NVS_HOME}/.git-remote-and-commit -bash ${NVS_HOME}/nvs.sh install -rm ${NVS_HOME}/cache/* - -# Clean up -rm -rf ${NVS_HOME}/.git - -updaterc "if [[ \"\${PATH}\" != *\"${NVS_HOME}\"* ]]; then export PATH=${NVS_HOME}:\${PATH}; fi" - -chown -R "${USERNAME}:nvs" "${NVS_HOME}" -chmod -R g+r+w "${NVS_HOME}" -find "${NVS_HOME}" -type d | xargs -n 1 chmod g+s - -NVS="/home/codespace/.nvs" -mkdir -p ${NVS} -ln -snf ${NVS_HOME}/* $NVS - -echo "Done!" diff --git a/src/universal/.devcontainer/local-features/setup-user/devcontainer-feature.json b/src/universal/.devcontainer/local-features/setup-user/devcontainer-feature.json index 60f864e4df..53a9ef2f40 100644 --- a/src/universal/.devcontainer/local-features/setup-user/devcontainer-feature.json +++ b/src/universal/.devcontainer/local-features/setup-user/devcontainer-feature.json @@ -15,7 +15,6 @@ "ORYX_ENV_TYPE": "vsonline-present", "PYTHONIOENCODING": "UTF-8", "NPM_GLOBAL": "/home/codespace/.npm-global", - "NVS_HOME": "/home/codespace/.nvs", "RVM_PATH": "/usr/local/rvm", "RAILS_DEVELOPMENT_HOSTS": ".githubpreview.dev,.preview.app.github.dev,.app.github.dev", "GOROOT": "/usr/local/go", diff --git a/src/universal/README.md b/src/universal/README.md index 0eb3545499..7c9c568e92 100644 --- a/src/universal/README.md +++ b/src/universal/README.md @@ -29,7 +29,7 @@ For example: - `mcr.microsoft.com/devcontainers/universal:6-noble` - `mcr.microsoft.com/devcontainers/universal:6.0-noble` -- `mcr.microsoft.com/devcontainers/universal:6.0.8-noble` +- `mcr.microsoft.com/devcontainers/universal:6.0.9-noble` See [history](history) for information on the contents of each version and [here for a complete list of available tags](https://mcr.microsoft.com/v2/devcontainers/universal/tags/list). diff --git a/src/universal/manifest.json b/src/universal/manifest.json index ce34d88cb2..58fe2cd495 100644 --- a/src/universal/manifest.json +++ b/src/universal/manifest.json @@ -1,5 +1,5 @@ { - "version": "6.0.8", + "version": "6.0.9", "build": { "latest": true, "rootDistro": "debian", @@ -87,7 +87,6 @@ "git": { "Oh My Zsh!": "/home/codespace/.oh-my-zsh", "nvm": "/usr/local/share/nvm", - "nvs": "/usr/local/nvs", "rbenv": "/usr/local/share/rbenv", "ruby-build": "/usr/local/share/ruby-build" }, diff --git a/src/universal/test-project/test.sh b/src/universal/test-project/test.sh index 8efb073e54..c2d0ef70bb 100755 --- a/src/universal/test-project/test.sh +++ b/src/universal/test-project/test.sh @@ -82,7 +82,6 @@ checkDirectoryOwnership "codespace user has ownership over extension directory" # Node.js check "node" node --version check "nvm" bash -c ". /usr/local/share/nvm/nvm.sh && nvm --version" -check "nvs" bash -c ". /usr/local/nvs/nvs.sh && nvs --version" check "yarn" yarn --version check "npm" npm --version count=$(ls /usr/local/share/nvm/versions/node | wc -l) @@ -136,9 +135,10 @@ check "default-node-version" bash -c "node --version | grep 24." check "default-node-location" bash -c "which node | grep /home/codespace/nvm/current/bin" check "oryx-build-node-project" bash -c "oryx build ./sample/node" check "oryx-configured-current-node-version" bash -c "ls -la /home/codespace/nvm/current | grep /opt/nodejs" -check "nvm-install-node" bash -c ". /usr/local/share/nvm/nvm.sh && nvm install 8.0.0" -check "nvm-works-in-node-project" bash -c "node --version | grep v8.0.0" -check "default-node-location-remained-same" bash -c "which node | grep /home/codespace/nvm/current/bin" +check "nvm-switches-to-additional-node-version" bash -c ". /usr/local/share/nvm/nvm.sh && nvm use 22 && node --version | grep v22." +check "node-location-after-switching-to-22" bash -c "which node | grep /home/codespace/nvm/current/bin" +check "nvm-switches-back-to-default-node-version" bash -c ". /usr/local/share/nvm/nvm.sh && nvm use default && node --version | grep v24." +check "node-location-after-switching-back-to-default" bash -c "which node | grep /home/codespace/nvm/current/bin" # Ensures sdkman works in a Java Project check "default-java-version" bash -c "java --version" From 9ba6147752556f88c1f6433749b3cd350d275b38 Mon Sep 17 00:00:00 2001 From: Kaniska Date: Fri, 10 Jul 2026 16:08:57 +0000 Subject: [PATCH 2/9] Implementing review comments. --- cgmanifest.json | 10 ---------- src/universal/manifest.json | 2 +- 2 files changed, 1 insertion(+), 11 deletions(-) diff --git a/cgmanifest.json b/cgmanifest.json index 3f10f43dbc..efa809951c 100644 --- a/cgmanifest.json +++ b/cgmanifest.json @@ -1510,16 +1510,6 @@ } } }, - { - "Component": { - "Type": "git", - "Git": { - "Name": "nvs", - "repositoryUrl": "https://github.com/jasongin/nvs", - "commitHash": "6b20e9f750ea371bd347e5dbac8406d677292b60" - } - } - }, { "Component": { "Type": "other", diff --git a/src/universal/manifest.json b/src/universal/manifest.json index 58fe2cd495..f4afcc6b35 100644 --- a/src/universal/manifest.json +++ b/src/universal/manifest.json @@ -1,5 +1,5 @@ { - "version": "6.0.9", + "version": "7.0.0", "build": { "latest": true, "rootDistro": "debian", From f44b5e05bcc86f04df9e60e5bd61b8812843499b Mon Sep 17 00:00:00 2001 From: Kaniska Date: Fri, 10 Jul 2026 16:11:57 +0000 Subject: [PATCH 3/9] Changes in the readme file. --- src/universal/README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/universal/README.md b/src/universal/README.md index 7c9c568e92..0aa255fae7 100644 --- a/src/universal/README.md +++ b/src/universal/README.md @@ -27,9 +27,9 @@ The container includes the `zsh` (and Oh My Zsh!) and `fish` shells that you can You can decide how often you want updates by referencing a [semantic version](https://semver.org/) of each image. For example: -- `mcr.microsoft.com/devcontainers/universal:6-noble` -- `mcr.microsoft.com/devcontainers/universal:6.0-noble` -- `mcr.microsoft.com/devcontainers/universal:6.0.9-noble` +- `mcr.microsoft.com/devcontainers/universal:7-noble` +- `mcr.microsoft.com/devcontainers/universal:7.0-noble` +- `mcr.microsoft.com/devcontainers/universal:7.0.0-noble` See [history](history) for information on the contents of each version and [here for a complete list of available tags](https://mcr.microsoft.com/v2/devcontainers/universal/tags/list). From 793b64fc649168002b33d172ed6925c6ca40f97f Mon Sep 17 00:00:00 2001 From: Kaniska Date: Mon, 13 Jul 2026 10:46:32 +0000 Subject: [PATCH 4/9] Fixing GHSA-jp4c-xjxw-mgf9 --- .../local-features/patch-python/install.sh | 7 +++++++ src/universal/test-project/test-utils.sh | 13 +++++++++++++ src/universal/test-project/test.sh | 1 + 3 files changed, 21 insertions(+) diff --git a/src/universal/.devcontainer/local-features/patch-python/install.sh b/src/universal/.devcontainer/local-features/patch-python/install.sh index ff9604ca91..e9aa4b7b00 100644 --- a/src/universal/.devcontainer/local-features/patch-python/install.sh +++ b/src/universal/.devcontainer/local-features/patch-python/install.sh @@ -5,6 +5,7 @@ #------------------------------------------------------------------------------------------------------------- USERNAME=${USERNAME:-"codespace"} +PATCH_PYTHON_PIP_UPGRADE_PATH=${PATCH_PYTHON_PIP_UPGRADE_PATH:-"/usr/local/python/3.13.8/bin/python"} set -eux @@ -38,3 +39,9 @@ update_package() { sudo_if "$PYTHON_PATH -m pip install --upgrade --no-cache-dir $PACKAGE==$VERSION" sudo_if "$PYTHON_PATH -m pip show --no-python-version-warning $PACKAGE" } + +# Updating pip version for python 3.13.8. Must be removed when pinned version 3.13.8 is updated to a different python version. +# https://github.com/advisories/GHSA-jp4c-xjxw-mgf9 +if [ -x "$PATCH_PYTHON_PIP_UPGRADE_PATH" ]; then + sudo_if "$PATCH_PYTHON_PIP_UPGRADE_PATH -m pip install --upgrade pip" +fi diff --git a/src/universal/test-project/test-utils.sh b/src/universal/test-project/test-utils.sh index 872ec8ee66..c4debd8c26 100644 --- a/src/universal/test-project/test-utils.sh +++ b/src/universal/test-project/test-utils.sh @@ -229,6 +229,19 @@ checkPythonPackageVersion() check-version-ge "${PACKAGE}-requirement" "${current_version}" "${REQUIRED_VERSION}" } +checkPythonPipVersion() +{ + PYTHON_PATH=$1 + REQUIRED_VERSION=$2 + + if [ ! -x "${PYTHON_PATH}" ]; then + return 0 + fi + + current_version=$(${PYTHON_PATH} -m pip --version | awk '{print $2}') + [ "$(printf '%s\n%s\n' "${REQUIRED_VERSION}" "${current_version}" | sort -V | tail -1)" = "${current_version}" ] +} + checkCondaPackageVersion() { PACKAGE=$1 diff --git a/src/universal/test-project/test.sh b/src/universal/test-project/test.sh index c2d0ef70bb..276eb8ca83 100755 --- a/src/universal/test-project/test.sh +++ b/src/universal/test-project/test.sh @@ -170,6 +170,7 @@ check "php-version-on-path-is-8.1.30" php --version | grep 8.1.30 # Test patches ls -la /home/codespace +check "python-3.13.8-pip-version" checkPythonPipVersion "/usr/local/python/3.13.8/bin/python" "26.1.0" ## Python - current checkPythonPackageVersion "python" "requests" "2.31.0" From 55c987a6be4d407b74309c34e033538cfb3c520a Mon Sep 17 00:00:00 2001 From: Kaniska Date: Mon, 13 Jul 2026 17:16:18 +0530 Subject: [PATCH 5/9] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- src/universal/test-project/test-utils.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/universal/test-project/test-utils.sh b/src/universal/test-project/test-utils.sh index c4debd8c26..a40b8ed952 100644 --- a/src/universal/test-project/test-utils.sh +++ b/src/universal/test-project/test-utils.sh @@ -235,12 +235,12 @@ checkPythonPipVersion() REQUIRED_VERSION=$2 if [ ! -x "${PYTHON_PATH}" ]; then - return 0 + echoStderr "❌ Python not found at ${PYTHON_PATH}" + return 1 fi - current_version=$(${PYTHON_PATH} -m pip --version | awk '{print $2}') + current_version=$("${PYTHON_PATH}" -m pip --version | awk '{print $2}') [ "$(printf '%s\n%s\n' "${REQUIRED_VERSION}" "${current_version}" | sort -V | tail -1)" = "${current_version}" ] -} checkCondaPackageVersion() { From afe644d42b97c5513bdbcfb2288620b3f1dc9731 Mon Sep 17 00:00:00 2001 From: Kaniska Date: Mon, 13 Jul 2026 13:34:27 +0000 Subject: [PATCH 6/9] Changes in script. --- src/universal/test-project/test-utils.sh | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/universal/test-project/test-utils.sh b/src/universal/test-project/test-utils.sh index a40b8ed952..73316da104 100644 --- a/src/universal/test-project/test-utils.sh +++ b/src/universal/test-project/test-utils.sh @@ -235,12 +235,14 @@ checkPythonPipVersion() REQUIRED_VERSION=$2 if [ ! -x "${PYTHON_PATH}" ]; then + echo "failing checkPythonPipVersion because python path is not executable: ${PYTHON_PATH}" echoStderr "❌ Python not found at ${PYTHON_PATH}" return 1 fi current_version=$("${PYTHON_PATH}" -m pip --version | awk '{print $2}') - [ "$(printf '%s\n%s\n' "${REQUIRED_VERSION}" "${current_version}" | sort -V | tail -1)" = "${current_version}" ] + check-version-ge "pip-requirement" "${current_version}" "${REQUIRED_VERSION}" +} checkCondaPackageVersion() { From 2fca0a173611cfc9ada1fb2465952b55eccb4dc7 Mon Sep 17 00:00:00 2001 From: Kaniska Date: Tue, 14 Jul 2026 14:56:34 +0000 Subject: [PATCH 7/9] Reverting to minor version bump --- src/universal/README.md | 8 ++++---- src/universal/manifest.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/universal/README.md b/src/universal/README.md index 0aa255fae7..f4f916da7c 100644 --- a/src/universal/README.md +++ b/src/universal/README.md @@ -27,9 +27,9 @@ The container includes the `zsh` (and Oh My Zsh!) and `fish` shells that you can You can decide how often you want updates by referencing a [semantic version](https://semver.org/) of each image. For example: -- `mcr.microsoft.com/devcontainers/universal:7-noble` -- `mcr.microsoft.com/devcontainers/universal:7.0-noble` -- `mcr.microsoft.com/devcontainers/universal:7.0.0-noble` +- `mcr.microsoft.com/devcontainers/universal:6-noble` +- `mcr.microsoft.com/devcontainers/universal:6.1-noble` +- `mcr.microsoft.com/devcontainers/universal:6.1.0-noble` See [history](history) for information on the contents of each version and [here for a complete list of available tags](https://mcr.microsoft.com/v2/devcontainers/universal/tags/list). @@ -47,7 +47,7 @@ Access to the Anaconda repository is covered by the [Anaconda Terms of Service]( While the image itself works unmodified, you can also directly reference pre-built versions of `Dockerfile` by using the `image` property in `.devcontainer/devcontainer.json` or updating the `FROM` statement in your own `Dockerfile` to: -`mcr.microsoft.com/devcontainers/universal:5.1-linux` +`mcr.microsoft.com/devcontainers/universal:6.1-linux` Alternatively, you can use the contents of [.devcontainer](.devcontainer) to fully customize your container's contents or to build it for a container host architecture not supported by the image. diff --git a/src/universal/manifest.json b/src/universal/manifest.json index f4afcc6b35..8311842f6b 100644 --- a/src/universal/manifest.json +++ b/src/universal/manifest.json @@ -1,5 +1,5 @@ { - "version": "7.0.0", + "version": "6.1.0", "build": { "latest": true, "rootDistro": "debian", From f9eaa5c603489e3497ed733aa38072de51d8282f Mon Sep 17 00:00:00 2001 From: Kaniska Date: Fri, 17 Jul 2026 11:47:10 +0000 Subject: [PATCH 8/9] Removing cached pip version from conda packages. --- .../local-features/patch-conda/install.sh | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/universal/.devcontainer/local-features/patch-conda/install.sh b/src/universal/.devcontainer/local-features/patch-conda/install.sh index 11d0a69bc3..10ddd0a63d 100644 --- a/src/universal/.devcontainer/local-features/patch-conda/install.sh +++ b/src/universal/.devcontainer/local-features/patch-conda/install.sh @@ -48,6 +48,20 @@ update_conda_package() { sudo_if /opt/conda/bin/python3 -m pip install --upgrade pip +# Remove any cached pip packages under /opt/conda/pkgs vulnerable to GHSA-jp4c-xjxw-mgf9 +# (pip < 26.1). Only pip cache entries older than 26.1 are removed; this does not touch +# the active pip install in site-packages (upgraded above via pip). +for pkg_path in /opt/conda/pkgs/pip-[0-9]*; do + [ -e "$pkg_path" ] || continue + pkg_name="$(basename "$pkg_path")" + pkg_version="${pkg_name#pip-}" + pkg_version="${pkg_version%%-*}" + greater_version="$(printf '%s\n%s\n' "$pkg_version" "26.1" | sort -V | tail -1)" + if [ "$pkg_version" != "$greater_version" ]; then + sudo_if "rm -rf $pkg_path" + fi +done + # Temporary: Upgrade python packages due to security vulnerabilities # They are installed by the conda feature and Conda distribution does not have the patches From 1efe5aa2706369395a387aa134c6204c6f89a0e2 Mon Sep 17 00:00:00 2001 From: Kaniska Date: Fri, 17 Jul 2026 12:29:05 +0000 Subject: [PATCH 9/9] Adding tests. --- src/universal/test-project/test-utils.sh | 19 +++++++++++++++++++ src/universal/test-project/test.sh | 1 + 2 files changed, 20 insertions(+) diff --git a/src/universal/test-project/test-utils.sh b/src/universal/test-project/test-utils.sh index 73316da104..575a9e6211 100644 --- a/src/universal/test-project/test-utils.sh +++ b/src/universal/test-project/test-utils.sh @@ -252,6 +252,25 @@ checkCondaPackageVersion() check-version-ge "conda-${PACKAGE}-requirement" "${current_version}" "${REQUIRED_VERSION}" } +checkNoVulnerablePipCache() +{ + CACHE_DIR=$1 + MIN_VERSION=$2 + + for pkg_path in "${CACHE_DIR}"/pip-[0-9]*; do + [ -e "${pkg_path}" ] || continue + pkg_name="$(basename "${pkg_path}")" + pkg_version="${pkg_name#pip-}" + pkg_version="${pkg_version%%-*}" + greater_version="$(printf '%s\n%s\n' "${pkg_version}" "${MIN_VERSION}" | sort -V | tail -1)" + if [ "${pkg_version}" != "${greater_version}" ]; then + echoStderr "Found vulnerable cached pip package: ${pkg_path} (version ${pkg_version} < ${MIN_VERSION})" + return 1 + fi + done + return 0 +} + checkBundledNpmVersion() { NODE_VERSION=$1 diff --git a/src/universal/test-project/test.sh b/src/universal/test-project/test.sh index 276eb8ca83..8589a7a0e5 100755 --- a/src/universal/test-project/test.sh +++ b/src/universal/test-project/test.sh @@ -171,6 +171,7 @@ check "php-version-on-path-is-8.1.30" php --version | grep 8.1.30 ls -la /home/codespace check "python-3.13.8-pip-version" checkPythonPipVersion "/usr/local/python/3.13.8/bin/python" "26.1.0" +check "no-vulnerable-pip-cache-in-conda-pkgs" checkNoVulnerablePipCache "/opt/conda/pkgs" "26.1" ## Python - current checkPythonPackageVersion "python" "requests" "2.31.0"